enumerations 1.3.2 → 2.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.
- checksums.yaml +5 -5
- data/Readme.md +98 -15
- data/enumerations.gemspec +6 -3
- data/lib/enumerations.rb +67 -35
- data/lib/enumerations/base.rb +16 -109
- data/lib/enumerations/finder_methods.rb +38 -0
- data/lib/enumerations/reflection.rb +8 -4
- data/lib/enumerations/value.rb +80 -0
- data/lib/enumerations/version.rb +2 -2
- data/test/base_test.rb +33 -36
- data/test/database_helper.rb +15 -0
- data/test/enumerations_test.rb +47 -0
- data/test/reflection_test.rb +32 -5
- data/test/test_helper.rb +24 -7
- data/test/value_test.rb +63 -0
- data/test/version_test.rb +1 -1
- metadata +55 -7
- data/.travis.yml +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a75cec2684b73e9e83a7b60ddd48b95ac01fd991
|
4
|
+
data.tar.gz: 822d8484b921612c31deadd8491994ad1e22a66d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3df5039cf8d021b312058982c26edee6d8e24c56bd05a4f9cba6db2f55adef3f311d3181a544e774cd3607750d0c813e4935fcbf3079608eec3a7a142a546d4d
|
7
|
+
data.tar.gz: 04c31625eecf7f4276bf11d673aa66d2290ca086d49d728502cde004166a32c7a2ff051367356299feeee8399afc0b486551b00694b51fe47b69d1e72aaf05d1
|
data/Readme.md
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
Enumerations
|
2
2
|
============
|
3
3
|
|
4
|
-
|
4
|
+
[](https://badge.fury.io/rb/enumerations)
|
5
|
+
[](https://codeclimate.com/github/infinum/enumerations)
|
6
|
+
[](https://semaphoreci.com/infinum/enumerations)
|
7
|
+
[](https://codeclimate.com/github/infinum/enumerations/coverage)
|
5
8
|
|
6
|
-
|
9
|
+
Rails plugin for enumerations in ActiveRecord models.
|
7
10
|
|
8
11
|
Installation
|
9
12
|
============
|
@@ -17,10 +20,12 @@ gem 'enumerations'
|
|
17
20
|
Usage
|
18
21
|
=====
|
19
22
|
|
23
|
+
## Defining enumerations
|
24
|
+
|
20
25
|
Create a model for your enumerations:
|
21
26
|
|
22
27
|
```ruby
|
23
|
-
class Status <
|
28
|
+
class Status < Enumerations::Base
|
24
29
|
values draft: { id: 1, name: 'Draft' },
|
25
30
|
review_pending: { id: 2, name: 'Review pending' },
|
26
31
|
published: { id: 3, name: 'Published' }
|
@@ -30,7 +35,7 @@ end
|
|
30
35
|
Or you can use `value` method for defining your enumerations:
|
31
36
|
|
32
37
|
```ruby
|
33
|
-
class Status <
|
38
|
+
class Status < Enumerations::Base
|
34
39
|
value :draft, id: 1, name: 'Draft'
|
35
40
|
value :review_pending, id: 2, name: 'Review pending'
|
36
41
|
value :published, id: 3, name: 'Published'
|
@@ -46,16 +51,21 @@ class Post < ActiveRecord::Base
|
|
46
51
|
end
|
47
52
|
```
|
48
53
|
|
49
|
-
You can pass attributes to specify which
|
54
|
+
You can pass attributes to specify which enumeration and which column to use:
|
50
55
|
|
51
56
|
```ruby
|
52
57
|
class Post < ActiveRecord::Base
|
53
58
|
enumeration :status,
|
54
|
-
foreign_key: :post_status_id,
|
55
|
-
class_name: Post::Status
|
59
|
+
foreign_key: :post_status_id, # specifies which column to use
|
60
|
+
class_name: Post::Status # specifies the class of the enumerator
|
56
61
|
validates :post_status_id, presence: true
|
57
62
|
end
|
58
63
|
```
|
64
|
+
Attribute `foreign_key` you can pass as a `String` or a `Symbol`. Attribute `class_name` can be set as a `String`, a `Symbol` or a `String`.
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
## Setting enumeration value to objects
|
59
69
|
|
60
70
|
Set enumerations, find enumerations by `symbol`:
|
61
71
|
|
@@ -71,20 +81,56 @@ Or you can set enumerations on this way:
|
|
71
81
|
@post.status = Status.draft
|
72
82
|
```
|
73
83
|
|
84
|
+
Also, you can set enumeration value like this:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
@post.status_draft!
|
88
|
+
```
|
89
|
+
|
90
|
+
> When you include enumerations into your model, you'll get methods for setting each enumeration value. Each method name is consists from enumeration name and enumeration value name with **!** at the end. Examples:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class Post < ActiveRecord::Base
|
94
|
+
enumeration :status
|
95
|
+
end
|
96
|
+
|
97
|
+
@post.status_draft!
|
98
|
+
```
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
class User < ActiveRecord::Base
|
102
|
+
enumeration :role
|
103
|
+
end
|
104
|
+
|
105
|
+
@user.role_admin!
|
106
|
+
```
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
class User < ActiveRecord::Base
|
110
|
+
enumeration :type, class_name: Role
|
111
|
+
end
|
112
|
+
|
113
|
+
@user.type_editor!
|
114
|
+
```
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
## Finder methods
|
119
|
+
|
74
120
|
Find enumerations by `id`:
|
75
121
|
|
76
122
|
```ruby
|
77
|
-
@post.status = Status.find(2)
|
123
|
+
@post.status = Status.find(2) # => Review pending
|
78
124
|
@post.save
|
79
125
|
```
|
80
126
|
|
81
127
|
Compare enumerations:
|
82
128
|
|
83
129
|
```ruby
|
84
|
-
@post.status == :published
|
85
|
-
@post.status == 3
|
86
|
-
@post.status == Status.find(:published)
|
87
|
-
@post.status.published?
|
130
|
+
@post.status == :published # => true
|
131
|
+
@post.status == 3 # => true
|
132
|
+
@post.status == Status.find(:published) # => true
|
133
|
+
@post.status.published? # => true
|
88
134
|
```
|
89
135
|
|
90
136
|
Get all enumerations:
|
@@ -93,6 +139,43 @@ Get all enumerations:
|
|
93
139
|
Status.all
|
94
140
|
```
|
95
141
|
|
142
|
+
|
143
|
+
|
144
|
+
## Scopes on model
|
145
|
+
|
146
|
+
With enumerations, you'll get scope for each enumeration value in the
|
147
|
+
following format:
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
with_#{enumeration_name}_#{enumeration_value_name}
|
151
|
+
```
|
152
|
+
|
153
|
+
Examples:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
class Post < ActiveRecord::Base
|
157
|
+
enumeration :status
|
158
|
+
end
|
159
|
+
|
160
|
+
Post.with_status_draft # => <#ActiveRecord::Relation []>
|
161
|
+
Post.with_status_review_pending # => <#ActiveRecord::Relation []>
|
162
|
+
```
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
class Post < ActiveRecord::Base
|
166
|
+
enumeration :my_status, class_name: Status
|
167
|
+
end
|
168
|
+
|
169
|
+
Post.with_my_status_draft # => <#ActiveRecord::Relation []>
|
170
|
+
Post.with_my_status_review_pending # => <#ActiveRecord::Relation []>
|
171
|
+
```
|
172
|
+
|
173
|
+
Each scope returns all records with specified enumeration value.
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
## Forms usage
|
178
|
+
|
96
179
|
Use in forms:
|
97
180
|
|
98
181
|
```ruby
|
@@ -108,7 +191,7 @@ Advance Usage
|
|
108
191
|
Except `id` and `name` you can specify other attributes to your enumerations:
|
109
192
|
|
110
193
|
```ruby
|
111
|
-
class Status <
|
194
|
+
class Status < Enumerations::Base
|
112
195
|
value :draft, id: 1, name: 'Draft'
|
113
196
|
value :review_pending, id: 2, name: 'Review pending', description: 'Some description...'
|
114
197
|
value :published, id: 3, name: 'Published'
|
@@ -118,8 +201,8 @@ end
|
|
118
201
|
Every enumeration has `id`, `name` and `description` methods. If you call method that is not in attribute list for enumeration, it will return `nil`.
|
119
202
|
|
120
203
|
```ruby
|
121
|
-
Status.review_pending.description
|
122
|
-
Status.draft.description
|
204
|
+
Status.review_pending.description # => 'Some description...'
|
205
|
+
Status.draft.description # => nil
|
123
206
|
```
|
124
207
|
|
125
208
|
Author
|
data/enumerations.gemspec
CHANGED
@@ -2,9 +2,9 @@ require File.expand_path('../lib/enumerations/version', __FILE__)
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'enumerations'
|
5
|
-
s.version =
|
6
|
-
s.date = '
|
7
|
-
s.summary =
|
5
|
+
s.version = Enumerations::VERSION
|
6
|
+
s.date = '2016-08-15'
|
7
|
+
s.summary = 'Enumerations for ActiveRecord!'
|
8
8
|
s.description = 'Extends ActiveRecord with enumeration capabilites.'
|
9
9
|
s.authors = ['Tomislav Car', 'Nikica Jokic', 'Nikola Santic']
|
10
10
|
s.email = ['tomislav@infinum.hr', 'nikica.jokic@infinum.hr', 'nikola.santic@infinum.hr']
|
@@ -13,6 +13,9 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.add_dependency 'activerecord'
|
14
14
|
s.add_dependency 'activesupport'
|
15
15
|
s.add_development_dependency 'pry-byebug'
|
16
|
+
s.add_development_dependency 'rake'
|
17
|
+
s.add_development_dependency 'codeclimate-test-reporter'
|
18
|
+
s.add_development_dependency 'sqlite3'
|
16
19
|
|
17
20
|
s.files = `git ls-files`.split("\n")
|
18
21
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/lib/enumerations.rb
CHANGED
@@ -7,8 +7,7 @@ require 'enumerations/version'
|
|
7
7
|
require 'enumerations/base'
|
8
8
|
require 'enumerations/reflection'
|
9
9
|
|
10
|
-
|
11
|
-
module Enumeration
|
10
|
+
module Enumerations
|
12
11
|
extend ActiveSupport::Concern
|
13
12
|
|
14
13
|
included do
|
@@ -27,17 +26,15 @@ module Enumeration
|
|
27
26
|
# end
|
28
27
|
#
|
29
28
|
# user.role_id = 1
|
30
|
-
# user.role => #<
|
29
|
+
# user.role => #<Enumerations::Value: @base=Role, @symbol=:admin...>
|
31
30
|
#
|
32
31
|
# user.role = Role.staff
|
33
32
|
# user.role_id => 2
|
34
33
|
#
|
35
|
-
# TODO: add documentation for foreign_key and class_name
|
36
34
|
def enumeration(name, options = {})
|
37
|
-
|
38
|
-
options[:class_name] ||= name.to_s.camelize
|
35
|
+
reflection = Reflection.new(name, options)
|
39
36
|
|
40
|
-
add_enumeration(
|
37
|
+
add_enumeration(reflection)
|
41
38
|
end
|
42
39
|
|
43
40
|
# Output all the enumerations that this model has defined
|
@@ -47,8 +44,9 @@ module Enumeration
|
|
47
44
|
# Example:
|
48
45
|
#
|
49
46
|
# User.reflect_on_all_enumerations => # [
|
50
|
-
# #<
|
51
|
-
# #<
|
47
|
+
# #<Enumerations::Reflection: @name=:role...>,
|
48
|
+
# #<Enumerations::Reflection: @name=:status...>
|
49
|
+
# ]
|
52
50
|
#
|
53
51
|
def reflect_on_all_enumerations
|
54
52
|
_enumerations
|
@@ -56,41 +54,75 @@ module Enumeration
|
|
56
54
|
|
57
55
|
private
|
58
56
|
|
59
|
-
def add_enumeration(
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
# user.role_id = 1
|
65
|
-
# user.role => #<Enumeration::Value:0x007fff45d7ec30 @base=Role, @symbol=:admin...>
|
66
|
-
#
|
67
|
-
define_method name do
|
68
|
-
enumerator_class = if options[:class_name].is_a?(Class)
|
69
|
-
options[:class_name]
|
70
|
-
else
|
71
|
-
options[:class_name].constantize
|
72
|
-
end
|
57
|
+
def add_enumeration(reflection)
|
58
|
+
define_getter_method(reflection)
|
59
|
+
define_setter_method(reflection)
|
60
|
+
define_bang_methods(reflection)
|
61
|
+
define_scopes(reflection)
|
73
62
|
|
74
|
-
|
63
|
+
self._enumerations += [reflection]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Getter for belongs_to
|
67
|
+
#
|
68
|
+
# Example:
|
69
|
+
#
|
70
|
+
# user.role_id = 1
|
71
|
+
# user.role => #<Enumerations::Value: @base=Role, @symbol=:admin...>
|
72
|
+
#
|
73
|
+
def define_getter_method(reflection)
|
74
|
+
define_method(reflection.name) do
|
75
|
+
reflection.enumerator_class.find(send(reflection.foreign_key))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Setter for belongs_to
|
80
|
+
#
|
81
|
+
# Example:
|
82
|
+
#
|
83
|
+
# user.role = Role.admin
|
84
|
+
# user.role_id => 1
|
85
|
+
#
|
86
|
+
def define_setter_method(reflection)
|
87
|
+
define_method("#{reflection.name}=") do |other|
|
88
|
+
send("#{reflection.foreign_key}=", other.id)
|
75
89
|
end
|
90
|
+
end
|
76
91
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
92
|
+
# Add bang methods for setting all enumeration values.
|
93
|
+
# All methods are prefixed with enumeration name.
|
94
|
+
#
|
95
|
+
# Example:
|
96
|
+
#
|
97
|
+
# user.role_admin!
|
98
|
+
# user.role => #<Enumerations::Value: @base=Role, @symbol=:admin...>
|
99
|
+
#
|
100
|
+
def define_bang_methods(reflection)
|
101
|
+
reflection.enumerator_class.all.each do |enumeration|
|
102
|
+
define_method("#{reflection.name}_#{enumeration.to_sym}!") do
|
103
|
+
send("#{reflection.name}=", enumeration)
|
104
|
+
end
|
86
105
|
end
|
106
|
+
end
|
87
107
|
|
88
|
-
|
108
|
+
# Scopes for enumerated ActiveRecord model.
|
109
|
+
# Format of scope name is with_#{enumeration_name}_#{enumeration_value_name}.
|
110
|
+
#
|
111
|
+
# Example:
|
112
|
+
#
|
113
|
+
# User.with_role_admin => <#ActiveRecord::Relation []>
|
114
|
+
# User.with_role_editor => <#ActiveRecord::Relation []>
|
115
|
+
#
|
116
|
+
def define_scopes(reflection)
|
117
|
+
reflection.enumerator_class.all.each do |enumeration|
|
118
|
+
scope "with_#{reflection.name}_#{enumeration.symbol}",
|
119
|
+
-> { where(reflection.foreign_key => enumeration.id) }
|
120
|
+
end
|
89
121
|
end
|
90
122
|
end
|
91
123
|
end
|
92
124
|
|
93
125
|
# Extend ActiveRecord with Enumeration capabilites
|
94
126
|
ActiveSupport.on_load(:active_record) do
|
95
|
-
include
|
127
|
+
include Enumerations
|
96
128
|
end
|
data/lib/enumerations/base.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
require 'active_support/core_ext/class/attribute'
|
2
2
|
require 'active_support/core_ext/string/inflections'
|
3
|
+
require 'enumerations/value'
|
4
|
+
require 'enumerations/finder_methods'
|
3
5
|
|
4
|
-
module
|
6
|
+
module Enumerations
|
5
7
|
class Base
|
8
|
+
extend Enumerations::FinderMethods
|
9
|
+
include Enumerations::Value
|
10
|
+
|
6
11
|
class_attribute :_values, :_symbol_index
|
7
12
|
self._values = {}
|
8
13
|
self._symbol_index = {}
|
@@ -13,13 +18,11 @@ module Enumeration
|
|
13
18
|
#
|
14
19
|
# value :admin, id: 1, name: 'Admin', description: 'Some description...'
|
15
20
|
#
|
16
|
-
# Role.admin.id
|
17
|
-
# Role.find(:admin).name
|
18
|
-
# Role.find(1).description
|
21
|
+
# Role.admin.id => # 1
|
22
|
+
# Role.find(:admin).name => # "Admin"
|
23
|
+
# Role.find(1).description => # "Some description..."
|
19
24
|
#
|
20
25
|
def self.value(symbol, attributes)
|
21
|
-
# TODO: make this errors better if needed
|
22
|
-
# TODO: test this errors
|
23
26
|
raise 'Enumeration id is required' if attributes[:id].nil?
|
24
27
|
raise "Duplicate symbol #{symbol}" if find(symbol)
|
25
28
|
raise "Duplicate id #{attributes[:id]}" if find(attributes[:id])
|
@@ -31,8 +34,8 @@ module Enumeration
|
|
31
34
|
#
|
32
35
|
# Example:
|
33
36
|
#
|
34
|
-
# Role.admin => #<
|
35
|
-
# Role.staff => #<
|
37
|
+
# Role.admin => #<Enumerations::Value: @base=Role, @symbol=:admin...>
|
38
|
+
# Role.staff => #<Enumerations::Value: @base=Role, @symbol=:staff...>
|
36
39
|
#
|
37
40
|
singleton_class.send(:define_method, symbol) do
|
38
41
|
find(symbol)
|
@@ -71,117 +74,21 @@ module Enumeration
|
|
71
74
|
#
|
72
75
|
# Example:
|
73
76
|
#
|
74
|
-
# Role.all => # [#<
|
75
|
-
# #<
|
76
|
-
# #<
|
77
|
+
# Role.all => # [#<Enumerations::Value: @base=Role, @symbol=:admin...>,
|
78
|
+
# #<Enumerations::Value: @base=Role, @symbol=:manager...>,
|
79
|
+
# #<Enumerations::Value: @base=Role, @symbol=:staff...>]
|
77
80
|
#
|
78
81
|
def self.all
|
79
82
|
_values.values
|
80
83
|
end
|
81
84
|
|
82
|
-
|
83
|
-
#
|
84
|
-
# Example:
|
85
|
-
#
|
86
|
-
# Role.find(:admin) => #<Enumeration::Value:0x007f8ed7f46100 @base=Role, @symbol=:admin...>
|
87
|
-
# Role.find(2) => #<Enumeration::Value:0x007f8ed7f45de0 @base=Role, @symbol=:manager...>
|
88
|
-
# Role.find('2') => #<Enumeration::Value:0x007f8ed7f45de0 @base=Role, @symbol=:manager...>
|
89
|
-
# Role.find('staff') => #<Enumeration::Value:0x007f8ed7f45ae8 @base=Role, @symbol=:staff...>
|
90
|
-
#
|
91
|
-
def self.find(key)
|
92
|
-
case key
|
93
|
-
when Symbol then find_by_key(key)
|
94
|
-
when String then find_by_key(key.to_sym) || find_by_id(key.to_i)
|
95
|
-
when Integer then find_by_id(key)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Finds an enumeration by defined attribute. Simmilar to AcriveRecord::FinderMethods#find_by
|
100
|
-
#
|
101
|
-
# Example:
|
102
|
-
#
|
103
|
-
# Role.find_by(name: 'Admin') => #<Enumeration::Value:0x007f8ed7f46100 @base=Role, @symbol=:admin...>
|
104
|
-
#
|
105
|
-
def self.find_by(**args)
|
106
|
-
_values.values.find { |value| args.map { |k, v| value.send(k) == v }.all? }
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.find_by_key(key)
|
110
|
-
_values[key]
|
111
|
-
end
|
112
|
-
|
113
|
-
def self.find_by_id(id)
|
114
|
-
_values[_symbol_index.key(id)]
|
115
|
-
end
|
85
|
+
attr_reader :symbol
|
116
86
|
|
117
87
|
def initialize(symbol, attributes)
|
118
88
|
@symbol = symbol
|
119
89
|
@attributes = attributes
|
120
|
-
create_instance_methods
|
121
|
-
end
|
122
90
|
|
123
|
-
|
124
|
-
|
125
|
-
def to_i
|
126
|
-
id
|
127
|
-
end
|
128
|
-
|
129
|
-
def to_s
|
130
|
-
name
|
131
|
-
end
|
132
|
-
|
133
|
-
def to_sym
|
134
|
-
@symbol
|
135
|
-
end
|
136
|
-
|
137
|
-
def to_param
|
138
|
-
id
|
139
|
-
end
|
140
|
-
|
141
|
-
# Comparison by id, symbol or object
|
142
|
-
#
|
143
|
-
# Example:
|
144
|
-
#
|
145
|
-
# Role.admin == 1 => true
|
146
|
-
# Role.admin == :admin => true
|
147
|
-
# Role.admin == Role.admin => true
|
148
|
-
# Role.admin == 2 => false
|
149
|
-
# Role.admin == :staff => false
|
150
|
-
# Role.admin == Role.staff => false
|
151
|
-
#
|
152
|
-
# TODO: test if case..when is working with this
|
153
|
-
def ==(other)
|
154
|
-
case other
|
155
|
-
when Integer then other == id
|
156
|
-
when Symbol then other == @symbol
|
157
|
-
else super
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
private
|
162
|
-
|
163
|
-
# Getters for all attributes
|
164
|
-
#
|
165
|
-
# Example:
|
166
|
-
#
|
167
|
-
# Role.admin => #<Enumeration::Value:0x007fff45d7ec30 @base=Role, @symbol=:admin,
|
168
|
-
# @attributes={:id=>1, :name=>"Admin", :description=>"Some description..."}>
|
169
|
-
# user.role.id => # 1
|
170
|
-
# user.role.name => # "Admin"
|
171
|
-
# user.role.description => # "Some description..."
|
172
|
-
# user.role.admin? => # true
|
173
|
-
# user.role.staff? => # false
|
174
|
-
#
|
175
|
-
def create_instance_methods
|
176
|
-
@attributes.each do |key, _|
|
177
|
-
self.class.send :define_method, key do
|
178
|
-
@attributes[key]
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
self.class.send :define_method, "#{@symbol}?" do
|
183
|
-
__callee__[0..-2].to_sym == @symbol
|
184
|
-
end
|
91
|
+
create_instance_methods
|
185
92
|
end
|
186
93
|
end
|
187
94
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Enumerations
|
2
|
+
module FinderMethods
|
3
|
+
# Finds an enumeration by symbol, id or name
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
#
|
7
|
+
# Role.find(:admin) => #<Enumerations::Value: @base=Role, @symbol=:admin...>
|
8
|
+
# Role.find(2) => #<Enumerations::Value: @base=Role, @symbol=:manager...>
|
9
|
+
# Role.find('2') => #<Enumerations::Value: @base=Role, @symbol=:manager...>
|
10
|
+
# Role.find('staff') => #<Enumerations::Value: @base=Role, @symbol=:staff...>
|
11
|
+
#
|
12
|
+
def find(key)
|
13
|
+
case key
|
14
|
+
when Symbol then find_by_key(key)
|
15
|
+
when String then find_by_key(key.to_sym) || find_by_id(key.to_i)
|
16
|
+
when Fixnum then find_by_id(key)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Finds an enumeration by defined attribute. Similar to ActiveRecord::FinderMethods#find_by
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
#
|
24
|
+
# Role.find_by(name: 'Admin') => #<Enumerations::Value: @base=Role, @symbol=:admin...>
|
25
|
+
#
|
26
|
+
def find_by(**args)
|
27
|
+
_values.values.find { |value| args.map { |k, v| value.send(k) == v }.all? }
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_by_key(key)
|
31
|
+
_values[key]
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_by_id(id)
|
35
|
+
_values[_symbol_index.key(id)]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,18 +1,22 @@
|
|
1
|
-
module
|
1
|
+
module Enumerations
|
2
2
|
class Reflection
|
3
3
|
attr_reader :name
|
4
4
|
|
5
|
-
def initialize(name, options)
|
5
|
+
def initialize(name, options = {})
|
6
6
|
@name = name
|
7
7
|
@options = options
|
8
8
|
end
|
9
9
|
|
10
10
|
def class_name
|
11
|
-
@options[:class_name]
|
11
|
+
@class_name ||= (@options[:class_name] || name).to_s.camelize
|
12
12
|
end
|
13
13
|
|
14
14
|
def foreign_key
|
15
|
-
@options[:foreign_key]
|
15
|
+
@foreign_key ||= (@options[:foreign_key] || "#{name}_id").to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def enumerator_class
|
19
|
+
@enumerator_class ||= class_name.constantize
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Enumerations
|
2
|
+
module Value
|
3
|
+
def to_i
|
4
|
+
id
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_s
|
8
|
+
name
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_sym
|
12
|
+
symbol
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_param
|
16
|
+
id
|
17
|
+
end
|
18
|
+
|
19
|
+
# Comparison by id, symbol or object
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
#
|
23
|
+
# Role.admin == 1 => true
|
24
|
+
# Role.admin == :admin => true
|
25
|
+
# Role.admin == Role.admin => true
|
26
|
+
# Role.admin == 2 => false
|
27
|
+
# Role.admin == :staff => false
|
28
|
+
# Role.admin == Role.staff => false
|
29
|
+
#
|
30
|
+
# TODO: test if case..when is working with this
|
31
|
+
def ==(other)
|
32
|
+
case other
|
33
|
+
when Fixnum then other == id
|
34
|
+
when Symbol then other == symbol
|
35
|
+
else super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def create_instance_methods
|
42
|
+
define_attributes_getters
|
43
|
+
define_value_checking_method
|
44
|
+
end
|
45
|
+
|
46
|
+
# Getters for all attributes
|
47
|
+
#
|
48
|
+
# Example:
|
49
|
+
#
|
50
|
+
# Role.admin => #<Enumerations::Value:0x007fff45d7ec30 @base=Role, @symbol=:admin,
|
51
|
+
# @attributes={:id=>1, :name=>"Admin", :description=>"Some description..."}>
|
52
|
+
# user.role.id => # 1
|
53
|
+
# user.role.name => # "Admin"
|
54
|
+
# user.role.description => # "Some description..."
|
55
|
+
#
|
56
|
+
def define_attributes_getters
|
57
|
+
@attributes.each do |key, _|
|
58
|
+
next if respond_to?(key)
|
59
|
+
|
60
|
+
self.class.send :define_method, key do
|
61
|
+
@attributes[key]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Predicate methods for values
|
67
|
+
#
|
68
|
+
# Example:
|
69
|
+
#
|
70
|
+
# user.role = Role.admin
|
71
|
+
# user.role.admin? => # true
|
72
|
+
# user.role.staff? => # false
|
73
|
+
#
|
74
|
+
def define_value_checking_method
|
75
|
+
self.class.send :define_method, "#{symbol}?" do
|
76
|
+
__callee__[0..-2].to_sym == symbol
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/enumerations/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = '
|
1
|
+
module Enumerations
|
2
|
+
VERSION = '2.0.0'
|
3
3
|
end
|
data/test/base_test.rb
CHANGED
@@ -13,6 +13,18 @@ class BaseTest < Minitest::Test
|
|
13
13
|
refute_same :published, status.symbol
|
14
14
|
end
|
15
15
|
|
16
|
+
def test_lookup_by_string_id
|
17
|
+
status = Status.find('1')
|
18
|
+
|
19
|
+
assert_equal :draft, status.symbol
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_lookup_by_string_key
|
23
|
+
status = Status.find('draft')
|
24
|
+
|
25
|
+
assert_equal :draft, status.symbol
|
26
|
+
end
|
27
|
+
|
16
28
|
def test_find_by
|
17
29
|
status = Status.find_by(name: 'Draft')
|
18
30
|
|
@@ -32,6 +44,19 @@ class BaseTest < Minitest::Test
|
|
32
44
|
assert_equal statuses.first, Status.draft
|
33
45
|
end
|
34
46
|
|
47
|
+
def test_symbols
|
48
|
+
status_symbols = Status.symbols
|
49
|
+
|
50
|
+
assert_equal 5, status_symbols.size
|
51
|
+
assert_equal status_symbols.first, Status.draft.to_sym
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_required_id
|
55
|
+
assert_raises 'Enumeration id is required' do
|
56
|
+
Class.new.value draft: { name: 'Draft' }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
35
60
|
def test_duplicated_id
|
36
61
|
assert_raises 'Duplicate id 1' do
|
37
62
|
Class.new.values draft: { id: 1, name: 'Draft' },
|
@@ -57,45 +82,17 @@ class BaseTest < Minitest::Test
|
|
57
82
|
end
|
58
83
|
end
|
59
84
|
|
60
|
-
def
|
61
|
-
|
85
|
+
def test_enumerations_custom_instance_method
|
86
|
+
role = Role.find(:admin)
|
62
87
|
|
63
|
-
assert_equal
|
88
|
+
assert_equal 'user_Admin', role.my_custom_name
|
64
89
|
end
|
65
90
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
assert_equal true, status.deleted
|
70
|
-
end
|
91
|
+
def test_all_enumerations_has_custom_instance_methods
|
92
|
+
roles = Role.all
|
71
93
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
assert_equal nil, status.visible
|
76
|
-
end
|
77
|
-
|
78
|
-
def test_equal_by_id
|
79
|
-
status = Status.find(:draft)
|
80
|
-
|
81
|
-
assert_equal true, status == 1
|
82
|
-
end
|
83
|
-
|
84
|
-
def test_equal_by_symbol
|
85
|
-
status = Status.draft
|
86
|
-
|
87
|
-
assert_equal true, status == :draft
|
88
|
-
end
|
89
|
-
|
90
|
-
def test_equal_by_enumeration
|
91
|
-
status = Status.draft
|
92
|
-
|
93
|
-
assert_equal true, status == Status.draft
|
94
|
-
end
|
95
|
-
|
96
|
-
def test_not_equal_by_enumeration
|
97
|
-
status = Status.draft
|
98
|
-
|
99
|
-
assert_equal false, status == Status.published
|
94
|
+
assert_silent do
|
95
|
+
roles.map(&:my_custom_name)
|
96
|
+
end
|
100
97
|
end
|
101
98
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
4
|
+
|
5
|
+
ActiveRecord::Schema.define do
|
6
|
+
create_table :posts, force: true do |t|
|
7
|
+
t.integer :status_id
|
8
|
+
t.integer :some_other_status_id
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :users, force: true do |t|
|
12
|
+
t.integer :role_id
|
13
|
+
t.string :status_id
|
14
|
+
end
|
15
|
+
end
|
data/test/enumerations_test.rb
CHANGED
@@ -18,6 +18,20 @@ class EnumerationsTest < Minitest::Test
|
|
18
18
|
assert_equal 'Draft', p.status.to_s
|
19
19
|
end
|
20
20
|
|
21
|
+
def test_model_bang_assignment
|
22
|
+
p = Post.new
|
23
|
+
p.status_draft!
|
24
|
+
|
25
|
+
assert_equal 'Draft', p.status.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_model_bang_assignment_with_custom_name
|
29
|
+
p = Post.new
|
30
|
+
p.different_status_draft!
|
31
|
+
|
32
|
+
assert_equal 'Draft', p.different_status.to_s
|
33
|
+
end
|
34
|
+
|
21
35
|
def test_model_via_id_assignment
|
22
36
|
p = Post.new
|
23
37
|
p.some_other_status_id = Status.published.id
|
@@ -38,4 +52,37 @@ class EnumerationsTest < Minitest::Test
|
|
38
52
|
|
39
53
|
assert_equal false, p.status.published?
|
40
54
|
end
|
55
|
+
|
56
|
+
def test_multiple_enumerations_on_model
|
57
|
+
enumerations = User.reflect_on_all_enumerations
|
58
|
+
|
59
|
+
assert_equal 2, enumerations.size
|
60
|
+
|
61
|
+
assert_equal :role, enumerations.first.name
|
62
|
+
assert_equal :role_id, enumerations.first.foreign_key
|
63
|
+
|
64
|
+
assert_equal :status, enumerations[1].name
|
65
|
+
assert_equal :status_id, enumerations[1].foreign_key
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_multiple_enumeration_assignments_on_model
|
69
|
+
u = User.new
|
70
|
+
u.role = Role.admin
|
71
|
+
u.status = Status.published
|
72
|
+
|
73
|
+
assert_equal 'Admin', u.role.to_s
|
74
|
+
assert_equal 'Published', u.status.to_s
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_enumerated_class_has_scopes
|
78
|
+
Role.all do |role|
|
79
|
+
assert_respond_to User, ['with_role', role.name].join('_').to_sym
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_enumerated_class_scope_hash_value
|
84
|
+
query_hash = User.with_role_admin.where_values_hash.symbolize_keys
|
85
|
+
|
86
|
+
assert_equal query_hash, role_id: 1
|
87
|
+
end
|
41
88
|
end
|
data/test/reflection_test.rb
CHANGED
@@ -1,11 +1,38 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
|
3
3
|
class ReflectionTest < Minitest::Test
|
4
|
-
def
|
5
|
-
reflection =
|
4
|
+
def test_reflection_with_all_attributes
|
5
|
+
reflection = Enumerations::Reflection.new(:status, class_name: 'Status',
|
6
|
+
foreign_key: :status_id)
|
6
7
|
|
7
|
-
assert_equal :
|
8
|
-
assert_equal '
|
9
|
-
assert_equal :
|
8
|
+
assert_equal :status, reflection.name
|
9
|
+
assert_equal 'Status', reflection.class_name
|
10
|
+
assert_equal :status_id, reflection.foreign_key
|
11
|
+
assert_equal ::Status, reflection.enumerator_class
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_reflection_without_class_name_and_foreign_key
|
15
|
+
reflection = Enumerations::Reflection.new(:status)
|
16
|
+
|
17
|
+
assert_equal :status, reflection.name
|
18
|
+
assert_equal 'Status', reflection.class_name
|
19
|
+
assert_equal :status_id, reflection.foreign_key
|
20
|
+
assert_equal ::Status, reflection.enumerator_class
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_reflection_with_custom_name_and_without_foreign_key
|
24
|
+
reflection = Enumerations::Reflection.new(:my_status, class_name: 'Status')
|
25
|
+
|
26
|
+
assert_equal :my_status, reflection.name
|
27
|
+
assert_equal 'Status', reflection.class_name
|
28
|
+
assert_equal :my_status_id, reflection.foreign_key
|
29
|
+
assert_equal ::Status, reflection.enumerator_class
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_reflection_with_class_name_as_constant
|
33
|
+
reflection = Enumerations::Reflection.new(:status, class_name: Status)
|
34
|
+
|
35
|
+
assert_equal 'Status', reflection.class_name
|
36
|
+
assert_equal ::Status, reflection.enumerator_class
|
10
37
|
end
|
11
38
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
|
1
|
+
require 'codeclimate-test-reporter'
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
2
4
|
require 'minitest/autorun'
|
3
5
|
require 'enumerations'
|
6
|
+
require 'active_record'
|
4
7
|
require 'pry'
|
5
8
|
|
6
|
-
|
7
|
-
class MockActiveRecordBase
|
8
|
-
include Enumeration
|
9
|
-
end
|
9
|
+
require_relative 'database_helper'
|
10
10
|
|
11
|
-
class Status <
|
11
|
+
class Status < Enumerations::Base
|
12
12
|
values draft: { id: 1, name: 'Draft' },
|
13
13
|
review_pending: { id: 2, name: 'Review pending' },
|
14
14
|
published: { id: 3, name: 'Published' }
|
@@ -17,9 +17,26 @@ class Status < Enumeration::Base
|
|
17
17
|
value :deleted, id: 5, deleted: true
|
18
18
|
end
|
19
19
|
|
20
|
-
class
|
20
|
+
class Role < Enumerations::Base
|
21
|
+
value :admin, id: 1, name: 'Admin', admin: true
|
22
|
+
value :editor, id: 2, name: 'Editor'
|
23
|
+
value :author, id: 3, name: 'Author'
|
24
|
+
|
25
|
+
def my_custom_name
|
26
|
+
['user', name].join('_')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Post < ActiveRecord::Base
|
21
31
|
attr_accessor :status_id, :some_other_status_id
|
22
32
|
|
23
33
|
enumeration :status
|
24
34
|
enumeration :different_status, foreign_key: :some_other_status_id, class_name: 'Status'
|
25
35
|
end
|
36
|
+
|
37
|
+
class User < ActiveRecord::Base
|
38
|
+
attr_accessor :role_id, :status_id
|
39
|
+
|
40
|
+
enumeration :role
|
41
|
+
enumeration :status
|
42
|
+
end
|
data/test/value_test.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class ValueTest < Minitest::Test
|
4
|
+
def test_equal_by_id
|
5
|
+
status = Status.find(:draft)
|
6
|
+
|
7
|
+
assert_equal true, status == 1
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_equal_by_symbol
|
11
|
+
status = Status.draft
|
12
|
+
|
13
|
+
assert_equal true, status == :draft
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_equal_by_enumeration
|
17
|
+
status = Status.draft
|
18
|
+
|
19
|
+
assert_equal true, status == Status.draft
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_not_equal_by_enumeration
|
23
|
+
status = Status.draft
|
24
|
+
|
25
|
+
assert_equal false, status == Status.published
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_with_defined_custom_attributes_visible
|
29
|
+
status = Status.find(:none)
|
30
|
+
|
31
|
+
assert_equal true, status.visible
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_with_defined_custom_attributes_deleted
|
35
|
+
status = Status.find(:deleted)
|
36
|
+
|
37
|
+
assert_equal true, status.deleted
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_without_defined_custom_attributes
|
41
|
+
status = Status.find(:draft)
|
42
|
+
|
43
|
+
assert_equal nil, status.visible
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_enumeration_to_i
|
47
|
+
status = Status.find(:draft)
|
48
|
+
|
49
|
+
assert_equal status.to_i, 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_enumeration_to_sym
|
53
|
+
status = Status.find(:draft)
|
54
|
+
|
55
|
+
assert_equal status.to_sym, :draft
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_enumeration_to_param
|
59
|
+
status = Status.find(:draft)
|
60
|
+
|
61
|
+
assert_equal status.to_param, 1
|
62
|
+
end
|
63
|
+
end
|
data/test/version_test.rb
CHANGED
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enumerations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomislav Car
|
8
8
|
- Nikica Jokic
|
9
9
|
- Nikola Santic
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2016-08-15 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -54,6 +54,48 @@ dependencies:
|
|
54
54
|
- - ">="
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '0'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: rake
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: codeclimate-test-reporter
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: sqlite3
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
57
99
|
description: Extends ActiveRecord with enumeration capabilites.
|
58
100
|
email:
|
59
101
|
- tomislav@infinum.hr
|
@@ -64,24 +106,27 @@ extensions: []
|
|
64
106
|
extra_rdoc_files: []
|
65
107
|
files:
|
66
108
|
- ".gitignore"
|
67
|
-
- ".travis.yml"
|
68
109
|
- Gemfile
|
69
110
|
- Rakefile
|
70
111
|
- Readme.md
|
71
112
|
- enumerations.gemspec
|
72
113
|
- lib/enumerations.rb
|
73
114
|
- lib/enumerations/base.rb
|
115
|
+
- lib/enumerations/finder_methods.rb
|
74
116
|
- lib/enumerations/reflection.rb
|
117
|
+
- lib/enumerations/value.rb
|
75
118
|
- lib/enumerations/version.rb
|
76
119
|
- test/base_test.rb
|
120
|
+
- test/database_helper.rb
|
77
121
|
- test/enumerations_test.rb
|
78
122
|
- test/reflection_test.rb
|
79
123
|
- test/test_helper.rb
|
124
|
+
- test/value_test.rb
|
80
125
|
- test/version_test.rb
|
81
126
|
homepage: https://github.com/infinum/enumerations
|
82
127
|
licenses: []
|
83
128
|
metadata: {}
|
84
|
-
post_install_message:
|
129
|
+
post_install_message:
|
85
130
|
rdoc_options: []
|
86
131
|
require_paths:
|
87
132
|
- lib
|
@@ -96,13 +141,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
141
|
- !ruby/object:Gem::Version
|
97
142
|
version: '0'
|
98
143
|
requirements: []
|
99
|
-
|
100
|
-
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.5.1
|
146
|
+
signing_key:
|
101
147
|
specification_version: 4
|
102
148
|
summary: Enumerations for ActiveRecord!
|
103
149
|
test_files:
|
104
150
|
- test/base_test.rb
|
151
|
+
- test/database_helper.rb
|
105
152
|
- test/enumerations_test.rb
|
106
153
|
- test/reflection_test.rb
|
107
154
|
- test/test_helper.rb
|
155
|
+
- test/value_test.rb
|
108
156
|
- test/version_test.rb
|