Making invalid method names valid - ruby

I remember watching a Dave Thomas Ruby tutorial where he used a technique to make invalid method names still acceptable to the interpreter. For example, even though "case date" is an invalid method name since there is a space, in the tutorial his trick allowed that method to still work.
Unfortunately, I don't remember it, but this is my situation. I accept user input and convert it to a method, as shown:
def self.define_field(name, type)
class_eval <<-EOS
field :#{ name}, type: #{type}
EOS
end
The problem is if there is a space in the user input or another invalid character for a method name, I get the following error:
syntax error, unexpected tIDENTIFIER, expecting end-of-input
field :damage date, type: Date
How can I can allow for my dynamic method creation, yet allow users to enter any input they want?

Given your class X, you can generate a symbol that accepts the space character:
s = input.to_sym
and then call, for example, define_singleton_method on your class to define a singleton method:
X.define_singleton_method(s) do
...
end
And then you can use send to call that method on a class X's instance x:
x.send(input.to_sym [, args...])

You could try converting name to a symbol directly, not through eval.
[1] pry(main)> "damage date".to_sym
=> :"damage date"
But unless you have a real solid reason to need spaces, I would find another way. Just seems like a recipe for disaster.
No idea what Dave Thomas was talking about, but I wonder if he was discussing method_missing?
http://www.ruby-doc.org/core-2.1.5/BasicObject.html#method-i-method_missing

Related

Ruby: usage of ? in if condition

In Ruby, what is the difference of using "?" inside if condition?
if object.property
or
if object.property?
I found the usage of both of them in a method, without understanding the difference
Thanks a lot
? can be part of the function name. It is not a special operator if it comes at the end of a method name. Also ! can be part of the method name too. So what that line is doing is calling both object.property and object.property? methods.
What are the restrictions for method names in Ruby?
In Ruby, foo.bar is the syntax for a message send. It will first evaluate the expression foo (which is either dereferencing a local variable or a receiverless message send to the implicit receiver self) and then send the message bar to the resulting object.
Once you know what message send in Ruby looks like, it is easy to see what object.property? does: It will first evaluate the expression object (which is either dereferencing a local variable or a receiverless message send to the implicit receiver self) and then send the message property? to the resulting object.
So, what is the difference between the two? Well, the first one sends the message property and the second one sends the message property?. This is no different than if the first one had been object.foo and the second one had been object.bar.
Method names ending in ? are typically used for predicate methods, i.e. for methods that ask a "Yes/No" question. A good example is Numeric#zero?.

Ruby named parameters with keywords

I'd love to use a method signature like:
def register(something, on:, for:)
This works, but I can't work out how to use "for" without causing a syntax error! Rather annoying, anyone know a way around this?
The problem is not the method definition line that you posted, the problem is the usage of the for variable inside the method body. Since for is a reserved word, you cannot use it as a plain variable name, but you can use it as part of a hash. In your case that means you must resort to using arbitrary keyword arguments (**opts), but you can use the keyword_argument for: in the method call. You may want to raise an ArgumentError if the key is not present to emulate the behavior of the method signature you posted above.
def register(something, on:, **opts)
raise ArgumentError, 'missing keyword: for' unless opts.has_key?(:for)
for_value = opts[:for]
puts "registering #{something} on #{on} for #{for_value}"
end
register 'chocolate chips', on: 'cookie'
# ArgumentError: missing keyword: for
register 'chocolate chips', on: 'cookie', for: 'cookie monster'
# registering chocolate chips on cookie for cookie monster
In Ruby, for is a reserved keyword - looks like you just cannot use them in other way to how they were meant to use.
That's the whole purpose of reserving keywords.
Additional resources on which keywords are reserved in Ruby:
http://www.rubymagic.org/posts/ruby-and-rails-reserved-words
http://www.java2s.com/Code/Ruby/Language-Basics/Rubysreservedwords.htm
(thanks #cremno for this comment) Both links don't mention ENCODING. Also, since 2.2.0 Ruby comes with an official document: $ ri ruby:keywords
UPD
Actualy, you can still use :for symbol as a key in hash (let's say, options hash), so, you can write like this:
def test(something, options = {})
puts something
puts options.values.join(' and ')
end
and it works like charm:
[4] pry(main)> test 'arguments', :for => :lulz, :with => :care, :while => 'you are writing code'
arguments
lulz and care and you are writing code
binding.local_variable_get(:for)
is a way I was thinking. Only works in ruby 2.1+ I think.
NOTE: Don't do this, I'm just interested in how you could get round it, you probably should just call your named parameter something else :)

Ruby koan 124 - Are these symbols spontaneously changing type?

I'd like to get a categorical answer on how (and why) symbols appear to change type.
I had learned that symbols that end with a colon were an alternate syntax for declaring a symbol, usually used to populate hashes.
Koan number 124 has added more ambiguity to my (non)understanding by doing the following:
def method_with_keyword_arguments(one: 1, two: 'two')
[one, two]
end
This page:
http://chriszetter.com/blog/2012/11/02/keyword-arguments-in-ruby-2-dot-0/
says Keyword arguments in the method definition must be symbols given in the new-style hash syntax but then what appear to be symbols in the parameters of the method are then method variables in the returned Array.
Can symbols change 'type' without even being changed by a method? Or, in this case, is there a method I'm not seeing that's changing the type? And if the type doesn't matter, why does it appear to change? Or can symbols be referred to without a colon?
If I'm wrong about any of this, please feel free to correct me and let me know what actually IS going on! Thanks.
No, symbols cannot change type on their own, but when you're using one or two in your example, you are using variables with similar names, provided by Ruby based on your method definition, not the symbols themselves.
Consider a hash: when you have a hash a = { one: 1, two: '1234' } and you write a[:one] you don't get a symbol, but an appropriate value. So :one still is a symbol, but with [a[:one], a[:two]] you will get [1, '1234'] array, not [:one, :two].
There are no symbols here. one and two are parameters, parameters become local variable variables in the method body.
In
def foo(bar:, baz:)
bar and baz are no more symbols than they are in
def foo(bar, baz)
In the first case they are keyword parameters, in the second case they are positional parameters, other than that, there is no difference. In the parameter list, they are parameter placeholders, in the method body, they become local variables bound to arguments.

How is the syntax of Ruby's alias allowed?

This one's been keeping me up at night for a while.
class Foo
def bar
'bar'
end
# What the hell is going on here?!?
alias :baz :bar
end
Foo.new.baz #=> 'bar'
Why does alias take 2 symbol as arguments, but without a comma separating them? That doesn't seem to be any form of valid syntax in any other context. And in fact, if you do use a comma, it actually throws a syntax error.
alias :bar, :baz
# syntax error, unexpected ','
However, if I try to pass 2 symbol in the same way to my own method, it also explodes:
def somemethod(*args)
:whatever
end
somemethod :a :b
# syntax error, unexpected ':', expecting $end
So why is the alias method get to use a syntax nothing else gets to use?
Is it possible to use this syntax in any other context?
What is the benefit of using this odd syntax quirk, when nothing else in the language works this way? I see no discernable benefit to this language inconsistency.
The reason alias works is because it's a Ruby keyword, similar to class, def, etc. It's not a method.
The alias keyword doesn't need a comma because the Ruby designers decided it didn't. Keywords are essentially hardcoded in the interpreter.
There is a good reason for alias when you need to be certain the alias happens at parse time, not runtime.
The alias keyword may be confusing or surprising. For typical development, I believe it's better to use the Ruby method Module#alias_method, which does use a comma and works at runtime.
Here's a good blog post about alias and alias_method:
This is because alias is a keyword and it is lexically scoped. It
means it treats self as the value of self at the time the source code
was read. In contrast alias_method treats self as the value
determined at the run time.
Overall my recommendation would be to use alias_method. Since
alias_method is a method defined in class Module it can be overridden
later and it offers more flexibility.
It's not a method, and those aren't arguments. alias is one of the few keywords in Ruby (see them all here!). It bothers me as well, so I switched to alias_method (and there are other arguments for this).

How do Ruby programmers do type checking?

Since there is no type in ruby, how do Ruby programmers make sure a function receives correct arguments? Right now, I am repeating if object.kind_of/instance_of statements to check and raise runtime errors everywhere, which is ugly. There must be a better way of doing this.
My personal way, which I am not sure if it a recommended way in general, is to type-check and do other validations once an error occurs. I put the type check routine in a rescue block. This way, I can avoid performance loss when correct arguments are given, but still give back the correct error message when an error occurs.
def foo arg1, arg2, arg3
...
main_routine
...
rescue
## check for type and other validations
raise "Expecting an array: #{arg1.inspect}" unless arg1.kind_of?(Array)
raise "The first argument must be of length 2: #{arg1.inspect}" unless arg1.length == 2
raise "Expecting a string: #{arg2.inspect}" unless arg2.kind_of?(String)
raise "The second argument must not be empty" if arg2.empty?
...
raise "This is `foo''s bug. Something unexpected happened: #{$!.message}"
end
Suppose in the main_routine, you use the method each on arg1 assuming that arg1 is an array. If it turns out that it is something else, to which each is not defined, then the bare error message will be something like method each not defined on ..., which, from the perspective of the user of the method foo, might be not helpful. In that case, the original error message will be replaced by the message Expecting an array: ..., which is much more helpful.
Ruby is, of course, dynamically typed.
Thus the method documentation determines the type contract; the type-information is moved from the formal type-system to the [informal type specification in the] method documentation. I mix generalities like "acts like an array" and specifics such as "is a string". The caller should only expect to work with the stated types.
If the caller violates this contract then anything can happen. The method need not worry: it was used incorrectly.
In light of the above, I avoid checking for a specific type and avoid trying to create overloads with such behavior.
Unit-tests can help ensure that the contract works for expected data.
If a method has a reason to exist, it will be called.
If reasonable tests are written, everything will be called.
And if every method is called, then every method will be type-checked.
Don't waste time putting in type checks that may unnecessarily constrain callers and will just duplicate the run-time check anyway. Spend that time writing tests instead.
I recommend to use raise at the beginning of the method to add manual type checking, simple and effective:
def foo(bar)
raise TypeError, "You called foo without the bar:String needed" unless bar.is_a? String
bar.upcase
end
Best way when you don't have much parameters, also a recommendation is to use keyword arguments available on ruby 2+ if you have multiple parameters and watch for its current/future implementation details, they are improving the situation, giving the programmer a way to see if the value is nil.
plus: you can use a custom exception
class NotStringError < TypeError
def message
"be creative, use metaprogramming ;)"
#...
raise NotStringError
You can use a Design by Contract approach, with the contracts ruby gem. I find it quite nice.

Resources