Ruby DSL define_method with arguments - ruby

This is what I'm looking to do.
# DSL Commands
command :foo, :name, :age
command :bar, :name
# Defines methods
def foo(name, age)
# Do something
end
def bar(name)
# Do something
end
Basically, I need a way to handle arguments through define_method, but I want a defined number of arguments instead of an arg array (i.e. *args)
This is what I have so far
def command(method, *args)
define_method(method) do |*args|
# Do something
end
end
# Which would produce
def foo(*args)
# Do something
end
def bar(*args)
# Do something
end
Thoughts?

I think the best workaround for this would be do to something like the following:
def command(method, *names)
count = names.length
define_method(method) do |*args|
raise ArgumentError.new(
"wrong number of arguments (#{args.length} for #{count})"
) unless args.length == count
# Do something
end
end

It's a little weird, but you can use some type of eval. instance_eval, module_eval or class_eval could be used for that purpose, depending on context. Something like that:
def command(method, *args)
instance_eval <<-EOS, __FILE__, __LINE__ + 1
def #{method}(#{args.join(', ')})
# method body
end
EOS
end
This way you'll get exact number of arguments for each method. And yes, it may be a bit weirder than 'a little'.

Related

How define a method generation receiving variadic arguments

In a ruby class definition, I want to define a method generator. Say us that every method that I want to generate differs in a symbol and the number of parameters. So, I have something like this:
def self.my_define_service1(name) # generate for 1 parameter
define_method(name) do |p1|
# basic stuff
call(name.intern, p1)
end
end
def self.my_define_service2(name) # generate for 1 parameter
define_method(name) do |p1, p2|
# basic stuff
call(name.intern, p1,p2)
end
end
# and so on ... many definitions as the number of parameters I have
call is a method kind of dispatcher which receives a symbol name and the parameters.
My question is: is it possible to write only a generator? Something like that
def self.my_define_service(name, *args)
define_method(name) do |args| # <-- how to do here?
# basic stuff
call(name.intern, *args)
end
end
and if the answer is affirmative, how?
try with:
def self.my_define_service(name, *args)
define_method(name) do |*args|
# basic stuff
call(name.intern, *args)
end
end
It was a lot easier with eval:
def self.my_define_service(name, *args)
pars_list = args.join ', '
d = %{
def #{name}(#{pars_list})
call(':#{name}', #{pars_list})
end
}
eval d
end

How can I automatically forward all paramters from one method to another in Ruby?

If I have methods:
def method_a(p1, p2)
# do some stuff
method_b(p1, p2)
end
def method_b(p1, p2)
# do other stuff
end
Is there a way to call method_b and automatically pass all parameters to it? (Sort like how you can call super and it automatically forwards all params)
I know one appriximate method:
def method_a *args
# do some stuff
method_b *args
end
def method_b *args
# do other stuff
end
or expanding arguments in the second method:
def method_a *args
# do some stuff
method_b *args
end
def method_b p1, p2
# do other stuff
end
Since super is key-work method, the ruby interperter can treat it as of the same argument list as in the specific method you've called. But default from to call a method without argument is the same as for super method, just method name:
method_a # calls to :method_a without arguments, not as the same argument list for the caller method.
So, it will be strong omonim for the call method syntax.
Considering arbitrary number of arguments and a possibility of a block, the most general format is:
def method_a(*args, &pr)
# do some stuff
method_b(*args, &pr)
end
Then, in the definition of method_b, you can set a specific number of arguments and whether or not it takes a block.
Use *args like this:
def method_a(*args)
...
method_b(*args)
end
def method_b(p1, p2)
...
end
You can process the arguments like an array in the method_a.
Wow, this is hacky. But it works
def fwd_call b, meth
send(meth, *b.eval('method(__method__).parameters.map { |p| eval(p.last.to_s) }'))
end
def method1 x, y
fwd_call(binding, :method2)
end
def method2 x, y
x+y
end
puts method1(3, 4)
# 7

Can I enforce arity on a block passed to a method?

Is there any way to "turn on" the strict arity enforcement of a Proc instantiated using Proc.new or Kernel.proc, so that it behaves like a Proc instantiated with lambda?
My initialize method takes a block &action and assigns it to an instance variable. I want action to strictly enforce arity, so when I apply arguments to it later on, it raises an ArgumentError that I can rescue and raise a more meaningful exception. Basically:
class Command
attr_reader :name, :action
def initialize(name, &action)
#name = name
#action = action
end
def perform(*args)
begin
action.call(*args)
rescue ArgumentError
raise(WrongArity.new(args.size))
end
end
end
class WrongArity < StandardError; end
Unfortunately, action does not enforce arity by default:
c = Command.new('second_argument') { |_, y| y }
c.perform(1) # => nil
action.to_proc doesn't work, nor does lambda(&action).
Any other ideas? Or better approaches to the problem?
Thanks!
Your #action will be a Proc instance and Procs have an arity method so you can check how many arguments the block is supposed to have:
def perform(*args)
if args.size != #action.arity
raise WrongArity.new(args.size)
end
#action.call(*args)
end
That should take care of splatless blocks like { |a| ... } and { |a,b| ... } but things are a little more complicated with splats. If you have a block like { |*a| ... } then #action.arity will be -1 and { |a,*b| ... } will give you an arity of -2. A block with arity -1 can take any number of arguments (including none), a block with arity -2 needs at least one argument but can take more than that, and so on. A simple modification of splatless test should take care of the splatful blocks:
def perform(*args)
if #action.arity >= 0 && args.size != #action.arity
raise WrongArity.new(args.size)
elsif #action.arity < 0 && args.size < -(#action.arity + 1)
raise WrongArity.new(args.size)
end
#action.call(*args)
end
According to this answer, the only way to convert a proc to a lambda is using define_method and friends. From the docs:
define_method always defines a method without the tricks [i.e. a lambda-style Proc], even if a non-lambda Proc object is given. This is the only exception for which the tricks are not preserved.
Specifically, as well as actually defining a method, define_method(:method_name, &block) returns a lambda. In order to use this without defining a bunch of methods on some poor object unnecessarily, you could use define_singleton_method on a temporary object.
So you could do something like this:
def initialize(name, &action)
#name = name
#action = to_lambda(&action)
end
def perform(*args)
action.call(*args)
# Could rescue ArgumentError and re-raise a WrongArity, but why bother?
# The default is "ArgumentError: wrong number of arguments (0 for 1)",
# doesn't that say it all?
end
private
def to_lambda(&proc)
Object.new.define_singleton_method(:_, &proc)
end
Your solution:
class Command
attr_reader :name, :action
def initialize(name) # The block argument is removed
#name = name
#action = lambda # We replace `action` with just `lambda`
end
def perform(*args)
begin
action.call(*args)
rescue ArgumentError
raise(WrongArity.new(args.size))
end
end
end
class WrongArity < StandardError; end
Some references:
"If Proc.new is called from inside a method without any arguments of its own, it will return a new Proc containing the block given to its surrounding method."
-- http://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block.html
It turns out that lambda works in the same manner.

Executing code for every method call in a Ruby module

I'm writing a module in Ruby 1.9.2 that defines several methods. When any of these methods is called, I want each of them to execute a certain statement first.
module MyModule
def go_forth
a re-used statement
# code particular to this method follows ...
end
def and_multiply
a re-used statement
# then something completely different ...
end
end
But I want to avoid putting that a re-used statement code explicitly in every single method. Is there a way to do so?
(If it matters, a re-used statement will have each method, when called, print its own name. It will do so via some variant of puts __method__.)
Like this:
module M
def self.before(*names)
names.each do |name|
m = instance_method(name)
define_method(name) do |*args, &block|
yield
m.bind(self).(*args, &block)
end
end
end
end
module M
def hello
puts "yo"
end
def bye
puts "bum"
end
before(*instance_methods) { puts "start" }
end
class C
include M
end
C.new.bye #=> "start" "bum"
C.new.hello #=> "start" "yo"
This is exactly what aspector is created for.
With aspector you don't need to write the boilerplate metaprogramming code. You can even go one step further to extract the common logic into a separate aspect class and test it independently.
require 'aspector'
module MyModule
aspector do
before :go_forth, :add_multiply do
...
end
end
def go_forth
# code particular to this method follows ...
end
def and_multiply
# then something completely different ...
end
end
You can implement it with method_missing through proxy Module, like this:
module MyModule
module MyRealModule
def self.go_forth
puts "it works!"
# code particular to this method follows ...
end
def self.and_multiply
puts "it works!"
# then something completely different ...
end
end
def self.method_missing(m, *args, &block)
reused_statement
if MyModule::MyRealModule.methods.include?( m.to_s )
MyModule::MyRealModule.send(m)
else
super
end
end
def self.reused_statement
puts "reused statement"
end
end
MyModule.go_forth
#=> it works!
MyModule.stop_forth
#=> NoMethodError...
You can do this by metaprogramming technique, here's an example:
module YourModule
def included(mod)
def mod.method_added(name)
return if #added
#added = true
original_method = "original #{name}"
alias_method original_method, name
define_method(name) do |*args|
reused_statement
result = send original_method, *args
puts "The method #{name} called!"
result
end
#added = false
end
end
def reused_statement
end
end
module MyModule
include YourModule
def go_forth
end
def and_multiply
end
end
works only in ruby 1.9 and higher
UPDATE: and also can't use block, i.e. no yield in instance methods
I dunno, why I was downvoted - but a proper AOP framework is better than meta-programming hackery. And thats what OP was trying to achieve.
http://debasishg.blogspot.com/2006/06/does-ruby-need-aop.html
Another Solution could be:
module Aop
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def before_filter(method_name, options = {})
aop_methods = Array(options[:only]).compact
return if aop_methods.empty?
aop_methods.each do |m|
alias_method "#{m}_old", m
class_eval <<-RUBY,__FILE__,__LINE__ + 1
def #{m}
#{method_name}
#{m}_old
end
RUBY
end
end
end
end
module Bar
def hello
puts "Running hello world"
end
end
class Foo
include Bar
def find_hello
puts "Running find hello"
end
include Aop
before_filter :find_hello, :only => :hello
end
a = Foo.new()
a.hello()
It is possible with meta-programming.
Another alternative is Aquarium. Aquarium is a framework that implements Aspect-Oriented Programming (AOP) for Ruby. AOP allow you to implement functionality across normal object and method boundaries. Your use case, applying a pre-action on every method, is a basic task of AOP.

Ruby class_eval method

I'm trying to figure out how to dynamically create methods
class MyClass
def initialize(dynamic_methods)
#arr = Array.new(dynamic_methods)
#arr.each { |m|
self.class.class_eval do
def m(*value)
puts value
end
end
}
end
end
tmp = MyClass.new ['method1', 'method2', 'method3']
Unfortunately this only creates the method m but I need to create methods based on the value of m, ideas?
There are two accepted ways:
Use define_method:
#arr.each do |method|
self.class.class_eval do
define_method method do |*arguments|
puts arguments
end
end
end
Use class_eval with a string argument:
#arr.each do |method|
self.class.class_eval <<-EVAL
def #{method}(*arguments)
puts arguments
end
EVAL
end
The first option converts a closure to a method, the second option evaluates a string (heredoc) and uses regular method binding. The second option has a very slight performance advantage when invoking the methods. The first option is (arguably) a little more readable.
define_method(m) do |*values|
puts value
end

Resources