Remove eval from method using dynamic variables in Ruby - ruby-on-rails

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.

Related

Why isn't the args parameter used in ActionController::Instrumentation::render?

I am new to Ruby and to Rails, and am trying to understand fully what I'm reading.
I am looking at some of the Rails source code, in this case action_controller/metal/instrumentation.rb.
def render(*args)
render_output = nil
self.view_runtime = cleanup_view_runtime do
Benchmark.ms { render_output = super }
end
render_output
end
I understand that *args is using the splat operator to collect the arguments together into an array. But after that, it stops making much sense to me.
I can't fathom why render_output is set to nil before being reassigned to equal super and then called with no arguments. I gather that some speedtest is being done, but coming from other languages I'd expect this to just be something more like Benchmark.ms(render_output) or perhaps Benchmark.start followed by render_output followed by Benchmark.end. I'm having a hard time following the way it works here.
But more importantly, I don't really follow why args isn't used again. Why bother defining a param that isn't used? And I mean, clearly it is getting used-- I just don't see how. There's some hidden mechanism here that I haven't learned about yet.
In this context, it is important to note how super works, because in some cases it passes implicitly arguments and you might not expect that.
When you have method like
def method(argument)
super
end
then super is calling the overridden implementation of method implicitly with the exact same arguments as the current method was called. That means in this example super will actually call super(argument).
Of course, you can still define a method call that explicitly sends other arguments to the original implementation, like in this example:
def method(argument)
super(argument + 1)
end
Another important edge-case is when you want to explicitly call super without any arguments although the current method was called with arguments then you need to be very explicit like this
def method(argument)
super() # note the empty parentheses
end
Let me try to describe you what I think this code does.
*args*
is using the splat operator to collect the arguments together into an array
that is totally correct, however they don't use it, and if you will go to master branch, they just changed it to *. Asking why it is defined and not used, I think that's question about bad design. They should have called it _args or at least like it is now just single splat *.
render_output is set to nil because of scopes, it has to be explicitly defined out block, lambda, proc in order to store value in it, otherwise its visibility will be locked only to those lambda, proc, block execution. Refer to this article
Benchmark.start. Blocks are great ruby construction. You are totally correct that speedtest is done, we can see it is just decorator for benchmark library.
source.
You are wondering why we cannot just pass it as Benchmark.ms(render_output), that's because what will be given to benchmark ms function? It will be given result, like <div> my html </div. And how we can measure this string result - no how. That's why we calling super right in this block, we want to access parent class function and wrap it inside block, so we are not calling it, we just construct it, and it will be called inside benchmark lib, and measured execution like
class Benchmark
...
def realtime # :yield:
r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0
end
...
end
So here we can count realtime of function execution, this is the code from original library

How to chain try() and scoped to_s() in Rails?

In a Rails view, one can use try to output only if there is a value in the database, e.g
#model.try(:date)
And one can chain trys if, for example, the output is needed as a string
#model.try(:date).try(:to_s)
But what if I need to call a scoped format? I've tried
#model.try(:date).try(:to_s(:long))
#model.try(:date).try(:to_s).try(:long)
What is the correct syntax for this? And what is a good reference for more explanation?
Thanks
From the fine manual:
try(*a, &b)
[...]
try also accepts arguments and/or a block, for the method it is trying
Person.try(:find, 1)
So I think you want:
#model.try(:date).try(:to_s, :long)
This one won't work:
#model.try(:date).try(:to_s(:long))
because you're trying to access the :to_s symbol as a method (:to_s(:long)). This one won't work:
#model.try(:date).try(:to_s).try(:long)
because you're trying to call the long method on what to_s returns and you probably don't have a String#long method defined.
mu is too short's answer shows the correct usage for the try method with parameters:
#model.try(:date).try(:to_s, :long)
However, if you are using Ruby 2.3 or later, you should stop using try and give the safe navigation operator a try (no pun intended):
#model&.date&.to_s(:long)
The following answer is here for historical purposes – adding a rescue nil to the end of statements is considered bad practice, since it suppresses all exceptions:
For long chains that can fail, I'd rather use:
#model.date.to_s(:long) rescue nil
Instead of filling up my view with try(...) calls.
Also, try to use I18n.localize for date formatting, like this:
l #model.date, format: :long rescue nil
See:
http://rails-bestpractices.com/posts/42-use-i18n-localize-for-date-time-formating
In case you often use try chains without blocks, an option is to extend the Object class:
class Object
def try_chain(*args)
args.inject(self) do |result, method|
result.try(method)
end
end
end
And then simply use #model.try_chain(:date, :to_s)

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.

Is there any built-in way to automatically enforce a type/class on an instance variable in Ruby?

I'm working with Ruby and Rails, so any Rails extension of Ruby should be fine too.
I'm wondering if there's a way to effectively force a type on instance variables (rather, their setters and getters) that's easier than manually defining them.
The class method attr_accessor and the like don't enforce a type. I noticed for instance that Rails' ActiveRecord::Base class does automatic casting on setters. It knows from the database that a particular active record variable is an integer, and setting it with #record.integer_variable = '1' automatically casts the argument and allows further access through #record.integer_variable # => 1.
Is there a way to tap into this?
I could write my own getters and setters as class methods, but if smarter folk have already done the heavy lifting, I'd rather not have to trust myself.
I don't know if there's already something about it, but you can solve this problem with just a few lines of meta-programming:
module EnforceTypes
def attr_accessor_of_type(name, type)
send :define_method, name do
instance_variable_get("##{name}")
end
send :define_method, "#{name}=" do |v|
raise ArgumentException unless v.is_a? type
instance_variable_set("##{name}", v)
end
end
end
Usage:
class MyClass
extend EnforceTypes
attr_accessor_of_type :some_string, String
end
Of course you can make it a little smart by changing the 2nd emitted method, performing some conversions, etc.
Here's a nice reference: http://www.raulparolari.com/Ruby2/attr_accessor
And remember, almost anything that you can do by manually copy-and-pasting lots of code, can be solved with meta-programming.

Variables in ruby method names

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)

Resources