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 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
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'mocha'
3
+ require 'spec'
4
+
5
+ require File.join(File.dirname(__FILE__), %w[.. lib enum_for])
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.mock_with(:mocha)
9
+ end
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