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'
And the answer is quite simple, after much experimenting:
Although the join refers to the association name
:role_readable
, the where must use the table name — ie.roles.rank
notrole_readable.rank
.A trap for new players? Apparently.
Kudos to ctcherry and his answer here which gave the game away.