Generating Functions in Ruby

Hi!

I’m looking for a construction in Ruby to generate a (big) set of
functions. I need these functions to be class methods and called by
unique names since they are called via an external API.

As an example, let me create two functions that hard codes an addition
and then uses this in a small test program.

def FivePlusFour
return 5+4
end

def OnePlusSeven
return 1+7
end

puts("Five + Four is " + FivePlusFour().to_s)
puts("One + Seven is " + OnePlusSeven().to_s)

I would like a way to generate the methods in runtime. As an old C-
programmer, I’m looking for something like this:

Define a macro

define SUM_TEMPLATE(namn,x,y) \
def namn \
return x+y \
end

Create each function with a one-liner in runtime.

SUM_TEMPLATE(FivePlusFour, 5, 4)
SUM_TEMPLATE(OnePlusSeven, 1, 7)

Test program

puts("Five + Four is " + FivePlusFour().to_s)
puts("One + Seven is " + OnePlusSeven().to_s)

Is this possible in Ruby?

(Please don’t reply how to solve this silly example in a better way,
the true use case is of cause much more complex. But conceptually it’s
the same.)

Best Regards,
Andreas L. - Sweden

What you want is define_method. Ill gives you some links when I’m not on
my
phone.

On Wed, May 18, 2011 at 3:46 PM, Andreas L.
[email protected] wrote:

return 5+4
I would like a way to generate the methods in runtime. As an old C-
SUM_TEMPLATE(OnePlusSeven, 1, 7)

Test program

puts("Five + Four is " + FivePlusFour().to_s)
puts("One + Seven is " + OnePlusSeven().to_s)

Is this possible in Ruby?

def SUM_TEMPLATE(name, x, y)
class <<self;self;end.class_eval “def #{name}; #{x} + #{y}; end”
end

(Please don’t reply how to solve this silly example in a better way,
the true use case is of cause much more complex. But conceptually it’s
the same.)

Is it really? Do you always have one name and two values as input?
Can you give a bit more contextual information?

Cheers

robert

Robert K. wrote in post #999483:

On Wed, May 18, 2011 at 3:46 PM, Andreas L.
[email protected] wrote:

return 5+4
I would like a way to generate the methods in runtime. As an old C-
SUM_TEMPLATE(OnePlusSeven, 1, 7)

Test program

puts("Five + Four is " + FivePlusFour().to_s)
puts("One + Seven is " + OnePlusSeven().to_s)

Is this possible in Ruby?

def SUM_TEMPLATE(name, x, y)
class <<self;self;end.class_eval “def #{name}; #{x} + #{y}; end”
end

I need these functions to be class methods

…of what class?

Create each function with a one-liner in runtime.

Perhaps something like this:

def SUM_TEMPLATE(the_class, meth_name, x, y)
class_obj = Object.const_get(the_class)

class_obj.singleton_class.class_eval do
define_method(meth_name) do
x + y
end
end
end

class Dog
end

SUM_TEMPLATE(‘Dog’, ‘FivePlusFour’, 5, 4)
puts Dog.FivePlusFour

–output:–
9

On 19.05.2011 03:55, 7stud – wrote:

Robert K. wrote in post #999483:

def SUM_TEMPLATE(name, x, y)
class<<self;self;end.class_eval “def #{name}; #{x} + #{y}; end”
end

I’m curious how you expected the op to use that method?

I am not sure I get your point. The method was intended to be used as a
top level method similarly to how he formulated his C macro sample.

$ ruby19 <<CODE

def SUM_TEMPLATE(name, x, y)
class <<self;self;end.class_eval “def #{name}; #{x} + #{y}; end”
end
SUM_TEMPLATE(“f”,1,2)
p f
CODE
3

But, frankly, this use case seems to be so far away from his real use
case that this approach is likely not what is needed. Since I have no
further information I stick with the example and leave speculation about
the nature of the real issue aside.

Kind regards

robert

Robert K. wrote in post #999483:

def SUM_TEMPLATE(name, x, y)
class <<self;self;end.class_eval “def #{name}; #{x} + #{y}; end”
end

I’m curious how you expected the op to call that method? By the way,
this gobbledygook:

class <<self;self;end.class_eval “def #{name}; #{x} + #{y}; end”

is equivalent to:

instance_eval(“def #{name}; #{x} + #{y}; end”)

On 18 May 2011 15:46, Andreas L. [email protected]
wrote:

I’m looking for a construction in Ruby to generate a (big) set of
functions. I need these functions to be class methods and called by
unique names since they are called via an external API.

another way could be the method_missing method.
In my gem ‘weekday’ you can call methods like first_monday(yyyy,mm) ,
third_tuesday(…), last_sunday(…) and any other combination of a
number
and weekday.

-Thomas

Have a look at how ActionView (from Rails) caches its compiled
templates. It does this by defining methods on a module.

[actionpack/lib/action_view/template.rb]

  def compile(view, mod) #:nodoc:


code = arity.abs == 1 ? @handler.call(self) :
@handler.call(self, view)

    source = <<-end_src
      def #{method_name}(local_assigns)
        _old_output_buffer = @output_buffer;#{locals_code};#{code}
      ensure
        @output_buffer = _old_output_buffer
      end
    end_src


begin
mod.module_eval(source, identifier, 0)
ObjectSpace.define_finalizer(self, Finalizer[method_name,
mod])
rescue Exception => e # errors from template code
if logger = (view && view.logger)
logger.debug “ERROR: compiling #{method_name} RAISED #{e}”
logger.debug “Function body: #{source}”
logger.debug “Backtrace: #{e.backtrace.join(”\n")}"
end

On Thu, May 19, 2011 at 8:00 PM, 7stud – [email protected]
wrote:

p f
CODE
3

Oh, okay. But that isn’t a class method.

That’s true. But OP mentioned class methods in just one sentence but
all his examples (Ruby and C) were not using class methods. Besides
it should not be too difficult to modify the code to generate class
methods. I still would like to see more realistic requirements as all
speculations about proper ways to solve the problem are moot as long
as we do not know the problem.

Kind regards

robert

Robert K. wrote in post #999582:

I am not sure I get your point. The method was intended to be used as a
top level method similarly to how he formulated his C macro sample.

$ ruby19 <<CODE

def SUM_TEMPLATE(name, x, y)
class <<self;self;end.class_eval “def #{name}; #{x} + #{y}; end”
end
SUM_TEMPLATE(“f”,1,2)
p f
CODE
3

Oh, okay. I was wondering how you wanted the op to wrangle a class
method out of that.

  1. Strings are mutable in ruby, so get rid of all those +'s. Use #{}
    and << instead.

  2. Don’t use back slashes in strings, e.g. for paths–unless you
    specifically intend to use an escape character, which should probably
    never occur in a path.

My final dilemma is how to get the value of STUB_CALLS into
Templates.rb by defining it in my test program Impl.rb.

Why can’t your methods in Templates.rb have a ‘debug’ parameter with a
default value? Then for debugging, you can ‘override’ the default.

  1. Don’t use constants for method names, and don’t capitalize method
    names.

Hi, and thank you all!

Yes, the example is far from reallity in one sense, but your example
really did the trick and conceptually it’s the same!

So, for the interessted reader, I will provide my code here. (I have a
similar method for setting values.) Via an Ole32 server i communicate
with a sw that can extract all types of data from a odd type of
database.

(My final dilemma is how to get the value of STUB_CALLS into
Templates.rb by defining it in my test program Impl.rb. To define it
direcrly in the template file in not very nice… And of cause I don’t
want to have to define it at all when running in non-test-mode.)

Templates.rb
STUB_CALLS = 1; # If this is ‘1’, all instruments are stubed for
debugging purpouse

def GET_VALUE(f_name, method, return_format, no_of_params)

Make a parameter list and an argument list

if 0<no_of_params
arg_list = ‘x1’;
params = ‘x1.to_s’;
for i in 2…no_of_params do
arg_list = arg_list + ‘, x’ + i.to_s;
params = params + ’ + ‘, ’ + x’ + i.to_s + ‘.to_s’;
end
params = params+’;’;
else
arg_list = ‘’;
params = 0;
end

if (0==STUB_CALLS)
class <<self;self;end.class_eval “def #{f_name}(#{arg_list});
handle = OA.instance.getWIN32OLEHandle();
params = #{params};
handle.call([‘MethodNameIn’,‘Params’],[#{method},params]);
ret_val = handle.GetControlValue(#{return_format});
error = handle.GetControlValue(‘error out’);
if OA.instance.checkForError(error);
return ‘NaN’;
else;
return ret_val;
end;
end”
else
class <<self;self;end.class_eval "def #{f_name}(#{arg_list});
params = #{params};
puts(‘Calling #{method}(#{arg_list}) with parameters <’ + params

  • ‘>’);
    end"
    end
    end

Templates.rb <- this is just three out of many
methods…
require ‘.\Difinitions.rb’
#***ParamsList [“database”]
GET_VALUE(‘customer_getFrequency’, ‘getcustomerfreq’, ‘ResultDbl’, 1)

#***ParamsList [“database”]
GET_VALUE(‘customer_getSpend_thr’, ‘getcustspend’, ‘ResultDbl’, 1)

#***ParamsList [“database”, “start”, “stop”, “inc”]
GET_VALUE(‘shop_GetFundReq’, ‘getfundreq’, ‘ResultDbl’, 4)

Impl.rb <- The implementation does already exist, and it’s
huge. This is just a few lines from my test program:
require ‘.\Difinitions.rb’
f = customer_getFrequency(cdb)
s = customer_getSpend_thr(cdb)
fr = shop_GetFundReq(sdb, 20110101, 20110201, ‘all’)

  1. If you will have less than 9 parameters, you can do this:

param_str = temp = ‘x1’

10.times do |i|
temp = temp.succ
param_str << ', ’ << temp
end

p param_str

–output:–
“x1, x2, x3, x4, x5, x6, x7, x8, x9, y0, y1”

But in ruby, you can define variadic methods:

def my_meth(*args)
args.each {|arg| p arg}
end

my_meth(1, 2, 3)
my_meth(‘a’, ‘b’, 4)

–output:–
1
2
3
“a”
“b”
4

  1. ruby programmers omit semi-colons to end statements.

  2. This is what your params string looks like:

params = ‘x1.to_s’;
i = 2
params = params + ’ + ‘, ’ + x’ + i.to_s + ‘.to_s’;

p params

–output:–
“x1.to_s + ', ’ + x2.to_s”

That looks like gobbledygook to me.

Andreas L. wrote in post #1000369:

  1. Most ruby programmers don’t use for-in because that construct just
    calls each(), so ruby programmers call each() directly:

2…no_of_params.each do |i|
arg_list = arg_list + ‘, x’ + i.to_s;
params = params + ’ + ‘, ’ + x’ + i.to_s + ‘.to_s’;
end

  1. Ruby has many ways to quote strings: ‘’, “”, %q{}, %Q{}, heredoc, so
    escaping internal quotes is rarely, if ever, necessary. In other words,
    don’t ever use a string that has interior quotes escaped.

params = ‘hello’
params = #{params};

–output:–
prog.rb:3: syntax error, unexpected $end

  1. You need to buy a beginning ruby book and read it.

Hi!

This is why I did not put the real example at first, because then some
people that do not understand the code always brings these stupid
comments. I do not have any compilation errors, and the code works
perfectly, so I don’t think that I need a beginning ruby book for that
reason. :slight_smile: (First params does not contain a simple string; please
note the escaped string characters in the code that generates params.
Kind of strings within strings).

I cannot change the function signature since I cannot change the upper
layer that makes calls to my Ruby glue layer… That is why I would
like to introduce debugging by making changes in the glue layer at
“compile” time. (In C I would have used a #define for the pre
processor.)

“1) Strings are mutable in ruby, so get rid of all those +'s.” - I’m
not sure that I understand this but it sounds interesting, what does
it mean?

Thanks for input about creating loops, new language = new flavours,
and that is nice to adapt to! :slight_smile:

I think that I could make use of the variable parameter input, with
the extended error control code making sure all parameters are in
place, I think that this will be the same amount of code. But I
learned something new, so thanks!

BR,
Andreas

My bad to use the name “param” for two different content, but any
ways:

First params typically assembles to this:
x1.to_s + ', ’ + x2.to_s + ', ’ + x3.to_s + ', ’ + x4.to_s
Now I have created the code, not the string.

The line
params_2 = #{params};
extends to:
params_2 = x1.to_s + ', ’ + x2.to_s + ', ’ + x3.to_s + ', ’ + x4.to_s
and assembles my input so that
params_2 == ‘myDB, 20110101, 20110201, all’

Output from the function when STUB_CALLS=1:
Calling shop_GetFundReq(x1, x2, x3, x4) with parameters <myDB,
20110101, 20110201, all>

BR,
Andreas

Andreas L. wrote in post #1000537:

The line
params_2 = #{params};
extends to:
params_2 = x1.to_s + ', ’ + x2.to_s + ', ’ + x3.to_s + ', ’ + x4.to_s

Or you could do it like this:

params_2 = “#{x1}, #{x2}, #{x3}, #{x4}”

#{} interpolates the value of an expression into a string and calls to_s
automatically.

A slightly more DRY version:

params_2 = [x1, x2, x3, x4].join(", ")

Again, to_s is called automatically in this case.

Finally, if you used the *args syntax to collect a variable number of
args into an array, then it would become

params_2 = args.join(", ")

or even just: handle.call([‘MethodNameIn’,‘Params’],[method]+args)

Output from the function when STUB_CALLS=1:

Tiny suggestion: STUB_CALLS = false / true would be clearer, because
then you could say

do_something if STUB_CALLS

rather than

do_something if STUB_CALLS != 0

A global variable ($stub_calls) would arguably be preferable, since you
can change it at runtime without generating a warning. And better again
would be an instance variable of a class or module, because then it
lives in its own namespace.

module MyStuff
@stub_calls = false # set the default
def self.stub_calls
@stub_calls
end
def self.stub_calls=(x)
@stub_calls = x
end
end

puts MyStuff.stub_calls
MyStuff.stub_calls = true
puts MyStuff.stub_calls

Another thing to look at is using heredoc, as it means you don’t have to
worry about quoting double-quotes:

class <<self;self;end.class_eval <<EOS
  def #{f_name}(#{arg_list})
    puts "Hello world"
  end
EOS

Anyway, these are just tiny style suggestions. Having working code is
worth far more :slight_smile:

On Tue, May 24, 2011 at 9:25 AM, Andreas L.
[email protected] wrote:

My bad to use the name “param” for two different content, but any
ways:

First params typically assembles to this:
x1.to_s + ', ’ + x2.to_s + ', ’ + x3.to_s + ', ’ + x4.to_s

That does not look good. If you have to encode numbers in variable
names, you probably rather want to use an Array for that or even
generate that directly:

params = no_of_params.times.map {|i| “x#{i}”}.join(’, ')

Now I have created the code, not the string.

The line
params_2 = #{params};
extends to:
params_2 = x1.to_s + ', ’ + x2.to_s + ', ’ + x3.to_s + ', ’ + x4.to_s
and assembles my input so that
params_2 == ‘myDB, 20110101, 20110201, all’

When looking at your posting from 2:55 you don’t really need “params =
…” in the generated code.

Output from the function when STUB_CALLS=1:
Calling shop_GetFundReq(x1, x2, x3, x4) with parameters <myDB,
20110101, 20110201, all>

SUB_CALLS should be a boolean and treated as such.

I can see why 7stud wrote his recommendation: the code looks overly
complicated. Also, there are some issues, for example:

 handle.call(['MethodNameIn','Params'],[#{method},params]);

This only works if you call that method with something like

(…, ““myMethod””, …)

where you should really passing the name plainly as string. Makes
usage much simpler and more robust. So in the method body you then
had

 handle.call(['MethodNameIn','Params'],['#{method}', params]);

Same reasoning applies in other locations such as return_format.

And if you get rid of the no_of_params you can simply use *args and do
not have to go through the hassle generating argument lists. If may
be though that you want to be able to query arity from the method and
have it fail fast if the argument list does not match in which case
it’s OK to generate the argument list.

All in all it seems this is simpler and more redundancy free:

def GET_VALUE(f_name, method, return_format, no_of_params)
args = no_of_params.times.map {|i| “x#{i}”}.join(", ")

body = if STUB_CALLS
%Q{printf(“Calling #{method}(#{args}) with parameters <%p>\n”,
[#{args}])}
else
%Q{
oai = OA.instance
handle = oai.getWIN32OLEHandle()
handle.call([‘MethodNameIn’,‘Params’],[’#{method}’, [#{args}]]);
ret_val = handle.GetControlValue(#{return_format});
error = handle.GetControlValue(‘error out’);
return oai.checkForError(error) ? ‘NaN’: ret_val
}
end

code = “def #{f_name}(#{args})\n#{body}\nend”
puts code if ENV[‘DEBUG’] == ‘true’
class <<self;self;end.class_eval code
end

Cheers

robert

On Tue, May 24, 2011 at 3:50 PM, Andreas L.
[email protected] wrote:

params = x1, x2, x3, x4
But with that I get an error (eval):3:in `+’: can’t convert Fixnum
into String (TypeError)

Without the code you are executing that error is pretty useless.

It works for me:

irb(main):023:0> STUB_CALLS=false
(irb):23: warning: already initialized constant STUB_CALLS
=> false
irb(main):024:0> GET_VALUE “f”, “m”, “r”, 4
def f(x0, x1, x2, x3)

 oai = OA.instance
 handle = oai.getWIN32OLEHandle()
 handle.call(['MethodNameIn','Params'],['m', [x0, x1, x2, x3]]);
 ret_val = handle.GetControlValue(r);
 error = handle.GetControlValue('error out');
 return oai.checkForError(error) ? 'NaN': ret_val

end
=> nil
irb(main):025:0> STUB_CALLS=true
(irb):25: warning: already initialized constant STUB_CALLS
=> true
irb(main):026:0> GET_VALUE “f”, “m”, “r”, 4
def f(x0, x1, x2, x3)
printf(“Calling m(x0, x1, x2, x3) with parameters <%p>\n”, [x0, x1, x2,
x3])
end
=> nil
irb(main):027:0>

(with my definition of GET_VALUE from earlier)

params_2 = “#{x1}, #{x2}, #{x3}, #{x4}”
Yes, I could use this, but still I need to assembly this line since I
dont know the number of input parameters at coding time. And I know
that no input parameters contain any expressions that needs to be
evaluated since it is an input from my generated function.

I find the solution with printf “%p” and the Array more elegant.

Cheers

robert

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs