3

I have two models like that

class Plan < ActiveRecord::Base
    belongs_to :profile

And

class Profile < ActiveRecord::Base
    has_many :plans

And routes like: (I need to)

resources :profiles do
    resources :plans
end
resources :plans

So, following up ruby-on-rails - Problem with Nested Resources, I've made my PLANS index controller like this, to works NESTED and UNESTED at same time (the only way I've found for now):

def index
  if params.has_key? :profile_id
    @profile = Profile.find(params[:profile_id])
    @plans = @profile.plans
  else
    @plans = Plan.all
end

There is a cleaner approach to this?

I have another models in this situation, and putting all actions, in all controllers to behave like this is cumbersome.

Community
  • 1
  • 1
rdlu
  • 630
  • 6
  • 10

1 Answers1

0

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 complex to understand if you have notions of Ruby metaprogramming: it declares a before_filter which declares the instance variables inferring the names from the controller name and from the association. It is implemented just for the index actions, which is the only action using the plural instance variable version, but it should be easy to write a filter version for the other actions.

mdesantis
  • 8,257
  • 4
  • 31
  • 63