# HG changeset patch # User Tyler Rick # Date 1267143338 28800 # Node ID 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 # Parent a0ab430243c79b1311925cef5745c2becdbdfb03 Rewrote tests in rspec with fewer dependencies and ensured they were passing. Changed the Readme to markdown format and updated it slightly. Added a gemspec. diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 .gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gitignore Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,1 @@ +pkg diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 Rakefile --- a/Rakefile Thu Feb 25 10:11:12 2010 -0800 +++ b/Rakefile Thu Feb 25 16:15:38 2010 -0800 @@ -1,22 +1,23 @@ -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' - -desc 'Default: run unit tests.' -task :default => :test - -desc 'Test the has_and_belongs_to_many_with_deferred_save plugin.' -Rake::TestTask.new(:test) do |t| - t.libs << 'lib' - t.pattern = 'test/**/*_test.rb' - t.verbose = true +task :default do |t| + options = "--colour" + files = FileList['spec/**/*_spec.rb'].map{|f| f.sub(%r{^spec/},'') } + exit system("cd spec && spec #{options} #{files}") ? 0 : 1 end -desc 'Generate documentation for the has_and_belongs_to_many_with_deferred_save plugin.' -Rake::RDocTask.new(:rdoc) do |rdoc| - rdoc.rdoc_dir = 'rdoc' - rdoc.title = 'HasAndBelongsToManyWithDeferredSave' - rdoc.options << '--line-numbers' << '--inline-source' - rdoc.rdoc_files.include('README') - rdoc.rdoc_files.include('lib/**/*.rb') +begin + require 'jeweler' + project_name = 'has_and_belongs_to_many_with_deferred_save' + Jeweler::Tasks.new do |gem| + gem.name = project_name + 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." + gem.email = "github.com@tylerrick.com" + gem.homepage = "http://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save" + gem.authors = ["Tyler Rick", "Alessio Caiazza"] + gem.add_dependency('activerecord') + gem.add_development_dependency('rspec') + end + + Jeweler::GemcutterTasks.new +rescue LoadError + puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 Readme --- a/Readme Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -=has_and_belongs_to_many_with_deferred_save - -==Example - -Room has_and_belongs_to_many_with_deferred_save People - -Lets 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... - -This isn't possible using the standard has_and_belongs_to_many due to these two problems: - -1. When I do the assignment to my collection (room.people = whatever), it immediately saves it in my join table (people_rooms) rather than waiting until I call room.save. - -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... - -We don't want to raise an exception when the user violates my 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. - -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. - -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.). - -The unsaved collection is automatically saved when you call save on the model. - -==How to install - -./script/plugin install http://habtm-with-deferred-save.googlecode.com/svn/trunk/has_and_belongs_to_many_with_deferred_save - -==Project home - -http://code.google.com/p/habtm-with-deferred-save/ - -==History - -It started as a post to the Rails mailing list asking how to validate a has_and_belongs_to_many collection/association (see http://www.ruby-forum.com/topic/81095). - -==Copyright - -2007 (c) QualitySmith, Inc. diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 Readme.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Readme.markdown Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,60 @@ +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. + +How to install +============== + +As Rails plugin: + ./script/plugin install git://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save.git + +As a gem: + sudo gem install has_and_belongs_to_many_with_deferred_save + +Usage +===== + + class Room < ActiveRecord::Base + has_and_belongs_to_many_with_deferred_save :people + end + +Motivation +========== + +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... + +This isn't possible using the standard has_and_belongs_to_many due to these two problems: + +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. + +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... + +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. + +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. + +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.). + +The unsaved collection is automatically saved when you call save on the model. + + +Compatibility +============= + +Tested with Rails 2.3.4. + +Bugs +==== + +http://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save/issues + +History +======= + +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. + +License +======= + +This plugin is licensed under the BSD license. + +2010 (c) Contributors +2007 (c) QualitySmith, Inc. diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 VERSION --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VERSION Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,1 @@ +0.1.0 diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 has_and_belongs_to_many_with_deferred_save.gemspec --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/has_and_belongs_to_many_with_deferred_save.gemspec Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,61 @@ +# Generated by jeweler +# DO NOT EDIT THIS FILE DIRECTLY +# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = %q{has_and_belongs_to_many_with_deferred_save} + s.version = "0.1.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = ["Tyler Rick", "Alessio Caiazza"] + s.date = %q{2010-02-25} + s.email = %q{github.com@tylerrick.com} + s.files = [ + ".gitignore", + "Rakefile", + "Readme.markdown", + "VERSION", + "has_and_belongs_to_many_with_deferred_save.gemspec", + "init.rb", + "install.rb", + "lib/has_and_belongs_to_many_with_deferred_save.rb", + "spec/.gitignore", + "spec/db/database.yml", + "spec/db/schema.rb", + "spec/has_and_belongs_to_many_with_deferred_save_spec.rb", + "spec/models/person.rb", + "spec/models/room.rb", + "spec/spec_helper.rb", + "uninstall.rb" + ] + s.homepage = %q{http://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save} + s.rdoc_options = ["--charset=UTF-8"] + s.require_paths = ["lib"] + s.rubygems_version = %q{1.3.5} + 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.} + s.test_files = [ + "spec/models/room.rb", + "spec/models/person.rb", + "spec/has_and_belongs_to_many_with_deferred_save_spec.rb", + "spec/spec_helper.rb", + "spec/db/schema.rb" + ] + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 3 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + else + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + end + else + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + end +end + diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 spec/.gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/.gitignore Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,2 @@ +*.log +*.db diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 spec/db/database.yml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/db/database.yml Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,21 @@ +sqlite3: + adapter: sqlite3 + database: test.sqlite3.db + +sqlite3mem: + adapter: sqlite3 + database: ":memory:" + +postgresql: + adapter: postgresql + username: postgres + password: postgres + database: has_and_belongs_to_many_with_deferred_save_test + min_messages: ERROR + +mysql: + adapter: mysql + host: localhost + username: root + password: + database: has_and_belongs_to_many_with_deferred_save_test diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 spec/db/schema.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/db/schema.rb Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,21 @@ +# This file is autogenerated. Instead of editing this file, please use the +# migrations feature of ActiveRecord to incrementally modify your database, and +# then regenerate this schema definition. + +ActiveRecord::Schema.define(:version => 1) do + + create_table "people", :force => true do |t| + t.column "name", :string + end + + create_table "people_rooms", :id => false, :force => true do |t| + t.column "person_id", :integer + t.column "room_id", :integer + end + + create_table "rooms", :force => true do |t| + t.column "name", :string + t.column "maximum_occupancy", :integer + end + +end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 spec/has_and_belongs_to_many_with_deferred_save_spec.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/has_and_belongs_to_many_with_deferred_save_spec.rb Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,74 @@ +require "spec_helper" +require 'has_and_belongs_to_many_with_deferred_save' + +describe "has_and_belongs_to_many_with_deferred_save" do + describe "room maximum_occupancy" do + before :all do + @people = [] + @people << Person.create + @people << Person.create + @people << Person.create + @room = Room.new(:maximum_occupancy => 2) + end + + it "initial checks" do + Room .count.should == 0 + Person.count.should == 3 + + @room.people.should == [] + @room.people_without_deferred_save.should == [] + @room.people_without_deferred_save.object_id.should_not == + @room.unsaved_people.object_id + end + + it "after adding people to room, it should not have saved anything to the database" do + @room.people << @people[0] + @room.people << @people[1] + + # Still not saved to the association table! + Room.count_by_sql("select count(*) from people_rooms").should == 0 + @room.people_without_deferred_save.size. should == 0 + end + + it "but room.people.size should still report the current size of 2" do + @room.people.size.should == 2 # 2 because this looks at unsaved_people and not at the database + end + + it "after saving the model, the association should be saved in the join table" do + @room.save # Only here is it actually saved to the association table! + @room.errors.full_messages.should == [] + Room.count_by_sql("select count(*) from people_rooms").should == 2 + @room.people.size. should == 2 + @room.people_without_deferred_save.size. should == 2 + end + + 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 + lambda { @room.people << @people[2] }.should_not raise_error + @room.people.size. should == 3 + + Room.count_by_sql("select count(*) from people_rooms").should == 2 + @room.valid? + @room.errors.on(:people).should == "There are too many people in this room" + @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*. + end + + it "when we try to save, it should fail, because room.people is still invaild" do + @room.save.should == false + Room.count_by_sql("select count(*) from people_rooms").should == 2 # It's still not there, because it didn't pass the validation. + @room.errors.on(:people).should == "There are too many people in this room" + @room.people.size. should == 3 + end + + it "when we reload, it should go back to only having 2 people in the room" do + @room.reload + @room.people.size. should == 2 + @room.people_without_deferred_save.size. should == 2 + end + + 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 + lambda do + @room.people_without_deferred_save << @people[2] + end.should raise_error(RuntimeError) + end + end +end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 spec/models/person.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/models/person.rb Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,3 @@ +class Person < ActiveRecord::Base + has_and_belongs_to_many :room +end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 spec/models/room.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/models/room.rb Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,17 @@ +class Room < ActiveRecord::Base + has_and_belongs_to_many_with_deferred_save :people, :before_add => :before_adding_person + + def validate + if people.size > maximum_occupancy + errors.add :people, "There are too many people in this room" + end + end + + # Just in case they try to bypass our new accessor and call people_without_deferred_save directly... + # (This should never be necessary; it is for demonstration purposes only...) + def before_adding_person(person) + if self.people_without_deferred_save.size + [person].size > maximum_occupancy + raise "There are too many people in this room" + end + end +end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 spec/spec_helper.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/spec_helper.rb Thu Feb 25 16:15:38 2010 -0800 @@ -0,0 +1,23 @@ +$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') +plugin_test_dir = File.dirname(__FILE__) + +#require 'multi_rails_init' +require 'active_record' +# Workaround for https://rails.lighthouseapp.com/projects/8994/tickets/2577-when-using-activerecordassociations-outside-of-rails-a-nameerror-is-thrown +ActiveRecord::ActiveRecordError + +require plugin_test_dir + '/../init.rb' + +ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/test.log") + +ActiveRecord::Base.configurations = YAML::load(IO.read(plugin_test_dir + "/db/database.yml")) +ActiveRecord::Base.establish_connection(ENV["DB"] || "sqlite3") +ActiveRecord::Migration.verbose = false +load(File.join(plugin_test_dir, "db", "schema.rb")) + +Dir["#{plugin_test_dir}/models/*.rb"].each {|file| require file } + +Spec::Runner.configure do |config| + config.before do + end +end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/Rakefile --- a/test/rails_root/Rakefile Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -# Add your own tasks in files placed in lib/tasks ending in .rake, -# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. - -require(File.join(File.dirname(__FILE__), 'config', 'boot')) - -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' - -require 'tasks/rails' diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/app/controllers/application.rb --- a/test/rails_root/app/controllers/application.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -# Filters added to this controller apply to all controllers in the application. -# Likewise, all the methods added will be available for all controllers. - -class ApplicationController < ActionController::Base - # Pick a unique cookie name to distinguish our session data from others' - session :session_key => '_rails_root_session_id' -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/app/helpers/application_helper.rb --- a/test/rails_root/app/helpers/application_helper.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -# Methods added to this helper will be available to all templates in the application. -module ApplicationHelper -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/app/models/person.rb --- a/test/rails_root/app/models/person.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -class Person < ActiveRecord::Base - has_and_belongs_to_many :room -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/app/models/room.rb --- a/test/rails_root/app/models/room.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -class Room < ActiveRecord::Base - has_and_belongs_to_many_with_deferred_save :people, :before_add => :before_adding_person - - def validate - if people.size > maximum_occupancy - errors.add :people, "There are too many people in this room" - end - end - - # Just in case they try to bypass our new accessor and call people_without_deferred_save directly... - # (This should never be necessary; it is for demonstration purposes only...) - def before_adding_person(person) - if self.people_without_deferred_save.size + [person].size > maximum_occupancy - raise "There are too many people in this room" - end - end -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/config/boot.rb --- a/test/rails_root/config/boot.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -# Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb - -unless defined?(RAILS_ROOT) - root_path = File.join(File.dirname(__FILE__), '..') - - unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ - require 'pathname' - root_path = Pathname.new(root_path).cleanpath(true).to_s - end - - RAILS_ROOT = root_path -end - -unless defined?(Rails::Initializer) - if File.directory?("#{RAILS_ROOT}/vendor/rails") - require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer" - else - require 'rubygems' - - environment_without_comments = IO.readlines(File.dirname(__FILE__) + '/environment.rb').reject { |l| l =~ /^#/ }.join - environment_without_comments =~ /[^#]RAILS_GEM_VERSION = '([\d.]+)'/ - rails_gem_version = $1 - - if version = defined?(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION : rails_gem_version - # Asking for 1.1.6 will give you 1.1.6.5206, if available -- makes it easier to use beta gems - rails_gem = Gem.cache.search('rails', "~>#{version}.0").sort_by { |g| g.version.version }.last - - if rails_gem - gem "rails", "=#{rails_gem.version.version}" - require rails_gem.full_gem_path + '/lib/initializer' - else - STDERR.puts %(Cannot find gem for Rails ~>#{version}.0: - Install the missing gem with 'gem install -v=#{version} rails', or - change environment.rb to define RAILS_GEM_VERSION with your desired version. - ) - exit 1 - end - else - gem "rails" - require 'initializer' - end - end - - Rails::Initializer.run(:set_load_path) -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/config/database.yml --- a/test/rails_root/config/database.yml Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -development: - adapter: sqlite3 - database: db/development.sqlite - -test: - adapter: sqlite3 - database: db/test.sqlite - -production: - adapter: sqlite3 - database: db/production.sqlite diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/config/environment.rb --- a/test/rails_root/config/environment.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -# Be sure to restart your web server when you modify this file. - -# Uncomment below to force Rails into production mode when -# you don't control web/app server and can't set it the proper way -# ENV['RAILS_ENV'] ||= 'production' - -# Specifies gem version of Rails to use when vendor/rails is not present -RAILS_GEM_VERSION = '1.2.3' unless defined? RAILS_GEM_VERSION - -# Bootstrap the Rails environment, frameworks, and default configuration -require File.join(File.dirname(__FILE__), 'boot') - -Rails::Initializer.run do |config| - # Settings in config/environments/* take precedence over those specified here - - # Skip frameworks you're not going to use (only works if using vendor/rails) - # config.frameworks -= [ :action_web_service, :action_mailer ] - - # Only load the plugins named here, by default all plugins in vendor/plugins are loaded - # config.plugins = %W( exception_notification ssl_requirement ) - - # Add additional load paths for your own custom dirs - # config.load_paths += %W( #{RAILS_ROOT}/extras ) - - # Force all environments to use the same logger level - # (by default production uses :info, the others :debug) - # config.log_level = :debug - - # Use the database for sessions instead of the file system - # (create the session table with 'rake db:sessions:create') - # config.action_controller.session_store = :active_record_store - - # Use SQL instead of Active Record's schema dumper when creating the test database. - # This is necessary if your schema can't be completely dumped by the schema dumper, - # like if you have constraints or database-specific column types - # config.active_record.schema_format = :sql - - # Activate observers that should always be running - # config.active_record.observers = :cacher, :garbage_collector - - # Make Active Record use UTC-base instead of local time - # config.active_record.default_timezone = :utc - - # See Rails::Configuration for more options -end - -# Add new inflection rules using the following format -# (all these examples are active by default): -# Inflector.inflections do |inflect| -# inflect.plural /^(ox)$/i, '\1en' -# inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' -# inflect.uncountable %w( fish sheep ) -# end - -# Add new mime types for use in respond_to blocks: -# Mime::Type.register "text/richtext", :rtf -# Mime::Type.register "application/x-mobile", :mobile - -# Include your application configuration below \ No newline at end of file diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/config/environments/development.rb --- a/test/rails_root/config/environments/development.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -# Settings specified here will take precedence over those in config/environment.rb - -# In the development environment your application's code is reloaded on -# every request. This slows down response time but is perfect for development -# since you don't have to restart the webserver when you make code changes. -config.cache_classes = false - -# Log error messages when you accidentally call methods on nil. -config.whiny_nils = true - -# Enable the breakpoint server that script/breakpointer connects to -config.breakpoint_server = true - -# Show full error reports and disable caching -config.action_controller.consider_all_requests_local = true -config.action_controller.perform_caching = false -config.action_view.cache_template_extensions = false -config.action_view.debug_rjs = true - -# Don't care if the mailer can't send -config.action_mailer.raise_delivery_errors = false diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/config/environments/production.rb --- a/test/rails_root/config/environments/production.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -# Settings specified here will take precedence over those in config/environment.rb - -# The production environment is meant for finished, "live" apps. -# Code is not reloaded between requests -config.cache_classes = true - -# Use a different logger for distributed setups -# config.logger = SyslogLogger.new - -# Full error reports are disabled and caching is turned on -config.action_controller.consider_all_requests_local = false -config.action_controller.perform_caching = true - -# Enable serving of images, stylesheets, and javascripts from an asset server -# config.action_controller.asset_host = "http://assets.example.com" - -# Disable delivery errors, bad email addresses will be ignored -# config.action_mailer.raise_delivery_errors = false diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/config/environments/test.rb --- a/test/rails_root/config/environments/test.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -# Settings specified here will take precedence over those in config/environment.rb - -# The test environment is used exclusively to run your application's -# test suite. You never need to work with it otherwise. Remember that -# your test database is "scratch space" for the test suite and is wiped -# and recreated between test runs. Don't rely on the data there! -config.cache_classes = true - -# Log error messages when you accidentally call methods on nil. -config.whiny_nils = true - -# Show full error reports and disable caching -config.action_controller.consider_all_requests_local = true -config.action_controller.perform_caching = false - -# Tell ActionMailer not to deliver emails to the real world. -# The :test delivery method accumulates sent emails in the -# ActionMailer::Base.deliveries array. -config.action_mailer.delivery_method = :test \ No newline at end of file diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/config/routes.rb --- a/test/rails_root/config/routes.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -ActionController::Routing::Routes.draw do |map| - # The priority is based upon order of creation: first created -> highest priority. - - # Sample of regular route: - # map.connect 'products/:id', :controller => 'catalog', :action => 'view' - # Keep in mind you can assign values other than :controller and :action - - # Sample of named route: - # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase' - # This route can be invoked with purchase_url(:id => product.id) - - # You can have the root of your site routed by hooking up '' - # -- just remember to delete public/index.html. - # map.connect '', :controller => "welcome" - - # Allow downloading Web Service WSDL as a file with an extension - # instead of a file named 'wsdl' - map.connect ':controller/service.wsdl', :action => 'wsdl' - - # Install the default route as the lowest priority. - map.connect ':controller/:action/:id.:format' - map.connect ':controller/:action/:id' -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/db/migrate/001_create_rooms_and_people.rb --- a/test/rails_root/db/migrate/001_create_rooms_and_people.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -class CreateRoomsAndPeople < ActiveRecord::Migration - def self.up - create_table :people do |t| - t.column :name, :string - end - create_table :rooms do |t| - t.column :name, :string - t.column :maximum_occupancy, :integer - end - create_table :people_rooms do |t| - t.column :person_id, :integer - t.column :room_id, :integer - end - end - - def self.down - drop_table :people - drop_table :rooms - drop_table :people_rooms - end -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/db/schema.rb --- a/test/rails_root/db/schema.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -# This file is autogenerated. Instead of editing this file, please use the -# migrations feature of ActiveRecord to incrementally modify your database, and -# then regenerate this schema definition. - -ActiveRecord::Schema.define(:version => 1) do - - create_table "people", :force => true do |t| - t.column "name", :string - end - - create_table "people_rooms", :force => true do |t| - t.column "person_id", :integer - t.column "room_id", :integer - end - - create_table "rooms", :force => true do |t| - t.column "name", :string - t.column "maximum_occupancy", :integer - end - -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/log/development.log --- a/test/rails_root/log/development.log Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ - SQL (0.000761) SELECT * FROM schema_info - SQL (0.000436) SELECT name FROM sqlite_master WHERE type = 'table' - SQL (0.000085) PRAGMA index_list(people) - SQL (0.000082) PRAGMA index_list(people_rooms) - SQL (0.000083) PRAGMA index_list(rooms) - SQL (0.000000) SQLite3::SQLException: no such table: people: DROP TABLE people - SQL (0.029406) CREATE TABLE people ("id" INTEGER PRIMARY KEY NOT NULL, "name" varchar(255) DEFAULT NULL)  - SQL (0.000000) SQLite3::SQLException: no such table: people_rooms: DROP TABLE people_rooms - SQL (0.002897) CREATE TABLE people_rooms ("id" INTEGER PRIMARY KEY NOT NULL, "person_id" integer DEFAULT NULL, "room_id" integer DEFAULT NULL)  - SQL (0.000000) SQLite3::SQLException: no such table: rooms: DROP TABLE rooms - SQL (0.002889) CREATE TABLE rooms ("id" INTEGER PRIMARY KEY NOT NULL, "name" varchar(255) DEFAULT NULL, "maximum_occupancy" integer DEFAULT NULL)  - SQL (0.003147) CREATE TABLE schema_info (version integer) - SQL (0.004004) INSERT INTO schema_info (version) VALUES(0) - SQL (0.002317) UPDATE schema_info SET version = 1 diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/log/test.log --- a/test/rails_root/log/test.log Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ - Person Load (0.000348) SELECT * FROM people WHERE (people."id" = 1)  - Person Load (0.000261) SELECT * FROM people WHERE (people."id" = 2)  - Room Count (0.000260) select count(*) from people_rooms - SQL (0.000425) INSERT INTO rooms ("name", "maximum_occupancy") VALUES(NULL, 2) - SQL (0.000256) INSERT INTO people_rooms ("id", "room_id", "person_id") VALUES (1, 1, 1) - SQL (0.000152) INSERT INTO people_rooms ("id", "room_id", "person_id") VALUES (2, 1, 2) - Room Count (0.000213) select count(*) from people_rooms - Person Load (0.000224) SELECT * FROM people WHERE (people."id" = 3)  - Room Count (0.000183) select count(*) from people_rooms - Room Count (0.000185) select count(*) from people_rooms - Room Load (0.000237) SELECT * FROM rooms WHERE (rooms."id" = 1)  - Person Load (0.000560) SELECT * FROM people INNER JOIN people_rooms ON people.id = people_rooms.person_id WHERE (people_rooms.room_id = 1 )  - Room Count (0.000185) select count(*) from people_rooms diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/test/fixtures/people.yml --- a/test/rails_root/test/fixtures/people.yml Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html -person1: - id: 1 -person2: - id: 2 -person3: - id: 3 diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/test/fixtures/rooms.yml --- a/test/rails_root/test/fixtures/rooms.yml Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html -first: - id: 1 -another: - id: 2 diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/test/test_helper.rb --- a/test/rails_root/test/test_helper.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -ENV["RAILS_ENV"] = "test" -require File.expand_path(File.dirname(__FILE__) + "/../config/environment") -require 'test_help' - -class Test::Unit::TestCase - # Transactional fixtures accelerate your tests by wrapping each test method - # in a transaction that's rolled back on completion. This ensures that the - # test database remains unchanged so your fixtures don't have to be reloaded - # between every test method. Fewer database queries means faster tests. - # - # Read Mike Clark's excellent walkthrough at - # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting - # - # Every Active Record database supports transactions except MyISAM tables - # in MySQL. Turn off transactional fixtures in this case; however, if you - # don't care one way or the other, switching from MyISAM to InnoDB tables - # is recommended. - self.use_transactional_fixtures = true - - # Instantiated fixtures are slow, but give you @david where otherwise you - # would need people(:david). If you don't want to migrate your existing - # test cases which use the @david style and don't mind the speed hit (each - # instantiated fixtures translates to a database query per test method), - # then set this back to true. - self.use_instantiated_fixtures = false - - # Add more helper methods to be used by all tests here... -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/test/unit/room_maximum_occupancy_test.rb --- a/test/rails_root/test/unit/room_maximum_occupancy_test.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class RoomMaximumOccupancyTest < Test::Unit::TestCase - fixtures :people - -# def test_1 -# room = Room.new(:maximum_occupancy => 4) -# room.maximum_occupancy = 10 -# -# assert_equal 10, room.maximum_occupancy # Still invalid -# assert_equal false, room.save -# assert_equal "You can't have the maximum set so high", room.errors.on(:maximum_occupancy) -# assert_equal 10, room.maximum_occupancy # Still invalid -# end - - def test_maximum_occupancy - room = Room.new(:maximum_occupancy => 2) - assert_equal [], room.people - assert_equal [], room.people_without_deferred_save - assert_not_equal room.unsaved_people.object_id, - room.people_without_deferred_save.object_id - - assert_nothing_raised { room.people << people(:person1) } - assert_nothing_raised { room.people << people(:person2) } - assert_equal 0, Room.count_by_sql("select count(*) from people_rooms") # Still not saved to the association table! - assert_equal 0, room.people_without_deferred_save.size - assert_equal 2, room.people.size # 2 because this looks at unsaved_people - - assert room.save # Only here is it actually saved to the association table! - assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") - assert_equal 2, room.people.size - assert_equal 2, room.people_without_deferred_save.size - - assert_nothing_raised { room.people << people(:person3) } - assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") # person3 is not yet saved to the association table - assert_equal false, room.valid? - assert_equal "There are too many people in this room", room.errors.on(:people) - - assert_equal false, room.save - assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") # It's still not there, because it didn't pass the validation. - assert_equal "There are too many people in this room", room.errors.on(:people) - assert_equal 3, room.people.size # 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*. - - room.reload - assert_equal 2, room.people.size - assert_equal 2, room.people_without_deferred_save.size - - assert_nothing_raised { room.people << people(:person3) } - assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") # person3 is not yet saved to the association table - - # 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... - assert_raise RuntimeError do - room.people_without_deferred_save << people(:person3) - end - end -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/test/unit/room_maximum_occupancy_test_1.rb --- a/test/rails_root/test/unit/room_maximum_occupancy_test_1.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class RoomMaximumOccupancyTest < Test::Unit::TestCase - fixtures :people - - def test_maximum_occupancy - room = Room.new(:maximum_occupancy => 2) - assert_equal 0, Room.count_by_sql("select count(*) from people_rooms") - assert_equal 0, room.people.size - - room.people << people(:person1) - room.people << people(:person2) - assert room.save - assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") - assert_equal 2, room.people.size - - room.people << people(:person3) - #assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") # FAILS because it saves it in people_rooms before we even call room.save ! - - assert_equal false, room.save - # Good, it has the error ... - assert_equal "There are too many people in this room", room.errors.on(:people) - # ... but it's too late. It didn't prevent the invalid data from getting in there! - #assert_equal 2, room.people.size # FAILS - end -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/test/unit/room_maximum_occupancy_test_2.rb --- a/test/rails_root/test/unit/room_maximum_occupancy_test_2.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class RoomMaximumOccupancyTest < Test::Unit::TestCase - fixtures :people - - def test_maximum_occupancy_using_build - room = Room.new(:maximum_occupancy => 2) - assert_equal 0, Room.count_by_sql("select count(*) from people_rooms") - assert_equal 0, room.people.size - - room.people.build(:name => 'person1') - room.people.build(:name => 'person2') - assert room.save - assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") - assert_equal 2, room.people.size - - room.people.build(:name => 'person3') - # Good, it prevented it from being saved to the database ... - assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") - # ... but it still added it to the collection stored in memory! - #assert_equal 2, room.people.size # Still FAILs. It thinks it has 3, even though the 3rd one is invalid. - - assert_equal false, room.save - assert_equal "There are too many people in this room", room.errors.on(:people) - - # If we reload from what is stored in memory, it will still just have the 2 valid people... - room.reload - assert_equal 2, room.people.size - end - -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/test/unit/room_maximum_occupancy_test_3.rb --- a/test/rails_root/test/unit/room_maximum_occupancy_test_3.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class RoomMaximumOccupancyTest < Test::Unit::TestCase - fixtures :people - - def test_maximum_occupancy - room = Room.new(:maximum_occupancy => 2) - assert_equal 0, Room.count_by_sql("select count(*) from people_rooms") - assert_equal 0, room.people.size - - assert_nothing_raised { room.people << people(:person1) } - assert_nothing_raised { room.people << people(:person2) } - assert room.save - assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") - assert_equal 2, room.people.size - - assert_raise RuntimeError do - room.people << people(:person3) - end - assert_equal 2, Room.count_by_sql("select count(*) from people_rooms") - - assert_equal "There are too many people in this room", room.errors.on(:people) # Passes (for now!) - - # But as soon as I go to save it, it clears out the errors array!! Arg! - room.save - #assert_equal "There are too many people in this room", room.errors.on(:people) # FAILS - - #assert_equal false, room.valid? # FAILS - #assert_equal false, room.save # FAILS - assert_equal 2, room.people.size - end - -end diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/rails_root/vendor/plugins/has_and_belong_to_many_with_deferred_save/init.rb --- a/test/rails_root/vendor/plugins/has_and_belong_to_many_with_deferred_save/init.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -# This tricks the test app into loading the plugin from its parent directory. -# The alternatives migth be: -# * creating a symlink from plugin_name/rails_root/vendor/plugins/plugin_name to plugin_name -# * creating an svn:external at plugin_name/rails_root/vendor/plugins/plugin_name that pulls in the contents of plugin_name (not only would that create a circular dependency, it also would mean that changes you made to plugin_name wouldn't show up in rails_root until you committed and updated) - -#require "#{RAILS_ROOT}/../init.rb" -init_path = "#{RAILS_ROOT}/../../init.rb" -silence_warnings { eval(IO.read(init_path), binding, init_path) } diff -r a0ab430243c79b1311925cef5745c2becdbdfb03 -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 test/test_helper.rb --- a/test/test_helper.rb Thu Feb 25 10:11:12 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -require 'rubygems' -require 'facets/core/kernel/require_local' - -# This loads the application's (default) test_helper, which loads the environment, etc. -require_local 'rails_root/test/test_helper' - -# This puts our working directory into the root of our test app. -Dir.chdir File.dirname(__FILE__) + 'rails_root/' - -# Side effect you should be aware of: -# rake_test_loader will have trouble finding your tests due to this chdir... -