edave-enumerated_attribute 0.2.18

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.
Files changed (107) hide show
  1. data/.gitignore +15 -0
  2. data/CHANGELOG.rdoc +49 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +558 -0
  5. data/Rakefile +30 -0
  6. data/init.rb +1 -0
  7. data/lib/enumerated_attribute.rb +24 -0
  8. data/lib/enumerated_attribute/attribute.rb +79 -0
  9. data/lib/enumerated_attribute/attribute/arguments.rb +48 -0
  10. data/lib/enumerated_attribute/attribute/attribute_descriptor.rb +51 -0
  11. data/lib/enumerated_attribute/attribute/class_methods.rb +45 -0
  12. data/lib/enumerated_attribute/attribute/instance_methods.rb +96 -0
  13. data/lib/enumerated_attribute/integrations.rb +33 -0
  14. data/lib/enumerated_attribute/integrations/active_record.rb +114 -0
  15. data/lib/enumerated_attribute/integrations/datamapper.rb +6 -0
  16. data/lib/enumerated_attribute/integrations/default.rb +25 -0
  17. data/lib/enumerated_attribute/integrations/object.rb +47 -0
  18. data/lib/enumerated_attribute/method_definition_dsl.rb +142 -0
  19. data/lib/enumerated_attribute/rails_helpers.rb +99 -0
  20. data/spec/active_record/active_record_spec.rb +384 -0
  21. data/spec/active_record/association_test_classes.rb +40 -0
  22. data/spec/active_record/associations_spec.rb +130 -0
  23. data/spec/active_record/cfg.rb +6 -0
  24. data/spec/active_record/df.rb +12 -0
  25. data/spec/active_record/inheritance_classes.rb +19 -0
  26. data/spec/active_record/inheritance_spec.rb +60 -0
  27. data/spec/active_record/race_car.rb +15 -0
  28. data/spec/active_record/single_table_inheritance_spec.rb +0 -0
  29. data/spec/active_record/sti_classes.rb +10 -0
  30. data/spec/active_record/sti_spec.rb +41 -0
  31. data/spec/active_record/test_in_memory.rb +71 -0
  32. data/spec/car.rb +67 -0
  33. data/spec/cfg.rb +8 -0
  34. data/spec/inheritance_classes.rb +16 -0
  35. data/spec/inheritance_spec.rb +80 -0
  36. data/spec/new_and_method_missing_spec.rb +73 -0
  37. data/spec/plural.rb +8 -0
  38. data/spec/poro_spec.rb +420 -0
  39. data/spec/rails/README +243 -0
  40. data/spec/rails/Rakefile +10 -0
  41. data/spec/rails/app/controllers/application_controller.rb +10 -0
  42. data/spec/rails/app/controllers/form_test_controller.rb +38 -0
  43. data/spec/rails/app/helpers/application_helper.rb +3 -0
  44. data/spec/rails/app/helpers/form_test_helper.rb +2 -0
  45. data/spec/rails/app/models/user.rb +9 -0
  46. data/spec/rails/app/views/form_test/form.html.erb +1 -0
  47. data/spec/rails/app/views/form_test/form_for.html.erb +10 -0
  48. data/spec/rails/app/views/form_test/form_tag.html.erb +9 -0
  49. data/spec/rails/app/views/form_test/index.html.erb +6 -0
  50. data/spec/rails/app/views/layouts/application.html.erb +11 -0
  51. data/spec/rails/config/boot.rb +110 -0
  52. data/spec/rails/config/database.yml +22 -0
  53. data/spec/rails/config/environment.rb +45 -0
  54. data/spec/rails/config/environments/development.rb +17 -0
  55. data/spec/rails/config/environments/production.rb +28 -0
  56. data/spec/rails/config/environments/test.rb +28 -0
  57. data/spec/rails/config/initializers/backtrace_silencers.rb +7 -0
  58. data/spec/rails/config/initializers/inflections.rb +10 -0
  59. data/spec/rails/config/initializers/mime_types.rb +5 -0
  60. data/spec/rails/config/initializers/new_rails_defaults.rb +19 -0
  61. data/spec/rails/config/initializers/session_store.rb +15 -0
  62. data/spec/rails/config/locales/en.yml +5 -0
  63. data/spec/rails/config/routes.rb +43 -0
  64. data/spec/rails/db/development.sqlite3 +0 -0
  65. data/spec/rails/db/migrate/20090804230221_create_sessions.rb +16 -0
  66. data/spec/rails/db/migrate/20090804230546_create_users.rb +21 -0
  67. data/spec/rails/db/schema.rb +35 -0
  68. data/spec/rails/db/test.sqlite3 +0 -0
  69. data/spec/rails/public/404.html +30 -0
  70. data/spec/rails/public/422.html +30 -0
  71. data/spec/rails/public/500.html +30 -0
  72. data/spec/rails/public/favicon.ico +0 -0
  73. data/spec/rails/public/images/rails.png +0 -0
  74. data/spec/rails/public/index.html +275 -0
  75. data/spec/rails/public/javascripts/application.js +2 -0
  76. data/spec/rails/public/javascripts/controls.js +963 -0
  77. data/spec/rails/public/javascripts/dragdrop.js +973 -0
  78. data/spec/rails/public/javascripts/effects.js +1128 -0
  79. data/spec/rails/public/javascripts/prototype.js +4320 -0
  80. data/spec/rails/public/robots.txt +5 -0
  81. data/spec/rails/public/stylesheets/scaffold.css +54 -0
  82. data/spec/rails/script/about +4 -0
  83. data/spec/rails/script/autospec +6 -0
  84. data/spec/rails/script/console +3 -0
  85. data/spec/rails/script/dbconsole +3 -0
  86. data/spec/rails/script/destroy +3 -0
  87. data/spec/rails/script/generate +3 -0
  88. data/spec/rails/script/performance/benchmarker +3 -0
  89. data/spec/rails/script/performance/profiler +3 -0
  90. data/spec/rails/script/plugin +3 -0
  91. data/spec/rails/script/runner +3 -0
  92. data/spec/rails/script/server +3 -0
  93. data/spec/rails/script/spec +10 -0
  94. data/spec/rails/script/spec_server +9 -0
  95. data/spec/rails/spec/controllers/form_test_controller_spec.rb +41 -0
  96. data/spec/rails/spec/integrations/enum_select_spec.rb +75 -0
  97. data/spec/rails/spec/matchers.rb +12 -0
  98. data/spec/rails/spec/rcov.opts +2 -0
  99. data/spec/rails/spec/spec.opts +4 -0
  100. data/spec/rails/spec/spec_helper.rb +40 -0
  101. data/spec/rails/spec/views/form_test/form.html.erb_spec.rb +12 -0
  102. data/spec/rails/spec/views/form_test/form_for.html.erb_spec.rb +12 -0
  103. data/spec/rails/spec/views/form_test/form_tag.html.erb_spec.rb +12 -0
  104. data/spec/rcov.opts +2 -0
  105. data/spec/spec.opts +4 -0
  106. data/spec/tractor.rb +48 -0
  107. metadata +190 -0
@@ -0,0 +1,15 @@
1
+ .DS_Store
2
+ **/*.log
3
+ spec/**/*.log
4
+ *.gem
5
+ pkg
6
+ rdoc
7
+ coverage
8
+ doc
9
+ log
10
+ nbproject
11
+ tmp
12
+ vendor
13
+ lib/tasks/*
14
+ TODO.rdoc
15
+ webrat-*.html
@@ -0,0 +1,49 @@
1
+ == master
2
+
3
+ == 0.1.7 / 2009-07-17
4
+
5
+ * Added abbreviated predicate methods
6
+ * Added and cleaned documentation
7
+
8
+ == 0.1.6 / 2009-07-15
9
+
10
+ * Refactored
11
+
12
+ == 0.1.5 / 2009-07-14
13
+
14
+ * Refactored to remove unnecessary class methods from class
15
+ * Dynamically load class methods into implementing class object
16
+
17
+ == 0.1.4 / 2009-07-14
18
+
19
+ * Removed log files from gem build
20
+
21
+ == 0.1.3 / 2009-07-14
22
+
23
+ * Adds Ingration module supporting Object and ActiveRecord
24
+ * Adds extensibility to Integration module
25
+ * Adds ActiveRecord Integration
26
+ * Improves pluralization -- supports /(sh|ch|x|s)$/, /y$/, /[aeiou]y$/ and +'s' pluralization
27
+
28
+ == 0.1.2 / 2009-07-11
29
+
30
+ * Added 'is_not' for negated custom method definitions
31
+ * Added dynamic creation of 'nil' predicate methods
32
+ * Refactored MethodDefinitionDSL
33
+ * Added EnumeratedAttribute::MethodDefinition
34
+
35
+ == 0.1.1 / 2009-07-09
36
+
37
+ * Added enum_attr_reader and enum_attr_writer
38
+ * Added option :nil=>true to allow attributes set to nil
39
+ * Added #{attr_name}_nil? method for testing nil case
40
+
41
+ == 0.1.0 / 2009-07-08
42
+
43
+ * Added dynamic predicate method generation
44
+ * Added options handling
45
+ * Added incrementor and decrementor
46
+ * Added enumeration accessor
47
+ * Added accessor and enumeration value definition
48
+ * Added simple attribute initialization
49
+ * Added DSL for short-hand method definition
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-2009 Jeff Patmon
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,558 @@
1
+ = enumerated_attribute
2
+
3
+ Easily code enumerations for your models and expose them as
4
+ drop-down lists with the +enum_select+ helper, or use any of +enumerated_attribute+
5
+ features to simplify coding enumerations in any Ruby object.
6
+
7
+ == Resources
8
+
9
+ Development
10
+
11
+ * http://github.com/jeffp/enumerated_attribute
12
+
13
+ Source
14
+
15
+ * git://github.com/jeffp/enumerated_attribute.git
16
+
17
+ Install
18
+
19
+ * sudo gem install enumerated_attribute
20
+
21
+ == Notice
22
+
23
+ * Rails 3 ... dynamic finders working... should be rails3 ready. We are completing form tests.
24
+
25
+ * Rails 2.3.8 breaks find_or_create_by_... and find_or_initialize_by_... methods. Write me if you gotta have it... otherwise happy Rails3.
26
+
27
+ * Write cleaner code ... implement state patterns for your enumerated attributes (enumerated_state[http://github.com/jeffp/enumerated_state])
28
+
29
+ == How to submit an Issue
30
+
31
+ If something needs fixed, please submit issues to this Github project in the Issues tab and
32
+ provide a series of FAILING RSPEC tests that I can drop into the current RSpec
33
+ test framework and run with little to no coersing. Thanks.
34
+
35
+ == Contributors
36
+
37
+ * Lailson Bandeira - Rails 3 updates
38
+
39
+ == Description
40
+
41
+ Typically, in Ruby, enumerated attributes are implemented with strings, symbols or constants. Often the
42
+ developer is burdened with repeatedly defining common methods in support of each
43
+ attribute. +enumerated_attribute+ provides a DRY implementation for enumerations in Rails.
44
+ Repetitive code such as initializers, accessors, predicate and enumeration methods are automatically generated
45
+ along with the following features:
46
+
47
+ * ActiveRecord integration
48
+ * ActionView form helpers
49
+ * Scaffold generator integration
50
+ * Definable enumeration labels
51
+ * Enum helper methods
52
+ * Dynamic predicate methods
53
+ * Initialization
54
+ * State pattern support (enumerated_state[http://github.com/jeffp/enumerated_state])
55
+
56
+ == Setup
57
+
58
+ For a Ruby application, install the gem and require it
59
+
60
+ require 'enumerated_attribute'
61
+
62
+ or for a rails application configure the gem in the config block of the
63
+ config/environment.rb file
64
+
65
+ config.gem "enumerated_attribute"
66
+
67
+ and run the gem install rake task
68
+
69
+ rake gems:install
70
+
71
+
72
+ == Rails Example
73
+
74
+ Here's an example of +enumerated_attribute+ features in a Rails application:
75
+
76
+ In the migration, declare your enumeration attributes with +enum+
77
+
78
+ create_table :users, :force=>true do |t|
79
+ t.string :first_name
80
+ t.enum :gender
81
+ t.enum :degree
82
+ ...
83
+ end
84
+
85
+ Define the enumerations in your models with +enum_attr+
86
+
87
+ class User < ActiveRecord::Base
88
+ enum_attr :gender, %w(male female)
89
+ enum_attr :degree, %w(^none high_school college graduate)
90
+ end
91
+
92
+ Expose the enumeration in your forms with +enum_select+
93
+
94
+ <% form_for :user do |f| %>
95
+ <%= f.label :user %> <%= f.text_field :first_name %><br/>
96
+ <%= f.label :gender %> <%= f.enum_select :gender %><br/>
97
+ <%= f.label :degree %> <%= f.enum_select :degree %><br/>
98
+ <%= submit_tag 'save' %>
99
+ <% end %>
100
+
101
+ or generate a scaffold with one of your favorite scaffold generators. Currently
102
+ supports most scaffold generators including scaffold, wizardly_scaffold, nifty_scaffold, rspec_scaffold, and haml_scaffold.
103
+ See the section 'Generating Scaffolds' below.
104
+
105
+ The select options text can be customized. See 'Customizing Labels' in the Integration section.
106
+
107
+ == Ruby Example
108
+
109
+ Here's an example of +enumerated_attribute+ features in a Ruby application:
110
+
111
+ require 'enumerated_attribute'
112
+
113
+ class Tractor
114
+ enum_attr :gear, %w(reverse ^neutral first second over_drive)
115
+
116
+ end
117
+
118
+ t = Tractor.new
119
+ t.gear # => :neutral
120
+ t.neutral? # => true
121
+ t.gear_next # => :first
122
+ t.not_neutral? # => true
123
+ t.gear_previous # => :neutral
124
+ t.gear = :second # => :second
125
+ t.gear_is_not_in_first? # => true
126
+
127
+ An explanation of the above features and their usage follows.
128
+
129
+ == Usage
130
+
131
+ === Defining the Attribute
132
+
133
+ Defining an enumerated attribute is as simple as this:
134
+
135
+ require 'enumerated_attribute'
136
+
137
+ class Tractor
138
+ enumerated_attribute :gear, %w(reverse neutral first second over_drive)
139
+
140
+ def initialize
141
+ @gear = :neutral
142
+ end
143
+ end
144
+
145
+ Notice the plugin +enumerated_attribute+ is required at the top of the code.
146
+ The +require+ line must be added at least once at some point in the code.
147
+ It is not included in subsequent examples.
148
+
149
+ The above code uses +enumerated_attribute+ to define an attribute named 'gear' with five enumeration values.
150
+ In general, +enumerated_attribute+ takes three parameters: the name of the attribute, an array of
151
+ enumeration values (either symbols or strings), and an optional hash of options (not shown above). The complete
152
+ form of +enumerated_attribute+ looks like this:
153
+
154
+ enumerated_attribute :name, array_of_enumerations, hash_of_options
155
+
156
+ Defining the attribute :gear has done a number things.
157
+ It has generated an instance variable '@gear', read/write accessors for the
158
+ attribute and support methods
159
+ for the enumeration, such as incrementor and decrementor methods. These methods are
160
+ demonstrated below using the Tractor class above:
161
+
162
+ Tractor.instance_methods(false)
163
+ # =>["gear", "gear=", "gears", "gear_next", "gear_previous", ...
164
+
165
+ t = Tractor.new
166
+ t.gear # => :neutral
167
+ t.gear = :reverse # => :reverse
168
+ t.gear # => :reverse
169
+ t.gear = :third
170
+ # => ArgumentError: 'third' is not an enumerated value for gear attribute
171
+
172
+ t.gears # => [:reverse, :neutral, :first, :second, :over_drive]
173
+ t.gear_next # => :neutral
174
+ t.gear_previous # => :reverse
175
+ t.gear_previous # => :over_drive
176
+
177
+ The plugin has defined +gear+ and gear= accessors for the attribute. They can be used
178
+ to set the attribute to one of the defined enumeration values. Attempting to set the
179
+ attribute to something besides a defined enumeration value raises an ArgumentError.
180
+
181
+ +gear_next+ and +gear_previous+ are incrementors and decrementors of the attribute.
182
+ The increment order is based on the order of the enumeration values in the attribute definition.
183
+ Both the incrementor and decrementor will wrap when reaching the boundary elements
184
+ of the enumeration array. For example:
185
+
186
+ t.gear = :second
187
+ t.gear_next # => :over_drive
188
+ t.gear_next # => :reverse
189
+
190
+
191
+ ==== Dynamically-Generating Predicates Methods
192
+
193
+ Predicate methods are methods that query the state of the attribute,
194
+ for instance, gear_is_neutral? is a predicate method that returns 'true' if
195
+ the gear attribute is in the :neutral state.
196
+ By default, predicate methods are not predefined, instead, they are dynamically generated.
197
+ The plugin will evaluate and respond to methods adhering to a format that it
198
+ can associate with an attribute name and one of the attribute's enumeration values.
199
+ +enumerated_attribute+ recognizes predicate methods of the following format:
200
+
201
+ {attribute name}_{anything}_{enumeration value}?
202
+
203
+ The predicate method must satisfy three requirements: it must begin with the name
204
+ of the attribute,
205
+ it must end with a question mark, and the question mark must be preceded with
206
+ a valid enumeration value (all connected by underscores without colons).
207
+ So we can write the following two predicate methods without any prior definition and
208
+ the plugin will recognize, define and respond to them as demonstrated here:
209
+
210
+ t.gear= :neutral
211
+ t.gear_is_in_neutral? # => true
212
+ t.gear_is_in_reverse? # => false
213
+
214
+ The '_is_in_' part of the methods above is merely semantic but enhances
215
+ readability. The contents of the {anything} portion is completely
216
+ at the discretion of the developer. However, there is one twist.
217
+ The evaluation of a predicate method can be negated
218
+ by including 'not' in the the middle {anything} section, such as here:
219
+
220
+ t.gear_is_not_in_neutral? # => false
221
+ t.gear_is_not_in_reverse? # => true
222
+
223
+ Basically, the shortest acceptable form of a predicate method is:
224
+
225
+ t.gear_neutral? # => true
226
+ t.gear_not_neutral? # => false
227
+
228
+
229
+ ==== Abbreviating Predicate Methods
230
+
231
+ In the case that an enumeration value is associated with only one
232
+ attribute, the attribute name can be left out of the predicate method name.
233
+ The plugin will infer the attribute from the enum value in the method name.
234
+ The abbreviate format can be written like this:
235
+
236
+ {anything}{_}{enumeration value}?
237
+
238
+ And results in the following possibilities:
239
+
240
+ t.gear = :neutral
241
+ t.neutral? # => true
242
+ t.is_neutral? # => true
243
+ t.not_neutral? # => false
244
+ t.is_not_neutral? # => false
245
+
246
+ Calling the abbreviated form of the method containing an enumeration value
247
+ belonging to two or more attributes throws an AmbiguousMethod error.
248
+
249
+
250
+ ==== Initializing Attributes
251
+
252
+ The plugin provides a few ways to eliminate setting the initial value of the attribute in
253
+ the +initialize+ method. Two ways are demonstrated here:
254
+
255
+ class Tractor
256
+ enum_attr :gear, %w(reverse ^neutral first second third)
257
+ enum_attr :front_light, %w(off low high), :init=>:off
258
+ end
259
+
260
+ t = Tractor.new
261
+ t.gear # => :neutral
262
+ t.front_light # => :off
263
+
264
+ *Note* +enumerated_attribute+ can be abbreviated to +enum_attr+. The abbreviated
265
+ form will be used in subsequent examples.
266
+
267
+ The first and simplest way involves designating the initial value by
268
+ prepending a carot '^' to one of the enumeration values in the definition.
269
+ The plugin recognizes that the gear attribute is to be initialized to :neutral.
270
+ Alternatively, the :init option can be used to indicate the initial value of the attribute.
271
+
272
+
273
+ ==== Setting Attributes to nil
274
+
275
+ By default, the attribute setter allows nils unless the :nil option is set to false.
276
+ When :nil is set to false, the attribute may initialize to nil, but may not be set
277
+ to nil thereafter.
278
+
279
+ class Tractor
280
+ enum_attr :plow, %w(up down), :nil=>false
281
+ end
282
+
283
+ t = Tractor.new
284
+ t.plow # => nil
285
+ t.plow_nil? # => true
286
+ t.plow = :up # => :up
287
+ t.plow_is_nil? # => false
288
+ t.plow_is_not_nil? # => true
289
+ t.plow = nil # => raises error
290
+
291
+ Regardless of the :nil option setting, the plugin can dynamically recognize and define
292
+ predicate methods for testing 'nil' values. The setter methods also treat empty
293
+ strings (or '') as nil values.
294
+
295
+
296
+ ==== Changing Method Names
297
+
298
+ The plugin provides options for changing the method names of the enumeration accessor, incrementor
299
+ and decrementor (ie, +gears+, +gear_next+, +gear_previous+):
300
+
301
+ class Tractor
302
+ enum_attr :lights, %w(^off low high), :plural=>:lights_values,
303
+ :inc=>'lights_inc', :dec=>'lights_dec'
304
+ end
305
+
306
+ t = Tractor.new
307
+ t.lights_values # => [:off, :low, :high]
308
+ t.lights_inc # => :low
309
+ t.lights_dec # => :off
310
+
311
+ By default, the plugin uses the plural of the attribute for the accessor method name of the enumeration
312
+ values. The pluralization uses a simple algorithm which does not support irregular forms. In
313
+ the case of 'lights' as an
314
+ attribute, the default pluralization does not work, so the accessor can be changed using
315
+ the :plural option. Likewise, the decrementor
316
+ and incrementor have options :decrementor and :incrementor, or :inc and :dec, for changing
317
+ their method names.
318
+
319
+
320
+ === Defining Other Methods
321
+
322
+ In the case that other methods are required to support the attribute,
323
+ the plugin provides a short-hand for defining these methods in the
324
+ +enumerated_attribute+ block.
325
+
326
+ class Tractor
327
+ enum_attr :gear, %w(reverse ^neutral first second over_drive) do
328
+ parked? :neutral
329
+ driving? [:first, :second, :over_drive]
330
+ end
331
+ end
332
+
333
+ t = Tractor.new
334
+ t.parked? # => true
335
+ t.driving? # => false
336
+
337
+ Two predicate methods are defined for the 'gear' attribute in the above example using
338
+ the plugin's short-hand.
339
+ The first method, parked?, defines a method which evaluates
340
+ the code {@gear == :neutral}. The second method, driving?, evaluates
341
+ to true if the attribute is set to one of the enumeration values defined in the array
342
+ [:first, :second, :over_drive].
343
+
344
+ The same short-hand can be used to define methods where the attribute 'is not' equal to the
345
+ indicated value or 'is not' included in the array of values.
346
+
347
+ class Tractor
348
+ enum_attr :gear, %w(reverse ^neutral first second over_drive) do
349
+ not_parked? is_not :neutral
350
+ not_driving? is_not [:first, :second, :over_drive]
351
+ end
352
+ end
353
+
354
+
355
+ ==== Defining Other Methods With Blocks
356
+
357
+ For predicate methods requiring fancier logic,
358
+ a block can be used to define the method body.
359
+
360
+ class Tractor
361
+ enum_attr :gear, %w(reverse ^neutral first second over_drive) do
362
+ parked? :neutral
363
+ driving? [:first, :second, :over_drive]
364
+ end
365
+ enum_attr :plow, %w(^up down), :plural=>:plow_values do
366
+ plowing? { self.gear_is_in_first? && @plow == :down }
367
+ end
368
+ end
369
+
370
+ Here, a method plowing? is true if the gear attribute equates to :first
371
+ and the plow attribute is set to :down. There is
372
+ no short-hand for the block. The code must be complete and evaluate in
373
+ the context of the instance.
374
+
375
+ Method definitions are not limited to predicate methods. Other methods
376
+ can be defined to manipulate the attributes. Here, two methods are defined acting
377
+ as bounded incrementor and decrementor of the gear attribute.
378
+
379
+ class Tractor
380
+ enum_attr :gear, %w(reverse ^neutral first second over_drive) do
381
+ parked? :neutral
382
+ driving? [:first, :second, :over_drive]
383
+ upshift { self.gear_is_in_over_drive? ? self.gear : self.gear_next }
384
+ downshift { self.driving? ? self.gear_previous : self.gear }
385
+ end
386
+ end
387
+
388
+ t = Tractor.new
389
+ t.gear # => :neutral
390
+ 10.times { t.upshift }
391
+ t.gear # => :over_drive
392
+ 10.times { t.downshift }
393
+ t.gear # => :neutral
394
+
395
+ Methods +upshift+ and +downshift+ use the automatically generated
396
+ incrementor and decrementor as
397
+ well as a couple predicate methods. +upshift+ increments the gear attribute until
398
+ it reaches over_drive and does not allow a wrap around. +downshift+ decrements
399
+ until the attribute reaches neutral.
400
+
401
+ === Integration
402
+
403
+ ==== ActiveRecord integration
404
+
405
+ The plugin can be used with ActiveRecord. Enumerated attributes may be declared on
406
+ column attributes or as independent enumerations. Declaring an enumerated attribute
407
+ on a column attribute will enforce the enumeration using symbols. The
408
+ enumerated column attribute must be declared as a STRING in the database schema.
409
+ The enumerated attribute will be stored as a string but retrieved in code as a symbol. The
410
+ enumeration functionality is consistent across integrations.
411
+
412
+ require 'enumerated_attribute'
413
+ require 'active_record'
414
+
415
+ class Order < ActiveRecord::Base
416
+ enum_attr :status, %w(^hold, processing, delayed, shipped)
417
+ enum_attr :billing_status,
418
+ %w(^unauthorized, authorized, auth_failed, captured, capture_failed, closed)
419
+ end
420
+
421
+ o = Order.new
422
+ o.status # => :hold
423
+ o.billing_status # => :unauthorized
424
+ o.save!
425
+
426
+ o = Order.new(:invoice=>'43556334-W84', :status=>:processing, :billing=>:authorized)
427
+ o.save!
428
+ o.status # => :processing
429
+ o.invoice # => "43556334-W84"
430
+
431
+
432
+ ==== Labels
433
+
434
+ Each enumeration value has a corresponding text label. The defaults are made
435
+ up from the enumeration symbols. For the Tractor class example:
436
+
437
+ t=Tractor.new
438
+ t.enums(:gear) # => [:reverse, :neutral, :first, :second, :over_drive]
439
+ t.enums(:gear).labels # => ['Reverse', 'Neutral', 'First', 'Second', 'Over drive']
440
+
441
+ The +enums(:attribute)+ method provides information about the attribute's enumerations.
442
+ It is the same as the plural form of the attribute name. There are several kinds
443
+ of information available from the +enums+ method.
444
+
445
+ t=Tractor.new
446
+ e = t.enums(:plow) # => [:up, :down]
447
+ e.labels # => ['Up', 'Down']
448
+ e.hash # => {:up=>'Up', :down=>'Down'}
449
+ e.select_options # => [['Up', 'up'], ['Down', 'down']]
450
+ e.label(:up) # => 'Up'
451
+
452
+ ==== Customizing Labels
453
+
454
+ Labels can be customized as shown here:
455
+
456
+ class User < ActiveRecord::Base
457
+ enum_attr :contact_options, %w(none phone email mail) do
458
+ label :none=>'Please do not contact me'
459
+ label :phone=>'I would like a representative to call me'
460
+ label :email=>'I would like information via email'
461
+ label :mail=>'I would like information mailed to me'
462
+ end
463
+ end
464
+
465
+ Likewise, the labels can be provided on the same line
466
+
467
+ class Tractor
468
+ enum_attr :gear, %w(reverse ^neutral first second over_drive) do
469
+ labels :first=>'1st Gear', :second=>'2nd Gear', :over_drive=>'Over Drive'
470
+ end
471
+ end
472
+
473
+ ==== View Helpers
474
+
475
+ There are two +enum_select+ helpers, one for use with +form_for+ and one for use without
476
+ it. An example for form_for was given in the examples at the beginning. Here's an
477
+ example with the +form_tag+ and a @user object.
478
+
479
+ <% form_tag :action=>:register do %>
480
+ <%= label_tag 'First name' %>: <%= text_field :user, :first_name %><br/>
481
+ <%= label_tag 'Gender' %>: <%= enum_select :user, :gender %><br/>
482
+ <%= label_tag 'Degree' %>: <%= enum_select :user, :degree %><br/>
483
+ ...
484
+ <%= submit_tag 'Register' %>
485
+ <% end %>
486
+
487
+ ==== Generating Scaffolds
488
+
489
+ You can generate views with enumerations using your favorite scaffold generator. Currently
490
+ supports most scaffold generators including scaffold, wizardly_scaffold, nifty_scaffold, rspec_scaffold and haml_scaffold.
491
+ For most scaffolds there are two steps. First, generate the scaffold views and
492
+ migrations using the 'enum' type for enumerations
493
+
494
+ ./script/generate scaffold contractor name:string gender:enum age:integer status:enum
495
+
496
+ Second, do not forget to add the +enum_attr+ macro to the generated model and migrate the database
497
+
498
+ class Contractor < ActiveRecord::Base
499
+ enum_attr :gender, %w(male female)
500
+ enum_attr :status, %w(available unavailable)
501
+ end
502
+
503
+
504
+ === Implementation Notes
505
+
506
+ ==== New and Method_missing methods
507
+
508
+ The plugin chains both the 'new' and the 'method_missing' methods. Any 'new' and 'method_missing'
509
+ implementations in the same class declaring an enumerated_attribute should come before the
510
+ declaration; otherwise, the 'new' and 'method_missing' implementations must chain in order to avoid
511
+ overwriting the plugin's methods. The best approach is shown here:
512
+
513
+ class Soup
514
+ def self.new(*args)
515
+ ...
516
+ end
517
+
518
+ private
519
+ def method_missing(methId, *args, &blk)
520
+ ...
521
+ end
522
+
523
+ enum_attr temp:, %w(cold warm hot boiling)
524
+ end
525
+
526
+ ==== ActiveRecord
527
+
528
+ ActiveRecord's write_attribute and read_attribute methods do not support symbols for enumerated attributes.
529
+
530
+
531
+ == Testing
532
+
533
+ The plugin uses RSpec and Webrat for testing. Make sure you have the RSpec gem installed:
534
+
535
+ gem install rspec webrat
536
+
537
+ To test the plugin for regular ruby objects, run:
538
+
539
+ rake spec:object
540
+
541
+ Testing ActiveRecord integration requires the install of Sqlite3 and the
542
+ sqlite3-ruby gem. To test ActiveRecord, run:
543
+
544
+ rake spec:active_record
545
+
546
+ And for testing +enum_select+ in form views:
547
+
548
+ rake spec:forms
549
+
550
+ To test all specs:
551
+
552
+ rake spec:all
553
+
554
+
555
+ == Dependencies
556
+
557
+ * ActiveRecord (but not required)
558
+ * Sqlite3 and sqlite3-ruby gem (for testing)