When having a polymorphic association in Rails model, you want to:
- generate URL
- find the parent model in the controller
See below our model structure. Let's answer those questions the Rails way.
class Company < ActiveRecord::Base
has_many :images
end
class Product < ActiveRecord::Base
has_many :images
end
class Image < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
Generating Image URL
RoR provides a simple way to generate polymorphic URLs: polymorphic_path
So polymorphic_path([parent, Image])
will generate:
company_images_path(parent) # /companies/1/images
# or
product_images_path(parent) # /products/1/images
Finding the parent model in the controller
If in Image controller, we need to have parent information in order to scope relevant data, we will use ResourceFinder
helper.
class ImagesController < ApplicationController
include ResourceFinder
def index
@resources = parentable.images
render json: @resources
end
end
and we will create a app/controllers/concerns/resource_finder.rb
helper
module ResourceFinder
def parentable
klass, param = parentable_class
return unless klass
klass.find(params[param.to_sym])
end
private
def parentable_class
params.each_key do |name|
if name.match?(/(.+)_id$/)
model = name[/([^\/.]*)_id$/, 1]
return model.classify.safe_constantize, name
end
nil
end
end
end
The parentable_class
read the param and define the class associated with the param. In our case, if we have /products/:product_id/images
this method will read the product_id
params key and will try to create a class from this model. If successful, it will return the key and the class to be used in our scope.
Happy hacking!
Top comments (1)
Is never reached, because
constantize
raises an error if no constant with the given name exists. If you want to be able to returnnil
if no constant exists, usesafe_constantize
;-)One cool thing about strings in Ruby: you can easily extract substrings via regexs like this: