How can I create a scope in a model that returns only those objects with an associated rank greater than the current_user's rank?

StackOverflow https://stackoverflow.com/questions/15470782

Question

I am trying to create a :readable scope in my Page model to return all the Pages that the current_person has sufficient 'rank' to read:

scope :readable, lambda { |current_person| joins(:role_readable)
    .where(:role_readable[:rank].gte(current_person.roles.first.rank) ) }

I've tried many scope permutations, including this one, with no success (the one above gives a "can't convert Symbol into Integer" error).

The problem is made more complex because Users (which handle authentication etc. / synonymous with accounts) have_many People, which represent the User's presence in an organization — ie. a User could be a member of multiple organizations, but only needs one way of logging in...

The Roles that a User's People has_many of, give the rank that the scope uses to return the :readable pages. (ie. Users handle authentication, whereas People handle authorization (among other things), as a User could be a member of multiple organizations for which the user has different Roles, and thus heirarchy and privilege.)

class User < ActiveRecord::Base  
  has_many :people
  has_many :roles, through: :people    

class Person < ActiveRecord::Base
  belongs_to :user
  has_and_belongs_to_many :roles

class Role < ActiveRecord::Base
  #  name       :string(255)
  #  rank       :integer 
  has_and_belongs_to_many :people

class Page < ActiveRecord::Base
  belongs_to :role_readable, class_name: "Role", foreign_key: :role_read_id
  belongs_to :role_editable, class_name: "Role", foreign_key: :role_write_id

Roles have a hierarchy (eg. Boss, Manager, Worker...) recorded as a rank (integer) — the lower the integer the greater the hierarchy (and thus privilege). If any one of the Person's Roles has a rank less than or equal to the :role_readable of a Page then the user has privilege to view the page.

Eg. a Person with a Manager role could view all pages with a :role_readable of Manager or Worker, but not Boss. I need a scope that lists all such readable pages.

Roles have a default_scope that ensures they are returned in ascending order of rank, so role.first for a Person will return the role with the greatest hierarchy (ie. lowest rank value).

I'm using Rails 3.2 and Postgres 9.2.2.0, so am also confusing myself with the many query / Arel options available.

Thanks in advance!

UPDATE:

If I try this scope:

scope :readable, lambda { |current_person| where("role_readable.rank
  => ?",current_person.roles.first.rank) }

I get this error:

PGError: ERROR:  missing FROM-clause entry for table "role_readable"
LINE 1: ...*) FROM "pages"  WHERE "pages"."team_id" = 2 AND (role_reada...
                                                         ^
: SELECT COUNT(*) FROM "pages"  WHERE "pages"."team_id" = 2 AND (role_readable.rank => 1)

I'm not sure what to make of the 'missing FROM-clause'

Was it helpful?

Solution

And the answer is quite simple, after much experimenting:

scope :readable, lambda { |current_person| joins(:role_readable)
      .where(["roles.rank >= ?", current_person.roles.first.rank]) }

Although the join refers to the association name :role_readable, the where must use the table name — ie. roles.rank not role_readable.rank.

A trap for new players? Apparently.

Kudos to ctcherry and his answer here which gave the game away.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top