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.
- data/.gitignore +16 -0
- data/CHANGELOG.rdoc +49 -0
- data/LICENSE +20 -0
- data/README.rdoc +555 -0
- data/Rakefile +104 -0
- data/init.rb +1 -0
- data/lib/enumerated_attribute.rb +24 -0
- data/lib/enumerated_attribute/attribute.rb +78 -0
- data/lib/enumerated_attribute/attribute/arguments.rb +48 -0
- data/lib/enumerated_attribute/attribute/attribute_descriptor.rb +51 -0
- data/lib/enumerated_attribute/attribute/class_methods.rb +42 -0
- data/lib/enumerated_attribute/attribute/instance_methods.rb +96 -0
- data/lib/enumerated_attribute/integrations.rb +33 -0
- data/lib/enumerated_attribute/integrations/active_record.rb +109 -0
- data/lib/enumerated_attribute/integrations/datamapper.rb +6 -0
- data/lib/enumerated_attribute/integrations/default.rb +25 -0
- data/lib/enumerated_attribute/integrations/object.rb +47 -0
- data/lib/enumerated_attribute/method_definition_dsl.rb +142 -0
- data/lib/enumerated_attribute/rails_helpers.rb +97 -0
- data/lib/jeffp-enumerated_attribute.rb +1 -0
- data/spec/active_record/active_record_spec.rb +363 -0
- data/spec/active_record/association_test_classes.rb +40 -0
- data/spec/active_record/associations_spec.rb +130 -0
- data/spec/active_record/cfg.rb +6 -0
- data/spec/active_record/inheritance_classes.rb +19 -0
- data/spec/active_record/inheritance_spec.rb +60 -0
- data/spec/active_record/race_car.rb +15 -0
- data/spec/active_record/single_table_inheritance_spec.rb +0 -0
- data/spec/active_record/sti_classes.rb +10 -0
- data/spec/active_record/sti_spec.rb +41 -0
- data/spec/active_record/test_in_memory.rb +71 -0
- data/spec/car.rb +67 -0
- data/spec/cfg.rb +8 -0
- data/spec/inheritance_classes.rb +16 -0
- data/spec/inheritance_spec.rb +80 -0
- data/spec/new_and_method_missing_spec.rb +73 -0
- data/spec/plural.rb +8 -0
- data/spec/poro_spec.rb +397 -0
- data/spec/rails/README +243 -0
- data/spec/rails/Rakefile +10 -0
- data/spec/rails/app/controllers/application_controller.rb +10 -0
- data/spec/rails/app/controllers/form_test_controller.rb +38 -0
- data/spec/rails/app/helpers/application_helper.rb +3 -0
- data/spec/rails/app/helpers/form_test_helper.rb +2 -0
- data/spec/rails/app/models/user.rb +9 -0
- data/spec/rails/app/views/form_test/form.html.erb +1 -0
- data/spec/rails/app/views/form_test/form_for.html.erb +10 -0
- data/spec/rails/app/views/form_test/form_tag.html.erb +9 -0
- data/spec/rails/app/views/form_test/index.html.erb +6 -0
- data/spec/rails/app/views/layouts/application.html.erb +11 -0
- data/spec/rails/config/boot.rb +110 -0
- data/spec/rails/config/database.yml +22 -0
- data/spec/rails/config/environment.rb +45 -0
- data/spec/rails/config/environments/development.rb +17 -0
- data/spec/rails/config/environments/production.rb +28 -0
- data/spec/rails/config/environments/test.rb +28 -0
- data/spec/rails/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails/config/initializers/inflections.rb +10 -0
- data/spec/rails/config/initializers/mime_types.rb +5 -0
- data/spec/rails/config/initializers/new_rails_defaults.rb +19 -0
- data/spec/rails/config/initializers/session_store.rb +15 -0
- data/spec/rails/config/locales/en.yml +5 -0
- data/spec/rails/config/routes.rb +43 -0
- data/spec/rails/db/development.sqlite3 +0 -0
- data/spec/rails/db/migrate/20090804230221_create_sessions.rb +16 -0
- data/spec/rails/db/migrate/20090804230546_create_users.rb +21 -0
- data/spec/rails/db/schema.rb +35 -0
- data/spec/rails/db/test.sqlite3 +0 -0
- data/spec/rails/public/404.html +30 -0
- data/spec/rails/public/422.html +30 -0
- data/spec/rails/public/500.html +30 -0
- data/spec/rails/public/favicon.ico +0 -0
- data/spec/rails/public/images/rails.png +0 -0
- data/spec/rails/public/index.html +275 -0
- data/spec/rails/public/javascripts/application.js +2 -0
- data/spec/rails/public/javascripts/controls.js +963 -0
- data/spec/rails/public/javascripts/dragdrop.js +973 -0
- data/spec/rails/public/javascripts/effects.js +1128 -0
- data/spec/rails/public/javascripts/prototype.js +4320 -0
- data/spec/rails/public/robots.txt +5 -0
- data/spec/rails/public/stylesheets/scaffold.css +54 -0
- data/spec/rails/script/about +4 -0
- data/spec/rails/script/autospec +6 -0
- data/spec/rails/script/console +3 -0
- data/spec/rails/script/dbconsole +3 -0
- data/spec/rails/script/destroy +3 -0
- data/spec/rails/script/generate +3 -0
- data/spec/rails/script/performance/benchmarker +3 -0
- data/spec/rails/script/performance/profiler +3 -0
- data/spec/rails/script/plugin +3 -0
- data/spec/rails/script/runner +3 -0
- data/spec/rails/script/server +3 -0
- data/spec/rails/script/spec +10 -0
- data/spec/rails/script/spec_server +9 -0
- data/spec/rails/spec/controllers/form_test_controller_spec.rb +41 -0
- data/spec/rails/spec/integrations/enum_select_spec.rb +75 -0
- data/spec/rails/spec/matchers.rb +12 -0
- data/spec/rails/spec/rcov.opts +2 -0
- data/spec/rails/spec/spec.opts +4 -0
- data/spec/rails/spec/spec_helper.rb +40 -0
- data/spec/rails/spec/views/form_test/form.html.erb_spec.rb +12 -0
- data/spec/rails/spec/views/form_test/form_for.html.erb_spec.rb +12 -0
- data/spec/rails/spec/views/form_test/form_tag.html.erb_spec.rb +12 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/tractor.rb +48 -0
- metadata +182 -0
data/.gitignore
ADDED
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)
|