Is it possible to somehow improve the syntax towards nested methods? - ruby

I have this method:
def prepare
lambda do |zone, data|
# Some code
end
end
It is used like this:
prepare.call(:box, {})
Is there any way to get rid of .call? Ideally, I would like this method to be called like this:
prepare.box({})

Related

Can I pass the minus (-) or plus (+) sign as params into a method and use them in the method?

I have a class, that has an attribute that's an integer. When I calculate the sum/difference of two instances of that class I actually want to calculate the sum/difference of their attribute. So my class looks like this:
class A
attr_reader :a
def initialize(a)
#a = a
end
def +(instance_of_a)
A.new(self.a + instance_of_a.a)
end
def -(instance_of_a)
A.new(self.a - instance_of_a.a)
end
end
Is there a way I could write a method to take the logic out of those 2 (- and +) methods and store them into the new method? I would like to write something like this:
def operation(sign, instance_of_a)
A.new(self.a sign instance_of_a.a)
end
def +(instance_of_a)
operation(+, instance_of_a)
end
def -(instance_of_a)
operation(-, instance_of_a)
end
Obviously this doesn't work like this, but I can't figure a way to implement something like this. That's probably because I don't really understand what the + and - are. I can't do something like -.class.name and I can't do something like this either:
def -
10 __method__ 5
end
Any clarification on this matter is more than welcome, thanks.
Yes. When your are calling a method on a object, your are sending a message to the object. In ruby send using you can send messages(call that method) to that object. For example to 1.+2 would give me three.
Now You could implement your operation method like
def operation(operation,instance_of_a)
a.send(operation,instance_of_a.a)
end
Your + method would be
operation("+",instance_of_a)

How do write two methods with different number of arguments in Ruby

I am trying to write this inside my class:
class << self
def steps
#steps.call
end
def transitions
#transitions.call
end
def steps(&steps)
#steps = steps
end
def transitions(&transitions)
#transitions = transitions
end
end
That won't work since in Ruby, I can't do this kind of method overloading. Is there a way around this?
You can kind of do this with method aliasing and mixins, but the way you handle methods with different signatures in Ruby is with optional arguments:
def steps(&block)
block.present? ? #steps = block : #steps.call
end
This sort of delegation is a code smell, though. It usually means there's something awkward about the interface you've designed. In this case, something like this is probably better:
def steps
#steps.call
end
def steps=(&block)
#steps = block
end
This makes it clear to other objects in the system how to use this interface since it follows convention. It also allows for other cases, like passing a block into the steps method for some other use:
def steps(&block)
#steps.call(&block)
end
Ruby does not support method overloading (see "Why doesn't ruby support method overloading?" for the reason). You can, however, do something like:
def run(args*)
puts args
end
args will then be an array of the arguments passed in.
You can also pass in a hash of options to handle arguments, or you can pass in nil when you don't want to supply arguments and handle nil in your method body.

Other ways to "embed" or make variable "static" in new method?

Is there any other ways to make it another way than with closure and define_method?
Say i have this:
def test
result=[1,2,3]
metadata=['foo', 'bar'] # for simplicity, could be fetched from database
result.define_singleton_method :headers, lambda { metadata }
result
end
I'm curious, are there other ways to embed, make static or well, "copy" metadata variable into method in Ruby?
I find it kind of iffy to be defining methods like this (probably you should have an object that looks like an array rather than making the array look like your object), but this will work as well.
def test
result=[1,2,3]
result.instance_eval { #headers = ['foo', 'bar'] }
result.define_singleton_method(:headers) { #headers }
result
end
You could also do something like this (it's a little different in that it creates a setter as well).
module HasHeaders
attr_accessor :headers
end
def test
result = [1,2,3].extend HasHeaders
result.headers = ['foo', 'bar']
result
end
Well, method definitions aren't closures, so this will not work:
def result.headers
metadata
end
Since you are testing, I recommend stubbing the method. With RSpec::Mocks:
result.stub(:headers).and_return metadata
Related:
Define a method that is a closure in Ruby

How to assert block of a mock in mocha

This example is contrived, please don't take it verbatim as my code.
I have the need to assert something like the following:
def mymethod
Dir.chdir('/tmp') do
`ls`
end
end
In the end I want to assert that:
Dir.chdir is invoked with the appropriate parameters.
` is invoked with the appropriate parameters
I started off with...
Dir.expects(:chdir).with('/tmp')
but after that I'm not sure how to invoke the block passed to Dir.chdir.
You need to use the mocha yields method. Also, writing an expectation for the backtick method is rather interesting. You need to make an expectation like this:
expects("`")
But on what object? You might think on Kernel or Object, but that doesn't actually work.
As an example, given this module:
module MyMethod
def self.mymethod
Dir.chdir('/tmp') do
`ls`
end
end
end
I could write a test like this:
class MyMethodTest < Test::Unit::TestCase
def test_my_method
mock_block = mock
mock_directory_contents = mock
MyMethod.expects("`").with('ls').returns(mock_directory_contents)
Dir.expects(:chdir).yields(mock_block).returns(mock_directory_contents)
assert_equal mock_directory_contents, MyMethod.mymethod
end
end
Part of the trick is to figure out which object to expect the backtick method to be invoked on. It depends on the context - whatever self is when that method is invoked. Here it is the module MyMethod, but depending on where you define mymethod it will be different.

Using define_method to define global methods outside of a module

I'd like to write this:
[:p, :h1, :h3].each do |tag|
define_method(tag) { |text| "<#{tag}>#{text}</#{tag}>" }
end
It's just some simple methods to wrap text in HTML tags. I want to be able to use these methods in the rest of the script. Unfortunately the define_method method seems to only work inside of a module. But if I did this inside a module, I wouldn't be able to cleanly write p "This is a paragraph.", it'd be something like HTML::p "This is a paragraph." which would be pretty terrible.
So how do I define methods like this globally?
If you really need to do it:
[:p, :h1, :h3].each do |tag|
Object.send(:define_method, tag) { |text| "<#{tag}>#{text}</#{tag}>" }
end
I don't know your whole situation, but you probably don't really want to be defining global methods. If you don't want to type the HTML:: then add an include HTML statement at the beginning of your code.
One hack would be to create the method inside Object, which would then be global method you desire:
class Object
def create_method(name, &block)
self.class.send(:define_method, name, &block)
end
end
tag = 'p'
a = Object.new
a.create_method(tag.intern) {|v| puts "<#{tag}>#{v}</#{tag}>"}
send(tag.intern, 'content') # => <p>content</p>

Resources