mcommons-values_for 0.0.7

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,105 @@
1
+ = values_for
2
+
3
+ values_for is an ActiveRecord extension enabling the easy use of enumerated types for your models.
4
+
5
+ == Description
6
+
7
+ values_for makes your ActiveRecord-backed class work with an enumerable type. Instead of existing
8
+ ActiveRecord plugins such as enum_fu, which store the enumerable attribute as an integer, values_for
9
+ stores the content of the enumerable attribute in a varchar column of the database.
10
+
11
+ values_for will also optionally create named scopes, predicate methods, and constants defining the
12
+ possible values for enumerable types on your model. By default, however, it avoids polluting your
13
+ model with things you may not need unless these features are specifically requested.
14
+
15
+ == Installation
16
+
17
+ values_for is available as a gem available on GitHub. Install with:
18
+
19
+ sudo gem install mcommons-values_for
20
+
21
+ You'll probable want a line like the following in your environment.rb:
22
+
23
+ config.gem 'mcommons-values_for', :lib => 'values_for'
24
+
25
+ == Basic Usage
26
+
27
+ The following example sets up an enumerable attribute on an ActiveRecord model. This assumes that a previous
28
+ migration has created a column of type VARCHAR on the table "taco".
29
+
30
+ class Taco < ActiveRecord::Base
31
+ values_for :state, :has => [ :new, :composed, :served, :eaten ]
32
+ end
33
+
34
+ For all values_for-enabled models, valid states are able to be read for an attribute by calling the plural of
35
+ the attribute name:
36
+
37
+ >> Taco.states
38
+ => [ :new, :composed, :served, :eaten ]
39
+
40
+ == Options
41
+
42
+ values_for will optionally create named scopes, predicate methods, and constants defining values for
43
+ enumerable types on your model. These may be specified on a case-by-case basis by using the :adds
44
+ option. As an example, the following would create an enumerable attribute called "flavor" with the
45
+ possible values "sour" and "sweet", while creating named scopes, predicate methods, and constants on
46
+ your class:
47
+
48
+ values_for :flavor, :has => [ :sour, :sweet ], :adds => [ :named_scopes, :predicate_methods, :constants ]
49
+
50
+ The different additive options are described below.
51
+
52
+ === Named Scopes
53
+
54
+ The named_scopes option adds a named scope, with either the default or custom prefix, to your model.
55
+ For example, the following model definition would create a named_scope Taco.flavor_sour on your class:
56
+
57
+ class Taco < ActiveRecord::Base
58
+ values_for :flavor, :has => [ :sour, :sweet ], :adds => :named_scopes
59
+ end
60
+
61
+ Afterwards, you may use the named scopes as follows. Remember to add the prefix!
62
+
63
+ Taco.flavor_sour
64
+ Taco.flavor_sweet
65
+
66
+ The prefix may be overridden or omitted by using the :prefix option.
67
+
68
+ === Predicate methods
69
+
70
+ values_for also creates predicate methods on instances of your class so that you can ask if they have any
71
+ of the given enumerable states. For example, if a Taco class has an enumerable type :taste with values
72
+ :good and :bad, taco.good? would return a boolean value indicating whether or not the taste is good.
73
+
74
+ This option is only enabled when the additive option predicate_methods is specified.
75
+
76
+ === Constants
77
+
78
+ values_for optionally defines constants corresponding to each of the valid enumerable types on your model.
79
+ For example, if you have an enumerable column called "states" with valid states :starting and :finished,
80
+ values_for would define constants on your model Model::STATE_STARTING and Model::STATE_FINISHED with the
81
+ contents of these constants :starting and :finished respectively. This is only enabled when the additive
82
+ option :constants is specified.
83
+
84
+ == Configuration
85
+
86
+ values_for by default adds the attribute being modified as a prefix to constant declarations, named scopes,
87
+ and predicate methods. If you wish to modify this prefix, pass the :prefix option to values_for. You may
88
+ also omit the addition of the prefix by passing :prefix => nil to values_for. Example:
89
+
90
+ class Taco < ActiveRecord::Base
91
+ values_for :state, :has => [ :new, :composed, :served, :eaten ], :prefix => 'wacky'
92
+ end
93
+
94
+ This makes Taco respond to named scopes like Taco.wacky_composed instead of the default, which
95
+ would have prefixed the named_scopes with the name of the attribute.
96
+
97
+ == Notes
98
+
99
+ This plugin doesn't implement default values for a model. If this behavior is desired, you may be interested
100
+ in the default_value_for plugin (available http://github.com/FooBarWidget/default_value_for/tree/master),
101
+ which has been successfully tested with values_for.
102
+
103
+ == Authors
104
+
105
+ 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 values_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.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
data/lib/values_for.rb ADDED
@@ -0,0 +1,76 @@
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
10
+ # follows:
11
+ #
12
+ # values_for :state, :has => [ :new, :composed, :served, :eaten ]
13
+ #
14
+ # Any additional options will be passed directly to the ActiveRecord method
15
+ # :validates_inclusion_of, which is used to validate the assigned values to
16
+ # this attribute. Requires a column of type VARCHAR with the name of the
17
+ # first argument to +values_for+.
18
+ def values_for(*args)
19
+ opts = args.extract_options!
20
+ attribute = args.first
21
+
22
+ attribute_s = attribute.to_s
23
+
24
+ additives = Array.wrap(opts[:add])
25
+ plural_attribute = attribute_s.pluralize
26
+
27
+ prefix = opts.has_key?(:prefix) ? opts[:prefix] : attribute_s
28
+
29
+ # Valid values are most likely Symbols anyway, but coerce them to be safe.
30
+ valid_symbols = opts[:has].map{|v| v.to_sym }
31
+
32
+ valid_symbols.each do |val_sym|
33
+ val_s = val_sym.to_s
34
+
35
+ prefixed_val = [ prefix, val_s ].compact.join('_')
36
+
37
+ # Create +optional+ constants
38
+ const_set(prefixed_val.upcase, val_sym) if additives.include?(:constants)
39
+
40
+ # Create +optional+ named scopes
41
+ named_scope prefixed_val, :conditions => { attribute => val_s } if additives.include?(:named_scopes)
42
+
43
+ # Create +optional+ predicate methods, but don't overwrite existing methods
44
+ if additives.include?(:predicate_methods) && !self.instance_methods.include?(prefixed_val)
45
+ define_method(prefixed_val + '?') do # def foo?
46
+ read_attribute(attribute) == val_s # read_attribute(:foo) == 'foo'
47
+ end # end
48
+ end
49
+
50
+ end
51
+
52
+ # Accepts assignment both from String and Symbol form of valid values.
53
+ validates_inclusion_of attribute, opts.except(:has, :prefix, :add).
54
+ merge(:in => valid_symbols | valid_symbols.map{|s| s.to_s } )
55
+
56
+ # Custom reader method presents attribute value in Symbol form.
57
+ define_method(attribute_s) do # def foo
58
+ self[attribute].to_sym unless self[attribute].nil? # self[:foo].to_sym unless self[:foo].nil?
59
+ end # end
60
+
61
+ # Custom setter method casting all attribute input to String, allows
62
+ # assignment from Symbol form.
63
+ define_method(attribute_s + '=') do |other| # def foo=(other)
64
+ self[attribute] = other.to_s # self[foo] = other
65
+ end # end
66
+
67
+ # Make collection of all valid attribute Symbols available to user
68
+ # from plural name of attribute as class method.
69
+ cattr_reader plural_attribute.to_sym
70
+ class_variable_set(:"@@#{plural_attribute}", opts[:has])
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ ActiveRecord::Base.send(:include, EnumFor)
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'mocha'
3
+ require 'spec'
4
+
5
+ require File.join(File.dirname(__FILE__), %w[.. lib values_for])
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.mock_with(:mocha)
9
+ end
@@ -0,0 +1,193 @@
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
+
22
+ describe EnumFor do
23
+ before do
24
+ @taco = Taco.new(:state => 'new')
25
+ end
26
+
27
+ class Taco < ActiveRecord::Base
28
+ values_for :state, :has => [ :new, :composed, :served, :eaten ]
29
+ end
30
+
31
+ describe "model validation" do
32
+ it "should make a valid model when given a valid state" do
33
+ @taco.should be_valid
34
+ end
35
+
36
+ it "should retrieve a symbol when the model is created with attribute set to string" do
37
+ @taco.state.should == :new
38
+ end
39
+
40
+ it "should be valid when an attribute is set to a valid symbol" do
41
+ Taco.new(:state => :composed).should be_valid
42
+ end
43
+
44
+ it "should not be valid when an attribute is set to an invalid symbol" do
45
+ Taco.new(:state => :bogus).should_not be_valid
46
+ end
47
+
48
+ it "should not create a valid model when given an invalid state" do
49
+ Taco.create(:state => 'barfed').should_not be_valid
50
+ end
51
+ end
52
+
53
+ describe "storage and retrieval of possible states in class" do
54
+ it "should return all possible states when plural of attribute is called as class method" do
55
+ Taco.states.should == [ :new, :composed, :served, :eaten ]
56
+ end
57
+ end
58
+
59
+ describe "definition of constants for each attribute value" do
60
+ class WithoutConstants < ActiveRecord::Base
61
+ values_for :state, :has => [ :new, :composed, :served, :eaten ]
62
+ end
63
+
64
+ class WithConstants < ActiveRecord::Base
65
+ values_for :state, :has => [ :new, :composed, :served, :eaten ], :add => :constants
66
+ end
67
+
68
+ it "should be disabled by default" do
69
+ WithoutConstants.constants.should_not include('WithoutConstants::STATE_NEW')
70
+ end
71
+
72
+ it "should be enabled when added" do
73
+ WithConstants.constants.should_not include('WithoutConstants::STATE_NEW')
74
+ end
75
+
76
+ it "should define constants for each type" do
77
+ WithConstants::STATE_EATEN.should == :eaten
78
+ end
79
+ end
80
+
81
+ describe "definition of predicate methods" do
82
+ class WithPredicateMethods < ActiveRecord::Base
83
+ set_table_name "tacos"
84
+ values_for :state, :has => [ :new, :composed, :served, :eaten ], :add => :predicate_methods
85
+ end
86
+
87
+ it "should not define predicate methods for each valid state by default" do
88
+ @taco.should_not respond_to(:state_eaten?)
89
+ end
90
+
91
+ it "should define predicate methods for each state when specified" do
92
+ WithPredicateMethods.new.should respond_to(:state_eaten?)
93
+ end
94
+ end
95
+
96
+ describe "with prefix option" do
97
+ class Food < ActiveRecord::Base
98
+ set_table_name "tacos"
99
+ values_for :state, :has => [:stuff, :morestuff], :prefix => nil, :add => :predicate_methods
100
+ end
101
+
102
+ it "should not prefix predicate methods if asked not to" do
103
+ Food.new(:state => :stuff).should respond_to(:stuff?)
104
+ end
105
+ end
106
+
107
+ describe "casting of values set and retrieved to correct type" do
108
+ it "should allow strings for attribute values even when initialized with symbols" do
109
+ Food.new(:state => 'stuff').should be_valid
110
+ end
111
+
112
+ it "should retrieve a symbol when a string is set and the attribute value is a symbol" do
113
+ f = Food.new(:state => 'stuff')
114
+ f.state.should == :stuff
115
+ end
116
+ end
117
+
118
+ describe "with an Array of additives" do
119
+ class BeefJerkey < ActiveRecord::Base
120
+ set_table_name "tacos"
121
+ values_for :flavor, :has => [:sour, :spicy], :add => [:named_scopes, :predicate_methods]
122
+ end
123
+
124
+ it "should construct named scopes" do
125
+ BeefJerkey.should respond_to(:flavor_sour)
126
+ end
127
+
128
+ it "should construct predicate methods" do
129
+ BeefJerkey.new.should respond_to(:flavor_sour?)
130
+ end
131
+ end
132
+
133
+ describe "behavior with existing methods of same name" do
134
+ class Balloon < ActiveRecord::Base
135
+ set_table_name "tacos"
136
+ values_for :state, :has => [:stuff, :morestuff], :prefix => nil
137
+
138
+ def stuff?
139
+ "not a boolean"
140
+ end
141
+ end
142
+
143
+ it "should not define a predicate method when a one already exists" do
144
+ Balloon.new(:state => :stuff).stuff?.should == "not a boolean"
145
+ end
146
+ end
147
+
148
+ describe "with validation options" do
149
+ class Food2 < ActiveRecord::Base
150
+ set_table_name "tacos"
151
+ values_for :state, :has => ['stuff', 'morestuff'], :prefix => nil, :allow_nil => true, :message => "is great!"
152
+ end
153
+
154
+ it "should allow validation message to be set" do
155
+ Food2.create(:state => :junk).errors.full_messages.should include('State is great!')
156
+ end
157
+
158
+ it "should respect :allow_nil setting" do
159
+ Food2.new.should be_valid
160
+ end
161
+ end
162
+
163
+ describe "named scope creation" do
164
+ describe "when not added" do
165
+ it "should not create named scopes" do
166
+ Taco.should_not respond_to(:state_composed)
167
+ end
168
+ end
169
+
170
+ describe "when added" do
171
+ class Tequila < ActiveRecord::Base
172
+ set_table_name "tacos"
173
+ values_for :state, :has => [ :wormy, :delicious ], :add => :named_scopes
174
+ end
175
+
176
+ before do
177
+ Tequila.delete_all
178
+
179
+ 2.times { Tequila.create(:state => 'wormy') }
180
+ 3.times { Tequila.create(:state => 'delicious') }
181
+ end
182
+
183
+ it "should have two wormy tequilas" do
184
+ Tequila.state_wormy.size.should == 2
185
+ end
186
+
187
+ it "should have two delicious tequilas" do
188
+ Tequila.state_delicious.size.should == 3
189
+ end
190
+ end
191
+ end
192
+
193
+ end
@@ -0,0 +1,38 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{values_for}
3
+ s.version = "0.0.7"
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 = ["values_for.gemspec", "lib/values_for.rb", "LICENSE", "Rakefile", "README.rdoc", "spec/values_for_spec.rb", "spec/spec_helper.rb"]
12
+
13
+ s.has_rdoc = true
14
+ s.homepage = %q{http://github.com/mcommons/values_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/values_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
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mcommons-values_for
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
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
+ - values_for.gemspec
45
+ - lib/values_for.rb
46
+ - LICENSE
47
+ - Rakefile
48
+ - README.rdoc
49
+ - spec/values_for_spec.rb
50
+ - spec/spec_helper.rb
51
+ has_rdoc: true
52
+ homepage: http://github.com/mcommons/values_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/values_for_spec.rb
85
+ - spec/spec_helper.rb