There seems to be this initialization idiom in the ruby world where you do
def initialize(*someargs)
#begin initialization
yield self #so that the user can customize writable attributes
#finish initialization
end
and the user then does:
some_object = SomeClass.new do |o|
o.attribute1 = 'foo'
o.attribute2 = 'bar'
end
The Rake TestTask uses this, for example.
Is there any point in continuing this now that ruby has keyword arguments?
Standard arguments could have easily replaced this idiom, but positional arguments are inconvenient to the user when there's many of them, and a hash argument is inconvenient to the developer as he or she would have to check for valid keys. The above idiom seemed to eliminate both inconveniences, but keyword args do too and additionally they eliminate the need for a block and a repeated yielded_something. initialization pattern.
E.g.:
class SomeClass
def initialize(attribute1: nil, attribute2: nil)
end
end
SomeClass.new(
attribute2: 'bar',
attribute1: 'foo'
)
#Order doesn't matter like with the block idiom
#Invalid keys raise an error, just like invalid attribute assignments in the block idiom
Is this correct or am I missing something?
I think before keyword arguments, just passing a hash of options would have been fine, so I don't think arguments were the issue. Probably this type of instantiation was done for readablity and flexibility.
It makes it much easier to read config options when written like this. And also it makes it easier to do something like this:
Config.new do |c|
c.default_address = c.default_address + "/new"
end
The block syntax allows the user to add their own logic and call methods within the class which is especially useful for configuration objects.
For instance, RSpec's configuration object allows you to do something like:
RSpec.configure do |config|
config.before :each do
# do something before each test
end
end
This particular example isn't performed in the initializer, but depending on use case it could very well be.
Related
In POODR 2nd edition, page 125, Sandy Metz writes
There are two new messages, default_chain and default_tire_size, sent
on lines 6 and 7 below. ... Wrapping the defaults in methods is good
practice in general
The code she's referring to is below. Note how the default value for chain and tire_size is set.
class Bicycle
attr_reader :size, :chain, :tire_size
def initialize(**opts)
#size = opts[:size]
#chain = opts[:chain] || default_chain
#tire_size = opts[:tire_size] || default_tire_size
end
def default_chain # <- common default
"11-speed"
end
def default_tire_size # <- common default
"2.1"
end
end
Why is this approach better than the familiar method of simply setting the default value inside initialize(), i.e.
def initialize(chain: "11-speed", tire_size:"2.1", **opts):
This book is about OOP design, so I'm guessing the answer has something to do with good OOP practice but I'm not sure what.
Methods are easier to work with. You can stub them in tests or change the implementation (load values from a config file or something like that), all without touching code that uses them.
But the bigger difference between your code and what Sandi suggests is the handling of falsy values.
In your code, it's possible to pass nils explicitly instead of the default value.
Bicycle.new(chain: nil, tire_size: "2.1", ...)
It might not be a hardcoded nil in your code, but come from somewhere else. Regardless, Bicycle will accept it and then maybe crash at runtime when you try to use the value.
Whereas code from the book does not accept nil values, no matter if it's a default nil or explicitly sent. If chain is falsy, "11-speed" will be used.
Using a method modularizes the code so that subclasses can override the behavior without overriding the initializer:
class MountainBike < Bicycle
def default_tire_size
'18'
end
end
If you used positional arguments initialize(chain="11-speed", tire_size="2.1", **opts) this actually is a major downgrade as you now have to remember the order of the arguments. Positional arguments should only be used when there is an obvious order to the arguments.
You can actually set the defaults for both positional and keyword arguments in the arguments list from a method like this:
class Bicycle
attr_reader :size, :chain, :tire_size
def initialize(tire_size: default_tire_size, chain: default_tire_size, **opts)
end
# ...
end
However its not done very often since it tends to make the method signature very cluttered.
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.
In python there is a pass keyword for defining an empty function, condition, loop, ...
Is there something similar for Ruby?
Python Example:
def some_function():
# do nothing
pass
No, there is no such thing in Ruby. If you want an empty block, method, module, class etc., just write an empty block:
def some_method
end
That's it.
In Python, every block is required to contain at least one statement, that's why you need a "fake" no-op statement. Ruby doesn't have statements, it only has expressions, and it is perfectly legal for a block to contain zero expressions.
nil is probably the equivalent of it:
def some_function
nil
end
It's basically helpful when ignoring exceptions using a simple one-line statement:
Process.kill('CONT', pid) rescue nil
Instead of using a block:
begin
Process.kill('CONT')
rescue
end
And dropping nil would cause syntax error:
> throw :x rescue
SyntaxError: (irb):19: syntax error, unexpected end-of-input
from /usr/bin/irb:11:in `<main>'
Notes:
def some_function; end; some_function returns nil.
def a; :b; begin; throw :x; rescue; end; end; a; also returns nil.
You always have end statements, so pass is not needed.
Ruby example:
def some_function()
# do nothing
end
Ruby 3.0
As of Ruby 3.0, so-called "endless" method definitions are now supported -- we no longer require end statements with every single method definition. This means the most concise way of expressing an empty method like the example above is now arguably something like this:
def some_function = nil
Alternatively, there has always been an uglier one-line option using the much-hated semicolon:
def some_function; end
Note that this doesn't really change anything about the first solution except how the code can be written.
Single line functions and classes
def name ; end
class Name ; end
works fine for pseudocode.
As answered before everything in ruby is an expression so it is fine to leave it blank.
def name
end
class Name
end
A ruby alternative for python programmers who love the pass keyword
def pass
end
# OR
def pass; end
Note that it is useless to do this in Ruby since it allows empty methods but if you're that keen on pass, this is the simplest and cleanest alternative.
and now you can use this function inside any block and it will work the same.
def name
pass
end
# OR
class Name
pass
end
Keep in mind that pass is a function that returns, so it is up to you how you can use it.
If you want to be able to use it freely with any number of arguments, you have to have a small trick on the arguments:
def gobble *args, ≺ end
As others have said, in Ruby you can just leave a method body empty. However, this could prove a bit different than what Python accomplishes with pass.
In Ruby, everything is an object. The absence of value, which some programming languages indicate with null or nil is actually an object of NilClass in Ruby.
Consider the following example (in irb):
class A
def no_op
end
end
A.new.no_op
# => nil
A.new.no_op.class
# => NilClass
A.new.no_op.nil?
# => true
Here's Ruby's NilClass documentation for reference.
I believe Python's pass is used mainly to overcome the syntactic limitations of the language (indentation), although I'm not that experienced in Python.
Ruby's equivalent to pass can be ().
if 1 == 1
()
else
puts "Hello"
end
=> nil
lambda do
()
end.call
=> nil
You can also use it as part of condition ? true-expr : false-expr ternary operator.
Are there any plans to implement ruby behavior similar to the CoffeeScript feature of specifying an instance variable name in a method argument list?
Like
class User
def initialize(#name, age)
# #name is set implicitly, but #age isn't.
# the local variable "age" will be set, just like it currently works.
end
end
I'm aware of this question: in Ruby can I automatically populate instance variables somehow in the initialize method? , but all the solutions (including my own) don't seem to fit the ruby simplicity philosophy.
And, would there be any downsides for having this behavior?
UPDATE
One of the reasons for this is the DRY (don't repeat yourself) philosophy of the ruby community. I often find myself needing to repeat the name of an argument variable because I want it to be assigned to the instance variable of the same name.
def initialize(name)
# not DRY
#name = name
end
One downside I can think of is that it may look as though a method is doing nothing if it has no body. If you're scanning quickly, this may look like a no-op. But I think given time, we can adapt.
Another downside: if you're setting other instance variables in the body, and you try to be readable by putting all the assignments at the beginning, it can take more cognitive "power" to see that there assignments also happening in the argument list. But I don't think this is any harder than, say, seeing a constant or method call and having to jump to its definition.
# notice: instance var assignments are happening in 2 places!
def initialize(#name)
#errors = []
end
After some pondering, I wondered if it's possible to actually get the argument names from a ruby method. If so, I could use a special argument prefix like "iv_" to indicate which args should be set as instance variables.
And it is possible: How to get argument names using reflection.
Yes! So I can maybe write a module to handle this for me. Then I got stuck because if I call the module's helper method, it doesn't know the values of the arguments because they're local to the caller. Ah, but ruby has Binding objects.
Here's the module (ruby 1.9 only):
module InstanceVarsFromArgsSlurper
# arg_prefix must be a valid local variable name, and I strongly suggest
# ending it with an underscore for readability of the slurped args.
def self.enable_for(mod, arg_prefix)
raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i
mod.send(:include, self)
mod.instance_variable_set(:#instance_vars_from_args_slurper_prefix, arg_prefix.to_s)
end
def slurp_args(binding)
defined_prefix = self.class.instance_variable_get(:#instance_vars_from_args_slurper_prefix)
method_name = caller[0][/`.*?'/][1..-2]
param_names = method(method_name).parameters.map{|p| p.last.to_s }
param_names.each do |pname|
# starts with and longer than prefix
if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1
ivar_name = pname[defined_prefix.size .. -1]
eval "##{ivar_name} = #{pname}", binding
end
end
nil
end
end
And here's the usage:
class User
InstanceVarsFromArgsSlurper.enable_for(self, 'iv_')
def initialize(iv_name, age)
slurp_args(binding) # this line does all the heavy lifting
p [:iv_name, iv_name]
p [:age, age]
p [:#name, #name]
p [:#age, #age]
end
end
user = User.new("Methuselah", 969)
p user
Output:
[:iv_name, "Methuselah"]
[:age, 969]
[:#name, "Methuselah"]
[:#age, nil]
#<User:0x00000101089448 #name="Methuselah">
It doesn't let you have an empty method body, but it is DRY. I'm sure it can be enhanced further by merely specifying which methods should have this behavior (implemented via alias_method), rather than calling slurp_args in each method - the specification would have to be after all the methods are defined though.
Note that the module and helper method name could probably be improved. I just used the first thing that came to mind.
Well, actually...
class User
define_method(:initialize) { |#name| }
end
User.new(:name).instance_variable_get :#name
# => :name
Works in 1.8.7, but not in 1.9.3. Now, just where did I learn about this...
I think you answered your own question, it does not fit the ruby simplicity philosophy. It would add additional complexity for how parameters are handled in methods and moves the logic for managing variables up into the method parameters. I can see the argument that it makes the code less readable a toss up, but it does strike me as not very verbose.
Some scenarios the # param would have to contend with:
def initialize( first, last, #scope, #opts = {} )
def search( #query, condition )
def ratchet( #*arg )
Should all of these scenarios be valid? Just the initialize? The #*arg seems particularly dicey in my mind. All these rules and exclusions make the Ruby language more complicated. For the benefit of auto instance variables, I do not think it would be worth it.
I'm trying to figure out how to create a sort of "class-less DSL" for my Ruby project, similar to how step definitions are defined in a Cucumber step definition file or routes are defined in a Sinatra application.
For example, I want to have a file where all my DSL functions are being called:
#sample.rb
when_string_matches /hello (.+)/ do |name|
call_another_method(name)
end
I assume it's a bad practice to pollute the global (Kernel) namespace with a bunch of methods that are specific to my project. So the methods when_string_matches and call_another_method would be defined in my library and the sample.rb file would somehow be evaluated in the context of my DSL methods.
Update: Here's an example of how these DSL methods are currently defined:
The DSL methods are defined in a class that is being subclassed (I'd like to find a way to reuse these methods between the simple DSL and the class instances):
module MyMod
class Action
def call_another_method(value)
puts value
end
def handle(text)
# a subclass would be expected to define
# this method (as an alternative to the
# simple DSL approach)
end
end
end
Then at some point, during the initialization of my program, I want to parse the sample.rb file and store these actions to be executed later:
module MyMod
class Parser
# parse the file, saving the blocks and regular expressions to call later
def parse_it
file_contents = File.read('sample.rb')
instance_eval file_contents
end
# doesnt seem like this belongs here, but it won't work if it's not
def self.when_string_matches(regex, &block)
MyMod.blocks_for_executing_later << { regex: regex, block: block }
end
end
end
# Later...
module MyMod
class Runner
def run
string = 'hello Andrew'
MyMod.blocks_for_executing_later.each do |action|
if string =~ action[:regex]
args = action[:regex].match(string).captures
action[:block].call(args)
end
end
end
end
end
The problem with what I have so far (and the various things I've tried that I didn't mention above) is when a block is defined in the file, the instance method is not available (I know that it is in a different class right now). But what I want to do is more like creating an instance and eval'ing in that context rather than eval'ing in the Parser class. But I don't know how to do this.
I hope that makes sense. Any help, experience, or advice would be appreciated.
It's a bit challenging to give you a pat answer on how to do what you are asking to do. I'd recommend that you take a look at the book Eloquent Ruby because there are a couple chapters in there dealing with DSLs which would probably be valuable to you. You did ask for some info on how these other libraries do what they do, so I can briefly try to give you an overview.
Sinatra
If you look into the sinatra code sinatra/main.rb you'll see that it extends Sinatra::Delegator into the main line of code. Delegator is pretty interesting..
It sets up all the methods that it wants to delegate
delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
:before, :after, :error, :not_found, :configure, :set, :mime_type,
:enable, :disable, :use, :development?, :test?, :production?,
:helpers, :settings
and sets up the class to delegate to as a class variable so that it can be overridden if needed..
self.target = Application
And the delegate method nicely allows you to override these methods by using respond_to? or it calls out to the target class if the method is not defined..
def self.delegate(*methods)
methods.each do |method_name|
define_method(method_name) do |*args, &block|
return super(*args, &block) if respond_to? method_name
Delegator.target.send(method_name, *args, &block)
end
private method_name
end
end
Cucumber
Cucumber uses the treetop language library. It's a powerful (and complex—i.e. non-trivial to learn) tool for building DSLs. If you anticipate your DSL growing a lot then you might want to invest in learning to use this 'big gun'. It's far too much to describe here.
HAML
You didn't ask about HAML, but it's just another DSL that is implemented 'manually', i.e. it doesn't use treetop. Basically (gross oversimplification here) it reads the haml file and processes each line with a case statement...
def process_line(text, index)
#index = index + 1
case text[0]
when DIV_CLASS; push div(text)
when DIV_ID
return push plain(text) if text[1] == ?{
push div(text)
when ELEMENT; push tag(text)
when COMMENT; push comment(text[1..-1].strip)
...
I think it used to call out to methods directly, but now it's preprocessing the file and pushing the commands into a stack of sorts. e.g. the plain method
FYI the definition of the constants looks like this..
# Designates an XHTML/XML element.
ELEMENT = ?%
# Designates a `<div>` element with the given class.
DIV_CLASS = ?.
# Designates a `<div>` element with the given id.
DIV_ID = ?#
# Designates an XHTML/XML comment.
COMMENT = ?/
You can use Modules to organize your code. You can add your DSL methods to the Module class using the Module#include method. Here's how RSpec does it. The last two lines being what you are probably looking for. +1 to #meagar about keeping DSL's simple!
Also as #UncleGene points out, RSpec does pollute Kernel with the DSL methods. I'm not sure how to get around that. If there was another DSL with a describe method, it would be hard to determine which describe one was using.
module RSpec
module Core
# Adds the `describe` method to the top-level namespace.
module DSL
# Generates a subclass of {ExampleGroup}
#
# ## Examples:
#
# describe "something" do
# it "does something" do
# # example code goes here
# end
# end
#
# #see ExampleGroup
# #see ExampleGroup.describe
def describe(*args, &example_group_block)
RSpec::Core::ExampleGroup.describe(*args, &example_group_block).register
end
end
end
end
extend RSpec::Core::DSL
Module.send(:include, RSpec::Core::DSL)
Just define a method called when_string_matches which takes a regex as an argument, tests it against whatever "string" you're talking about, and conditionally yields, passing whatever name is to its block:
def when_string_matches(regex)
# do whatever is required to produce `my_string` and `name`
yield(name) if my_string =~ regex
end
This is essentially all Ruby DSLs are: Methods with interesting names that often accept blocks.