enumerated_attribute 0.2.7

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