guise 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.codeclimate.yml +36 -0
- data/.rubocop.yml +1063 -0
- data/.travis.yml +25 -5
- data/.yardopts +1 -0
- data/Appraisals +5 -0
- data/README.md +120 -29
- data/gemfiles/3.1.gemfile +1 -1
- data/gemfiles/3.1.gemfile.lock +39 -30
- data/gemfiles/3.2.gemfile +1 -1
- data/gemfiles/3.2.gemfile.lock +47 -38
- data/gemfiles/4.0.gemfile +1 -1
- data/gemfiles/4.0.gemfile.lock +47 -38
- data/gemfiles/4.1.gemfile +1 -1
- data/gemfiles/4.1.gemfile.lock +45 -38
- data/gemfiles/4.2.gemfile +1 -1
- data/gemfiles/4.2.gemfile.lock +44 -37
- data/gemfiles/5.0.gemfile +8 -0
- data/gemfiles/5.0.gemfile.lock +94 -0
- data/guise.gemspec +9 -14
- data/lib/guise.rb +29 -5
- data/lib/guise/builders.rb +129 -0
- data/lib/guise/callbacks.rb +3 -0
- data/lib/guise/errors.rb +32 -0
- data/lib/guise/introspection.rb +23 -11
- data/lib/guise/options.rb +75 -0
- data/lib/guise/registry.rb +19 -0
- data/lib/guise/scopes.rb +65 -0
- data/lib/guise/syntax.rb +158 -83
- data/lib/guise/version.rb +1 -1
- data/spec/guise_spec.rb +112 -46
- data/spec/spec_helper.rb +12 -8
- metadata +50 -29
- data/spec/factories.rb +0 -39
data/.travis.yml
CHANGED
@@ -1,13 +1,33 @@
|
|
1
|
+
sudo: false
|
2
|
+
cache: bundler
|
1
3
|
rvm:
|
2
|
-
- 1.9.3
|
3
4
|
- 2.0.0
|
4
|
-
- 2.1.
|
5
|
-
-
|
5
|
+
- 2.1.8
|
6
|
+
- 2.2.4
|
7
|
+
- 2.3.0
|
6
8
|
gemfile:
|
7
9
|
- gemfiles/3.1.gemfile
|
8
10
|
- gemfiles/3.2.gemfile
|
9
11
|
- gemfiles/4.0.gemfile
|
10
|
-
|
12
|
+
- gemfiles/4.1.gemfile
|
13
|
+
- gemfiles/4.2.gemfile
|
14
|
+
- gemfiles/5.0.gemfile
|
11
15
|
script: 'bundle exec rake'
|
12
16
|
env:
|
13
|
-
|
17
|
+
global:
|
18
|
+
# Code Climate coverage reporting
|
19
|
+
- secure: "I0cDQLpPt8NlOswL5Vomim4gb+NHvLW+0mPf2iimP1gTE5C+3PnoomCRx//0CyI/NSMhjrHg8DbrkvtBCfAYinrgzCi3cUkofi0zn5T6GaGUfYQz4Bmh7fmvppRVpVDCSXMOzOSYYEXBnie5b865BYTDrt1U3aM8qvq7pd/XOXg="
|
20
|
+
matrix:
|
21
|
+
exclude:
|
22
|
+
- rvm: 2.2.4
|
23
|
+
gemfile: gemfiles/3.1.gemfile
|
24
|
+
- rvm: 2.3.0
|
25
|
+
gemfile: gemfiles/3.1.gemfile
|
26
|
+
- rvm: 2.2.4
|
27
|
+
gemfile: gemfiles/3.2.gemfile
|
28
|
+
- rvm: 2.0.0
|
29
|
+
gemfile: gemfiles/5.0.gemfile
|
30
|
+
- rvm: 2.1.8
|
31
|
+
gemfile: gemfiles/5.0.gemfile
|
32
|
+
allow_failures:
|
33
|
+
- gemfile: gemfiles/5.0.gemfile
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --title "paraphrase Documentation" --protected -M github-markup -M redcarpet
|
data/Appraisals
CHANGED
data/README.md
CHANGED
@@ -3,14 +3,106 @@
|
|
3
3
|
[![Build Status](https://travis-ci.org/ecbypi/guise.png?branch=master)](https://travis-ci.org/ecbypi/guise)
|
4
4
|
[![Code Climate](https://codeclimate.com/github/ecbypi/guise.png)](https://codeclimate.com/github/ecbypi/guise)
|
5
5
|
|
6
|
+
A typical, quick-and-easy role management system involves `users` and `roles`
|
7
|
+
tables with a join table between them to determine membership to a role:
|
6
8
|
|
7
|
-
|
9
|
+
```ruby
|
10
|
+
class User < ActiveRecord::Base
|
11
|
+
has_many :user_roles
|
12
|
+
has_many :roles, through: :user_roles
|
13
|
+
end
|
14
|
+
|
15
|
+
class UserRole < ActiveRecord::Bae
|
16
|
+
belongs_to :user
|
17
|
+
belongs_to :role
|
18
|
+
end
|
19
|
+
|
20
|
+
class Role < ActiveRecord::Base
|
21
|
+
has_many :user_roles
|
22
|
+
has_many :users, through: :user_roles
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
A problem with this is that in simple setups, application behavior tends to be
|
27
|
+
hard-coded to rely on the existence of a database record representing the role
|
28
|
+
object in the `roles` table.
|
29
|
+
|
30
|
+
`guise` de-normalizes the above setup by storing the name of the role as a
|
31
|
+
column in what would be the join table between `users` and `roles`. The allowed
|
32
|
+
values are limited to the values defined in a declaration on the model that is
|
33
|
+
meant to have different roles.
|
34
|
+
|
35
|
+
Given `User` and `Role` models where the `Role` model has a `value` column.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class User < ActiveRecord::Base
|
39
|
+
end
|
40
|
+
|
41
|
+
class Role < ActiveRecord::Base
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
By adding the following method call to `has_guises` to `User` and `guise_for` to
|
46
|
+
`Role`:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class User < ActiveRecord::Base
|
50
|
+
has_guises :DeskWorker, :MailForwarder, association: :roles, attribute: :value
|
51
|
+
end
|
52
|
+
|
53
|
+
class Role < ActiveRecord::Base
|
54
|
+
guise_for :User
|
55
|
+
end
|
56
|
+
```
|
8
57
|
|
9
|
-
|
10
|
-
using `has_many :through` or `has_and_belongs_to_many.` The `has_many` association
|
11
|
-
stores the role or type information as a string representing the class name.
|
58
|
+
The equivalent associations, model scopes and validations are configured:
|
12
59
|
|
13
|
-
|
60
|
+
```ruby
|
61
|
+
class User < ActiveRecord::Base
|
62
|
+
has_many :roles
|
63
|
+
|
64
|
+
scope :desk_workers, -> { joins(:roles).where(roles: { value: "DeskWorker" }) }
|
65
|
+
scope :mail_forwarders, -> { joins(:roles).where(roles: { value: "MailForwarder" }) }
|
66
|
+
|
67
|
+
def has_role?(value)
|
68
|
+
roles.detect { |role| role.value == value }
|
69
|
+
end
|
70
|
+
|
71
|
+
def has_roles?(*values)
|
72
|
+
values.all? { |value| has_role?(value)
|
73
|
+
end
|
74
|
+
|
75
|
+
def has_any_roles?(*values)
|
76
|
+
values.any? { |value| has_role?(value)
|
77
|
+
end
|
78
|
+
|
79
|
+
def desk_worker?
|
80
|
+
has_role?("DeskWorker")
|
81
|
+
end
|
82
|
+
|
83
|
+
def mail_forwarder?
|
84
|
+
has_role?("MailForwarder")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Role < ActiveRecord::Base
|
89
|
+
belongs_to :user
|
90
|
+
|
91
|
+
scope :desk_workers, -> { where(value: "DeskWorker") }
|
92
|
+
scope :mail_forwarders, -> { where(value: "MailForwarder") }
|
93
|
+
|
94
|
+
validates(
|
95
|
+
:value,
|
96
|
+
presence: true,
|
97
|
+
uniqueness: { scope: :user_id },
|
98
|
+
inclusion: { in: %w( DeskWorker MailForwarder ) }
|
99
|
+
)
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
This allows filtering users by role / type and assigning records a role without
|
104
|
+
requiring an existing record in the database. The predicate methods can be used
|
105
|
+
for permissions / authorization.
|
14
106
|
|
15
107
|
## Installation
|
16
108
|
|
@@ -20,7 +112,7 @@ Add this line to your application's Gemfile:
|
|
20
112
|
gem 'guise'
|
21
113
|
```
|
22
114
|
|
23
|
-
|
115
|
+
Then execute:
|
24
116
|
|
25
117
|
```
|
26
118
|
$ bundle
|
@@ -32,23 +124,25 @@ Or install it yourself as:
|
|
32
124
|
$ gem install guise
|
33
125
|
```
|
34
126
|
|
35
|
-
|
36
127
|
## Usage
|
37
128
|
|
38
129
|
Create a table to store your type information:
|
39
130
|
|
40
131
|
```
|
41
|
-
rails generate model
|
132
|
+
rails generate model role user:references value:string:uniq
|
42
133
|
rake db:migrate
|
43
134
|
```
|
44
135
|
|
136
|
+
It is recommended to add an index on the foreign key and guise attribute. In
|
137
|
+
this case the columns are `user_id` and `value`.
|
138
|
+
|
45
139
|
Then add `has_guises` to your model. This will setup the `has_many` association
|
46
140
|
for you. It requires the name of the association and name of the column that
|
47
|
-
the
|
141
|
+
the subclass name will be stored in.
|
48
142
|
|
49
143
|
```ruby
|
50
144
|
class User < ActiveRecord::Base
|
51
|
-
has_guises :DeskWorker, :MailForwarder, association: :
|
145
|
+
has_guises :DeskWorker, :MailForwarder, association: :roles, attribute: :value
|
52
146
|
end
|
53
147
|
```
|
54
148
|
|
@@ -72,7 +166,11 @@ This method does the following:
|
|
72
166
|
* Validates the column storing the name of the guise in the list supplied is
|
73
167
|
unique to the resource it belongs to and is one of the provided names.
|
74
168
|
|
75
|
-
|
169
|
+
### Role Subclasses
|
170
|
+
|
171
|
+
If using `User.<guise_scope>` is too tedious, it is possible to setup
|
172
|
+
subclasses to represent each value referenced in `has_guises` using the
|
173
|
+
`guise_of` method:
|
76
174
|
|
77
175
|
```ruby
|
78
176
|
class DeskWorker < User
|
@@ -80,25 +178,21 @@ class DeskWorker < User
|
|
80
178
|
end
|
81
179
|
```
|
82
180
|
|
83
|
-
This
|
181
|
+
This is equivalent to the following:
|
84
182
|
|
85
183
|
```ruby
|
86
184
|
class DeskWorker < User
|
87
|
-
default_scope -> { joins(:
|
185
|
+
default_scope -> { joins(:roles).where(roles: { value: 'DeskWorker'}) }
|
88
186
|
|
89
187
|
after_initialize do
|
90
|
-
self.guises.build(
|
91
|
-
end
|
92
|
-
|
93
|
-
after_create do
|
94
|
-
self.guises.create(title: 'DeskWorker')
|
188
|
+
self.guises.build(value: 'DeskWorker')
|
95
189
|
end
|
96
190
|
end
|
97
191
|
```
|
98
192
|
|
99
193
|
To scope the association class to a guise, use `scoped_guise_for`. The name of
|
100
|
-
the class must be the guise it
|
101
|
-
class.
|
194
|
+
the class must be `<guise_value><association_class_name>` (i.e. the guise it
|
195
|
+
represents combined with the name of the parent class.
|
102
196
|
|
103
197
|
```ruby
|
104
198
|
class DeskWorkerUserRole < UserRole
|
@@ -110,23 +204,20 @@ This sets up the class as follows:
|
|
110
204
|
|
111
205
|
```ruby
|
112
206
|
class DeskWorkerUserRole < UserRole
|
113
|
-
default_scope -> { where(
|
207
|
+
default_scope -> { where(value: "DeskWorker") }
|
114
208
|
|
115
209
|
after_initialize do
|
116
|
-
self.
|
117
|
-
end
|
118
|
-
|
119
|
-
before_create do
|
120
|
-
self.title = 'DeskWorker'
|
210
|
+
self.value = "DeskWorker"
|
121
211
|
end
|
122
212
|
end
|
123
213
|
```
|
124
214
|
|
125
215
|
### Customization
|
126
216
|
|
127
|
-
If the association doesn't standard association assumptions
|
128
|
-
the options for `has_many` into `has_guises`.
|
129
|
-
with the addition that you can specify not to
|
217
|
+
If the association doesn't standard association assumptions made by
|
218
|
+
`activerecord`, you can pass in the options for `has_many` into `has_guises`.
|
219
|
+
The same applies to `guise_for` with the addition that you can specify not to
|
220
|
+
validate attributes.
|
130
221
|
|
131
222
|
```ruby
|
132
223
|
class Person < ActiveRecord::Base
|
data/gemfiles/3.1.gemfile
CHANGED
data/gemfiles/3.1.gemfile.lock
CHANGED
@@ -19,48 +19,53 @@ GEM
|
|
19
19
|
tzinfo (~> 0.3.29)
|
20
20
|
activesupport (3.1.12)
|
21
21
|
multi_json (~> 1.0)
|
22
|
-
appraisal (
|
22
|
+
appraisal (2.1.0)
|
23
23
|
bundler
|
24
24
|
rake
|
25
|
+
thor (>= 0.14.0)
|
25
26
|
arel (2.2.3)
|
26
27
|
builder (3.0.4)
|
27
|
-
|
28
|
+
byebug (5.0.0)
|
29
|
+
columnize (= 0.9.0)
|
30
|
+
codeclimate-test-reporter (0.4.8)
|
28
31
|
simplecov (>= 0.7.1, < 1.0.0)
|
29
32
|
coderay (1.1.0)
|
33
|
+
columnize (0.9.0)
|
30
34
|
diff-lcs (1.2.5)
|
31
35
|
docile (1.1.5)
|
32
|
-
|
33
|
-
|
34
|
-
i18n (0.6.11)
|
36
|
+
i18n (0.7.0)
|
37
|
+
json (1.8.3)
|
35
38
|
method_source (0.8.2)
|
36
|
-
multi_json (1.
|
37
|
-
pry (0.10.
|
39
|
+
multi_json (1.11.2)
|
40
|
+
pry (0.10.3)
|
38
41
|
coderay (~> 1.1.0)
|
39
42
|
method_source (~> 0.8.1)
|
40
43
|
slop (~> 3.4)
|
41
|
-
rake (10.
|
42
|
-
|
43
|
-
|
44
|
-
rspec-
|
45
|
-
rspec-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
rake (10.4.2)
|
45
|
+
redcarpet (3.3.3)
|
46
|
+
rspec (3.4.0)
|
47
|
+
rspec-core (~> 3.4.0)
|
48
|
+
rspec-expectations (~> 3.4.0)
|
49
|
+
rspec-mocks (~> 3.4.0)
|
50
|
+
rspec-core (3.4.0)
|
51
|
+
rspec-support (~> 3.4.0)
|
52
|
+
rspec-expectations (3.4.0)
|
49
53
|
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
-
rspec-support (~> 3.
|
51
|
-
rspec-mocks (3.0
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
simplecov (0.9.0)
|
54
|
+
rspec-support (~> 3.4.0)
|
55
|
+
rspec-mocks (3.4.0)
|
56
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
57
|
+
rspec-support (~> 3.4.0)
|
58
|
+
rspec-support (3.4.0)
|
59
|
+
simplecov (0.10.0)
|
57
60
|
docile (~> 1.1.0)
|
58
|
-
|
59
|
-
simplecov-html (~> 0.
|
60
|
-
simplecov-html (0.
|
61
|
+
json (~> 1.8)
|
62
|
+
simplecov-html (~> 0.10.0)
|
63
|
+
simplecov-html (0.10.0)
|
61
64
|
slop (3.6.0)
|
62
|
-
sqlite3 (1.3.
|
63
|
-
|
65
|
+
sqlite3 (1.3.11)
|
66
|
+
thor (0.19.1)
|
67
|
+
tzinfo (0.3.45)
|
68
|
+
yard (0.8.7.6)
|
64
69
|
|
65
70
|
PLATFORMS
|
66
71
|
ruby
|
@@ -68,12 +73,16 @@ PLATFORMS
|
|
68
73
|
DEPENDENCIES
|
69
74
|
activerecord (~> 3.1.0)
|
70
75
|
activesupport (~> 3.1.0)
|
71
|
-
appraisal (
|
76
|
+
appraisal (>= 1.0)
|
77
|
+
byebug (~> 5.0)
|
72
78
|
codeclimate-test-reporter (~> 0.3)
|
73
|
-
factory_girl (~> 4.4)
|
74
79
|
guise!
|
75
80
|
pry (~> 0.9)
|
76
81
|
rake (~> 10.1)
|
82
|
+
redcarpet (~> 3.2)
|
77
83
|
rspec (~> 3.0)
|
78
|
-
shoulda-matchers (~> 2.5)
|
79
84
|
sqlite3 (~> 1.3)
|
85
|
+
yard (~> 0.8)
|
86
|
+
|
87
|
+
BUNDLED WITH
|
88
|
+
1.10.6
|
data/gemfiles/3.2.gemfile
CHANGED
data/gemfiles/3.2.gemfile.lock
CHANGED
@@ -8,59 +8,64 @@ PATH
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
activemodel (3.2.
|
12
|
-
activesupport (= 3.2.
|
11
|
+
activemodel (3.2.22)
|
12
|
+
activesupport (= 3.2.22)
|
13
13
|
builder (~> 3.0.0)
|
14
|
-
activerecord (3.2.
|
15
|
-
activemodel (= 3.2.
|
16
|
-
activesupport (= 3.2.
|
14
|
+
activerecord (3.2.22)
|
15
|
+
activemodel (= 3.2.22)
|
16
|
+
activesupport (= 3.2.22)
|
17
17
|
arel (~> 3.0.2)
|
18
18
|
tzinfo (~> 0.3.29)
|
19
|
-
activesupport (3.2.
|
20
|
-
i18n (
|
19
|
+
activesupport (3.2.22)
|
20
|
+
i18n (~> 0.6, >= 0.6.4)
|
21
21
|
multi_json (~> 1.0)
|
22
|
-
appraisal (
|
22
|
+
appraisal (2.1.0)
|
23
23
|
bundler
|
24
24
|
rake
|
25
|
-
|
25
|
+
thor (>= 0.14.0)
|
26
|
+
arel (3.0.3)
|
26
27
|
builder (3.0.4)
|
27
|
-
|
28
|
+
byebug (5.0.0)
|
29
|
+
columnize (= 0.9.0)
|
30
|
+
codeclimate-test-reporter (0.4.8)
|
28
31
|
simplecov (>= 0.7.1, < 1.0.0)
|
29
32
|
coderay (1.1.0)
|
33
|
+
columnize (0.9.0)
|
30
34
|
diff-lcs (1.2.5)
|
31
35
|
docile (1.1.5)
|
32
|
-
|
33
|
-
|
34
|
-
i18n (0.6.1)
|
36
|
+
i18n (0.7.0)
|
37
|
+
json (1.8.3)
|
35
38
|
method_source (0.8.2)
|
36
|
-
multi_json (1.
|
37
|
-
pry (0.10.
|
39
|
+
multi_json (1.11.2)
|
40
|
+
pry (0.10.3)
|
38
41
|
coderay (~> 1.1.0)
|
39
42
|
method_source (~> 0.8.1)
|
40
43
|
slop (~> 3.4)
|
41
|
-
rake (10.
|
42
|
-
|
43
|
-
|
44
|
-
rspec-
|
45
|
-
rspec-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
rake (10.4.2)
|
45
|
+
redcarpet (3.3.3)
|
46
|
+
rspec (3.4.0)
|
47
|
+
rspec-core (~> 3.4.0)
|
48
|
+
rspec-expectations (~> 3.4.0)
|
49
|
+
rspec-mocks (~> 3.4.0)
|
50
|
+
rspec-core (3.4.0)
|
51
|
+
rspec-support (~> 3.4.0)
|
52
|
+
rspec-expectations (3.4.0)
|
49
53
|
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
-
rspec-support (~> 3.
|
51
|
-
rspec-mocks (3.0
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
simplecov (0.9.0)
|
54
|
+
rspec-support (~> 3.4.0)
|
55
|
+
rspec-mocks (3.4.0)
|
56
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
57
|
+
rspec-support (~> 3.4.0)
|
58
|
+
rspec-support (3.4.0)
|
59
|
+
simplecov (0.10.0)
|
57
60
|
docile (~> 1.1.0)
|
58
|
-
|
59
|
-
simplecov-html (~> 0.
|
60
|
-
simplecov-html (0.
|
61
|
+
json (~> 1.8)
|
62
|
+
simplecov-html (~> 0.10.0)
|
63
|
+
simplecov-html (0.10.0)
|
61
64
|
slop (3.6.0)
|
62
|
-
sqlite3 (1.3.
|
63
|
-
|
65
|
+
sqlite3 (1.3.11)
|
66
|
+
thor (0.19.1)
|
67
|
+
tzinfo (0.3.45)
|
68
|
+
yard (0.8.7.6)
|
64
69
|
|
65
70
|
PLATFORMS
|
66
71
|
ruby
|
@@ -68,12 +73,16 @@ PLATFORMS
|
|
68
73
|
DEPENDENCIES
|
69
74
|
activerecord (~> 3.2.0)
|
70
75
|
activesupport (~> 3.2.0)
|
71
|
-
appraisal (
|
76
|
+
appraisal (>= 1.0)
|
77
|
+
byebug (~> 5.0)
|
72
78
|
codeclimate-test-reporter (~> 0.3)
|
73
|
-
factory_girl (~> 4.4)
|
74
79
|
guise!
|
75
80
|
pry (~> 0.9)
|
76
81
|
rake (~> 10.1)
|
82
|
+
redcarpet (~> 3.2)
|
77
83
|
rspec (~> 3.0)
|
78
|
-
shoulda-matchers (~> 2.5)
|
79
84
|
sqlite3 (~> 1.3)
|
85
|
+
yard (~> 0.8)
|
86
|
+
|
87
|
+
BUNDLED WITH
|
88
|
+
1.10.6
|