Im trying to learn how to use Pundit with Rails 4. I have been trying to learn this for the last 2 years and am slowly making a tiny bit of progress.
I am also trying to learn how to write scopes. I'm still trying to figure out how to translate advice into plain english so that I can begin to understand.
I'm getting stuck at the intersection of the scopes pundit policies use and the general scope class that I can write in a model.
I have models for Uer, Profile and Project.
The associations are:
User
has_one :profile
Profile
belongs_to :user
has_many :projects
Project
belongs_to :profile
I am trying to write a pundit policy that allows different users to see different projects. I am writing a scoped policy, in pundit to manage this.
In my project model, I'm trying to write scope that finds all of a user's projects. In plain english, I want to search all projects for those that have a profile id that belongs to a user id that is equal to the current user.
In my pundit policy, I am trying to write this resolve method:
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
if user.has_role?(:admin)
scope.all
elsif user.id == @project.profile.user_id
scope.projects_for_user
elsif user.present?
scope.in_state(:publish)
else
Project.none
end
end
end
I have tried about 100 different ways of trying to write a scope on my project model that finds projects that belong to a current user. I know I can't use devise's current_user in the model, so I can't use it in a scope. These two are my best attempts- both are wrong.
scope :projects_for_user, -> { joins(:user_id).where('project.profile.user_id = ?', user.id) }
scope :projects_for_user, -> { where(project.profile.user_id: User.id) }
My main problem with learning this is I can't see how to deconstruct this line into different pieces.
From what I can understand the bit before the ":" is the thing you are looking for. The bit after the ":" is the instance you're using when you're running the scope. If that's right, then I'm confused about why my 2nd attempt didn't work (and also very confused about the joins statement in the 1st attempt).
If someone could help with a plain english explanation of how to write scopes, I am confident that I know what I want to look for, just desperately lost on how to write the query to find it.
Currently, when I try to use my project policy (I have tried incorporating Taryn's suggestion below - although I don't understand each component of the scope, so I'm not sure what's going on with it).
class ProjectPolicy < ApplicationPolicy
attr_reader :user, :record
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
if user.has_role?(:admin)
scope.all
elsif user.id == @project.profile.user_id
scope.projects_for_user(user)
elsif user.present?
scope.in_state(:publish)
else
Project.none
end
end
end
def index?
true
end
def show?
true
end
private
def project
record
end
Project model:
scope :projects_for_user, -> (user){ joins(:user_id).where('project.profile.user_id = ?', user.id) }
In my project controller, I have:
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy ]
before_action :authenticate_user!
def index
@projects = Project.all
authorize @projects
end
def show
@project = Project.find(params[:id])
# authorize @project
end
private
def set_project
@project = Project.find(params[:id])
authorize @project
end
When I save this and try it, it gives an error which says:
wrong number of arguments (given 2, expected 0)
This error is returned when I try to see an index of projects or a specific project (so I don't think it is to do with the scope). I don't understand which two arguments are being given to know how to go about trying to solve this problem.
ADD STACK TRACE
ArgumentError - wrong number of arguments (given 2, expected 0):
pundit (1.1.0) lib/pundit.rb:112:in `policy!'
pundit (1.1.0) lib/pundit.rb:235:in `policy'
pundit (1.1.0) lib/pundit.rb:194:in `authorize'
app/controllers/eois_controller.rb:20:in `show'
actionpack (4.2.4) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
actionpack (4.2.4) lib/abstract_controller/base.rb:198:in `process_action'
actionpack (4.2.4) lib/action_controller/metal/rendering.rb:10:in `process_action'
actionpack (4.2.4) lib/abstract_controller/callbacks.rb:20:in `block in process_action'
activesupport (4.2.4) lib/active_support/callbacks.rb:117:in `call'
activesupport (4.2.4) lib/active_support/callbacks.rb:555:in `block (2 levels) in compile'
activesupport (4.2.4) lib/active_support/callbacks.rb:505:in `call'
activesupport (4.2.4) lib/active_support/callbacks.rb:92:in `__run_callbacks__'
activesupport (4.2.4) lib/active_support/callbacks.rb:778:in `_run_process_action_callbacks'
activesupport (4.2.4) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (4.2.4) lib/abstract_controller/callbacks.rb:19:in `process_action'
actionpack (4.2.4) lib/action_controller/metal/rescue.rb:29:in `process_action'
actionpack (4.2.4) lib/action_controller/metal/instrumentation.rb:32:in `block in process_action'
activesupport (4.2.4) lib/active_support/notifications.rb:164:in `block in instrument'
activesupport (4.2.4) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
activesupport (4.2.4) lib/active_support/notifications.rb:164:in `instrument'
actionpack (4.2.4) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
actionpack (4.2.4) lib/action_controller/metal/params_wrapper.rb:250:in `process_action'
searchkick (1.3.0) lib/searchkick/logging.rb:153:in `process_action'
activerecord (4.2.4) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
actionpack (4.2.4) lib/abstract_controller/base.rb:137:in `process'
actionview (4.2.4) lib/action_view/rendering.rb:30:in `process'
actionpack (4.2.4) lib/action_controller/metal.rb:196:in `dispatch'
actionpack (4.2.4) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch'
actionpack (4.2.4) lib/action_controller/metal.rb:237:in `block in action'
actionpack (4.2.4) lib/action_dispatch/routing/route_set.rb:76:in `dispatch'
actionpack (4.2.4) lib/action_dispatch/routing/route_set.rb:45:in `serve'
actionpack (4.2.4) lib/action_dispatch/journey/router.rb:43:in `block in serve'
actionpack (4.2.4) lib/action_dispatch/journey/router.rb:30:in `serve'
actionpack (4.2.4) lib/action_dispatch/routing/route_set.rb:821:in `call'
omniauth (1.3.1) lib/omniauth/strategy.rb:186:in `call!'
omniauth (1.3.1) lib/omniauth/strategy.rb:164:in `call'
omniauth (1.3.1) lib/omniauth/strategy.rb:186:in `call!'
omniauth (1.3.1) lib/omniauth/strategy.rb:164:in `call'
omniauth (1.3.1) lib/omniauth/strategy.rb:186:in `call!'
omniauth (1.3.1) lib/omniauth/strategy.rb:164:in `call'
omniauth (1.3.1) lib/omniauth/strategy.rb:186:in `call!'
omniauth (1.3.1) lib/omniauth/strategy.rb:164:in `call'
meta_request (0.4.0) lib/meta_request/middlewares/app_request_handler.rb:13:in `call'
meta_request (0.4.0) lib/meta_request/middlewares/meta_request_handler.rb:13:in `call'
warden (1.2.6) lib/warden/manager.rb:35:in `block in call'
warden (1.2.6) lib/warden/manager.rb:34:in `call'
rack (1.6.4) lib/rack/etag.rb:24:in `call'
rack (1.6.4) lib/rack/conditionalget.rb:25:in `call'
rack (1.6.4) lib/rack/head.rb:13:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/flash.rb:260:in `call'
rack (1.6.4) lib/rack/session/abstract/id.rb:225:in `context'
rack (1.6.4) lib/rack/session/abstract/id.rb:220:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/cookies.rb:560:in `call'
activerecord (4.2.4) lib/active_record/query_cache.rb:36:in `call'
activerecord (4.2.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:653:in `call'
activerecord (4.2.4) lib/active_record/migration.rb:377:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
activesupport (4.2.4) lib/active_support/callbacks.rb:88:in `__run_callbacks__'
activesupport (4.2.4) lib/active_support/callbacks.rb:778:in `_run_call_callbacks'
activesupport (4.2.4) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (4.2.4) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/reloader.rb:73:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/remote_ip.rb:78:in `call'
better_errors (2.1.1) lib/better_errors/middleware.rb:84:in `protected_app_call'
better_errors (2.1.1) lib/better_errors/middleware.rb:79:in `better_errors_call'
better_errors (2.1.1) lib/better_errors/middleware.rb:57:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
rack-contrib (1.4.0) lib/rack/contrib/response_headers.rb:17:in `call'
meta_request (0.4.0) lib/meta_request/middlewares/headers.rb:16:in `call'
web-console (2.3.0) lib/web_console/middleware.rb:28:in `block in call'
web-console (2.3.0) lib/web_console/middleware.rb:18:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
railties (4.2.4) lib/rails/rack/logger.rb:38:in `call_app'
railties (4.2.4) lib/rails/rack/logger.rb:20:in `block in call'
activesupport (4.2.4) lib/active_support/tagged_logging.rb:68:in `block in tagged'
activesupport (4.2.4) lib/active_support/tagged_logging.rb:26:in `tagged'
activesupport (4.2.4) lib/active_support/tagged_logging.rb:68:in `tagged'
railties (4.2.4) lib/rails/rack/logger.rb:20:in `call'
request_store (1.3.1) lib/request_store/middleware.rb:9:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/request_id.rb:21:in `call'
rack (1.6.4) lib/rack/methodoverride.rb:22:in `call'
rack (1.6.4) lib/rack/runtime.rb:18:in `call'
activesupport (4.2.4) lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'
rack (1.6.4) lib/rack/lock.rb:17:in `call'
actionpack (4.2.4) lib/action_dispatch/middleware/static.rb:116:in `call'
rack (1.6.4) lib/rack/sendfile.rb:113:in `call'
skylight (0.10.6) lib/skylight/middleware.rb:61:in `call'
railties (4.2.4) lib/rails/engine.rb:518:in `call'
railties (4.2.4) lib/rails/application.rb:165:in `call'
rack (1.6.4) lib/rack/content_length.rb:15:in `call'
puma (3.4.0) lib/puma/configuration.rb:224:in `call'
puma (3.4.0) lib/puma/server.rb:569:in `handle_request'
puma (3.4.0) lib/puma/server.rb:406:in `process_client'
puma (3.4.0) lib/puma/server.rb:271:in `block in run'
puma (3.4.0) lib/puma/thread_pool.rb:114:in `block in spawn_thread'
Started POST "/__better_errors/123578515c1e4e10/variables" for ::1 at 2016-09-08 13:23:01 +1000
ANALYSIS OF STACK TRACE
The only line of this that I have written myself is the authorize @eoi line in the eois controller (inside the show action). That is a key part of using pundit. The rest of the stack trace is from things I didn't write and don't know how to change.
app/controllers/eois_controller.rb:20:in `show'
RESPONSE TO POSSIBLE DUPLICATE TAG
The other question also one I posted. They are going to different points. In this post I thought maybe I was writing the scopes wrong (I may have been). In the other post, I tried to set out the whole process to see if someone might have been able to help me understand where I was going wrong.