has_enumeration 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/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.txt +35 -0
- data/Rakefile +17 -0
- data/VERSION +1 -0
- data/features/explicitly_mapped_enumeration.feature +51 -0
- data/features/implicitly_mapped_enumeration.feature +51 -0
- data/features/step_definitions/has_enumeration_steps.rb +58 -0
- data/features/support/env.rb +17 -0
- data/features/support/explicitly_mapped_model.rb +3 -0
- data/features/support/implicitly_mapped_model.rb +3 -0
- data/lib/has_enumeration/aggregate_conditions_override.rb +16 -0
- data/lib/has_enumeration/class_methods.rb +67 -0
- data/lib/has_enumeration.rb +7 -0
- metadata +78 -0
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2010 Greg Spurrier
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.txt
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
== Description
|
2
|
+
This ActiveRecord plugin enables attributes to be declared as enumerations
|
3
|
+
of symbols.
|
4
|
+
|
5
|
+
== Example
|
6
|
+
class TestObject < ActiveRecord::Base
|
7
|
+
# Integer attribute with explicit value mapping
|
8
|
+
has_enumeration :color, :red => 1, :green => 2, :blue => 3
|
9
|
+
|
10
|
+
|
11
|
+
# String attribute with implicit value mapping
|
12
|
+
# has_enumeration :color, [:red, :green, :blue]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Value and predicates
|
16
|
+
obj = TestObject.new
|
17
|
+
obj.color.value # => nil
|
18
|
+
obj.color = :red # => :red
|
19
|
+
obj.color.value # => :red
|
20
|
+
obj.color.red? # => true
|
21
|
+
obj.color.green? # => false
|
22
|
+
|
23
|
+
# Querying
|
24
|
+
TestObject.where(:color => :red)
|
25
|
+
|
26
|
+
== Getting the Latest Version
|
27
|
+
The repository for has_enumeration is hosted at GitHub:
|
28
|
+
Web page: http://github.com/gregspurrier/has_enumeration
|
29
|
+
Clone URL: git://github.com/gregspurrier/has_enumeration.git
|
30
|
+
|
31
|
+
== Supported Versions
|
32
|
+
has_enumeration has been tested with:
|
33
|
+
- ActiveRecord 3.0.0rc
|
34
|
+
- Ruby 1.8.7-p299
|
35
|
+
- Ruby 1.9.2-head (revision 28788)
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'jeweler'
|
2
|
+
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
4
|
+
gemspec.name = "has_enumeration"
|
5
|
+
gemspec.summary = "Support for symbol-based enumerations in ActiveRecord"
|
6
|
+
gemspec.description = <<-EOF
|
7
|
+
Extends ActiveRecord with the has_enumeration method that allows an
|
8
|
+
attribute to be declared as storing an enumeration. The enumeration
|
9
|
+
is specified as a mapping between symbols and their underlying
|
10
|
+
representation in the database. Predicates are provided for each
|
11
|
+
symbol in the enumeration and the symbols may be used in finder methods.
|
12
|
+
EOF
|
13
|
+
gemspec.email = "greg@rujubu.com"
|
14
|
+
gemspec.homepage = "http://github.com/gregspurrier/has_enumeration"
|
15
|
+
gemspec.author = "Greg Spurrier"
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,51 @@
|
|
1
|
+
Feature:
|
2
|
+
|
3
|
+
As a programmer using an ActiveRecord model with an integer column
|
4
|
+
representing an enumerated value
|
5
|
+
I want to be able to refer to the enumeration symbolically and have
|
6
|
+
predicates to test its value
|
7
|
+
So that I can write pretty, concise code.
|
8
|
+
|
9
|
+
Scenario Outline: set an enumerated value on an unsaved object
|
10
|
+
Given a model with an explicitly-mapped enumeration of red, green, and blue
|
11
|
+
And an unsaved instance of that model
|
12
|
+
When I assign a symbolic value <assigned> to the enumeration
|
13
|
+
Then it should have the assigned value as its value
|
14
|
+
And the red? predicate should be <red?>
|
15
|
+
And the green? predicate should be <green?>
|
16
|
+
And the blue? predicate should be <blue?>
|
17
|
+
|
18
|
+
Scenarios: setting various values
|
19
|
+
| assigned | red? | green? | blue? |
|
20
|
+
| :red | true | false | false |
|
21
|
+
| :green | false | true | false |
|
22
|
+
| :blue | false | false | true |
|
23
|
+
|
24
|
+
Scenario Outline: set an enumerated value then save and reload
|
25
|
+
Given a model with an explicitly-mapped enumeration of red, green, and blue
|
26
|
+
And an unsaved instance of that model
|
27
|
+
When I assign a symbolic value <assigned> to the enumeration
|
28
|
+
And I save and reload the object
|
29
|
+
Then it should have the assigned value as its value
|
30
|
+
And the red? predicate should be <red?>
|
31
|
+
And the green? predicate should be <green?>
|
32
|
+
And the blue? predicate should be <blue?>
|
33
|
+
|
34
|
+
Scenarios: setting various values
|
35
|
+
| assigned | red? | green? | blue? |
|
36
|
+
| :red | true | false | false |
|
37
|
+
| :green | false | true | false |
|
38
|
+
| :blue | false | false | true |
|
39
|
+
|
40
|
+
Scenario Outline: find objects with a specific enumerated value
|
41
|
+
Given a model with an explicitly-mapped enumeration of red, green, and blue
|
42
|
+
And a set of objects with a variety of values for the enumeration
|
43
|
+
When I query for objects with the value <value>
|
44
|
+
Then I should get all of the objects having that value
|
45
|
+
And I should not get any objects having other values
|
46
|
+
|
47
|
+
Scenarios: querying various values
|
48
|
+
| value |
|
49
|
+
| :red |
|
50
|
+
| :green |
|
51
|
+
| :blue |
|
@@ -0,0 +1,51 @@
|
|
1
|
+
Feature:
|
2
|
+
|
3
|
+
As a programmer using an ActiveRecord model with a string column
|
4
|
+
representing an enumerated value
|
5
|
+
I want to be able to refer to the enumeration symbolically and have
|
6
|
+
predicates to test its value
|
7
|
+
So that I can write pretty, concise code.
|
8
|
+
|
9
|
+
Scenario Outline: set an enumerated value on an unsaved object
|
10
|
+
Given a model with an implicitly-mapped enumeration of red, green, and blue
|
11
|
+
And an unsaved instance of that model
|
12
|
+
When I assign a symbolic value <assigned> to the enumeration
|
13
|
+
Then it should have the assigned value as its value
|
14
|
+
And the red? predicate should be <red?>
|
15
|
+
And the green? predicate should be <green?>
|
16
|
+
And the blue? predicate should be <blue?>
|
17
|
+
|
18
|
+
Scenarios: setting various values
|
19
|
+
| assigned | red? | green? | blue? |
|
20
|
+
| :red | true | false | false |
|
21
|
+
| :green | false | true | false |
|
22
|
+
| :blue | false | false | true |
|
23
|
+
|
24
|
+
Scenario Outline: set an enumerated value then save and reload
|
25
|
+
Given a model with an implicitly-mapped enumeration of red, green, and blue
|
26
|
+
And an unsaved instance of that model
|
27
|
+
When I assign a symbolic value <assigned> to the enumeration
|
28
|
+
And I save and reload the object
|
29
|
+
Then it should have the assigned value as its value
|
30
|
+
And the red? predicate should be <red?>
|
31
|
+
And the green? predicate should be <green?>
|
32
|
+
And the blue? predicate should be <blue?>
|
33
|
+
|
34
|
+
Scenarios: setting various values
|
35
|
+
| assigned | red? | green? | blue? |
|
36
|
+
| :red | true | false | false |
|
37
|
+
| :green | false | true | false |
|
38
|
+
| :blue | false | false | true |
|
39
|
+
|
40
|
+
Scenario Outline: find objects with a specific enumerated value
|
41
|
+
Given a model with an implicitly-mapped enumeration of red, green, and blue
|
42
|
+
And a set of objects with a variety of values for the enumeration
|
43
|
+
When I query for objects with the value <value>
|
44
|
+
Then I should get all of the objects having that value
|
45
|
+
And I should not get any objects having other values
|
46
|
+
|
47
|
+
Scenarios: querying various values
|
48
|
+
| value |
|
49
|
+
| :red |
|
50
|
+
| :green |
|
51
|
+
| :blue |
|
@@ -0,0 +1,58 @@
|
|
1
|
+
Given /^a model with an explicitly-mapped enumeration of red, green, and blue$/ do
|
2
|
+
@model_class = ExplicitlyMappedModel
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^a model with an implicitly-mapped enumeration of red, green, and blue$/ do
|
6
|
+
@model_class = ImplicitlyMappedModel
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^an unsaved instance of that model$/ do
|
10
|
+
@object = @model_class.new
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^I assign a symbolic value :([a-z_]+) to the enumeration$/ do |value|
|
14
|
+
@assigned = value.to_sym
|
15
|
+
@object.color = @assigned
|
16
|
+
end
|
17
|
+
|
18
|
+
When /^I save and reload the object$/ do
|
19
|
+
@object.save!
|
20
|
+
@object = @model_class.find(@object.id)
|
21
|
+
end
|
22
|
+
|
23
|
+
Then /^it should have the assigned value as its value$/ do
|
24
|
+
@object.color.value.should == @assigned
|
25
|
+
end
|
26
|
+
|
27
|
+
Then /^the ([a-z_]+\?) predicate should be (true|false)$/ do |predicate, value|
|
28
|
+
if value == 'true'
|
29
|
+
expected_value = true
|
30
|
+
else
|
31
|
+
expected_value = false
|
32
|
+
end
|
33
|
+
@object.color.send(predicate).should == expected_value
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
Given /^a set of objects with a variety of values for the enumeration$/ do
|
38
|
+
@model_class.delete_all
|
39
|
+
2.times do
|
40
|
+
[:red, :green, :blue].each do |color|
|
41
|
+
@model_class.create!(:color => color)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@all_objects = @model_class.all(:order => :id)
|
45
|
+
end
|
46
|
+
|
47
|
+
When /^I query for objects with the value :([a-z_]+)$/ do |value|
|
48
|
+
@desired_color = value.to_sym
|
49
|
+
@results = @model_class.where(:color => @desired_color).order(:id)
|
50
|
+
end
|
51
|
+
|
52
|
+
Then /^I should get all of the objects having that value$/ do
|
53
|
+
@results.should == @all_objects.select {|x| x.color.value == @desired_color}
|
54
|
+
end
|
55
|
+
|
56
|
+
Then /^I should not get any objects having other values$/ do
|
57
|
+
@results.reject {|x| x.color.value == @desired_color}.should be_empty
|
58
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH << File.expand_path('../../../lib', __FILE__)
|
2
|
+
require 'has_enumeration'
|
3
|
+
|
4
|
+
ActiveRecord::Base.establish_connection(
|
5
|
+
:adapter => 'sqlite3',
|
6
|
+
:database => File.expand_path('../database', __FILE__)
|
7
|
+
)
|
8
|
+
|
9
|
+
class CreateTables < ActiveRecord::Migration
|
10
|
+
create_table :explicitly_mapped_models, :force => true do |t|
|
11
|
+
t.integer :color
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :implicitly_mapped_models, :force => true do |t|
|
15
|
+
t.string :color
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module HasEnumeration
|
2
|
+
module AggregateConditionsOverride
|
3
|
+
# Override the aggregate hash conditions behavior to coerce has_enumeration
|
4
|
+
# attributes that show up in finder options as symbols into instances of
|
5
|
+
# the aggregate class before hash expansion.
|
6
|
+
def expand_hash_conditions_for_aggregates(attrs)
|
7
|
+
expanded_attrs = attrs.dup
|
8
|
+
attr_enumeration_mapping_classes.each do |attr, klass|
|
9
|
+
if expanded_attrs[attr].is_a?(Symbol)
|
10
|
+
expanded_attrs[attr] = klass.from_sym(expanded_attrs[attr])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
super(expanded_attrs)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module HasEnumeration
|
2
|
+
module ClassMethods
|
3
|
+
def has_enumeration(attribute, mapping)
|
4
|
+
unless mapping.is_a?(Hash)
|
5
|
+
# Recast the mapping as a symbol -> string hash
|
6
|
+
mapping_hash = {}
|
7
|
+
mapping.each {|m| mapping_hash[m] = m.to_s}
|
8
|
+
mapping = mapping_hash
|
9
|
+
end
|
10
|
+
|
11
|
+
# ActiveRecord's composed_of method will do most of the work for us.
|
12
|
+
# All we have to do is cons up a class that implements the bidirectional
|
13
|
+
# mapping described by the provided hash.
|
14
|
+
klass = create_enumeration_mapping_class(mapping)
|
15
|
+
attr_enumeration_mapping_classes[attribute] = klass
|
16
|
+
|
17
|
+
# Bind the class to a name within the scope of this class
|
18
|
+
attribute_name = attribute.to_s
|
19
|
+
mapping_class_name = attribute_name.camelize
|
20
|
+
const_set(mapping_class_name, klass)
|
21
|
+
scoped_class_name = [self.name, mapping_class_name].join('::')
|
22
|
+
|
23
|
+
composed_of(attribute,
|
24
|
+
:class_name => scoped_class_name,
|
25
|
+
:mapping => [attribute_name, 'raw_value'],
|
26
|
+
:converter => :from_sym
|
27
|
+
)
|
28
|
+
|
29
|
+
# Install our aggregate condition handling override, but only once
|
30
|
+
unless @aggregate_conditions_override_installed
|
31
|
+
extend HasEnumeration::AggregateConditionsOverride
|
32
|
+
@aggregate_conditions_override_installed = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def attr_enumeration_mapping_classes
|
38
|
+
@attr_enumeration_mapping_classes ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_enumeration_mapping_class(mapping)
|
42
|
+
inverted_mapping = mapping.invert
|
43
|
+
Class.new do
|
44
|
+
attr_reader :raw_value, :value
|
45
|
+
|
46
|
+
define_method :initialize do |raw_value|
|
47
|
+
@raw_value = raw_value
|
48
|
+
@value = inverted_mapping[raw_value]
|
49
|
+
end
|
50
|
+
|
51
|
+
mapping.keys.each do |sym|
|
52
|
+
predicate = "#{sym}?".to_sym
|
53
|
+
value = mapping[sym]
|
54
|
+
define_method predicate do
|
55
|
+
@raw_value == value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
(class <<self;self;end).class_eval do
|
60
|
+
define_method :from_sym do |sym|
|
61
|
+
new(mapping[sym])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: has_enumeration
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Greg Spurrier
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-08-09 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: " Extends ActiveRecord with the has_enumeration method that allows an\n attribute to be declared as storing an enumeration. The enumeration\n is specified as a mapping between symbols and their underlying\n representation in the database. Predicates are provided for each\n symbol in the enumeration and the symbols may be used in finder methods.\n"
|
22
|
+
email: greg@rujubu.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- LICENSE.txt
|
29
|
+
- README.txt
|
30
|
+
files:
|
31
|
+
- Gemfile
|
32
|
+
- LICENSE.txt
|
33
|
+
- README.txt
|
34
|
+
- Rakefile
|
35
|
+
- VERSION
|
36
|
+
- features/explicitly_mapped_enumeration.feature
|
37
|
+
- features/implicitly_mapped_enumeration.feature
|
38
|
+
- features/step_definitions/has_enumeration_steps.rb
|
39
|
+
- features/support/env.rb
|
40
|
+
- features/support/explicitly_mapped_model.rb
|
41
|
+
- features/support/implicitly_mapped_model.rb
|
42
|
+
- lib/has_enumeration.rb
|
43
|
+
- lib/has_enumeration/aggregate_conditions_override.rb
|
44
|
+
- lib/has_enumeration/class_methods.rb
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/gregspurrier/has_enumeration
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --charset=UTF-8
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.3.7
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Support for symbol-based enumerations in ActiveRecord
|
77
|
+
test_files: []
|
78
|
+
|