has_enumeration 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+