ar-enums 0.3.0

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