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
Related
Either this doesn't exist or i am looking at this the wrong way.
In rspec, I want to stub a method/attribute of all the instances of a class but only if that instance follows a certain condition, for example:
the following code will stub all posts with given comments:
Post.any_instance.stub(:comments).and_return([comment1, comment2])
but I only want to stub comments if the post is published, otherwise i want a blank comments array.
Is there any way I can do something like this:
Post.any_instance.stub(:comments) do |post|
post.published ? [comment1,comment2] : []
end
I have seen solutions where you send an argument to the stubbed method and based on argument value you can return different values, but that's not the case here.
The code you've included should work fine. Stubbing with a block is documented in https://relishapp.com/rspec/rspec-mocks/v/3-3/docs/old-syntax/any-instance#block-implementation-is-passed-the-receiver-as-first-arg, although it's deprecated now in favor of the new methods described at https://relishapp.com/rspec/rspec-mocks/v/3-3/docs/working-with-legacy-code/any-instance
Consider a controller action that returns the following:
Post.includes(:comments).to_json(:include => [:comments])
Is it somehow possible to pass arguments to the to_json comments method call so that I could modify comments json representation in this place rather than doing it in Comment model?
to_json only accepts a list of options, per the documentation... If you absolutely had to, you could monkey-patch to_json to do whatever work you needed to do, then call super to execute the default to_json, but I wouldn't recommend this.
In your situation, based on what I understand you're describing, it's probably best to simply do it either on the Comment model, or as a private method on the controller.
I understand the rails params hash is available in my controller methods, but I'm trying to understand how that happens. Is it a parameter passed to the method? If so, how can we access it since controller methods don't have any declared arguments?
Its not a parameter, its actually just a 'getter' function, that is declared in https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/parameters.rb
So calls to params[:something] is really a method call which returns a hash, from which you access the http parameters.
In active_record/base.rb, module ActiveRecord you can see this code:
delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete,
:delete_all, :update, :update_all, :to => :scoped
Let's take first method, so i assumed that first method delegates to scoped method and then scoped should return the first record from database. But scoped is just the anonymous scope, how the current construction is doing its job?
At the same time, how dynamic methods work, like find_by_name, find_all_by_name_and_colour?
Thanks
According to the documentation, delegate:
Provides a delegate class method to
easily expose contained objects’
methods as your own. Pass one or more
methods (specified as symbols or
strings) and the name of the target
object via the :to option (also a
symbol or string)
So this delegates the methods in the list to the scoped method, which is defined in ActiveRecord::NamedScoped::ClassMethods, and which returns an anonymous scope.
As to why ActiveRecord does this, it is so we can continue to use familiar methods such as find while behind the scenes AR is actually calling the fancy new Arel methods. For instance, when you do
Post.find(37)
What actually gets executed is:
Post.where(primary_key.eq(37))
I'll answer to your second question. find_by_name and find_all_by_what_you_want rely on ruby's precious method_missing. Whenever a method doesn't exist, the object calls method_missing, which you can overwrite. For example, I may want to overwrite method_missing, catch all non-existing method calls, check with some regex if they start/end/contain some keywords, etc.
In your example, I'll overwrite method_missing, check if it starts by find, and if yes, split on the 'and' keyword to get an array of they attributes with which I want to do my find.
Here is a pretty good example : http://technicalpickles.com/posts/using-method_missing-and-respond_to-to-create-dynamic-methods/
First: "delegate" delegates to an object, not a method - so "scope" must be some object.
Without inspecting the source to verify and just going on my working knowledge of ActiveRecord, I'm going to assume that "scope" will be the current AR instance, unless it's being called on an association proxy.
Therefore:
User.first # "scope" will be User
User.posts.first # "scope" will be the Post collection proxy
#christianblais is correct on question #2, method_missing is handling these calls. Furthermore, Rails actually defines the missing method on first call, so that subsequent calls to it do not incur the overhead of method_missing
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).