mongoid_enum 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b67dcfd5fb5d061dd20797d9ebe7dc54114030d2
4
+ data.tar.gz: 982b2d073dd01bb563f8446b05f37a9e30dee7a7
5
+ SHA512:
6
+ metadata.gz: 19171e4d95f8c56c387306f2745af4a685ac3122e5d9804dfe9bc1db84866a32fdde3fa90f5d2805abe8a6522f9d44d2d63122c94739412a24378bccbd92f13d
7
+ data.tar.gz: 0732be4b09f52e3b8a6b3896d52c61b51aa5fbdcf31a3d9212ca7b26e7bc92e8f06733c22b30a251025119b5150309981a638d5ea23c91122c025cb16cd55ebe
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2013 David Heinemeier Hansson and other Rails contributors
2
+ Copyright (c) 2015 Adam Wróbel
3
+
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Introduction
2
+
3
+ Enum implementation for Mongoid. Similar to that found in ActiveRecord.
4
+
5
+ # Installation
6
+
7
+ gem "mongoid_enum"
8
+
9
+ # Usage
10
+
11
+ By default values saved to DB are strings matching enum labels.
12
+
13
+ class Conversation
14
+ include Mongoid::Document
15
+ include Mongoid::Enum
16
+
17
+ enum status: [ :active, :archived ]
18
+ end
19
+
20
+ conversation = Conversation.active.first
21
+ conversation.active? # true
22
+ conversation.archived! # immediately saves document
23
+ conversation.active? # false
24
+ conversation["status"] # "archived"
25
+
26
+ You can use other Mongo value types (numbers, booleans, nil) when explicitly defining
27
+ the enum mapping:
28
+
29
+ class Part
30
+ include Mongoid::Document
31
+ include Mongoid::Enum
32
+
33
+ enum quality_control: {pending: nil, passed: true, failed: false}, _prefix: :qc
34
+ end
35
+
36
+ part = Part.qc_pending.first
37
+ part.quality_control # "pending"
38
+ part.qc_passed!
39
+ part.quality_control # "passed"
40
+ part["quality_control"] # true
41
+
42
+ Enum values are validated.
43
+
44
+ part.quality_control = "unknown value"
45
+ part.valid? # false
46
+
47
+ You can access the mapping as hash with indifferent access via class level constant.
48
+ Constant name is a pluralized field name set in SCREAMING\_SNAKE\_CASE.
49
+
50
+ Part::QUALITY_CONTROLS["passed"] # true
51
+ Part::QUALITY_CONTROLS[:failed] # false
52
+
53
+ # Differences from ActiveRecord
54
+
55
+ 1. Default values are strings, not integers. I think it fits MongoDB better.
56
+
57
+ 2. Mapping hash is accessible via class constant instead of class method. I prefer
58
+ constant because it is accessible without prefix both on class methods and instance
59
+ methods. Also I think SCREAMING\_SNAKE\_CASE is less likely to cause name conflicts.
60
+
61
+ 3. I do not raise exception when invalid value is assigned. Instead document fails
62
+ validation. Assigned invalid value is remembered and can be corrected in forms.
63
+ Exception will still be raised if you try to save invalid label by skipping validation
64
+ (`save validate: false`). That's because the document does not know how to convert
65
+ invalid enum option to database value.
66
+
67
+ # Credits
68
+
69
+ Original implementation for ActiveRecord was created by David Heinemeier Hansson.
70
+ Following modifications by other Rails contributors. Port to Mongoid prepared by Adam
71
+ Wróbel.
@@ -0,0 +1,43 @@
1
+ module Mongoid
2
+ module Enum
3
+ # Internal: Mapping wrapper used as type for enum fields.
4
+ #
5
+ # Handles label-value conversion.
6
+ class EnumType # :nodoc:
7
+ attr_reader :mappings
8
+
9
+ def initialize(mappings)
10
+ @mappings = mappings
11
+ end
12
+
13
+ def mongoize(key)
14
+ if key.blank?
15
+ value = nil
16
+ elsif mappings.key? key
17
+ value = mappings[key]
18
+ elsif mappings.value? key
19
+ # XXX Mongoid may try to mongoize multiple times so if key is a valid value
20
+ # assume that it has already been converted to value.
21
+ value = key
22
+ else
23
+ value = Enum::InvalidKey.new(key)
24
+ end
25
+ value
26
+ end
27
+ alias_method :evolve, :mongoize
28
+
29
+ def demongoize(value)
30
+ if mappings.value? value
31
+ key = mappings.key value
32
+ elsif value.blank?
33
+ key = nil
34
+ elsif value.is_a? Enum::InvalidKey
35
+ key = value.original_key
36
+ else
37
+ key = Enum::InvalidValue.new(value)
38
+ end
39
+ key
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ module Mongoid
2
+ module Enum
3
+ # raised when InvalidKey is forced to be saved in DB (skipping validation)
4
+ class InvalidKeyError < StandardError
5
+ end
6
+
7
+ # Internal: Wraps invalid keys passed to setter so that getter returns the same key.
8
+ class InvalidKey # :nodoc:
9
+ attr_reader :original_key
10
+
11
+ def initialize(key)
12
+ @original_key = key
13
+ end
14
+
15
+ def raise_error
16
+ raise InvalidKeyError, "invalid enum key: #{original_key}"
17
+ end
18
+ alias_method :bson_type, :raise_error
19
+ alias_method :to_bson, :raise_error
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module Mongoid
2
+ module Enum
3
+ # Public: Wraps invalid values loaded from DB before returning them via getter.
4
+ class InvalidValue
5
+ # Public: Holds the value loaded from DB that is not part of defined enum.
6
+ attr_reader :database_value
7
+
8
+ # :nodoc:
9
+ def initialize(value)
10
+ @database_value = value
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,255 @@
1
+ require "mongoid/enum/enum_type"
2
+ require "mongoid/enum/invalid_key"
3
+ require "mongoid/enum/invalid_value"
4
+
5
+ module Mongoid # :nodoc:
6
+ # Declare an enum field with scope and value checking helper methods. Example:
7
+ #
8
+ # class Conversation
9
+ # include Mongoid::Document
10
+ # include Mongoid::Enum
11
+ #
12
+ # enum status: [ :active, :archived ]
13
+ # end
14
+ #
15
+ # # conversation.update! status: "active"
16
+ # conversation.active!
17
+ # conversation.active? # => true
18
+ # conversation.status # => "active"
19
+ #
20
+ # # conversation.update! status: "archived"
21
+ # conversation.archived!
22
+ # conversation.archived? # => true
23
+ # conversation.status # => "archived"
24
+ #
25
+ # conversation.status = nil
26
+ # conversation.status.nil? # => true
27
+ # conversation.status # => nil
28
+ #
29
+ # Scopes based on the allowed values of the enum field will be provided
30
+ # as well. With the above example:
31
+ #
32
+ # Conversation.active
33
+ # Conversation.archived
34
+ #
35
+ # Of course, you can also query them directly if the scopes don't fit your
36
+ # needs:
37
+ #
38
+ # Conversation.where(status: [:active, :archived])
39
+ # Conversation.not.where(status: :active)
40
+ #
41
+ # You can set the default value:
42
+ #
43
+ # class Conversation
44
+ # include Mongoid::Document
45
+ # include Mongoid::Enum
46
+ #
47
+ # enum status: [ :active, :archived ], _default: :active
48
+ # end
49
+ #
50
+ # Finally, it's also possible to explicitly map the relation between attribute and
51
+ # values stored in the database with a hash. The values don't even need to be Strings.
52
+ # Numbers, Booleans, nil and some others are allowed.
53
+ #
54
+ # class Conversation
55
+ # include Mongoid::Document
56
+ # include Mongoid::Enum
57
+ #
58
+ # enum status: { active: 0, archived: 1 }
59
+ # end
60
+ #
61
+ # The mappings are exposed through a class constant with the pluralized attribute
62
+ # name. It defines the mapping in a +HashWithIndifferentAccess+:
63
+ #
64
+ # Conversation::STATUSES[:active] # => 0
65
+ # Conversation::STATUSES["archived"] # => 1
66
+ #
67
+ # Defining an enum automatically adds a validator on its field. Assigning values
68
+ # not included in enum definition will make the document invalid.
69
+ #
70
+ # conversation = Conversation.new
71
+ # conversation.status = :unknown
72
+ # conversation.valid? # false
73
+ #
74
+ # Default validator allows nil values. Add your own presence validator if you require
75
+ # a value for the enum field.
76
+ #
77
+ # class Conversation
78
+ # include Mongoid::Document
79
+ # include Mongoid::Enum
80
+ #
81
+ # enum status: { active: 0, archived: 1 }
82
+ #
83
+ # validates :status, presence: true
84
+ # end
85
+ #
86
+ #
87
+ # You can use the +:_prefix+ or +:_suffix+ options when you need to define
88
+ # multiple enums with same values. If the passed value is +true+, the methods
89
+ # are prefixed/suffixed with the name of the field. It is also possible to
90
+ # supply a custom value:
91
+ #
92
+ # class Conversation
93
+ # include Mongoid::Document
94
+ # include Mongoid::Enum
95
+ #
96
+ # enum status: [:active, :archived], _suffix: true
97
+ # enum comments_status: [:active, :inactive], _prefix: :comments
98
+ # end
99
+ #
100
+ # With the above example, the bang and predicate methods along with the
101
+ # associated scopes are now prefixed and/or suffixed accordingly:
102
+ #
103
+ # conversation.active_status!
104
+ # conversation.archived_status? # => false
105
+ #
106
+ # conversation.comments_inactive!
107
+ # conversation.comments_active? # => false
108
+ #
109
+ # If you want to you can give nil value an explicit label.
110
+ #
111
+ # class Part
112
+ # include Mongoid::Document
113
+ # include Mongoid::Enum
114
+ #
115
+ # enum quality_control: {pending: nil, passed: true, failed: false}, _prefix: :qc
116
+ # end
117
+ #
118
+ # part = Part.qc_pending.first
119
+ # part.qc_pending? # true
120
+ # part.qc_passed!
121
+ # part.qc_pending? # false
122
+ module Enum
123
+ extend ActiveSupport::Concern
124
+
125
+ included do
126
+ class_attribute(:enums)
127
+ self.enums = {}
128
+ end
129
+
130
+ # :nodoc:
131
+ module ClassMethods
132
+ # Define enum field on the model. See description of Mongoid::Enum
133
+ def enum(definitions)
134
+ klass = self
135
+ enum_prefix = definitions.delete(:_prefix)
136
+ enum_suffix = definitions.delete(:_suffix)
137
+ default_key = definitions.delete(:_default)
138
+
139
+ definitions.each do |name, values|
140
+ enum_values = ActiveSupport::HashWithIndifferentAccess.new
141
+ name = name.to_sym
142
+ const_name = name.to_s.pluralize.upcase
143
+
144
+ if klass.const_defined?(const_name)
145
+ fail ArgumentError, "Defining enum :#{name} on #{klass} would " \
146
+ "overwrite existing constant #{klass}::#{const_name}"
147
+ end
148
+
149
+ detect_enum_conflict!(name, name)
150
+ detect_enum_conflict!(name, "#{name}=")
151
+
152
+ if values.respond_to? :each_pair
153
+ values.each_pair { |key, value| enum_values[key.to_s] = value }
154
+ else
155
+ values.each { |v| enum_values[v.to_s] = v.to_s }
156
+ end
157
+
158
+ enum_values.each do |key, value|
159
+ key.freeze
160
+ value.freeze
161
+ end
162
+ enum_values.freeze
163
+
164
+ if default_key && !enum_values.key?(default_key)
165
+ fail ArgumentError, "default key #{default_key} is not among enum options"
166
+ end
167
+
168
+ field name, type: EnumType.new(enum_values), default: default_key
169
+
170
+ klass.const_set const_name, enum_values
171
+ klass.validates name,
172
+ inclusion: {
173
+ in: enum_values.keys,
174
+ allow_nil: true,
175
+ message: "is invalid"
176
+ }
177
+
178
+ _enum_methods_module.module_eval do
179
+ enum_values.each do |key, value|
180
+ if enum_prefix == true
181
+ prefix = "#{name}_"
182
+ elsif enum_prefix
183
+ prefix = "#{enum_prefix}_"
184
+ end
185
+ if enum_suffix == true
186
+ suffix = "_#{name}"
187
+ elsif enum_suffix
188
+ suffix = "_#{enum_suffix}"
189
+ end
190
+
191
+ value_method_name = "#{prefix}#{key}#{suffix}"
192
+
193
+ # def active?() status == 0 end
194
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
195
+ define_method("#{value_method_name}?") { self[name] == value }
196
+
197
+ # def active!() update! status: :active end
198
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
199
+ define_method("#{value_method_name}!") { update! name => key }
200
+
201
+ # scope :active, -> { where status: 0 }
202
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
203
+ klass.scope value_method_name, -> { klass.where name => key }
204
+ end
205
+ end
206
+
207
+ # dup so children classes don't add their own enums to parent definitions
208
+ self.enums = enums.dup
209
+
210
+ enums[name] = enum_values
211
+ enums.freeze
212
+ end
213
+ end
214
+
215
+ private
216
+
217
+ def _enum_methods_module
218
+ @_enum_methods_module ||= begin
219
+ mod = Module.new
220
+ include mod
221
+ mod
222
+ end
223
+ end
224
+
225
+ # :nodoc:
226
+ ENUM_CONFLICT_MESSAGE = \
227
+ "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
228
+ "this will generate %{type} method \"%{method}\", which is already defined."
229
+
230
+ def detect_enum_conflict!(enum_name, method_name, class_method = false)
231
+ method_name = method_name.to_sym
232
+
233
+ if class_method
234
+ if self.respond_to?(method_name, true)
235
+ raise_conflict_error(enum_name, method_name, "class")
236
+ end
237
+ else
238
+ if Mongoid.destructive_fields.include?(method_name) ||
239
+ instance_methods.include?(method_name)
240
+ raise_conflict_error(enum_name, method_name, "instance")
241
+ end
242
+ end
243
+ end
244
+
245
+ def raise_conflict_error(enum_name, method_name, type)
246
+ fail ArgumentError, ENUM_CONFLICT_MESSAGE % {
247
+ enum: enum_name,
248
+ klass: name,
249
+ type: type,
250
+ method: method_name
251
+ }
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1 @@
1
+ require "mongoid/enum"
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid_enum
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Wróbel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mongoid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: factory_girl
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.35.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.35.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: rdoc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.2'
69
+ description: Fields with closed set of possible values and helper methods to query/set
70
+ them by label.
71
+ email: adam@adamwrobel.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files:
75
+ - README.md
76
+ files:
77
+ - LICENSE
78
+ - README.md
79
+ - lib/mongoid/enum.rb
80
+ - lib/mongoid/enum/enum_type.rb
81
+ - lib/mongoid/enum/invalid_key.rb
82
+ - lib/mongoid/enum/invalid_value.rb
83
+ - lib/mongoid_enum.rb
84
+ homepage: https://github.com/amw/mongoid_enum
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options:
90
+ - "--main"
91
+ - README.md
92
+ - "--markup=tomdoc"
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 1.9.3
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.4.5.1
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Enum fields for Mongoid
111
+ test_files: []
112
+ has_rdoc: