jeffp-enumerated_attribute 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/CHANGELOG.rdoc +11 -0
- data/LICENSE +20 -0
- data/README.rdoc +266 -0
- data/Rakefile +87 -0
- data/init.rb +1 -0
- data/lib/enumerated_attribute.rb +274 -0
- data/spec/car.rb +37 -0
- data/spec/car_spec.rb +21 -0
- data/spec/tractor.rb +38 -0
- data/spec/tractor_spec.rb +239 -0
- metadata +64 -0
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
== master
|
2
|
+
|
3
|
+
== 0.1.0 / 2009-07-09
|
4
|
+
|
5
|
+
* Added dynamic predicate method generation
|
6
|
+
* Added options handling
|
7
|
+
* Added incrementor and decrementor
|
8
|
+
* Added enumeration accessor
|
9
|
+
* Added accessor and enumeration value definition
|
10
|
+
* Added simple attribute initialization
|
11
|
+
* 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,266 @@
|
|
1
|
+
= enumerated_attribute
|
2
|
+
|
3
|
+
+enumerated_attribute+ simplifies the definition of enumeration-based attributes, their accessors, initializers and common predicate and support methods.
|
4
|
+
|
5
|
+
== Resources
|
6
|
+
|
7
|
+
Development
|
8
|
+
|
9
|
+
* http://github.com/jeffp/enumerated_attribute
|
10
|
+
|
11
|
+
Source
|
12
|
+
|
13
|
+
* git://github.com/jeffp/enumerated_attribute.git
|
14
|
+
|
15
|
+
== Description
|
16
|
+
|
17
|
+
Enumerations are a common and useful pattern in programming. Typically, in Ruby,
|
18
|
+
enumerated attributes are implemented with strings, symbols or constants. Often the
|
19
|
+
developer is burdened with repeatedly defining common methods in support of each
|
20
|
+
attribute. Such repetition coding unnecessarily increases
|
21
|
+
costs and wastes time.
|
22
|
+
|
23
|
+
+enumerated_attribute+ simplifies the definition of enumerated attributes by emphasizing
|
24
|
+
convention and DRYing the implementation. Repetitive code such as initializers, accessors,
|
25
|
+
predicate and support methods are automatically generated, resulting in better
|
26
|
+
encapsulation and quicker, more readable code.
|
27
|
+
|
28
|
+
Features include:
|
29
|
+
* Short and simple definition
|
30
|
+
* Dynamically-generated support methods
|
31
|
+
* Run-time generated attribute predicates
|
32
|
+
* Attribute initialization
|
33
|
+
* Method definition short-hand (DSL)
|
34
|
+
|
35
|
+
Examples of the usage patterns for some of the above features are shown below.
|
36
|
+
|
37
|
+
== Usage
|
38
|
+
|
39
|
+
=== Defining the Attribute
|
40
|
+
|
41
|
+
Defining an enumerated attribute is as simple as this:
|
42
|
+
|
43
|
+
require 'enumerated_attribute'
|
44
|
+
|
45
|
+
class Tractor
|
46
|
+
enumerated_attribute :gear, %w(reverse neutral first second over_drive)
|
47
|
+
|
48
|
+
def initialize
|
49
|
+
@gear = :neutral
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Notice the plugin +enumerated_attribute+ is required at the top of the code.
|
54
|
+
The +require+ line must be added at least once at some point in the code.
|
55
|
+
It is not included in subsequent examples.
|
56
|
+
|
57
|
+
The above code uses +enumerated_attribute+ to define an attribute named 'gear' with five enumeration values.
|
58
|
+
In general, +enumerated_attribute+ takes three parameters: the name of the attribute, an array of
|
59
|
+
enumeration values (either symbols or strings), and an optional hash of options (not shown above). The complete
|
60
|
+
form of +enumerated_attribute+ looks like this:
|
61
|
+
|
62
|
+
enumerated_attribute :name, array_of_enumerations, hash_of_options
|
63
|
+
|
64
|
+
Defining the attribute :gear has done a number things.
|
65
|
+
It has generated an instance variable '@gear', read/write accessors for the
|
66
|
+
attribute and three support methods
|
67
|
+
for the enumeration, including an incrementor and decrementor. These methods are
|
68
|
+
demonstrated below using the Tractor class defined above:
|
69
|
+
|
70
|
+
Tractor.instance_methods(false)
|
71
|
+
# =>["gear", "gear=", "gears", "gear_next", "gear_previous", ...
|
72
|
+
|
73
|
+
t = Tractor.new
|
74
|
+
t.gear # => :neutral
|
75
|
+
t.gear= :reverse # => :reverse
|
76
|
+
t.gear # => :reverse
|
77
|
+
t.gear= :third
|
78
|
+
# => ArgumentError: 'third' is not an enumerated value for gear attribute
|
79
|
+
|
80
|
+
t.gears # => [:reverse, :neutral, :first, :second, :over_drive]
|
81
|
+
t.gear_next # => :neutral
|
82
|
+
t.gear_previous # => :reverse
|
83
|
+
t.gear_previous # => :over_drive
|
84
|
+
|
85
|
+
The plugin has defined +gear+ and +gear=+ accessors for the attribute. They can be used
|
86
|
+
to set the attribute to one of the defined enumeration values. Attempting to set the
|
87
|
+
attribute to something besides a defined enumeration value raises an ArgumentError.
|
88
|
+
|
89
|
+
+gear_next+ and +gear_previous+ are incrementors and decrementors of the attribute.
|
90
|
+
The increment order is based on the order of the enumeration values in the attribute definition.
|
91
|
+
Both the incrementor and decrementor will wrap when reaching the boundary elements
|
92
|
+
of the enumeration array. For example:
|
93
|
+
|
94
|
+
t.gear = :second
|
95
|
+
t.gear_next # => :over_drive
|
96
|
+
t.gear_next # => :reverse
|
97
|
+
|
98
|
+
|
99
|
+
==== Using Attribute Predicates
|
100
|
+
|
101
|
+
Attribute predicates are methods used to query the state of the attribute,
|
102
|
+
for instance, +gear_is_neutral?+ is a predicate method for the gear attribute.
|
103
|
+
The plugin will evaluate and respond to predicate methods
|
104
|
+
for any attribute defined with it. Predicate methods are not pre-generated. By
|
105
|
+
adhering to a specific format, the plugin can recognize attribute predicates
|
106
|
+
and generate the methods dynamically. +enumerated_attribute+ recognizes
|
107
|
+
methods of the following format:
|
108
|
+
|
109
|
+
{attribute name}_{anything}_{enumeration value}?
|
110
|
+
|
111
|
+
The predicate method must satisfy three requirements: it must begin with the name
|
112
|
+
of the attribute,
|
113
|
+
it must end with a question mark, and the question mark must be preceded with
|
114
|
+
a valid enumeration value (all connected by underscores without colons).
|
115
|
+
So we can write the following two predicate methods without any prior definition and
|
116
|
+
the plugin will recognize, define and respond to them as demonstrated here:
|
117
|
+
|
118
|
+
t.gear= :neutral
|
119
|
+
t.gear_is_in_neutral? # => true
|
120
|
+
t.gear_is_in_reverse? # => false
|
121
|
+
|
122
|
+
The '_is_in_' part of the methods above is merely semantic but enhances
|
123
|
+
readability. The contents of the {anything} portion is completely
|
124
|
+
at the discretion of the developer. However, there is one twist.
|
125
|
+
The evaluation of a predicate method can be negated
|
126
|
+
by including 'not' in the the middle {anything} section, such as here:
|
127
|
+
|
128
|
+
t.gear_is_not_in_neutral? # => false
|
129
|
+
t.gear_is_not_in_reverse? # => true
|
130
|
+
|
131
|
+
Basically, the shortest acceptable form of a predicate method is:
|
132
|
+
|
133
|
+
t.gear_neutral? # => true
|
134
|
+
t.gear_not_neutral? # => false
|
135
|
+
|
136
|
+
|
137
|
+
==== Initializing Attributes
|
138
|
+
|
139
|
+
The plugin provides a few ways to eliminate setting the initial value of the attribute in
|
140
|
+
the +initialize+ method. Two ways are demonstrated here:
|
141
|
+
|
142
|
+
class Tractor
|
143
|
+
enum_attr :gear, %w(reverse ^neutral first second third)
|
144
|
+
enum_attr :front_light, %w(off low high), :init=>:off
|
145
|
+
end
|
146
|
+
|
147
|
+
t=Tractor.new
|
148
|
+
t.gear # => :neutral
|
149
|
+
t.front_light # => :off
|
150
|
+
|
151
|
+
*Note* +enumerated_attribute+ can be abbreviated to +enum_attr+. The abbreviated
|
152
|
+
form will be used in subsequent examples.
|
153
|
+
|
154
|
+
The first and simplest way involves designating the initial value by
|
155
|
+
prepending a carot '^' to one of the enumeration values in the definition.
|
156
|
+
The plugin recognizes that the gear attribute is to be initialized to :neutral.
|
157
|
+
Alternatively, the :init option can be used to indicate the initial value of the attribute.
|
158
|
+
|
159
|
+
|
160
|
+
==== Changing Method Names
|
161
|
+
|
162
|
+
The plugin provides options for changing the method names of the enumeration accessor, incrementor
|
163
|
+
and decrementor (ie, +gears+, +gear_next+, +gear_previous+):
|
164
|
+
|
165
|
+
class Tractor
|
166
|
+
enum_attr :lights, %w(^off low high), :plural=>:lights_values,
|
167
|
+
:inc=>'lights_inc', :dec=>'lights_dec'
|
168
|
+
end
|
169
|
+
|
170
|
+
t=Tractor.new
|
171
|
+
t.lights_values # => [:off, :low, :high]
|
172
|
+
t.lights_inc # => :low
|
173
|
+
t.lights_dec # => :off
|
174
|
+
|
175
|
+
By default, the plugin uses the plural of the attribute for the accessor method name of the enumeration
|
176
|
+
values. The pluralization is done simply by adding an 's'. In the case of 'lights' as an
|
177
|
+
attribute, the default pluralization does not work, so the accessor can be changed using
|
178
|
+
the :plural option. Likewise, the decrementor
|
179
|
+
and incrementor have options :decrementor and :incrementor, or :inc and :dec, for changing
|
180
|
+
their method names.
|
181
|
+
|
182
|
+
|
183
|
+
=== Defining Other Methods
|
184
|
+
|
185
|
+
In the case that other methods are required to support the attribute,
|
186
|
+
the plugin provides a short-hand for defining these methods in the
|
187
|
+
+enumerated_attribute+ block.
|
188
|
+
|
189
|
+
class Tractor
|
190
|
+
enum_attr :gear, %(reverse ^neutral first second over_drive) do
|
191
|
+
parked? :neutral
|
192
|
+
driving? [:first, :second, :third]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
t=Tractor.new
|
197
|
+
t.parked? # => true
|
198
|
+
t.driving? # => false
|
199
|
+
|
200
|
+
Two predicate methods are defined for the 'gear' attribute in the above example using
|
201
|
+
the plugin's short-hand.
|
202
|
+
The first method, parked?, defines a method which evaluates
|
203
|
+
the code {@gear == :neutral}. The second method, driving?, evaluates
|
204
|
+
to true if the attribute is set to one of the enumeration values defined in the array
|
205
|
+
[:first, :second, :third].
|
206
|
+
|
207
|
+
For predicate methods requiring fancier logic,
|
208
|
+
a block can be used to define the method body.
|
209
|
+
|
210
|
+
class Tractor
|
211
|
+
enum_attr :gear, %(reverse ^neutral first second over_drive) do
|
212
|
+
parked? :neutral
|
213
|
+
driving? [:first, :second, :third]
|
214
|
+
end
|
215
|
+
enum_attr :plow, %(^up down), :plural=>:plow_values do
|
216
|
+
plowing? { self.gear_is_in_first? && @plow == :down }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
Here, a method plowing? is true if the gear attribute equates to :first
|
221
|
+
and the plow attribute is set to :down. There is
|
222
|
+
no short-hand for the block. The code must be complete and evaluate in
|
223
|
+
the context of the instance.
|
224
|
+
|
225
|
+
Method definitions are not limited to predicate methods. Other methods
|
226
|
+
can be defined to manipulate the attributes. Here, two methods are defined acting
|
227
|
+
as bounded incrementor and decrementor of the gear attribute.
|
228
|
+
|
229
|
+
class Tractor
|
230
|
+
enum_attr :gear, %(reverse ^neutral first second over_drive) do
|
231
|
+
parked? :neutral
|
232
|
+
driving? [:first, :second, :third]
|
233
|
+
upshift { self.gear_is_in_over_drive? ? self.gear : self.gear_next }
|
234
|
+
downshift { self.driving? ? self.gear_previous : self.gear }
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
t = Tractor.new
|
239
|
+
t.gear # => :neutral
|
240
|
+
10.times { t.upshift }
|
241
|
+
t.gear # => :over_drive
|
242
|
+
10.times { t.downshift }
|
243
|
+
t.gear # => :neutral
|
244
|
+
|
245
|
+
Methods +upshift+ and +downshift+ use the automatically generated
|
246
|
+
incrementor and decrementor as
|
247
|
+
well as a couple predicate methods. +upshift+ increments the gear attribute until
|
248
|
+
it reaches over_drive and does not allow a wrap around. +downshift+ decrements
|
249
|
+
until the attribute reaches neutral.
|
250
|
+
|
251
|
+
|
252
|
+
== Testing
|
253
|
+
|
254
|
+
The plugin uses RSpec for testing. Make sure you have the RSpec gem installed:
|
255
|
+
|
256
|
+
gem install rspec
|
257
|
+
|
258
|
+
To test the plugin, run:
|
259
|
+
|
260
|
+
rake spec:test
|
261
|
+
|
262
|
+
|
263
|
+
== Dependencies
|
264
|
+
|
265
|
+
By default, there are no dependencies.
|
266
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
#require 'rake/testtask'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rake/contrib/sshpublisher'
|
5
|
+
|
6
|
+
spec = Gem::Specification.new do |s|
|
7
|
+
s.name = 'enumerated_attribute'
|
8
|
+
s.version = '0.1.0'
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.description = 'A enumerated attribute accessor'
|
11
|
+
s.summary = 'Defines enumerated attributes, initial state and dynamic state methods.'
|
12
|
+
|
13
|
+
s.files = FileList['{examples,lib,tasks,spec}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc .gitignore) - FileList['test/*.log']
|
14
|
+
s.require_path = 'lib'
|
15
|
+
s.has_rdoc = true
|
16
|
+
s.test_files = Dir['spec/**/*_spec.rb']
|
17
|
+
|
18
|
+
s.author = 'Jeff Patmon'
|
19
|
+
s.email = 'jpatmon@yahoo.com'
|
20
|
+
s.homepage = 'http://www.jpatmon.com'
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Default: run all tests.'
|
24
|
+
task :default => :test
|
25
|
+
|
26
|
+
require 'spec/version'
|
27
|
+
require 'spec/rake/spectask'
|
28
|
+
|
29
|
+
namespace :spec do
|
30
|
+
desc "Run all specs"
|
31
|
+
Spec::Rake::SpecTask.new('test') do |t|
|
32
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
33
|
+
#t.spec_opts = ['--options', 'spec/spec.opts']
|
34
|
+
t.rcov = false
|
35
|
+
#t.rcov_dir = 'coverage'
|
36
|
+
#t.rcov_opts = ['--exclude', "kernel,load-diff-lcs\.rb,instance_exec\.rb,lib/spec.rb,lib/spec/runner.rb,^spec/*,bin/spec,examples,/gems,/Library/Ruby,\.autotest,#{ENV['GEM_HOME']}"]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
=begin
|
42
|
+
desc "Test the #{spec.name} plugin."
|
43
|
+
Rake::TestTask.new(:test) do |t|
|
44
|
+
t.libs << 'lib'
|
45
|
+
t.libs << 'spec'
|
46
|
+
t.test_files = spec.test_files
|
47
|
+
t.verbose = true
|
48
|
+
end
|
49
|
+
|
50
|
+
begin
|
51
|
+
require 'rcov/rcovtask'
|
52
|
+
namespace :test do
|
53
|
+
desc "Test the #{spec.name} plugin with Rcov."
|
54
|
+
Rcov::RcovTask.new(:rcov) do |t|
|
55
|
+
t.libs << 'lib'
|
56
|
+
t.test_files = spec.test_files
|
57
|
+
t.rcov_opts << '--exclude="^(?!lib/)"'
|
58
|
+
t.verbose = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
rescue LoadError
|
62
|
+
end
|
63
|
+
=end
|
64
|
+
|
65
|
+
desc "Generate documentation for the #{spec.name} plugin."
|
66
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
67
|
+
rdoc.rdoc_dir = 'rdoc'
|
68
|
+
rdoc.title = spec.name
|
69
|
+
#rdoc.template = '../rdoc_template.rb'
|
70
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
71
|
+
rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'LICENSE', 'lib/**/*.rb')
|
72
|
+
end
|
73
|
+
|
74
|
+
desc 'Generate a gemspec file.'
|
75
|
+
task :gemspec do
|
76
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
77
|
+
f.write spec.to_ruby
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Rake::GemPackageTask.new(spec) do |p|
|
82
|
+
p.gem_spec = spec
|
83
|
+
p.need_tar = true
|
84
|
+
p.need_zip = true
|
85
|
+
end
|
86
|
+
|
87
|
+
Dir['tasks/**/*.rake'].each {|rake| load rake}
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'enumerated_attribute'
|
@@ -0,0 +1,274 @@
|
|
1
|
+
module EnumeratedAttribute
|
2
|
+
|
3
|
+
#todo: system wide constants
|
4
|
+
#todo: allow nil
|
5
|
+
#todo: setter_callback
|
6
|
+
#todo: ArgumentError may need to use Errors for ActiveRecord
|
7
|
+
|
8
|
+
def enumerated_attribute(*args, &block)
|
9
|
+
return if args.empty?
|
10
|
+
attr_name = args[0].to_s
|
11
|
+
attr_sym = attr_name.to_sym
|
12
|
+
enums = (args[1] && args[1].instance_of?(Array) ? args[1] : [])
|
13
|
+
index = enums.empty? ? 1 : 2
|
14
|
+
opts = (args[index] && args[index].instance_of?(Hash) ? args[index] : {})
|
15
|
+
|
16
|
+
raise(ArgumentError, 'second argument of enumerated_attribute/enum_attr is not an array of symbols or strings representing the enum values', caller) if enums.empty?
|
17
|
+
|
18
|
+
#todo: better pluralization of attribute
|
19
|
+
initial_value = nil
|
20
|
+
plural_name = opts[:plural] || opts[:enums_accessor] || opts[:enums] || "#{attr_name}s"
|
21
|
+
incrementor = opts[:incrementor] || opts[:inc] || "#{attr_name}_next"
|
22
|
+
decrementor = opts[:decrementor] || opts[:dec] || "#{attr_name}_previous"
|
23
|
+
|
24
|
+
class_eval <<-ATTRIB
|
25
|
+
@@enumerated_attribute_names ||= []
|
26
|
+
@@enumerated_attribute_names << '#{attr_name}'
|
27
|
+
ATTRIB
|
28
|
+
|
29
|
+
unless enums.empty?
|
30
|
+
enums = enums.map{|v| (v =~ /^\^/ ? (initial_value = v[1, v.length-1].to_sym) : v.to_sym )}
|
31
|
+
class_eval <<-ENUMS
|
32
|
+
@@enumerated_attribute_values ||= {}
|
33
|
+
@@enumerated_attribute_values[:#{attr_name}] = [:#{enums.join(',:')}]
|
34
|
+
ENUMS
|
35
|
+
end
|
36
|
+
|
37
|
+
#create accessors
|
38
|
+
attr_reader attr_sym
|
39
|
+
if enums.empty?
|
40
|
+
attr_writer attr_sym
|
41
|
+
else
|
42
|
+
enumerated_attr_writer attr_sym
|
43
|
+
end
|
44
|
+
|
45
|
+
#define dynamic methods in method_missing
|
46
|
+
class_eval <<-METHOD
|
47
|
+
unless @missing_method_for_enumerated_attribute_defined
|
48
|
+
if method_defined?(:method_missing)
|
49
|
+
alias_method(:method_missing_without_enumerated_attribute, :method_missing)
|
50
|
+
def method_missing(methId, *args, &block)
|
51
|
+
return self.send(methId) if define_enumerated_attribute_dynamic_method(methId)
|
52
|
+
method_missing_without_enumerated_attribute(methId, *args, &block)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
def method_missing(methId, *args, &block)
|
56
|
+
return self.send(methId) if define_enumerated_attribute_dynamic_method(methId)
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
@missing_method_for_enumerated_attribute_defined = true
|
61
|
+
|
62
|
+
alias_method :respond_to_without_enumerated_attribute?, :respond_to?
|
63
|
+
def respond_to?(method)
|
64
|
+
respond_to_without_enumerated_attribute?(method) || !!parse_dynamic_method_parts(method.to_s)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def parse_dynamic_method_parts(meth_name)
|
70
|
+
return(nil) unless meth_name[-1, 1] == '?'
|
71
|
+
|
72
|
+
meth_name.chop! #remove the ?
|
73
|
+
|
74
|
+
attr = nil
|
75
|
+
@@enumerated_attribute_names.each do |name|
|
76
|
+
if meth_name.sub!(Regexp.new("^"+name.to_s), "")
|
77
|
+
attr = name; break
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return (nil) unless attr
|
81
|
+
attr_sym = attr.to_sym
|
82
|
+
|
83
|
+
value = nil
|
84
|
+
if @@enumerated_attribute_values.key?(attr_sym)
|
85
|
+
@@enumerated_attribute_values[attr_sym].each do |v|
|
86
|
+
if meth_name.sub!(Regexp.new(v.to_s+"$"), "")
|
87
|
+
value = v; break
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
return (nil) unless value
|
92
|
+
|
93
|
+
[attr, value, meth_name]
|
94
|
+
end
|
95
|
+
|
96
|
+
def define_enumerated_attribute_dynamic_method(methId)
|
97
|
+
meth_name = methId.id2name
|
98
|
+
return(false) unless (parts = parse_dynamic_method_parts(meth_name))
|
99
|
+
return(false) unless parts
|
100
|
+
|
101
|
+
negated = !!parts[2].downcase.match(/_not_/)
|
102
|
+
self.class.define_attribute_state_method(methId, parts[0], parts[1].to_sym, negated)
|
103
|
+
|
104
|
+
true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
METHOD
|
108
|
+
|
109
|
+
#create state and action methods from block
|
110
|
+
initial_value = opts[:init] || initial_value
|
111
|
+
if block_given?
|
112
|
+
m = EnumeratedAttribute::MethodDefinitionDSL.new(self, attr_name, enums)
|
113
|
+
m.instance_eval(&block)
|
114
|
+
initial_value = m.initial_value || initial_value
|
115
|
+
plural_name = m.pluralized_name || plural_name
|
116
|
+
decrementor = m.decrementor_name || decrementor
|
117
|
+
incrementor = m.incrementor_name || incrementor
|
118
|
+
end
|
119
|
+
|
120
|
+
#define the enum values accessor
|
121
|
+
unless enums.empty?
|
122
|
+
class_eval <<-ENUM
|
123
|
+
def #{plural_name}
|
124
|
+
@@enumerated_attribute_values[:#{attr_name}]
|
125
|
+
end
|
126
|
+
def #{incrementor}
|
127
|
+
z = @@enumerated_attribute_values[:#{attr_name}]
|
128
|
+
index = z.index(@#{attr_name})
|
129
|
+
@#{attr_name} = z[index >= z.size-1 ? 0 : index+1]
|
130
|
+
end
|
131
|
+
def #{decrementor}
|
132
|
+
z = @@enumerated_attribute_values[:#{attr_name}]
|
133
|
+
index = z.index(@#{attr_name})
|
134
|
+
@#{attr_name} = z[index > 0 ? index-1 : z.size-1]
|
135
|
+
end
|
136
|
+
ENUM
|
137
|
+
end
|
138
|
+
|
139
|
+
#establish initial value
|
140
|
+
if (initial_value = opts[:init] || initial_value)
|
141
|
+
class_eval <<-INITVAL
|
142
|
+
unless @enumerated_attribute_init
|
143
|
+
@enumerated_attribute_init = {}
|
144
|
+
class << self
|
145
|
+
def new_with_enumerated_attribute(*args)
|
146
|
+
result = new_without_enumerated_attribute(*args)
|
147
|
+
@enumerated_attribute_init.each do |k,v|
|
148
|
+
result.instance_variable_set("@"+k.to_s, v)
|
149
|
+
end
|
150
|
+
result
|
151
|
+
end
|
152
|
+
alias_method :new_without_enumerated_attribute, :new
|
153
|
+
alias_method :new, :new_with_enumerated_attribute
|
154
|
+
end
|
155
|
+
end
|
156
|
+
@enumerated_attribute_init[:#{attr_name}] = :#{initial_value}
|
157
|
+
INITVAL
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
#a short cut
|
163
|
+
alias :enum_attr :enumerated_attribute
|
164
|
+
|
165
|
+
def define_attribute_state_method(symbol, attr_name, value, negated)
|
166
|
+
define_method symbol do
|
167
|
+
ival = instance_variable_get('@'+attr_name)
|
168
|
+
negated ? ival != value : ival == value
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def enumerated_attr_writer name
|
175
|
+
name = name.to_s
|
176
|
+
class_eval <<-METHOD
|
177
|
+
def #{name}=(val)
|
178
|
+
val = val.to_sym if val.instance_of?(String)
|
179
|
+
raise(ArgumentError, "'" + val.to_s + "' is not an enumerated value for #{name} attribute", caller) unless @@enumerated_attribute_values[:#{name}].include?(val)
|
180
|
+
@#{name} = val
|
181
|
+
end
|
182
|
+
METHOD
|
183
|
+
end
|
184
|
+
|
185
|
+
public
|
186
|
+
|
187
|
+
class MethodDefinitionDSL
|
188
|
+
attr_reader :initial_value, :pluralized_name, :decrementor_name, :incrementor_name
|
189
|
+
|
190
|
+
def initialize(class_obj, attr_name, values=[])
|
191
|
+
@class_obj = class_obj
|
192
|
+
@attr_name = attr_name
|
193
|
+
@attr_values = values
|
194
|
+
end
|
195
|
+
|
196
|
+
def method_missing(methId, *args, &block)
|
197
|
+
meth_name = methId.id2name
|
198
|
+
if meth_name[-1,1] != '?'
|
199
|
+
#not a state method - this must include either a proc or block - no short cuts
|
200
|
+
if args.size > 0
|
201
|
+
arg = args[0]
|
202
|
+
if arg.instance_of?(Proc)
|
203
|
+
@class_obj.send(:define_method, methId, arg)
|
204
|
+
return
|
205
|
+
end
|
206
|
+
elsif block_given?
|
207
|
+
@class_obj.send(:define_method, methId, block)
|
208
|
+
return
|
209
|
+
end
|
210
|
+
raise ArgumentError, "method '#{meth_name}' must be followed by a proc or block", caller
|
211
|
+
end
|
212
|
+
|
213
|
+
if (args.size > 0)
|
214
|
+
arg = args[0]
|
215
|
+
case arg
|
216
|
+
when Symbol
|
217
|
+
return create_method_from_symbol_or_string(meth_name, arg)
|
218
|
+
when String
|
219
|
+
return create_method_from_symbol_or_string(meth_name, arg)
|
220
|
+
when Array
|
221
|
+
return create_method_from_array(meth_name, arg)
|
222
|
+
when Proc
|
223
|
+
@class_obj.send(:define_method, methId, arg)
|
224
|
+
return
|
225
|
+
end
|
226
|
+
elsif block_given?
|
227
|
+
@class_obj.send(:define_method, methId, block)
|
228
|
+
return
|
229
|
+
end
|
230
|
+
raise ArgumentError , "method '#{meth_name}' for :#{@attr_name} attribute must be followed by a symbol, array, proc or block", caller
|
231
|
+
end
|
232
|
+
|
233
|
+
def init(value)
|
234
|
+
if (!@attr_values.empty? && !@attr_values.include?(value.to_sym))
|
235
|
+
raise(NameError, "'#{value}' in method 'init' is not an enumeration value for :#{@attr_name} attribute", caller)
|
236
|
+
end
|
237
|
+
@initial_value = value
|
238
|
+
end
|
239
|
+
|
240
|
+
def decrementor(value); @decrementor_name = value; end
|
241
|
+
def incrementor(value); @incrementor_name = value; end
|
242
|
+
def enums_accessor(value); @pluralized_name = value; end
|
243
|
+
alias :inc :incrementor
|
244
|
+
alias :dec :decrementor
|
245
|
+
alias :enums :enums_accessor
|
246
|
+
alias :plural :enums_accessor
|
247
|
+
|
248
|
+
private
|
249
|
+
|
250
|
+
def create_method_from_symbol_or_string(meth_name, arg)
|
251
|
+
if (!@attr_values.empty? && !@attr_values.include?(arg.to_sym))
|
252
|
+
raise(NameError, "'#{arg}' in method '#{meth_name}' is not an enumeration value for :#{@attr_name} attribute", caller)
|
253
|
+
end
|
254
|
+
@class_obj.class_eval("def #{meth_name}; @#{@attr_name} == :#{arg}; end")
|
255
|
+
end
|
256
|
+
|
257
|
+
def create_method_from_array(meth_name, arg)
|
258
|
+
if !@attr_values.empty?
|
259
|
+
arg.each do |m|
|
260
|
+
if !@attr_values.include?(m.to_sym)
|
261
|
+
raise(NameError, "'#{m}' in method '#{meth_name}' is not an enumeration value for :#{@attr_name} attribute", caller)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
@class_obj.class_eval("def #{meth_name}; [:#{arg.join(',:')}].include?(@#{@attr_name}); end")
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
class Class
|
271
|
+
include EnumeratedAttribute
|
272
|
+
end
|
273
|
+
|
274
|
+
|
data/spec/car.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'enumerated_attribute'
|
2
|
+
|
3
|
+
#used to test that method_missing chaining plays nice in inheritance situations
|
4
|
+
|
5
|
+
class Vehicle
|
6
|
+
|
7
|
+
attr_accessor :vehicle_method_hit
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@vehicle_method_hit = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(methId, *args)
|
14
|
+
@vehicle_method_hit = true
|
15
|
+
#end here
|
16
|
+
end
|
17
|
+
|
18
|
+
alias :vmh :vehicle_method_hit
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class Car < Vehicle
|
23
|
+
attr_accessor :car_method_hit
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
super
|
27
|
+
@car_method_hit = false
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(methId, *args)
|
31
|
+
@car_method_hit = true
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
enum_attr :gear, %w(reverse ^neutral drive)
|
36
|
+
alias :cmt :car_method_hit
|
37
|
+
end
|
data/spec/car_spec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec/car'
|
2
|
+
|
3
|
+
#used to test that method_missing chaining plays nice in inheritance situation
|
4
|
+
|
5
|
+
describe "Car" do
|
6
|
+
|
7
|
+
it "should not hit Car method_missing when calling dynamic state method :gear_is_in_reverse?" do
|
8
|
+
c = Car.new
|
9
|
+
c.gear_is_in_reverse?
|
10
|
+
c.car_method_hit.should be_false
|
11
|
+
c.vehicle_method_hit.should be_false
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should hit Car and Vehicle method_missing when not calling supported dynamic state method" do
|
15
|
+
c = Car.new
|
16
|
+
c.parking_break_is_on?
|
17
|
+
c.car_method_hit.should be_true
|
18
|
+
c.vehicle_method_hit.should be_true
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/spec/tractor.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'enumerated_attribute'
|
2
|
+
|
3
|
+
class Tractor
|
4
|
+
|
5
|
+
GEAR_ENUM_VALUES = %w(reverse neutral first second over_drive).map{|v| v.to_sym}
|
6
|
+
LIGHTS_ENUM_VALUES = %w(off low high).map{|v| v.to_sym}
|
7
|
+
SIDE_LIGHT_ENUM_VALUES = [:off,:low,:high,:super_high]
|
8
|
+
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@name = 'old faithful'
|
13
|
+
end
|
14
|
+
|
15
|
+
enumerated_attribute :gear, %w(reverse ^neutral first second over_drive) do
|
16
|
+
parked? :neutral
|
17
|
+
driving? [:first, :second, :over_drive]
|
18
|
+
upshift { self.gear_is_in_over_drive? ? self.gear : self.gear_next }
|
19
|
+
downshift { self.driving? ? self.gear_previous : self.gear }
|
20
|
+
end
|
21
|
+
|
22
|
+
enum_attr :plow, %w(^up down) do
|
23
|
+
plowing? { self.gear_is_in_first? && self.plow == :down }
|
24
|
+
end
|
25
|
+
|
26
|
+
enum_attr :lights, LIGHTS_ENUM_VALUES, :enums_accessor=>:lights_enums, :init=>:off, :decrementor=>:turn_lights_down, :incrementor=>:turn_lights_up do
|
27
|
+
lights_are_on? [:low, :high]
|
28
|
+
lights_are_not_on? :off
|
29
|
+
end
|
30
|
+
|
31
|
+
enum_attr :side_light, %w(off low high super_high) do
|
32
|
+
init :off
|
33
|
+
enums_accessor :side_light_enums
|
34
|
+
incrementor :side_light_up
|
35
|
+
decrementor :side_light_down
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'spec/tractor'
|
2
|
+
|
3
|
+
describe "Tractor" do
|
4
|
+
|
5
|
+
it "should have respond_to? method for :gear_is_in_neutral?" do
|
6
|
+
t=Tractor.new
|
7
|
+
t.respond_to?('gear_is_in_neutral?').should be_true
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have respond_to? method for :side_light_is_super_high?" do
|
11
|
+
t=Tractor.new
|
12
|
+
t.respond_to?(:side_light_is_super_high?).should be_true
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should not have respond_to? method for :gear_is_in_high?" do
|
16
|
+
t=Tractor.new
|
17
|
+
t.respond_to?(:gear_is_in_high?).should be_false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should initially set :plow to :up" do
|
21
|
+
t=Tractor.new
|
22
|
+
t.plow.should == :up
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should have plowing? state method" do
|
26
|
+
t=Tractor.new
|
27
|
+
t.plowing?.should be_false
|
28
|
+
t.plow=:down
|
29
|
+
t.plowing?.should be_false
|
30
|
+
t.gear= :first
|
31
|
+
t.plowing?.should be_true
|
32
|
+
t.plow=:up
|
33
|
+
t.plowing?.should be_false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should have :side_light_up incrementor" do
|
37
|
+
t=Tractor.new
|
38
|
+
t.side_light = :off
|
39
|
+
t.side_light_up.should == :low
|
40
|
+
t.side_light_up.should == :high
|
41
|
+
t.side_light_up.should == :super_high
|
42
|
+
t.side_light_up.should == :off
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should have :side_light_down incrementor" do
|
46
|
+
t=Tractor.new
|
47
|
+
t.side_light_down.should == :super_high
|
48
|
+
t.side_light_down.should == :high
|
49
|
+
t.side_light_down.should == :low
|
50
|
+
t.side_light_down.should == :off
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should have :turn_lights_up incrementor" do
|
54
|
+
t=Tractor.new
|
55
|
+
t.lights = :off
|
56
|
+
t.turn_lights_up.should == :low
|
57
|
+
t.turn_lights_up.should == :high
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should have :turn_lights_down decrementor" do
|
61
|
+
t=Tractor.new
|
62
|
+
t.lights=:high
|
63
|
+
t.turn_lights_down.should == :low
|
64
|
+
t.turn_lights_down.should == :off
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should have :gear_previous which wraps from :neutral to :over_drive" do
|
68
|
+
t=Tractor.new
|
69
|
+
t.gear_previous.should == :reverse
|
70
|
+
t.gear.should == :reverse
|
71
|
+
t.gear_previous.should == :over_drive
|
72
|
+
t.gear.should == :over_drive
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should have :gear_next which wraps from :second to :reverse" do
|
76
|
+
t=Tractor.new
|
77
|
+
t.gear = :second
|
78
|
+
t.gear_next.should == :over_drive
|
79
|
+
t.gear.should == :over_drive
|
80
|
+
t.gear_next.should == :reverse
|
81
|
+
t.gear.should == :reverse
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should have :upshift which increments :gear from :neutral to :over_drive without wrapping" do
|
85
|
+
t=Tractor.new
|
86
|
+
t.upshift.should == :first
|
87
|
+
t.upshift.should == :second
|
88
|
+
t.upshift.should == :over_drive
|
89
|
+
t.upshift.should == :over_drive
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should have :downshift which decrements :gear from :over_drive to :neutral without wrapping" do
|
93
|
+
t=Tractor.new
|
94
|
+
t.gear = :over_drive
|
95
|
+
t.downshift.should == :second
|
96
|
+
t.downshift.should == :first
|
97
|
+
t.downshift.should == :neutral
|
98
|
+
t.downshift.should == :neutral
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should have parked? method" do
|
102
|
+
t=Tractor.new
|
103
|
+
t.parked?.should be_true
|
104
|
+
t.gear = :reverse
|
105
|
+
t.parked?.should be_false
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should have driving? method" do
|
109
|
+
t=Tractor.new
|
110
|
+
t.driving?.should be_false
|
111
|
+
[:first, :second, :over_drive].each do |g|
|
112
|
+
t.gear=g
|
113
|
+
t.driving?.should be_true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should initially set side_light to :off" do
|
118
|
+
t=Tractor.new
|
119
|
+
t.side_light.should == :off
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should have side_light_enums method" do
|
123
|
+
t = Tractor.new
|
124
|
+
t.side_light_enums.should == Tractor::SIDE_LIGHT_ENUM_VALUES
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should have state method side_light_is_off?" do
|
128
|
+
t=Tractor.new
|
129
|
+
t.side_light_is_off?.should be_true
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should have state method side_light_is_super_high?" do
|
133
|
+
t=Tractor.new
|
134
|
+
t.side_light_is_super_high?.should be_false
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should initially set :gear to :neutral" do
|
138
|
+
t=Tractor.new
|
139
|
+
t.gear.should == :neutral
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should set lights initially to :off" do
|
143
|
+
t=Tractor.new
|
144
|
+
t.lights.should == :off
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should create a lights_enums method for all light enumerated values" do
|
148
|
+
t=Tractor.new
|
149
|
+
t.lights_enums.should == Tractor::LIGHTS_ENUM_VALUES
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should initially set lights to :off" do
|
153
|
+
t=Tractor.new
|
154
|
+
t.lights.should equal(:off)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should have dynamic state methods for :reverse and :neutral" do
|
158
|
+
t = Tractor.new
|
159
|
+
t.gear_is_in_reverse?.should be_false
|
160
|
+
t.gear_is_in_neutral?.should be_true
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should have negative dynamic state methods for :reverses and :neutral" do
|
164
|
+
t = Tractor.new
|
165
|
+
t.gear_is_not_in_reverse?.should be_true
|
166
|
+
t.gear_is_not_in_neutral?.should be_false
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should have negative and positive dynamic state methods for :over_drive" do
|
170
|
+
t = Tractor.new
|
171
|
+
t.gear_is_in_over_drive?.should be_false
|
172
|
+
t.gear_is_not_in_over_drive?.should be_true
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should have created instance methods for :reverse" do
|
176
|
+
m = Tractor.instance_methods(false)
|
177
|
+
m.should include('gear_is_in_reverse?')
|
178
|
+
m.should include('gear_is_not_in_reverse?')
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should have created instance methods for :neutral" do
|
182
|
+
m = Tractor.instance_methods(false)
|
183
|
+
m.should include('gear_is_in_neutral?')
|
184
|
+
m.should include('gear_is_not_in_neutral?')
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should have created instance methods for :over_drive" do
|
188
|
+
m = Tractor.instance_methods(false)
|
189
|
+
m.should include('gear_is_in_over_drive?')
|
190
|
+
m.should include('gear_is_not_in_over_drive?')
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should raise NoMethodError for dynamic state methods not querying valid enumeration values" do
|
194
|
+
t = Tractor.new
|
195
|
+
lambda { t.gear_is_in_high? }.should raise_error(NoMethodError)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should convert string values to symbols for attr setters" do
|
199
|
+
t = Tractor.new
|
200
|
+
t.gear= 'reverse'
|
201
|
+
t.gear.should == :reverse
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should have class variable @@enumerated_attribute_names" do
|
205
|
+
Tractor.class_variable_defined?('@@enumerated_attribute_names').should be_true
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should have instance method gears equal to enumeration array" do
|
209
|
+
Tractor.new.gears.should == Tractor::GEAR_ENUM_VALUES
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should have gear attribute initialized to :neutral" do
|
213
|
+
t = Tractor.new
|
214
|
+
t.gear.should == :neutral
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should set gear attribute to :first" do
|
218
|
+
t = Tractor.new
|
219
|
+
t.gear = :first
|
220
|
+
t.gear.should == :first
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should raise error when set gear attribute to :broken" do
|
224
|
+
t = Tractor.new
|
225
|
+
lambda { t.gear= :broken }.should raise_error(ArgumentError)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should have name attribute initially set to 'old faithful'" do
|
229
|
+
t = Tractor.new
|
230
|
+
t.name.should == 'old faithful'
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should set name attribute to 'broke n busted'" do
|
234
|
+
t = Tractor.new
|
235
|
+
t.name = 'broke n busted'
|
236
|
+
t.name.should == 'broke n busted'
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jeffp-enumerated_attribute
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeff Patmon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-09 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A enumerated attribute accessor
|
17
|
+
email: jpatmon@yahoo.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/enumerated_attribute.rb
|
26
|
+
- spec/car.rb
|
27
|
+
- spec/car_spec.rb
|
28
|
+
- spec/tractor.rb
|
29
|
+
- spec/tractor_spec.rb
|
30
|
+
- CHANGELOG.rdoc
|
31
|
+
- init.rb
|
32
|
+
- LICENSE
|
33
|
+
- Rakefile
|
34
|
+
- README.rdoc
|
35
|
+
- .gitignore
|
36
|
+
has_rdoc: false
|
37
|
+
homepage: http://www.jpatmon.com
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.2.0
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: Defines enumerated attributes, initial state and dynamic state methods.
|
62
|
+
test_files:
|
63
|
+
- spec/car_spec.rb
|
64
|
+
- spec/tractor_spec.rb
|