I am new to ruby and trying to override the omniauth library method. I would like to make it support env.header Please see this pull request on omniauth. So how can I make this changes in my project?
In Ruby classes can simply override any methods provided by a mixin (an included module):
module Strategy
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def hello
"Hello from Strategy"
end
end
end
class MyStrategy
include Strategy
def self.hello
"Hello from MyStrategy"
end
end
puts MyStrategy.hello
Which means that a Strategy author can simply override any of the methods when authoring a strategy class. So you can simply override the request_call method in your strategy instead which is a better alternative since most strategies do not need all the request headers to be stored in the session.
Since OmniAuth::Strategy is a module rather than a class you cannot use super to call the original method. What you can do instead is to alias the mixin method before overriding it in your class:
module OmniAuth
class MyStrategy
include Strategy
class << self
alias :original_request_call :request_call
end
def self.request_call
session['omniauth.headers'] = headers
original_request_call
end
# ...
end
end
The weird alias syntax is because its not a method but rather syntactic sugar.
Related
I am trying to modify a gem (Devise token auth to be precise) to suit my needs. For that I want to override certain functions inside the concern SetUserByToken.
The problem is how do I override that?
I don't want to change the gem files. Is there an easy/standard way of doing that?
Bear in mind here that a "concern" in Rails is just a module with a few programmer conveniences from ActiveSupport::Concern.
When you include a module in a class the methods defined in the class itself will have priority over the included module.
module Greeter
def hello
"hello world"
end
end
class LeetSpeaker
include Greeter
def hello
super.tr("e", "3").tr("o", "0")
end
end
LeetSpeaker.new.hello # => "h3ll0 w0rld"
So you can quite simply redefine the needed methods in ApplicationController or even compose a module of your own of your own without monkey patching the library:
module Greeter
extend ActiveSupport::Concern
def hello
"hello world"
end
class_methods do
def foo
"bar"
end
end
end
module BetterGreeter
extend ActiveSupport::Concern
def hello
super.titlecase
end
# we can override class methods as well.
class_methods do
def foo
"baz"
end
end
end
class Person
include Greeter # the order of inclusion matters
include BetterGreeter
end
Person.new.hello # => "Hello World"
Person.foo # => "baz"
See Monkey patching: the good, the bad and the ugly for a good explanation why it often is better to overlay your custom code on top of a framework or library rather than modifying a library component at runtime.
You can monkey patch concerns like any other module:
module DeviseTokenAuth::Concerns::SetUserByToken
# Your code here
end
If you want to extend the behavior of an existing method, try using an around alias:
http://rubyquicktips.com/post/18427759343/around-alias
What is the difference between adding instance methods whith active concern and through the normal ruby def keyword on the module?
module MonsterConcern
extend ActiveSupport::Concern
included do
def engage_rage
end
def chew_bones
end
end
end
and
module MonsterConcern
def engage_rage
end
def chew_bones
end
end
As far as I know there is no difference if the only thing you are interested in is instance methods.
The ActiveSupport::Concern advantage is the ability to define class methods, and to handle some nasty module interdependencies better (related to calling class methods in the included block).
You can read more here: http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
I'm a total newbie at ruby (java background) so I'm sorry if this is a really dumb question.
I'm working through some tutorials on modules and they seem somewhat similar to static classes. The bit I'm having trouble wrapping my head around is why you would do something like the following:
module ExampleModule
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def myMethod
end
end
end
Why wouldn't you just put the methods in ClassMethods into ExampleModule and save adding the method hook. I'm sure I'm missing something really basic but I've been at this for a while now so I feel the need to ask.
It's a ruby idiom. It's useful when you want a module that:
adds some instance methods to a class
adds class methods in the same time /like Java static methods/
in the same time
Example:
module ExampleModule
def self.included(base)
puts 'included'
base.extend(ClassMethods)
end
def foo
puts 'foo!'
end
module ClassMethods
def bar
puts 'bar!'
end
end
end
class ExampleClass
include ExampleModule
end
ExampleClass.bar
ExampleClass.new.foo
If you only want to add class methods, you don't need this idiom, you can just add a method to your module and 'extend' it instead of including it.
When on Rails, this idiom is obsolete and you should use ActiveSupport::Concern instead.
The patter you use here is common when both class methods and instance methods are included via a module in ruby. It gives you the advantage of just having to write
include ExampleModule
for including instance methods and extending class methods instead of
# include instance methods
include ExampleModule
# extend class methods
extend ExampleModule::ClassMethods
So, if used just to extend the class with some methods, my personal preference is to use extend directly.
module ExtensionAtClassLevel
def bla
puts 'foo'
end
end
class A
extend ExtensionAtClassLevel
end
A.bla #=> 'foo'
If both instance and class methods are added, I use the include hook you described.
Some rubyists tend to prefer using extend via the include hook to pure extend, which has no reason if you are just adding class methods like in your example.
I'm missing something stupid. Help?
lib/api.rb
require 'httparty'
module API
def self.call_api(query)
base_url = "http://myapi.com"
return HTTParty.get("#{base_url}/#{query}.json")
end
end
models/job.rb
require 'api'
class Job
include API
def self.all(page=1)
self.call_api "jobs?page=#{page}"
end
end
Job::all
NoMethodError: undefined method `call_api' for Job:Class
If I move my "call_api" directly into the Job class, it works. What am I missing?
The other answers have pointed out your issue you should have extend instead of include:
class Job
extend API
You might also like to consider using the following pattern which aside from being useful can also help to alleviate the confusion, since you can tell straight away that your class methods are in the ClassMethods module while your instance methods are directly in the MyModule module.
module MyModule
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def my_class_method
end
end
def my_instance_method
end
end
This uses Ruby's included callback (which is fired every time a module in included in a class), we redefine the callback (which normally doesn't do anything), to extend the class in which the module was included with the ClassMethods sub module. This is a very common metaprogramming pattern in Ruby. If you use this pattern you no longer have to worry about extend, just use include:
class MyClass
include MyModule
then:
MyClass.my_class_method
MyClass.new.my_instance_method
You can also take this pattern a little bit further:
module MyModule
include ClassMethods
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def my_class_method
end
end
def my_instance_method
end
end
Notice that the parent module includes its child ClassMethods module directly. This way my_class_method becomes both an instance and a class method:
class MyClass
include MyModule
MyClass.my_class_method
MyClass.new.my_class_method
You have to be a bit careful if you do that about how you code your method in the ClassMethods child module. But I have found this pattern extremely handy on occasion.
I believe what you need is to define call_api as an instance method in your module, then extend API instead of include API. In general, include is for instance methods and extend is for class methods.
The problem is that you are defining the method call_api directly on the API module, rather than on the inheritance chain of the Job class.
You can call API.call_api just fine, but the problem is that API's "instance methods" are not part of the inheritance chain (not instance methods defined for the receiver of the included module, but API's singleton instance methods - remember API is an instance of the class Module with its own methods)
This is way of doing what you are trying to do:
module API
def self.included(base)
def base.call_api(...)
...
end
end
end
but there may be cleaner ways using extend rather than include on your classes.
Ok so I am using some module/lib/plugin (not sure of the exact name), let's say its a authentication/authorization plugin like: http://github.com/technoweenie/restful-authentication
class HomeController < ApplicationController
some_module_name_here
end
Now just from adding the code above 'some_module_name_here', I can access to methods from that module.
What is that line of code doing that gives me access to methods/objects from the module?
Is that declaring a variable like in say java/c#:
public SomeModule _someModule;
I know that plugins/modules basically extend the class under the covers, but how does it do this with a single line of code?
Is it called in the constructor somehow?
When you create a ruby plugin, and load it into the rails app via environment.rb, bundler, or a require call, the methods are loaded as "modules" that can be called. The ones that act like you're talking about will have an extra method called acts_as_list or something similar. All that method does is include the methods of the module into the class where that line was called.
Here's an example, which you could include in your app's lib folder and play with:
module Bellmyer
module Pointless
def self.included(base)
base.extend PointlessMethods
end
module PointlessMethods
def acts_as_pointless
unless included_modules.include? InstanceMethods
extend ClassMethods
include InstanceMethods
end
end
end
module ClassMethods
def pointless_class?
true
end
end
module InstanceMethods
def pointless_instance?
true
end
end
end
end
The module is available to any ruby class in your app, but the methods don't actually get loaded until you call acts_as_pointless, which then includes and extends your class with the methods listed. Only the acts_as_pointless method is immediately available to the model. This is the standard pattern for an ActiveRecord plugin.
That's not how it works.
When the plugin or gem is loaded it adds a class method to, in this case, ApplicationController named some_module_name. When you call that methods, a bunch of other class and instance methods are included.
Check out your favourite gem or plugin to see how they do it exactly.