ar-enums 0.3.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/.autotest ADDED
@@ -0,0 +1,2 @@
1
+ require 'autotest/growl'
2
+ require 'autotest/fsevent'
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Emma Nicolau
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,122 @@
1
+ = ar-enums
2
+
3
+ This is a simple solution for defining enumerations in your Rails models.
4
+
5
+ == Description
6
+
7
+ It provides two forms of enumerations:
8
+ * Internal, i.e. defined within the owner of the enum, for the simple cases.
9
+ * External, i.e. defined on it's own class, for the complex ones.
10
+
11
+ In both cases the enum values are stored in-memory. Let's see some examples of both kinds.
12
+
13
+ == Internal enumerations
14
+
15
+ Can be defined in three flavours:
16
+
17
+ === Array of values style
18
+
19
+ This is the simplest and probably the most frecuent case:
20
+
21
+ class TrafficLight < ActiveRecord::Base
22
+ enum :state, %w[red green yellow]
23
+ end
24
+
25
+ tl = TrafficLight.new(:state => :green)
26
+ tl.state # => #<TrafficLight::State @name="green", @id=2>
27
+ tl.state_id # => 2
28
+ tl.state.green? # => true
29
+
30
+ As you can see each enum value is an instance of a dinamically generated class (which inherits from ActiveRecord::Enum) and has an ordinal value, id, autogenerated staring on 1.
31
+
32
+ The enumerations array is accessed with a class method:
33
+
34
+ TrafficLight.states # => [#<TrafficLight::State @name="red", @id=1>, #<TrafficLight::State @name="green", @id=2>, ...]
35
+
36
+ By default when <tt>#to_s</tt> is called on a Enum instance it returns the name titleized, but this behaviour can be overrided. See Displaying Enums section below.
37
+
38
+ === Array of hashes style
39
+
40
+ This allows you to define new columns in the Enum value. If the :id is not specified is generated for you.
41
+
42
+ class TrafficLight < ActiveRecord::Base
43
+ enum :state, [
44
+ { :name => :red, :stop_traffic => true, :rgb => 0xF00 },
45
+ { :name => :green, :stop_traffic => false, :rgb => 0x0F0 }
46
+ ]
47
+ end
48
+
49
+ tl = TrafficLight.new(:state => :green)
50
+ tl.state_id # => 2
51
+ tl.state.stop_traffic # => false
52
+ tl.state.rgb # => 0x0F0
53
+
54
+ === Block style
55
+
56
+ class TrafficLight < ActiveRecord::Base
57
+ enum :state do
58
+ red :stop_traffic => true, :rgb => 0xF00
59
+ green :stop_traffic => false, :rgb => 0x0F0
60
+ end
61
+ end
62
+
63
+ == External enumerations
64
+
65
+ When you have shared enumerations or just don't want to clutter the owner model with enums definitions,
66
+ the enumeration can be on it's own class, as the State enum in the following example:
67
+
68
+ class TrafficLight < ActiveRecord::Base
69
+ enum :state
70
+ end
71
+
72
+ class State < ActiveRecord::Enum
73
+ enumeration do
74
+ red :stop_traffic => true, :rgb => 0xF00, :desc => 'Rojo'
75
+ green :stop_traffic => false, :rgb => 0x0F0, :desc => 'Verde'
76
+ end
77
+ end
78
+
79
+ State[:red] # => #<State @name="red", @id=1, ...>
80
+ State[:red].to_s # => "Rojo"
81
+
82
+ Note the block style sintax is exactly the same as in the internal enumerations. In fact any of the 3 styles mencioned above can be used on external enumerations.
83
+
84
+ Also note the <tt>#to_s</tt> by default return the value of the <tt>:desc</tt> column. Of course you can override the <tt>#to_s</tt> method as usual.
85
+
86
+ Then you can access the enumerations collection from both places:
87
+
88
+ TrafficLight.states # => [#<State @name="red", @id=1, ...>, #<State @name="green", @id=2, ...>]
89
+ States.all # => [#<State @name="red", @id=1, ...>, #<State @name="green", @id=2, ...>]
90
+
91
+ The <tt>enum</tt> method accept a <tt>:class_method</tt> option similar to the one in <tt>belongs_to</tt> and others to specify the class of the enum.
92
+
93
+ == Displaying Enums
94
+
95
+ In the array of values style the default is the name titleized but you can pass a <tt>:label</tt> options to override:
96
+
97
+ class TrafficLight < ActiveRecord::Base
98
+ enum :state, %w[red green yellow], :label => :upcase
99
+ end
100
+
101
+ TrafficLight.states.map(&:to_s) # => ["RED", "GREEN", "YELLOW"]
102
+
103
+ If you use the block o array of hashes sintax and add a <tt>:desc</tt> column it will be used as <tt>#to_s</tt>, unless a <tt>:label</tt> option is passed.
104
+
105
+ == TODO
106
+
107
+ * Remove the dependency with ActiveRecord so it can be used outside Rails.
108
+ * Allow to store enums in the database.
109
+
110
+ == Note on Patches/Pull Requests
111
+
112
+ * Fork the project.
113
+ * Make your feature addition or bug fix.
114
+ * Add tests for it. This is important so I don't break it in a
115
+ future version unintentionally.
116
+ * Commit, do not mess with rakefile, version, or history.
117
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
118
+ * Send me a pull request. Bonus points for topic branches.
119
+
120
+ == Copyright
121
+
122
+ Copyright (c) 2010 Emmanuel Nicolau. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ar-enums"
8
+ gem.summary = %Q{Enumerations for ActiveRecord models}
9
+ gem.description = %Q{Provides a simple way for defining enumerations in ActiveRecord models}
10
+ gem.email = "emmanicolau@gmail.com"
11
+ gem.homepage = "http://github.com/eeng/ar-enums"
12
+ gem.authors = ["Emmanuel Nicolau"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "ar-enums #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
data/ar-enums.gemspec ADDED
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ar-enums}
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Emmanuel Nicolau"]
12
+ s.date = %q{2010-02-11}
13
+ s.description = %q{Provides a simple way for defining enumerations in ActiveRecord models}
14
+ s.email = %q{emmanicolau@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".autotest",
21
+ ".document",
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "ar-enums.gemspec",
28
+ "examples/internal_enums.rb",
29
+ "lib/ar-enums.rb",
30
+ "lib/enum.rb",
31
+ "lib/enum_block.rb",
32
+ "lib/enum_definition.rb",
33
+ "lib/enum_field.rb",
34
+ "lib/factory.rb",
35
+ "lib/metaprogramming_extensions.rb",
36
+ "lib/options_helper.rb",
37
+ "spec/enum_definition_spec.rb",
38
+ "spec/enum_spec.rb",
39
+ "spec/factory_spec.rb",
40
+ "spec/schema.rb",
41
+ "spec/spec.opts",
42
+ "spec/spec_helper.rb"
43
+ ]
44
+ s.homepage = %q{http://github.com/eeng/ar-enums}
45
+ s.rdoc_options = ["--charset=UTF-8"]
46
+ s.require_paths = ["lib"]
47
+ s.rubygems_version = %q{1.3.5}
48
+ s.summary = %q{Enumerations for ActiveRecord models}
49
+ s.test_files = [
50
+ "spec/enum_definition_spec.rb",
51
+ "spec/enum_spec.rb",
52
+ "spec/factory_spec.rb",
53
+ "spec/schema.rb",
54
+ "spec/spec_helper.rb",
55
+ "examples/internal_enums.rb"
56
+ ]
57
+
58
+ if s.respond_to? :specification_version then
59
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
60
+ s.specification_version = 3
61
+
62
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
63
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
64
+ else
65
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
66
+ end
67
+ else
68
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
69
+ end
70
+ end
71
+
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'ar-enums'
4
+
5
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
6
+ load(File.dirname(__FILE__) + "/../spec/schema.rb")
7
+
8
+ class TrafficLight < ActiveRecord::Base
9
+ enum :state, %w[red green yellow]
10
+ end
11
+
12
+ tl = TrafficLight.new(:state => :green)
13
+ p tl.state # => #<TrafficLight::State @name="green", @id=2>
14
+ p tl.state_id # => 2
15
+ p TrafficLight.states.map(&:to_s)
16
+
17
+ # class TrafficLight < ActiveRecord::Base
18
+ # enum :state, [
19
+ # { :name => :red, :stop_traffic => true, :rgb => 0xF00 },
20
+ # { :name => :green, :stop_traffic => false, :rgb => 0x0F0 }
21
+ # ]
22
+ # end
23
+ #
24
+ # tl = TrafficLight.new(:state => :green)
25
+ # p tl.state_id # => 2
26
+ # p tl.state.stop_traffic # => false
27
+ # p tl.state.rgb # => 0x0F0
data/lib/ar-enums.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'active_record'
2
+ %w[metaprogramming_extensions options_helper enum enum_block enum_field factory enum_definition].each do |f|
3
+ require f
4
+ end
data/lib/enum.rb ADDED
@@ -0,0 +1,57 @@
1
+ module ActiveRecord
2
+ class Enum
3
+ extend ActiveRecord::Enumerations::OptionsHelper
4
+
5
+ attr_reader :id, :name, :extra_columns
6
+ class_inheritable_accessor :label_method
7
+
8
+ def initialize attrs = {}
9
+ @id = attrs.delete(:id).to_i
10
+ @name = attrs.delete(:name).to_s
11
+ @extra_columns = attrs.reject { |k, _| [:enum_class, :on_style_not_matched].include?(k) }
12
+ end
13
+
14
+ def self.create_from value, values, options
15
+ required_attrs = case value
16
+ when String, Symbol
17
+ { :name => value }
18
+ else
19
+ value
20
+ end
21
+ required_attrs[:id] ||= values.index(value) + 1
22
+ new options.merge(required_attrs)
23
+ end
24
+
25
+ def == other
26
+ return id == other.id if other.is_a?(Enum)
27
+ [id.to_s, name].include?(other.to_s)
28
+ end
29
+
30
+ def to_s
31
+ try_labelize(self, :desc) || try_labelize(name, :titleize)
32
+ end
33
+
34
+ def to_sym
35
+ name.to_sym
36
+ end
37
+
38
+ def self.enumeration *config, &block
39
+ add_option config, :enum_class => self
40
+ define_enums_getter ActiveRecord::Enumerations::Factory.make_enums(*config, &block)
41
+ end
42
+
43
+ def self.[] name_or_id
44
+ all.detect { |enum| enum == name_or_id }
45
+ end
46
+
47
+ private
48
+ def self.define_enums_getter enums
49
+ cattr_accessor :all
50
+ self.all = enums
51
+ end
52
+
53
+ def try_labelize object, default_method
54
+ object.respond_to?(label_method) && object.send(label_method) or object.respond_to?(default_method) && object.send(default_method)
55
+ end
56
+ end
57
+ end
data/lib/enum_block.rb ADDED
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ module Enumerations
3
+ class EnumBlock
4
+ def initialize options = {}
5
+ @enums = []
6
+ @last_id = 0
7
+ @options = options
8
+ end
9
+
10
+ def method_missing method, args = {}
11
+ attrs = @options.merge(args).merge(:name => method)
12
+ attrs[:id] ||= @last_id += 1
13
+ @enums << @options[:enum_class].new(attrs)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ module ActiveRecord
2
+ module Enumerations
3
+ def self.included(base)
4
+ base.send :extend, ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ include ActiveRecord::Enumerations::OptionsHelper
9
+
10
+ def enum field_name, *config, &block
11
+ field = EnumField.new field_name
12
+ enum_class = Class.new Enum
13
+ const_set field.name.camelize, enum_class
14
+ add_options config, :enum_class => enum_class, :on_style_not_matched => asume_external_style(field)
15
+ enums = Factory.new.make_enums *config, &block
16
+ define_enums_getter field, enums
17
+ define_enum_getter_and_setter field, enums
18
+ end
19
+
20
+ private
21
+ def asume_external_style field
22
+ lambda { |options| field.external_class(options).all }
23
+ end
24
+
25
+ def define_enums_getter field, enums
26
+ define_class_method(field.enums_getter) { enums }
27
+ end
28
+
29
+ def define_enum_getter_and_setter field, enums
30
+ define_method field.name do
31
+ enums.detect { |enum| enum.id == read_attribute(field.foreign_key) }
32
+ end
33
+
34
+ define_method "#{field.name}=" do |value|
35
+ write_attribute field.foreign_key, enums.detect { |enum| enum == value }.try(:id)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ ActiveRecord::Base.send :include, ActiveRecord::Enumerations
data/lib/enum_field.rb ADDED
@@ -0,0 +1,27 @@
1
+ module ActiveRecord
2
+ module Enumerations
3
+ class EnumField
4
+ attr_reader :name
5
+
6
+ def initialize name
7
+ @name = name.to_s
8
+ end
9
+
10
+ def enums_getter
11
+ name.pluralize
12
+ end
13
+
14
+ def enums_setter
15
+ "#{enums_getter}="
16
+ end
17
+
18
+ def foreign_key
19
+ "#{name}_id"
20
+ end
21
+
22
+ def external_class options = {}
23
+ (options.delete(:class_name) || name).camelize.constantize
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/factory.rb ADDED
@@ -0,0 +1,60 @@
1
+ module ActiveRecord
2
+ module Enumerations
3
+ class Factory
4
+ include OptionsHelper
5
+
6
+ def self.make_enums *config, &block
7
+ new.make_enums *config, &block
8
+ end
9
+
10
+ def make_enums *config, &block
11
+ values, options = extract_values_and_options config
12
+ options[:enum_class].label_method = options.delete(:label) || :desc
13
+ create_enums(values, options, &block).tap do |enums|
14
+ define_question_methods options[:enum_class], enums
15
+ define_extra_columns_methods options[:enum_class], enums
16
+ end
17
+ end
18
+
19
+ private
20
+ def create_enums values, options, &block
21
+ enums = if block_given?
22
+ block_style options, &block
23
+ elsif values.any?
24
+ array_of_values_or_hashes_style values, options
25
+ elsif options[:on_style_not_matched]
26
+ options[:on_style_not_matched].call options
27
+ end
28
+ end
29
+
30
+ def block_style options, &block
31
+ EnumBlock.new(options).instance_eval(&block)
32
+ end
33
+
34
+ def array_of_values_or_hashes_style values, options
35
+ values.map { |value| options[:enum_class].create_from(value, values, options) }
36
+ end
37
+
38
+ def define_question_methods enum_class, enums
39
+ enums.each do |e|
40
+ enum_class.class_eval %Q{
41
+ def #{e.name}?
42
+ self == :#{e.name}
43
+ end
44
+ }
45
+ end
46
+ end
47
+
48
+ def define_extra_columns_methods enum_class, enums
49
+ extra_columns_names = enums.map(&:extra_columns).map(&:keys).flatten.uniq
50
+ extra_columns_names.each do |ecn|
51
+ enum_class.class_eval %Q{
52
+ def #{ecn}
53
+ extra_columns[:#{ecn}]
54
+ end
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,22 @@
1
+ class Object
2
+ # The hidden singleton lurks behind everyone
3
+ def metaclass; class << self; self; end; end
4
+ def meta_eval &blk; metaclass.instance_eval &blk; end
5
+
6
+ # Adds methods to a metaclass
7
+ def meta_def name, &blk
8
+ meta_eval { define_method name, &blk }
9
+ end
10
+
11
+ # Defines an instance method within a class
12
+ def class_def name, &blk
13
+ class_eval { define_method name, &blk }
14
+ end
15
+
16
+ # Defines a class method
17
+ def define_class_method name, &blk
18
+ self.class.instance_eval do
19
+ define_method name, &blk
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveRecord
2
+ module Enumerations
3
+ module OptionsHelper
4
+ def add_option config, option
5
+ new_config = if config.first.is_a?(Array)
6
+ [config[0], (config[1] || {}).merge(option)]
7
+ else
8
+ [(config[0] || {}).merge(option)]
9
+ end
10
+ config.replace new_config
11
+ end
12
+ alias_method :add_options, :add_option
13
+
14
+ def extract_values_and_options config
15
+ if config.first.is_a?(Array)
16
+ [config[0], config[1] || {}]
17
+ else
18
+ [[], config[0] || {}]
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,132 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Internal enumerations" do
4
+ def define_traffic_light *options
5
+ define_model_class 'TrafficLight' do
6
+ enum *options
7
+ end
8
+ end
9
+
10
+ before do
11
+ define_traffic_light :state, %w[red green yellow]
12
+ end
13
+
14
+ context "getter and setter" do
15
+ it "getter should return an Enum" do
16
+ TrafficLight.new(:state_id => 3).state.should be_enum_with(:id => 3, :name => 'yellow')
17
+ end
18
+
19
+ it "should store ordinal by default as foreign key" do
20
+ TrafficLight.new(:state => :green).state_id.should == 2
21
+ end
22
+
23
+ it "should store nil if enum doesn't exists" do
24
+ TrafficLight.new(:state => :black).state_id.should be_nil
25
+ end
26
+
27
+ it "should allow to set enum with symbol" do
28
+ TrafficLight.new(:state => :red).state.should == :red
29
+ TrafficLight.new(:state => :green).state.should == :green
30
+ end
31
+
32
+ it "should allow to set enum with string" do
33
+ TrafficLight.new(:state => 'red').state.should == :red
34
+ TrafficLight.new(:state => 'green').state.should == :green
35
+ end
36
+
37
+ it "should allow to set enum with ordinal" do
38
+ TrafficLight.new(:state_id => 1).state.should == :red
39
+ TrafficLight.new(:state_id => 2).state.should == :green
40
+ end
41
+ end
42
+
43
+ context "options" do
44
+ it "should pass the options to the factory" do
45
+ define_traffic_light :state, %w[green red], :label => :upcase
46
+ TrafficLight.new(:state => :green).state.to_s.should == 'GREEN'
47
+ TrafficLight.new(:state => :red).state.to_s.should == 'RED'
48
+ end
49
+ end
50
+
51
+ context "question methods" do
52
+ before do
53
+ define_traffic_light :state, %w[green red]
54
+ end
55
+
56
+ it "should provide question method" do
57
+ TrafficLight.new(:state => :green).state.should be_green
58
+ TrafficLight.new(:state => :green).state.should_not be_red
59
+ TrafficLight.new(:state => :red).state.should_not be_green
60
+ TrafficLight.new(:state => :red).state.should be_red
61
+ end
62
+
63
+ it "should raise error if tested with inexistant enum" do
64
+ lambda { TrafficLight.new(:state => :green).state.blue? }.should raise_error(NameError)
65
+ end
66
+
67
+ it "block style should also provide question method" do
68
+ define_model_class 'TrafficLight' do
69
+ enum :state do
70
+ green
71
+ red
72
+ end
73
+ end
74
+ TrafficLight.new(:state => :green).state.should be_green
75
+ TrafficLight.new(:state => :green).state.should_not be_red
76
+ end
77
+ end
78
+
79
+ it "should be instances of a new subclass of Enum" do
80
+ TrafficLight.states.first.should be_a(TrafficLight::State)
81
+ end
82
+ end
83
+
84
+ describe "External enumerations" do
85
+ before do
86
+ define_model_class 'State', 'ActiveRecord::Enum' do
87
+ enumeration do
88
+ green :rgb => 0x0F0
89
+ red :rgb => 0xF00
90
+ end
91
+ end
92
+
93
+ define_model_class 'TrafficLight' do
94
+ enum :state
95
+ end
96
+ end
97
+
98
+ context "enums creation" do
99
+ it "should allow to define enumerations on it's own class" do
100
+ TrafficLight.new(:state => :red).state.should be_enum_with(:name => 'red', :rgb => 0xF00, :id => 2)
101
+ end
102
+
103
+ it "should be posible to access all enums from withing the owner" do
104
+ TrafficLight.states.should equal(State.all)
105
+ end
106
+
107
+ it "should accept :class_name options to override de class of the external enum" do
108
+ define_model_class 'TrafficLight' do
109
+ enum :state_on_weekdays, :class_name => 'State'
110
+ enum :state_on_weekends, :class_name => 'State'
111
+ end
112
+ TrafficLight.state_on_weekdays.should equal(State.all)
113
+ TrafficLight.state_on_weekends.should equal(State.all)
114
+ end
115
+
116
+ it "external enums should be instances of the subclass of Enum" do
117
+ State.all.each { |s| s.should be_a(State) }
118
+ end
119
+
120
+ it "should be posible to define new methods in Enum subclass" do
121
+ define_model_class 'State', 'ActiveRecord::Enum' do
122
+ enumeration do
123
+ green :factor => 1
124
+ red :factor => 2
125
+ end
126
+
127
+ def double_factor() factor * 2 end
128
+ end
129
+ State.all.map(&:double_factor).should == [2, 4]
130
+ end
131
+ end
132
+ end
data/spec/enum_spec.rb ADDED
@@ -0,0 +1,42 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Enum" do
4
+ it "should provide :to_sym method returning name as symbols" do
5
+ ActiveRecord::Enum.new(:name => :green).to_sym.should == :green
6
+ end
7
+
8
+ it "should store extra columns as a hash without the :enum_class that is passed from other classes" do
9
+ ActiveRecord::Enum.new(:name => :green, :factor => 1.5, :enum_class => Class.new).extra_columns.should == { :factor => 1.5 }
10
+ end
11
+
12
+ context "External enums" do
13
+ before do
14
+ define_model_class 'Color', 'ActiveRecord::Enum' do
15
+ enumeration do
16
+ red :rgb => 0xF00
17
+ green :rgb => 0x0F0
18
+ end
19
+ end
20
+
21
+ define_model_class 'State', 'ActiveRecord::Enum' do
22
+ enumeration do
23
+ on :id => 80
24
+ off :id => 90
25
+ end
26
+ end
27
+ end
28
+
29
+ it "should provide :all method to access the enums" do
30
+ Color.all[0].should be_enum_with(:name => 'red', :rgb => 0xF00)
31
+ Color.all[1].should be_enum_with(:name => 'green', :rgb => 0x0F0)
32
+ State.all[0].should be_enum_with(:name => 'on', :id => 80)
33
+ State.all[1].should be_enum_with(:name => 'off', :id => 90)
34
+ end
35
+
36
+ it "should provide [] method to access the enums" do
37
+ Color[:red].should be_enum_with(:name => 'red')
38
+ Color['green'].should be_enum_with(:name => 'green')
39
+ Color[2].should be_enum_with(:name => 'green')
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,100 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Enums creation styles" do
4
+ include ActiveRecord::Enumerations::OptionsHelper
5
+
6
+ def make_enums *config, &block
7
+ add_option config, :enum_class => ActiveRecord::Enum
8
+ ActiveRecord::Enumerations::Factory.make_enums *config, &block
9
+ end
10
+
11
+ context "array of values style" do
12
+ it "should generate ids" do
13
+ enums = make_enums %w[red green blue]
14
+ enums[0].should be_enum_with(:id => 1, :name => 'red')
15
+ enums[1].should be_enum_with(:id => 2, :name => 'green')
16
+ enums[2].should be_enum_with(:id => 3, :name => 'blue')
17
+ end
18
+
19
+ it "default to_s should be name titleized" do
20
+ make_enums(%w[green red]).map(&:to_s).should == %w[Green Red]
21
+ end
22
+
23
+ it "should override default to_s" do
24
+ make_enums(%w[green red], :label => :upcase).map(&:to_s).should == %w[GREEN RED]
25
+ end
26
+ end
27
+
28
+ context "array of hashes style" do
29
+ it "should accept ids if provided" do
30
+ enums = make_enums [{ :id => 20, :name => :red }, { :id => 10, :name => :green }]
31
+ enums[0].should be_enum_with(:id => 20, :name => 'red')
32
+ enums[1].should be_enum_with(:id => 10, :name => 'green')
33
+ end
34
+
35
+ it "should generate ids if not provided" do
36
+ enums = make_enums [{ :name => :red }, { :name => :green }]
37
+ enums[0].should be_enum_with(:id => 1, :name => 'red')
38
+ enums[1].should be_enum_with(:id => 2, :name => 'green')
39
+ end
40
+
41
+ it "default to_s should be :desc column" do
42
+ enums = make_enums [{ :name => :red, :desc => 'Rojo' }, { :name => :green, :desc => 'Verde' }]
43
+ enums.map(&:to_s).should == %w[Rojo Verde]
44
+ end
45
+
46
+ it ":label options can be a method to call on name" do
47
+ enums = make_enums [{ :name => :red }, { :name => :green }], :label => :upcase
48
+ enums.map(&:to_s).should == %w[RED GREEN]
49
+ end
50
+
51
+ it ":label option can be a enum column" do
52
+ enums = make_enums [{ :name => :red, :title => 'Rojo' }, { :name => :green, :title => 'Verde' }], :label => :title
53
+ enums.map(&:to_s).should == %w[Rojo Verde]
54
+ end
55
+
56
+ it "should accept extra columns" do
57
+ enums = make_enums [
58
+ { :name => :red, :factor => 1.5, :stop_traffic => true },
59
+ { :name => :green, :factor => 2.5, :stop_traffic => false }
60
+ ]
61
+ enums.map(&:factor).should == [1.5, 2.5]
62
+ enums.map(&:stop_traffic).should == [true, false]
63
+ end
64
+ end
65
+
66
+ context "block style" do
67
+ it "can be created with a block" do
68
+ enums = make_enums do
69
+ red :rgb => 0xF00
70
+ green :rgb => 0x0F0
71
+ end
72
+ enums[0].should be_enum_with(:id => 1, :name => 'red', :rgb => 0xF00)
73
+ enums[1].should be_enum_with(:id => 2, :name => 'green', :rgb => 0x0F0)
74
+ end
75
+
76
+ it "should accept :label option" do
77
+ enums = make_enums :label => :title do
78
+ red :title => 'Rojo'
79
+ green :title => 'Verde'
80
+ end
81
+ enums.map(&:to_s).should == %w[Rojo Verde]
82
+ end
83
+
84
+ it "should accept extra columns" do
85
+ enums = make_enums do
86
+ red :factor => 1.5
87
+ green :factor => 2.5
88
+ end
89
+ enums.map(&:factor).should == [1.5, 2.5]
90
+ end
91
+
92
+ it "when extra column is empty should return nil" do
93
+ enums = make_enums do
94
+ red :factor => 1.5
95
+ green
96
+ end
97
+ enums.map(&:factor).should == [1.5, nil]
98
+ end
99
+ end
100
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,5 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table "traffic_lights", :force => true do |t|
3
+ t.integer "state_id"
4
+ end
5
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --debugger
@@ -0,0 +1,29 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'rubygems'
4
+ require 'ar-enums'
5
+ require 'spec'
6
+ require 'spec/autorun'
7
+
8
+ Spec::Runner.configure do |config|
9
+ config.before :suite do
10
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
11
+ load(File.dirname(__FILE__) + "/schema.rb")
12
+ end
13
+
14
+ def define_model_class(name = "TestClass", parent_class_name = "ActiveRecord::Base", &block)
15
+ Object.send(:remove_const, name) rescue nil
16
+ eval("class #{name} < #{parent_class_name}; end", TOPLEVEL_BINDING)
17
+ klass = eval(name, TOPLEVEL_BINDING)
18
+ klass.class_eval(&block) if block_given?
19
+ end
20
+ end
21
+
22
+ Spec::Matchers.define :be_enum_with do |expected_attrs|
23
+ match do |enum|
24
+ enum.should be_a(ActiveRecord::Enum)
25
+ expected_attrs.each do |atrib, expected_value|
26
+ enum.send(atrib).should == expected_value
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ar-enums
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Emmanuel Nicolau
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-11 00:00:00 -02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ description: Provides a simple way for defining enumerations in ActiveRecord models
26
+ email: emmanicolau@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .autotest
36
+ - .document
37
+ - .gitignore
38
+ - LICENSE
39
+ - README.rdoc
40
+ - Rakefile
41
+ - VERSION
42
+ - ar-enums.gemspec
43
+ - examples/internal_enums.rb
44
+ - lib/ar-enums.rb
45
+ - lib/enum.rb
46
+ - lib/enum_block.rb
47
+ - lib/enum_definition.rb
48
+ - lib/enum_field.rb
49
+ - lib/factory.rb
50
+ - lib/metaprogramming_extensions.rb
51
+ - lib/options_helper.rb
52
+ - spec/enum_definition_spec.rb
53
+ - spec/enum_spec.rb
54
+ - spec/factory_spec.rb
55
+ - spec/schema.rb
56
+ - spec/spec.opts
57
+ - spec/spec_helper.rb
58
+ has_rdoc: true
59
+ homepage: http://github.com/eeng/ar-enums
60
+ licenses: []
61
+
62
+ post_install_message:
63
+ rdoc_options:
64
+ - --charset=UTF-8
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.5
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Enumerations for ActiveRecord models
86
+ test_files:
87
+ - spec/enum_definition_spec.rb
88
+ - spec/enum_spec.rb
89
+ - spec/factory_spec.rb
90
+ - spec/schema.rb
91
+ - spec/spec_helper.rb
92
+ - examples/internal_enums.rb