enum_field 0.1.2
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/History.txt +10 -0
- data/Manifest.txt +18 -0
- data/PostInstall.txt +3 -0
- data/README.rdoc +117 -0
- data/Rakefile +28 -0
- data/lib/enum_field/builder.rb +79 -0
- data/lib/enum_field/define_enum.rb +16 -0
- data/lib/enum_field/enumerated_attribute.rb +35 -0
- data/lib/enum_field/exceptions.rb +19 -0
- data/lib/enum_field.rb +17 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/define_enum_spec.rb +209 -0
- data/spec/enumerated_attribute_spec.rb +64 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +10 -0
- data/tasks/rspec.rake +21 -0
- metadata +106 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
lib/enum_field.rb
|
7
|
+
lib/enum_field/builder.rb
|
8
|
+
lib/enum_field/define_enum.rb
|
9
|
+
lib/enum_field/enumerated_attribute.rb
|
10
|
+
lib/enum_field/exceptions.rb
|
11
|
+
script/console
|
12
|
+
script/destroy
|
13
|
+
script/generate
|
14
|
+
spec/define_enum_spec.rb
|
15
|
+
spec/enumerated_attribute_spec.rb
|
16
|
+
spec/spec.opts
|
17
|
+
spec/spec_helper.rb
|
18
|
+
tasks/rspec.rake
|
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
= enum_field
|
2
|
+
|
3
|
+
* http://github.com/paraseba/enum_field
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Enables Active Record attributes to point to enum like objects, by saving in your database
|
8
|
+
only an integer ID.
|
9
|
+
|
10
|
+
|
11
|
+
== FEATURES:
|
12
|
+
|
13
|
+
* Allows creation of Classes with enum like behaviour.
|
14
|
+
* Allows any number of members and methods in the enum classes.
|
15
|
+
* Allows an integer id to be used in your database columns to link to the enum members (user.role_id)
|
16
|
+
* Enables higher abstraction interaction with +AR+ attributes:
|
17
|
+
* <code>user.role = Role.admin</code>
|
18
|
+
* <code>if user.role.can_edit?</code>
|
19
|
+
* Saves in your +AR+ tables, only an integer id pointing to the enumeration member.
|
20
|
+
|
21
|
+
== SYNOPSIS:
|
22
|
+
|
23
|
+
When in an Active Record class, you have an attribute like role, state or country you have
|
24
|
+
several options.
|
25
|
+
|
26
|
+
* You can create a roles, states or countries table, and dump there all possible values.
|
27
|
+
* You can use a string to identify, for instance, the role.
|
28
|
+
* You can use an id to identify the role.
|
29
|
+
|
30
|
+
If you are not confortable with any of this options, maybe +enum_field+ is an answer for you.
|
31
|
+
|
32
|
+
== BASIC USAGE:
|
33
|
+
|
34
|
+
class Role
|
35
|
+
define_enum do |builder|
|
36
|
+
builder.member :admin
|
37
|
+
builder.member :manager
|
38
|
+
builder.member :employee
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class User < ActiveRecord::Base
|
43
|
+
# in the database table there is a role_id integer column
|
44
|
+
enumerated_attribute :role
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
link_to_if(current_user.role == Role.admin, edit_foo_path(@foo))
|
49
|
+
|
50
|
+
user.role = Role.manager
|
51
|
+
user.role_id == Role.manager.id #will be true
|
52
|
+
|
53
|
+
User.first.role.id == User.first.role_id #will be true
|
54
|
+
|
55
|
+
Your enum classes can have all the methods you need:
|
56
|
+
|
57
|
+
class PhoneType
|
58
|
+
def initialize(name)
|
59
|
+
@name = name
|
60
|
+
end
|
61
|
+
attr_reader :name
|
62
|
+
|
63
|
+
define_enum do |b|
|
64
|
+
b.member :home, :object => PhoneType.new('home')
|
65
|
+
b.member :commercial, :object => PhoneType.new('commercial')
|
66
|
+
b.member :mobile, :object => PhoneType.new('mobile')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
user.phone.type.name
|
71
|
+
|
72
|
+
You have some +AR+ like methods in enum classes
|
73
|
+
|
74
|
+
PhoneType.all == [PhoneType.home, PhoneType.commercial, PhoneType.mobile] # ordered all
|
75
|
+
PhoneType.first == PhoneType.home
|
76
|
+
PhoneType.last == PhoneType.mobile
|
77
|
+
|
78
|
+
PhoneType.find_by_id(PhoneType.home.id) == PhoneType.home
|
79
|
+
PhoneType.find_by_id(123456) == nil
|
80
|
+
PhoneType.find(2) == PhoneType.commercial
|
81
|
+
PhoneType.find(123456) # will raise
|
82
|
+
|
83
|
+
== REQUIREMENTS:
|
84
|
+
|
85
|
+
* activerecord
|
86
|
+
* hoe
|
87
|
+
* rubyforge
|
88
|
+
|
89
|
+
|
90
|
+
== INSTALL:
|
91
|
+
|
92
|
+
sudo gem install enum_field
|
93
|
+
|
94
|
+
== LICENSE:
|
95
|
+
|
96
|
+
(The MIT License)
|
97
|
+
|
98
|
+
Copyright (c) 2009 Sebastián Bernardo Galkin
|
99
|
+
|
100
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
101
|
+
a copy of this software and associated documentation files (the
|
102
|
+
'Software'), to deal in the Software without restriction, including
|
103
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
104
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
105
|
+
permit persons to whom the Software is furnished to do so, subject to
|
106
|
+
the following conditions:
|
107
|
+
|
108
|
+
The above copyright notice and this permission notice shall be
|
109
|
+
included in all copies or substantial portions of the Software.
|
110
|
+
|
111
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
112
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
113
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
114
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
115
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
116
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
117
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
2
|
+
%w[rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
3
|
+
require File.dirname(__FILE__) + '/lib/enum_field'
|
4
|
+
|
5
|
+
# Generate all the Rake tasks
|
6
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
7
|
+
$hoe = Hoe.new('enum_field', EnumField::VERSION) do |p|
|
8
|
+
p.developer('Sebastián Bernardo Galkin', 'sgalkin@grantaire.com.ar')
|
9
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
10
|
+
p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
11
|
+
p.rubyforge_name = p.name # TODO this is default value
|
12
|
+
p.extra_deps = [
|
13
|
+
['activerecord'],
|
14
|
+
]
|
15
|
+
p.extra_dev_deps = [
|
16
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
17
|
+
]
|
18
|
+
|
19
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
20
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
21
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
22
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
26
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
27
|
+
|
28
|
+
task :default => [:spec]
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module EnumField
|
2
|
+
class Builder
|
3
|
+
def initialize(target)
|
4
|
+
@target = target
|
5
|
+
@next_id = 0
|
6
|
+
@id2obj = {}
|
7
|
+
@name2obj = {}
|
8
|
+
@sorted = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def member(name, options = {})
|
12
|
+
obj, candidate_id = process_options(options)
|
13
|
+
obj.freeze
|
14
|
+
assign_id(obj, candidate_id)
|
15
|
+
define_in_meta(name) { obj }
|
16
|
+
save(name, obj)
|
17
|
+
end
|
18
|
+
|
19
|
+
def all
|
20
|
+
@sorted.dup
|
21
|
+
end
|
22
|
+
|
23
|
+
def names
|
24
|
+
@name2obj.keys
|
25
|
+
end
|
26
|
+
|
27
|
+
def find(id)
|
28
|
+
find_by_id(id) or raise EnumField::ObjectNotFound
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_by_id(id)
|
32
|
+
@id2obj[id.to_i]
|
33
|
+
end
|
34
|
+
|
35
|
+
def first; @sorted.first; end
|
36
|
+
def last; @sorted.last; end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def define_in_meta(name, &block)
|
41
|
+
metaclass = class << @target; self; end
|
42
|
+
metaclass.send(:define_method, name, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def assign_id(obj, candidate_id)
|
46
|
+
id = new_id(candidate_id)
|
47
|
+
obj.class.send(:attr_reader, :id)
|
48
|
+
obj.instance_variable_set(:@id, id)
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_id(candidate)
|
52
|
+
validate_candidate_id(candidate)
|
53
|
+
candidate || find_next_id
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate_candidate_id(id)
|
57
|
+
raise EnumField::InvalidId.new(id) unless id.nil? || id.is_a?(Integer) && id > 0
|
58
|
+
raise EnumField::RepeatedId.new(id) if @id2obj.has_key?(id)
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_next_id
|
62
|
+
@next_id += 1 while @id2obj.has_key?(@next_id) || @next_id <= 0
|
63
|
+
@next_id
|
64
|
+
end
|
65
|
+
|
66
|
+
def process_options(options)
|
67
|
+
raise EnumField::InvalidOptions unless options.reject {|k,v| k == :object || k == :id}.empty?
|
68
|
+
[options[:object] || @target.new, options[:id]]
|
69
|
+
end
|
70
|
+
|
71
|
+
def save(name, obj)
|
72
|
+
@id2obj[obj.id] = obj
|
73
|
+
@sorted << obj
|
74
|
+
@name2obj[name] = obj
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module EnumField
|
2
|
+
module DefineEnum
|
3
|
+
def define_enum(&block)
|
4
|
+
@enum_builder ||= EnumField::Builder.new(self)
|
5
|
+
yield @enum_builder
|
6
|
+
|
7
|
+
[:all, :names, :find_by_id, :find, :first, :last].each do |method|
|
8
|
+
instance_eval <<-END
|
9
|
+
def #{method}(*args, &block)
|
10
|
+
@enum_builder.send(:#{method}, *args, &block)
|
11
|
+
end
|
12
|
+
END
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module EnumField
|
2
|
+
# Easies the inclusion of enumerations as ActiveRecord columns.
|
3
|
+
# If you have a User AR, with a role_id column, you could do:
|
4
|
+
# <tt>
|
5
|
+
# class User
|
6
|
+
# enumerated_attribute :role
|
7
|
+
# end
|
8
|
+
# </tt>
|
9
|
+
#
|
10
|
+
# This assumes a Role class, and will define role and role= methods in User class.
|
11
|
+
# These added methods expect an object of class Role, and Role should provide a find class method.
|
12
|
+
# You could get this by using Enumerated mixin
|
13
|
+
#
|
14
|
+
module EnumeratedAttribute
|
15
|
+
# Define an enumerated field of the AR class
|
16
|
+
# * +name_attribute+: the name of the field that will be added, for instance +role+
|
17
|
+
# * +options+: Valid options are:
|
18
|
+
# * +id_attribute+: the name of the AR column where the enumerated id will be save. Defaults to
|
19
|
+
# +name_attribute+ with an +_id+ suffix.
|
20
|
+
# * +class+: the class that will be instantiated when +name_attribute+ method is called. Defaults to
|
21
|
+
# +name_attribute+ in camelcase form.
|
22
|
+
def enumerated_attribute(name_attribute, options = {})
|
23
|
+
id_attribute = options[:id_attribute] || (name_attribute.to_s + '_id').to_sym
|
24
|
+
klass = options[:class] || name_attribute.to_s.camelcase.constantize
|
25
|
+
define_method(name_attribute) do
|
26
|
+
(raw = read_attribute(id_attribute)) && klass.find_by_id(raw)
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method(name_attribute.to_s + '=') do |value|
|
30
|
+
write_attribute(id_attribute, value ? value.id : nil)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module EnumField
|
2
|
+
class BadId < StandardError
|
3
|
+
def initialize(repeated_id)
|
4
|
+
@repeated_id = repeated_id
|
5
|
+
end
|
6
|
+
attr_reader :repeated_id
|
7
|
+
end
|
8
|
+
|
9
|
+
class RepeatedId < StandardError; end
|
10
|
+
class InvalidId < StandardError; end
|
11
|
+
|
12
|
+
class InvalidOptions < StandardError
|
13
|
+
end
|
14
|
+
|
15
|
+
class ObjectNotFound < StandardError
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
data/lib/enum_field.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'enum_field/define_enum'
|
6
|
+
require 'enum_field/exceptions'
|
7
|
+
require 'enum_field/builder'
|
8
|
+
require 'enum_field/enumerated_attribute'
|
9
|
+
require 'activerecord'
|
10
|
+
|
11
|
+
Module.send(:include, EnumField::DefineEnum)
|
12
|
+
ActiveRecord::Base.send(:extend, EnumField::EnumeratedAttribute)
|
13
|
+
|
14
|
+
module EnumField
|
15
|
+
VERSION = '0.1.1'
|
16
|
+
|
17
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/enum_field.rb'}"
|
9
|
+
puts "Loading enum_field gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe EnumField::DefineEnum do
|
4
|
+
class Colors
|
5
|
+
define_enum do |builder|
|
6
|
+
builder.member :red
|
7
|
+
builder.member :green
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context "defining enums" do
|
12
|
+
it "should add method :define_enum to class" do
|
13
|
+
class TestClass
|
14
|
+
end
|
15
|
+
|
16
|
+
TestClass.should respond_to(:define_enum)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should yield in define_enum" do
|
20
|
+
class Checkpoint
|
21
|
+
@list = []
|
22
|
+
class << self
|
23
|
+
attr_accessor :list
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Test
|
28
|
+
define_enum {|builder| Checkpoint.list << :here}
|
29
|
+
end
|
30
|
+
|
31
|
+
Checkpoint.list.should == [:here]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should yield a builder in define_enum" do
|
35
|
+
lambda {
|
36
|
+
class Test
|
37
|
+
define_enum do |builder|
|
38
|
+
raise unless builder.respond_to?(:member)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
}.should_not raise_error
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should reject invalid options" do
|
45
|
+
lambda {
|
46
|
+
class Test
|
47
|
+
define_enum do |builder|
|
48
|
+
builder.member :foo, :bar => 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
}.should raise_error(EnumField::InvalidOptions)
|
52
|
+
end
|
53
|
+
|
54
|
+
class NotEnum
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should not define enumerated methods in base Class" do
|
58
|
+
NotEnum.should_not respond_to(:red)
|
59
|
+
Class.should_not respond_to(:red)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should generate instances of class for the enums" do
|
63
|
+
Colors.red.should be_instance_of(Colors)
|
64
|
+
Colors.green.should be_instance_of(Colors)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should allow creation of simple constants" do
|
68
|
+
Colors.red.should_not be_nil
|
69
|
+
Colors.green.should_not be_nil
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should define incremental ids for created constants" do
|
73
|
+
Colors.red.id.should == 1
|
74
|
+
Colors.green.id.should == 2
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should allow custom objects" do
|
78
|
+
class Colors
|
79
|
+
define_enum do |b|
|
80
|
+
b.member :blue, :object => 'blue'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
Colors.red.should be_instance_of(Colors)
|
84
|
+
Colors.blue.should be_instance_of(String)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should allow custom ids" do
|
88
|
+
class Colors
|
89
|
+
define_enum do |b|
|
90
|
+
b.member :yellow, :id => 98765
|
91
|
+
end
|
92
|
+
end
|
93
|
+
Colors.yellow.id.should == 98765
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should not accept repeated ids during deffinition" do
|
97
|
+
lambda {
|
98
|
+
class Colors
|
99
|
+
define_enum do |b|
|
100
|
+
b.member :brown, :id => 2
|
101
|
+
end
|
102
|
+
end
|
103
|
+
}.should raise_error(EnumField::RepeatedId)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should not accept invalid ids" do
|
107
|
+
lambda {
|
108
|
+
class Colors
|
109
|
+
define_enum do |b|
|
110
|
+
b.member :cyan, :id => :hola
|
111
|
+
end
|
112
|
+
end
|
113
|
+
}.should raise_error(EnumField::InvalidId)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should not generate repeated ids" do
|
117
|
+
class Sizes
|
118
|
+
define_enum do |b|
|
119
|
+
b.member :small, :id => 1
|
120
|
+
b.member :medium
|
121
|
+
end
|
122
|
+
end
|
123
|
+
Sizes.small.id.should == 1
|
124
|
+
Sizes.medium.id.should == 2
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should accept both object and id options" do
|
128
|
+
class Sizes
|
129
|
+
define_enum do |b|
|
130
|
+
b.member :large, :id => 10
|
131
|
+
end
|
132
|
+
end
|
133
|
+
Sizes.large.id.should == 10
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "providing interface" do
|
138
|
+
|
139
|
+
class Positions
|
140
|
+
define_enum do |b|
|
141
|
+
b.member :top
|
142
|
+
b.member :right
|
143
|
+
b.member :bottom
|
144
|
+
b.member :left, :id => 100
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should define all method" do
|
149
|
+
Positions.should respond_to(:all)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should return ordered members in all" do
|
153
|
+
Positions.all.should == [Positions.top, Positions.right, Positions.bottom, Positions.left]
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should define names method" do
|
157
|
+
Positions.should respond_to(:names)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should return all names method" do
|
161
|
+
Positions.names.should include(:top, :right, :bottom, :left)
|
162
|
+
Positions.names.size.should == 4
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should provide find_by_id and find working for autogenerated ids" do
|
166
|
+
Positions.find_by_id(1).should == Positions.top
|
167
|
+
Positions.find(1).should == Positions.top
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should provide find_by_id and find working for custom ids" do
|
171
|
+
Positions.find_by_id(100).should == Positions.left
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'should return nil in find_by_id for nonnexistent ids' do
|
175
|
+
Positions.find_by_id(200).should be_nil
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should return throw in find for nonnexistent ids' do
|
179
|
+
lambda { Positions.find(200)}.should raise_error(EnumField::ObjectNotFound)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should return first defined element in .first" do
|
183
|
+
Positions.first.should == Positions.first
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should return last defined element in .first" do
|
187
|
+
Positions.last.should == Positions.left
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "with rich classes" do
|
192
|
+
class PhoneType
|
193
|
+
def initialize(name)
|
194
|
+
@name = name
|
195
|
+
end
|
196
|
+
attr_reader :name
|
197
|
+
|
198
|
+
define_enum do |b|
|
199
|
+
b.member :home, :object => PhoneType.new('home')
|
200
|
+
b.member :commercial, :object => PhoneType.new('commercial')
|
201
|
+
b.member :mobile, :object => PhoneType.new('mobile')
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should allow behaviour to be called" do
|
206
|
+
PhoneType.home.name.should == 'home'
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe EnumField::EnumeratedAttribute do
|
4
|
+
class Role
|
5
|
+
define_enum do |b|
|
6
|
+
b.member :admin
|
7
|
+
b.member :supervisor
|
8
|
+
b.member :manager
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class User < ActiveRecord::Base
|
13
|
+
enumerated_attribute :role
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
before(:each) {
|
18
|
+
User.stub!(:columns).and_return([ActiveRecord::ConnectionAdapters::Column.new(:role_id, nil)])
|
19
|
+
}
|
20
|
+
|
21
|
+
it "should add enumerated_attribute class method to ActiveRecord::Base" do
|
22
|
+
ActiveRecord::Base.should respond_to(:enumerated_attribute)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with AR objects" do
|
26
|
+
|
27
|
+
before(:each) {@user = User.new}
|
28
|
+
|
29
|
+
it "should define reader on the field" do
|
30
|
+
@user.should respond_to(:role)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should define writter on the field" do
|
34
|
+
@user.should respond_to(:'role=')
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should assign field id when field is assigned" do
|
38
|
+
@user.role = Role.admin
|
39
|
+
@user.role_id.should == Role.admin.id
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return field after field_id is assigned" do
|
43
|
+
@user.role_id = Role.admin.id
|
44
|
+
@user.role.should == Role.admin
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should return nil field for nil field id" do
|
48
|
+
@user.role.should be_nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should return nil field id for nilled field" do
|
52
|
+
@user.role_id = Role.admin.id
|
53
|
+
@user.role = nil
|
54
|
+
@user.role_id.should be_nil
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should return nil field for nilled field id" do
|
58
|
+
@user.role = Role.admin
|
59
|
+
@user.role_id = nil
|
60
|
+
@user.role.should be_nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: enum_field
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Sebasti\xC3\xA1n Bernardo Galkin"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-23 00:00:00 -03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activerecord
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: newgem
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.4.1
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.8.0
|
44
|
+
version:
|
45
|
+
description: Enables Active Record attributes to point to enum like objects, by saving in your database only an integer ID.
|
46
|
+
email:
|
47
|
+
- sgalkin@grantaire.com.ar
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- PostInstall.txt
|
56
|
+
- README.rdoc
|
57
|
+
files:
|
58
|
+
- History.txt
|
59
|
+
- Manifest.txt
|
60
|
+
- PostInstall.txt
|
61
|
+
- README.rdoc
|
62
|
+
- Rakefile
|
63
|
+
- lib/enum_field.rb
|
64
|
+
- lib/enum_field/builder.rb
|
65
|
+
- lib/enum_field/define_enum.rb
|
66
|
+
- lib/enum_field/enumerated_attribute.rb
|
67
|
+
- lib/enum_field/exceptions.rb
|
68
|
+
- script/console
|
69
|
+
- script/destroy
|
70
|
+
- script/generate
|
71
|
+
- spec/define_enum_spec.rb
|
72
|
+
- spec/enumerated_attribute_spec.rb
|
73
|
+
- spec/spec.opts
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
- tasks/rspec.rake
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: http://github.com/paraseba/enum_field
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message: PostInstall.txt
|
81
|
+
rdoc_options:
|
82
|
+
- --main
|
83
|
+
- README.rdoc
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
91
|
+
version:
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: "0"
|
97
|
+
version:
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project: enum_field
|
101
|
+
rubygems_version: 1.3.5
|
102
|
+
signing_key:
|
103
|
+
specification_version: 2
|
104
|
+
summary: Enables Active Record attributes to point to enum like objects, by saving in your database only an integer ID.
|
105
|
+
test_files: []
|
106
|
+
|