Best Approach to Nested (and Unested at-same-Time) Resources
I have two models like
class Plan < ActiveRecord::Base
belongs_to :profile
and
class Profile < ActiveRecord::Base
has_many :plans
And routes like: (I need)
resources :profiles do
resources :plans
end
resources :plans
So after ruby-on-rails - nested resources issue , I made this PLANS index controller like this, to NESTED and UNESTED works at the same time (the only way I have found so far):
def index
if params.has_key? :profile_id
@profile = Profile.find(params[:profile_id])
@plans = @profile.plans
else
@plans = Plan.all
end
Is there a cleaner approach to this?
I have other models in this situation, and all the actions in all controllers to behave like this is cumbersome.
source to share
You gave me an idea:
models / user.rb:
class User < ActiveRecord::Base
has_many :posts
attr_accessible :name
end
models / post.rb:
class Post < ActiveRecord::Base
belongs_to :user
attr_accessible :title, :user_id
end
Controllers / posts_controller.rb:
class PostsController < ApplicationController
belongs_to :user # creates belongs_to_user filter
# @posts = Post.all # managed by belongs_to_user filter
# GET /posts
# GET /posts.json
def index
respond_to do |format|
format.html # index.html.erb
format.json { render json: @posts }
end
end
end
And now the substance:
Controllers / application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery
def self.belongs_to(model)
# Example: model == :user
filter_method_name = :"belongs_to_#{model}_index" # :belongs_to_user_index
foreign_key = "#{model}_id" # 'user_id'
model_class = model.to_s.classify # User
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{filter_method_name} # def belongs_to_user_index
if params.has_key? :'#{foreign_key}' # if params.has_key? :user_id
instance_variable_set :"@#{model}", # instance_variable_set :"@user",
#{model_class}.find(params[:'#{foreign_key}']) # User.find(params[:user_id])
instance_variable_set :"@\#{controller_name}", # instance_variable_set :"@#{controller_name}",
@#{model}.send(controller_name.pluralize) # @user.send(controller_name.pluralize)
else # else
instance_variable_set :"@\#{controller_name}", # instance_variable_set :"@#{controller_name}",
controller_name.classify.constantize.all # controller_name.classify.constantize.all
end # end
end # end
EOV
before_filter filter_method_name, only: :index # before_filter :belongs_to_user_index, only: :index
end
end
The code is not hard to understand if you have Ruby metaprogramming concepts: it declares a before_filter, which declares instance variables, inferring names from the controller name and from the association. It is only implemented for index actions, which is a single action using a multiple version of an instance instance, but it should be easy to write a filter version for other actions.
source to share