To generate mocks for Omniauth, I Added this method to config/environments/development.rb
def provides_mocks_for(*providers)
providers.each do |provider|
class_eval %Q{
OmniAuth.config.add_mock(provider, {
:uid => '123456',
:provider => provider,
:nickname => 'nickname',
:info => {
'email' => "#{provider}#webs.com",
'name' => 'full_name_' + provider
}
})
}
end
end
then I call in the same file:
provides_mocks_for :facebook, :twitter, :github, :meetup
But I get:
3.1.3/lib/active_support/core_ext/kernel/singleton_class.rb:11:in `class_eval': can't create instance of singleton class (TypeError)
class_evaland module_eval (which are equivalent) are used to evaluate and immediately execute strings as Ruby code. As such they can be used as a meta-programming facility to dynamically create methods. An example is
class Foo
%w[foo bar].each do |name|
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}
puts '#{name}'
end
RUBY
end
end
It will create two methods foo and bar which print their respective values. As you can see, I create a string containing the actual source code of the function and pass it into class_eval.
While this is a very capable instrument of executing dynamically created code, it must be used with great care. If you make errors here, BAD THINGS WILL HAPPEN™. E.g. if you use user-supplied values when generating code, make really sure the variables contain only values you expect. Eval-based function should generally be used as the last resort.
A cleaner and generally preferred variant is to use define_method like so:
class Foo
%w[foo bar].each do |name|
define_method name do
puts name
end
end
end
(Note that MRI is a wee bit faster with the eval variant. But that doesn't matter most of the time when compared to the added safety and clarity.)
Now in your given code, you effectively write code into a string that can be run directly. Using class_eval here leads to the string being executed in the context of the top-most object (Kernel in this case). As this is a special singleton object which can't be instanciated (similar to nil, true and false), you get this error.
However, as you create directly executable code, you don't need to use class_eval (or any form of eval) at all. Just run your code in a loop as is. And always remember: meta-programming variants (of which the eval methods are among the most bad-ass) should only be used as the last resort.
Related
I have a Rails application with a recurring need for setting default attributes. Sometimes the user will supply values for the attributes which will be respected, but in other circumstances either the model or the user might desire that these attributes are overridden with default values disregarding original values.
I guessed that this problem called for a banged (!) and non-banged method for setting default values allowing the user and program to switch to the appropriate state. The non-banged setter will only set default values when they are not nil whilst the banged version will always overwrite the attributes with defaults. The difference is minor:
class BangDiBang
attr_accessor :value
def set_default
self.value ||= do_some_suff_to_determine_default_value
end
def set_default!
self.value = do_some_suff_to_determine_default_value
end
...
end
The issue with this code is that if I had a bunch of variables to set, I would end up repeating the same code twice for each variable.
My question is how to partial out this code? Saving the logic in one method and having two methods set_value and set_value! calling the central logic with the different assignment operators.
I have conjured one solution: write the central logic as text, replace the assignment operation from the setter methods and evaluate (but this does not feel right). How do I not repeat myself?
The way you're going about this isn't going to work for multiple params since you're calling set_default but not specifying which variable. You'll want to define behavior for each variable, ideally. There are libraries that handle this sort of thing, but you can roll your own pretty easily:
class Example
def self.default_param(name, default_value)
define_method("default_#{name}") { default_value }
define_method(name) do
instance_variable_get("##{name}") || default_value
end
attr_writer name
end
default_param :foo, 'foo default'
end
ex = Example.new
ex.foo #=> "foo default"
ex.foo = 'bar'
ex.foo #=> "bar"
ex.default_foo #=> "foo default"
I've renamed set_default and set_default! to be more clear: for each variable with a default value, three methods are created (example using foo as the variable name):
foo — returns the value of #foo if it is truthy, and default_foo otherwise
foo= — sets the value of #foo
default_foo — returns the specified default
You could compartmentalize and dry up some of the code above further, creating a default_params (plural) method to take a hash, extracting the class macro to a concern:
module DefaultParams
def default_param(name, default_value)
define_method("default_#{name}") { default_value }
define_method(name) do
instance_variable_get("##{name}") || default_value
end
attr_writer name
end
def default_params(params)
params.each { |default| default_param(*default) }
end
end
class Example
extend DefaultParams
default_params foo: 'default foo', bar: 'my favorite bar'
end
I have implemented a solution with great inspiration from coreyward. I did not realize at first that rather than having two methods for setting an array of default values, I needed the default values separated out into single methods. This gives a great flexibility to the design of the application. So thanks a lot for the answer.
For the rails setup I’ve added to application_record.rb
def set_attr_from_defaults
default_attrs.each do |atr|
eval("self.#{atr[0..atr.length-9]} ||= self.#{atr}")
end
end
def set_attr_from_defaults!
default_attrs.each do |atr|
eval("self.#{atr[0..atr.length-9]} = self.#{atr}")
end
end
def set_default_attr params
params.each { |key, value| self.define_singleton_method(key){value} }
end
def default_attrs
self.attributes.keys.map{|i| (i+'_default').to_sym} & self.methods
end
default_attrs yields a list of symbols off default attributes. set_default_attr defines singleton methods, with intended use of parsing in a hash like attrname_default: 'default_value' .... The set_attr_from_defaults will set attributes from default values, when an attribute has a pair like var: var_default. The number 9 is the length of _default + 1.
I created a Rails Concern for my ActiveModel called List
When I run Product.all from the Rails Console, I get:
NameError: undefined local variable or method `parameters' for
Product:Class
When parameters is changed to #parameters I get this error:
NoMethodError: undefined method `include?' for nil:NilClass
Possible Solutions
Which is better, using a constant PARAMETERS or ##Parameters? Pros and Cons?
Code
module List
extend ActiveSupport::Concern
require 'csv'
parameters = [
:visible,
:desc,
:value,
]
attr_accessor(*parameters)
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
module ClassMethods
def all
list = []
filename = File.join(Rails.root,"app/models/data/#{self.name.downcase}.csv")
CSV.foreach(filename, headers: true) do |row|
list << self.new(row.select{|key,_| parameters.include? key.to_sym })
end
return list
end
def visible
list = []
filename = File.join(Rails.root,"app/models/data/#{self.name.downcase}.csv")
CSV.foreach(filename, headers: true) do |row|
list << self.new(row.select{|key,_| parameters.include? key.to_sym }) if row['visible']=='1'
end
return list
end
end
end
For a quick solution, to make Product.all and Product.visible work with the least amount of modification to your existing code, you can define a parameters method inside module ClassMethods. For example:
def parameters
#parameters ||= [:visible, :desc, :value]
end
This method solution can also serve as a long-term solution if you plan to use the parameters outside of the concern, or if a subclass might want to define its own parameters.
However, if the parameters are only meant to be used inside this concern, and this data will never change, at least not through any application logic, then a constant would be the best solution because it conveys the proper meaning to the reader. I would also freeze it to prevent modification:
PARAMETERS = [:visible, :desc, :value].freeze
Another option, as Rich mentioned, is to define a class variable. Note that the constant will work whether you define it inside the List module, or inside the ClassMethods module. However, a class variable will only work inside the ClassMethods module if you want Product to be able to call it as parameters.
Also, note that self is implied in any method within ClassMethods, so you don't need to specify it. If you defined a parameters method, it would be considered a Product class method, and if you used parameters within the all method, it would refer to the class method, not an instance method as suggested by Rich.
Class variables are generally discouraged in Ruby because their side effects are often misunderstood. The Ruby Style Guide recommends avoiding them: https://github.com/bbatsov/ruby-style-guide#no-class-vars
As for speed, I compared the method and constant solutions, and it looks like the constant is faster:
require "benchmark/ips"
PARAMETERS = [:visible, :desc, :value].freeze
def parameters
#parameters ||= [:visible, :desc, :value]
end
def uses_constant
puts PARAMETERS
end
def uses_method
puts parameters
end
Benchmark.ips do |x|
x.report("constant") { uses_constant }
x.report("method") { uses_method }
x.compare!
end
The result:
Comparison:
constant: 45256.8 i/s
method: 44799.6 i/s - 1.01x slower
Set it as a class var:
module List
extend ActiveSupport::Concern
##parameters = [:visible, :desc, :value]
cattr_Accessor :parameters #-> List.parameters && List.new.parameters
The problem you have right now is you're calling an instance method from a class method:
module ClassMethods
self.new(row.select{|key,_| parameters.include? key.to_sym })
With the class variable code, you'd be able to run:
self.new(row.select{|key,_| self.parameters.include? key.to_sym })
A method created with def doesn't see local variables present when the method was defined, so your first attempt doesn't work.
With the instance variable, the object your are setting the variable on (your module) and the object trying to read it (the class that included your module) are different. Instance variables don't take part in inheritance at all, so that doesn't work.
What would work is a constant in your List module, ie
PARAMETERS = [:visible, :desc, :value]
Because your class methods module is inside the List module, code inside it will find constants set on List. Constant lookup first looks at lexical scope (see Module.nesting for this search path) and then inheritance.
I have a several classes, each of which define various statistics.
class MonthlyStat
attr_accessor :cost, :size_in_meters
end
class DailyStat
attr_accessor :cost, :weight
end
I want to create a decorator/presenter for a collection of these objects, that lets me easily access aggregate information about each collection, for example:
class YearDecorator
attr_accessor :objs
def self.[]= *objs
new objs
end
def initialize objs
self.objs = objs
define_helpers
end
def define_helpers
if o=objs.first # assume all objects are the same
o.instance_methods.each do |method_name|
# sums :cost, :size_in_meters, :weight etc
define_method "yearly_#{method_name}_sum" do
objs.inject(0){|o,sum| sum += o.send(method_name)}
end
end
end
end
end
YearDecorator[mstat1, mstat2].yearly_cost_sum
Unfortunately define method isn't available from within an instance method.
Replacing this with:
class << self
define_method "yearly_#{method_name}_sum" do
objs.inject(0){|o,sum| sum += o.send(method_name)}
end
end
...also fails because the variables method_name and objs which are defined in the instance are no longer available. Is there an idomatic was to accomplish this in ruby?
(EDITED: I get what you're trying to do now.)
Well, I tried the same approaches that you probably did, but ended up having to use eval
class Foo
METHOD_NAMES = [:foo]
def def_foo
METHOD_NAMES.each { |method_name|
eval <<-EOF
def self.#{method_name}
\"#{method_name}\".capitalize
end
EOF
}
end
end
foo=Foo.new
foo.def_foo
p foo.foo # => "Foo"
f2 = Foo.new
p f2.foo # => "undefined method 'foo'..."
I myself will admit it's not the most elegant solution (may not even be the most idiomatic) but I've run into similar situations in the past where the most blunt approach that worked was eval.
I'm curious what you're getting for o.instance_methods. This is a class-level method and isn't generally available on instances of objects, which from what I can tell, is what you're dealing with here.
Anyway, you probably are looking for method_missing, which will define the method dynamically the first time you call it, and will let you send :define_method to the object's class. You don't need to redefine the same instance methods every time you instantiate a new object, so method_missing will allow you to alter the class at runtime only if the called method hasn't already been defined.
Since you're expecting the name of a method from your other classes surrounded by some pattern (i.e., yearly_base_sum would correspond to a base method), I'd recommend writing a method that returns a matching pattern if it finds one. Note: this would NOT involve making a list of methods on the other class - you should still rely on the built-in NoMethodError for cases when one of your objects doesn't know how to respond to message you send it. This keeps your API a bit more flexible, and would be useful in cases where your stats classes might also be modified at runtime.
def method_missing(name, *args, &block)
method_name = matching_method_name(name)
if method_name
self.class.send :define_method, name do |*args|
objs.inject(0) {|obj, sum| sum + obj.send(method_name)}
end
send name, *args, &block
else
super(name, *args, &block)
end
end
def matching_method_name(name)
# ... this part's up to you
end
In railscasts project you can see this code:
before(:each) do
login_as Factory(:user, :admin => true)
end
The corresponding definition for the function is:
Factory.define :user do |f|
f.sequence(:github_username) { |n| "foo#{n}" }
end
I can't understand how the admin parameter is passing to function, while in the function there's no word about admin parameter. Thanks
Factory.define is not a function definition, it is a method that takes a symbol or string (in this case user) and a block that defines the factory you are making. Factory(:user, :admin => true) makes a User object, with admin attributes. It is not calling the code in your second snippet, it is calling Factory() which initializes a factory, and selects one (in this case the one defined in second snippet). Then it passes options in hash form to Factory as well.
Factory selects the :user factory which is very generic. The option :admin=>true just tells Factory to set the admin instance variable on User to true.
This is actually what it is calling in factory.rb in factory girl
def initialize(name, options = {}) #:nodoc:
assert_valid_options(options)
#name = factory_name_for(name)
#options = options
#attributes = []
end
So Factory(name,options) is equivalent to Factory.new(name,options) in this code.
http://www.ruby-doc.org/core/classes/Kernel.html Notice Array and String etc have similar constructs. I am trying to figure out how they did that now.
This is all confusing even for decent Ruby programmers. I recommend strongly the book "Metaprogramming Ruby" It is probably the best book I have read in ruby and it tells you a lot about this magic stuff.
Michael Papile's response is essentially correct. However, I'd like to elaborate upon it a bit as there are some technical nuances that you might wish to be aware of. I looked over the code for railscasts and factory_girl and I believe there are a few extra pieces to the puzzle that explain how the :admin => true arg ends up creating the admin attribute of the user factory. The attribute addition does not actually happen by way of Factory's initialize() method, although as Michael pointed out that method is indeed being called in service of building the new user factory object.
I'm going to include in this explanation all the steps I took in case you'd like to see how to go about investigating similar questions you might have.
Since your original post is dated Feb. 17th I looked at the version of railscasts that closely matches that date.
I looked in its Gemfile:
https://github.com/ryanb/railscasts/blob/d124319f4ca2a2367c1fa705f5c8229cce70921d/Gemfile
Line 18:
gem "factory_girl_rails"
I then checked out the commit of factory_girl_rails that most closely matched the Feb 17th date.
https://github.com/thoughtbot/factory_girl_rails/blob/544868740c3e26d8a5e8337940f9de4990b1cd0b/factory_girl_rails.gemspec
Line 16:
s.add_runtime_dependency('factory_girl', '~> 2.0.0.beta')
factory_girl version 2.0.0.beta was actually not so easy to find. There are no github tags with that name so I just checked out the closest in terms of commit date.
https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/syntax/vintage.rb
Lines 122-128:
# Shortcut for Factory.default_strategy.
#
# Example:
# Factory(:user, :name => 'Joe')
def Factory(name, attrs = {})
Factory.default_strategy(name, attrs)
end
So the Factory invocation in railscasts is actually calling a convenience method which invokes the "default strategy", which is located in the same file:
Lines 39-52:
# Executes the default strategy for the given factory. This is usually create,
# but it can be overridden for each factory.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this instance.
#
# Returns: +Object+
# The result of the default strategy.
def self.default_strategy(name, overrides = {})
self.send(FactoryGirl.find(name).default_strategy, name, overrides)
end
Note that FactoryGirl.find is invoked to get the object on which to invoke default_strategy. The find method resolves to here:
https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/registry.rb
Lines 12-14:
def find(name)
#items[name.to_sym] or raise ArgumentError.new("Not registered: #{name.to_s}")
end
Here the name is :user. Thus we wish to invoke default_strategy on the user factory. As Michael Papile pointed out, this user factory was defined and registered by the railscasts code that you originally thought was the class definition for Factory.
https://ryanb/railscasts/blob/d124319f4ca2a2367c1fa705f5c8229cce70921d/spec/factories.rb
Lines 23-25:
Factory.define :user do |f|
f.sequence(:github_username) { |n| "foo#{n}" }
end
So in investigating what the default strategy is for the user factory, I looked around in the railscasts project and found this:
https://ryanb/railscasts/blob/d124319f4ca2a2367c1fa705f5c8229cce70921d/spec/factories.rb
Lines 43-45:
def default_strategy #:nodoc:
#options[:default_strategy] || :create
end
:create is the default strategy. We go back to factory_girl to find the def for create.
https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/syntax/methods.rb
Lines 37-55:
# Generates, saves, and returns an instance from this factory. Attributes can
# be individually overridden by passing in a Hash of attribute => value
# pairs.
#
# Instances are saved using the +save!+ method, so ActiveRecord models will
# raise ActiveRecord::RecordInvalid exceptions for invalid attribute sets.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this instance.
#
# Returns: +Object+
# A saved instance of the class this factory generates, with generated
# attributes assigned.
def create(name, overrides = {})
FactoryGirl.find(name).run(Proxy::Create, overrides)
end
The create strategy calls the run method defined here:
https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/factory.rb
Lines 86-97:
def run(proxy_class, overrides) #:nodoc:
proxy = proxy_class.new(build_class)
overrides = symbolize_keys(overrides)
overrides.each {|attr, val| proxy.set(attr, val) }
passed_keys = overrides.keys.collect {|k| FactoryGirl.aliases_for(k) }.flatten
#attributes.each do |attribute|
unless passed_keys.include?(attribute.name)
attribute.add_to(proxy)
end
end
proxy.result(#to_create_block)
end
A translation/summarization of what this code is doing:
First, the proxy object is built by calling new on the proxy_class, which in this case is Proxy::Create, which is defined here:
https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/proxy/create.rb
Basically all you need to know is that proxy is building a new user factory object and invoking callbacks before and after the factory object is created.
Going back to the run method, we see that all the extra args that were originally passed into the Factory convenience method (in this case, :admin => true) are now being labelled as overrides. The proxy object then invokes a set method, passing in each attribute-name/value pair as args.
The set() method is part of the Build class, the parent class of Proxy.
https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/proxy/build.rb
Lines 12-14:
def set(attribute, value)
#instance.send(:"#{attribute}=", value)
end
Here #instance refers to the proxied object, the user factory object.
This then, is how :admin => true is set as an attribute on the user factory that the railscasts spec code creates.
If you want, you can google "programming design patterns" and read about the following patterns: Factory, Proxy, Builder, Strategy.
Michael Papile wrote:
http://www.ruby-doc.org/core/classes/Kernel.html Notice Array and
String etc have similar constructs. I am trying to figure out how they
did that now.
If you are still curious, the Array and String you see in the Kernel doc are actually just factory methods used to create new objects of those types. That's why no new method invocation is needed. They are not actually constructor calls per se, but they do allocate and initialize Array and String objects, and hence under the hood they are doing the equivalent of calling initialize() on objects of those types. (In C though, of course, not Ruby)
I don't think that second snippet is the definition for the function. Function definitions have def and end. I think that second snippet looks like a function or method being called with an argument of :user and a block that takes an f parameter.
Of course with metaprogramming you can never really be sure what the hell is going on.
I have a private method in my Rails app to connect to Amazon S3, execute a passed block of code, then close the connection to S3. It looks like so;
def S3
AWS::S3::Base.establish_connection!(
:access_key_id => 'Not telling',
:secret_access_key => 'Really not telling'
)
data = yield
AWS::S3::Base.disconnect
data
end
It is called like this (as an example);
send_data(S3 {AWS::S3::S3Object.value("#{#upload_file.name}",'bucket')}, :filename => #upload_file.name)
I call this method in a number of ways in my controller and model so have it included in both classes as a private method. This works fine and I'm happy with it but it's not very DRY.
How can I make this method accessible to both my model and controller but only have the code appear once? This is more of a Ruby question than a Rails question and reflects my newness to OOP. I'm guessing a module or a mix-in is the answer but I haven't really been using either of these up until now and need a little hand-holding.
Thanks.
Modules are used for 3 different things in ruby. First is namespacing. Having class or constant definitions inside a module won't collide with classes or constants outside that module. Something like this
class Product
def foo
puts 'first'
end
end
module Affiliate
class Product
puts 'second'
end
end
p = Product.new
p.foo # => 'first'
p = Affiliate::Product.new
p.foo # => 'second'
The second use for modules is as a place to stick methods that don't really have a place anywhere else. You can do this inside a class too, but using a module sort of tells people reading the code that it is not meant to be instanciated. Something like this
module Foo
def self.bar
puts 'hi'
end
end
Foo.bar #=> 'hi'
Finally (and the most confusing) is that modules can be included into other classes. Using them this way is also referred to as a mixin, because you are "mixing in" all the methods into whatever you are including.
module Foo
def bar
puts 'hi'
end
end
class Baz
include Foo
end
b = Baz.new
b.bar #=> 'hi'
Mixins are actually a way more complected topic then I am covering here, but going deeper would probably be confusing.
Now, to me, S3 seems to be something that really belongs in the controller, since controllers are usually the things dealing with incoming and outgoing connections. If that is the case, I would just have a protected method on application controller, since that will be accessible to all other controllers, but still be private.
If you do have a good reason for it being in the model too, I would go for a mixin. Something like
module AwsUtils
private
def S3
AWS::S3::Base.establish_connection!\
:access_key_id => 'Not telling',
:secret_access_key => 'Really not telling'
data = yield
AWS::S3::Base.disconnect
data
end
end
If you put that in lib/aws_utils.rb, you should be able to use it by adding include AwsUtils in both your controller and your model. Rails knows to look for classes and modules in lib, but only if the name matches (in wide case). I called it AwsUtils because I know what rails will look for when it sees that (aws_utils.rb), and to be honest, I have no idea what it will need for S3Utils ;-)
Feel free to ask for more info if I wasn't clear on something. Modules tend to be one of those things in ruby that while amazing, are downright baffling to newcomers.
Your hunch is correct: you can put a module in the lib directory. In
order to make these methods available to your models, simply include it
with:
class Model < ActiveRecord::Base
include MyModule
end
The included module's instance methods will become instance methods on your class. (This is known as a mixin)
module MyModule
def S3
#...
end
end
You can write a module as :
module MyModule
def self.S3(args*)
AWS::S3::Base.establish_connection!(
:access_key_id => 'Not telling',
:secret_access_key => 'Really not telling'
)
data = yield
AWS::S3::Base.disconnect
data
end
end
and then call it in your controller or model as
MyModule.S3(params*)