mcommons-enum_for 0.0.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/LICENSE +20 -0
- data/README.rdoc +63 -0
- data/Rakefile +21 -0
- data/enum_for.gemspec +38 -0
- data/lib/enum_for.rb +55 -0
- data/spec/enum_for_spec.rb +120 -0
- data/spec/spec_helper.rb +9 -0
- metadata +85 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Mal McKay and Justin S. Leitgeb
|
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,63 @@
|
|
1
|
+
= Enum For
|
2
|
+
|
3
|
+
enum_for makes your ActiveRecord-backed class work with an enumerable type. Instead of existing
|
4
|
+
ActiveRecord plugins such as enum_fu, which store the enumerable attribute as an integer, enum_for
|
5
|
+
stores the content of the enumerable attribute in a varchar column of the database.
|
6
|
+
|
7
|
+
It also defines certain useful methods on your model. For example, if you declare :state to be
|
8
|
+
an enumerable column, enum_fu allow you to see the valid states by calling the method "states"
|
9
|
+
(note the automatically pluralized form) on the class.
|
10
|
+
|
11
|
+
enum_fu defines constants corresponding to each of the valid enumerable types on your model. For example,
|
12
|
+
if you have an enumerable column called "states" with valid states :starting and :finished, enum_fu would
|
13
|
+
define constants on your model Model::STATE_STARTING and Model::STATE_FINISHED with the contents of these
|
14
|
+
constants :starting and :finished respectively.
|
15
|
+
|
16
|
+
enum_fu also creates predicate methods on instances of your class so that you can ask if they have any
|
17
|
+
of the given enumerable states. For example, if a Taco class has an enumerable type :taste with values
|
18
|
+
:good and :bad, taco.good? would return a boolean value indicating whether or not the taste is good.
|
19
|
+
|
20
|
+
Finally, enum_fu creates named scopes on your model, so that you can easily find all records belonging to
|
21
|
+
a given enumerable type. Following the above example, Taco.taste_good would return all tacos that taste
|
22
|
+
good.
|
23
|
+
|
24
|
+
== Installation
|
25
|
+
|
26
|
+
enum_for is available as a gem available on GitHub. Install with:
|
27
|
+
|
28
|
+
sudo gem install mcommons-enum_for
|
29
|
+
|
30
|
+
You'll probable want a line like the following in your environment.rb:
|
31
|
+
|
32
|
+
config.gem "enum_for"
|
33
|
+
|
34
|
+
== Examples
|
35
|
+
|
36
|
+
Sets up a basic enumerable attribute on an ActiveRecord model. This assumes that a previous
|
37
|
+
migration has created a column of type VARCHAR on the table "taco".
|
38
|
+
|
39
|
+
class Taco < ActiveRecord::Base
|
40
|
+
enum_for :state, :has => [ :new, :composed, :served, :eaten ]
|
41
|
+
end
|
42
|
+
|
43
|
+
To query all eaten tacos using the automatically-generated named scopes:
|
44
|
+
|
45
|
+
Taco.state_eaten
|
46
|
+
|
47
|
+
To find out valid states on a model:
|
48
|
+
|
49
|
+
Taco.states
|
50
|
+
|
51
|
+
To create an enumerable type on a model, overriding the default prefix given to named_scopes:
|
52
|
+
|
53
|
+
class Taco < ActiveRecord::Base
|
54
|
+
enum_for :state, :has => [ :new, :composed, :served, :eaten ], :prefix => 'wacky'
|
55
|
+
end
|
56
|
+
|
57
|
+
This makes Taco respond to named scopes like Taco.wacky_composed instead of the default, which
|
58
|
+
would have prefixed the named_scopes with the name of the attribute. Note that this feature
|
59
|
+
also allows for the prefix to be omitted entirely if the value 'nil' is given instead of a String.
|
60
|
+
|
61
|
+
== Authors
|
62
|
+
|
63
|
+
Mal McKay, Justin Leitgeb and Ben Stein
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
desc 'Test the plugin.'
|
6
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
7
|
+
t.libs << 'lib'
|
8
|
+
t.verbose = true
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Run all the tests"
|
12
|
+
task :default => :spec
|
13
|
+
|
14
|
+
desc 'Generate documentation for the enum_for plugin.'
|
15
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'Enum For'
|
18
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
19
|
+
rdoc.rdoc_files.include('README')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
data/enum_for.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{enum_for}
|
3
|
+
s.version = "0.0.2"
|
4
|
+
|
5
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
6
|
+
s.authors = ["Mal McKay and Justin Leitgeb"]
|
7
|
+
s.date = %q{2009-06-17}
|
8
|
+
s.description = %q{Adds an enumerable attribute to an ActiveRecord-backed class}
|
9
|
+
s.email = %q{justin@mobilecommons.com}
|
10
|
+
|
11
|
+
s.files = ["enum_for.gemspec", "lib/enum_for.rb", "LICENSE", "Rakefile", "README.rdoc", "spec/enum_for_spec.rb", "spec/spec_helper.rb"]
|
12
|
+
|
13
|
+
s.has_rdoc = true
|
14
|
+
s.homepage = %q{http://github.com/mcommons/enum_for}
|
15
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubygems_version = %q{1.3.1}
|
18
|
+
s.summary = %q{Adds an enumerable attribute to an ActiveRecord-backed class}
|
19
|
+
s.test_files = ["spec/enum_for_spec.rb", "spec/spec_helper.rb"]
|
20
|
+
|
21
|
+
s.extra_rdoc_files = [ "README.rdoc" ]
|
22
|
+
|
23
|
+
s.rdoc_options += [
|
24
|
+
'--title', 'Enum For',
|
25
|
+
'--main', 'README.rdoc',
|
26
|
+
'--line-numbers',
|
27
|
+
'--inline-source'
|
28
|
+
]
|
29
|
+
|
30
|
+
%w[ activerecord activesupport ].each do |dep|
|
31
|
+
s.add_dependency(dep)
|
32
|
+
end
|
33
|
+
|
34
|
+
if s.respond_to? :specification_version then
|
35
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
36
|
+
s.specification_version = 2
|
37
|
+
end
|
38
|
+
end
|
data/lib/enum_for.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module EnumFor
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(SingletonMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module SingletonMethods
|
8
|
+
|
9
|
+
# Creates an enumerable attribute on an ActiveRecord model. Usage is as follows:
|
10
|
+
# enum_for :state, :has => [ :new, :composed, :served, :eaten ]
|
11
|
+
# Any additional options will be passed directly to the ActiveRecord method
|
12
|
+
# :validates_inclusion_of, which is used to validate the assigned values to this
|
13
|
+
# attribute. Requires a column of type VARCHAR with the name of the first argument
|
14
|
+
# to +enum_for+.
|
15
|
+
def enum_for(*args)
|
16
|
+
options = args.extract_options!
|
17
|
+
attribute = args.first
|
18
|
+
|
19
|
+
plural_attribute = attribute.to_s.pluralize
|
20
|
+
validates_inclusion_of attribute, options.except(:has, :prefix).merge(:in => options[:has].map{|v| v.to_s})
|
21
|
+
const_set(plural_attribute.upcase, options[:has])
|
22
|
+
|
23
|
+
prefix = options.has_key?(:prefix) ? options[:prefix] : attribute.to_s
|
24
|
+
|
25
|
+
prefixed_values = options[:has].map{|v| [[prefix, v].compact.join('_'), v] }
|
26
|
+
|
27
|
+
prefixed_values.each do |pv, v|
|
28
|
+
# Set constants for values
|
29
|
+
const_set(pv.upcase, v)
|
30
|
+
|
31
|
+
# Create named scope for this value
|
32
|
+
named_scope pv, :conditions => { attribute => v.to_s }
|
33
|
+
|
34
|
+
# Create predicate methods for values
|
35
|
+
unless self.instance_methods.include?(pv)
|
36
|
+
define_method("#{pv}?") do
|
37
|
+
read_attribute(attribute) == v.to_s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Define a custom setter method which casts input to a String. Since we present the configuration
|
43
|
+
# of enum_fu using Symbols, this makes sense to provide to the user.
|
44
|
+
define_method("#{attribute}=") do |other|
|
45
|
+
self[attribute] = other.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
cattr_reader plural_attribute.to_sym
|
49
|
+
class_variable_set(:"@@#{plural_attribute}", options[:has])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
ActiveRecord::Base.send(:include, EnumFor)
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'activerecord'
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
5
|
+
|
6
|
+
ActiveRecord::Base.establish_connection(
|
7
|
+
:adapter => 'sqlite3',
|
8
|
+
:dbfile => ":memory:"
|
9
|
+
)
|
10
|
+
|
11
|
+
ActiveRecord::Migration.verbose = false
|
12
|
+
|
13
|
+
ActiveRecord::Base.silence do
|
14
|
+
ActiveRecord::Schema.define do
|
15
|
+
create_table :tacos do |table|
|
16
|
+
table.string :state
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Taco < ActiveRecord::Base
|
22
|
+
enum_for :state, :has => [ :new, :composed, :served, :eaten ]
|
23
|
+
end
|
24
|
+
|
25
|
+
describe EnumFor do
|
26
|
+
before do
|
27
|
+
@taco = Taco.new(:state => 'new')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should make a valid model when given a valid state" do
|
31
|
+
@taco.should be_valid
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be valid when an attribute is set to a valid symbol" do
|
35
|
+
Taco.new(:state => :composed).should be_valid
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not be valid when an attribute is set to an invalid symbol" do
|
39
|
+
Taco.new(:state => :bogus).should_not be_valid
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should not create a valid model when given an invalid state" do
|
43
|
+
Taco.create(:state => 'barfed').should_not be_valid
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return all possible states when plural of the attribute is called" do
|
47
|
+
Taco::STATES.should == [ :new, :composed, :served, :eaten ]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should return all possible states when plural of attribute is called as class method" do
|
51
|
+
Taco.states.should == [ :new, :composed, :served, :eaten ]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should define constants for each type" do
|
55
|
+
Taco::STATE_EATEN.should == :eaten
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should define predicate methods for each valid state" do
|
59
|
+
@taco.state_eaten?
|
60
|
+
end
|
61
|
+
|
62
|
+
class Food < ActiveRecord::Base
|
63
|
+
set_table_name "tacos"
|
64
|
+
enum_for :state, :has => [:stuff, :morestuff], :prefix => nil
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should not prefix predicate methods if asked not to" do
|
68
|
+
Food.new(:state => :stuff).stuff?.should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should allow strings for attribute values even when initialized with symbols" do
|
72
|
+
Food.new(:state => 'stuff').should be_valid
|
73
|
+
end
|
74
|
+
|
75
|
+
class Balloon < ActiveRecord::Base
|
76
|
+
set_table_name "tacos"
|
77
|
+
enum_for :state, :has => [:stuff, :morestuff], :prefix => nil
|
78
|
+
|
79
|
+
def stuff?
|
80
|
+
"not a boolean"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should not define a predicate method when a one already exists" do
|
85
|
+
Balloon.new(:state => :stuff).stuff?.should == "not a boolean"
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "with validation options" do
|
89
|
+
class Food2 < ActiveRecord::Base
|
90
|
+
set_table_name "tacos"
|
91
|
+
enum_for :state, :has => ['stuff', 'morestuff'], :prefix => nil, :allow_nil => true, :message => "is great!"
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should allow validation message to be set" do
|
95
|
+
Food2.create(:state => :junk).errors.full_messages.should include('State is great!')
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should respect :allow_nil setting" do
|
99
|
+
Food2.new.should be_valid
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "named scope creation" do
|
104
|
+
before do
|
105
|
+
Taco.delete_all
|
106
|
+
|
107
|
+
2.times { Taco.create(:state => 'new') }
|
108
|
+
3.times { Taco.create(:state => 'composed') }
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should have two new tacos" do
|
112
|
+
Taco.state_new.size.should == 2
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should have two composed tacos" do
|
116
|
+
Taco.state_composed.size.should == 3
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mcommons-enum_for
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mal McKay and Justin Leitgeb
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-17 00:00:00 -07: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: activesupport
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description: Adds an enumerable attribute to an ActiveRecord-backed class
|
36
|
+
email: justin@mobilecommons.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- enum_for.gemspec
|
45
|
+
- lib/enum_for.rb
|
46
|
+
- LICENSE
|
47
|
+
- Rakefile
|
48
|
+
- README.rdoc
|
49
|
+
- spec/enum_for_spec.rb
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://github.com/mcommons/enum_for
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --charset=UTF-8
|
56
|
+
- --title
|
57
|
+
- Enum For
|
58
|
+
- --main
|
59
|
+
- README.rdoc
|
60
|
+
- --line-numbers
|
61
|
+
- --inline-source
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.2.0
|
80
|
+
signing_key:
|
81
|
+
specification_version: 2
|
82
|
+
summary: Adds an enumerable attribute to an ActiveRecord-backed class
|
83
|
+
test_files:
|
84
|
+
- spec/enum_for_spec.rb
|
85
|
+
- spec/spec_helper.rb
|