How do write two methods with different number of arguments in Ruby - 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.

Related

Method returning different type if block_given?

Would it be bad practice to have a method that returns self on block_given? and a different type if a block was not provided?
The example:
Config#item will return the item if a block is not given, and will return Config if it is given.
class Item
:attr_reader :key
def initialize(key)
#key = key
end
def do_stuff
puts "#{key} doing some stuff"
self
end
end
class Config
attr_reader :items
def initialize
#items = {}
end
def item(key)
itm = #items[key] ||= Item.new(key)
if block_given?
yield(itm)
self
else
itm
end
end
end
Usage:
cnf = Config.new
cnf.item("foo") do |itm|
itm.do_stuff
end
.item("bar") do |itm|
itm.do_stuff
end
foo = .item("foo").do_stuff
cnf.item("baz").do_stuff
foo.do_stuff
The model is meant to use the same method item as a getter and as a way to refer to an item that needs to be configured or which configuration needs to be reopened.
Would it be bad practice to have a method that returns self on block_given? and a different type if a block was not provided?
No. In fact, there is an extremely well-known example of a method that has this exact signature: each. each returns self if a block is given, and an Enumerator when no block is given. In fact, many methods in Enumerable return an Enumerator when no block is given and something else if there is a block.
(I am actually surprised that you haven't encountered each or Enumerable so far.)
Not at all, as long as the users of your method have adequate understanding of this. Documentation helps quite significantly in these situations.
Consider the Ruby Standard Library. Many methods return different types based on their inputs and block_given?, such as Enumerable#map, Hash#each, and Range#step.
Like the standard library authors, you have to decide whether you prefer a compact interface to your class/model or consistent behavior from your methods. There are always tradeoffs to make, and you have numerous strong examples of each of these to draw from within the Ruby Standard Library.

Ruby: the role of `initialize`

I'm wondering if there's a convention / best practice for how initialize should be used when building Ruby classes. I've recently built a class as follows:
class MyClass
def initialize(file_path)
#mapped_file = map_file(file_path)
end
def map_file(file_path)
# do some processing and return the data
end
def run
#mapped_file.do_something
end
end
This uses initialize to do a lot of heavy lifting, before methods are subsequently called (all of which rely on #mapped_data).
My question is whether such processing should be handled outside of the constructor, with initialize used simply to store the instances' inputs. Would the following, for example, be preferable?
class MyClass
def initialize(file_path)
#file_path = file_path
end
def run
mapped_file.do_something_else do
etc_etc
end
end
def mapped_file(file_path)
#mapped_file ||= map_the_file_here
end
end
I hope this question isn't considered too opinion based, but will happily remove if it's deemed to be.
So, is there a 'correct' way to use initialize, and how would this fit with the scenarios above?
Any questions or comments, let me know.
As was mentioned in the comments, constructor is usually used to prepare the object, not do any actual work. More than a ruby convention, this a rule of thumb of almost all Object-Oriented languages.
What does "preparing the object" usually entail? Initializing members with default values, assigning passed arguments, calling the initializer of a super-class if such exists, etc.
In your case, this how I would rewrite your class:
class MyClass
def initialize(file_path)
#file_path = file_path
end
def map_file
#mapped_file ||= map_file_here(#file_path)
end
def run
map_file.do_something
end
end
Since run requires the file to be mapped, it always calls map_file first. But the internal map_file_here executes only once.

How to compactly write a block that executes a method with arguments

In the following code:
def main
someArray.all? { |item| checkSomething(item) }
end
private
def checkSomething(arg)
...
end
How do I shorten the all? statement in order to ged rid of the redundant item variable?
I'm looking for something like someArray.all?(checkSomething) which gives a "wrong number of arguments" error.
You could have a slightly shorter code if checkSomething was a method on your object class. Don't know what it is, so, I'm guessing, you're working with primitives (numbers, strings, etc.). So something like this should work:
class Object
def check_something
# check self
end
end
some_array.all?(&:check_something)
But this is, of course, a horrible, horrible way of going about it. Saving a few keystrokes at the cost of such global pollution - absolutely not worth it. Moreover, even this trick will not be available as soon as you will need to pass additional parameters to the check method.
Besides, the original code is quite readable too.
You could use Object#method and Method#to_proc (i.e. &method) to get rid of the item variable, although it is slower:
def main(array)
array.all?(&method(:check_something))
end
def check_something(arg)
arg.odd?
end
main [1,3,5] #=> true
main [1,3,6] #=> false
If checkSomething is an item method (i.e. defined in the class of the 'i' object) you could do symbol to proc...
def main
someArray.all?(&:checkSomething)
end
A method only has access to passed arguments, or to selfso to bypass passing arguments you need to make the method an instance method of the object class (so it can use self)
The way you have it... where checkSomething is external to the i class... you can't do that.
Considering you want to keep your object's checkSomething private, I think this would be a good work around :
class Something
def main
someArray.all?(&checkSomething)
end
private
def checkSomething
->(item) do
# Checking part.
end
end
end
For block that executes a method with arguments, Checkout this way...
def main
someArray.all? &checkSomething(arg1, arg2, ...)
end
private
def checkSomething(arg1, arg2, ...)
Proc.new { |item| ..... }
end
could you not use a Ruby's collection method 'any?' instead?
def main
#students is an array of students
students.any?(&:passed)
end
class Student
def passed
#code to check if student passed
end
end
Ref http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-any-3F

Calling a method in Ruby with optional parameters, regardless of position in parameters list, and setting to hash

I have a class method, call it PetSearch. I want to initialize a search with options via a class variable, ##options. I can pass in either an array or a hash. Chose array as such:
def self.init_options(options=['dog', 94123, 'Young', 'F'])
##options[:animal] = options[0]
##options[:location] = options[1]
##options[:age] = options[2]
##options[:sex] = options[3]
end
However, I want to be able to pass in options like:
def self.init_options(dog, Young)
##options[:dog] = dog
##options[:age] = Young
end
Notice that I would like to pass in a non-string "variable" like dog - not 'dog', and I am passing in the variables indiscriminately without regard to order. I'm assuming there's a meta-programming block/proc/etc. sort of way to do this, but I am still learning how to harness that power. Can someone help me out? I will receive my undying gratitude and major up-votes.
I would never recommend doing this, and it doesn't really make sense: most of the ways you would collect user input would be as strings (e.g., script arguments, form values, etc.).
You can abuse method_missing and const_missing to pass in your arguments as non-strings.
So in your class or module you could have something like:
def self.init_options(*args)
##options[:dog] = args.grep(/[Dd]og|etc|etc/).first
##options[:age] = args.grep(/[Oo]ld|[Yy]oung/).first
end
def self.options
##options
end
And then, in the context where your user is doing the "initializing":
def method_missing(m)
m.to_s
end
def Object.const_missing(c)
c.to_s
end
Testmm.init_options(dog, Young)
puts Testmm.options.inspect
#=> {:dog=>"dog", :age=>"Young"}
Not sure if that is the behavior you are looking for, but it sounds like it. Also note that this won't work in IRB, but I've confirmed that it works as a script.

Trying to create a method that chooses a random method from that same class

Maybe this is a stupid idea... I am new to Ruby (and to OOP, so I still dont really know what I am doing most of the time), and I thought of a small, fun project to build, and I am struggling with some concepts.
What I am building is basically a string manipulator. I am building a module with extra methods, and then including that on the String class.
My module has several methods that manipulate the strings in different ways, mostly replacing words, and then return the modified string.
What I want to do in order to make the string manipulation more 'spontaneous' and natural, is to create a "main" method (the one I will be calling from the strings), that randomly selects one of the string manipulation methods, and then returns the string (and then can be called again to apply several manipulations in one go)
How can I do this, or something similar? Hope I explained myself
Thanks
O.
Here's the random manipulation module, as you described it. something_random is the main method:
module RandomStringManipulation
def something_random
methods = RandomStringManipulation.instance_methods
methods -= [:something_random]
send methods.sample # Ruby >= 1.9 required. See below for Ruby 1.8.
end
def foo
self + "foo"
end
def bar
self + "bar"
end
end
Mix it into String:
class String
include RandomStringManipulation
end
Now we can create an empty string, and then do something random to it a few times, printing it out each time:
s = ""
4.times do
s = s.something_random
p s
end
# => "foo"
# => "foobar"
# => "foobarbar"
# => "foobarbarfoo"
There are two bits that are interesting. The first is this:
methods -= [:something_random]
That removes :something_random from the array methods, preventing the *something_random* method from calling itself. The second interesting bit is this:
send methods.sample
Array.sample (Ruby >= 1.9) selects a random method. send then dispatches that method. In Ruby 1.8, do this instead:
send methods[rand(methods.size)]
If all the code that will be using your functionality is your code, I would make a new class instead of monkey-patching String. Monkey-patching leads to code that is harder to understand and share.
Anyway, given a list of mutator methods, you can easily pick one at random and use Object#send to call it:
module StringMutator
def fold() ... end
def spindle() ... end
def mutilate() ... end
end
class NiftyString
include StringMutator
def random_change()
im = StringMutator.instance_methods
self.send im[rand im.length]
end
end

Resources