has_and_belongs_to_many_with_deferred_save 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Rakefile +23 -0
- data/Readme.markdown +60 -0
- data/VERSION +1 -0
- data/has_and_belongs_to_many_with_deferred_save.gemspec +61 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/has_and_belongs_to_many_with_deferred_save.rb +133 -0
- data/spec/.gitignore +2 -0
- data/spec/db/database.yml +21 -0
- data/spec/db/schema.rb +21 -0
- data/spec/has_and_belongs_to_many_with_deferred_save_spec.rb +74 -0
- data/spec/models/person.rb +3 -0
- data/spec/models/room.rb +17 -0
- data/spec/spec_helper.rb +23 -0
- data/uninstall.rb +1 -0
- metadata +94 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
task :default do |t|
|
2
|
+
options = "--colour"
|
3
|
+
files = FileList['spec/**/*_spec.rb'].map{|f| f.sub(%r{^spec/},'') }
|
4
|
+
exit system("cd spec && spec #{options} #{files}") ? 0 : 1
|
5
|
+
end
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
project_name = 'has_and_belongs_to_many_with_deferred_save'
|
10
|
+
Jeweler::Tasks.new do |gem|
|
11
|
+
gem.name = project_name
|
12
|
+
gem.summary = "Make ActiveRecord defer/postpone saving the records you add to an habtm (has_and_belongs_to_many) association until you call model.save, allowing validation in the style of normal attributes."
|
13
|
+
gem.email = "github.com@tylerrick.com"
|
14
|
+
gem.homepage = "http://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save"
|
15
|
+
gem.authors = ["Tyler Rick", "Alessio Caiazza"]
|
16
|
+
gem.add_dependency('activerecord')
|
17
|
+
gem.add_development_dependency('rspec')
|
18
|
+
end
|
19
|
+
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
23
|
+
end
|
data/Readme.markdown
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
Make ActiveRecord defer/postpone saving the records you add to an habtm (has_and_belongs_to_many) association until you call model.save, allowing validation in the style of normal attributes.
|
2
|
+
|
3
|
+
How to install
|
4
|
+
==============
|
5
|
+
|
6
|
+
As Rails plugin:
|
7
|
+
./script/plugin install git://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save.git
|
8
|
+
|
9
|
+
As a gem:
|
10
|
+
sudo gem install has_and_belongs_to_many_with_deferred_save
|
11
|
+
|
12
|
+
Usage
|
13
|
+
=====
|
14
|
+
|
15
|
+
class Room < ActiveRecord::Base
|
16
|
+
has_and_belongs_to_many_with_deferred_save :people
|
17
|
+
end
|
18
|
+
|
19
|
+
Motivation
|
20
|
+
==========
|
21
|
+
|
22
|
+
Let's say you want to validate the room.people collection and prevent the user from adding more people to the room than will fit. If they do try to add more people than will fit, you want to display a nice error message on the page and let them try again...
|
23
|
+
|
24
|
+
This isn't possible using the standard has_and_belongs_to_many due to these two problems:
|
25
|
+
|
26
|
+
1. When we do the assignment to our collection (room.people = whatever), it immediately saves it in our join table (people_rooms) rather than waiting until we call room.save.
|
27
|
+
|
28
|
+
2. You can "validate" using habtm's :before_add option ... but it any errors added there end up being ignored/lost. The only way to abort the save from a before_add seems to be to raise an exception...
|
29
|
+
|
30
|
+
But we don't want to raise an exception when the user violates our validation; we want validation of the people collection to be handled the same as any other field in the Room model: we want it to simply add an error to the Room model's error array which we can than display on the form with the other input errors.
|
31
|
+
|
32
|
+
has_and_belongs_to_many_with_deferred_save solves this problem by overriding the setter method for your collection (people=), causing it to store the new members in a temporary variable (unsaved_people) rather than saving it immediately.
|
33
|
+
|
34
|
+
You can then validate the unsaved collection as you would any other attribute, adding to self.errors if something is invalid about the collection (too many members, etc.).
|
35
|
+
|
36
|
+
The unsaved collection is automatically saved when you call save on the model.
|
37
|
+
|
38
|
+
|
39
|
+
Compatibility
|
40
|
+
=============
|
41
|
+
|
42
|
+
Tested with Rails 2.3.4.
|
43
|
+
|
44
|
+
Bugs
|
45
|
+
====
|
46
|
+
|
47
|
+
http://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save/issues
|
48
|
+
|
49
|
+
History
|
50
|
+
=======
|
51
|
+
|
52
|
+
It started as a [post](http://www.ruby-forum.com/topic/81095) to the Rails mailing list asking how to validate a has_and_belongs_to_many collection/association.
|
53
|
+
|
54
|
+
License
|
55
|
+
=======
|
56
|
+
|
57
|
+
This plugin is licensed under the BSD license.
|
58
|
+
|
59
|
+
2010 (c) Contributors
|
60
|
+
2007 (c) QualitySmith, Inc.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{has_and_belongs_to_many_with_deferred_save}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Tyler Rick", "Alessio Caiazza"]
|
12
|
+
s.date = %q{2010-02-25}
|
13
|
+
s.email = %q{github.com@tylerrick.com}
|
14
|
+
s.files = [
|
15
|
+
".gitignore",
|
16
|
+
"Rakefile",
|
17
|
+
"Readme.markdown",
|
18
|
+
"VERSION",
|
19
|
+
"has_and_belongs_to_many_with_deferred_save.gemspec",
|
20
|
+
"init.rb",
|
21
|
+
"install.rb",
|
22
|
+
"lib/has_and_belongs_to_many_with_deferred_save.rb",
|
23
|
+
"spec/.gitignore",
|
24
|
+
"spec/db/database.yml",
|
25
|
+
"spec/db/schema.rb",
|
26
|
+
"spec/has_and_belongs_to_many_with_deferred_save_spec.rb",
|
27
|
+
"spec/models/person.rb",
|
28
|
+
"spec/models/room.rb",
|
29
|
+
"spec/spec_helper.rb",
|
30
|
+
"uninstall.rb"
|
31
|
+
]
|
32
|
+
s.homepage = %q{http://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save}
|
33
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
34
|
+
s.require_paths = ["lib"]
|
35
|
+
s.rubygems_version = %q{1.3.5}
|
36
|
+
s.summary = %q{Make ActiveRecord defer/postpone saving the records you add to an habtm (has_and_belongs_to_many) association until you call model.save, allowing validation in the style of normal attributes.}
|
37
|
+
s.test_files = [
|
38
|
+
"spec/models/room.rb",
|
39
|
+
"spec/models/person.rb",
|
40
|
+
"spec/has_and_belongs_to_many_with_deferred_save_spec.rb",
|
41
|
+
"spec/spec_helper.rb",
|
42
|
+
"spec/db/schema.rb"
|
43
|
+
]
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
47
|
+
s.specification_version = 3
|
48
|
+
|
49
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
50
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
54
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
55
|
+
end
|
56
|
+
else
|
57
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
58
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'lib/has_and_belongs_to_many_with_deferred_save')
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# To do: make it work to call this twice in a class. Currently that probably wouldn't work, because it would try to alias methods to existing names...
|
2
|
+
# Note: before_save must be defined *before* including this module, not after.
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module Associations
|
6
|
+
module ClassMethods
|
7
|
+
# Instructions:
|
8
|
+
#
|
9
|
+
# Replace your existing call to has_and_belongs_to_many with has_and_belongs_to_many_with_deferred_save.
|
10
|
+
#
|
11
|
+
# Then add a validation method that adds an error if there is something wrong with the (unsaved) collection. This will prevent it from being saved if there are any errors.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# def validate
|
16
|
+
# if people.size > maximum_occupancy
|
17
|
+
# errors.add :people, "There are too many people in this room"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
def has_and_belongs_to_many_with_deferred_save(*args)
|
21
|
+
has_and_belongs_to_many *args
|
22
|
+
collection_name = args[0].to_s
|
23
|
+
collection_singular_ids = collection_name.singularize + "_ids"
|
24
|
+
|
25
|
+
# this will delete all the assocation into the join table after obj.destroy
|
26
|
+
after_destroy { |record| record.save }
|
27
|
+
|
28
|
+
attr_accessor :"unsaved_#{collection_name}"
|
29
|
+
attr_accessor :"use_original_collection_reader_behavior_for_#{collection_name}"
|
30
|
+
|
31
|
+
define_method "#{collection_name}_with_deferred_save=" do |collection|
|
32
|
+
#puts "has_and_belongs_to_many_with_deferred_save: #{collection_name} = #{collection.collect(&:id).join(',')}"
|
33
|
+
self.send "unsaved_#{collection_name}=", collection
|
34
|
+
end
|
35
|
+
|
36
|
+
define_method "#{collection_name}_with_deferred_save" do |*args|
|
37
|
+
if self.send("use_original_collection_reader_behavior_for_#{collection_name}")
|
38
|
+
self.send("#{collection_name}_without_deferred_save")
|
39
|
+
else
|
40
|
+
if self.send("unsaved_#{collection_name}").nil?
|
41
|
+
send("initialize_unsaved_#{collection_name}", *args)
|
42
|
+
end
|
43
|
+
self.send("unsaved_#{collection_name}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
alias_method_chain :"#{collection_name}=", 'deferred_save'
|
48
|
+
alias_method_chain :"#{collection_name}", 'deferred_save'
|
49
|
+
|
50
|
+
define_method "#{collection_singular_ids}_with_deferred_save" do |*args|
|
51
|
+
if self.send("use_original_collection_reader_behavior_for_#{collection_name}")
|
52
|
+
self.send("#{collection_singular_ids}_without_deferred_save")
|
53
|
+
else
|
54
|
+
if self.send("unsaved_#{collection_name}").nil?
|
55
|
+
send("initialize_unsaved_#{collection_name}", *args)
|
56
|
+
end
|
57
|
+
self.send("unsaved_#{collection_name}").map { |e| e[:id] }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
alias_method_chain :"#{collection_singular_ids}", 'deferred_save'
|
62
|
+
|
63
|
+
|
64
|
+
define_method "before_save_with_deferred_save_for_#{collection_name}" do
|
65
|
+
# Question: Why do we need this @use_original_collection_reader_behavior stuff?
|
66
|
+
# Answer: Because AssociationCollection#replace(other_array) performs a diff between current_array and other_array and deletes/adds only
|
67
|
+
# records that have changed.
|
68
|
+
# In order to perform that diff, it needs to figure out what "current_array" is, so it calls our collection_with_deferred_save, not
|
69
|
+
# knowing that we've changed its behavior. It expects that method to return the elements of that collection that are in the *database*
|
70
|
+
# (the original behavior), so we have to provide that behavior... If we didn't provide it, it would end up trying to take the diff of
|
71
|
+
# two identical collections so nothing would ever get saved.
|
72
|
+
# But we only want the old behavior in this case -- most of the time we want the *new* behavior -- so we use
|
73
|
+
# @use_original_collection_reader_behavior as a switch.
|
74
|
+
|
75
|
+
if self.respond_to? :"before_save_without_deferred_save_for_#{collection_name}"
|
76
|
+
self.send("before_save_without_deferred_save_for_#{collection_name}")
|
77
|
+
end
|
78
|
+
|
79
|
+
self.send "use_original_collection_reader_behavior_for_#{collection_name}=", true
|
80
|
+
if self.send("unsaved_#{collection_name}").nil?
|
81
|
+
send("initialize_unsaved_#{collection_name}", *args)
|
82
|
+
end
|
83
|
+
self.send "#{collection_name}_without_deferred_save=", self.send("unsaved_#{collection_name}")
|
84
|
+
# /\ This is where the actual save occurs.
|
85
|
+
self.send "use_original_collection_reader_behavior_for_#{collection_name}=", false
|
86
|
+
|
87
|
+
true
|
88
|
+
end
|
89
|
+
alias_method_chain :"before_save", "deferred_save_for_#{collection_name}"
|
90
|
+
|
91
|
+
|
92
|
+
define_method "reload_with_deferred_save_for_#{collection_name}" do
|
93
|
+
# Reload from the *database*, discarding any unsaved changes.
|
94
|
+
returning self.send("reload_without_deferred_save_for_#{collection_name}") do
|
95
|
+
self.send "unsaved_#{collection_name}=", nil
|
96
|
+
# /\ If we didn't do this, then when we called reload, it would still have the same (possibly invalid) value of
|
97
|
+
# unsaved_collection that it had before the reload.
|
98
|
+
end
|
99
|
+
end
|
100
|
+
alias_method_chain :"reload", "deferred_save_for_#{collection_name}"
|
101
|
+
|
102
|
+
|
103
|
+
define_method "initialize_unsaved_#{collection_name}" do |*args|
|
104
|
+
#puts "Initialized to #{self.send("#{collection_name}_without_deferred_save").clone.inspect}"
|
105
|
+
self.send "unsaved_#{collection_name}=", self.send("#{collection_name}_without_deferred_save", *args).clone
|
106
|
+
# /\ We initialize it to collection_without_deferred_save in case they just loaded the object from the
|
107
|
+
# database, in which case we want unsaved_collection to start out with the "saved collection".
|
108
|
+
# If they just constructed a *new* object, this will still work, because self.collection_without_deferred_save.clone
|
109
|
+
# will return a new HasAndBelongsToManyAssociation (which acts like an empty array, []).
|
110
|
+
# Important: If we don't use clone, then it does an assignment by reference and any changes to unsaved_collection
|
111
|
+
# will also change *collection_without_deferred_save*! (Not what we want! Would result in us saving things
|
112
|
+
# immediately, which is exactly what we're trying to avoid.)
|
113
|
+
|
114
|
+
# trick collection_name.include?(obj)
|
115
|
+
# If you use a collection of SignelTableInheritance and didn't :select 'type' the
|
116
|
+
# include? method will not find any subclassed object.
|
117
|
+
class << eval("@unsaved_#{collection_name}")
|
118
|
+
def include_with_deferred_save?(obj)
|
119
|
+
if self.find { |itm| itm == obj || (itm[:id] == obj[:id] && obj.is_a?(itm.class) ) }
|
120
|
+
return true
|
121
|
+
else
|
122
|
+
return false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
alias_method_chain :include?, 'deferred_save'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
private :"initialize_unsaved_#{collection_name}"
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/spec/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
sqlite3:
|
2
|
+
adapter: sqlite3
|
3
|
+
database: test.sqlite3.db
|
4
|
+
|
5
|
+
sqlite3mem:
|
6
|
+
adapter: sqlite3
|
7
|
+
database: ":memory:"
|
8
|
+
|
9
|
+
postgresql:
|
10
|
+
adapter: postgresql
|
11
|
+
username: postgres
|
12
|
+
password: postgres
|
13
|
+
database: has_and_belongs_to_many_with_deferred_save_test
|
14
|
+
min_messages: ERROR
|
15
|
+
|
16
|
+
mysql:
|
17
|
+
adapter: mysql
|
18
|
+
host: localhost
|
19
|
+
username: root
|
20
|
+
password:
|
21
|
+
database: has_and_belongs_to_many_with_deferred_save_test
|
data/spec/db/schema.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file is autogenerated. Instead of editing this file, please use the
|
2
|
+
# migrations feature of ActiveRecord to incrementally modify your database, and
|
3
|
+
# then regenerate this schema definition.
|
4
|
+
|
5
|
+
ActiveRecord::Schema.define(:version => 1) do
|
6
|
+
|
7
|
+
create_table "people", :force => true do |t|
|
8
|
+
t.column "name", :string
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table "people_rooms", :id => false, :force => true do |t|
|
12
|
+
t.column "person_id", :integer
|
13
|
+
t.column "room_id", :integer
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table "rooms", :force => true do |t|
|
17
|
+
t.column "name", :string
|
18
|
+
t.column "maximum_occupancy", :integer
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require 'has_and_belongs_to_many_with_deferred_save'
|
3
|
+
|
4
|
+
describe "has_and_belongs_to_many_with_deferred_save" do
|
5
|
+
describe "room maximum_occupancy" do
|
6
|
+
before :all do
|
7
|
+
@people = []
|
8
|
+
@people << Person.create
|
9
|
+
@people << Person.create
|
10
|
+
@people << Person.create
|
11
|
+
@room = Room.new(:maximum_occupancy => 2)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "initial checks" do
|
15
|
+
Room .count.should == 0
|
16
|
+
Person.count.should == 3
|
17
|
+
|
18
|
+
@room.people.should == []
|
19
|
+
@room.people_without_deferred_save.should == []
|
20
|
+
@room.people_without_deferred_save.object_id.should_not ==
|
21
|
+
@room.unsaved_people.object_id
|
22
|
+
end
|
23
|
+
|
24
|
+
it "after adding people to room, it should not have saved anything to the database" do
|
25
|
+
@room.people << @people[0]
|
26
|
+
@room.people << @people[1]
|
27
|
+
|
28
|
+
# Still not saved to the association table!
|
29
|
+
Room.count_by_sql("select count(*) from people_rooms").should == 0
|
30
|
+
@room.people_without_deferred_save.size. should == 0
|
31
|
+
end
|
32
|
+
|
33
|
+
it "but room.people.size should still report the current size of 2" do
|
34
|
+
@room.people.size.should == 2 # 2 because this looks at unsaved_people and not at the database
|
35
|
+
end
|
36
|
+
|
37
|
+
it "after saving the model, the association should be saved in the join table" do
|
38
|
+
@room.save # Only here is it actually saved to the association table!
|
39
|
+
@room.errors.full_messages.should == []
|
40
|
+
Room.count_by_sql("select count(*) from people_rooms").should == 2
|
41
|
+
@room.people.size. should == 2
|
42
|
+
@room.people_without_deferred_save.size. should == 2
|
43
|
+
end
|
44
|
+
|
45
|
+
it "when we try to add a 3rd person, it should add a validation error to the errors object like any other validation error" do
|
46
|
+
lambda { @room.people << @people[2] }.should_not raise_error
|
47
|
+
@room.people.size. should == 3
|
48
|
+
|
49
|
+
Room.count_by_sql("select count(*) from people_rooms").should == 2
|
50
|
+
@room.valid?
|
51
|
+
@room.errors.on(:people).should == "There are too many people in this room"
|
52
|
+
@room.people.size. should == 3 # Just like with normal attributes that fail validation... the attribute still contains the invalid data but we refuse to save until it is changed to something that is *valid*.
|
53
|
+
end
|
54
|
+
|
55
|
+
it "when we try to save, it should fail, because room.people is still invaild" do
|
56
|
+
@room.save.should == false
|
57
|
+
Room.count_by_sql("select count(*) from people_rooms").should == 2 # It's still not there, because it didn't pass the validation.
|
58
|
+
@room.errors.on(:people).should == "There are too many people in this room"
|
59
|
+
@room.people.size. should == 3
|
60
|
+
end
|
61
|
+
|
62
|
+
it "when we reload, it should go back to only having 2 people in the room" do
|
63
|
+
@room.reload
|
64
|
+
@room.people.size. should == 2
|
65
|
+
@room.people_without_deferred_save.size. should == 2
|
66
|
+
end
|
67
|
+
|
68
|
+
it "if they try to go around our accessors and use the original accessors, then (and only then) will the exception be raised in before_adding_person..." do
|
69
|
+
lambda do
|
70
|
+
@room.people_without_deferred_save << @people[2]
|
71
|
+
end.should raise_error(RuntimeError)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/spec/models/room.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class Room < ActiveRecord::Base
|
2
|
+
has_and_belongs_to_many_with_deferred_save :people, :before_add => :before_adding_person
|
3
|
+
|
4
|
+
def validate
|
5
|
+
if people.size > maximum_occupancy
|
6
|
+
errors.add :people, "There are too many people in this room"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Just in case they try to bypass our new accessor and call people_without_deferred_save directly...
|
11
|
+
# (This should never be necessary; it is for demonstration purposes only...)
|
12
|
+
def before_adding_person(person)
|
13
|
+
if self.people_without_deferred_save.size + [person].size > maximum_occupancy
|
14
|
+
raise "There are too many people in this room"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
plugin_test_dir = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
#require 'multi_rails_init'
|
5
|
+
require 'active_record'
|
6
|
+
# Workaround for https://rails.lighthouseapp.com/projects/8994/tickets/2577-when-using-activerecordassociations-outside-of-rails-a-nameerror-is-thrown
|
7
|
+
ActiveRecord::ActiveRecordError
|
8
|
+
|
9
|
+
require plugin_test_dir + '/../init.rb'
|
10
|
+
|
11
|
+
ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/test.log")
|
12
|
+
|
13
|
+
ActiveRecord::Base.configurations = YAML::load(IO.read(plugin_test_dir + "/db/database.yml"))
|
14
|
+
ActiveRecord::Base.establish_connection(ENV["DB"] || "sqlite3")
|
15
|
+
ActiveRecord::Migration.verbose = false
|
16
|
+
load(File.join(plugin_test_dir, "db", "schema.rb"))
|
17
|
+
|
18
|
+
Dir["#{plugin_test_dir}/models/*.rb"].each {|file| require file }
|
19
|
+
|
20
|
+
Spec::Runner.configure do |config|
|
21
|
+
config.before do
|
22
|
+
end
|
23
|
+
end
|
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: has_and_belongs_to_many_with_deferred_save
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tyler Rick
|
8
|
+
- Alessio Caiazza
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2010-02-25 00:00:00 -08:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: activerecord
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rspec
|
28
|
+
type: :development
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
version:
|
36
|
+
description:
|
37
|
+
email: github.com@tylerrick.com
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- Rakefile
|
47
|
+
- Readme.markdown
|
48
|
+
- VERSION
|
49
|
+
- has_and_belongs_to_many_with_deferred_save.gemspec
|
50
|
+
- init.rb
|
51
|
+
- install.rb
|
52
|
+
- lib/has_and_belongs_to_many_with_deferred_save.rb
|
53
|
+
- spec/.gitignore
|
54
|
+
- spec/db/database.yml
|
55
|
+
- spec/db/schema.rb
|
56
|
+
- spec/has_and_belongs_to_many_with_deferred_save_spec.rb
|
57
|
+
- spec/models/person.rb
|
58
|
+
- spec/models/room.rb
|
59
|
+
- spec/spec_helper.rb
|
60
|
+
- uninstall.rb
|
61
|
+
has_rdoc: true
|
62
|
+
homepage: http://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save
|
63
|
+
licenses: []
|
64
|
+
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options:
|
67
|
+
- --charset=UTF-8
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "0"
|
81
|
+
version:
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.3.5
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Make ActiveRecord defer/postpone saving the records you add to an habtm (has_and_belongs_to_many) association until you call model.save, allowing validation in the style of normal attributes.
|
89
|
+
test_files:
|
90
|
+
- spec/models/room.rb
|
91
|
+
- spec/models/person.rb
|
92
|
+
- spec/has_and_belongs_to_many_with_deferred_save_spec.rb
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
- spec/db/schema.rb
|