Implementing DRY with a function call

Hi,

I’m working on my first Ruby project…and I’m trying to adhere to the
DRY principle. What I have is a lot, and I mean a LOT, of repetition.
The calls all look similar to this (by the way I’m writing an interface
to a library with FFI):

module MyLibrary
extend FFI::Library
ffi_lib [MyLibrary]

attach_function :LibFunc, [ :string, :pointer ], :void
attach_function :CheckError, [], :int

class SubClass
def lib_func(name)
ret = FFI::MemoryPointer.new(:char,256)
MyLibrary.LibFunc(name,ret)
if MyLibrary.CheckError != 0
raise “You got an error”
end
end
end
end

That’s the basic idea, but I have several classes within the module,
each with a dozen, or more, functions. What I am doing now is, within
the module:

def call_check_error(function)
ret = lambda {function}.call
if MyLibrary.CheckError != 0
raise “You got an error”
end
return ret
end

For functions that don’t actually return a value, ret is nil, so I can
just ignore it when I don’t need it, and that way I avoid having to have
two methods, one with, and one without, a return. So that in lib_func I
do:

def lib_func(name)
  ret = FFI::MemoryPointer.new(:char, 256)
  MyLibrary.call_check_error(MyLibrary.LibFunc(name,ret))
end

This seems to be working fine, although I’ve just started doing it this
way.

So my question is: Is that a good way to implement this? I’m assuming
it’s probably not the best way, and I would hope someone with more
experience with Ruby might have a suggestion. It kind of feels like:

lambda {function}.call

is a bit hokey, but I wasn’t sure how else to do it.

Thanks,

Rob

On Tue, Feb 5, 2013 at 9:15 PM, Rob M. [email protected]
wrote:

 end
  raise "You got an error"
  ret = FFI::MemoryPointer.new(:char, 256)
  MyLibrary.call_check_error(MyLibrary.LibFunc(name,ret))
end

This is not doing what you think it does. Ruby evaluates the arguments
of a method before calling the method. So that that line you are
actually calling the MyLibrary.LibFunc, getting its return value and
passing it to call_check_error.
Say for example that LibFunc returns “a”, you are passing that string
“a” to the call_check_error, so that you do:

lambda {“a”}.call, which also returns “a”.

This seems to be working fine, although I’ve just started doing it this
way.

So my question is: Is that a good way to implement this? I’m assuming
it’s probably not the best way, and I would hope someone with more
experience with Ruby might have a suggestion. It kind of feels like:

lambda {function}.call

is a bit hokey, but I wasn’t sure how else to do it.

If I remember correctly, you asked some time ago a way to refactor
this and Robert K. suggested to use blocks. So something like:

def call_with_check_error
ret = yield
if MyLibrary.CheckError != 0
raise “You got an error”
end
return ret
end

def lib_func(name)
pointer = FFI::MemoryPointer.new(:char, 256)
call_with_check_error { MyLibrary.LibFunc(name,ret) }
end

Hope this helps,

Jesus.

Hi Jesus,

Thanks for the help. I think I see your point about the “lambda {}call”
simply returning the already evaluated expression, as opposed to
actually evaluating it at the time of the yield. But in my case it
doesn’t really matter. Where the expression gets evaluated is less
important than making sure I check for errors immediately afterward. So,
from my perspective (and in this case) I am getting the “correct”
result. Here’s what I’ve tried in irb (removing the prompt):

def use_yield
ret = yield
puts “This is where I check for errors”
return ret
end

def use_lambda(func)
ret = lambda {func}.call
puts “This is where I check for errors”
return ret
end

def return_value(val)
return “This is the returning value: #{val}”
end

use_yield { return_value(“who cares?”) }
This is where I check for errors
=> “This is the returning value: who cares?”

use_lambda(return_value(“who cares?”))
This is where I check for errors
=> “This is the returning value: who cares?”

Again, they both seem to be doing the exact same thing. And in my module
it is working correctly, giving me the results I want.

Plus I would like to add a slight wrinkle, i.e. the ability to pass in
additional arguments. The yield method doesn’t seem to allow me to pass
in anything but a block and I don’t seem able to set local variables,
etc. With the “lambda {}.call” I can pass a variable argument list and
do additional testing on the items in the list as well as execute the
function (the last item on the argument list). Or is there some way to
pass/initialize additional arguments in a block that will get evaluated
by the yield?

For example:

def use_lambda(*list)
if list.length > 1
list[0…-2].each_with_index {|v,i| puts “This is argument #{i}:
#{v}”}
list.slice!(0…-2)
end
ret = lambda { list[0] }.call
puts “This is where I test for errors”
return ret
end

Doing that I can pass as many additional arguments as I want and
evaluate them etc. I haven’t figured out how to do that with the
block/yield method.

Thanks,

Rob

On Wed, Feb 6, 2013 at 4:27 PM, Rob M. [email protected]
wrote:

Hi Jesus,

Thanks for the help. I think I see your point about the “lambda {}call”
simply returning the already evaluated expression, as opposed to
actually evaluating it at the time of the yield. But in my case it
doesn’t really matter.
Where the expression gets evaluated is less
important than making sure I check for errors immediately afterward.

Unless the expression raises an exception. Passing the result of the
call means that the call is perform at a point where no exception
handling is being done, while if you pass a block, you can execute it
in a controlled place, where you can catch excepctions and handle
them.

def use_lambda(func)
This is where I check for errors
additional arguments. The yield method doesn’t seem to allow me to pass
if list.length > 1
evaluate them etc. I haven’t figured out how to do that with the
block/yield method.

You can pass a variable argument list to a block too:

1.9.2p290 :001 > def yield_args *args
1.9.2p290 :002?> yield *args
1.9.2p290 :003?> end
=> nil
1.9.2p290 :004 > yield_args(1,2,3,4) {|a,b,c,d| p a,b,c,d}
1
2
3
4
=> [1, 2, 3, 4]
1.9.2p290 :005 > yield_args(1,2,3,4) {|*args| p args}
[1, 2, 3, 4]
=> [1, 2, 3, 4]

Jesus.

To Robert…yes, I have NOT, yet, grasped the concept of blocks :slight_smile:

Let me explain: I’m using FFI to access a library. The way the library
calls work is that none of them return errors or even raise exceptions.
The expectation is, that after calling a function, the user will check
(via a function call) for errors. So what I end up with is:

Module MyModule
class SubClass
def initialize(instance_var)
@instance_var = instance_var
end

def my_function_call(arg1,arg2)
  unless @instance_var.nil?
    ret = MyModule.Real_FunctionCall(@instance_var,arg1,arg2)
    if MyModule.CheckLastError != 0
      raise MyException, MyModule.GetLastErrorText
    end
    return ret
  else
    raise ArgumentError, "Instance variable missing"
  end
end

def my_other_call(arg1,arg2, arg3)
  ret = MyModule.Real_OtherCall(arg1,arg2,arg3)
  if MyModule.CheckLastError != 0
    raise MyException, MyModule.GetLastErrorText
  end
  return ret
end

end
end

The difference is that I have many classes in the module and LOTS of
functions within each class. As you can see, in some cases I want to
check an instance variable before making the call (because the the
function call will need it), and in other cases I don’t need to. But in
ALL cases I need to do the error check. With close to a hundred+
functions, that’s 300+ repetitious lines of code.

What I think will work is (here’s a simplified example):

def check_error(*args)
if args.length > 0
args.each_with_index { |v,i| printf “%2d argument value: %s\n”, i, v
}
end
ret = yield
puts “Check error here”
ret
end

check_error(“this”,“is”,“a”,“test”){ return_value(“something again”) }
0 argument value: this
1 argument value: is
2 argument value: a
3 argument value: test
Check error here
=> “This is the returning value: something again”

check_error { return_value(“something again”) }
Check error here
=> “This is the returning value: something again”

The part I didn’t understand is how Ruby will allow “you” to put a block
after the args in parens…Once I figured that out (thanks to:
http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html#.URK0k-j6bYA) it
all made sense :slight_smile:

Thanks for the help,

Rob

On Wed, Feb 6, 2013 at 4:27 PM, Rob M. [email protected]
wrote:

Hi Jesus,

Thanks for the help. I think I see your point about the “lambda {}call”
simply returning the already evaluated expression, as opposed to
actually evaluating it at the time of the yield. But in my case it
doesn’t really matter. Where the expression gets evaluated is less
important than making sure I check for errors immediately afterward.

I agree to what Jesus said: the block allows more control. For
example you can also allocate resources and ensure they are cleaned up
properly after evaluating the block.

I am not 100% sure yet I understood your use case because I haven’t
seen the identical pattern (as far as I can see you only posted one
example which makes it difficult for others to detect what your
multiple identical pieces of code have in common and what not). Can
we maybe start over and see like 5 examples of these repeated calls?
Or get a more general explanation (i.e. in English and not code) what
repeated steps there are which are repeated.

def return_value(val)
return “This is the returning value: #{val}”
end

What do you need this for?

use_yield { return_value(“who cares?”) }
This is where I check for errors
=> “This is the returning value: who cares?”

use_lambda(return_value(“who cares?”))
This is where I check for errors
=> “This is the returning value: who cares?”

If you use the block form you can integrate the return_value logic
there and do not need to explicitly invoke that method.

Plus I would like to add a slight wrinkle, i.e. the ability to pass in
additional arguments. The yield method doesn’t seem to allow me to pass
in anything but a block and I don’t seem able to set local variables,
etc. With the “lambda {}.call” I can pass a variable argument list and
do additional testing on the items in the list as well as execute the
function (the last item on the argument list). Or is there some way to
pass/initialize additional arguments in a block that will get evaluated
by the yield?

Yes, see Jesus example.

For example:

def use_lambda(*list)
if list.length > 1
list[0…-2].each_with_index {|v,i| puts “This is argument #{i}:
#{v}”}
list.slice!(0…-2)
end
ret = lambda { list[0] }.call

Sorry, but this is nonsense - it’s the same as

ret = list[0]

and you are wasting a few CPU cycles.

Example:

irb(main):001:0> list = %w{foo bar}
=> [“foo”, “bar”]
irb(main):002:0> lambda { list[0] }.call
=> “foo”

I get the impression that you didn’t yet fully get the hang of blocks.
:slight_smile:

puts “This is where I test for errors”
return ret
end

Doing that I can pass as many additional arguments as I want and
evaluate them etc. I haven’t figured out how to do that with the
block/yield method.

You do not even need to pass arguments to the /method/ to work with
them in the block:

def checked
ret = yield
raise “Foo” unless ret == 0
ret
end

def foo(a, b)
checked do
a + b
end
end

Kind regards

robert

Hi Robert,

At present I’m using the approach I mentioned before, but let me try and
explain my confusion…

Every other language I’ve worked in, C, Perl, Python, Java…the way you
call a function with arguments is: func(arg1, arg2, arg3). In Python you
can have variable args and keyword args specified as (*args, **kwargs),
but in general you define the arguments that a function expects, and
then pass those arguments between parentheses. So the idea that I could
do:

func(arg1,arg2,arg3) { block }

And a) Not get a syntax error, and b) Be able to define the parameters
for the function as:

def func(*args)
yield
end

and not only get the args specified between the parens in *args, but
also still pass the block and “yield” it, is a very new, and foreign,
concept for me.

What I find a bit confusing about Ruby is that, given:

def func(*args)
args.each { |a| puts a }
yield
end

Then do the following:

func “this”, “is”, “a”, “test”, return_value(“something”)
this
is
a
test
This is the returning value: something
LocalJumpError: no block given
from (irb):108:in `func’
from (irb):110

This one is sort-of obvious since the function return_value (which is
just a dummy function that simply shows calling a function with some
arbitrary value and returning it slightly modified…so don’t get hung
up on it) is evaluated prior to being passed as an argument to func. But
since there’s no block, I get the LocalJumpError.

func “this”, “is”, “a”, “test”, { return_value(“something”) }
SyntaxError: compile error
(irb):111: odd number list for Hash
from (irb):111

This is is also somewhat obvious because it isn’t treating the {} as
defining a block but attempting to define a hash…That’s because I
haven’t “formally” defined the arguments, so the interpreter assumes
everything is supposed to be an argument and not a block.

func(“this”, “is”, “a”, “test”) { return_value(“something”) }
this
is
a
test
=> “This is the returning value: something”

This is still the odd one for me that now (sort-of) makes sense but
would NOT be allowed in any language I’ve used in the past. So it’s
going to take some getting used to…

Rob

On Wed, Feb 6, 2013 at 9:14 PM, Rob M. [email protected]
wrote:

To Robert…yes, I have NOT, yet, grasped the concept of blocks :slight_smile:

OK. First of all a block is just an anonymous function. Because it
doesn’t have a name it is invoked via keyword “yield” as you are
already aware. You can see it from the stack trace:

irb(main):001:0> def f; yield; end
=> nil
irb(main):002:0> puts f { caller(0) }
(irb):3:in block in irb_binding' (irb):1:inf’
(irb):3:in `irb_binding’

An alternative way to invoke the block is via an explicit last argument:

irb(main):007:0> def f(&any_name) any_name.call end
=> nil
irb(main):008:0> puts f { caller(0) }
(irb):8:in block in irb_binding' (irb):7:incall’
(irb):7:in f' (irb):8:inirb_binding’

Also, a block is scoped in the calling scope. So it has access to
all variables defined there:

irb(main):005:0* def g; x=1; f { puts x } end
=> nil
irb(main):006:0> g
1
=> nil

end

def my_other_call(arg1,arg2, arg3)
  ret = MyModule.Real_OtherCall(arg1,arg2,arg3)
  if MyModule.CheckLastError != 0
    raise MyException, MyModule.GetLastErrorText
  end
  return ret
end

end
end

Here’s a possible implementation idea:

untested

def my_function_call(arg1,arg2)
invoke_fun(:Real_FunctionCall, [arg1, arg2], %w{@instance_var})
end

def my_other_call(arg1,arg2, arg3)
invoke_fun(:Real_OtherCall, [arg1, arg2, arg3])
end

def fun_tastic(arg1,arg2)
invoke_fun(:Real_FunctionCall, [arg1, arg2], %w{@length @size})
end

private
def invoke_fun(name, args, ivars = [])
ivals = ivars.map {|iv| instance_variable_get(iv).tap {|val| raise
ArgumentError, “Ivar #{iv} missing” if val.nil?}}
MyModule.send(name, *ivals, *args).tap {raise MyException,
MyModule.GetLastErrorText unless MyModule.CheckLastError == 0}
end

Maybe this calls for meta programming. You could define a method on
class level which will define instance methods based on these inputs

  • name of the method to invoke
  • number of additional arguments
  • list of instance variables to use (possibly empty)

The generated code would behave like invoke_fun only that it would
have “burned” method name and instance variable names into it.

What I think will work is (here’s a simplified example):

I don’t think this is such a good idea. You want to get rid of as
much repetition as possible. That’s what I did with the code above.
Since the structure of the methods is so identical you can automate
much more. Once you found out what your inputs are (in terms of
variants of method implementations) things fall into place pretty
naturally.

check_error(“this”,“is”,“a”,“test”){ return_value(“something again”) }
As I said: return_value is completely superfluous. The code should
sit in check_error method because that avoids typing return_value
repetitively.

The part I didn’t understand is how Ruby will allow “you” to put a block
after the args in parens…Once I figured that out (thanks to:
http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html#.URK0k-j6bYA) it
all made sense :slight_smile:

I am not sure I understand what you mean by “put a block after the
args in parens”.

Thanks for the help,

You’re welcome!

Kind regards

robert

On 7 February 2013 10:39, Rob M. [email protected] wrote:

Every other language I’ve worked in, C, Perl, Python, Java…the way you
call a function with arguments is: func(arg1, arg2, arg3). In Python you
can have variable args and keyword args specified as (*args, **kwargs),
but in general you define the arguments that a function expects, and
then pass those arguments between parentheses.

Give or take a few other var-args tricks. Remember C has stdarg.h and
allows function signatures like:

void func(...);

And Java has special array syntactic sugar:

public static void func(Object... args)

And PHP almost completely ignores the variables named in the function
signature, because you can always use func_num_args() and
func_get_args()

etc.

Ruby allows non-parenthetical args, the same as shell script (e.g.
Bash).
Technically perl doesn’t require parentheses either, except that
functions
in perl expect a list of parameters, and perl lists use parentheses,
etc.
etc. Upshot: $thingy->foo is also a function call in perl.

end

and not only get the args specified between the parens in *args, but
also still pass the block and “yield” it, is a very new, and foreign,
concept for me.

If it helps, you can also define it as:

def func(*args, &a_block)

if you want to make explicit the fact that there is (or expects to be) a
block. It also gives you direct access to the block, so you can poke it
and stuff.

this
arbitrary value and returning it slightly modified…so don’t get hung
up on it) is evaluated prior to being passed as an argument to func. But
since there’s no block, I get the LocalJumpError.

To the first remark: this is the same as any other programming
language.
For example in Java:

func("this", "is", "a", "test", return_value("something"));

…will execute the ‘return_value’ function first, then pass its result
as
the final parameter to ‘func’.

As an aside to the second: You can get around/preempt the LocalJumpError
by
wrapping yield thus:

def func(*args)
  args.each { |a| puts a }
  yield if block_given?
end

Not necessarily what you want to do here, but handy to know.

func “this”, “is”, “a”, “test”, { return_value(“something”) }
SyntaxError: compile error
(irb):111: odd number list for Hash
from (irb):111

This is is also somewhat obvious because it isn’t treating the {} as
defining a block but attempting to define a hash…That’s because I
haven’t “formally” defined the arguments, so the interpreter assumes
everything is supposed to be an argument and not a block.

That would happen irrespective of how you define the function
parameters.
SyntaxErrors happen when ruby is parsing and interpreting the file, not
when it’s executing. A curly brace that follows a comma is always
interpreted as the start of a Hash literal.

Since blocks are special, “out-of-band” parameters, you don’t need a
comma
to distinguish them from the other args. However, since curly-brace
block
syntax looks a lot like Hash literal syntax, you need to give the parser
a
bit more help in this case (i.e. by explicitly wrapping the “real” args
in
parens, then giving the block.)

func(“this”, “is”, “a”, “test”) { return_value(“something”) }
this
is
a
test
=> “This is the returning value: something”

This is still the odd one for me that now (sort-of) makes sense but
would NOT be allowed in any language I’ve used in the past. So it’s
going to take some getting used to…

This is true, but only because those other languages don’t have blocks.
Note that the following calls are equivalent:

func("this", "is", "a", "test") { return_value("something") }

func "this", "is", "a", "test" do
  return_value "something"
end

my_block = proc { return_value("something") }
func "this", "is", "a", "test", &my_block

my_block = proc { return_value("something") }
func("this", "is", "a", "test", &my_block)

The third and fourth versions are the call-site equivalent of the def func(*args, &a_block) definition I mentioned above.

I hope this helps shed a little light on the issue, and gives you some
pointers for further research.


Matthew K., B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

“You’ll never find a programming language that frees
you from the burden of clarifying your ideas.” - xkcd

Hi Matthew,

Thanks. That was very helpful.

By the way, when I do: def func(*args,&ablock), I found:

func(“this”,“is”,“a”,“test”, proc { return_value(“something”) })

The “proc” ends up as the final member of args, so the yield fails. But
both of the following worked:

my_block = proc { return_value(“something”) }
func(“this”,“is”,“a”,“test”,&my_block)

and

func(“this”,“is”,“a”,“test”, &proc { return_value(“something”) })

For now I think I’ll stick with:

func(“this”,“is”,“a”,“test”){ return_value(“something”) }

It makes some sense to me now and avoids the need for “proc”.

Thanks again,

Rob

Rob M. wrote in post #1095677:

Hi Matthew,

Thanks. That was very helpful.

Glad to help.

By the way, when I do: def func(*args,&ablock), I found:

func(“this”,“is”,“a”,“test”, proc { return_value(“something”) })

The “proc” ends up as the final member of args, so the yield fails.

Indeed, because the ‘proc’ function returns an object, which isn’t
special in any way. It’s just another object, so becomes just another
arg.

But both of the following worked:

my_block = proc { return_value(“something”) }
func(“this”,“is”,“a”,“test”,&my_block)

and

func(“this”,“is”,“a”,“test”, &proc { return_value(“something”) })

Indeed. In either case the ‘proc’ function is evaluated before ‘func’,
and ‘proc’ returns a Proc object. A Proc is basically a wrapper around a
block; the & operator instructs ruby to peel back the Proc, revealing
the inner block.

For now I think I’ll stick with:

func(“this”,“is”,“a”,“test”){ return_value(“something”) }

It makes some sense to me now and avoids the need for “proc”.

It’s also a much more sensible solution, and what most normal people
would use. :wink: But now you’re aware that ‘proc’ is a thing, and one day
it might come in handy.

On Thu, Feb 7, 2013 at 1:39 AM, Rob M. [email protected]
wrote:

Hi Robert,

At present I’m using the approach I mentioned before, but let me try and
explain my confusion…

OK, I’ll try to clarify where I can.

for the function as:

def func(*args)
yield
end

and not only get the args specified between the parens in *args, but
also still pass the block and “yield” it, is a very new, and foreign,
concept for me.

Well, every language has its features which one needs to get used to.
:slight_smile: A block is special since Ruby will happily pass it to every
method called even though the method does not do anything with it.
And it does not need any declaration. Unless you want to store it
somewhere where the &argument_name syntax is needed (see my last
post). But even that does not affect errors you are seeing:

this
is
a
test
This is the returning value: something
LocalJumpError: no block given
from (irb):108:in `func’
from (irb):110

This one is sort-of obvious since the function return_value […]
is evaluated prior to being passed as an argument to func.

Exactly. Not a function is passed but the result of evaluating a
function. Nothing special here. That would be the same as in other
languages. And it doesn’t really make a difference in what position
you have the method call. Same as in other languages. I think you’re
making it more difficult for you to understand than necessary by
introducing #return_value here: this is just an expression as all the
literals you have in the invocation above.

But since there’s no block, I get the LocalJumpError.

Yes, that’s caused by invoking yield in absence of a block passed.
Btw you can check that with #block_given?

irb(main):001:0> def f; block_given? end
=> nil
irb(main):002:0> f
=> false
irb(main):003:0> f {}
=> true

func “this”, “is”, “a”, “test”, { return_value(“something”) }
SyntaxError: compile error
(irb):111: odd number list for Hash
from (irb):111

This is is also somewhat obvious because it isn’t treating the {} as
defining a block but attempting to define a hash…That’s because I
haven’t “formally” defined the arguments, so the interpreter assumes
everything is supposed to be an argument and not a block.

No, this is wrong. That has absolutely nothing to do with how you
defined the method! This is a syntax error. And it has to do with
different precedence of {} and do end. You don’t even have to define
the method for getting those errors - it’s all about how the call is
written:

$ ruby -ce ‘func “this”, “is”, “a”, “test”, { return_value(“something”)
}’
-e:1: syntax error, unexpected ‘}’, expecting tASSOC
$ ruby -ce ‘func “this”, “is”, “a”, “test” { return_value(“something”)
}’
-e:1: syntax error, unexpected ‘{’, expecting $end
func “this”, “is”, “a”, “test” { return_value(“something”) }
^
$ ruby -ce ‘func(“this”, “is”, “a”, “test”) { return_value(“something”)
}’
Syntax OK
$ ruby -ce ‘func “this”, “is”, “a”, “test” do return_value(“something”)
end’
Syntax OK

For Ruby at the calling location all methods have the same signature
at compile time. Issues like wrong number of arguments etc. are
only detected at runtime!

$ ruby -ce ‘def f(a) p a end; f(); f(1); f(1,2)’
Syntax OK
$ ruby -e ‘def f(a) p a end; f(); f(1); f(1,2)’
-e:1:in f': wrong number of arguments (0 for 1) (ArgumentError) from -e:1:in
$ ruby -e ‘def f(a) p a end; f(1); f(1,2)’
1
-e:1:in f': wrong number of arguments (2 for 1) (ArgumentError) from -e:1:in

So, to put it differently, for Ruby all methods have the same signature

f(*args, &block)

when it looks at them on calling sites at compile time.

func(“this”, “is”, “a”, “test”) { return_value(“something”) }
this
is
a
test
=> “This is the returning value: something”

This is still the odd one for me that now (sort-of) makes sense but
would NOT be allowed in any language I’ve used in the past. So it’s
going to take some getting used to…

So far your issues have been only syntax errors and they are totally
unrelated to how you define the method. I think that’s where you took
the wrong turn: Ruby is not checking whether argument lists are
properly passed or whether a method uses a block (all will accept a
block!) at compile time. Only at runtime Ruby will notice

  • wrong number of arguments
  • using a block when none was passed

Kind regards

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