Ruby Integer(), Array(), et al -- what are they? Where do they come from? - ruby-on-rails

I've come across conversions of the form Array(value), String(value), and Integer(value) on occasion. It appears to me that these are just syntactic sugar for a call to the corresponding value.to_a, value.to_s, or value.to_i methods.
So I'm wondering:
Where/how are these are defined? I can't find them in Object, Module, Class, etc
Are there any common scenarios for which it's preferable to use these rather than the corresponding/underlying to_X method?
Could these be used in type-generic coercion? That is, can I do something along the lines of
[Integer, String, Array].each {|klass| klass.do_generic_coercion(foo) }
? (...and no, I don't really want to do that; I know the type I want out, but I'm looking to avoid the case statement.)

This is a good and difficult question. Let's answer the three parts.
First part
To find the definition, it is important to realize that the name of the method is "Array", etc., which can be quite counterintuitive, since methods are usually lowercase...
irb> method(:Array)
=> #<Method: Object(Kernel)#Array>
This tells you these are defined in Kernel, and thus available everywhere without requiring an explicit prefix.
Second part
Array(), String(),... are conversion methods. Calling obj.to_a will return an array, but will raise an NoMethodError if obj doesn't respond_to? :to_a. So the typical case when you'd prefer using Array(), String(), instead of to_a or to_s is when you are not positive an object responds to a given conversion method.
String(obj) will return nil if obj doesn't respond_to? :to_s. String(obj) will also check that the result of to_s is actually a string; it should be, but maybe an overly creative programmer decided to return something else?
Most other conversion methods act the same way, but Array(obj) is different. It will return [obj] if obj doesn't respond_to? :to_a. It will actually call to_ary (which is the implicit conversion operation, while to_a is the explicit one).
There is another important way to convert objects in 1.9 (and upcoming 1.8.8): Array.try_convert(obj). This returns nil if the obj does not respond_to? :to_ary. It will not call the to_a. Although they are longer to type, you might prefer using them when writing very general code that might accept different types of objects and want to avoid converting a hash to an array by mistake, for example (since Hash has a to_a method but not to_ary). When your method requires an array-like object and you are willing to do an explicit conversion, then obj.to_a is fine. The typical use of Array(obj) would be in a method that accepts either a single obj to act on, or a list of objects (although typically this is written as [*obj]).
Last part
Hopefully, the answers to the first two parts give you your final answer...
You can use:
[Integer, String, Array].each {|klass| klass.try_convert(foo) }
or
[:Integer, :String, :Array].each{|method| send(method, obj)}

Good question! Let's see if we can figure it out.
Ross-Harveys-MacBook-Pro:ruby-1.9.1-p376 ross$ irb
irb(main):001:0> Object.ancestors
=> [Object, Kernel]
irb(main):002:0> Kernel.ancestors
=> [Kernel]
irb(main):003:0> Kernel.class
=> Module
irb(main):004:0> Kernel.public_methods.include? "Array"
=> true
So, it looks like these are methods in the Kernel module that are mixed in to Object, so they are available without specifying a receiver. We might also want to peek at the C implementation, in object.c:
VALUE
rb_Array(VALUE val)
{
VALUE tmp = rb_check_array_type(val);
if (NIL_P(tmp)) {
tmp = rb_check_convert_type(val, T_ARRAY, "Array", "to_a");
if (NIL_P(tmp)) {
return rb_ary_new3(1, val);
}
}
return tmp;
}
One thing seems easy to conclude, the default .to_a is deprecated, so it does seem like Array(x) is the canonical way to do the conversion. It apparently does nothing if given an Array, calls .to_a if that's present, and if not it just wraps its argument in an Array.
Regarding whether to_a is deprecated...well, I said "the default":
Ross-Harveys-MacBook-Pro:puppet_sd ross$ irb
irb(main):001:0> class X; X; end.new.to_a
(irb):1: warning: default `to_a' will be obsolete

They are Defined in Ruby Kernel Module, like:
Array(), Complex(), Float(), Integer(), Rational(), Stirng(), etc.
I found those method references in Dave Thomas's Pickaxe book "Progamming Ruby 1.9", page 555.
For example: Array(arg) will convert arg as an Array, following are copied from the book:
"Returns arg as an Array. First tries to call rg.to_ary, then arg.to_a. If both fail, creates a single element array containing arg( or an empty array if arg is nil)."
ex. Array(1..5) # => [1, 2, 3, 4, 5]

From what I understand, the simple version is like this:
object.to_a tries to convert 'object' to an Array using a class member function.
Array(object) tries to make a new Array using 'object'.
I can re-define what .to_a means for a given class (it is just another member after all). The Array(...) call is defined in Kernel so it behaves the same for any class. I typically use type conversions of the style Array(...) when I don't know ahead of time what type of object will be passed in. It's better at handling cases where an object doesn't know how to convert itself to an array or can't be converted to an array. If the object to be converted is the result of a long or complex expression, using the Array(...) style is often clearer. I save the .to_a form for instances when I know the class of the object and exactly what to expect from the output of .to_a (mostly instances when I have written or modified the .to_a member function myself).

Related

Condition in ruby on rails - if else

I'm not really sure what this means. This is from Ruby on Rails in controller class and been trying to figure out what the below code do.
payload = if params.key? :data
//do something
else
//else do something
end
This is in the controller class. What does the params.key? :data do?
The data variable doesn't exist in the whole class but just in this block.
:data is not a variable but a symbol.
.key? is a method and in ruby you do not need parentheses to pass a parameter such as :data.
So this bit of code asks if params has the symbol :data as a key (in a map) and uses the returning boolean for the conditional.
In this snippet data is not a variable, it is a symbol literal. Java doesn't have a direct counterpart of Ruby's symbols AFAIR, but you can think of it as of some immutable identifier (kinda "immutable string with some additional cool properties that don't matter in the context we discuss here").
Next, params represents query params and is provided by the underlying middleware. It is a Hash-like data structure where Hash is a Ruby's counterpart of Java's HashMap that maps keys to values.
Next, params.key? :data is the same as params.key?(:data) - parentheses around method's arguments are optional in Ruby in most cases, and people tend to abuse this controversial feature. It just checks if params hash(map) contains a :data key (see Hash#key?).
And finally since everything in Ruby is an expression, if... else... end has a meaningful return value (the result of the particular branch execution) that is further assigned to the payload.

What does that 2 dots mean? What is the difference between 1 and 2?

I have seen a lot of tutorials using dot, while some use 2. What is the actual meaning of this?
Example,
Array().add()
Animation()..addListener(() {})
The .. operator is dart "cascade" operator. Useful for chaining operations when you don't care about the return value.
This is also dart solution to chainable functions that always return this
It is made so that the following
final foo = Foo()
..first()
..second();
Is strictly equals to this:
final foo = Foo();
foo.first();
foo.second();
Just to be a nitpicker, .. isn't actually an operator in Dart, just part of Dart's syntactic sugar.
In addition to the mentioned use of cascades for chaining calls to functions, you can also use it to access fields on the same object.
Consider this code, taken from the Dart documentation:
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
The first method call, querySelector(), returns a selector object. The code that follows the cascade notation operates on this selector object, ignoring any subsequent values that might be returned.
For more information about cascades, check out Dart's outstanding documentation!

Is there a way to overwrite the `{ }` object?

I'm trying to make all Hashes in my program be ActiveSupport::OrderedHash.
I can override the Hash.new constructor by ::Hash = ActiveSupport::OrderedHash but {}.class is still hash.
def {} gives me a syntax error.
It was recommended that this is a duplicate of this question, but I don't think that is the case. My question isn't about subclassing Hash, it's about overwriting the default { } => Hash constructor.
Hash literal {} is hard-coded in Ruby, and you cannot change it. {} will become a Hash. However, since Ruby's class can be modified, you can remove all unnecessary methods, constants, variables from Hash, and copy everything that is in ActiveSupport::OrderedHash into Hash.
There are only very few languages which allow you to overload literals (I only know of two: Ioke and Seph). Ruby is not one of them.
See also overloading Ruby's […] Array creation shorthand, Which method is invoked by […] in Ruby?, and How to intercept the call to constructor of class Hash?.

Rails params "method" isn't a method, is it?

In the Rails documentation, the following example is given as a way to display what the server receives from a POST request:
def create
render plain: params[:article].inspect
end
In the subsequent description, the text states
The params method is the object which represents the parameters (or fields) coming in from the form. The params method returns an ActiveSupport::HashWithIndifferentAccess object.
While I understand that all methods are objects, I don't understand how it's correct to refer to the params object as a method. Specifically, the phrase "returns an ActiveSupport::HashWithIndifferentAccess object" suggests to me that there are two calls going on--what in python might look like:
params().__getitem__('article')
but I don't think that's what's actually going on.
The conversation around those lines also refers to params as a method, so I'm starting to think I must be missing something.
I'm new to Ruby, and while I understand that all methods are objects,
No, they aren't. Methods belong to objects (more precisely: methods are defined in modules, and executed in the context of objects), but they are not, by themselves, objects. (It is, however, possible to obtain a reflective proxy which represents the concept of a method by calling the method method, which returns a Method object.)
I don't understand how it's correct to refer to the params object as a method.
Because it is a method. Not an object.
What else would it be? Syntactically, it's obvious that it can only be one of three things: a keyword, a variable, or a method call.
It can't be a keyword, because Rails is just a Ruby library, and Ruby libraries can't change the syntax of the language (e.g. add or remove keywords). It can't be a variable, because in order for it to be parsed as a variable, the parser would need to have seen an assignment to it within the same block.
Ergo, the only thing it can possibly be, is a method call. You don't even need to know anything about Rails to know this. It's just basic Ruby syntax 101.
Specifically, the phrase "returns an ActiveSupport::HashWithIndifferentAccess object" suggests to me that there are two calls going on--what in python might look like:
params().__getitem__('article')
but I don't think that's what's actually going on.
That is exactly what is going on. You call the method params and then you call the method [] on the object that is returned by calling the method params.
This is in no way different from foo.bar: you call foo, then call bar on the return value of foo.
The params method is a method, returns a hash (which holds some details about parameters send to the app). Simplified it looks like this:
def fake_params
{ :controller => 'foo', :action => 'bar' }
end
You can call another method directly on the returned hash like this:
fake_params[:action] #=> 'bar'
params is a method defined in ActionController::Metal which returns the request.parameters object.
https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal.rb#L140

Saving a block as an object to a variable without defining a function in ruby

I'm looking into blocks at the moment, and they have stumped me.
I used this as an example:
class ProcExample
attr_reader :proc_class
def initialize(&block)
#stored_proc = block
#proc_class = #stored_proc.class
end
def use_proc(arg)
#stored_proc.call(arg)
end
end
eg = ProcExample.new {|t| puts t}
p eg.proc_class
p eg.use_proc("Whoooooooo")
Now I kind of (not really( understand how the block is passed into #stored_proc. I used #proc_class because I was curious what class the block object was actually stored as.
But what if I wanted to store a block in a regular variable?
E.g.:
block_object = {|param| puts param**2}
But I found that this is treated as a Hash and not a block/proc. Naturally an error arises. I've tried assigning it with an ampersand in the variable name, and at the beginning of the block but that doesn't work.
Eventually I was wondering if it was possible to call a function and replace the block with a variable containing the block.
Like so:
(1..10).each block_object
Is this possible in Ruby?
You cannot assign blocks to a variable.
Blocks aren't really objects. They are special syntax for passing code to a higher-order method. If you want a piece of executable code that you can assign to a variable, pass around and manipulate, you need to use a Proc object.
There are two kinds of Procs: lambdas and regular procs. They behave differently in two aspects: argument binding semantics and return semantics. lambdas bind arguments like methods and return returns from the lambda, just like return in a method returns from the method. Regular procs bind arguments like blocks and return returns from the enclosing method, not the proc, just like return in a block.
Regular procs can be created by passing a block to Proc.new or alternatively to Kernel#proc. Lambdas can be created by passing a block to Kernel#lambda or with the "stabby lambda" literal syntax:
lambda_object = ->param { puts param**2 }
In order to convert Procs to blocks and the other way around, Ruby has the unary prefix & modifier. This modifier is only valid in parameter lists and argument lists. When used in a parameter list, it means "wrap the block in a proc and bind it to this variable". When used in an argument list. it means "unwrap this proc into a block (and if it's not a proc already, call to_proc on it first) and pass it as a block argument".
(1..10).each(&lambda_object)
I'm surprised that you haven't already seen the unary prefix & modifier used in this way, it is actually fairly common, e.g. in something like ['1', '2'].map(&:to_s).
Another kind of object that also represents a piece of executable code is a Method object. It supports some of the same interface as Procs do, in particular #call, #to_proc, #arguments, #arity etc. There are two ways to get a Method object: either grab a method that is bound to a receiver from that receiver using the Object#method method or grab an UnboundMethod object from a class or module (e.g. using Module#instance_method) and bind it to a receiver using UnboundMethod#bind which will return a Method object.
Since Method implements to_proc, you can pass it to a method as a block using the unary prefix & modifier, e.g.:
# Warning: extremely silly example :-)
ary = []
(1..10).each(&ary.method(:<<))
ary
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
ary = []
(1..10).each(&Array.instance_method(:<<).bind(ary))
ary
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
You are looking for a proc object, I believe.
block = proc { ... }
You can use a proc or lambda. There are some subtle differences between them; and between Ruby versions. A good overview can been seen here: https://www.youtube.com/watch?v=VBC-G6hahWA given by Peter Cooper

Resources