enumerations 2.2.3 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6bff6ddbd1cfb803b3463c11796daf3c81f1c741
4
- data.tar.gz: 20a258a2ed4129ed92b044f2933fb366d45aba6e
3
+ metadata.gz: ce06adb418c77b2cfe89af486bff75362b4183bc
4
+ data.tar.gz: e65afcbf46c76d46625443de26b52445a7a12a29
5
5
  SHA512:
6
- metadata.gz: 584c186148b1038d4aadbdd1ee4cb230ee32b40332aca1888933a2ac2813ad61dd356eb548701ab52a10a9e426122ba70e6022cc6254001431b3fd2e51c3d198
7
- data.tar.gz: 7048a1acf02560a3311d3956d09ce76d25651a631bf7b4a169d7513d28f4f656142ec93a0f44fff6667022d8651f07cdc81104b2eab969c5dabb0468c65bc72e
6
+ metadata.gz: 168a363ddbbcf47dbe58f2024d455c6b5aae795360b92ad947d77f396ca25a4714067e4affb7e283d72ba80539d42f573d83cec68e68d2ba3d8334721f1e346e
7
+ data.tar.gz: d09864e055364e4415870471d0ed16f2f00a7a2afbda0604f4d8dd37c65d165054de7524a5ac62f5173d71bd4c8eca4a626d6993ff06d4d6fa15d7ee1cab125f
@@ -0,0 +1,8 @@
1
+ LineLength:
2
+ Max: 100
3
+
4
+ Documentation:
5
+ Enabled: False
6
+
7
+ Style/SymbolArray:
8
+ Enabled: False
data/Readme.md CHANGED
@@ -6,7 +6,7 @@ Enumerations
6
6
  [![Build Status](https://semaphoreci.com/api/v1/infinum/enumerations/branches/master/shields_badge.svg)](https://semaphoreci.com/infinum/enumerations)
7
7
  [![Test Coverage](https://codeclimate.com/github/infinum/enumerations/badges/coverage.svg)](https://codeclimate.com/github/infinum/enumerations/coverage)
8
8
 
9
- Rails plugin for enumerations in ActiveRecord models.
9
+ Rails plugin for Enumerations in ActiveRecord models.
10
10
 
11
11
  Installation
12
12
  ============
@@ -83,6 +83,8 @@ Or you can set enumerations by `symbol`:
83
83
  @post.status = Status.find(:draft)
84
84
  ```
85
85
 
86
+ > If you try to set value that is not an Enumeration value (except `nil`), you will get an `Enumerations::InvalidValueError` exception.
87
+
86
88
  Also, you can set enumeration value like this:
87
89
 
88
90
  ```ruby
@@ -175,6 +177,14 @@ following format:
175
177
 
176
178
  ```ruby
177
179
  with_#{enumeration_name}_#{enumeration_value_name}
180
+ without_#{enumeration_name}_#{enumeration_value_name}
181
+ ```
182
+
183
+ or you can use the following scope and pass an array of enumerations:
184
+
185
+ ```ruby
186
+ with_#{enumeration_name}(enumeration, ...)
187
+ without_#{enumeration_name}(enumeration, ...)
178
188
  ```
179
189
 
180
190
  Examples:
@@ -185,7 +195,13 @@ class Post < ActiveRecord::Base
185
195
  end
186
196
 
187
197
  Post.with_status_draft # => <#ActiveRecord::Relation []>
188
- Post.with_status_review_pending # => <#ActiveRecord::Relation []>
198
+ Post.without_status_review_pending # => <#ActiveRecord::Relation []>
199
+ Post.with_status(:draft) # => <#ActiveRecord::Relation []>
200
+ Post.without_status(:draft) # => <#ActiveRecord::Relation []>
201
+ Post.with_status(Status.draft) # => <#ActiveRecord::Relation []>
202
+ Post.with_status(:draft, :review_pending) # => <#ActiveRecord::Relation []>
203
+ Post.with_status(Status.draft, 'published') # => <#ActiveRecord::Relation []>
204
+ Post.with_status([:draft, :review_pending]) # => <#ActiveRecord::Relation []>
189
205
  ```
190
206
 
191
207
  ```ruby
@@ -195,6 +211,8 @@ end
195
211
 
196
212
  Post.with_my_status_draft # => <#ActiveRecord::Relation []>
197
213
  Post.with_my_status_review_pending # => <#ActiveRecord::Relation []>
214
+ Post.with_my_status(:draft) # => <#ActiveRecord::Relation []>
215
+ Post.without_my_status(:draft) # => <#ActiveRecord::Relation []>
198
216
  ```
199
217
 
200
218
  Each scope returns all records with specified enumeration value.
@@ -219,7 +237,7 @@ Except `name` you can specify any other attributes to your enumerations:
219
237
 
220
238
  ```ruby
221
239
  class Status < Enumerations::Base
222
- value :draft, id: 1, name: 'Draft'
240
+ value :draft, id: 1, name: 'Draft', published: false
223
241
  value :review_pending, id: 2, name: 'Review pending', description: 'Some description...'
224
242
  value :published, id: 3, name: 'Published', published: true
225
243
  value :other # passing no attributes is also allowed
@@ -234,6 +252,16 @@ Status.review_pending.description # => 'Some description...'
234
252
  Status.draft.description # => nil
235
253
  ```
236
254
 
255
+ For each attribute, you have predicate method. Predicate methods are actually calling `present?`
256
+ method on attribute value:
257
+
258
+ ```ruby
259
+ Status.draft.name? # => true
260
+ Status.draft.published? # => false
261
+ Status.published.published? # => true
262
+ Status.other.name? # => false
263
+ ```
264
+
237
265
  Translations
238
266
  =====
239
267
 
@@ -291,7 +319,7 @@ If you set enumeration as:
291
319
  ```ruby
292
320
  enumeration :status
293
321
  ```
294
- then model should have `status`column (as `String` type).
322
+ then model should have `status` column (as `String` type).
295
323
  If you want save an `ID` to this column, you can set `foreign_key_suffix` to `id`.
296
324
  Then model should have `status_id` column.
297
325
 
@@ -323,6 +351,17 @@ post = Post.new
323
351
  post.status = Status.draft # => post.status_id = 1
324
352
  ```
325
353
 
354
+ If you want to configure primary key per enumeration class, you can use `primary_key=` class method:
355
+
356
+ ```ruby
357
+ class Status < Enumerations::Base
358
+ self.primary_key = :id
359
+
360
+ value :draft, id: 1, name: 'Draft'
361
+ value :review_pending, id: 2, name: 'Review pending'
362
+ end
363
+ ```
364
+
326
365
  Database Enumerations
327
366
  =====================
328
367
 
@@ -3,7 +3,7 @@ require File.expand_path('../lib/enumerations/version', __FILE__)
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'enumerations'
5
5
  s.version = Enumerations::VERSION
6
- s.date = '2016-09-20'
6
+ s.date = '2017-09-08'
7
7
  s.summary = 'Enumerations for ActiveRecord!'
8
8
  s.description = 'Extends ActiveRecord with enumeration capabilites.'
9
9
  s.homepage = 'https://github.com/infinum/enumerations'
@@ -7,7 +7,7 @@ require 'enumerations/configuration'
7
7
  require 'enumerations/version'
8
8
  require 'enumerations/base'
9
9
  require 'enumerations/reflection'
10
- require 'enumerations/enumerations_error'
10
+ require 'enumerations/errors'
11
11
 
12
12
  module Enumerations
13
13
  extend ActiveSupport::Concern
@@ -58,7 +58,8 @@ module Enumerations
58
58
  define_getter_method(reflection)
59
59
  define_setter_method(reflection)
60
60
  define_bang_methods(reflection)
61
- define_scopes(reflection)
61
+ define_scopes_for_each_enumeration_value(reflection)
62
+ define_enumeration_scope(reflection)
62
63
 
63
64
  self._enumerations += [reflection]
64
65
  end
@@ -86,9 +87,11 @@ module Enumerations
86
87
  define_method("#{reflection.name}=") do |other|
87
88
  enumeration_value = reflection.enumerator_class.find(other)
88
89
 
90
+ raise Enumerations::InvalidValueError if other.present? && enumeration_value.nil?
91
+
89
92
  self[reflection.foreign_key] =
90
93
  enumeration_value &&
91
- enumeration_value.send(Enumerations.configuration.primary_key || :symbol)
94
+ enumeration_value.send(reflection.enumerator_class.primary_key || :symbol)
92
95
  end
93
96
  end
94
97
 
@@ -116,12 +119,44 @@ module Enumerations
116
119
  # User.with_role_admin => <#ActiveRecord::Relation []>
117
120
  # User.with_role_editor => <#ActiveRecord::Relation []>
118
121
  #
119
- def define_scopes(reflection)
122
+ def define_scopes_for_each_enumeration_value(reflection)
120
123
  reflection.enumerator_class.all.each do |enumeration|
121
- foreign_key = enumeration.send(Enumerations.configuration.primary_key || :symbol)
124
+ foreign_key = enumeration.send(reflection.enumerator_class.primary_key || :symbol)
125
+
126
+ scope("with_#{reflection.name}_#{enumeration.symbol}",
127
+ -> { where(reflection.foreign_key => foreign_key) })
128
+
129
+ scope("without_#{reflection.name}_#{enumeration.symbol}",
130
+ -> { where.not(reflection.foreign_key => foreign_key) })
131
+ end
132
+ end
133
+
134
+ # Scope for enumerated ActiveRecord model.
135
+ # Format of scope name is with_#{enumeration_name}(*enumerations).
136
+ #
137
+ # Example:
138
+ #
139
+ # User.with_role(:admin) => <#ActiveRecord::Relation []>
140
+ # User.with_role(:admin, Role.editor) => <#ActiveRecord::Relation []>
141
+ #
142
+ def define_enumeration_scope(reflection)
143
+ scope("with_#{reflection.name}",
144
+ lambda do |*symbols|
145
+ where(reflection.foreign_key => fetch_foreign_key_values(reflection, symbols))
146
+ end)
147
+
148
+ scope("without_#{reflection.name}",
149
+ lambda do |*symbols|
150
+ where.not(reflection.foreign_key => fetch_foreign_key_values(reflection, symbols))
151
+ end)
152
+ end
153
+
154
+ def fetch_foreign_key_values(reflection, *symbols)
155
+ symbols.flatten.map do |symbol|
156
+ enumeration_value = reflection.enumerator_class.find(symbol)
122
157
 
123
- scope "with_#{reflection.name}_#{enumeration.symbol}",
124
- -> { where(reflection.foreign_key => foreign_key) }
158
+ enumeration_value &&
159
+ enumeration_value.send(reflection.enumerator_class.primary_key || :symbol)
125
160
  end
126
161
  end
127
162
  end
@@ -8,10 +8,11 @@ module Enumerations
8
8
  extend Enumerations::FinderMethods
9
9
  include Enumerations::Value
10
10
 
11
- class_attribute :_values, :_symbol_index
11
+ class_attribute :_values, :_symbol_index, :_primary_key
12
12
 
13
13
  self._values = {}
14
14
  self._symbol_index = {}
15
+ self._primary_key = nil
15
16
 
16
17
  # Adding new value to enumeration
17
18
  #
@@ -79,16 +80,45 @@ module Enumerations
79
80
  _values.values
80
81
  end
81
82
 
83
+ # Sets primary key for enumeration class.
84
+ #
85
+ # Example:
86
+ #
87
+ # class Status < Enumeration::Base
88
+ # primary_key = :id
89
+ # end
90
+ #
91
+ def self.primary_key=(key)
92
+ self._primary_key = key && key.to_sym
93
+ end
94
+
95
+ # Gets primary key for enumeration class.
96
+ #
97
+ # Example:
98
+ #
99
+ # Status.primary_key => # nil
100
+ #
101
+ # class Status < Enumeration::Base
102
+ # primary_key = :id
103
+ # end
104
+ #
105
+ # Status.primary_key => # :id
106
+ #
107
+ def self.primary_key
108
+ _primary_key || Enumerations.configuration.primary_key
109
+ end
110
+
82
111
  def self.validate_symbol_and_primary_key(symbol, attributes)
83
- raise EnumerationsError, "Duplicate symbol #{symbol}" if find(symbol)
112
+ raise Enumerations::DuplicatedSymbolError if find(symbol)
84
113
 
85
- primary_key = Enumerations.configuration.primary_key
86
114
  return if primary_key.nil?
87
115
 
88
- raise EnumerationsError, 'Enumeration primary key is required' if attributes[primary_key].nil?
89
- raise EnumerationsError, "Duplicate primary key #{attributes[primary_key]}" if find(attributes[primary_key])
116
+ primary_key_value = attributes[primary_key]
117
+
118
+ raise Enumerations::MissingPrimaryKeyError if primary_key_value.nil?
119
+ raise Enumerations::DuplicatedPrimaryKeyError if find(primary_key_value)
90
120
 
91
- self._symbol_index = _symbol_index.merge(symbol => attributes[primary_key])
121
+ self._symbol_index = _symbol_index.merge(symbol => primary_key_value)
92
122
  end
93
123
 
94
124
  private_class_method :validate_symbol_and_primary_key
@@ -0,0 +1,7 @@
1
+ module Enumerations
2
+ class Error < StandardError; end
3
+ class DuplicatedSymbolError < Error; end
4
+ class MissingPrimaryKeyError < Error; end
5
+ class DuplicatedPrimaryKeyError < Error; end
6
+ class InvalidValueError < Error; end
7
+ end
@@ -1,6 +1,6 @@
1
1
  module Enumerations
2
2
  class Reflection
3
- attr_reader :name
3
+ attr_reader :name, :options
4
4
 
5
5
  def initialize(name, options = {})
6
6
  @name = name
@@ -8,11 +8,11 @@ module Enumerations
8
8
  end
9
9
 
10
10
  def class_name
11
- @class_name ||= (@options[:class_name] || name).to_s.camelize
11
+ @class_name ||= (options[:class_name] || name).to_s.camelize
12
12
  end
13
13
 
14
14
  def foreign_key
15
- @foreign_key ||= (@options[:foreign_key] || default_foreign_key_name).to_sym
15
+ @foreign_key ||= (options[:foreign_key] || default_foreign_key_name).to_sym
16
16
  end
17
17
 
18
18
  def enumerator_class
@@ -39,6 +39,7 @@ module Enumerations
39
39
  def create_instance_methods
40
40
  define_attributes_getters
41
41
  define_value_checking_method
42
+ define_predicate_methods_for_attributes
42
43
  end
43
44
 
44
45
  # Getters for all attributes
@@ -83,5 +84,28 @@ module Enumerations
83
84
  __callee__[0..-2].to_sym == symbol
84
85
  end
85
86
  end
87
+
88
+ # Predicate methods for all attributes
89
+ #
90
+ # Example:
91
+ #
92
+ # class Role < Enumerations::Base
93
+ # value :admin, name: 'Administrator', active: false
94
+ # end
95
+ #
96
+ # user.role.name? => # true
97
+ # user.role.admin? => # false
98
+ #
99
+ def define_predicate_methods_for_attributes
100
+ @attributes.each do |key, _|
101
+ method_name = "#{key}?"
102
+
103
+ next if respond_to?(method_name.to_sym)
104
+
105
+ self.class.send :define_method, method_name do
106
+ @attributes[key].present?
107
+ end
108
+ end
109
+ end
86
110
  end
87
111
  end
@@ -1,3 +1,3 @@
1
1
  module Enumerations
2
- VERSION = '2.2.3'.freeze
2
+ VERSION = '2.3.1'.freeze
3
3
  end
@@ -16,7 +16,7 @@ class BaseTest < Minitest::Test
16
16
  end
17
17
 
18
18
  def test_duplicated_symbol
19
- assert_raises EnumerationsError, 'Duplicate symbol draft' do
19
+ assert_raises Enumerations::DuplicatedSymbolError do
20
20
  obj = Class.new(Enumerations::Base)
21
21
 
22
22
  obj.value :draft, id: 1, name: 'Draft'
@@ -0,0 +1,27 @@
1
+ module Configuration
2
+ ::CustomConfigurationEnum = Class.new(Enumerations::Base)
3
+
4
+ CustomConfigurationEnum.primary_key = :id
5
+ CustomConfigurationEnum.value :draft, id: 1
6
+
7
+ ::CustomConfigurationModel = Class.new(ActiveRecord::Base)
8
+ CustomConfigurationModel.table_name = :custom_models
9
+ CustomConfigurationModel.enumeration :custom_configuration_enum, foreign_key: :custom_enum_id
10
+
11
+ class CustomConfigurationTest < Minitest::Test
12
+ def test_primary_key_value
13
+ assert_equal :id, CustomConfigurationEnum.primary_key
14
+ end
15
+
16
+ def test_primary_key_value_is_not_set_in_configuration
17
+ assert_nil Enumerations.configuration.primary_key
18
+ end
19
+
20
+ def test_value_set_for_custom_foreign_key_config
21
+ model = CustomConfigurationModel.new
22
+ model.custom_configuration_enum = :draft
23
+
24
+ assert_equal :draft, model.custom_configuration_enum.symbol
25
+ end
26
+ end
27
+ end
@@ -37,7 +37,7 @@ class ConfigurationTest < Minitest::Test
37
37
  def test_required_primary_key_when_primary_key_configured
38
38
  Enumerations.configure { |config| config.primary_key = :id }
39
39
 
40
- assert_raises EnumerationsError, 'Enumeration primary key is required' do
40
+ assert_raises Enumerations::MissingPrimaryKeyError, 'Enumeration primary key is required' do
41
41
  Class.new(Enumerations::Base).value :draft, name: 'Draft'
42
42
  end
43
43
  end
@@ -45,7 +45,7 @@ class ConfigurationTest < Minitest::Test
45
45
  def test_duplicated_primary_key_when_primary_key_configured
46
46
  Enumerations.configure { |config| config.primary_key = :id }
47
47
 
48
- assert_raises EnumerationsError, 'Duplicate primary key 1' do
48
+ assert_raises Enumerations::DuplicatedPrimaryKeyError, 'Duplicate primary key 1' do
49
49
  Class.new(Enumerations::Base).values draft: { id: 1, name: 'Draft' },
50
50
  test: { id: 1, name: 'Draft' }
51
51
  end
@@ -1,5 +1,5 @@
1
1
  require_relative 'helpers/test_helper'
2
- require 'enumerations/enumerations_error'
2
+ require 'enumerations/errors'
3
3
 
4
4
  class EnumerationsTest < Minitest::Test
5
5
  def test_reflect_on_all_enumerations
@@ -75,21 +75,62 @@ class EnumerationsTest < Minitest::Test
75
75
  assert_equal 'published', u.status.to_s
76
76
  end
77
77
 
78
+ def test_enumerated_class_has_enumeration_scope
79
+ assert_respond_to User, :with_role
80
+ end
81
+
78
82
  def test_enumerated_class_has_scopes
79
83
  Role.all.each do |role|
80
84
  assert_respond_to User, ['with_role', role.to_s].join('_').to_sym
81
85
  end
82
86
  end
83
87
 
84
- def test_enumerated_class_scope_hash_value
88
+ def test_enumerated_class_enumeration_scope_hash_value
89
+ query_hash = User.with_role(:admin).where_values_hash.symbolize_keys
90
+
91
+ assert_equal query_hash, role: :admin
92
+ end
93
+
94
+ def test_enumerated_class_enumeration_without_scope_results
95
+ User.create(role: :admin)
96
+ User.create(role: :editor)
97
+
98
+ results = User.without_role(:admin)
99
+
100
+ assert_equal results, User.where.not(role: :admin)
101
+ end
102
+
103
+ def test_enumerated_class_enumeration_scope_hash_value_for_multiple_enums
104
+ query_hash = User.with_role(:admin, Role.author, 'editor').where_values_hash.symbolize_keys
105
+
106
+ assert_equal query_hash, role: [:admin, :author, :editor]
107
+ end
108
+
109
+ def test_enumerated_class_enumeration_scope_hash_value_for_multiple_enums_as_array
110
+ query_hash = User.with_role([:admin, Role.author, 'editor']).where_values_hash.symbolize_keys
111
+
112
+ assert_equal query_hash, role: [:admin, :author, :editor]
113
+ end
114
+
115
+ def test_enumerated_class_without_scope_hash_value
85
116
  query_hash = User.with_role_admin.where_values_hash.symbolize_keys
86
117
 
87
118
  assert_equal query_hash, role: :admin
88
119
  end
89
120
 
121
+ def test_enumerated_class_without_scope_results
122
+ User.create(role: :admin)
123
+ User.create(role: :editor)
124
+
125
+ results = User.without_role_admin
126
+
127
+ assert_equal results, User.where.not(role: :admin)
128
+ end
129
+
90
130
  def test_nonexistent_value_assignment
91
- user = User.new(role: :nonexistent_value)
92
- assert_nil user.role
131
+ assert_raises Enumerations::InvalidValueError do
132
+ User.new(role: :nonexistent_value)
133
+ end
93
134
  end
94
135
 
95
136
  def test_on_nil_value_assignment
@@ -68,4 +68,18 @@ class ValueTest < Minitest::Test
68
68
 
69
69
  assert_equal role.type, 'croatist'
70
70
  end
71
+
72
+ def test_enumeration_attribute_predicate_methods
73
+ status = Status.none
74
+
75
+ assert_equal status.name?, true
76
+ assert_equal status.visible?, true
77
+ assert_equal status.deleted?, false
78
+ end
79
+
80
+ def test_enumeration_attribute_predicate_method_on_undefined_attribute
81
+ status = Status.deleted
82
+
83
+ assert_equal status.name?, false
84
+ end
71
85
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enumerations
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.3
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomislav Car
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2016-09-20 00:00:00.000000000 Z
15
+ date: 2017-09-08 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activerecord
@@ -124,6 +124,7 @@ extensions: []
124
124
  extra_rdoc_files: []
125
125
  files:
126
126
  - ".gitignore"
127
+ - ".rubocop.yml"
127
128
  - Gemfile
128
129
  - Migrate.md
129
130
  - Rakefile
@@ -132,13 +133,14 @@ files:
132
133
  - lib/enumerations.rb
133
134
  - lib/enumerations/base.rb
134
135
  - lib/enumerations/configuration.rb
135
- - lib/enumerations/enumerations_error.rb
136
+ - lib/enumerations/errors.rb
136
137
  - lib/enumerations/finder_methods.rb
137
138
  - lib/enumerations/reflection.rb
138
139
  - lib/enumerations/value.rb
139
140
  - lib/enumerations/version.rb
140
141
  - test/base_test.rb
141
142
  - test/configuration/configuration.rb
143
+ - test/configuration/custom_configuration_test.rb
142
144
  - test/configuration/enumerations_test.rb
143
145
  - test/configuration/finder_test.rb
144
146
  - test/configuration_test.rb
@@ -170,13 +172,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
172
  version: '0'
171
173
  requirements: []
172
174
  rubyforge_project:
173
- rubygems_version: 2.6.8
175
+ rubygems_version: 2.6.11
174
176
  signing_key:
175
177
  specification_version: 4
176
178
  summary: Enumerations for ActiveRecord!
177
179
  test_files:
178
180
  - test/base_test.rb
179
181
  - test/configuration/configuration.rb
182
+ - test/configuration/custom_configuration_test.rb
180
183
  - test/configuration/enumerations_test.rb
181
184
  - test/configuration/finder_test.rb
182
185
  - test/configuration_test.rb
@@ -1,2 +0,0 @@
1
- class EnumerationsError < RuntimeError
2
- end