attribute_defaults 0.1

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/Changelog.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1 (July 2, 2011)
2
+
3
+ Initial release supporting Rails 3.0 and 3.1.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,41 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ attribute_defaults (0.1)
5
+ activerecord (>= 3)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ activemodel (3.0.9)
11
+ activesupport (= 3.0.9)
12
+ builder (~> 2.1.2)
13
+ i18n (~> 0.5.0)
14
+ activerecord (3.0.9)
15
+ activemodel (= 3.0.9)
16
+ activesupport (= 3.0.9)
17
+ arel (~> 2.0.10)
18
+ tzinfo (~> 0.3.23)
19
+ activesupport (3.0.9)
20
+ arel (2.0.10)
21
+ builder (2.1.2)
22
+ diff-lcs (1.1.2)
23
+ i18n (0.5.0)
24
+ mysql2 (0.2.11)
25
+ rspec (2.6.0)
26
+ rspec-core (~> 2.6.0)
27
+ rspec-expectations (~> 2.6.0)
28
+ rspec-mocks (~> 2.6.0)
29
+ rspec-core (2.6.4)
30
+ rspec-expectations (2.6.0)
31
+ diff-lcs (~> 1.1.2)
32
+ rspec-mocks (2.6.0)
33
+ tzinfo (0.3.29)
34
+
35
+ PLATFORMS
36
+ ruby
37
+
38
+ DEPENDENCIES
39
+ attribute_defaults!
40
+ mysql2 (< 0.3)
41
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Jonathan Viney
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.md ADDED
@@ -0,0 +1,84 @@
1
+ # Active Record Attribute Defaults
2
+
3
+ An easy way to specify default attribute values for new records.
4
+
5
+ ## Quick start
6
+
7
+ class Person < ActiveRecord::Base
8
+ defaults :country => 'New Zealand', :type => 'Unknown', :address => lambda { Address.new }
9
+
10
+ default :last_name do |person|
11
+ person.first_name
12
+ end
13
+ end
14
+
15
+ The default value is only used if the attribute is not present in the attributes hash, so any values you pass in when creating the record will take precedence.
16
+
17
+ Note that the defaults are evaluated when the model class is loaded, so if a default is meant to
18
+ be dynamic, such as today's date, it must be specified as a proc.
19
+
20
+ class Person < ActiveRecord::Base
21
+ default :today => Date.today # WRONG
22
+ default :today => lambda { Date.today } # RIGHT
23
+ end
24
+
25
+ Interestingly, because the model classes are reloaded for every request in development mode,
26
+ the first default would always work as expected. But in production, where the model classes are
27
+ only loaded once, the date will shortly become incorrect.
28
+
29
+ ## More information
30
+
31
+ Use this gem to define default values for attributes on new records.
32
+ Requires a hash of `attribute => value` pairs, or a single attribute with an associated block.
33
+
34
+ * If the value is a block, it will be called to retrieve the default value.
35
+ * If the value is a symbol, a method by that name will be called on the object to retrieve the default value.
36
+
37
+ The following code demonstrates the different ways default values can be specified. Defaults are applied in the order they are defined.
38
+
39
+ class Person < ActiveRecord::Base
40
+ defaults :name => "My name", :city => lambda { "My city" }
41
+
42
+ default :birthdate do |person|
43
+ Date.current if person.wants_birthday_today?
44
+ end
45
+
46
+ default :favourite_colour => :default_favourite_colour
47
+
48
+ def default_favourite_colour
49
+ "Blue"
50
+ end
51
+ end
52
+
53
+ The `defaults` and the `default` methods behave the same way. Use whichever is appropriate.
54
+
55
+ The default values are only used if the key is not present in the given attributes.
56
+ Therefore, the above code will behave in the following way:
57
+
58
+ p = Person.new
59
+ p.name # "My name"
60
+ p.city # "My city"
61
+
62
+ p = Person.new(:name => nil)
63
+ p.name # nil
64
+ p.city # "My city"
65
+
66
+ ### Default values for belongs_to associations
67
+
68
+ Default values can also be specified for an association. For instance:
69
+
70
+ class Student < ActiveRecord::Base
71
+ belongs_to :school
72
+
73
+ default :school => lambda { School.favourite }
74
+ end
75
+
76
+ In this scenario, if a `school_id` was provided in the attributes hash, the default value for the association will be ignored:
77
+
78
+ s = Student.new
79
+ s.school # => #<School: ...>
80
+
81
+ s = Student.new(:school_id => nil)
82
+ s.school # => nil
83
+
84
+ Similarly, if a default value is specified for the foreign key and an object for the association is provided, the default foreign key is ignored.
@@ -0,0 +1,17 @@
1
+ require File.expand_path("../lib/active_record/attribute_defaults/version", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "attribute_defaults"
5
+ s.version = ActiveRecord::AttributeDefaults::VERSION
6
+ s.summary = "attribute_defaults-#{s.version}"
7
+ s.description = "Add default attribute values when creating models."
8
+ s.authors = ["Jonathan Viney"]
9
+ s.email = "jonathan.viney@gmail.com"
10
+ s.files = `git ls-files`.split("\n")
11
+ s.homepage = "http://github.com/jviney/attribute_defaults"
12
+
13
+ s.add_dependency "activerecord", ">= 3"
14
+
15
+ s.add_development_dependency "rspec"
16
+ s.add_development_dependency "mysql2", "< 0.3"
17
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "active_record/attribute_defaults"
@@ -0,0 +1,138 @@
1
+ require "active_support/concern"
2
+ require "active_support/core_ext/module/aliasing"
3
+ require "active_support/core_ext/class/attribute"
4
+
5
+ require "active_record/attribute_defaults/default"
6
+
7
+ module ActiveRecord
8
+ module AttributeDefaults
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ alias_method_chain :initialize, :defaults
13
+
14
+ class_attribute :attribute_defaults
15
+ self.attribute_defaults = []
16
+ end
17
+
18
+ module ClassMethods
19
+ # Define default values for attributes on new records. Requires a hash of <tt>attribute => value</tt> pairs, or a single attribute with an associated block.
20
+ # If the value is a block, it will be called to retrieve the default value.
21
+ # If the value is a symbol, a method by that name will be called on the object to retrieve the default value.
22
+ #
23
+ # The following code demonstrates the different ways default values can be specified. Defaults are applied in the order they are defined.
24
+ #
25
+ # class Person < ActiveRecord::Base
26
+ # defaults :name => 'My name', :city => lambda { 'My city' }
27
+ #
28
+ # default :birthdate do |person|
29
+ # Date.today if person.wants_birthday_today?
30
+ # end
31
+ #
32
+ # default :favourite_colour => :default_favourite_colour
33
+ #
34
+ # def default_favourite_colour
35
+ # "Blue"
36
+ # end
37
+ # end
38
+ #
39
+ # The <tt>defaults</tt> and the <tt>default</tt> methods behave the same way. Use whichever is appropriate.
40
+ #
41
+ # The default values are only used if the key is not present in the given attributes.
42
+ #
43
+ # p = Person.new
44
+ # p.name # "My name"
45
+ # p.city # "My city"
46
+ #
47
+ # p = Person.new(:name => nil)
48
+ # p.name # nil
49
+ # p.city # "My city"
50
+ #
51
+ # == Default values for belongs_to associations
52
+ #
53
+ # Default values can also be specified for an association. For instance:
54
+ #
55
+ # class Student < ActiveRecord::Base
56
+ # belongs_to :school
57
+ # default :school => lambda { School.new }
58
+ # end
59
+ #
60
+ # In this scenario, if a school_id was provided in the attributes hash, the default value for the association will be ignored:
61
+ #
62
+ # s = Student.new
63
+ # s.school # => #<School: ...>
64
+ #
65
+ # s = Student.new(:school_id => nil)
66
+ # s.school # => nil
67
+ #
68
+ # Similarly, if a default value is specified for the foreign key and an object for the association is provided, the default foreign key is ignored.
69
+ def defaults(defaults, &block)
70
+ default_objects = case
71
+ when defaults.is_a?(Hash)
72
+ defaults.map { |attribute, value| Default.new(attribute, value) }
73
+
74
+ when defaults.is_a?(Symbol) && block
75
+ Default.new(defaults, block)
76
+
77
+ else
78
+ raise "pass either a hash of attribute/value pairs, or a single attribute with a block"
79
+ end
80
+
81
+ self.attribute_defaults += Array.wrap(default_objects)
82
+ end
83
+
84
+ alias_method :default, :defaults
85
+ end
86
+
87
+ module InstanceMethods
88
+ if ActiveRecord::VERSION::STRING >= "3.1"
89
+ def initialize_with_defaults(attributes = nil, options = {})
90
+ initialize_without_defaults(attributes, options) do |record|
91
+ record.apply_default_attribute_values(attributes)
92
+ yield record if block_given?
93
+ end
94
+ end
95
+ else
96
+ def initialize_with_defaults(attributes = nil)
97
+ initialize_without_defaults(attributes) do |record|
98
+ record.apply_default_attribute_values(attributes)
99
+ yield record if block_given?
100
+ end
101
+ end
102
+ end
103
+
104
+ def apply_default_attribute_values(specific_attributes)
105
+ specific_attributes = (specific_attributes || {}).stringify_keys
106
+
107
+ # Rails 3.1 deprecates #primary_key_name in favour of :foreign_key
108
+ foreign_key_method = if ActiveRecord::VERSION::STRING >= "3.1"
109
+ :foreign_key
110
+ else
111
+ :primary_key_name
112
+ end
113
+
114
+ self.class.attribute_defaults.each do |default|
115
+ next if specific_attributes.include?(default.attribute)
116
+
117
+ # Ignore a default value for association_id if association has been specified
118
+ reflection = self.class.reflections[default.attribute.to_sym]
119
+ if reflection and reflection.macro == :belongs_to and specific_attributes.include?(reflection.send(foreign_key_method).to_s)
120
+ next
121
+ end
122
+
123
+ # Ignore a default value for association if association_id has been specified
124
+ reflection = self.class.reflections.values.find { |r| r.macro == :belongs_to && r.send(foreign_key_method).to_s == default.attribute }
125
+ if reflection and specific_attributes.include?(reflection.name.to_s)
126
+ next
127
+ end
128
+
129
+ send("#{default.attribute}=", default.value(self))
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ class ActiveRecord::Base
137
+ include ActiveRecord::AttributeDefaults
138
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord
2
+ module AttributeDefaults
3
+ class Default
4
+ attr_reader :attribute
5
+
6
+ def initialize(attribute, value)
7
+ @attribute, @value = attribute.to_s, value
8
+ end
9
+
10
+ def value(record)
11
+ if @value.is_a?(Symbol)
12
+ record.send(@value)
13
+ elsif @value.respond_to?(:call)
14
+ @value.call(record)
15
+ else
16
+ @value.duplicable? ? @value.dup : @value
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module AttributeDefaults
3
+ VERSION = "0.1"
4
+ end
5
+ end
@@ -0,0 +1,78 @@
1
+ require "spec_helper"
2
+
3
+ describe "active_record/attribute_defaults" do
4
+ it "should supply defaults for a new record" do
5
+ p = Person.new
6
+
7
+ p.city.should == "Christchurch"
8
+ p.country.should == "New Zealand"
9
+ p.first_name.should == "Sean"
10
+ p.last_name.should == "Fitzpatrick"
11
+ p.lucky_number.should == 2
12
+ p.favourite_colour.should == "Blue"
13
+ end
14
+
15
+ it "should ignore the default if a specific value is given" do
16
+ p = Person.new(:city => "", "lucky_number" => nil)
17
+
18
+ p.city.should == ""
19
+ p.lucky_number.should be_nil
20
+
21
+ p.country.should == "New Zealand"
22
+ p.first_name.should == "Sean"
23
+ p.last_name.should == "Fitzpatrick"
24
+ p.favourite_colour.should == "Blue"
25
+ end
26
+
27
+ it "should not cache the result of the default block" do
28
+ one = Person.new
29
+ two = Person.new
30
+
31
+ one.first_name.object_id.should_not == two.first_name.object_id
32
+ end
33
+
34
+ it "should not provide defaults for existing records" do
35
+ existing_person = Person.create!(:last_name => "Key")
36
+ Person.find(existing_person.id).last_name.should == "Key"
37
+ end
38
+
39
+ it "should process the defaults in the order of definition" do
40
+ Person.new(:last_name => "Carter").favourite_colour.should == "Red"
41
+ end
42
+
43
+ it "should add defaults on create!" do
44
+ p = Person.create!
45
+
46
+ p.city.should == "Christchurch"
47
+ p.country.should == "New Zealand"
48
+ p.first_name.should == "Sean"
49
+ p.last_name.should == "Fitzpatrick"
50
+ p.lucky_number.should == 2
51
+ p.favourite_colour.should == "Blue"
52
+ end
53
+
54
+ it "should allow a default object for a belongs_to association" do
55
+ @school = School.create! { |s| s.id = 1 }
56
+ PersonWithDefaultSchool.new.school.should == @school
57
+ @school.destroy
58
+ end
59
+
60
+ it "should not use the default for a belongs to association when a nil id is supplied" do
61
+ PersonWithDefaultSchool.new(:school_id => nil).school.should be_nil
62
+ end
63
+
64
+ it "should allow a default id for a belongs_to association" do
65
+ @school = School.create! { |s| s.id = 1 }
66
+ PersonWithDefaultSchoolId.new.school.should == @school
67
+ @school.destroy
68
+ end
69
+
70
+ it "should ignore a default id for a belongs_to association when an object is supplied" do
71
+ PersonWithDefaultSchoolId.new(:school => nil).school.should be_nil
72
+ end
73
+
74
+ it "should not raise an error when no defaults are defined" do
75
+ klass = Class.new(ActiveRecord::Base) { set_table_name "people" }
76
+ lambda { klass.new }.should_not raise_error
77
+ end
78
+ end
data/spec/database.yml ADDED
@@ -0,0 +1,7 @@
1
+ mysql:
2
+ :adapter: mysql2
3
+ :host: localhost
4
+ :username: rails
5
+ :password:
6
+ :database: rails_plugin_test
7
+ :socket: /tmp/mysql.sock
data/spec/schema.rb ADDED
@@ -0,0 +1,18 @@
1
+ ActiveRecord::Schema.define :version => 0 do
2
+ create_table :people, :force => true do |t|
3
+ t.column :first_name, :string
4
+ t.column :middle_name, :string
5
+ t.column :last_name, :string
6
+ t.column :city, :string
7
+ t.column :country, :string
8
+ t.column :birthdate, :date
9
+ t.column :lucky_number, :integer
10
+ t.column :favourite_colour, :string
11
+ t.column :type, :string
12
+ t.column :school_id, :integer
13
+ end
14
+
15
+ create_table :schools, :force => true do |t|
16
+ t.column :name, :string
17
+ end
18
+ end
@@ -0,0 +1,55 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require "rspec"
4
+
5
+ require "active_record"
6
+ require "active_record/base"
7
+
8
+ require File.expand_path("../../lib/active_record/attribute_defaults", __FILE__)
9
+
10
+ ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + "/database.yml"))
11
+ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(__FILE__) + "/debug.log")
12
+ ActiveRecord::Base.establish_connection(ENV["DB"] || "mysql")
13
+
14
+ load(File.dirname(__FILE__) + "/schema.rb")
15
+
16
+ # Fixtures
17
+ Address = Struct.new(:suburb, :city)
18
+
19
+ class Group < ActiveRecord::Base
20
+ end
21
+
22
+ class Person < ActiveRecord::Base
23
+ belongs_to :school
24
+
25
+ # Include an aggregate reflection to check compatibility
26
+ composed_of :address, :mapping => [%w(address_suburb suburb), %(address_city city)]
27
+
28
+ defaults :city => "Christchurch", :country => lambda { "New Zealand" }
29
+
30
+ default :first_name => "Sean"
31
+
32
+ default :last_name do
33
+ "Fitzpatrick"
34
+ end
35
+
36
+ defaults :lucky_number => lambda { 2 }, :favourite_colour => :default_favourite_colour
37
+
38
+ def default_favourite_colour
39
+ last_name == "Fitzpatrick" ? "Blue" : "Red"
40
+ end
41
+ end
42
+
43
+ class PersonWithDefaultSchool < Person
44
+ default :school do
45
+ School.find(1)
46
+ end
47
+ end
48
+
49
+ class PersonWithDefaultSchoolId < Person
50
+ default :school_id => 1
51
+ end
52
+
53
+ class School < ActiveRecord::Base
54
+ has_many :people
55
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attribute_defaults
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Jonathan Viney
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-07-02 00:00:00 +12:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 5
29
+ segments:
30
+ - 3
31
+ version: "3"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: mysql2
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - <
55
+ - !ruby/object:Gem::Version
56
+ hash: 13
57
+ segments:
58
+ - 0
59
+ - 3
60
+ version: "0.3"
61
+ type: :development
62
+ version_requirements: *id003
63
+ description: Add default attribute values when creating models.
64
+ email: jonathan.viney@gmail.com
65
+ executables: []
66
+
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - Changelog.md
73
+ - Gemfile
74
+ - Gemfile.lock
75
+ - LICENSE
76
+ - Readme.md
77
+ - attribute_defaults.gemspec
78
+ - init.rb
79
+ - lib/active_record/attribute_defaults.rb
80
+ - lib/active_record/attribute_defaults/default.rb
81
+ - lib/active_record/attribute_defaults/version.rb
82
+ - spec/active_record/attribute_defaults_spec.rb
83
+ - spec/database.yml
84
+ - spec/schema.rb
85
+ - spec/spec_helper.rb
86
+ has_rdoc: true
87
+ homepage: http://github.com/jviney/attribute_defaults
88
+ licenses: []
89
+
90
+ post_install_message:
91
+ rdoc_options: []
92
+
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: 3
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ requirements: []
114
+
115
+ rubyforge_project:
116
+ rubygems_version: 1.6.2
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: attribute_defaults-0.1
120
+ test_files: []
121
+