jeffp-enumerated_attribute 0.1.0
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 +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
|