Sorry in advance for the long post. I have been working in Ruby on Rails for about 6 months now and find myself trying to apply design patterns from java/C# to problems in RoR. I feel like there are better ways to do the same things in RoR, but sadly I am just a noob. So here is one basic problem for which I am trying to find a Ruby-oriented design pattern. Any guidance would be much appreciated.
Within my app's controllers, there is a lot of 'shared' behavior. In many cases there is controller-specific initialization (ie, setting the object's user_id attribute to the current user, filtering out write-once attributes) and authorizing actions based on the object instance and the current user. So, many models and controllers look similar to this:
class Object < ActiveRecord::Base
...
def authorize_action(user, action)
[:show,:create].include?(action) || self.created_by_user_id == user.id # only the user who created can update/delete
end
def init_new(signed_in_user)
self.created_by_user_id = signed_in_user.id
if self.location_id.nil?
self.location_id = signed_in_user.default_location_id
end
end
end
ObjectController < ApplicationController
before_action :set_object, only: [:show, :edit, :update, :destroy]
before_action :authorize_action, only: [:show, :edit, :update, :destroy]
...
def new
@object = Object.new
@object.parent_id = params[:parent_id] # nested resource, assign parent_id from request url
@object.created_by_user_id = current_user.id
end
def edit
end
def create
@object = Object.new(object_create_params)
@object.parent_id = params[:parent_id] # nested resource, assign parent_id from request url
@object.created_by_user_id = current_user.id
@object.save
end
def update
@hunting_plot_user_access.update(object_update_params)
end
def destroy
@object.destroy
end
private
set_object
@object = Object.find(params[:id])
end
def object_create_params
params.require(:object).permit(:location_id, :attribute_1, :attribute_2)
end
def object_update_params
params.require(:object).permit(:attribute_1, :attribute_2)
end
def authorize_action
raise Exceptions::NotAuthorized unless @object.authorize_action?(current_user, action_name)
end
end
I'd like to move the general 'flow' of each common action into shared logic. For example, to create a new instance of any given Object, a controller should create the instance, call the init_new method on the instance (all models have this method), potentially apply controller-specific changes, authorize the action, and save the object instance.
I've to think this is a fairly common design problem out there. I have been kicking around solutions that use a combination of adding 'virtual' methods and custom callbacks to the ApplicationController class, but it feels like I am trying to fit a square peg into a round hole.
Anyone out there have any suggested articles, blog posts, etc that might address the same issue?
Inheritance is one way to go:
class BaseController < ApplicationController
before_filter :some_common_thing
def some_common_thing
# common code
end
end
class ObjectController < BaseController
# just the code that is unique to this controller
end
...with the caveat that you don't go too far - here's a good article about inheritance in ruby (although it's applicable to any OO language)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.