Default parameters in dynamically defined methods - ruby

I am trying to dynamically define methods and pass a parameter to it. This is what I have so far:
class Commands
end
#commands = Commands.new
def route(name, &block)
Commands.send(:define_method, name, &block)
end
route 'foo' do
puts 'oh hai'
puts data
end
When route is called, this is what I expect to be executed:
data = {name: 'foo', optional: 'bar'}
#commands.send(data['name'].to_sym, data)
This fails because data isn't defined unless I do something like:
route 'foo' do |data|
puts 'oh hai'
puts data
end
Is there a way to pass a default parameter? Seems like it's sinatrarb does it, but I haven't been able to make head or tails from the code yet.

Your question isn't very clear, but I suspect that the functionality you're looking for is the sort usually achieved with instance_eval and friends. instance_eval evaluates the given block in the context of its callee, so that the callee's instance methods and instance variables are available inside the block.
Here's a really elementary example:
"foo".instance_eval do
puts size
puts upcase
end
# -> 3
# -> FOO
size and upcase aren't parameters we've given to the block, they're methods that are already available in the context of the String object.
With that in mind, you might start with something like this:
class Route
attr_reader :data
def initialize(name)
#data = { name: name, optional: "bar" }
end
end
rt = Route.new("foo")
rt.instance_eval do
puts 'oh hai'
puts data
end
# -> oh hai
# -> {:name=>"foo", :optional=>"bar"}
Here we've defined a Route class whose instances can serve as a context in which to evaluate blocks given to the route method:
def route(name, &block)
rt = Route.new(name)
Commands.send(:define_method, name) do
rt.instance_eval(&block)
end
end
route 'foo' do
puts 'oh hai'
puts data
end
route 'qux' do
puts "My name is %{name} and optional is %{optional}" % data
end
#commands = Commands.new
#commands.foo
# -> oh hai
# -> {:name=>"foo", :optional=>"bar"}
#commands.qux
# -> My name is qux and optional is bar

The block is what you are passing to route method; you are free to pass whatever you want, hence ruby won’t complain against any number of parameters (till the #commands.foo is actually called.)
One might assign a default value to block parameter:
λ = lambda do |data = 'hello'|
puts data
end
and pass this lambda to route:
route 'foo', &λ
#⇒ "hello"
AFAIK, there is no way to explicitly assign local variable in foreign binding using some kind of hack like:
block.binding.local_variable_set(:data, 'hello')

Related

Before and after event handlers in Ruby

Is there a way to watch objects so that a block or lamba is run before and/or after calls on specific methods for that object? For example, something like this:
watch(lionel, :say_you, :before) do
puts '[before say_you]'
end
lionel.say_you()
# outputs [before say_you]
An important part of my requirement is that I don't want to monkey patch the object at all. There should be no changes to the object. I just want to watch it, not change it (Heisenberg would be proud).
I've written a module that sort of does what I describe. Unfortunately, it has some bad side-effects: it slows down the system and never cleans up its hash of object ids. So I wouldn't use it in production, but it shows the concept of watching an object without monkey patching it.
# watcher module
# (Adrian is one of The Watchmen)
module Adrian
#registry = {}
EVENT_IDS = {:before => :call, :after => :return}
# watch
def self.watch(obj, method, event_id, &handler)
# get event type
event = EVENT_IDS[event_id]
event or raise 'unknown-event: unknown event param'
# get object id
obj_id = obj.object_id
# store handler
#registry[obj_id] ||= {}
#registry[obj_id][method] ||= {}
#registry[obj_id][method][event] ||= []
#registry[obj_id][method][event].push(handler)
end
# trace
TracePoint.trace(:call, :return) do |tp|
# get watched object or return
if handlers = #registry[tp.binding.receiver.object_id]
handle tp, handlers
end
end
# handle
def self.handle(tp, handlers)
# $tm.hrm
# if this method is watched
callee = handlers[tp.callee_id]
callee or return
# get blocks
blocks = callee[tp.event]
blocks or return
# loop through series
blocks.each do |block|
block.call
end
end
end
# Lionel class
class Lionel
def say_you
puts 'you'
end
def say_me
puts 'me'
end
end
# instance
lionel = Lionel.new()
# before
Adrian.watch(lionel, :say_you, :before) do
puts '[before say_you]'
end
# after
Adrian.watch(lionel, :say_me, :after) do
puts '[after say_me]'
end
# call method
lionel.say_you
lionel.say_me
That outputs:
[before say_you]
you
me
[after say_me]
Here is a solution making use of the singleton class for the object you want to watch. I am not sure it's good solution, but it might be at least interesting.
The watch method takes, and object, a method name, and an order parameter along with a block. The order parameter specifies when you want to run the block, ie before, after or around the watched method.
The watch method saves the original instance method, then defines a singleton method with the same name as the original method on the object. The singleton method calls the passed block at the time or times specified by the order parameter. The singleton method also calls with the original instance method bound to the object at the appropriate time.
Perhaps there is a better way to call an instance method from with in a singleton method, but I have not found one. I am also not sure how this would work with various argument types, ie keyword args, etc.
Here is the watch method.
def watch(obj,method_name,order,&block)
inst_method = obj.class.instance_method(method_name)
obj.define_singleton_method(method_name) do |*args|
block.call if [:before,:around].include?(order)
inst_method.bind(self).call(*args)
block.call if [:after,:around].include?(order)
end
end
Usage examples:
class Foo
def initialize(type)
#type = type
end
def i_method(arg)
puts "#{#type} instance method called with arg = #{arg}"
end
end
watched_inst = Foo.new("watched")
unwatched_inst = Foo.new("unwatched")
watch(watched_inst,:i_method,:before) do
puts "do before"
end
watched_inst.i_method("foo")
unwatched_inst.i_method("bar")
watched_inst.i_method("foo_bar")
watch(watched_inst,:i_method,:after) do
puts "do after"
end
watched_inst.i_method("hello")
watch(watched_inst,:i_method,:around) do
puts "do around"
end
watched_inst.i_method("hello")
unwatched_inst.i_method("foo")
output:
do before
watched instance method called with arg = foo
unwatched instance method called with arg = bar
do before
watched instance method called with arg = foo_bar
watched instance method called with arg = hello
do after
do around
watched instance method called with arg = hello
do around
unwatched instance method called with arg = foo

Reflection in ruby?

I am curious how this works. For example if I create a factory pattern based class where you can "register" classes for later use and then do something like
FactoryClass.register('YourClassName', [param, param, ...]);
FactoryClass.create('your_class_name').call_method_from_this_object
where 'class_name' is a key in a hash that maps to value: ClassName
is there anything like php reflection, where I can create an instance of a class based on a string name and pass in the arguments in? (in php the arguments would be an array of them that php then knows how what to do with)
So if we take a real world example:
class Foo
attr_reader :something
def initialize(input)
#something = input
end
def get_something
return #something
end
end
# In the factory class, foo is then placed in a hash: {'foo' => 'Foo'}
# This step might not be required??
FactoryClass.create('Foo', ['hello'])
# Some where in your code:
FactoryClass.create('foo').get_something # => hello
Is this possible to do in ruby? I know everything is essentially an object, but I haven't seen any API or docs on creating class instances from string names like this and also passing in objects.
As for the hash above, thinking about it now I would probably have to do something like:
{'foo' => {'class' => 'Foo', 'params' => [param, param, ...]}}
This way when you call .create on the FactoryClass it would know, ok I can instantiate Foo with the associated params.
If I am way off base, please feel free to educate me.
Check out Module#const_get (retrieving a constant from a String) and Object#send (calling a method from a String).
Here is an answer that doesn't use eval.
PHP's Reflection is called Metaprogramming in Ruby, but they are quite different. Everything in Ruby is open and could be accessed.
Consider the following code:
class Foo
attr_reader :something
def initialize(input)
#something = input
end
def get_something
return #something
end
end
#registered = { }
def register(reference_name, class_name, params=[])
#registered[reference_name] = { class_name: class_name, params: [params].flatten }
end
def create(reference_name)
h = #registered[reference_name]
Object.const_get(h[:class_name]).new(*(h[:params]))
end
register('foo', 'Foo', ['something'])
puts create('foo').get_something
You can use Object#const_get to get objects from strings. Object.const_get('Foo') will give you the object Foo.
However, you don't need to send class name as string. You can also pass around the class name as object and use that directly.
class Foo
attr_reader :something
def initialize(input)
#something = input
end
def get_something
return #something
end
end
#registered = { }
def register(reference_name, class_name, params=[])
#registered[reference_name] = { class_name: class_name, params: [params].flatten }
end
def create(reference_name)
h = #registered[reference_name]
h[:class_name].new(*(h[:params]))
end
register('foo', Foo, ['something else'])
puts create('foo').get_something
Actually one of the strong points in ruby is meta-programming. So this is really easy to do in ruby.
I am going to skip the registering part, and jump straight to the creation
A simple implementation would be this
class FactoryClass
def self.create(class_name, params)
klass = Object.const_get(class_name)
klass.new(*params)
end
end
and then you can just do:
FactoryClass.create('YourClassName', [param, param, ...]);
and this would be equivalent to calling
YourClassName.new(param, param, ...)

Method chaining in ruby

I want to build an API client that has an interface similar to rails active record. I want the consumers to be able to chain methods and after the last method is chained, the client requests a url based on the methods called. So it's method chaining with some lazy evaluation. I looked into Active Record but this is very complicated (spawning proceses, etc).
Here is a toy example of the sort of thing I am talking about. You can chain as many 'bar' methods together as you like before calling 'get', like this:
puts Foo.bar.bar.get # => 'bar,bar'
puts Foo.bar.bar.bar.get # => 'bar,bar,bar'
I have successfully implemented this, but I would rather not need to call the 'get' method. So what I want is this:
puts Foo.bar.bar # => 'bar,bar'
But my current implementation does this:
puts Foo.bar.bar #=> [:bar, :bar]
I have thought of overriding array methods like each and to_s but I am sure there is a better solution.
How would I chain the methods and know which was the last one so I could return something like the string returned in the get method?
Here is my current implementation:
#!/usr/bin/env ruby
class Bar
def get(args)
# does a request to an API and returns things but this will do for now.
args.join(',')
end
end
class Foo < Array
def self.bar
#q = new
#q << :bar
#q
end
def bar
self << :bar
self
end
def get
Bar.new.get(self)
end
end
Also see: Ruby Challenge - Method chaining and Lazy Evaluation
How it works with activerecord is that the relation is a wrapper around the array, delegating any undefined method to this internal array (called target). So what you need is to start with a BasicObject instead of Object:
class Foo < BasicObject
then you need to create internal variable, to which you will delegate all the methods:
def method_missing(*args, &block)
reload! unless loaded?
#target.send(*args, &block)
end
def reload!
# your logic to populate target, e.g:
#target = #counter
#loaded = true
end
def loaded?
!!#loaded
end
To chain methods, your methods need to return new instance of your class, e.g:
def initialize(counter=0)
#counter = counter
end
def bar
_class.new(#counter + 1)
end
private
# BasicObject does not define class method. If you want to wrap your target
# completely (like ActiveRecord does before rails 4), you want to delegate it
# to #target as well. Still you need to access the instance class to create
# new instances. That's the way (if there are any suggestion how to improve it,
# please comment!)
def _class
(class << self; self end).superclass
end
Now you can check it in action:
p Foo.new.bar.bar.bar #=> 3
(f = Foo.new) && nil # '&& nil' added to prevent execution of inspect
# object in the console , as it will force #target
# to be loaded
f.loaded? #=> false
puts f #=> 0
f.loaded? #=> true
A (very simple, maybe simplistic) option would be to implement the to_s method - as it is used to "coerce" to string (for instance in a puts), you could have your specific "this is the end of the chain" code there.

Name of the current object/class instance

I need to load a YAML file (I'm experimenting with SettingsLogic) and I'd like the instance to load the YAML with the same name as it. Briefly:
class MySettings < SettingsLogic
source "whatever_the_instance_is_called.yml"
# Do some other stuff here
end
basic_config = MySettings.new # loads & parses basic_config.yml
advanced_cfg = MySettings.new # loads & parses advanced_cfg.yml
...and so on...
The reason for this I don't yet know what configuration files I'll have to load, and typing:
my_config = MySettings.new("my_config.yml")
or
my_config = MySettings.new(:MyConfig)
just seems to be repeating myself.
I took a look around both Google and Stackoverflow, and the closest I came to an answer is either "Get Instance Name" or a discussion about how meaningless an instance name is! (I'm probably getting the query wrong, however.)
I have tried instance#class, and instance#name; I also tried instance#_id2ref(self).
What am I missing?!
Thanks in advance!
O.K., so with local variable assignment, there are snags, such as that assignment might occur slightly later than local variable symbol addition to the local variable list. But here is my module ConstMagicErsatz that I used to implement something similar to out-of-the box Ruby constant magic:
a = Class.new
a.name #=> nil - anonymous
ABC = a # constant magic at work
a.name #=> "ABC"
The advantage here is that you don't have to write ABC = Class.new( name: "ABC" ), name gets assigned 'magically'. This also works with Struct class:
Koko = Struct.new
Koko.name #=> "Koko"
but with no other classes. So here goes my ConstMagicErsatz that allows you to do
class MySettings < SettingsLogic
include ConstMagicErsatz
end
ABC = MySettings.new
ABC.name #=> "ABC"
As well as
a = MySettings.new name: "ABC"
a.name #=> "ABC"
Here it goes:
module ConstMagicErsatz
def self.included receiver
receiver.class_variable_set :##instances, Hash.new
receiver.class_variable_set :##nameless_instances, Array.new
receiver.extend ConstMagicClassMethods
end
# The receiver class will obtain #name pseudo getter method.
def name
self.class.const_magic
name_string = self.class.instances[ self ].to_s
name_string.nil? ? nil : name_string.demodulize
end
# The receiver class will obtain #name setter method
def name= ɴ
self.class.const_magic
self.class.instances[ self ] = ɴ.to_s
end
module ConstMagicClassMethods
# #new method will consume either:
# 1. any parameter named :name or :ɴ from among the named parameters,
# or,
# 2. the first parameter from among the ordered parameters,
# and invoke #new of the receiver class with the remaining arguments.
def new( *args, &block )
oo = args.extract_options!
# consume :name named argument if it was supplied
ɴς = if oo[:name] then oo.delete( :name ).to_s
elsif oo[:ɴ] then oo.delete( :ɴ ).to_s
else nil end
# but do not consume the first ordered argument
# and call #new method of the receiver class with the remaining args:
instance = super *args, oo, &block
# having obtained the instance, attach the name to it
instances.merge!( instance => ɴς )
return instance
end
# The method will search the namespace for constants to which the objects
# of the receiver class, that are so far nameless, are assigned, and name
# them by the first such constant found. The method returns the number of
# remaining nameless instances.
def const_magic
self.nameless_instances =
class_variable_get( :##instances ).select{ |key, val| val.null? }.keys
return 0 if nameless_instances.size == 0
catch :no_nameless_instances do search_namespace_and_subspaces Object end
return nameless_instances.size
end # def const_magic
# ##instances getter and setter for the target class
def instances; const_magic; class_variable_get :##instances end
def instances= val; class_variable_set :##instances, val end
# ##nameless_instances getter for the target class
def nameless_instances; class_variable_get :##nameless_instances end
def nameless_instances= val; class_variable_set :##nameless_instances, val end
private
# Checks all the constants in some module's namespace, recursivy
def search_namespace_and_subspaces( ɱodule, occupied = [] )
occupied << ɱodule.object_id # mark the module "occupied"
# Get all the constants of ɱodule namespace (in reverse - more effic.)
const_symbols = ɱodule.constants( false ).reverse
# check contents of these constant for wanted objects
const_symbols.each do |sym|
# puts "#{ɱodule}::#{sym}" # DEBUG
# get the constant contents
obj = ɱodule.const_get( sym ) rescue nil
# is it a wanted object?
if nameless_instances.map( &:object_id ).include? obj.object_id then
class_variable_get( :##instances )[ obj ] = ɱodule.name + "::#{sym}"
nameless_instances.delete obj
# and stop working in case there are no more unnamed instances
throw :no_nameless_instances if nameless_instances.empty?
end
end
# and recursively descend into the subspaces
const_symbols.each do |sym|
obj = ɱodule.const_get sym rescue nil # get the const value
search_namespace_and_subspaces( obj, occupied ) unless
occupied.include? obj.object_id if obj.kind_of? Module
end
end
end # module ConstMagicClassMethods
end # module ConstMagicErsatz
The above code implements automatic searching of whole Ruby namespace with the aim of finding which constant refers to the given instance, whenever #name method is called.
The only constraint using constants gives you, is that you have to capitalize it. Of course, what you want would be modifying the metaclass of the object after it is already born and assigned to a constant. Since, again, there is no hook, you have to finde the occasion to do this, such as when the new object is first used for its purpose. So, having
ABC = MySettings.new
and then, when the first use of your MySettings instance occurs, before doing anything else, to patch its metaclass:
class MySettings
def do_something_useful
# before doing it
instance_name = self.name
singleton_class.class_exec { source "#{instance_name}.yml" }
end
# do other useful things
end
Shouldn't you be able to do either
File.open(File.join(File.expand_path(File.dir_name(__FILE__)), foo.class), "r")
or
require foo.class
The first one need not be that complicated necessarily. But if I'm understanding you correctly, you can just use foo.class directly in a require or file load statement.
Adjust as necessary for YAML loading, but #class returns a plain old string.
Well if you have tons of variables to instantiate, I'd personally just create a Hash to hold them, it's cleaner this way. Now to instantiate all of this, you could do a loop other all your yaml files :
my_settings = {}
[:basic_config, :advanced_cfg, :some_yaml, :some_yaml2].each do |yaml_to_parse|
my_settings[yaml_to_parse] = MySettings.new(yaml_to_parse)
end
Make sure your initialize method in MySettings deals with the symbol you give it!
Then get your variables like this :
my_settings[:advanced_cfg]
Unfortunately, Ruby has no hooks for variable assignment, but this can be worked around. The strategy outline is as follows: First, you will need to get your MySettings.new method to eval code in the caller's binding. Then, you will find the list of local variable symbols in the caller's binding by calling local_variables method there. Afterwards, you will iterate over them to find which one refers to the instance returned by super call in your custom MySettings.new method. And you will pass its symbol to source method call.

What does send() do in Ruby?

Can someone please tell me what the following snippet
obj.send("#{method_name}")
is and does?
send sends a message to an object instance and its ancestors in class hierarchy until some method reacts (because its name matches the first argument).
Practically speaking, those lines are equivalent:
1.send '+', 2
1.+(2)
1 + 2
Note that send bypasses visibility checks, so that you can call private methods, too (useful for unit testing).
If there is really no variable before send, that means that the global Object is used:
send :to_s # "main"
send :class # Object
send is a Ruby method allowing to invoke another method by name passing it any arguments specified.
class Klass
def hello(*args)
"Hello " + args.join(' ')
end
end
k = Klass.new
k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
Source
One of the most useful feature I think with .send method is that it can dynamically call on method. This can save you a lot of typing. One of the most popular use of .send method is to assign attributes dynamically. For example:
class Car
attr_accessor :make, :model, :year
end
To assign attributes regularly one would need to
c = Car.new
c.make="Honda"
c.model="CRV"
c.year="2014"
Or using .send method:
c.send("make=", "Honda")
c.send("model=", "CRV")
c.send("year=","2014")
But it can all be replaced with the following:
Assuming your Rails app needs to assign attributes to your car class from user input, you can do
c = Car.new()
params.each do |key, value|
c.send("#{key}=", value)
end
Another example, similar to Antonio Jha's https://stackoverflow.com/a/26193804/1897857
is if you need to read attributes on an object.
For example, if you have an array of strings, if you try to iterate through them and call them on your object, it won't work.
atts = ['name', 'description']
#project = Project.first
atts.each do |a|
puts #project.a
end
# => NoMethodError: undefined method `a'
However, you can send the strings to the object:
atts = ['name', 'description']
#project = Project.first
atts.each do |a|
puts #project.send(a)
end
# => Vandalay Project
# => A very important project
What does send do?
send is another way of "calling a method". Example:
o = Object.new
o.to_s # => "#<Object:0x00005614d7a24fa3>"
# is equivalent to:
o.send(:to_s) # => "#<Object:0x00005614d7a24fa3>"
Send lives in the Object class.
What is the benefit of this?
The benefit of this approach is that you can pass in the method you want to call as a parameter. Here is a simple example:
def dynamically_call_a_method(method_name)
o = Object.new
o.send method_name
end
dynamically_call_a_method(:to_s) # => "#<Object:0x00005614d7a24fa3>"
You can pass in the method you want to be called. In this case we passed in :to_s. This can be very handy when doing ruby metaprogramming, because this allows us to call different methods according to our different requirements.
Send can also be used as a way of showing how everything in Ruby is an object
1.send(:+, 1) ## -> 2
3.send(:*, 2) ## -> 6
Another use case for views:
<%= link_to
send("first_part_of_path_#{some_dynamic_parameters}_end_path",
attr1, attr2), ....
%>
Allow . you to write scalable view who work with all kind of objects
with:
render 'your_view_path', object: "my_object"

Resources