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 ADDED
@@ -0,0 +1,5 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'activerecord', '>= 3.0.0.rc'
4
+ gem 'rspec', '>= 2.0.0.beta.19'
5
+ gem 'cucumber', '>= 0.8.5'
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,3 @@
1
+ class ExplicitlyMappedModel < ActiveRecord::Base
2
+ has_enumeration :color, :red => 1, :green => 2, :blue => 3
3
+ end
@@ -0,0 +1,3 @@
1
+ class ImplicitlyMappedModel < ActiveRecord::Base
2
+ has_enumeration :color, [:red, :green, :blue]
3
+ 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
@@ -0,0 +1,7 @@
1
+ require 'active_record'
2
+
3
+ require 'has_enumeration/class_methods'
4
+ require 'has_enumeration/aggregate_conditions_override'
5
+
6
+ # Install our class methods.
7
+ ActiveRecord::Base.extend(HasEnumeration::ClassMethods)
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
+