Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change with_any_role and with_all_roles to return an AR relation instead of an Array #441

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
9 changes: 5 additions & 4 deletions lib/rolify/adapters/active_record/role_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,16 @@ def build_conditions(relation, args)
end

def build_query(role, resource = nil)
return [ "#{role_table}.name = ?", [ role ] ] if resource == :any
query = "((#{role_table}.name = ?) AND (#{role_table}.resource_type IS NULL) AND (#{role_table}.resource_id IS NULL))"
role = [role].flatten
return [ "#{role_table}.name IN (?)", [ role ] ] if resource == :any
query = "((#{role_table}.name IN (?)) AND (#{role_table}.resource_type IS NULL) AND (#{role_table}.resource_id IS NULL))"
values = [ role ]
if resource
query.insert(0, "(")
query += " OR ((#{role_table}.name = ?) AND (#{role_table}.resource_type = ?) AND (#{role_table}.resource_id IS NULL))"
query += " OR ((#{role_table}.name IN (?)) AND (#{role_table}.resource_type = ?) AND (#{role_table}.resource_id IS NULL))"
values << role << (resource.is_a?(Class) ? resource.to_s : resource.class.name)
if !resource.is_a? Class
query += " OR ((#{role_table}.name = ?) AND (#{role_table}.resource_type = ?) AND (#{role_table}.resource_id = ?))"
query += " OR ((#{role_table}.name IN (?)) AND (#{role_table}.resource_type = ?) AND (#{role_table}.resource_id = ?))"
values << role << resource.class.name << resource.id
end
query += ")"
Expand Down
65 changes: 41 additions & 24 deletions lib/rolify/finders.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,53 @@ def without_role(role_name, resource = nil)
end

def with_all_roles(*args)
users = []
parse_args(args, users) do |users_to_add|
users = users_to_add if users.empty?
users &= users_to_add
return [] if users.empty?
end
users
intersect_ars(parse_args(args, :split)).uniq
end

def with_any_role(*args)
users = []
parse_args(args, users) do |users_to_add|
users += users_to_add
end
users.uniq
union_ars(parse_args(args, :join)).uniq
end
end

private

def parse_args(args, users, &block)
args.each do |arg|
if arg.is_a? Hash
users_to_add = self.with_role(arg[:name], arg[:resource])
elsif arg.is_a?(String) || arg.is_a?(Symbol)
users_to_add = self.with_role(arg)
else
raise ArgumentError, "Invalid argument type: only hash or string or symbol allowed"

def parse_args(args, mode, &block)
normalize_args(args, mode).map do |arg|
self.with_role(arg[:name], arg[:resource]).tap do |users_to_add|
block.call(users_to_add) if block
end
block.call(users_to_add)
end
end
end

# In: [:a, "b", { name: :c, resource: :d }]
# Out: [{ name: [:a, "b"] }, { name: :c, resource: :d }]
def normalize_args(args, mode)
groups = args.group_by(&:class)
unless groups.keys.all? { |type| [Hash, Symbol, String].include?(type) }
raise ArgumentError, "Invalid argument type: only hash or string or symbol allowed"
end

normalized = [groups[Hash]]
sym_str_args = [groups[Symbol], groups[String]].flatten.compact
case mode
when :join
normalized += [{ name: sym_str_args }] unless sym_str_args.empty?
when :split
normalized += sym_str_args.map { |arg| { name: arg } }
end

normalized.flatten.compact
end


def intersect_ars(ars)
query = ars.map(&:to_sql).join(" INTERSECT ")
from("(#{query}) AS #{table_name}")
end

# http://stackoverflow.com/a/16868735/1202488
def union_ars(ars)
query = ars.map(&:to_sql).join(" UNION ")
from("(#{query}) AS #{table_name}")
end
end
14 changes: 12 additions & 2 deletions spec/rolify/shared_examples/shared_examples_for_finders.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
end
end
end


describe ".with_all_roles" do
it { should respond_to(:with_all_roles) }
Expand All @@ -129,6 +129,11 @@
it { subject.with_all_roles({ :name => "visitor".send(param_method), :resource => Forum.last }, { :name => "moderator".send(param_method), :resource => Group }).should eq([ root ]) }
it { subject.with_all_roles({ :name => "visitor".send(param_method), :resource => Group.first }, { :name => "moderator".send(param_method), :resource => Forum }).should eq([ modo ]) }
it { subject.with_all_roles({ :name => "visitor".send(param_method), :resource => :any }, { :name => "moderator".send(param_method), :resource => :any }).should =~ [ root, modo ] }

it 'should return an AR relation' do
subject.with_all_roles("admin".send(param_method), :staff).should be_a(ActiveRecord::Relation)
subject.with_all_roles({ :name => "visitor".send(param_method), :resource => :any }, { :name => "moderator".send(param_method), :resource => :any }).should be_a(ActiveRecord::Relation)
end
end

describe ".with_any_role" do
Expand All @@ -144,6 +149,11 @@
it { subject.with_any_role({ :name => "visitor".send(param_method), :resource => Forum.last }, { :name => "moderator".send(param_method), :resource => Group }).should =~ [ root, visitor ] }
it { subject.with_any_role({ :name => "visitor".send(param_method), :resource => Group.first }, { :name => "moderator".send(param_method), :resource => Forum }).should eq([ modo ]) }
it { subject.with_any_role({ :name => "visitor".send(param_method), :resource => :any }, { :name => "moderator".send(param_method), :resource => :any }).should =~ [ root, modo, visitor ] }

it 'should return an AR relation' do
subject.with_any_role("admin".send(param_method), :staff, { :name => "moderator".send(param_method), :resource => Group }).should be_a(ActiveRecord::Relation)
subject.with_any_role({ :name => "visitor".send(param_method), :resource => :any }, { :name => "moderator".send(param_method), :resource => :any }).should be_a(ActiveRecord::Relation)
end
end
end
end
end