slow_your_roles 2.0.2
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +73 -0
- data/README.md +223 -0
- data/Rakefile +15 -0
- data/init.rb +3 -0
- data/lib/generators/active_record/slow_your_roles_generator.rb +55 -0
- data/lib/generators/active_record/templates/migration_bitmask.rb.erb +12 -0
- data/lib/generators/active_record/templates/migration_serialize.rb.erb +12 -0
- data/lib/generators/active_record/templates/model.rb.erb +5 -0
- data/lib/generators/install_generator_helpers.rb +27 -0
- data/lib/generators/slow_your_roles/slow_your_roles_generator.rb +26 -0
- data/lib/generators/templates/README +9 -0
- data/lib/methods/bitmask.rb +78 -0
- data/lib/methods/serialize.rb +108 -0
- data/lib/slow_your_roles.rb +63 -0
- data/slow_your_roles.gemspec +37 -0
- data/spec/methods/bitmask_spec.rb +261 -0
- data/spec/methods/serialize_spec.rb +321 -0
- data/spec/spec_helper.rb +67 -0
- metadata +152 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SlowYourRoles
|
4
|
+
module Generators
|
5
|
+
# Generator class to add SlowYourRoles to an ActiveRecord model.
|
6
|
+
class SlowYourRolesGenerator < Rails::Generators::Base
|
7
|
+
namespace 'slow_your_roles'
|
8
|
+
|
9
|
+
argument :role_col, type: :string, required: false, default: 'roles', banner: 'role column'
|
10
|
+
|
11
|
+
class_option :use_bitmask_method, type: :boolean, required: false, default: false,
|
12
|
+
desc: 'Setup migration for Bitmask method'
|
13
|
+
|
14
|
+
desc 'Create ActiveRecord migration for slow_your_roles on NAME model using \
|
15
|
+
[ROLE] column -- defaults to \'roles\''
|
16
|
+
|
17
|
+
source_root File.expand_path('../templates', __dir__)
|
18
|
+
|
19
|
+
hook_for :orm
|
20
|
+
|
21
|
+
def show_readme
|
22
|
+
readme 'README' if behavior == :invoke
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SlowYourRoles
|
4
|
+
# Bitmask support
|
5
|
+
class Bitmask
|
6
|
+
def initialize(base, column_name, _options)
|
7
|
+
base.send :define_method, :_roles= do |roles|
|
8
|
+
states = base.const_get(column_name.upcase.to_sym)
|
9
|
+
self[column_name.to_sym] = (roles & states).map { |r| 2**states.index(r) }.sum
|
10
|
+
end
|
11
|
+
|
12
|
+
base.send :define_method, :_roles do
|
13
|
+
states = base.const_get(column_name.upcase.to_sym)
|
14
|
+
masked_integer = self[column_name.to_sym] || 0
|
15
|
+
states.reject.with_index { |_r, i| masked_integer[i].zero? }
|
16
|
+
end
|
17
|
+
|
18
|
+
base.send :define_method, :has_role? do |role|
|
19
|
+
_roles.include?(role)
|
20
|
+
end
|
21
|
+
|
22
|
+
base.send :define_method, :add_role do |*roles|
|
23
|
+
roles.each do |role|
|
24
|
+
self._roles = _roles.push(role).uniq
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
base.send :define_method, :add_role! do |*roles|
|
29
|
+
roles.each do |role|
|
30
|
+
add_role(role)
|
31
|
+
end
|
32
|
+
save!
|
33
|
+
end
|
34
|
+
|
35
|
+
base.send :define_method, :remove_role do |role|
|
36
|
+
new_roles = _roles
|
37
|
+
new_roles.delete(role)
|
38
|
+
self._roles = new_roles
|
39
|
+
end
|
40
|
+
|
41
|
+
base.send :define_method, :remove_role! do |role|
|
42
|
+
remove_role(role)
|
43
|
+
save!
|
44
|
+
end
|
45
|
+
|
46
|
+
base.send :define_method, :clear_roles do
|
47
|
+
self[column_name.to_sym] = 0
|
48
|
+
end
|
49
|
+
|
50
|
+
base.send :define_method, :clear_roles! do
|
51
|
+
clear_roles
|
52
|
+
save!
|
53
|
+
end
|
54
|
+
|
55
|
+
base.class_eval do
|
56
|
+
alias_method :add_roles, :add_role
|
57
|
+
alias_method :add_roles!, :add_role
|
58
|
+
|
59
|
+
scope :with_role, (proc { |role|
|
60
|
+
states = base.const_get(column_name.upcase.to_sym)
|
61
|
+
raise ArgumentError unless states.include? role
|
62
|
+
|
63
|
+
role_bit_index = states.index(role)
|
64
|
+
valid_mask_integers = (0..2**states.count - 1).select { |i| i[role_bit_index] == 1 }
|
65
|
+
where(column_name => valid_mask_integers)
|
66
|
+
})
|
67
|
+
scope :without_role, (proc { |role|
|
68
|
+
states = base.const_get(column_name.upcase.to_sym)
|
69
|
+
raise ArgumentError unless states.include? role
|
70
|
+
|
71
|
+
role_bit_index = states.index(role)
|
72
|
+
valid_mask_integers = (0..2**states.count - 1).reject { |i| i[role_bit_index] == 1 }
|
73
|
+
where(column_name => valid_mask_integers)
|
74
|
+
})
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SlowYourRoles
|
4
|
+
# Serialize support
|
5
|
+
class Serialize
|
6
|
+
def initialize(base, column_name, _options)
|
7
|
+
base.serialize column_name.to_sym, Array
|
8
|
+
base.before_validation(:make_default_roles, on: :create)
|
9
|
+
base.send :define_method, :has_role? do |role|
|
10
|
+
self[column_name.to_sym].include?(role)
|
11
|
+
end
|
12
|
+
|
13
|
+
base.send :define_method, :add_role do |*roles|
|
14
|
+
clear_roles if self[column_name.to_sym].blank?
|
15
|
+
|
16
|
+
roles.each do |role|
|
17
|
+
return false if !roles_marker.empty? && role.include?(roles_marker)
|
18
|
+
end
|
19
|
+
|
20
|
+
roles.each do |role|
|
21
|
+
next if has_role?(role)
|
22
|
+
|
23
|
+
self[column_name.to_sym] << role
|
24
|
+
end
|
25
|
+
|
26
|
+
self[column_name.to_sym]
|
27
|
+
end
|
28
|
+
|
29
|
+
base.send :define_method, :add_role! do |role|
|
30
|
+
return false unless add_role(role)
|
31
|
+
|
32
|
+
save!
|
33
|
+
end
|
34
|
+
|
35
|
+
base.send :define_method, :remove_role do |role|
|
36
|
+
self[column_name.to_sym].delete(role)
|
37
|
+
end
|
38
|
+
|
39
|
+
base.send :define_method, :remove_role! do |role|
|
40
|
+
remove_role(role)
|
41
|
+
save!
|
42
|
+
end
|
43
|
+
|
44
|
+
base.send :define_method, :clear_roles do
|
45
|
+
self[column_name.to_sym] = []
|
46
|
+
end
|
47
|
+
|
48
|
+
base.send :define_method, :make_default_roles do
|
49
|
+
clear_roles if self[column_name.to_sym].blank?
|
50
|
+
end
|
51
|
+
|
52
|
+
base.send :private, :make_default_roles
|
53
|
+
|
54
|
+
# Scopes:
|
55
|
+
# ---------
|
56
|
+
# For security, wrapping markers must be included in the LIKE search,
|
57
|
+
# otherwise a user with role 'administrator' would erroneously be included
|
58
|
+
# in `User.with_scope('admin')`.
|
59
|
+
#
|
60
|
+
# Rails uses YAML for serialization, so the markers are newlines.
|
61
|
+
# Unfortunately, sqlite can't match newlines reliably, and it doesn't
|
62
|
+
# natively support REGEXP. Therefore, hooks are currently being used to
|
63
|
+
# wrap roles in '!' markers when talking to the database. This is hacky,
|
64
|
+
# but unavoidable. The implication is that, for security, it must be
|
65
|
+
# actively enforced that role names cannot include the '!' character.
|
66
|
+
#
|
67
|
+
# An alternative would be to use JSON instead of YAML to serialize the
|
68
|
+
# data, but I've wrestled countless SerializationTypeMismatch errors
|
69
|
+
# trying to accomplish this, in vain. The real problem, of course, is even
|
70
|
+
# trying to query serialized data. I'm unsure how well this would work in
|
71
|
+
# different ruby versions or implementations, which may handle object
|
72
|
+
# dumping differently. Bitmasking seems to be a more reliable strategy.
|
73
|
+
|
74
|
+
base.class_eval do
|
75
|
+
alias_method :add_roles, :add_role
|
76
|
+
alias_method :add_roles!, :add_role
|
77
|
+
|
78
|
+
cattr_accessor :roles_marker
|
79
|
+
cattr_accessor :column
|
80
|
+
|
81
|
+
self.roles_marker = '!'
|
82
|
+
self.column = "#{table_name}.#{column_name}"
|
83
|
+
|
84
|
+
scope :with_role, (proc { |r|
|
85
|
+
where("#{column} LIKE '%#{roles_marker}#{r}#{roles_marker}%'")
|
86
|
+
})
|
87
|
+
|
88
|
+
scope :without_role, (proc { |r|
|
89
|
+
where("#{column} NOT LIKE '%#{roles_marker}#{r}#{roles_marker}%' OR #{column} IS NULL")
|
90
|
+
})
|
91
|
+
|
92
|
+
define_method :add_role_markers do
|
93
|
+
self[column_name.to_sym].map! { |r| [roles_marker, r, roles_marker].join }
|
94
|
+
end
|
95
|
+
|
96
|
+
define_method :strip_role_markers do
|
97
|
+
self[column_name.to_sym].map! { |r| r.gsub(roles_marker, '') }
|
98
|
+
end
|
99
|
+
|
100
|
+
private :add_role_markers, :strip_role_markers
|
101
|
+
before_save :add_role_markers
|
102
|
+
after_save :strip_role_markers
|
103
|
+
after_rollback :strip_role_markers
|
104
|
+
after_find :strip_role_markers
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
|
5
|
+
# Include this module in your ActiveRecord model for role support.
|
6
|
+
module SlowYourRoles
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do |base|
|
10
|
+
base.send :alias_method, :method_missing_without_roles, :method_missing
|
11
|
+
base.send :alias_method, :method_missing, :method_missing_with_roles
|
12
|
+
|
13
|
+
base.send :alias_method, :respond_to_without_roles?, :respond_to?
|
14
|
+
base.send :alias_method, :respond_to?, :respond_to_with_roles?
|
15
|
+
end
|
16
|
+
|
17
|
+
ALLOWED_METHODS = %i[serialize bitmask].freeze
|
18
|
+
|
19
|
+
ALLOWED_METHODS.each do |method|
|
20
|
+
autoload method.to_s.capitalize.to_sym, "methods/#{method}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Adding slow_your_roles to the model class.
|
24
|
+
module ClassMethods
|
25
|
+
def slow_your_roles(name, options = { method: :serialize })
|
26
|
+
begin
|
27
|
+
raise NameError unless ALLOWED_METHODS.include? options[:method]
|
28
|
+
rescue NameError
|
29
|
+
puts '[Slow Your Roles] Storage method does not exist reverting to Serialize'
|
30
|
+
options[:method] = :serialize
|
31
|
+
end
|
32
|
+
"SlowYourRoles::#{options[:method].to_s.camelize}".constantize.new(self, name, options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing_with_roles(method_id, *args, &block)
|
37
|
+
match = method_id.to_s.match(/^is_(\w+)[?]$/)
|
38
|
+
if match && respond_to?('has_role?')
|
39
|
+
self.class.send(:define_method, "is_#{match[1]}?") do
|
40
|
+
send :has_role?, (match[1]).to_s
|
41
|
+
end
|
42
|
+
send "is_#{match[1]}?"
|
43
|
+
else
|
44
|
+
method_missing_without_roles(method_id, *args, &block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def respond_to_with_roles?(method_id, _include_private = false)
|
49
|
+
match = method_id.to_s.match(/^is_(\w+)[?]$/)
|
50
|
+
if match && respond_to?('has_role?')
|
51
|
+
true
|
52
|
+
else
|
53
|
+
respond_to_without_roles?(method_id, false)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module ActiveRecord
|
59
|
+
# Include SlowYourRoles in ActiveRecord::Base
|
60
|
+
class Base
|
61
|
+
include SlowYourRoles
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'slow_your_roles'
|
5
|
+
s.version = '2.0.2'
|
6
|
+
s.authors = ['Aaron A']
|
7
|
+
s.date = '2020-04-21'
|
8
|
+
s.summary = 'Easy role authorization in Rails'
|
9
|
+
s.description = 'Easy role authorization in Rails ported from an unmaintained library'
|
10
|
+
s.email = '_aaron@tutanota.com'
|
11
|
+
s.extra_rdoc_files = ['CHANGELOG.md', 'README.md'] + Dir['lib/**/*']
|
12
|
+
s.files = [
|
13
|
+
'CHANGELOG.md',
|
14
|
+
'Gemfile',
|
15
|
+
'Gemfile.lock',
|
16
|
+
'README.md',
|
17
|
+
'Rakefile',
|
18
|
+
'slow_your_roles.gemspec',
|
19
|
+
'init.rb'] + Dir['lib/**/*']
|
20
|
+
s.test_files = Dir['spec/**/*']
|
21
|
+
s.homepage = 'http://github.com/aarona/slow_your_roles'
|
22
|
+
s.rdoc_options = [
|
23
|
+
'--line-numbers',
|
24
|
+
'--inline-source',
|
25
|
+
'--title',
|
26
|
+
'Slow_your_roles',
|
27
|
+
'--main',
|
28
|
+
'README.md'
|
29
|
+
]
|
30
|
+
s.require_paths = ['lib']
|
31
|
+
|
32
|
+
s.add_runtime_dependency('activesupport', ['>= 6.0.3.1'])
|
33
|
+
s.add_development_dependency('activerecord', ['>= 6.0.3.1'])
|
34
|
+
s.add_development_dependency('rspec', ['>= 0'])
|
35
|
+
s.add_development_dependency('rubocop', ['>= 0'])
|
36
|
+
s.add_development_dependency('sqlite3', ['>= 0'])
|
37
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe SlowYourRoles do
|
6
|
+
describe 'bitmask method' do
|
7
|
+
it 'should allow me to set a users role' do
|
8
|
+
user = BitmaskUser.new
|
9
|
+
user.add_role 'admin'
|
10
|
+
expect(user._roles). to include 'admin'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should allow me to set multiple roles at one time' do
|
14
|
+
user = BitmaskUser.new
|
15
|
+
user.add_roles 'admin', 'manager'
|
16
|
+
expect(user._roles).to include 'admin'
|
17
|
+
expect(user._roles).to include 'manager'
|
18
|
+
expect(user._roles.length).to eq 2
|
19
|
+
user.add_roles 'admin', 'manager','user'
|
20
|
+
expect(user._roles).to include 'user'
|
21
|
+
expect(user._roles.length).to eq 3
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should return true for is_admin? if the admin role is added to the user' do
|
25
|
+
user = BitmaskUser.new
|
26
|
+
user.add_role 'admin'
|
27
|
+
expect(user.is_admin?).to eq(true)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return true for has_role? 'admin' if the admin role is added to the user" do
|
31
|
+
user = BitmaskUser.new
|
32
|
+
user.add_role 'admin'
|
33
|
+
expect(user.has_role?('admin')).to eq(true)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should turn false for has_role? 'manager' if manager role is not added to the user" do
|
37
|
+
user = BitmaskUser.new
|
38
|
+
expect(user.has_role?('manager')).to eq(false)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should turn false for is_manager? if manager role is not added to the user' do
|
42
|
+
user = BitmaskUser.new
|
43
|
+
expect(user.is_manager?).to eq(false)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should not allow you to add a role not in the array list of roles' do
|
47
|
+
user = BitmaskUser.new
|
48
|
+
user.add_role 'lolcat'
|
49
|
+
expect(user.is_lolcat?).to eq(false)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should only add valid roles when adding multiple roles' do
|
53
|
+
user = BitmaskUser.new
|
54
|
+
user.add_roles 'admin', 'manager', 'lolcat'
|
55
|
+
expect(user._roles.length).to eq 2
|
56
|
+
expect(user._roles).to include 'admin'
|
57
|
+
expect(user._roles).to include 'manager'
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'normal methods' do
|
61
|
+
it 'should not save to the database if not implicitly saved' do
|
62
|
+
user = BitmaskUser.create(name: 'Ryan')
|
63
|
+
user.add_role 'admin'
|
64
|
+
expect(user.is_admin?).to eq(true)
|
65
|
+
user.reload
|
66
|
+
|
67
|
+
expect(user.is_admin?).to eq(false)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should save to the database if implicity saved' do
|
71
|
+
user = BitmaskUser.create(name: 'Ryan')
|
72
|
+
user.add_role 'admin'
|
73
|
+
expect(user.is_admin?).to eq(true)
|
74
|
+
user.save
|
75
|
+
user.reload
|
76
|
+
|
77
|
+
expect(user.is_admin?).to eq(true)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should clear all roles and not save if not implicitly saved' do
|
81
|
+
user = BitmaskUser.create(name: 'Ryan')
|
82
|
+
user.add_role 'admin'
|
83
|
+
expect(user.is_admin?).to eq(true)
|
84
|
+
user.save
|
85
|
+
user.reload
|
86
|
+
expect(user.is_admin?).to eq(true)
|
87
|
+
|
88
|
+
user.clear_roles
|
89
|
+
expect(user.is_admin?).to eq(false)
|
90
|
+
user.reload
|
91
|
+
|
92
|
+
expect(user.is_admin?).to eq(true)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should clear all roles and save if implicitly saved' do
|
96
|
+
user = BitmaskUser.create(name: 'Ryan')
|
97
|
+
user.add_role 'admin'
|
98
|
+
expect(user.is_admin?).to eq(true)
|
99
|
+
user.save
|
100
|
+
user.reload
|
101
|
+
expect(user.is_admin?).to eq(true)
|
102
|
+
|
103
|
+
user.clear_roles
|
104
|
+
expect(user.is_admin?).to eq(false)
|
105
|
+
user.save
|
106
|
+
user.reload
|
107
|
+
|
108
|
+
expect(user.is_admin?).to eq(false)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should remove a role and not save unless implicitly saved' do
|
112
|
+
user = BitmaskUser.create(name: 'Ryan')
|
113
|
+
user.add_role 'admin'
|
114
|
+
expect(user.is_admin?).to eq(true)
|
115
|
+
user.save
|
116
|
+
user.reload
|
117
|
+
|
118
|
+
expect(user.is_admin?).to eq(true)
|
119
|
+
user.remove_role 'admin'
|
120
|
+
expect(user.is_admin?).to eq(false)
|
121
|
+
user.reload
|
122
|
+
|
123
|
+
expect(user.is_admin?).to eq(true)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should remove a role and save if implicitly saved' do
|
127
|
+
user = BitmaskUser.create(name: 'Ryan')
|
128
|
+
user.add_role 'admin'
|
129
|
+
expect(user.is_admin?).to eq(true)
|
130
|
+
user.save
|
131
|
+
user.reload
|
132
|
+
|
133
|
+
expect(user.is_admin?).to eq(true)
|
134
|
+
user.remove_role 'admin'
|
135
|
+
expect(user.is_admin?).to eq(false)
|
136
|
+
user.save
|
137
|
+
user.reload
|
138
|
+
|
139
|
+
expect(user.is_admin?).to eq(false)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe 'bang method' do
|
144
|
+
it 'should save to the database if the bang method is used' do
|
145
|
+
user = BitmaskUser.create(name: 'Ryan')
|
146
|
+
user.add_role! 'admin'
|
147
|
+
expect(user.is_admin?).to eq(true)
|
148
|
+
user.reload
|
149
|
+
|
150
|
+
expect(user.is_admin?).to eq(true)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should allow me to set multiple roles at one time' do
|
154
|
+
user = BitmaskUser.new
|
155
|
+
user.add_roles! 'admin', 'manager'
|
156
|
+
expect(user._roles).to include 'admin'
|
157
|
+
expect(user._roles).to include 'manager'
|
158
|
+
expect(user._roles.length).to eq 2
|
159
|
+
user.add_roles! 'admin', 'manager','user'
|
160
|
+
expect(user._roles).to include 'user'
|
161
|
+
expect(user._roles.length).to eq 3
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should remove a role and save' do
|
165
|
+
user = BitmaskUser.create(name: 'Ryan')
|
166
|
+
user.add_role 'admin'
|
167
|
+
expect(user.is_admin?).to eq(true)
|
168
|
+
user.save
|
169
|
+
user.reload
|
170
|
+
|
171
|
+
expect(user.is_admin?).to eq(true)
|
172
|
+
user.remove_role! 'admin'
|
173
|
+
expect(user.is_admin?).to eq(false)
|
174
|
+
user.reload
|
175
|
+
|
176
|
+
expect(user.is_admin?).to eq(false)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should clear all roles and save' do
|
180
|
+
user = BitmaskUser.create(name: 'Ryan')
|
181
|
+
user.add_role 'admin'
|
182
|
+
expect(user.is_admin?).to eq(true)
|
183
|
+
user.save
|
184
|
+
user.reload
|
185
|
+
expect(user.is_admin?).to eq(true)
|
186
|
+
|
187
|
+
user.clear_roles!
|
188
|
+
expect(user.is_admin?).to eq(false)
|
189
|
+
user.reload
|
190
|
+
|
191
|
+
expect(user.is_admin?).to eq(false)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe 'scopes' do
|
196
|
+
describe 'with_role' do
|
197
|
+
it 'should implement the `with_role` scope' do
|
198
|
+
expect(BitmaskUser).to respond_to :with_role
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should return an ActiveRecord::Relation' do
|
202
|
+
expect(BitmaskUser.with_role('admin').class).to eq(
|
203
|
+
"BitmaskUser::ActiveRecord_Relation".constantize
|
204
|
+
)
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should raise an ArgumentError for undefined roles' do
|
208
|
+
expect { BitmaskUser.with_role('your_mom') }.to raise_error(ArgumentError)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should match records with a given role' do
|
212
|
+
user = BitmaskUser.create(name: 'Daniel')
|
213
|
+
expect(BitmaskUser.with_role('admin')).not_to include user
|
214
|
+
user.add_role! 'admin'
|
215
|
+
expect(BitmaskUser.with_role('admin')).to include user
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should be chainable' do
|
219
|
+
(daniel = BitmaskUser.create(name: 'Daniel')).add_role! 'user'
|
220
|
+
(ryan = BitmaskUser.create(name: 'Ryan')).add_role! 'user'
|
221
|
+
ryan.add_role! 'admin'
|
222
|
+
admin_users = BitmaskUser.with_role('user').with_role('admin')
|
223
|
+
expect(admin_users).to include ryan
|
224
|
+
expect(admin_users).not_to include daniel
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe 'without_role' do
|
229
|
+
it 'should implement the `without_role` scope' do
|
230
|
+
expect(BitmaskUser).to respond_to :without_role
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should return an ActiveRecord::Relation' do
|
234
|
+
expect(BitmaskUser.without_role('admin').class).to eq(
|
235
|
+
"BitmaskUser::ActiveRecord_Relation".constantize
|
236
|
+
)
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'should raise an ArgumentError for undefined roles' do
|
240
|
+
expect { BitmaskUser.without_role('your_mom') }.to raise_error(ArgumentError)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'should match records with a given role' do
|
244
|
+
user = BitmaskUser.create(name: 'Daniel')
|
245
|
+
expect(BitmaskUser.without_role('admin')).to include user
|
246
|
+
user.add_role! 'admin'
|
247
|
+
expect(BitmaskUser.without_role('admin')).not_to include user
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'should be chainable' do
|
251
|
+
(daniel = BitmaskUser.create(name: 'Daniel')).add_role! 'user'
|
252
|
+
(ryan = BitmaskUser.create(name: 'Ryan')).add_role! 'user'
|
253
|
+
ryan.add_role! 'admin'
|
254
|
+
non_admin_users = BitmaskUser.with_role('user').without_role('admin')
|
255
|
+
expect(non_admin_users).not_to include ryan
|
256
|
+
expect(non_admin_users).to include daniel
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|