Variables in ruby method names - ruby-on-rails

I have the following code:
for attribute in site.device_attributes
device.attribute
end
where I would like the code to substitute the value of "attribute" for the method name.
I have tried device."#{attribute}" and various permutations.
Is this completely impossible? Am I missing something?
I have considered overriding method_missing, but I can't figure out how that would actually help me when my problem is that I need to call an "unknown" method.

You can use #send method to call object's method by method's name:
object.send(:foo) # same as object.foo
You can pass arguments with to invoked method:
object.send(:foo, 1, "bar", 1.23) # same as object.foo(1, "bar", 1.23)
So, if you have attribute name in variable "attribute" you can read object's attribute with
object.send(attribute.to_sym)
and write attribute's value with
object.send("#{attribute}=".to_sym, value)
In Ruby 1.8.6 #send method can execute any object's method regardless of its visibility (you can e.g. call private methods). This is subject to change in future versions of Ruby and you shouldn't rely on it. To execute private methods, use #instance_eval:
object.instance_eval {
# code as block, can reference variables in current scope
}
# or
object.instance_eval <<-CODE
# code as string, can generate any code text
CODE
Update
You can use public_send to call methods with regard to visibility rules.
object.public_send :public_foo # ok
object.public_send :private_bar # exception

The "send" method should do what you're looking for:
object = "upcase me!"
method = "upcase"
object.send(method.to_sym) # => "UPCASE ME!"

Matt and Maxim are both correct, but leave out a detail that might help you get your head around the #send syntax: In Ruby, calling a method is really sending a message. Softies on Rails has a relatively straightforward explanation of that.

you can also do
device.instance_eval(attribute)

Related

Remove eval from method using dynamic variables in Ruby

The following method is passed a :symbol which correlates to both a method name and an instance variable set via attr_accessor :symbol. The only way I have been able to make this work is via eval. Is there a way around this?
def collection_exists?(event_list)
return unless self.class.method_defined?(event_list)
eval("self.#{event_list.to_s}").any?
end
You can make use of public_send
def collection_exists?(event_list)
send(event_list).any?
end
You might use one of the following options:
Object#method:
method(event_list.to_sym).call.any?
Object#public_send
public_send(event_list.to_sym).any?
Please note, that using public_send is safer, than send, because latter does not care about the method's visibility and would work with protected and private methods smoothly, whereas public_send (as you'd guess from it's name) would raise if you try to call non-public method with it.
Also note, that you do not need self for reading values, it is implicit.

Some questions about symbol and instance method in Ruby

I'm currently try to learn ruby on 'Learn Ruby The Hard Way'
Here's my question...
The following code are from exercise 40:
cities = {'CA'=> 'San Francisco', 'MI'=> 'Detroit', 'FL'=> 'Jacksonville'}
cities['NY'] = 'New York'
cities['OR'] = 'Portland'
def find_city(map, state)
if map.include? state
return map[state]
else
return 'Not found.'
end
end
cities[:find] = method(:find_city)
while true
print 'State? (ENTER to quit) '
state = gets.chomp
break if state.empty?
puts cities[:find].call(cities, state)
end
I played around with the code, and finally understand how it works.
But I still don't understand about two things:
first...
In about middle of the code,
it defined a variable
cities[:find] = method(:find_city)
As what I know for now, the :(colon) declare a symbol.
I want to know is it a better practice to name a variable as cities[:find]
instead of using cities_find in this case?
I'm not quite sure what's the differences, or maybe it's much readable for most rubyist?
And the second one is also about the same line.
method(:find_city)
I know it allows me to call the find_city method.
But again, why I have to put a colon before find_city?
Does this code means parse the arguments I put in to symbols?
I have to say that Learn Ruby The Hard Way gives us a really Really REALLY GOOD example of what we should NOT do. No rubyist will ever type such code in his/her projects. This piece of code is confusing, unreadable and is an abuse of metaprogramming.
Anyway, I dissect that code for you.
The confusing part starts with this line:
cities[:find] = method(:find_city)
Let's look at the right side of the =. It calls a method whose name is method, as you may guess, the return value of the method call is the method find_city, more precisely, a Method object that wraps the method find_city with its scope. Then that method is stored in the hash cities, with a symbol :find as the key. So the value of cities now become
{
'CA'=> 'San Francisco',
'MI'=> 'Detroit',
'FL'=> 'Jacksonville',
'NY' => 'New York',
'OR' => 'Portland',
:find => #<Method:main.find_city>
}
You can see that the last key-value pair is really really weird, and it shouldn't be there because the hash cities should only store states and their capitals. Heck!
Then here comes this even weirder expression cities[:find].call(cities, state). Let's see how this work.
cities[:find] simply retrieve the Method object from the hash (still remember what method it wraps?)
cities[:find].call(cities, state) invokes the method it wraps, which is find_city, in the scope that the Method object wraps, which is the top level object (a.k.a. main), Method#call returns whatever the method wrapped in returns. So this expression is just find_city(cities, state), written in an alien style.
cities[:find] = method(:find_city)
Here, cities is a hash and the method object returned by method(:find_city) is assigned to the hash key find which is a symbol.
I think it depends upon you and the context of program where you are writing this.
A simple method_var = method(:find_city) would work here as well.
method(:find_city)
I know it allows me to call the find_city method. But again, why I have to put a colon before find_city? Does this code means parse the arguments I put in to symbols?
Here, you are passing the method name as an argument, you have to either pass it as a symbol or string.
In Ruby, the method method creates a Method Object. This allow you to pass it around in your code and call it later using the .call method on your Method object.
Since calling method(my_method) would evaluate my_method and pass the result to method(...), you need a way to tell the method method which method to use. That's why you basically pass in the method name as a Symbol into the method method :D
So it actually defined a proc, and make :find and find_city sort like key and value...Probably...

how does this ruby code work for setting configuration

I see ruby code that looks like the following. It seems to be some sort of idiom for creating configuration or settings but I don't really understand it. Also, how would the Application.configure part of this code look?
MyApp::Application.configure do
config.something = foo
config.....
config....
.
config....
end
First of all, that configuration way is not specific to Ruby ; it's the applications (or libraries, gems) that choose to use it or not.
To explain you what does that code do, I'll take your snippet as an example:
MyApp::Application.configure do
config.something = foo
end
Here, you are calling MyApp::Application.configure method, with no parameter. After the call, you're giving it a block.
You can think of blocks as a piece of code that you can use however you want.
They can be written in one single line or many:
{ puts 'hello' }
{ |param| puts param } # with passing it a param
# or
do |param|
puts param
end
(remember my_array.each do ... end? It's a block you pass it. ;) )
Now, that block would be called inside the configure method thanks to yield.
yield uses (or executes) the instructions of the block that has been passed to the method.
Example: Let's define a method with a yield inside of it:
def hello
puts "Hello #{yield}"
end
If you call this method, you'd get a 'hello': no block given (yield) (LocalJumpError)'.
You need to pass it a block: hello { :Samy }.
The result would then be Hello Samy. As you can see, it simply used what was in the block passed to the method.
That's exactly what's happening in the Rails configuration code. You simply set config.something (config is a method) to some value, and that same config.something = foo is execute inside configure.
You can learn more about yield and blocks here, and on this great book.
The part from "do" until "end" is called a block, and is getting passed to the configure class method on Application. (all ruby methods can accept arguments and a block)
so the Application.configure method is creating a configuration object with a set of defaults, and then calling the block. The block is then setting the values you see, having the effect of overriding them.
It's then setting that configuration object as a class variable (like a global variable) so that other classes can use the configuration object later in the application lifecycle.
Hope that simplified description helps!

Respond_to in combination with aliassing a method in Rails

In a plugin I am writing I am using alias to override one of the default Rails validators like so:
# Alias the original validator so it's still available under a different name
alias original_validates_uniqueness_of :validates_uniqueness_of unless method_defined?(:original_validates_uniqueness_of)
# Then alias the custom validator under the original name
alias validates_uniqueness_of :custom_validates_uniqueness_of
This all works pretty well. When "validates_uniqueness_of" is defined on an attribute in a AR model, it will use my "custom_validates_uniqueness_of" method instead. Validations are running as expected.
However, when I call:
SomeARclass.respond_to?(:validates_uniqueness_of)
..it will return false. This behavior will mess with several popular plugins.
My question:
Why is respond_to returning "false"? Is this behavior the result of alliasing? How can adjust my custom validator to make it return true?
Thank you for your help.
Erwin
Your sample, on the surface, looks like it should work. I expect you've found that, inside of the class body for class SomeARclass, you actually can call validates_uniqueness_of.
class SomeARclass
validates_uniqueness_of :first_field # works
end
However, I expect that scoped_validates_uniqueness_of is a private method. That's why you have been unable to send that message to SomeARclass from outside of its class body.
SomeARclass.validates_scoped_uniqueness_of :first_field # fails
#=> NoMethodError: private method "validates_scoped_uniqueness_of" called
for SomeARclass:Class
When you alias a private method with a new name, such as aliasing the private method validates_scoped_uniqueness_of with the new name validates_uniqueness_of, then that new method is also private. I expect that's what happened here.
The documentation for #respond_to? reads:
- (Boolean) respond_to?(symbol, include_private = false)
Returns true if obj responds to the given method. Private methods are included in the search only if the optional second parameter evaluates to true.

Help with ruby and def respond_to?(method,*args, &block) in library

I have a Gem that deals with images that get modified. I want to modify it to update the old image but, I need to be able to get the object id.
Right now it uses the following code:
def respond_to?(method,*args, &block)
puts ("++++METHOD #{method.to_s} ARGS #{args} BLOCK #{block}")
args.each do |value|
puts ("ARGS #{value}")
end
but I don't know how to get id from something I pass in nor do I know how to pass the id in, I've tried
#asset.image.s_245_245(:asset_id=>#asset.id) with no success. Args returns nothing. What am I doing wrong?
Update: I am currently reading http://www.simonecarletti.com/blog/2009/09/inside-ruby-on-rails-extract_options-from-arrays/
Update: This too returned blank.
Your question is very unclear. How is the method respond_to? related to your problem with object id?
I am guessing that in reality you wanted to override method_missing, because now you do not call respond_to?, or it is not shown in your examples.
If you have not defined such method, calling image.s_245_245 will trigger method_missing (in the image object) with the parameters you used for respond_to?.
There is a rule, which says that if you use method_missing to handle some calls, then you should also modify respond_to?, and make it returning true when asked for the methods handled by method_missing.
As for object ID, there are two possibilities:
Every object in ruby responds to .object_id (which returns an internal identifier of every object)
ActiveRecord objects respond to .id (which is a primary key in the database).
This is just a side-note, because I suppose that if you start experimenting with method_missing instead of respond_to? you will know which one you want.

Resources