extended_has_enumeration 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +41 -0
- data/HISTORY.txt +44 -0
- data/LICENSE.txt +21 -0
- data/README.md +126 -0
- data/Rakefile +56 -0
- data/TODO.md +8 -0
- data/VERSION +1 -0
- data/all_tests.sh +4 -0
- data/features/arel_attributes.feature +20 -0
- data/features/explicitly_mapped_enumeration.feature +51 -0
- data/features/implicitly_mapped_enumeration.feature +51 -0
- data/features/meta_where_queries.feature +20 -0
- data/features/nonstandard_attribute_enumeration.feature +51 -0
- data/features/step_definitions/has_enumeration_steps.rb +77 -0
- data/features/support/env.rb +30 -0
- data/features/support/explicitly_mapped_model.rb +3 -0
- data/features/support/implicitly_mapped_model.rb +3 -0
- data/features/support/nonstandard_attribute_model.rb +4 -0
- data/install_gemsets.sh +14 -0
- data/lib/extended_has_enumeration/aggregate_conditions_override.rb +16 -0
- data/lib/extended_has_enumeration/arel/table_extensions.rb +66 -0
- data/lib/extended_has_enumeration/arel/table_extensions_arel_one.rb +70 -0
- data/lib/extended_has_enumeration/class_methods.rb +122 -0
- data/lib/{has_enumeration.rb → extended_has_enumeration.rb} +6 -6
- data/spec/extended_has_enumeration/extended_has_enumeration_spec.rb +100 -0
- data/spec/spec_helper.rb +23 -0
- metadata +87 -18
data/Gemfile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
version = ENV['AR_VERSION'] || '3.2.x'
|
4
|
+
|
5
|
+
if version.end_with? 'x'
|
6
|
+
# fuzzy version support
|
7
|
+
version = version.gsub /x$/, '0'
|
8
|
+
gem 'activerecord', '~> ' +version, :require => 'active_record'
|
9
|
+
elsif version == '3'
|
10
|
+
version = '3.0.0'
|
11
|
+
gem 'activerecord', '~> 3.0', :require => 'active_record'
|
12
|
+
else
|
13
|
+
gem 'activerecord', version, :require => 'active_record'
|
14
|
+
end
|
15
|
+
|
16
|
+
gem 'builder'
|
17
|
+
|
18
|
+
#if version >= '3.0.0'
|
19
|
+
# group :meta_where do
|
20
|
+
# gem 'squeel'
|
21
|
+
# end
|
22
|
+
#end
|
23
|
+
|
24
|
+
group :development do
|
25
|
+
gem 'jeweler'
|
26
|
+
end
|
27
|
+
|
28
|
+
group :test do
|
29
|
+
gem 'rspec', '~> 2.13.0'
|
30
|
+
gem 'cucumber'
|
31
|
+
|
32
|
+
gem 'ruby-debug19'
|
33
|
+
platforms :ruby do
|
34
|
+
gem 'sqlite3-ruby', :require => 'sqlite3'
|
35
|
+
end
|
36
|
+
|
37
|
+
platforms :jruby do
|
38
|
+
gem 'jdbc-sqlite3'
|
39
|
+
gem 'activerecord-jdbcsqlite3-adapter', '~> 0.9.7'
|
40
|
+
end
|
41
|
+
end
|
data/HISTORY.txt
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
Version 1.0.2:
|
2
|
+
- No code changes
|
3
|
+
- Fixed problem with gem dependencies
|
4
|
+
|
5
|
+
Version 1.0.1:
|
6
|
+
- Added support for Arel 2, which is required by ActiveRecord >= 3.0.3
|
7
|
+
|
8
|
+
Version 1.0.0:
|
9
|
+
- Added #to_sym for generated enumeration class. #value is deprecated.
|
10
|
+
- Raises ArgumentError when assigning, or querying, with a symbol that is
|
11
|
+
not part of the enumeration
|
12
|
+
- A value of nil in underlying attribute results in a nil value for the
|
13
|
+
enumeration
|
14
|
+
- Assigning nil to the enumeration results in a value of nil for the underlying
|
15
|
+
attribute
|
16
|
+
|
17
|
+
Version 0.5.0:
|
18
|
+
- Default name of underlying attribute can be overridden with the :attribute
|
19
|
+
option
|
20
|
+
- Internal cleanup: removed some unnecessary code and patching for Rails 3
|
21
|
+
|
22
|
+
Version 0.4.0:
|
23
|
+
- MetaWhere interoperability
|
24
|
+
- Tested with Rails 3.0.0
|
25
|
+
|
26
|
+
Version 0.3.1:
|
27
|
+
- Re-release of 0.3.0 to pick up missed files
|
28
|
+
|
29
|
+
Version 0.3.0:
|
30
|
+
- Support for Rails 3.0.0.rc2
|
31
|
+
- NOTE: no longer compatible with Rails 3.0.0.rc
|
32
|
+
|
33
|
+
Version 0.2.2:
|
34
|
+
- Cosmetic change: simply gemfile description
|
35
|
+
|
36
|
+
Version 0.2.1:
|
37
|
+
- Support for JRuby 1.5.1
|
38
|
+
- Support for ActiveRecord 2.3.8
|
39
|
+
|
40
|
+
Version 0.2.0:
|
41
|
+
- Support for Arel predicates
|
42
|
+
|
43
|
+
Version 0.1.0:
|
44
|
+
- Initial release
|
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.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# has_enumeration
|
2
|
+
## Overview
|
3
|
+
`has_enumeration` adds support for enumerated types to ActiveRecord. The values
|
4
|
+
of an enumerated attribute are represented as symbols regardless of the
|
5
|
+
attribute's underlying representation in the database. Predicates
|
6
|
+
(e.g. `car.color.red?`) are provided for each value of the enumerated type.
|
7
|
+
|
8
|
+
## Defining Enumerated Attributes
|
9
|
+
Enumerated attributes are declared in the model's class definition with the
|
10
|
+
`has_enumeration` class method. For example:
|
11
|
+
|
12
|
+
class Car < ActiveRecord::Base
|
13
|
+
has_enumeration :color, [:red, :green, :blue]
|
14
|
+
end
|
15
|
+
|
16
|
+
The above definition assumes that the `color` attribute is stored as a string
|
17
|
+
and that its values in the database match those obtained by calling `to_s` on
|
18
|
+
their corresponding symbols. I.e., :red is stored as 'red'.
|
19
|
+
|
20
|
+
If the underlying column is not a string or its values do not match the string
|
21
|
+
version of the enumeration's symbols, then the mapping between symbols and their
|
22
|
+
underlying values may be provided as a hash:
|
23
|
+
|
24
|
+
class Car < ActiveRecord::Base
|
25
|
+
has_enumeration :color, :red => 1, :green => 2, :blue => 3
|
26
|
+
end
|
27
|
+
|
28
|
+
By default, the underlying attribute is assumed to have the same name as the
|
29
|
+
enumeration. If this is not the case, the name of the underlying attibute may
|
30
|
+
be provided with the `:attribute` option:
|
31
|
+
|
32
|
+
class Car < ActiveRecord::Base
|
33
|
+
has_enumeration :color, {:red => 1, :green => 2, :blue => 3},
|
34
|
+
:attribute => :hue
|
35
|
+
end
|
36
|
+
|
37
|
+
## Using Enumerated Attributes
|
38
|
+
### Assignment
|
39
|
+
Enumerated attributes are assigned using symbols:
|
40
|
+
|
41
|
+
car = Car.new(:color => :red)
|
42
|
+
car.color = :blue
|
43
|
+
|
44
|
+
The symbols are coerced into an instance of a nested class (`Car::Color` in
|
45
|
+
this example) that is created by `has_enumeration`. If for some reason you
|
46
|
+
need to avoid the type coercion, you can assign a value of that class directly:
|
47
|
+
|
48
|
+
car.color = Car::Color.from_sym(:green)
|
49
|
+
|
50
|
+
### Querying
|
51
|
+
When constructing queries referencing the enumerated attribute, use the symbol
|
52
|
+
as its value:
|
53
|
+
|
54
|
+
Car.find(:all, :conditions => {:color => :red})
|
55
|
+
|
56
|
+
`has_enumeration` supports Rails 3 and is aware of the model's underlying Arel
|
57
|
+
representation:
|
58
|
+
|
59
|
+
Car.where(:color => :red)
|
60
|
+
Car.where(Car.arel_table[:color].not_in([:red, :green]))
|
61
|
+
|
62
|
+
If you are using MetaWhere, `has_enumeration` plays nicely with it:
|
63
|
+
|
64
|
+
# This example requires the meta_where:
|
65
|
+
Car.where(:color.not_in => [:red, :green])
|
66
|
+
|
67
|
+
### Testing Values
|
68
|
+
The primary means of interacting with enumerated attributes is through the
|
69
|
+
predicate methods that are automatically generated for each value in the
|
70
|
+
enumeration:
|
71
|
+
|
72
|
+
car = Car.new(:color => :red)
|
73
|
+
car.color.red?
|
74
|
+
# => true
|
75
|
+
car.color.green?
|
76
|
+
# => false
|
77
|
+
|
78
|
+
If the value of the attribute is needed as a symbol, e.g., for direct
|
79
|
+
comparison, it can be retrieved with `to_sym`:
|
80
|
+
|
81
|
+
car.color.to_sym
|
82
|
+
# => :red
|
83
|
+
|
84
|
+
## Installation
|
85
|
+
`has_enumeration` is packaged as a gem:
|
86
|
+
|
87
|
+
gem install has_enumeration
|
88
|
+
|
89
|
+
### Rails 3
|
90
|
+
To use `has_enumeration` with Rails 3, simply add it to your application's
|
91
|
+
Gemfile:
|
92
|
+
|
93
|
+
gem 'has_enumeration'
|
94
|
+
|
95
|
+
### Rails 2.x
|
96
|
+
To use `has_enumeration` with Rails 2, add it to your application's
|
97
|
+
environment.rb file:
|
98
|
+
|
99
|
+
config.gem 'has_enumeration'
|
100
|
+
|
101
|
+
## Supported Configurations
|
102
|
+
`has_enumeration` has been tested with the following versions of ActiveRecord:
|
103
|
+
|
104
|
+
* 2.3.10
|
105
|
+
* 3.0.1
|
106
|
+
* 3.0.3
|
107
|
+
|
108
|
+
and the following Ruby implementations:
|
109
|
+
|
110
|
+
* 1.8.7-p302
|
111
|
+
* 1.9.2-p0
|
112
|
+
* JRuby 1.5.5
|
113
|
+
* Rubinius 1.1.0
|
114
|
+
|
115
|
+
|
116
|
+
## Getting the Latest
|
117
|
+
`has_enumeration` is hosted on github at
|
118
|
+
[http://github.com/gregspurrier/has_enumeration](http://github.com/gregspurrier/has_enumeration).
|
119
|
+
|
120
|
+
You can make a local clone of the repository with the following command:
|
121
|
+
|
122
|
+
git clone git://github.com/gregspurrier/has_enumeration.git
|
123
|
+
|
124
|
+
## License
|
125
|
+
`has_enumeration` is Copyright 2010 by Greg Spurrier. It is released under
|
126
|
+
the MIT License. Please see LICENSE.txt for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'cucumber'
|
3
|
+
require 'cucumber/rake/task'
|
4
|
+
require 'rspec'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'jeweler'
|
10
|
+
Jeweler::Tasks.new do |gemspec|
|
11
|
+
gemspec.name = "extended_has_enumeration"
|
12
|
+
gemspec.summary = "Support for symbol-based enumerations in ActiveRecord"
|
13
|
+
gemspec.description = <<-EOF
|
14
|
+
Extends ActiveRecord with the has_enumeration method allowing a symbolic
|
15
|
+
enumeration to be stored in an ActiveRecord attribute. The enumeration is
|
16
|
+
specified as a mapping between symbols and their underlying representation
|
17
|
+
in the database. Predicates are provided for each symbol in the enumeration
|
18
|
+
and the symbols may be used in finder methods. When using ActiveRecord 3,
|
19
|
+
the symbols may also be used when interacting with the underlying Arel attribute
|
20
|
+
for the enumeration. has_enumeration has been tested with Ruby 1.8.7,
|
21
|
+
Ruby 1.9.2, JRuby 1.5.5, Rubinius 1.1.0, ActiveRecord 2.3.10, and ActiveRecord
|
22
|
+
3.0.3.
|
23
|
+
EOF
|
24
|
+
gemspec.email = "maxtsap@gmail.com"
|
25
|
+
gemspec.homepage = "http://github.com/maxtsap/has_enumeration"
|
26
|
+
gemspec.author = "Greg Spurrier and Maxim Tsaplin"
|
27
|
+
end
|
28
|
+
Jeweler::GemcutterTasks.new
|
29
|
+
rescue LoadError
|
30
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
31
|
+
end
|
32
|
+
|
33
|
+
task :features => 'features:all'
|
34
|
+
namespace :features do
|
35
|
+
task :all => [:common, :rails3]
|
36
|
+
|
37
|
+
Cucumber::Rake::Task.new(:common) do |t|
|
38
|
+
features = %w(explicitly_mapped_enumeration implicitly_mapped_enumeration
|
39
|
+
nonstandard_attribute_enumeration)
|
40
|
+
feature_files = features.map {|f| "features/#{f}.feature"}.join(' ')
|
41
|
+
t.cucumber_opts = feature_files
|
42
|
+
end
|
43
|
+
|
44
|
+
Cucumber::Rake::Task.new(:rails3) do |t|
|
45
|
+
# the ../ hack is because of a gherkin bug encountered when the file paths
|
46
|
+
# have exactly two / characters in some interpreters (coughjrubycough)
|
47
|
+
features = %w(../features/arel_attributes meta_where_queries)
|
48
|
+
feature_files = features.map {|f| "features/#{f}.feature"}.join(' ')
|
49
|
+
|
50
|
+
# With the ../ hack we have to specify the features directory via -r
|
51
|
+
t.cucumber_opts = '-r features ' +feature_files
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Run all specs"
|
56
|
+
RSpec::Core::RakeTask.new('spec')
|
data/TODO.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# Release 1.1
|
2
|
+
* Support string assignment
|
3
|
+
* Intended for bulk assignment from form posts
|
4
|
+
* Unlike symbol assignment, will not raise an exception when a value
|
5
|
+
is invalid
|
6
|
+
* Invalid values will fail validation and add an error on the attribute
|
7
|
+
* Expose the accepted values for an enumeration via something like
|
8
|
+
`Car::Color.values`
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
data/all_tests.sh
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Feature:
|
2
|
+
|
3
|
+
As a programmer using has_enumeration in Rails 3
|
4
|
+
I want to be able to refer to the enumeration symbolically when working with
|
5
|
+
the underlying arel attributes for a model
|
6
|
+
So that I can write pretty, concise code.
|
7
|
+
|
8
|
+
|
9
|
+
Scenario Outline: find objects with a specific enumerated value via arel
|
10
|
+
Given a model with an explicitly-mapped enumeration of red, green, and blue
|
11
|
+
And a set of objects with a variety of values for the enumeration
|
12
|
+
When I query for objects with the value <value> via arel
|
13
|
+
Then I should get all of the objects having that value
|
14
|
+
And I should not get any objects having other values
|
15
|
+
|
16
|
+
Scenarios: querying various values
|
17
|
+
| value |
|
18
|
+
| :red |
|
19
|
+
| :green |
|
20
|
+
| :blue |
|
@@ -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,20 @@
|
|
1
|
+
Feature:
|
2
|
+
|
3
|
+
As a programmer using has_enumeration in Rails 3
|
4
|
+
I want to be able to refer to the enumeration symbolically when working with
|
5
|
+
the MetaWhere queries for a model
|
6
|
+
So that I can write pretty, concise code.
|
7
|
+
|
8
|
+
|
9
|
+
Scenario Outline: find objects with a specific enumerated value via MetaWhere
|
10
|
+
Given a model with an explicitly-mapped enumeration of red, green, and blue
|
11
|
+
And a set of objects with a variety of values for the enumeration
|
12
|
+
When I query for objects with the value <value> via MetaWhere
|
13
|
+
Then I should get all of the objects having that value
|
14
|
+
And I should not get any objects having other values
|
15
|
+
|
16
|
+
Scenarios: querying various values
|
17
|
+
| value |
|
18
|
+
| :red |
|
19
|
+
| :green |
|
20
|
+
| :blue |
|
@@ -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 nonstandard-attribute 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 nonstandard-attribute 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 nonstandard-attribute 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,77 @@
|
|
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 /^a model with an nonstandard-attribute enumeration of red, green, and blue$/ do
|
10
|
+
@model_class = NonstandardAttributeModel
|
11
|
+
end
|
12
|
+
|
13
|
+
Given /^an unsaved instance of that model$/ do
|
14
|
+
@object = @model_class.new
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^I assign a symbolic value :([a-z_]+) to the enumeration$/ do |value|
|
18
|
+
@assigned = value.to_sym
|
19
|
+
@object.color = @assigned
|
20
|
+
end
|
21
|
+
|
22
|
+
When /^I save and reload the object$/ do
|
23
|
+
@object.save!
|
24
|
+
@object = @model_class.find(@object.id)
|
25
|
+
end
|
26
|
+
|
27
|
+
Then /^it should have the assigned value as its value$/ do
|
28
|
+
@object.color.to_sym.should == @assigned
|
29
|
+
end
|
30
|
+
|
31
|
+
Then /^the ([a-z_]+\?) predicate should be (true|false)$/ do |predicate, value|
|
32
|
+
if value == 'true'
|
33
|
+
expected_value = true
|
34
|
+
else
|
35
|
+
expected_value = false
|
36
|
+
end
|
37
|
+
@object.color.send(predicate).should == expected_value
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
Given /^a set of objects with a variety of values for the enumeration$/ do
|
42
|
+
a = ExplicitlyMappedModel.new(:color => :red)
|
43
|
+
a.color = :red
|
44
|
+
p a
|
45
|
+
@model_class.delete_all
|
46
|
+
|
47
|
+
2.times do
|
48
|
+
[:red, :green, :blue].each do |color|
|
49
|
+
@model_class.create!(:color => color)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
@all_objects = @model_class.all(:order => :id)
|
53
|
+
end
|
54
|
+
|
55
|
+
When /^I query for objects with the value :([a-z_]+)$/ do |value|
|
56
|
+
@desired_color = value.to_sym
|
57
|
+
@results = @model_class.all(:conditions => {:color => @desired_color}, :order => :id)
|
58
|
+
end
|
59
|
+
|
60
|
+
When /^I query for objects with the value :([a-z_]+) via arel$/ do |value|
|
61
|
+
@desired_color = value.to_sym
|
62
|
+
arel_attr = @model_class.arel_table[:color]
|
63
|
+
@results = @model_class.where(arel_attr.eq(@desired_color)).order(:id)
|
64
|
+
end
|
65
|
+
|
66
|
+
When /^I query for objects with the value :([a-z_]+) via MetaWhere$/ do |value|
|
67
|
+
@desired_color = value.to_sym
|
68
|
+
@results = @model_class.where(:color.eq => @desired_color).order(:id)
|
69
|
+
end
|
70
|
+
|
71
|
+
Then /^I should get all of the objects having that value$/ do
|
72
|
+
@results.should == @all_objects.select {|x| x.color.raw_value == @desired_color}
|
73
|
+
end
|
74
|
+
|
75
|
+
Then /^I should not get any objects having other values$/ do
|
76
|
+
@results.reject {|x| x.color.to_sym == @desired_color}.should be_empty
|
77
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
$LOAD_PATH << File.expand_path('../../../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.require(:default, :test)
|
6
|
+
|
7
|
+
require 'extended_has_enumeration'
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection(
|
10
|
+
:adapter => defined?(JRUBY_VERSION) ? 'jdbcsqlite3': 'sqlite3',
|
11
|
+
:database => File.expand_path('../database', __FILE__)
|
12
|
+
)
|
13
|
+
|
14
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
15
|
+
Bundler.require(:meta_where)
|
16
|
+
end
|
17
|
+
|
18
|
+
class CreateTables < ActiveRecord::Migration
|
19
|
+
create_table :explicitly_mapped_models, :force => true do |t|
|
20
|
+
t.integer :color
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :implicitly_mapped_models, :force => true do |t|
|
24
|
+
t.string :color
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table :nonstandard_attribute_models, :force => true do |t|
|
28
|
+
t.integer :hue
|
29
|
+
end
|
30
|
+
end
|
data/install_gemsets.sh
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
source ~/.rvm/scripts/rvm
|
3
|
+
for ruby in 1.8.7 1.9.2 jruby rbx
|
4
|
+
do
|
5
|
+
rvm use $ruby
|
6
|
+
for ar_rev in 2.3.10 3.0.x
|
7
|
+
do
|
8
|
+
gemset=ar_$ar_rev
|
9
|
+
echo yes | rvm gemset delete $gemset
|
10
|
+
rvm gemset create $gemset
|
11
|
+
rvm gemset use $gemset
|
12
|
+
AR_VERSION=$ar_rev bundle install
|
13
|
+
done
|
14
|
+
done
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ExtendedHasEnumeration
|
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,66 @@
|
|
1
|
+
module ExtendedHasEnumeration
|
2
|
+
module Arel
|
3
|
+
module TableExtensions
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
alias_method_chain :columns, :has_enumeration
|
7
|
+
|
8
|
+
def self.has_enumeration_mappings
|
9
|
+
@has_enumeration_mappings ||= Hash.new {|h,k| h[k] = Hash.new}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def columns_with_has_enumeration
|
15
|
+
return @columns if @columns
|
16
|
+
columns = columns_without_has_enumeration
|
17
|
+
mappings = self.class.has_enumeration_mappings[name]
|
18
|
+
if mappings
|
19
|
+
mappings.each do |attr_name, mapping|
|
20
|
+
attr = columns.detect {|c| c.name.to_s == attr_name.to_s}
|
21
|
+
install_has_enumeration_attribute_mapping(attr, mapping)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
columns
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def install_has_enumeration_attribute_mapping(arel_attr, mapping)
|
29
|
+
# For this attribute only, override all of the methods defined
|
30
|
+
# in Arel::Attribute::PREDICATES so that they will perform the
|
31
|
+
# symbol-to-underlying-value mapping before proceeding with their work.
|
32
|
+
(class <<arel_attr;self;end).class_eval do
|
33
|
+
define_method :map_enumeration_arg do |arg|
|
34
|
+
if arg.is_a?(Symbol)
|
35
|
+
unless mapping.has_key?(arg)
|
36
|
+
raise ArgumentError.new(
|
37
|
+
"#{arg.inspect} is not one of {#{mapping.keys.map(&:inspect).sort.join(', ')}}"
|
38
|
+
)
|
39
|
+
end
|
40
|
+
mapping[arg]
|
41
|
+
elsif arg.is_a?(Array)
|
42
|
+
arg.map {|a| map_enumeration_arg(a)}
|
43
|
+
else
|
44
|
+
arg
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
predicates = ::Arel::Predications.instance_methods.map &:to_sym
|
49
|
+
predicates.each do |method_name|
|
50
|
+
arity = ::Arel::Attribute.instance_method(method_name).arity
|
51
|
+
case arity
|
52
|
+
when 1
|
53
|
+
define_method method_name do |arg|
|
54
|
+
super(map_enumeration_arg(arg))
|
55
|
+
end
|
56
|
+
when 0
|
57
|
+
# No-op
|
58
|
+
else
|
59
|
+
raise "Unexpected arity #{arity} for #{method_name}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Support for Arel 1. Will eventually be removed once most people are on
|
2
|
+
# Arel 2.
|
3
|
+
module ExtendedHasEnumeration
|
4
|
+
module Arel
|
5
|
+
module TableExtensionsArelOne
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
alias_method_chain :attributes, :has_enumeration
|
9
|
+
|
10
|
+
def self.has_enumeration_mappings
|
11
|
+
@has_enumeration_mappings ||= Hash.new {|h,k| h[k] = Hash.new}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def attributes_with_has_enumeration
|
17
|
+
return @attributes if @attributes
|
18
|
+
attrs = attributes_without_has_enumeration
|
19
|
+
mappings = self.class.has_enumeration_mappings[name]
|
20
|
+
if mappings
|
21
|
+
mappings.each do |attr_name, mapping|
|
22
|
+
attr = attrs[attr_name]
|
23
|
+
install_has_enumeration_attribute_mapping(attr, mapping)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
attrs
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def install_has_enumeration_attribute_mapping(arel_attr, mapping)
|
31
|
+
# For this attribute only, override all of the methods defined
|
32
|
+
# in Arel::Attribute::PREDICATES so that they will perform the
|
33
|
+
# symbol-to-underlying-value mapping before proceeding with their work.
|
34
|
+
(class <<arel_attr;self;end).class_eval do
|
35
|
+
define_method :map_enumeration_arg do |arg|
|
36
|
+
if arg.is_a?(Symbol)
|
37
|
+
unless mapping.has_key?(arg)
|
38
|
+
raise ArgumentError.new(
|
39
|
+
"#{arg.inspect} is not one of {#{mapping.keys.map(&:inspect).sort.join(', ')}}"
|
40
|
+
)
|
41
|
+
end
|
42
|
+
mapping[arg]
|
43
|
+
elsif arg.is_a?(Array)
|
44
|
+
arg.map {|a| map_enumeration_arg(a)}
|
45
|
+
else
|
46
|
+
arg
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
::Arel::Attribute::PREDICATES.each do |method_name|
|
51
|
+
# Preserve the arity of the method we are overriding
|
52
|
+
arity = ::Arel::Attribute.instance_method(method_name).arity
|
53
|
+
case arity
|
54
|
+
when 1
|
55
|
+
define_method method_name do |arg|
|
56
|
+
super(map_enumeration_arg(arg))
|
57
|
+
end
|
58
|
+
when -1
|
59
|
+
define_method method_name do |*args|
|
60
|
+
super(map_enumeration_arg(args))
|
61
|
+
end
|
62
|
+
else
|
63
|
+
raise "Unexpected arity #{arity} for #{method_name}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module ExtendedHasEnumeration
|
2
|
+
module ClassMethods
|
3
|
+
# Declares an enumerated attribute called +enumeration+ consisting of
|
4
|
+
# the symbols defined in +mapping+.
|
5
|
+
#
|
6
|
+
# When the database representation of the attribute is a string, +mapping+
|
7
|
+
# can be an array of symbols. The string representation of the symbol
|
8
|
+
# will be stored in the databased. E.g.:
|
9
|
+
#
|
10
|
+
# has_enumeration :color, [:red, :green, :blue]
|
11
|
+
#
|
12
|
+
# When the database representation of the attribute is not a string, or
|
13
|
+
# if its values do not match up with the string versions of its symbols,
|
14
|
+
# an hash mapping symbols to their underlying values may be used:
|
15
|
+
#
|
16
|
+
# has_enumeration :color, :red => 1, :green => 2, :blue => 3
|
17
|
+
#
|
18
|
+
# By default, has_enumeration assumes that the column in the database
|
19
|
+
# has the same name as the enumeration. If this is not the case, the
|
20
|
+
# column can be specified with the :attribute option:
|
21
|
+
#
|
22
|
+
# has_enumeration :color, [:red, :green, :blue], :attribute => :hue
|
23
|
+
#
|
24
|
+
def has_enumeration(enumeration, mapping, options = {})
|
25
|
+
unless mapping.is_a?(Hash)
|
26
|
+
# Recast the mapping as a symbol -> string hash
|
27
|
+
mapping_hash = {}
|
28
|
+
mapping.each {|m| mapping_hash[m] = m.to_s}
|
29
|
+
mapping = mapping_hash
|
30
|
+
end
|
31
|
+
|
32
|
+
# The underlying attribute
|
33
|
+
attribute = options[:attribute] || enumeration
|
34
|
+
|
35
|
+
# ActiveRecord's composed_of method will do most of the work for us.
|
36
|
+
# All we have to do is cons up a class that implements the bidirectional
|
37
|
+
# mapping described by the provided hash.
|
38
|
+
klass = create_enumeration_mapping_class(mapping)
|
39
|
+
attr_enumeration_mapping_classes[enumeration] = klass
|
40
|
+
|
41
|
+
# Bind the class to a name within the scope of this class
|
42
|
+
mapping_class_name = enumeration.to_s.camelize
|
43
|
+
const_set(mapping_class_name, klass)
|
44
|
+
scoped_class_name = [self.name, mapping_class_name].join('::')
|
45
|
+
|
46
|
+
composed_of(enumeration,
|
47
|
+
:class_name => scoped_class_name,
|
48
|
+
:mapping => [attribute.to_s, 'raw_value'],
|
49
|
+
:converter => :from_sym,
|
50
|
+
:allow_nil => true
|
51
|
+
)
|
52
|
+
|
53
|
+
if ActiveRecord::VERSION::MAJOR >= 3 && ActiveRecord::VERSION::MINOR == 0
|
54
|
+
# Install this attributes mapping for use later when extending
|
55
|
+
# Arel attributes on the fly.
|
56
|
+
::Arel::Table.has_enumeration_mappings[table_name][attribute] = mapping
|
57
|
+
else
|
58
|
+
# Install our aggregate condition handling override, but only once
|
59
|
+
unless @aggregate_conditions_override_installed
|
60
|
+
extend ExtendedHasEnumeration::AggregateConditionsOverride
|
61
|
+
@aggregate_conditions_override_installed = true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def attr_enumeration_mapping_classes
|
68
|
+
@attr_enumeration_mapping_classes ||= {}
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_enumeration_mapping_class(mapping)
|
72
|
+
mapping = mapping.with_indifferent_access
|
73
|
+
Class.new do
|
74
|
+
attr_reader :raw_value
|
75
|
+
alias_method :humanize, :raw_value
|
76
|
+
|
77
|
+
define_method :initialize do |raw_value|
|
78
|
+
@raw_value = raw_value
|
79
|
+
@value = mapping[raw_value]
|
80
|
+
end
|
81
|
+
|
82
|
+
define_method :to_sym do
|
83
|
+
@value
|
84
|
+
end
|
85
|
+
|
86
|
+
define_method :value do
|
87
|
+
@values
|
88
|
+
end
|
89
|
+
|
90
|
+
define_method :to_s do
|
91
|
+
@value.to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
define_method :humanize do
|
95
|
+
@raw_value.to_s.humanize
|
96
|
+
end
|
97
|
+
|
98
|
+
mapping.keys.each do |sym|
|
99
|
+
predicate = "#{sym}?".to_sym
|
100
|
+
value = mapping[sym]
|
101
|
+
define_method predicate do
|
102
|
+
@raw_value.to_sym == sym.to_sym
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
(class <<self;self;end).class_eval do
|
107
|
+
define_method :source do
|
108
|
+
mapping
|
109
|
+
end
|
110
|
+
define_method :from_sym do |sym|
|
111
|
+
unless mapping.has_key?(sym)
|
112
|
+
raise ArgumentError.new(
|
113
|
+
"#{sym.inspect} is not one of {#{mapping.keys.map(&:inspect).sort.join(', ')}}"
|
114
|
+
)
|
115
|
+
end
|
116
|
+
new(sym)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
|
3
3
|
# Install our common ActiveRecord extentions
|
4
|
-
require '
|
5
|
-
ActiveRecord::Base.extend(
|
4
|
+
require 'extended_has_enumeration/class_methods'
|
5
|
+
ActiveRecord::Base.extend(ExtendedHasEnumeration::ClassMethods)
|
6
6
|
|
7
7
|
# We have two primary use cases, versions of ActiveRecord which define the column mapping in Arel,
|
8
8
|
# and those which do not. For ActiveRecord 2.x.x and versions ActiveRecord which rely on
|
@@ -12,14 +12,14 @@ ActiveRecord::Base.extend(HasEnumeration::ClassMethods)
|
|
12
12
|
# whereas older versions of Arel need the code in HasEnumeration::Arel::TableExtensionsArelOne.
|
13
13
|
if ActiveRecord::VERSION::MAJOR >= 3
|
14
14
|
if Arel::VERSION >= "3.0.0"
|
15
|
-
require '
|
15
|
+
require 'extended_has_enumeration/aggregate_conditions_override'
|
16
16
|
elsif Arel::VERSION >= '2.0.0'
|
17
|
-
require '
|
17
|
+
require 'extended_has_enumeration/arel/table_extensions'
|
18
18
|
Arel::Table.send(:include, HasEnumeration::Arel::TableExtensions)
|
19
19
|
else
|
20
|
-
require '
|
20
|
+
require 'extended_has_enumeration/arel/table_extensions_arel_one'
|
21
21
|
Arel::Table.send(:include, HasEnumeration::Arel::TableExtensionsArelOne)
|
22
22
|
end
|
23
23
|
else
|
24
|
-
require '
|
24
|
+
require 'extended_has_enumeration/aggregate_conditions_override'
|
25
25
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe ExtendedHasEnumeration, 'with invalid values' do
|
4
|
+
before(:each) do
|
5
|
+
@model = ExplicitlyMappedModel.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'raises an exception when assigned an invalid value' do
|
9
|
+
lambda do
|
10
|
+
@model.color = :beige
|
11
|
+
end.should raise_error(ArgumentError, ':beige is not one of {"blue", "green", "red"}')
|
12
|
+
end
|
13
|
+
|
14
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
15
|
+
context 'with ActiveRecord 3.x' do
|
16
|
+
it 'raises an exception when finding with an invalid value' do
|
17
|
+
lambda do
|
18
|
+
ExplicitlyMappedModel.where(:color => :beige).all
|
19
|
+
end.should raise_error(ArgumentError, ':beige is not one of {"blue", "green", "red"}')
|
20
|
+
end
|
21
|
+
|
22
|
+
#TODO: handle porting to some other meta_where equivalent that is forward compatible with ActiveRecord 3.1.x+
|
23
|
+
#it 'raises an exception when finding with an invalid value via meta_where' do
|
24
|
+
# lambda do
|
25
|
+
# ExplicitlyMappedModel.where(:color.not_eq => :beige).all
|
26
|
+
# end.should raise_error(ArgumentError, ':beige is not one of {:blue, :green, :red}')
|
27
|
+
#end
|
28
|
+
end
|
29
|
+
else
|
30
|
+
context 'With ActiveRecord 2.x' do
|
31
|
+
it 'raises an exception when finding with an invalid value' do
|
32
|
+
lambda do
|
33
|
+
ExplicitlyMappedModel.find(:all, :conditions => {:color => :beige})
|
34
|
+
end.should raise_error(ArgumentError, ':beige is not one of {"blue", "green", "red"}')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ExtendedHasEnumeration, 'with an uninitialied value' do
|
41
|
+
context 'in a newly-created object' do
|
42
|
+
it 'returns nil for the value of the enumeration' do
|
43
|
+
ExplicitlyMappedModel.new.color.should be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'in an existing object' do
|
48
|
+
it 'returns nil for the value of the enumeration' do
|
49
|
+
object = ExplicitlyMappedModel.find(ExplicitlyMappedModel.create!.id)
|
50
|
+
object.color.should be_nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ExtendedHasEnumeration, 'assignment of nil' do
|
56
|
+
it 'sets the enumeration to nil' do
|
57
|
+
object = ExplicitlyMappedModel.new(:color => :red)
|
58
|
+
object.color = nil
|
59
|
+
object.color.should be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'persists across a trip to the database' do
|
63
|
+
object = ExplicitlyMappedModel.create!(:color => :red)
|
64
|
+
object.color = nil
|
65
|
+
object.save!
|
66
|
+
ExplicitlyMappedModel.find(object.id).color.should be_nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe ExtendedHasEnumeration, 'string formatting' do
|
71
|
+
it 'returns the value as a string if to_s is called on it' do
|
72
|
+
object = ExplicitlyMappedModel.new(:color => :red)
|
73
|
+
object.color.to_s.should == 'Red color'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe ExtendedHasEnumeration, 'hash value' do
|
78
|
+
it 'returns the raw value as a string if raw_value is called on it' do
|
79
|
+
object = ExplicitlyMappedModel.new(:color => :red)
|
80
|
+
object.color.raw_value.should == :red
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'returns the raw value as a string if humanize is called on it' do
|
84
|
+
object = ExplicitlyMappedModel.new(:color => :red)
|
85
|
+
object.color.humanize.should == 'Red'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe ExtendedHasEnumeration, 'source' do
|
90
|
+
it 'returns the passed hash' do
|
91
|
+
ExplicitlyMappedModel::Color.source.should == {'red'=>'Red color', 'green'=>2, 'blue'=>3}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe ExtendedHasEnumeration, 'has hash with with_indifferent_access' do
|
96
|
+
it 'it allows assign string' do
|
97
|
+
object = ExplicitlyMappedModel.create!(:color => 'red')
|
98
|
+
object.color.raw_value.should == 'red'
|
99
|
+
end
|
100
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.require(:default, :test)
|
4
|
+
|
5
|
+
require 'rspec'
|
6
|
+
|
7
|
+
require File.expand_path('../lib/extended_has_enumeration', File.dirname(__FILE__))
|
8
|
+
require File.expand_path('../features/support/explicitly_mapped_model', File.dirname(__FILE__))
|
9
|
+
|
10
|
+
ActiveRecord::Base.establish_connection(
|
11
|
+
:adapter => defined?(JRUBY_VERSION) ? 'jdbcsqlite3': 'sqlite3',
|
12
|
+
:database => File.expand_path('../database', __FILE__)
|
13
|
+
)
|
14
|
+
|
15
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
16
|
+
Bundler.require(:meta_where)
|
17
|
+
end
|
18
|
+
|
19
|
+
class CreateTables < ActiveRecord::Migration
|
20
|
+
create_table :explicitly_mapped_models, :force => true do |t|
|
21
|
+
t.integer :color
|
22
|
+
end
|
23
|
+
end
|
metadata
CHANGED
@@ -1,32 +1,101 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: extended_has_enumeration
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
|
-
-
|
9
|
-
- Maxim Tsaplin
|
8
|
+
- Greg Spurrier
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
date: 2012-04-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.3.10
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.3.10
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: builder
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: ! 'Extends ActiveRecord with the has_enumeration method allowing a symbolic
|
47
|
+
|
48
|
+
enumeration to be stored in an ActiveRecord attribute. The enumeration is
|
49
|
+
|
50
|
+
specified as a mapping between symbols and their underlying representation
|
51
|
+
|
52
|
+
in the database. Predicates are provided for each symbol in the enumeration
|
53
|
+
|
54
|
+
and the symbols may be used in finder methods. When using ActiveRecord 3,
|
55
|
+
|
56
|
+
the symbols may also be used when interacting with the underlying Arel attribute
|
57
|
+
|
58
|
+
for the enumeration. has_enumeration has been tested with Ruby 1.8.7,
|
59
|
+
|
60
|
+
Ruby 1.9.2, JRuby 1.5.5, Rubinius 1.1.0, ActiveRecord 2.3.10, and ActiveRecord
|
61
|
+
|
62
|
+
3.0.3.
|
63
|
+
|
64
|
+
'
|
65
|
+
email: greg@rujubu.com
|
24
66
|
executables: []
|
25
67
|
extensions: []
|
26
|
-
extra_rdoc_files:
|
68
|
+
extra_rdoc_files:
|
69
|
+
- LICENSE.txt
|
70
|
+
- README.md
|
27
71
|
files:
|
28
|
-
-
|
29
|
-
|
72
|
+
- Gemfile
|
73
|
+
- HISTORY.txt
|
74
|
+
- LICENSE.txt
|
75
|
+
- README.md
|
76
|
+
- Rakefile
|
77
|
+
- TODO.md
|
78
|
+
- VERSION
|
79
|
+
- all_tests.sh
|
80
|
+
- features/arel_attributes.feature
|
81
|
+
- features/explicitly_mapped_enumeration.feature
|
82
|
+
- features/implicitly_mapped_enumeration.feature
|
83
|
+
- features/meta_where_queries.feature
|
84
|
+
- features/nonstandard_attribute_enumeration.feature
|
85
|
+
- features/step_definitions/has_enumeration_steps.rb
|
86
|
+
- features/support/env.rb
|
87
|
+
- features/support/explicitly_mapped_model.rb
|
88
|
+
- features/support/implicitly_mapped_model.rb
|
89
|
+
- features/support/nonstandard_attribute_model.rb
|
90
|
+
- install_gemsets.sh
|
91
|
+
- lib/extended_has_enumeration.rb
|
92
|
+
- lib/extended_has_enumeration/aggregate_conditions_override.rb
|
93
|
+
- lib/extended_has_enumeration/arel/table_extensions.rb
|
94
|
+
- lib/extended_has_enumeration/arel/table_extensions_arel_one.rb
|
95
|
+
- lib/extended_has_enumeration/class_methods.rb
|
96
|
+
- spec/extended_has_enumeration/extended_has_enumeration_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
homepage: http://github.com/maxtsap/has_enumeration
|
30
99
|
licenses: []
|
31
100
|
post_install_message:
|
32
101
|
rdoc_options: []
|
@@ -49,5 +118,5 @@ rubyforge_project:
|
|
49
118
|
rubygems_version: 1.8.25
|
50
119
|
signing_key:
|
51
120
|
specification_version: 3
|
52
|
-
summary:
|
121
|
+
summary: Support for symbol-based enumerations in ActiveRecord
|
53
122
|
test_files: []
|