Reflection on method parameters in Ruby
If you set no other local variables inside the method, local_variables
will give you a list of the method's parameter names (if you do set other variables you can just call local_variables
first thing and remember the result). So you can do what you want with local_variables
+eval
:
class Automator
def fill_specific_form(first_name, last_name)
local_variables.each do |var|
puts "Setting #{var} to #{eval var.to_s}"
end
end
end
Automator.new().fill_specific_form("Mads", "Mobaek")
Be however advised that this is pure evil.
And at least for your example
puts "Setting first_name to #{first_name}"
puts "Setting last_name to #{last_name}"
seems much more sensible.
You could also do fields = {:first_name => first_name, :last_name => last_name}
at the beginning of the method and then go with your fields.each_pair
code.
How to get argument names using reflection
I suggest you take a look at Merb's action-args library.
require 'rubygems'
require 'merb'
include GetArgs
def foo(bar, zed=42)
end
method(:foo).get_args # => [[[:bar], [:zed, 42]], [:zed]]
If you don't want to depend on Merb, you can choose and pick the best parts from the source code in github.
Any ruby library to inspect what are the arguments that a certain methods take?
I don't know of any third-party libraries or even RubyGems that can do this reliably. It just isn't possible to do this kind of inspection given the limited reflection facilities Ruby provides.
You will have to make do with what is available in the core library, which is basically Method#parameters
:
def foo(a, b=nil, *c, d, &e); end
method(:foo).parameters
# => [[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]]
Is there a way to access method arguments in Ruby?
In Ruby 1.9.2 and later you can use the parameters
method on a method to get the list of parameters for that method. This will return a list of pairs indicating the name of the parameter and whether it is required.
e.g.
If you do
def foo(x, y)
end
then
method(:foo).parameters # => [[:req, :x], [:req, :y]]
You can use the special variable __method__
to get the name of the current method. So within a method the names of its parameters can be obtained via
args = method(__method__).parameters.map { |arg| arg[1].to_s }
You could then display the name and value of each parameter with
logger.error "Method failed with " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
Note: since this answer was originally written, in current versions of Ruby eval
can no longer be called with a symbol. To address this, an explicit to_s
has been added when building the list of parameter names i.e. parameters.map { |arg| arg[1].to_s }
How do i get the names or arguments inside the method definition?
You can use the values directly.
def speak(name, age, address)
puts "Hello #{name}!"
end
To access the names you could use local_variables
but I don't really recommend it.
def speak(name, age, address)
p local_variables # => [:name, :age, :address]
end
But most likely you'll want to use a hash:
def speak(hash)
# use the keys/values of hash
end
Now you can use
speak({:name => "foo", :age => 42, :address => "123"})
# or
speak(:name => "foo", :age => 42, :address => "123")
Ruby reflection composition: call original method from redefined method
In my opinion, this approach is way too complex, and an inappropriate use of Module
s.
I recommend thinking about a simpler way to implement this.
One simple way is to just include all the methods in the Phone class.
Or, you could use a hash as a lookup table for ring strategies:
class Phone
attr_accessor :ring_strategy
RING_STRATEGIES = {
ringtone: -> { ring_with_tone },
discreet: -> { ring_quietly },
screening: -> { ring_with_tone; ring_screening_too }
# ...
}
def initialize(ring_strategy = :ringtone)
@ring_strategy = ring_strategy
end
def ring
RING_STRATEGIES[:ring_strategy].()
end
end
How can I inspect what is the default value for optional parameter in ruby's method?
I think the reason such utility is not made available is that the values of the default arguments are evaluated when they have to be assigned. Therefore, trying to evaluate them might have side additional effects.
Let me tell you a story about the Russian government's nuclear plans:
Some time ago, they hired ultra hardcore Russian hackers to come up with a a solution that is both error-proof and mega secure that allows to either launch all available nukes or simply run a simulation. They decided to create one method called
launch_all_nukes
, which optionally accepts a keyword argumentsimulation_number:
. They loaded the implementation in a REPL and deleted the code so enemy spies could never find out how it actually works.
Each day for the past couple of years, the trusted specialist Ivan travels to a giga secret location where he sits in front of what looks to be a regular irb and evaluates the chances of the Russian Federation surviving a supposed mutual assured destruction.
$: launch_all_nukes simulation_number: 1
...
Just another regular day.$: launch_all_nukes simulation_number: 2
...
$: launch_all_nukes simulation_number: 3
...
Even though these take 25 minutes on average, it feels like hours sometimes.$: launch_all_nukes simulation_number: 4
...
Staring at the screen. Just another regular day. Another... regular... day...$: launch_all_nukes simulation_number: 5
...
Tik-tok, tik-tok, tik-tok... Wondering what might there be for lunch?$: launch_all_nukes simulation_number: 6
...
Finally! 7 is always the most interesting. It's the only one that sometimes shows there is a 0.03% - 0.08% chance of not complete annihilation. Ivan has no idea what stands behind the number 7. Or any of the other simulations for that matter. He just runs commands and waits. But surely, number 7 is the one that brings little beams of joy and excitement in his otherwise dull assignment.
Aaaaaaand, go!$: launch_all_nukes simulation_number: 7
...
0%. As all the others. How regular.$: launch_all_nukes simulation_number: 8
...
Does it matter, actually? Why would one nation be superior to all the others? Is human life valuable by itself to begin with? Is Earth as a whole inherently valuable? Just a tiny spectacle of rock floating in an endless Universe...$: launch_all_nukes simulation_number: 9
...
What happened? Ivan used to be a great developer. And now he just stares at a console, running repetitive commands from time to time... Is this what progress feels like...$: launch_all_nukes simulation_number: 10
...
Wait a second... What is the default value ofsimulation_number:
? What is it? Surely, the implementation has some check like__actually_launch_nukes__ if simulation_number.nil?
. But is it reallynil
? Or is it something else? ...$: launch_all_nukes simulation_number: 11
...
Like a repetitive earworm, this tiny question never left his mind... what is it? ... He never feared accidentally endangering the world because he saw that runninglaunch_all_nukes
with no arguments prompts for three different access keys, none of which he knows.$: launch_all_nukes simulation_number: 12
...
Ivan has ran ordinary Ruby commands in the console before. By all means, it's just a regular irb... Just running one simple introspection method... He knows he is not allowed to do it... But no one will know, right? No one even knows how this program works anyway... Ah...$: launch_all_nukes simulation_number: 13
...
13 and 14 are the worst! 13 usually takes an hour and a half. 14 is even longer. Damn it, Ivan craves, just an itsy bitsy tiny information to keep his mind engaged for at least a couple of minutes... Lets do it!$: method(:launch_all_nukes).default_value_for(:simulation_number)
...
Mortified, Ivan froze motionless as the sudden realization hit him. He now knows what the default value is. But it is too late...
Here is a poor man's attempt:
argument_name = 'arg2'
origin_file, definition_line = MyClass.instance_method(:index).source_location
method_signature = IO.readlines(origin_file)[definition_line.pred]
eval(method_signature.match(/#{argument_name}\s*[=:]\s*\K[^\s),]*/)[0]) # => "hello"
Obviously very error prone:
- Doesn't work with native methods
- Doesn't work with methods defined in REPLs
- You need read privileges
- The regex doesn't handle many cases (like more complex default values that have whitespaces,
)
or,
in them), but this can be improved.
If someone comes up with a purely introspective solution, go with that.
Related Topics
How to Run a Ruby File in a Rails Environment
Rspec: "Array.Should == Another_Array" But Without Concern for Order
What's the Difference Between Rspec's Subject and Let? When Should They Be Used or Not
How to Stub Applicationcontroller Method in Request Spec
How to Make Rails 3.1 Use SASS (Over SCSS) as the Default
Ruby Ternary Operator Without Else
Replace Words in a String - Ruby
Ruby Class Inheritance: What Is '<<' (Double Less Than)
Can You Get Db Username, Pw, Database Name in Rails
Ruby and Linux, Preferred Setup
Create Directory If It Doesn't Exist with Ruby
How to Implement Cookie Support in Ruby Net/Http
Vim Slow with Ruby Syntax Highlighting