rolify 4.1.1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/activerecord.yml +27 -0
  3. data/.github/workflows/mongoid.yml +32 -0
  4. data/.hakiri.yml +1 -0
  5. data/Appraisals +42 -0
  6. data/CHANGELOG.rdoc +52 -8
  7. data/CONTRIBUTORS +11 -0
  8. data/Gemfile +6 -14
  9. data/LICENSE +1 -1
  10. data/README.md +72 -26
  11. data/Rakefile +6 -1
  12. data/gemfiles/activerecord_4.gemfile +20 -0
  13. data/gemfiles/activerecord_5.gemfile +22 -0
  14. data/gemfiles/activerecord_6.gemfile +22 -0
  15. data/gemfiles/mongoid_5.gemfile +20 -0
  16. data/gemfiles/mongoid_6.gemfile +19 -0
  17. data/gemfiles/mongoid_7.gemfile +20 -0
  18. data/lib/generators/active_record/rolify_generator.rb +45 -11
  19. data/lib/generators/active_record/templates/migration.rb +2 -3
  20. data/lib/generators/active_record/templates/model.rb +15 -0
  21. data/lib/generators/rolify/templates/initializer.rb +4 -1
  22. data/lib/rolify.rb +3 -4
  23. data/lib/rolify/adapters/active_record/resource_adapter.rb +14 -8
  24. data/lib/rolify/adapters/active_record/role_adapter.rb +40 -17
  25. data/lib/rolify/adapters/mongoid/role_adapter.rb +38 -8
  26. data/lib/rolify/configure.rb +4 -2
  27. data/lib/rolify/dynamic.rb +2 -16
  28. data/lib/rolify/finders.rb +6 -1
  29. data/lib/rolify/matchers.rb +2 -2
  30. data/lib/rolify/resource.rb +1 -1
  31. data/lib/rolify/role.rb +11 -2
  32. data/lib/rolify/version.rb +1 -1
  33. data/rolify.gemspec +15 -7
  34. data/spec/common_helper.rb +16 -0
  35. data/spec/generators/rolify/rolify_activerecord_generator_spec.rb +112 -6
  36. data/spec/generators_helper.rb +9 -2
  37. data/spec/rolify/config_spec.rb +2 -0
  38. data/spec/rolify/custom_spec.rb +1 -1
  39. data/spec/rolify/resource_spec.rb +6 -0
  40. data/spec/rolify/shared_examples/shared_examples_for_finders.rb +50 -32
  41. data/spec/rolify/shared_examples/shared_examples_for_has_role.rb +65 -0
  42. data/spec/rolify/utils_spec.rb +19 -0
  43. data/spec/spec_helper.rb +19 -6
  44. data/spec/support/adapters/active_record.rb +4 -3
  45. data/spec/support/adapters/mongoid.rb +19 -2
  46. data/spec/support/adapters/{mongoid.yml → mongoid_5.yml} +2 -2
  47. data/spec/support/adapters/mongoid_6.yml +6 -0
  48. data/spec/support/adapters/mongoid_7.yml +6 -0
  49. data/spec/support/adapters/utils/active_record.rb +12 -0
  50. data/spec/support/adapters/utils/mongoid.rb +13 -0
  51. metadata +60 -26
  52. data/.travis.yml +0 -25
  53. data/gemfiles/Gemfile.rails-3.2 +0 -27
  54. data/gemfiles/Gemfile.rails-4.0 +0 -33
  55. data/gemfiles/Gemfile.rails-4.1 +0 -37
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'bundler'
2
2
  require 'rspec/core/rake_task'
3
3
  require 'coveralls/rake/task'
4
+ require 'appraisal'
4
5
 
5
6
  Bundler::GemHelper.install_tasks
6
7
 
@@ -14,7 +15,11 @@ RSpec::Core::RakeTask.new(:rolify) do |task|
14
15
  task.pattern = 'spec/rolify/**/*_spec.rb'
15
16
  end
16
17
 
17
- task :default => [ :spec, 'coveralls:push' ]
18
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["CI"]
19
+ task :default => :appraisal
20
+ else
21
+ task :default => [ :spec, 'coveralls:push' ]
22
+ end
18
23
 
19
24
  desc 'Run all specs'
20
25
  task 'spec' do
@@ -0,0 +1,20 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.6"
6
+ gem "activerecord", "~> 4.2.11", require: "active_record"
7
+ gem "bigdecimal", "1.4.2"
8
+
9
+ group :test do
10
+ gem "codeclimate-test-reporter", require: nil
11
+ gem "coveralls", require: false
12
+ gem "database_cleaner", "~> 1.6.2"
13
+ gem "its"
14
+ gem "test-unit"
15
+ gem "byebug"
16
+ gem "pry"
17
+ gem "pry-byebug"
18
+ end
19
+
20
+ gemspec path: "../"
@@ -0,0 +1,22 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.6"
6
+ gem "activerecord", "~> 5.2.4", require: "active_record"
7
+ gem "actionpack", "~> 5.2.4"
8
+ gem "activemodel", "~> 5.2.4"
9
+ gem "railties", "~> 5.2.4"
10
+
11
+ group :test do
12
+ gem "codeclimate-test-reporter", require: nil
13
+ gem "coveralls", require: false
14
+ gem "database_cleaner", "~> 1.6.2"
15
+ gem "its"
16
+ gem "test-unit"
17
+ gem "byebug"
18
+ gem "pry"
19
+ gem "pry-byebug"
20
+ end
21
+
22
+ gemspec path: "../"
@@ -0,0 +1,22 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.4", platform: "ruby"
6
+ gem "activerecord", ">= 6.0.0", require: "active_record"
7
+ gem "actionpack", ">= 6.0.0"
8
+ gem "activemodel", ">= 6.0.0"
9
+ gem "railties", ">= 6.0.0"
10
+
11
+ group :test do
12
+ gem "codeclimate-test-reporter", require: nil
13
+ gem "coveralls", require: false
14
+ gem "database_cleaner", "~> 1.6.2"
15
+ gem "its"
16
+ gem "test-unit"
17
+ gem "byebug"
18
+ gem "pry"
19
+ gem "pry-byebug"
20
+ end
21
+
22
+ gemspec path: "../"
@@ -0,0 +1,20 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mongoid", "~> 5"
6
+ gem "bson_ext", "1.5.1"
7
+ gem "bigdecimal", "1.4.2"
8
+
9
+ group :test do
10
+ gem "codeclimate-test-reporter", require: nil
11
+ gem "coveralls", require: false
12
+ gem "database_cleaner", "~> 1.6.2"
13
+ gem "its"
14
+ gem "test-unit"
15
+ gem "byebug"
16
+ gem "pry"
17
+ gem "pry-byebug"
18
+ end
19
+
20
+ gemspec path: "../"
@@ -0,0 +1,19 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mongoid", "~> 6"
6
+ gem "bson_ext", "1.5.1"
7
+
8
+ group :test do
9
+ gem "codeclimate-test-reporter", require: nil
10
+ gem "coveralls", require: false
11
+ gem "database_cleaner", "~> 1.6.2"
12
+ gem "its"
13
+ gem "test-unit"
14
+ gem "byebug"
15
+ gem "pry"
16
+ gem "pry-byebug"
17
+ end
18
+
19
+ gemspec path: "../"
@@ -0,0 +1,20 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mongoid", "~> 7"
6
+ gem "bson_ext", "1.5.1"
7
+ gem "railties", "5.2.4.1"
8
+
9
+ group :test do
10
+ gem "codeclimate-test-reporter", require: nil
11
+ gem "coveralls", require: false
12
+ gem "database_cleaner", "~> 1.6.2"
13
+ gem "its"
14
+ gem "test-unit"
15
+ gem "byebug"
16
+ gem "pry"
17
+ gem "pry-byebug"
18
+ end
19
+
20
+ gemspec path: "../"
@@ -1,5 +1,6 @@
1
1
  require 'rails/generators/active_record'
2
2
  require 'active_support/core_ext'
3
+ require 'erb'
3
4
 
4
5
  module ActiveRecord
5
6
  module Generators
@@ -8,6 +9,13 @@ module ActiveRecord
8
9
 
9
10
  argument :user_cname, :type => :string, :default => "User", :banner => "User"
10
11
 
12
+ def ensure_user_class_defined
13
+ unless user_class_defined?
14
+ prompt_missing_user
15
+ abort
16
+ end
17
+ end
18
+
11
19
  def generate_model
12
20
  invoke "active_record:model", [ name ], :migration => false
13
21
  end
@@ -22,15 +30,18 @@ module ActiveRecord
22
30
  require "#{ENGINE_ROOT}/app/models/#{user_cname.downcase}.rb"
23
31
  end
24
32
  end
33
+
25
34
  inject_into_class(model_path, class_name, model_content)
26
35
  end
27
36
 
28
37
  def copy_rolify_migration
29
- migration_template "migration.rb", "db/migrate/rolify_create_#{table_name}.rb"
38
+ migration_template "migration.rb", "db/migrate/rolify_create_#{table_name}.rb", migration_version: migration_version
30
39
  end
31
40
 
41
+ private
42
+
32
43
  def join_table
33
- user_cname.constantize.table_name + "_" + table_name
44
+ user_class.table_name + "_" + table_name
34
45
  end
35
46
 
36
47
  def user_reference
@@ -46,18 +57,41 @@ module ActiveRecord
46
57
  end
47
58
 
48
59
  def model_content
49
- content = <<RUBY
50
- has_and_belongs_to_many :%{user_cname}, :join_table => :%{join_table}
51
- belongs_to :resource, :polymorphic => true
60
+ ERB.new(File.read(File.join(__dir__, 'templates/model.rb'))).result(binding)
61
+ end
62
+
63
+ def user_class
64
+ user_cname.constantize
65
+ end
52
66
 
53
- validates :resource_type,
54
- :inclusion => { :in => Rolify.resource_types },
55
- :allow_nil => true
67
+ def user_class_defined?
68
+ user_class
69
+ true
70
+ rescue NameError => ex
71
+ if ex.missing_name == user_cname
72
+ false
73
+ else
74
+ raise ex
75
+ end
76
+ end
77
+
78
+ def prompt_missing_user
79
+ puts <<MSG
80
+ Rolify expected a model named #{user_cname} to be defined but could not find one.
81
+ Please ensure that this model exists and is not mis-spelled and re-run the generator.
82
+ MSG
83
+ end
56
84
 
57
- scopify
58
- RUBY
59
- content % { :user_cname => user_cname.constantize.table_name, :join_table => "#{user_cname.constantize.table_name}_#{table_name}"}
85
+ def versioned_migrations?
86
+ Rails::VERSION::MAJOR >= 5
60
87
  end
88
+
89
+ def migration_version
90
+ if versioned_migrations?
91
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
92
+ end
93
+ end
94
+
61
95
  end
62
96
  end
63
97
  end
@@ -1,4 +1,4 @@
1
- class RolifyCreate<%= table_name.camelize %> < ActiveRecord::Migration
1
+ class RolifyCreate<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
3
  create_table(:<%= table_name %>) do |t|
4
4
  t.string :name
@@ -11,8 +11,7 @@ class RolifyCreate<%= table_name.camelize %> < ActiveRecord::Migration
11
11
  t.references :<%= user_reference %>
12
12
  t.references :<%= role_reference %>
13
13
  end
14
-
15
- add_index(:<%= table_name %>, :name)
14
+ <% if ActiveRecord::Base.connection.class.to_s.demodulize != 'PostgreSQLAdapter' %><%= "\n " %>add_index(:<%= table_name %>, :name)<% end %>
16
15
  add_index(:<%= table_name %>, [ :name, :resource_type, :resource_id ])
17
16
  add_index(:<%= join_table %>, [ :<%= user_reference %>_id, :<%= role_reference %>_id ])
18
17
  end
@@ -0,0 +1,15 @@
1
+ has_and_belongs_to_many :<%= user_class.table_name %>, :join_table => :<%= join_table %>
2
+ <% if Rails::VERSION::MAJOR < 5 %>
3
+ belongs_to :resource,
4
+ :polymorphic => true
5
+ <% else %>
6
+ belongs_to :resource,
7
+ :polymorphic => true,
8
+ :optional => true
9
+ <% end %>
10
+
11
+ validates :resource_type,
12
+ :inclusion => { :in => Rolify.resource_types },
13
+ :allow_nil => true
14
+
15
+ scopify
@@ -4,4 +4,7 @@ Rolify.configure<%= "(\"#{class_name.camelize.to_s}\")" if class_name != "Role"
4
4
 
5
5
  # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false
6
6
  # config.use_dynamic_shortcuts
7
- end
7
+
8
+ # Configuration to remove roles from database once the last resource is removed. Default is: true
9
+ # config.remove_role_if_empty = false
10
+ end
data/lib/rolify.rb CHANGED
@@ -25,12 +25,11 @@ module Rolify
25
25
 
26
26
  rolify_options = { :class_name => options[:role_cname].camelize }
27
27
  rolify_options.merge!({ :join_table => self.role_join_table_name }) if Rolify.orm == "active_record"
28
- rolify_options.merge!(options.reject{ |k,v| ![ :before_add, :after_add, :before_remove, :after_remove ].include? k.to_sym })
28
+ rolify_options.merge!(options.reject{ |k,v| ![ :before_add, :after_add, :before_remove, :after_remove, :inverse_of ].include? k.to_sym })
29
29
 
30
- has_and_belongs_to_many :roles, rolify_options
30
+ has_and_belongs_to_many :roles, **rolify_options
31
31
 
32
32
  self.adapter = Rolify::Adapter::Base.create("role_adapter", self.role_cname, self.name)
33
- load_dynamic_methods if Rolify.dynamic_shortcuts
34
33
 
35
34
  #use strict roles
36
35
  self.strict_rolify = true if options[:strict]
@@ -49,7 +48,7 @@ module Rolify
49
48
  self.role_cname = options[:role_cname]
50
49
  self.role_table_name = self.role_cname.tableize.gsub(/\//, "_")
51
50
 
52
- has_many association_name, resourcify_options
51
+ has_many association_name, **resourcify_options
53
52
 
54
53
  self.resource_adapter = Rolify::Adapter::Base.create("resource_adapter", self.role_cname, self.name)
55
54
  @@resource_types << self.name
@@ -17,16 +17,17 @@ module Rolify
17
17
  str << ', ' unless klass == klasses.last
18
18
  str
19
19
  end
20
- resources = relation.joins("INNER JOIN #{quote(roles_table)} ON #{quote(roles_table)}.resource_type IN (#{relations}) AND
21
- (#{quote(roles_table)}.resource_id IS NULL OR #{quote(roles_table)}.resource_id = #{quote(relation.table_name)}.#{relation.primary_key})")
22
- resources = resources.where("#{quote(roles_table)}.name IN (?) AND #{quote(roles_table)}.resource_type IN (?)", Array(role_name), klasses)
23
- resources = resources.select("#{quote(relation.table_name)}.*")
20
+
21
+ resources = relation.joins("INNER JOIN #{quote_table(roles_table)} ON #{quote_table(roles_table)}.resource_type IN (#{relations}) AND
22
+ (#{quote_table(roles_table)}.resource_id IS NULL OR #{quote_table(roles_table)}.resource_id = #{quote_table(relation.table_name)}.#{quote_column(relation.primary_key)})")
23
+ resources = resources.where("#{quote_table(roles_table)}.name IN (?) AND #{quote_table(roles_table)}.resource_type IN (?)", Array(role_name), klasses)
24
+ resources = resources.select("#{quote_table(relation.table_name)}.*")
24
25
  resources
25
26
  end
26
27
 
27
28
  def in(relation, user, role_names)
28
- roles = user.roles.where(:name => role_names).select("#{quote(role_class.table_name)}.#{role_class.primary_key}")
29
- relation.where("#{quote(role_class.table_name)}.#{role_class.primary_key} IN (?) AND ((resource_id = #{quote(relation.table_name)}.#{relation.primary_key}) OR (resource_id IS NULL))", roles)
29
+ roles = user.roles.where(:name => role_names).select("#{quote_table(role_class.table_name)}.#{quote_column(role_class.primary_key)}")
30
+ relation.where("#{quote_table(role_class.table_name)}.#{quote_column(role_class.primary_key)} IN (?) AND ((#{quote_table(role_class.table_name)}.resource_id = #{quote_table(relation.table_name)}.#{quote_column(relation.primary_key)}) OR (#{quote_table(role_class.table_name)}.resource_id IS NULL))", roles)
30
31
  end
31
32
 
32
33
  def applied_roles(relation, children)
@@ -39,14 +40,19 @@ module Rolify
39
40
 
40
41
  def all_except(resource, excluded_obj)
41
42
  prime_key = resource.primary_key.to_sym
42
- resource.where(prime_key => (resource.all - excluded_obj).map(&prime_key))
43
+ resource.where.not(prime_key => excluded_obj.pluck(prime_key))
43
44
  end
44
45
 
45
46
  private
46
47
 
47
- def quote(column)
48
+ def quote_column(column)
48
49
  ActiveRecord::Base.connection.quote_column_name column
49
50
  end
51
+
52
+ def quote_table(table)
53
+ ActiveRecord::Base.connection.quote_table_name table
54
+ end
55
+
50
56
  end
51
57
  end
52
58
  end
@@ -9,13 +9,42 @@ module Rolify
9
9
  end
10
10
 
11
11
  def where_strict(relation, args)
12
- resource = if args[:resource].is_a?(Class)
13
- {class: args[:resource].to_s, id: nil}
14
- else
15
- {class: args[:resource].class.name, id: args[:resource].id}
16
- end
12
+ wrap_conditions = relation.name != role_class.name
17
13
 
18
- relation.where(:name => args[:name], :resource_type => resource[:class], :resource_id => resource[:id])
14
+ conditions = if args[:resource].is_a?(Class)
15
+ {:resource_type => args[:resource].to_s, :resource_id => nil }
16
+ elsif args[:resource].present?
17
+ {:resource_type => args[:resource].class.name, :resource_id => args[:resource].id}
18
+ else
19
+ {}
20
+ end
21
+
22
+ conditions.merge!(:name => args[:name])
23
+ conditions = wrap_conditions ? { role_table => conditions } : conditions
24
+
25
+ relation.where(conditions)
26
+ end
27
+
28
+ def find_cached(relation, args)
29
+ resource_id = (args[:resource].nil? || args[:resource].is_a?(Class) || args[:resource] == :any) ? nil : args[:resource].id
30
+ resource_type = args[:resource].is_a?(Class) ? args[:resource].to_s : args[:resource].class.name
31
+
32
+ return relation.find_all { |role| role.name == args[:name].to_s } if args[:resource] == :any
33
+
34
+ relation.find_all do |role|
35
+ (role.name == args[:name].to_s && role.resource_type == nil && role.resource_id == nil) ||
36
+ (role.name == args[:name].to_s && role.resource_type == resource_type && role.resource_id == nil) ||
37
+ (role.name == args[:name].to_s && role.resource_type == resource_type && role.resource_id == resource_id)
38
+ end
39
+ end
40
+
41
+ def find_cached_strict(relation, args)
42
+ resource_id = (args[:resource].nil? || args[:resource].is_a?(Class)) ? nil : args[:resource].id
43
+ resource_type = args[:resource].is_a?(Class) ? args[:resource].to_s : args[:resource].class.name
44
+
45
+ relation.find_all do |role|
46
+ role.resource_id == resource_id && role.resource_type == resource_type && role.name == args[:name].to_s
47
+ end
19
48
  end
20
49
 
21
50
  def find_or_create_by(role_name, resource_type = nil, resource_id = nil)
@@ -23,7 +52,7 @@ module Rolify
23
52
  end
24
53
 
25
54
  def add(relation, role)
26
- relation.role_ids |= [role.id]
55
+ relation.roles << role unless relation.roles.include?(role)
27
56
  end
28
57
 
29
58
  def remove(relation, role_name, resource = nil)
@@ -44,20 +73,14 @@ module Rolify
44
73
  relation.where("#{column} IS NOT NULL")
45
74
  end
46
75
 
47
- def scope(relation, conditions)
48
- if Rails.version < "4.0"
49
- query = relation.scoped
50
- else
51
- query = relation.all
52
- end
53
- query = query.joins(:roles)
54
- query = where(query, conditions)
76
+ def scope(relation, conditions, strict)
77
+ query = relation.joins(:roles)
78
+ query = strict ? where_strict(query, conditions) : where(query, conditions)
55
79
  query
56
80
  end
57
81
 
58
82
  def all_except(user, excluded_obj)
59
- prime_key = user.primary_key.to_sym
60
- user.where(prime_key => (user.all - excluded_obj).map(&prime_key))
83
+ user.where.not(user.primary_key => excluded_obj)
61
84
  end
62
85
 
63
86
  private
@@ -9,13 +9,42 @@ module Rolify
9
9
  end
10
10
 
11
11
  def where_strict(relation, args)
12
- resource = if args[:resource].is_a?(Class)
13
- {class: args[:resource].to_s, id: nil}
14
- else
15
- {class: args[:resource].class.name, id: args[:resource].id}
16
- end
12
+ wrap_conditions = relation.name != role_class.name
17
13
 
18
- relation.where(:name => args[:name], :resource_type => resource[:class], :resource_id => resource[:id])
14
+ conditions = if args[:resource].is_a?(Class)
15
+ {:resource_type => args[:resource].to_s, :resource_id => nil }
16
+ elsif args[:resource].present?
17
+ {:resource_type => args[:resource].class.name, :resource_id => args[:resource].id}
18
+ else
19
+ {}
20
+ end
21
+
22
+ conditions.merge!(:name => args[:name])
23
+ conditions = wrap_conditions ? { role_table => conditions } : conditions
24
+
25
+ relation.where(conditions)
26
+ end
27
+
28
+ def find_cached(relation, args)
29
+ resource_id = (args[:resource].nil? || args[:resource].is_a?(Class) || args[:resource] == :any) ? nil : args[:resource].id
30
+ resource_type = args[:resource].is_a?(Class) ? args[:resource].to_s : args[:resource].class.name
31
+
32
+ return relation.find_all { |role| role.name == args[:name].to_s } if args[:resource] == :any
33
+
34
+ relation.find_all do |role|
35
+ (role.name == args[:name].to_s && role.resource_type == nil && role.resource_id == nil) ||
36
+ (role.name == args[:name].to_s && role.resource_type == resource_type && role.resource_id == nil) ||
37
+ (role.name == args[:name].to_s && role.resource_type == resource_type && role.resource_id == resource_id)
38
+ end
39
+ end
40
+
41
+ def find_cached_strict(relation, args)
42
+ resource_id = (args[:resource].nil? || args[:resource].is_a?(Class)) ? nil : args[:resource].id
43
+ resource_type = args[:resource].is_a?(Class) ? args[:resource].to_s : args[:resource].class.name
44
+
45
+ relation.find_all do |role|
46
+ role.resource_id == resource_id && role.resource_type == resource_type && role.name == args[:name].to_s
47
+ end
19
48
  end
20
49
 
21
50
  def find_or_create_by(role_name, resource_type = nil, resource_id = nil)
@@ -61,8 +90,9 @@ module Rolify
61
90
  relation.where(column.to_sym.ne => nil)
62
91
  end
63
92
 
64
- def scope(relation, conditions)
65
- roles = where(role_class, conditions).map { |role| role.id }
93
+ def scope(relation, conditions, strict)
94
+ query = strict ? where_strict(role_class, conditions) : where(role_class, conditions)
95
+ roles = query.map { |role| role.id }
66
96
  return [] if roles.size.zero?
67
97
  query = relation.any_in(:role_ids => roles)
68
98
  query