How do nested resources work? - ruby-on-rails

As the official site, I defined two models Post and Comment. Comment is nested in Post.
resources :posts do
resources :comments
end
So I can use #post.comments to access all comments a post own. In console:
$ post = Post.new()
$ post.comments.class
> Array
But:
$ post.comments.respond_to?("build")
> true
Why? An array has the method build? How did Rails do? And how can I know other hidden methods of post.comments?

Firstly, your resources calls are in your routes.rb file, which only deals with the URL parsing side of Rails. It's nothing to do with the associations between your models, which are set up using has_many and belongs_to calls in the relevant model files. Ignore the routes file for now as it's not related to the main part of your question.
With respect to associations, you'll find that post.comments is not returning you an Array, it's actually returning an ActiveRecord::Relation object. A Relation object is like an array - in fact any method you call on it which isn't relation-specific (like build) is actually passed on to an Array representation of the Relation's contents. So, when you call post.comments.length, the comments association object is calling .length on its internal array.
One of the consequences of this is that calling post.comments.class actually passes on the .class call to the Array too!
So, what can that Relation object actually do? Since the association is set up by the has_many call, you can find out in the has_many documentation. Similarly for a belongs_to association

In ruby you can add or change any method in any object. You can even add a new method to a string instance, for example:
x = "xyzzy"
x.name # => NoMethodError: undefined method `name' for "xyzzy":String
x.instance_eval do
class << self
define_method(:name) {"I got a name"}
end
end
x.class # => String
x.name # => "I got a name"
y = "other"
y.class # => String
y.name # => NoMethodError: undefined method `name' for "other":String
That build method might have been 'added' to an instance of Array returned by the comments accessor, created by a has_many macro. (As Gareth pointed, Rails complicated things a little, and the associations do not work this way. However, extending the objects may be a more 'clean' way in comparison to working as transparent proxy. Consider my example as a ruby-related, not rails)
As for the second part of your question, you may know the methods of a given object by accessing its methods function. It returns the names of all the (public) methods defined for this object. (If you want private ones, see the private_methods function.)
In my example x.methods would include also the newly defined "name" method, but y.methods will not.
There are also other functions in ruby, with which you can examine the objects. You may find them in Ruby API documentation. The API may have some (usually slight) changes in various versions of ruby. See the documentation for the version you are using.
If you want to examine the code of some method, then it may be a little problem, because the "executable code" of a function may be created by many ways: it may be a copy (alias) of another function, it may be created dynamically by using eval function, and so on.
Knowing the name of the method you may 'grep' the source code you have available, and maybe the method you want has not been created dynamically.
I see that the project ParseTree is still alive. Maybe you will find it helpful, if you really want to know the code of a function to which you do not have sources.

Related

things_add method in seeds.rb?

I'm working some legacy code right now in seeds.rb. The previous developer used a method like this to add rows to the tables:
things_holder.oldthings_add(name)
where:
oldthings.rb
belongs_to :things_holder
things_holder.rb
has_many :oldthings
I can manipulate the object they created and adjust the seeds of the models they'd created. However, when I try to do the same to a model that I created myself (newthings), I can't seem to make it work.
Instead I get:
undefined method: newthings_add
where does this things_add method come from? I don't see it in any of the oldthings.rb files
*_add is not a standard Rails / Active Record method, so it's either defined somewhere in your application, or it's provided by some other gem.
As #jvillian's commented, you may be able to things_holder.method(:oldthings_add).source_location to learn where the method is defined.
If that doesn't work, you could try passing a blatantly invalid value to the method (e.g. things_holder.oldthings_add(true)), and see where the backtrace points.

Reform to wrap a collection of records to be saved simultaneously

I have a collection of records that are related to a specific parent object. I have no need to update the parent, just want to update all the children.
I tried making a Reform::Form and simply adding the collection declaration. An example might be a developer has many projects. When the developer goes on holiday his projects are "on_hold", but some of them might still be active. So, I would like to list all the projects with a checkbox to check if they should be put on hold. I essentially want to have a simple_fields_for :projects.
Does it make sense to use Reform for this?
I have this code:
class ProjectsForm < Reform::Form
collection :projects do
property :name #perhaps I want to rename them in the same form (bear with me)
property :on_hold
end
end
This should work, but when initializing the form with a hash
#form = ProjectsForm.new({projects: #array_of_projects})
I get an error
undefined method `projects' for #<Hash:0x007fce8f2783b8>
As if the collection method is not working. I am obviously doing something stupid.
I'd really like to use Reform. I love the philosophy behind a lot of the trailblazing suit of gems. It will be great if someone can point me in the right direction here.
If it turns out this isn't a good usecase for Reform I'll find another :P
Update:
I think reform is slightly more coupled with the idea of a model than what I thought. I thought it was just a form object with properties to play nicely with form builder. I now find that your model is key. You need to initialize the Reform form with A model, or in the case of composition, a few models. However, if you pass in an array of hash reform believes this is the model and then tries to access the :projects property on the model. In my case a hash.
I have now added a
# my contrived example is getting lame
attr_accessor :projects
to the developer class and it is working. At least the generating the reform object is working.
I am still curious wether this is a good use-case for Reform.

Rails: How To Track Down A Method Called On An Array That Has No Such Method?

I'm trying to figure out something in a Rails application. We have a class Site that has themes (of class Theme) and when a site uploads a theme it can install it for a specific site. So I'm tracking down the code for that and I see it calls the method 'build' like this:
#theme = #current_site.themes.build(params[:theme])
the themes method simply returns an array of Theme objects associated with that site. So in the rails console I tried this (result included):
>> site.themes.method(:build).__file__
NameError: undefined method `build' for class `Array'
and then I tried this:
>> site.themes.respond_to? :build
=> true
So my question is twofold, how can I find out where 'build' is defined so I can understand how it works?, and can someone explain to me how a method like 'build' works in terms of it being able to be called at the end of an Array object that has no such method? Thanks!
Given your Site model has_many :themes, this is one of the association methods that automatically gets added to it by the association.
If you want some more reading material, check out the Association Basics Guide (for example, the section on has_many associations), this lists all of the methods added along with the parameters they take.
As to the exactly how this works, the .themes object is actually an AssociationProxy (source here), with an Array as its target, not an Array itself. .class returns 'Array' because the proxy itself does not respond to the .class method, and instead forwards it to its target (the Array) via method_missing. This answer gives a more detailed explanation.

Determine the class to which a method belongs in rails

Ap::Application.routes.draw do
resources :accounts
end
I want to know the class or module to which the "resources" method belongs. If i search for "resources" method in http://apidock.com/rails/ (in the search text box provided), a list of classes are appearing which has the method name "resources". Confused, with knowing the origin of the method.
Is their any command which i can use in puts to see the origin.
The question is bit of beginners level.
Thanks
Ruby is an object-oriented language. And while methods aren't objects in Ruby, you can ask Ruby to give you a Method object representing the method in question, and then you can simply tell that Method to give you its owner:
Ap::Application.routes.draw do
p method(:resources).owner
end
More enlightening than searching for resources is searching for draw, since that method must do something with the block passed in.
Indeed, we find the source code for draw, which shows that the supplied block is executed in the context of a Mapper, which includes Resources, which (finally!) defines resources
Assume that current_user is an instance of class User, you can call method function to check whether the method_name belong to class User. Example
current_user.method(:method_name).owner
User.method(:method_name).owner
Hope this help you!

Is an object in a given collection?

Let's say I have a blog application. On each Post there are Comments.
So, I have a collection of comments in "Post.comments"
Let's say I have an object called "this_comment"
Is there a magical keyword or construct in Ruby such that I can test "is 'this_comment' in 'Post.comments"?
In other words, I want to know if "this_comment" is a member of the collection "Post.comments". I could do a 'find' and get my answer, but it seems like the kind of thing that Ruby might make easy via a cool keyword like "if this_comment.in(Post.comments)"
I suppose if not, I could just write my own "in" method for "Comment" (or 'is_in' method, as I think 'in' is a reserved keyword).
Thanks!
As an array you can do
[:a, :b, :c].include?(:a)
But ActiveRecord does some cool things to keep your queries sane if you are dealing with models. Assuming comments is a named scope or association of some sort you can do:
Post.comments.exists?(this_comment.id)
named scopes and associations can have pretty much all of the Activerecord class methods called on it
How did you define the relationship between 'Post' and 'Comments'? Post has_many Comments ?
If so, try .exists?().

Resources