In my PromoCodesController I have this code:
load_and_authorize_resource :restaurant, find_by: :permalink
load_resource :discount, through: :restaurant
load_resource :promo_code, collection: [:create], through: :discount
It should be good since in #index, it loads the collection #promo_codes and in #create it loads #promo_code.
But it does not load the collection #promo_codes in #create. Where is the problem? In the documentation it says:
:collection argument: Specify which actions are resource collection actions in addition to :index.
Thank you
It's not working because Cancancan's method load_resource (controller_resource_loader.rb) assumes
only one resource variable to be set at a time: either resource_instance or collection_instance.
Your load_resource collection: [:create] can load #promo_codes in #create action via monkey patch to CanCan::ControllerResourceLoader:
# config/initializers/cancan.rb
module CanCan
module ControllerResourceLoader
def load_resource
return if skip?(:load)
# Original condition has been split into two separate conditions
if load_instance?
self.resource_instance ||= load_resource_instance
end
if load_collection?
self.collection_instance ||= load_collection
end
end
end
end
The common way in which this patch works is a create form integrated into index action:
class TicketsController < ActionController::Base
load_and_authorize_resource collection: [:create]
def index
#ticket = Ticket.new
end
def create
if #ticket.valid?
#ticket.create_for_user! params[:message]
redirect_to ticket_path(#ticket)
else
# #tickets are also defined here
render :index
end
end
end
My code like this:
validates :user, presence: true
before_create :set_user
def set_user
self.user = current_user
end
I know before_create doesn't work but I need before action get user data.
You can use the before_action helper only on the actions you want.
before_action :set_user, only: :create
You can use an array to do this on more actions or use the except: :some_stage to exclude stages.
You can make it before_validation but to be honest I think that's the wrong solution too.
I would create whatever it is through the association.
class User
has_one :foo
has_many :bars
end
In the controller either
For has_one:
def new
#foo = current_user.build_foo
end
For has_many:
def new
#bar = current_user.bars.new
end
I have a Admin::SessionsController who extends from Admin::ApplicationController.
I'm using a before_filter except to the login page, but the server is entering in a loop
I think there`s is something with the except thing, I think that I need to set the namespace or something...
This is the line of before_filte:
before_filter :authenticate_user , :except => { :sessions => :new }
This is my SessionController
class Admin::SessionsController < Admin::ApplicationController
def new
end
end
It might be easiest just to put a skip_before_filter in the Admin:SessionsController
class Admin::SessionsController < Admin::ApplicationController
skip_before_filter :authenticate_user, :only => [:new]
def new
end
end
I want to add macro to controller like active record :has_many
class ApplicationController < ActionController::Base
macro_to_define_methods :parameters => :here
end
How can I achieve this?
Thank You.
module ControllerExtensions
def do_something(options)
# do something with options here, e.g. define_method
end
end
ActionController::Base.extend(ControllerExtensions)
class ApplicationController < ActionController::Base
do_something :parameters => :here
end
I have a number of resources (Trips, Schedules, etc) with actions that should be limited to just the resource's owner.
How do you implement code with a #require_owner method defined in ApplicationController to achieve this? Ideally, the code will look up the inheritance chain for the owner so the before_filter will work on a :comment that belongs_to :trip that belongs_to :user.
class TripsController < ApplicationController
belongs_to :member
before_filter :require_owner
...
end
I don't fully follow the description (would a comment really be owned by the trip owner?), but expanding slightly on jonnii's answer, here is an example that restricts the trip controller:
class ApplicationController < ActionController::Base
...
protected
# relies on the presence of an instance variable named after the controller
def require_owner
object = instance_variable_get("##{self.controller_name.singularize}")
unless current_user && object.is_owned_by?(current_user)
resond_to do |format|
format.html { render :text => "Not Allowed", :status => :forbidden }
end
end
end
end
class TripsController < ApplicationController
before_filter :login_required # using restful_authentication, for example
# only require these filters for actions that act on single resources
before_filter :get_trip, :only => [:show, :edit, :update, :destroy]
before_filter :require_owner, :only => [:show, :edit, :update, :destroy]
...
protected
def get_trip
#trip = Trip.find(params[:id])
end
end
Assuming the model looks like this:
class Trip < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
...
def is_owned_by?(agent)
self.owner == agent
# or, if you can safely assume the agent is always a User, you can
# avoid the additional user query:
# self.owner_id == agent.id
end
end
The login_required method (provided by or relying on an auth plugin like restful_authentication or authlogic) makes sure that the user is logged in and provides the user with a current_user method, get_trip sets the trip instance variable which is then checked in require_owner.
This same pattern can be adapted to just about any other resource, provided the model has implemented the is_owned_by? method. If you are trying to check it when the resource is a comment, then you'd be in the CommentsController:
class CommentsController < ApplicationController
before_filter :login_required # using restful_authentication, for example
before_filter :get_comment, :only => [:show, :edit, :update, :destroy]
before_filter :require_owner, :only => [:show, :edit, :update, :destroy]
...
protected
def get_comment
#comment = Comment.find(params[:id])
end
end
with a Comment model that looks like:
class Comment < ActiveRecord::Base
belongs_to :trip
# either
# delegate :is_owned_by?, :to => :trip
# or the long way:
def is_owned_by?(agent)
self.trip.is_owned_by?(agent)
end
end
Make sure to check the logs as you are doing this since association-dependent checks can balloon into a lot of queries if you aren't careful.
There's a few different ways to do this. You should definitely check out the acl9 plugin (https://github.com/be9/acl9/wiki/tutorial:-securing-a-controller).
If you decide you want to do this yourself, I'd suggest doing something like:
class Trip < ...
def owned_by?(user)
self.user == user
end
end
class Comment < ...
delegate :owned_by?, :to => :trip
end
# in your comment controller, for example
before_filter :find_comment
before_filter :require_owner
def require_owner
redirect_unless_owner_of(#commemt)
end
# in your application controller
def redirect_unless_owner_of(model)
redirect_to root_url unless model.owned_by?(current_user)
end
Forgive me if there are any syntax errors =) I hope this helps!
Acl9 is a authorization plugin. I'd give you the link, but I don't have cut and paste on my iPhone. If no one else provides the link by the time I get to a computer, I'll get it for you. Or you can google. Whichever. :)
I have only just started using it, but it has an extremely simple interface. You just have to create a roles table and a roles_user. Let me know how it goes if you decide to use it.
Or just use inherited resources:
InheritedResources also introduces another method called begin_of_association_chain. It’s mostly used when you want to create resources based on the #current_user and you have urls like “account/projects”. In such cases you have to do #current_user.projects.find or #current_user.projects.build in your actions.
You can deal with it just by doing:
class ProjectsController < InheritedResources::Base
protected
def begin_of_association_chain
#current_user
end
end