I have to agree that this cast was timed correctly for me as i was looking into something that could benefit from a polymorphic relation.
the other thing which i found useful is this code from stackoverflow, which shows how to set up a has many through polymorphic table
you just saved my day! Thank your for pointing out the //= require_tree . issue. After I put in at the end of the application.js file everything started working.
hi, is there any way to encrypt on the way out / decrypt on the way in? i have sensitive info in my app that i'd like to ensure remains inaccessible to anyone who may connect to my memcache server.
With the "server side solution" you can just pass your extra params through the path helper like this and the autocomplete jquery plugin will append them to the Ajax request:
This autocomplete data source can be put in a separate generic autocomplete action instead of the index action and be introduced to the client through a view helper:
application_helper.erb
ruby
module ApplicationHelper
def autocomplete_input(f, field)
f.input "#{field}_name", data: {autocomplete_source: eval("autocomplete_#{field.to_s.tableize}_path")}
end
end
resources :categories do
collection do
get :autocomplete
end
end
resources :categoriesdo
collection do
get :autocompleteendend
This module about a generic autocomplete action can be included at the top of every resource controller based on a ActiveRecord::Base child class (you have to set the autocomplete route for each controller as shown above):
ruby
module AutocompleteAction
extend ActiveSupport::Concern
def autocomplete
render json: (
request.path.split('/')[1, 2].first.classify.constantize.
select(:name).order(:name).where("name LIKE ?", "%#{params[:term]}%").
map(&:name)
)
end
end
The association setters could be generated dynamically through meta programming.
So the following code generates special setter methods for all belongs_to and polymorphic associations when included in an ActiveRecord::Base child class.
ruby
module AssociationSetter
extend ActiveSupport::Concern
included do
columns.map(&:name).select{|c| c.match('_id')}.each do |column|
association = column.split('_id').first.classify
define_method "#{association.underscore}_name" do
self.send(association.underscore).try(:name)
end
accessible_attributes << "#{association.underscore}_name"
define_method "#{association.underscore}_name=" do |name|
return if name.blank?
association_type = association
if self.class.columns.map(&:name).include?("#{association.underscore}_type")
association_type = self.send("#{association.underscore}_type")
end
self.send("#{association.underscore}=", association_type.constantize.find_or_initialize_by_name(name))
end
end
end
end
moduleAssociationSetter
extend ActiveSupport::Concern
included do
columns.map(&:name).select{|c| c.match('_id')}.each do |column|
association = column.split('_id').first.classify
define_method "#{association.underscore}_name"doself.send(association.underscore).try(:name)
end
accessible_attributes << "#{association.underscore}_name"
define_method "#{association.underscore}_name="do |name|
returnif name.blank?
association_type = association
ifself.class.columns.map(&:name).include?("#{association.underscore}_type")
association_type = self.send("#{association.underscore}_type")
endself.send("#{association.underscore}=", association_type.constantize.find_or_initialize_by_name(name))
endendendend
P.S.: Maybe it turns out to be worth to extract in a gem some day ;-)
Just put :comment_type => :article, or :events, :photo in the routes as a parameter inside of the resources block and then setup a before filter to classify that value (params[:comment_type]).
I'm using delayed_job (Rails 3.1) on Heroku. It seems to stall a lot, and can be tricky to start workers. I'm constantly paranoid that delayed_job may have fallen asleep and jobs are piling up in the queue (this happens pretty often, actually). Anyone know of any good resources for getting delayed_job to work reliably on Heroku? I installed the 'daemons' gem, but I generally just run "heroku rake jobs: work" to get things going again.
I'm sure I'm just doing it wrong, but I can't find too much out there to help me get everything set up to run efficiently.
Products.all when called from LocationsController::Location
=> MONGODB (0ms) db_development['products'].find({:_id=>BSON::ObjectId('4fb93d02c34cb976d5000004')}).limit(-1).sort([[:_id, :asc]])
Products.all when called from Api::V1::LocationsController::Location
=> MONGODB (0ms) db_development['products'].find({:_id=>BSON::ObjectId('4fb93d02c34cb976d5000004'), :_type=>{"$in"=>["Api::V1::ProductsController::Products"]}}).limit(-1).sort([[:_id, :asc]])
How to convert / remove "Api::V1::ProductsController::Products" ?
I think it's a little confusing that you're showing two forms; a user registration form and than two extra steps in a separate form, while really all three steps could go in one form, making it a lot more sustainable.
More important I was wondering how Wicked handles file uploads?
Is there a way this can be implemented where you only define new functionality in new versions? Say for example:
I have a Users controller and a Products controller. Products has stayed the same between V1 and V2, but Users has changed. It would be nice if we could set it up so that if I call for V2 of Products, my application knows it doesn't exist and simply uses the most recent controller version instead (in this case, V1).
I would love to see some AngularJS too. It seems like the thinnest one, compared to Backbone.js and Knockout.js (although it does not provide a full server like meteor).
I think the load_commentable before filter used in this RailsCasts episode is somewhat brittle and, probably too complicated to be used as an example, especially when demonstrating this feature to new users.
I would rather go with a simpler alternative. Something like:
def load_commentable
@commentable = if params[:article_id]
Article.find(params[:article_id])
elsif params[:photo_id]
Photo.find(params[:photo_id])
elsif params[:event_id]
Event.find(params[:event_id])
end
end
Or even more verbose:
def load_commentable
if params[:article_id]
@commentable = Article.find(params[:article_id])
elsif params[:photo_id]
@commentable = Photo.find(params[:photo_id])
elsif params[:event_id]
@commentable = Event.find(params[:event_id])
end
end
I see several benefits in this kind of code:
Code is less clever, which is a good thing for controller code.
Filter code is decoupled from changes to routes. For example, if a scope or namespace was added or the path option was used to produce easier to read routes, you would not need to change filter code or tests.
Scopes can be added independently to each find call. Articles could be published and events active, without the need for aliases.
Code produces no temporary objects nor relies on string processing which, over time, add up to app execution time.
Tests are easier to follow and coverage easier to check.
I would also like to mention that, in real world, some of the most popular uses of polymorphic associations, like comments, are a bad pattern.
In this example, article comments will never be queried as event comments nor -most probably- listed together and, they even might end up having very different requirements. In Ruby, behavior can always be shared using modules.
Still, there are some valid uses for polymorphic associations but, way less than what one would think of first.
P.S. thanks so much for your work and contribution! I admit this is one of the very few cases where I don't agree with the code presented. :-)
After this Railscast I found out that internally Rails is handling the build of the associated object when needed. It seems that this behavior has in fact changed.
The only thing I needed to add is a instantiation of my join model to the action 'new'.
I have a Proposal model which can have many Product through a join model called ProductEntry.
In the action 'new' from my ProposalsController I simply added:
ruby
@proposal.product_entries << ProductEntry.new
@proposal.product_entries << ProductEntry.new
This is just to display the form field when no ProductEntry is available yet, which is always the case when I'm creating a new Proposal.
When I go and edit a Proposal, Rails automatically instantiate all ProductEntry that are associated. If I edit any of those ProductEntry (such as the quantity attribute that I have), it simply works too.
Unfortunately the official Rails api is misleading when describing how to use url_for and consequently link_to with nested routes. " If you have a nested route, such as admin_workshop_path you’ll have to call that explicitly (it’s impossible for url_for to guess that route)." This isn't the case since you could use url_for([:admin, Workshop.new]) or similar. All you need to do is provide a symbol or an object that's above the target wrapped in an array. Thanks Ryan for pointing this out.
Nice example, has anyone tried this with associated tables?
i.e. product table has a category_id
e.g. so the search will result in the searching category.name or product.name...
I don't think so. Unless you only index fields with only a few words such as title. If you want to also index content field, I think you need a dedicated searching software such as sphinx or solr.
how does one output a field as a label? I have a user id. A user cannot edit the user id. One way is to set it to disabled, but that doesn't look so nice and gives the impression that it is an editable field.
I want to use the factories.rb file exclusively. However, when I run rails generate model User ... I get a spec/factories/user.rb file. How can I configure it so that this does not happen?
Thank you and keep up the great work you're doing with this site.
hello, i was wondering if it was possible to have the drop down display something different from what the user selects? ie in stack overflow, if you type in java, it might display something like 'java x 34' because its count is 34. but when you select it, it just becomes 'java'. is it possible to do it with the jquery token input? ive been reading the documentation and it seems like it only has the :id and :name attribute. i tried doing something like...
I just made a gem for dynamically adding has_many association fields, similar to ryan's nested form gem: https://github.com/ncri/nested_form_fields
It allows arbitrarily deep nesting. You are welcome to play with it and send me feedback.
Thanks Ryan! And I picked up another text mate tip, CTRL + Shift + H (create partial from template). Actually, do you reckon that could make for a good topic? TextMate tips?
kind of off-topic, but I try to figure out how to use the route manually without using pagination. Is it pagination figuring out if it has to add the page number as get parameter or as ressource?
ruby
[...]
get "/posts/:id/page/:page", to: 'posts#show'
resources :posts
[...]
[...]
get "/posts/:id/page/:page", to:'posts#show'
resources :posts
[...]
I wasn't able to figure out how to make the link_to helper to use of the new route (in this episode it looks like pagination does the "magic"?!).
I don't like hard wired links, so perhaps someone can help me out, I already tried post_url(@post, page: 2), but that leads to the expected /posts/1?page=2 ...
hello
Why not just use a route like this?
hello
Holy crap, why have I not been using this!
thanks a lot for this, was pulling out my hair over it!
hi
I have to agree that this cast was timed correctly for me as i was looking into something that could benefit from a polymorphic relation.
the other thing which i found useful is this code from stackoverflow, which shows how to set up a has many through polymorphic table
It looks like a great way to link locales to items for me
Hey Alan,
you just saved my day! Thank your for pointing out the
//= require_tree .issue. After I put in at the end of the application.js file everything started working.Thanks again!
hi, is there any way to encrypt on the way out / decrypt on the way in? i have sensitive info in my app that i'd like to ensure remains inaccessible to anyone who may connect to my memcache server.
With the "server side solution" you can just pass your extra params through the path helper like this and the autocomplete jquery plugin will append them to the Ajax request:
_form.html.erb
<%= f.text_field :category_name, data: {autocomplete_source: categories_path(your_extra_param: 'value')} %>Great screencast. It helped me to DRY this problem a little bit up for SIMPLE model constellations as mentioned in this screencast.
Enclosed you'll just find an untested refactoring version but an older similar refactoring version has been tested successfully ;-)
So I would generally transform all tags with a data-autocomplete-source attribute into an autocomplete input.
application.js.coffee
This autocomplete data source can be put in a separate generic autocomplete action instead of the index action and be introduced to the client through a view helper:
application_helper.erb
_form.html.erb
<%= autocomplete_input(f, :category) %>routes.rb
This module about a generic autocomplete action can be included at the top of every resource controller based on a ActiveRecord::Base child class (you have to set the autocomplete route for each controller as shown above):
The association setters could be generated dynamically through meta programming.
So the following code generates special setter methods for all belongs_to and polymorphic associations when included in an ActiveRecord::Base child class.
P.S.: Maybe it turns out to be worth to extract in a gem some day ;-)
An update to this railscast to help write better cucumber scripts would be a welcome step forward.
Maybe a silly question but why the jquery-rails gem is not in the assets group?
Just put :comment_type => :article, or :events, :photo in the routes as a parameter inside of the resources block and then setup a before filter to classify that value (params[:comment_type]).
I'm using delayed_job (Rails 3.1) on Heroku. It seems to stall a lot, and can be tricky to start workers. I'm constantly paranoid that delayed_job may have fallen asleep and jobs are piling up in the queue (this happens pretty often, actually). Anyone know of any good resources for getting delayed_job to work reliably on Heroku? I installed the 'daemons' gem, but I generally just run "heroku rake jobs: work" to get things going again.
I'm sure I'm just doing it wrong, but I can't find too much out there to help me get everything set up to run efficiently.
Suggestions?
I went ahead and started a project with meteor then got stuck on a multi-table-join problem.
I documented it here:
https://github.com/meteor/meteor/issues/147
For mainly this reason, I don't think meteor is production ready, but it certainly has some good ideas.
+ 1
I think, I have the same problem as Eran Kampf
How to convert / remove "Api::V1::ProductsController::Products" ?
Adrian: look at the solution I describe above. Way less verbose than yours and more flexible.
and me!
I think it's a little confusing that you're showing two forms; a user registration form and than two extra steps in a separate form, while really all three steps could go in one form, making it a lot more sustainable.
More important I was wondering how Wicked handles file uploads?
Is there a way this can be implemented where you only define new functionality in new versions? Say for example:
I have a Users controller and a Products controller. Products has stayed the same between V1 and V2, but Users has changed. It would be nice if we could set it up so that if I call for V2 of Products, my application knows it doesn't exist and simply uses the most recent controller version instead (in this case, V1).
It would appear that the FactoryGirl syntax has changed a bit. Instead of
Factory.define :user do |f|f.sequence(:email) { |n| "foo#{n}@example.com" }
f.password "secret"
end
You now need something more like
FactoryGirl.define dofactory :user do
sequence :email do |n|
"foo#{n}@example.com"
end
password "secret"
end
end
Also then use FactoryGirl.create(:user) instead of Factory(:user)
EC2 comes with Elastic Load Balancing, it would be nice to use it instead of a web role.
Is it feasible ?
Trying to make it works with mongoid. It doesn't seems to work. Product is always empty. What am I doing wrong?
Anyone using mongoid ?
I would love to see some AngularJS too. It seems like the thinnest one, compared to Backbone.js and Knockout.js (although it does not provide a full server like meteor).
Ryan,
I think the
load_commentablebefore filter used in this RailsCasts episode is somewhat brittle and, probably too complicated to be used as an example, especially when demonstrating this feature to new users.I would rather go with a simpler alternative. Something like:
Or even more verbose:
I see several benefits in this kind of code:
Code is less clever, which is a good thing for controller code.
Filter code is decoupled from changes to routes. For example, if a
scopeornamespacewas added or thepathoption was used to produce easier to read routes, you would not need to change filter code or tests.Scopes can be added independently to each
findcall. Articles could bepublishedand eventsactive, without the need for aliases.Code produces no temporary objects nor relies on string processing which, over time, add up to app execution time.
Tests are easier to follow and coverage easier to check.
I would also like to mention that, in real world, some of the most popular uses of polymorphic associations, like comments, are a bad pattern.
In this example, article comments will never be queried as event comments nor -most probably- listed together and, they even might end up having very different requirements. In Ruby, behavior can always be shared using modules.
Still, there are some valid uses for polymorphic associations but, way less than what one would think of first.
P.S. thanks so much for your work and contribution! I admit this is one of the very few cases where I don't agree with the code presented. :-)
Excellent episode. I'm looking forward a "part 2" where you would explain how to set up your own templates!
Thanks!
After this Railscast I found out that internally Rails is handling the build of the associated object when needed. It seems that this behavior has in fact changed.
The only thing I needed to add is a instantiation of my join model to the action 'new'.
I have a Proposal model which can have many Product through a join model called ProductEntry.
In the action 'new' from my ProposalsController I simply added:
This is just to display the form field when no ProductEntry is available yet, which is always the case when I'm creating a new Proposal.
When I go and edit a Proposal, Rails automatically instantiate all ProductEntry that are associated. If I edit any of those ProductEntry (such as the quantity attribute that I have), it simply works too.
Figured it out, key was using the actual join relationship rather than the target of the join, then everything pretty much "just worked"
I won't lie though, this also helped (from a comment on the second part of the complex forms casts) : http://pastie.org/987614
What gem was it? I've been having perhaps the same problem and haven't seen any solutions.
Unfortunately the official Rails api is misleading when describing how to use url_for and consequently link_to with nested routes. " If you have a nested route, such as admin_workshop_path you’ll have to call that explicitly (it’s impossible for url_for to guess that route)." This isn't the case since you could use url_for([:admin, Workshop.new]) or similar. All you need to do is provide a symbol or an object that's above the target wrapped in an array. Thanks Ryan for pointing this out.
Nice example, has anyone tried this with associated tables?
i.e. product table has a category_id
e.g. so the search will result in the searching category.name or product.name...
Problem is this format wasn't properly supported in Rails until very recently, and I don't think the fix has actually been released yet.
https://github.com/rails/rails/issues/4127#issuecomment-4167613
I don't think so. Unless you only index fields with only a few words such as title. If you want to also index content field, I think you need a dedicated searching software such as sphinx or solr.
Me too. OS X FF10
how does one output a field as a label? I have a user id. A user cannot edit the user id. One way is to set it to disabled, but that doesn't look so nice and gives the impression that it is an editable field.
I want to use the factories.rb file exclusively. However, when I run rails generate model User ... I get a spec/factories/user.rb file. How can I configure it so that this does not happen?
Thank you and keep up the great work you're doing with this site.
I had the same issue.
I fixed the jquery syntax with this:
$(link).parent().before(content.replace(regexp, new_id));instead of the old syntax (prototype I guess)
hello, i was wondering if it was possible to have the drop down display something different from what the user selects? ie in stack overflow, if you type in java, it might display something like 'java x 34' because its count is 34. but when you select it, it just becomes 'java'. is it possible to do it with the jquery token input? ive been reading the documentation and it seems like it only has the :id and :name attribute. i tried doing something like...
tags.map{ |tag| { id: "#{tag.id}", name: "#{tag.name} x #{tag.count}", value: "#{tag.name}" } }
but no success. any ideas?
thanks = )
I think he uploaded that image to the gmail account railscasts.example@gmail.com.
thank you so much. Very helpful
Ryan: to get the type of commentable tpye and ID, I use the follow code:
and append to the nested comment resource this
, :defaults => { :commentable => 'picture' }
(when commenting on picture)
This way, the only thing you have to remember when adding more commentable is to add the default param to the route and you are done ;-)
I just made a gem for dynamically adding has_many association fields, similar to ryan's nested form gem: https://github.com/ncri/nested_form_fields
It allows arbitrarily deep nesting. You are welcome to play with it and send me feedback.
what a perfect timing for me :)
yay MeteorCasts
Thanks Ryan! And I picked up another text mate tip, CTRL + Shift + H (create partial from template). Actually, do you reckon that could make for a good topic? TextMate tips?
polymorphic has_many is much more useful. that way THE SAME photo can belong to many different models.
Hi,
I missed the as option to create the helper:
So calling
post_page_path(@post, 2)now generated the link/posts/1/page/2as expected.But would be great to know how pagination handles that (or if I missed something else in the cast).
Regards,
Hagen
Hi,
kind of off-topic, but I try to figure out how to use the route manually without using pagination. Is it pagination figuring out if it has to add the page number as get parameter or as ressource?
I wasn't able to figure out how to make the link_to helper to use of the new route (in this episode it looks like pagination does the "magic"?!).
I don't like hard wired links, so perhaps someone can help me out, I already tried
post_url(@post, page: 2), but that leads to the expected/posts/1?page=2...Regards,
Hagen