0

User has_many constructusers, the latter being a join table for a has_many :through relationship to Construct. For the application purposes, the boolean roles are defined in the join table (constructusers.manager, constructusers.operator, etc.), while admin is a user attribute.

So when it comes time to define the policies on the actions the following throws a no method error for 'manager', while a relationship is recognised ActiveRecord::Relation:0x000001035c4370

  def show?
    user.admin? or user.constructusers.manager?
  end

if the relationship (I assume the proper one) is correct, why is there no recognition of the boolean attribute?

As per comment below, for the simple reason that is plural. Thus filtering requires:

Constructuser.where(['construct_id = ? and user_id = ?', params[:id], current_user]).first

...which is running in the controller and impacts the view. Nonetheless, for proper Pundit handling, this needs to be factored out... still de application_controller in a before filter to set that attribute. However a before_filter :set_constructuser_manager with that find condition, with nil case handling, still has no impact when stating the policy

  def show?
    set_constructuser_manager?
  end

Update: as per comment below. Pundit class private method

def contractorconstruct 
  @contructs = Construct.where(['constructusers.user_id = ?', user]).joins(:users).all
  @contractorconstruct ||= Contractor.where(['construct_id IN (?)', @contructs]).first
end

and action rule

|| contractorconstruct?

returns no method error.

Jerome
  • 5,583
  • 3
  • 33
  • 76

1 Answers1

1

manager? will be a method on an instance of Constructuser, not on the relation. Think about what you are asking, "Is this constructusers a manager?" - it makes no sense. How would the computer know what constructuser you are talking about?

If a user has_many constructusers, in order to use manager? you need to find the instance you are concerned about. If this is in the ConstructPolicy, then you need to find the specific constructuser that links user to the construct that you are authorizing, then check if that single constructuser is manager?.

If you are in the Construct controller, you'll have something like

class ConstructsController

  before_action :set_construct

  def show
    authorize @construct
    # ...
  end

  # ...

end

In your policy then, user will be the current user and record will be @construct.

class ConstructPolicy

  def show?
    user.admin? || constructuser.manage?
  end

  private

    def constructuser 
      @constructuser ||= Constructuser.find_by(user_id: user, construct_id: record)
    end

end
Logan Serman
  • 29,447
  • 27
  • 102
  • 141
  • Correct. I need to state Constructuser.where(['construct_id = ? and user_id = ?', params[:id], current_user]).first question amended – Jerome Nov 28 '13 at 17:06
  • Is this in the Construct policy and Construct controller? – Logan Serman Nov 28 '13 at 17:10
  • Yes in both Construct policy and controller – Jerome Nov 28 '13 at 17:19
  • `undefined method 'before_action'` But that I can comment out. Having initialize(user, construct) syntaxically I've stated @constructuser ||= Constructuser.where(['construct_id = ? and user_id = ?', construct, user]). Still failing with undefined method `manager?' when calling || constructuser.manager? and the case I'm testing is not a record with nil value... – Jerome Nov 28 '13 at 17:51
  • `before_action` is the Rails 4 version of `before_filter`. `find_by` is the Rails 4 version of `where(...).first` (technically `.take`). If you are only using `where(...)`, then you will still get no method error. You need to use `.first` on the end. – Logan Serman Nov 28 '13 at 20:22
  • ah! haven't dived into rails4 yet (now I'm a wee bit wet!). The where statement was fine which allowed for proper data views. However I did not manage to factor this out to the application level, even using before_filter – Jerome Nov 29 '13 at 07:52
  • Still fiddling. Realised I was on a garden path at the application level. I've now determined that one of the filters is the presence of a record in a related join table. Tried the following private method `def contractorconstruct @contructs = Construct.where(['constructusers.user_id = ?', user]).joins(:users).all @contractorconstruct ||= Contractor.where(['construct_id IN (?)', @contructs]).first end` But declaring in one the actions `|| contractorconstruct?` still returns a no method error. Sent as edit to question. – Jerome Dec 01 '13 at 15:05
  • `contractorconstruct?` gives you a NoMethodError because its not a method. `contractorconstruct` is, which returns a Contractorconstruct instance. So you should be using `contractorconstruct.manager?` – Logan Serman Dec 01 '13 at 16:36
  • [humble] yes... I guess I got caught in some weeds. As the method aims to verify the existence of a join table record that fits the conditions, when I attempted contractorconstruct.exists? I got a no method error. I guess I've misinterpreted the documentation: "Asserts the existence of a resource, returning true if the resource is found". Whereas !contractorconstruct.nil? does the proper filtering. constractorconstruct.present? would work just as well. – Jerome Dec 02 '13 at 18:11