Problem
While understanding how to create a controller dealing with polymorphism that handles individual, independent models fine ; I’m having a hard time understand how to create such a controller that deals with models that aren’t independent, are children of other models that I also want the polymorphism controller be able to handle
Would I have to create a ‘nested’ variant of the controller or should I avoid attempting to force having to accomodate such deep nesting?
Suppose I have the following polymorphic association defined:
class Goals < ActiveRecord::Base
belongs_to :goalable, polymorphic: true
end
Now suppose I have Project
, Client
, and Persona
models with the following relationships with one another via routes (including having a have_many :goals
defined inside each model :
#would create a concern to clean up the goal resources repition
resources :projects do
resources :goals, only: [:new,:create, :edit, :update]
resources :personas do
resources :goals, only: [:new,:create, :edit, :update]
end
resources :client do
resources :goals, only: [:new,:create, :edit, :update]
end
end
Tried looking up things online with no luck; Bates from Railscasts reviews the usual steps of creating a controller dealing with polymorphism, using the find_Xable
pattern involving regular expressions, but he also doesn’t address the use case of models nested inside other models
Since I couldn’t do the usual pattern, I’ve attempted to do the following:
class GoalsController < ApplicationController
before_action :get_project
before_action :set_up_goalable
#before_action :get_existing_goal, only[:edit, :update, :destroy]
def new
@goalable.goals.new
end
def create
@goal = @goalable.goals.new(goal_params)
if @goal.save
redirect_to project_path(@project), notice: "#{@goalable.class.to_s} goal successfully added"
#for_now for get about redirecting to the @goalable path; probably can use id: nil for that.
end
def edit
@goal = @goalable.goals.find(params[:id])
end
def update
end
private
def get_persona
#get_project
@project.find.personas.friendly.find(params[:persona_id])
end
def get_client
#get_project
@project.find.personas.friendly.find(params[:client_id])
end
def goal_params
params.require(:goal).permit(:copy)
end
def get_project
@project = Project.friendly.find(params[:project_id])
end
def set_up_goalable
# hard assumption = if you know the params are project_id and persona_id or client_id exists, @goalable is either one (2)
# otherwise, get @project and see if there's another params with the x_id pattern to set to @goalable
# if that's nil, @project IS the @goalable.
if params[:project_id] && params[:persona_id]
@goalable = get_persona
elsif params[:project_id] && params[:client_id]
@goalable = get_client
else
# regular expression duty : find parameter ending with _id that's not 'project_id; otherwise @goalable = get_project
end
end
end
Note: I’m aware that famed Rubyists such as Jamis Buck would probably throw a golf club at me for trying to go deeper than one level with the current routing map; however, the only use of Persona
and Client
is with a Project
; perhaps I should rethink that.
** If I were to do it that way, I would have a if/else clause that a project_id
parameter exists to redirect differently than ‘normally’, but then a new problem of ensuring that parameter is passed in meaningfully as a hidden_field while also making sure a find_x_able pattern doesn’t pick up project_id by mistake as the model to create a goal for…**