Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

merge collectiveidea/master

  • Loading branch information...
commit 2a991eff12d926090ebbcfdde5540a221ea79870 2 parents 994845e + a216155
@jamesgolick authored
Showing with 93 additions and 937 deletions.
  1. +4 −1 .gitignore
  2. +6 −0 README
  3. +1 −0  Rakefile
  4. +17 −5 lib/attribute_fu/associations.rb
  5. +0 −10 test/Rakefile
  6. +0 −10 test/app/controllers/application.rb
  7. +0 −3  test/app/helpers/application_helper.rb
  8. +1 −1  test/{test/unit → }/associated_form_helper_test.rb
  9. +1 −1  test/{test/unit → }/comment_test.rb
  10. +0 −97 test/config/boot.rb
  11. +0 −9 test/config/database.yml
  12. +0 −15 test/config/environment.rb
  13. +0 −18 test/config/environments/development.rb
  14. +0 −22 test/config/environments/test.rb
  15. +0 −35 test/config/routes.rb
  16. +18 −0 test/db/database.yml
  17. +0 −14 test/db/migrate/001_create_photos.rb
  18. +0 −15 test/db/migrate/002_create_comments.rb
  19. +9 −22 test/db/schema.rb
  20. 0  test/{app → }/models/comment.rb
  21. 0  test/{app → }/models/photo.rb
  22. +12 −1 test/{test/unit → }/photo_test.rb
  23. +0 −3  test/script/console
  24. +0 −3  test/script/destroy
  25. +0 −3  test/script/generate
  26. +0 −3  test/script/server
  27. +0 −6 test/test/test_helper.rb
  28. +24 −0 test/test_helper.rb
  29. +0 −3  test/vendor/plugins/shoulda/init.rb
  30. +0 −20 test/vendor/plugins/shoulda/lib/shoulda.rb
  31. +0 −338 test/vendor/plugins/shoulda/lib/shoulda/active_record_helpers.rb
  32. +0 −143 test/vendor/plugins/shoulda/lib/shoulda/context.rb
  33. +0 −119 test/vendor/plugins/shoulda/lib/shoulda/general.rb
  34. +0 −17 test/vendor/plugins/shoulda/lib/shoulda/private_helpers.rb
View
5 .gitignore
@@ -1 +1,4 @@
-.DS_Store
+.DS_Store
+test/debug.log
+test/db/*.sqlite3
+test/log/*
View
6 README
@@ -105,6 +105,12 @@ Come join the discussion on the {mailing list}[link:http://groups.google.com/gro
Updates will be available {here}[http://jamesgolick.com/attribute_fu]
+= Running the tests
+
+To run the tests, you need Shoulda, mocha and multi-rails:
+
+ $ sudo gem install thoughtbot-shoulda --source http://gems.github.com/
+ $ sudo gem install mocha multi_rails
== Credits
View
1  Rakefile
@@ -1,4 +1,5 @@
require 'rake'
+require "load_multi_rails_rake_tasks"
require 'rake/testtask'
require 'rake/rdoctask'
View
22 lib/attribute_fu/associations.rb
@@ -62,12 +62,14 @@ def has_many_attributes(association_id, attributes) #:nodoc:
def save_managed_associations #:nodoc:
if managed_association_attributes != nil
managed_association_attributes.keys.each do |association_id|
- association = send(association_id)
- association.each(&:save)
+ if send(association_id).loaded? # don't save what we haven't even loaded
+ association = send(association_id)
+ association.each(&:save)
- unless (objects_to_remove = instance_variable_get removal_variable_name(association_id)).nil?
- objects_to_remove.each { |remove_id| association.delete association.detect { |obj| obj.id.to_s == remove_id.to_s } }
- instance_variable_set removal_variable_name(association_id), nil
+ unless (objects_to_remove = instance_variable_get removal_variable_name(association_id)).nil?
+ objects_to_remove.each { |remove_id| association.delete association.detect { |obj| obj.id.to_s == remove_id.to_s } }
+ instance_variable_set removal_variable_name(association_id), nil
+ end
end
end
end
@@ -114,10 +116,20 @@ def has_many_with_association_option(association_id, options = {}, &extension)
discard_if = discard_if.to_proc if discard_if.is_a?(Symbol)
managed_association_attributes[association_id][:discard_if] = discard_if
end
+ collection_with_attributes_writer(association_id)
end
has_many_without_association_option(association_id, options, &extension)
end
+
+ private
+
+ def collection_with_attributes_writer(association_name)
+ define_method("#{association_name.to_s.singularize}_attributes=") do |attributes|
+ has_many_attributes association_name, attributes
+ end
+ end
+
end
end # Associations
View
10 test/Rakefile
@@ -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'
View
10 test/app/controllers/application.rb
@@ -1,10 +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
- helper :all # include all helpers, all the time
-
- # See ActionController::RequestForgeryProtection for details
- # Uncomment the :secret if you're not using the cookie session store
- protect_from_forgery # :secret => '012cbaf0c3d36504f3b1bc397b838d24'
-end
View
3  test/app/helpers/application_helper.rb
@@ -1,3 +0,0 @@
-# Methods added to this helper will be available to all templates in the application.
-module ApplicationHelper
-end
View
2  .../test/unit/associated_form_helper_test.rb → test/associated_form_helper_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__)+'/../test_helper'
+require File.dirname(__FILE__)+'/test_helper'
class AssociatedFormHelperTest < Test::Unit::TestCase
include ActionView::Helpers::FormHelper
View
2  test/test/unit/comment_test.rb → test/comment_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/test_helper'
class CommentTest < ActiveSupport::TestCase
should_belong_to :photo
View
97 test/config/boot.rb
@@ -1,97 +0,0 @@
-# Don't change this file!
-# Configure your app in config/environment.rb and config/environments/*.rb
-
-RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
-
-module Rails
- class << self
- def boot!
- pick_boot.run unless booted?
- end
-
- def booted?
- defined? Rails::Initializer
- end
-
- def pick_boot
- (vendor_rails? ? VendorBoot : GemBoot).new
- end
-
- def vendor_rails?
- File.exist?("#{RAILS_ROOT}/vendor/rails")
- end
- end
-
- class Boot
- def run
- load_initializer
- Rails::Initializer.run(:set_load_path)
- end
- end
-
- class VendorBoot < Boot
- def load_initializer
- require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
- end
- end
-
- class GemBoot < Boot
- def load_initializer
- self.class.load_rubygems
- load_rails_gem
- require 'initializer'
- end
-
- def load_rails_gem
- if version = self.class.gem_version
- gem 'rails', "=#{version}"
- else
- gem 'rails'
- end
- rescue Gem::LoadError => load_error
- $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
- exit 1
- end
-
- class << self
- def rubygems_version
- Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
- end
-
- def gem_version
- if defined? RAILS_GEM_VERSION
- RAILS_GEM_VERSION
- elsif ENV.include?('RAILS_GEM_VERSION')
- ENV['RAILS_GEM_VERSION']
- else
- parse_gem_version(read_environment_rb)
- end
- end
-
- def load_rubygems
- require 'rubygems'
-
- unless rubygems_version >= '0.9.4'
- $stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
- exit 1
- end
-
- rescue LoadError
- $stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
- exit 1
- end
-
- def parse_gem_version(text)
- $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*'([\d.]+)'/
- end
-
- private
- def read_environment_rb
- File.read("#{RAILS_ROOT}/config/environment.rb")
- end
- end
- end
-end
-
-# All that for this:
-Rails.boot!
View
9 test/config/database.yml
@@ -1,9 +0,0 @@
-development:
- adapter: sqlite3
- database: db/development.sqlite3
- timeout: 5000
-
-test:
- adapter: sqlite3
- database: db/test.sqlite3
- timeout: 5000
View
15 test/config/environment.rb
@@ -1,15 +0,0 @@
-$:.reject! { |e| e.include? 'TextMate' }
-RAILS_GEM_VERSION = '2.0.2' unless defined? RAILS_GEM_VERSION
-
-require File.join(File.dirname(__FILE__), 'boot')
-
-Rails::Initializer.run do |config|
- config.action_controller.session = {
- :session_key => '_test_session',
- :secret => '012cbaf0c3d36504f3b1bc397b838d24'
- }
-
- config.load_paths += %W( #{RAILS_ROOT}/../lib )
-end
-
-require "#{RAILS_ROOT}/../init"
View
18 test/config/environments/development.rb
@@ -1,18 +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
-
-# 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
View
22 test/config/environments/test.rb
@@ -1,22 +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
-
-# Disable request forgery protection in test environment
-config.action_controller.allow_forgery_protection = 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
View
35 test/config/routes.rb
@@ -1,35 +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)
-
- # Sample resource route (maps HTTP verbs to controller actions automatically):
- # map.resources :products
-
- # Sample resource route with options:
- # map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }
-
- # Sample resource route with sub-resources:
- # map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller
-
- # Sample resource route within a namespace:
- # map.namespace :admin do |admin|
- # # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)
- # admin.resources :products
- # end
-
- # You can have the root of your site routed with map.root -- just remember to delete public/index.html.
- # map.root :controller => "welcome"
-
- # See how all your routes lay out with "rake routes"
-
- # Install the default routes as the lowest priority.
- map.connect ':controller/:action/:id'
- map.connect ':controller/:action/:id.:format'
-end
View
18 test/db/database.yml
@@ -0,0 +1,18 @@
+sqlite3:
+ adapter: sqlite3
+ dbfile: attribute_fu.sqlite3.db
+sqlite3mem:
+ :adapter: sqlite3
+ :dbfile: ":memory:"
+postgresql:
+ :adapter: postgresql
+ :username: postgres
+ :password: postgres
+ :database: attribute_fu_plugin_test
+ :min_messages: ERROR
+mysql:
+ :adapter: mysql
+ :host: localhost
+ :username: root
+ :password:
+ :database: attribute_fu_plugin_test
View
14 test/db/migrate/001_create_photos.rb
@@ -1,14 +0,0 @@
-class CreatePhotos < ActiveRecord::Migration
- def self.up
- create_table :photos do |t|
- t.string :title
- t.text :description
-
- t.timestamps
- end
- end
-
- def self.down
- drop_table :photos
- end
-end
View
15 test/db/migrate/002_create_comments.rb
@@ -1,15 +0,0 @@
-class CreateComments < ActiveRecord::Migration
- def self.up
- create_table :comments do |t|
- t.integer :photo_id
- t.string :author
- t.text :body
-
- t.timestamps
- end
- end
-
- def self.down
- drop_table :comments
- end
-end
View
31 test/db/schema.rb
@@ -1,29 +1,16 @@
-# This file is auto-generated from the current state of the database. Instead of editing this file,
-# please use the migrations feature of ActiveRecord to incrementally modify your database, and
-# then regenerate this schema definition.
-#
-# Note that this schema.rb definition is the authoritative source for your database schema. If you need
-# to create the application database on another system, you should be using db:schema:load, not running
-# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
-# you'll amass, the slower it'll run and the greater likelihood for issues).
-#
-# It's strongly recommended to check this file into your version control system.
-
ActiveRecord::Schema.define(:version => 2) do
- create_table "comments", :force => true do |t|
- t.integer "photo_id"
- t.string "author"
- t.text "body"
- t.datetime "created_at"
- t.datetime "updated_at"
+ create_table :comments, :force => true do |t|
+ t.integer :photo_id
+ t.string :author
+ t.text :body
+ t.timestamps
end
- create_table "photos", :force => true do |t|
- t.string "title"
- t.text "description"
- t.datetime "created_at"
- t.datetime "updated_at"
+ create_table :photos, :force => true do |t|
+ t.string :title
+ t.text :description
+ t.timestamps
end
end
View
0  test/app/models/comment.rb → test/models/comment.rb
File renamed without changes
View
0  test/app/models/photo.rb → test/models/photo.rb
File renamed without changes
View
13 test/test/unit/photo_test.rb → test/photo_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/test_helper'
class PhotoTest < ActiveSupport::TestCase
should_have_many :comments
@@ -69,6 +69,17 @@ class PhotoTest < ActiveSupport::TestCase
end
end
+ context "with comment_attributes unset" do
+ setup do
+ @photo.reload
+ @photo.save
+ end
+
+ should "not load the comments" do
+ assert !@photo.comments.loaded?, "comments were loaded unnecessarily: #{@photo.comments.inspect}"
+ end
+ end
+
context "with discard_if => proc { }" do
setup do
create_photo_with_discard(proc { |comment| comment.author.blank? && comment.body.blank? })
View
3  test/script/console
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/console'
View
3  test/script/destroy
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/destroy'
View
3  test/script/generate
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/generate'
View
3  test/script/server
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../config/boot'
-require 'commands/server'
View
6 test/test/test_helper.rb
@@ -1,6 +0,0 @@
-ENV["RAILS_ENV"] = "test"
-require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
-require 'test_help'
-require 'mocha'
-
-class Test::Unit::TestCase; end
View
24 test/test_helper.rb
@@ -0,0 +1,24 @@
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+plugin_test_dir = File.dirname(__FILE__)
+
+require 'rubygems'
+require 'multi_rails_init'
+require 'active_record'
+require 'action_view'
+require 'test/unit'
+require 'mocha'
+require 'shoulda/rails'
+
+require 'attribute_fu'
+require 'attribute_fu/associations'
+require 'attribute_fu/associated_form_helper'
+require plugin_test_dir + '/../init.rb'
+
+ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/debug.log")
+
+ActiveRecord::Base.configurations = YAML::load(IO.read(plugin_test_dir + "/db/database.yml"))
+ActiveRecord::Base.establish_connection(ENV["DB"] || "sqlite3mem")
+ActiveRecord::Migration.verbose = false
+load(File.join(plugin_test_dir, "db", "schema.rb"))
+
+Dir["#{plugin_test_dir}/models/*.rb"].each {|file| require file }
View
3  test/vendor/plugins/shoulda/init.rb
@@ -1,3 +0,0 @@
-require 'rubygems'
-require 'active_support'
-require 'shoulda'
View
20 test/vendor/plugins/shoulda/lib/shoulda.rb
@@ -1,20 +0,0 @@
-require 'yaml'
-require 'shoulda/private_helpers'
-require 'shoulda/general'
-require 'shoulda/context'
-require 'shoulda/active_record_helpers'
-
-
-module Test # :nodoc: all
- module Unit
- class TestCase
-
- include ThoughtBot::Shoulda::General
-
- class << self
- include ThoughtBot::Shoulda::Context
- include ThoughtBot::Shoulda::ActiveRecord
- end
- end
- end
-end
View
338 test/vendor/plugins/shoulda/lib/shoulda/active_record_helpers.rb
@@ -1,338 +0,0 @@
-module ThoughtBot # :nodoc:
- module Shoulda # :nodoc:
- # = Macro test helpers for your active record models
- #
- # These helpers will test most of the validations and associations for your ActiveRecord models.
- #
- # class UserTest < Test::Unit::TestCase
- # should_require_attributes :name, :phone_number
- # should_not_allow_values_for :phone_number, "abcd", "1234"
- # should_allow_values_for :phone_number, "(123) 456-7890"
- #
- # should_protect_attributes :password
- #
- # should_have_one :profile
- # should_have_many :dogs
- # should_have_many :messes, :through => :dogs
- # should_belong_to :lover
- # end
- #
- # For all of these helpers, the last parameter may be a hash of options.
- #
- module ActiveRecord
- # Ensures that the model cannot be saved if one of the attributes listed is not present.
- # Requires an existing record.
- #
- # Options:
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
- # Regexp or string. Default = <tt>/blank/</tt>
- #
- # Example:
- # should_require_attributes :name, :phone_number
- def should_require_attributes(*attributes)
- message = get_options!(attributes, :message)
- message ||= /blank/
- klass = model_class
-
- attributes.each do |attribute|
- should "require #{attribute} to be set" do
- object = klass.new
- assert !object.valid?, "#{klass.name} does not require #{attribute}."
- assert object.errors.on(attribute), "#{klass.name} does not require #{attribute}."
- assert_contains(object.errors.on(attribute), message)
- end
- end
- end
-
- # Ensures that the model cannot be saved if one of the attributes listed is not unique.
- # Requires an existing record
- #
- # Options:
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
- # Regexp or string. Default = <tt>/taken/</tt>
- #
- # Example:
- # should_require_unique_attributes :keyword, :username
- def should_require_unique_attributes(*attributes)
- message, scope = get_options!(attributes, :message, :scoped_to)
- message ||= /taken/
-
- klass = model_class
- attributes.each do |attribute|
- attribute = attribute.to_sym
- should "require unique value for #{attribute}#{" scoped to #{scope}" if scope}" do
- assert existing = klass.find(:first), "Can't find first #{klass}"
- object = klass.new
-
- object.send(:"#{attribute}=", existing.send(attribute))
- if scope
- assert_respond_to object, :"#{scope}=", "#{klass.name} doesn't seem to have a #{scope} attribute."
- object.send(:"#{scope}=", existing.send(scope))
- end
-
- assert !object.valid?, "#{klass.name} does not require a unique value for #{attribute}."
- assert object.errors.on(attribute), "#{klass.name} does not require a unique value for #{attribute}."
-
- assert_contains(object.errors.on(attribute), message)
-
- if scope
- # Now test that the object is valid when changing the scoped attribute
- # TODO: actually find all values for scope and create a unique one.
- object.send(:"#{scope}=", existing.send(scope).nil? ? 1 : existing.send(scope).next)
- object.errors.clear
- object.valid?
- assert_does_not_contain(object.errors.on(attribute), message,
- "after :#{scope} set to #{object.send(scope.to_sym)}")
- end
- end
- end
- end
-
- # Ensures that the attribute cannot be set on update
- # Requires an existing record
- #
- # should_protect_attributes :password, :admin_flag
- def should_protect_attributes(*attributes)
- get_options!(attributes)
- klass = model_class
- attributes.each do |attribute|
- attribute = attribute.to_sym
- should "not allow #{attribute} to be changed by update" do
- assert object = klass.find(:first), "Can't find first #{klass}"
- value = object[attribute]
- # TODO: 1 may not be a valid value for the attribute (due to validations)
- assert object.update_attributes({ attribute => 1 }),
- "Cannot update #{klass} with { :#{attribute} => 1 }, #{object.errors.full_messages.to_sentence}"
- assert object.valid?, "#{klass} isn't valid after changing #{attribute}"
- assert_equal value, object[attribute], "Was able to change #{klass}##{attribute}"
- end
- end
- end
-
- # Ensures that the attribute cannot be set to the given values
- # Requires an existing record
- #
- # Options:
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
- # Regexp or string. Default = <tt>/invalid/</tt>
- #
- # Example:
- # should_not_allow_values_for :isbn, "bad 1", "bad 2"
- def should_not_allow_values_for(attribute, *bad_values)
- message = get_options!(bad_values, :message)
- message ||= /invalid/
- klass = model_class
- bad_values.each do |v|
- should "not allow #{attribute} to be set to \"#{v}\"" do
- assert object = klass.find(:first), "Can't find first #{klass}"
- object.send("#{attribute}=", v)
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{v}\""
- assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{v}\""
- assert_contains(object.errors.on(attribute), message, "when set to \"#{v}\"")
- end
- end
- end
-
- # Ensures that the attribute can be set to the given values.
- # Requires an existing record
- #
- # Options:
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
- # Regexp or string. Default = <tt>/invalid/</tt>
- #
- # Example:
- # should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"
- def should_allow_values_for(attribute, *good_values)
- message = get_options!(good_values, :message)
- message ||= /invalid/
- klass = model_class
- good_values.each do |v|
- should "allow #{attribute} to be set to \"#{v}\"" do
- assert object = klass.find(:first), "Can't find first #{klass}"
- object.send("#{attribute}=", v)
- object.save
- assert_does_not_contain(object.errors.on(attribute), message, "when set to \"#{v}\"")
- end
- end
- end
-
- # Ensures that the length of the attribute is in the given range
- # Requires an existing record
- #
- # Options:
- # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
- # Regexp or string. Default = <tt>/short/</tt>
- # * <tt>:long_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
- # Regexp or string. Default = <tt>/long/</tt>
- #
- # Example:
- # should_ensure_length_in_range :password, (6..20)
- def should_ensure_length_in_range(attribute, range, opts = {})
- short_message, long_message = get_options!([opts], :short_message, :long_message)
- short_message ||= /short/
- long_message ||= /long/
-
- klass = model_class
- min_length = range.first
- max_length = range.last
-
- if min_length > 0
- min_value = "x" * (min_length - 1)
- should "not allow #{attribute} to be less than #{min_length} chars long" do
- assert object = klass.find(:first), "Can't find first #{klass}"
- object.send("#{attribute}=", min_value)
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{min_value}\""
- assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{min_value}\""
- assert_contains(object.errors.on(attribute), short_message, "when set to \"#{min_value}\"")
- end
- end
-
- max_value = "x" * (max_length + 1)
- should "not allow #{attribute} to be more than #{max_length} chars long" do
- assert object = klass.find(:first), "Can't find first #{klass}"
- object.send("#{attribute}=", max_value)
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{max_value}\""
- assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{max_value}\""
- assert_contains(object.errors.on(attribute), long_message, "when set to \"#{max_value}\"")
- end
- end
-
- # Ensure that the attribute is in the range specified
- # Requires an existing record
- #
- # Options:
- # * <tt>:low_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
- # Regexp or string. Default = <tt>/included/</tt>
- # * <tt>:high_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
- # Regexp or string. Default = <tt>/included/</tt>
- #
- # Example:
- # should_ensure_value_in_range :age, (0..100)
- def should_ensure_value_in_range(attribute, range, opts = {})
- low_message, high_message = get_options!([opts], :low_message, :high_message)
- low_message ||= /included/
- high_message ||= /included/
-
- klass = model_class
- min = range.first
- max = range.last
-
- should "not allow #{attribute} to be less than #{min}" do
- v = min - 1
- assert object = klass.find(:first), "Can't find first #{klass}"
- object.send("#{attribute}=", v)
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{v}\""
- assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{v}\""
- assert_contains(object.errors.on(attribute), low_message, "when set to \"#{v}\"")
- end
-
- should "not allow #{attribute} to be more than #{max}" do
- v = max + 1
- assert object = klass.find(:first), "Can't find first #{klass}"
- object.send("#{attribute}=", v)
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{v}\""
- assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{v}\""
- assert_contains(object.errors.on(attribute), high_message, "when set to \"#{v}\"")
- end
- end
-
- # Ensure that the attribute is numeric
- # Requires an existing record
- #
- # Options:
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
- # Regexp or string. Default = <tt>/number/</tt>
- #
- # Example:
- # should_only_allow_numeric_values_for :age
- def should_only_allow_numeric_values_for(*attributes)
- message = get_options!(attributes, :message)
- message ||= /number/
- klass = model_class
- attributes.each do |attribute|
- attribute = attribute.to_sym
- should "only allow numeric values for #{attribute}" do
- assert object = klass.find(:first), "Can't find first #{klass}"
- object.send(:"#{attribute}=", "abcd")
- assert !object.valid?, "Instance is still valid"
- assert_contains(object.errors.on(attribute), message)
- end
- end
- end
-
- # Ensures that the has_many relationship exists.
- #
- # Options:
- # * <tt>:through</tt> - association name for <tt>has_many :through</tt>
- #
- # Example:
- # should_have_many :friends
- # should_have_many :enemies, :through => :friends
- def should_have_many(*associations)
- through = get_options!(associations, :through)
- klass = model_class
- associations.each do |association|
- should "have many #{association}#{" through #{through}" if through}" do
- reflection = klass.reflect_on_association(association)
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
- assert_equal :has_many, reflection.macro
- if through
- through_reflection = klass.reflect_on_association(through)
- assert through_reflection, "#{klass.name} does not have any relationship to #{through}"
- assert_equal(through, reflection.options[:through])
- end
- end
- end
- end
-
- # Ensures that the has_and_belongs_to_many relationship exists.
- #
- # should_have_and_belong_to_many :posts, :cars
- def should_have_and_belong_to_many(*associations)
- get_options!(associations)
- klass = model_class
- associations.each do |association|
- should "should have and belong to many #{association}" do
- assert klass.reflect_on_association(association), "#{klass.name} does not have any relationship to #{association}"
- assert_equal :has_and_belongs_to_many, klass.reflect_on_association(association).macro
- end
- end
- end
-
- # Ensure that the has_one relationship exists.
- #
- # should_have_one :god # unless hindu
- def should_have_one(*associations)
- get_options!(associations)
- klass = model_class
- associations.each do |association|
- should "have one #{association}" do
- assert klass.reflect_on_association(association), "#{klass.name} does not have any relationship to #{association}"
- assert_equal :has_one, klass.reflect_on_association(association).macro
- end
- end
- end
-
- # Ensure that the belongs_to relationship exists.
- #
- # should_belong_to :parent
- def should_belong_to(*associations)
- get_options!(associations)
- klass = model_class
- associations.each do |association|
- should "belong_to #{association}" do
- reflection = klass.reflect_on_association(association)
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
- assert_equal :belongs_to, reflection.macro
- fk = reflection.options[:foreign_key] || "#{association}_id"
- assert klass.column_names.include?(fk), "#{klass.name} does not have a #{fk} foreign key."
- end
- end
- end
-
- private
-
- include ThoughtBot::Shoulda::Private
- end
- end
-end
View
143 test/vendor/plugins/shoulda/lib/shoulda/context.rb
@@ -1,143 +0,0 @@
-module ThoughtBot # :nodoc:
- module Shoulda # :nodoc:
- # = context and should blocks
- #
- # A context block groups should statements under a common setup/teardown method.
- # Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability
- # and readability of your test code.
- #
- # A context block can contain setup, should, should_eventually, and teardown blocks.
- #
- # class UserTest << Test::Unit::TestCase
- # context "a User instance" do
- # setup do
- # @user = User.find(:first)
- # end
- #
- # should "return its full name"
- # assert_equal 'John Doe', @user.full_name
- # end
- # end
- # end
- #
- # This code will produce the method <tt>"test a User instance should return its full name"</tt>.
- #
- # Contexts may be nested. Nested contexts run their setup blocks from out to in before each test.
- # They then run their teardown blocks from in to out after each test.
- #
- # class UserTest << Test::Unit::TestCase
- # context "a User instance" do
- # setup do
- # @user = User.find(:first)
- # end
- #
- # should "return its full name"
- # assert_equal 'John Doe', @user.full_name
- # end
- #
- # context "with a profile" do
- # setup do
- # @user.profile = Profile.find(:first)
- # end
- #
- # should "return true when sent :has_profile?"
- # assert @user.has_profile?
- # end
- # end
- # end
- # end
- #
- # This code will produce the following methods
- # * <tt>"test: a User instance should return its full name."</tt>
- # * <tt>"test: a User instance with a profile should return true when sent :has_profile?."</tt>
- #
- # <b>A context block can exist next to normal <tt>def test_the_old_way; end</tt> tests</b>,
- # meaning you do not have to fully commit to the context/should syntax in a test file.
- #
-
- module Context
- def Context.included(other) # :nodoc:
- @@context_names = []
- @@setup_blocks = []
- @@teardown_blocks = []
- end
-
- # Defines a test method. Can be called either inside our outside of a context.
- # Optionally specify <tt>:unimplimented => true</tt> (see should_eventually).
- #
- # Example:
- #
- # class UserTest << Test::Unit::TestCase
- # should "return first user on find(:first)"
- # assert_equal users(:first), User.find(:first)
- # end
- # end
- #
- # Would create a test named
- # 'test: should return first user on find(:first)'
- #
- def should(name, opts = {}, &should_block)
- test_name = ["test:", @@context_names, "should", "#{name}. "].flatten.join(' ').to_sym
-
- name_defined = eval("self.instance_methods.include?('#{test_name.to_s.gsub(/['"]/, '\$1')}')", should_block.binding)
- raise ArgumentError, "'#{test_name}' is already defined" and return if name_defined
-
- setup_blocks = @@setup_blocks.dup
- teardown_blocks = @@teardown_blocks.dup
-
- if opts[:unimplemented]
- define_method test_name do |*args|
- # XXX find a better way of doing this.
- assert true
- STDOUT.putc "X" # Tests for this model are missing.
- end
- else
- define_method test_name do |*args|
- begin
- setup_blocks.each {|b| b.bind(self).call }
- should_block.bind(self).call(*args)
- ensure
- teardown_blocks.reverse.each {|b| b.bind(self).call }
- end
- end
- end
- end
-
- # Creates a context block with the given name.
- def context(name, &context_block)
- saved_setups = @@setup_blocks.dup
- saved_teardowns = @@teardown_blocks.dup
- saved_contexts = @@context_names.dup
-
- @@context_names << name
- context_block.bind(self).call
-
- @@context_names = saved_contexts
- @@setup_blocks = saved_setups
- @@teardown_blocks = saved_teardowns
- end
-
- # Run before every should block in the current context.
- # If a setup block appears in a nested context, it will be run after the setup blocks
- # in the parent contexts.
- def setup(&setup_block)
- @@setup_blocks << setup_block
- end
-
- # Run after every should block in the current context.
- # If a teardown block appears in a nested context, it will be run before the teardown
- # blocks in the parent contexts.
- def teardown(&teardown_block)
- @@teardown_blocks << teardown_block
- end
-
- # Defines a specification that is not yet implemented.
- # Will be displayed as an 'X' when running tests, and failures will not be shown.
- # This is equivalent to:
- # should(name, {:unimplemented => true}, &block)
- def should_eventually(name, &block)
- should("eventually #{name}", {:unimplemented => true}, &block)
- end
- end
- end
-end
View
119 test/vendor/plugins/shoulda/lib/shoulda/general.rb
@@ -1,119 +0,0 @@
-module ThoughtBot # :nodoc:
- module Shoulda # :nodoc:
- module General
- def self.included(other) # :nodoc:
- other.class_eval do
- extend ThoughtBot::Shoulda::General::ClassMethods
- # include ThoughtBot::Shoulda::General::InstanceMethods
- end
- end
-
- module ClassMethods
- # Loads all fixture files (<tt>test/fixtures/*.yml</tt>)
- def load_all_fixtures
- all_fixtures = Dir.glob(File.join(Test::Unit::TestCase.fixture_path, "*.yml")).collect do |f|
- File.basename(f, '.yml').to_sym
- end
- fixtures *all_fixtures
- end
- end
-
- # Prints a message to stdout, tagged with the name of the calling method.
- def report!(msg = "")
- puts("#{caller.first}: #{msg}")
- end
-
- # Ensures that the number of items in the collection changes
- #
- # assert_difference(User, :count, 1) { User.create }
- # assert_difference(User.packages, :size, 3, true) { User.add_three_packages }
- #
- # Setting reload to true will call <tt>object.reload</tt> after the block (for ActiveRecord associations)
- def assert_difference(object, method, difference, reload = false, msg = nil)
- initial_value = object.send(method)
- yield
- object.send(:reload) if reload
- assert_equal initial_value + difference, object.send(method), (msg || "#{object}##{method} after block")
- end
-
- # Ensures that object.method does not change. See assert_difference for usage.
- def assert_no_difference(object, method, reload = false, msg = nil, &block)
- assert_difference(object, method, 0, reload, msg, &block)
- end
-
- # Asserts that two arrays contain the same elements, the same number of times. Essentially ==, but unordered.
- #
- # assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
- def assert_same_elements(a1, a2, msg = nil)
- [:select, :inject, :size].each do |m|
- [a1, a2].each {|a| assert_respond_to(a, m, "Are you sure that #{a.inspect} is an array? It doesn't respond to #{m}.") }
- end
-
- assert a1h = a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h }
- assert a2h = a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
-
- assert_equal(a1h, a2h, msg)
- end
-
- # Asserts that the given collection contains item x. If x is a regular expression, ensure that
- # at least one element from the collection matches x. +extra_msg+ is appended to the error message if the assertion fails.
- #
- # assert_contains(['a', '1'], /\d/) => passes
- # assert_contains(['a', '1'], 'a') => passes
- # assert_contains(['a', '1'], /not there/) => fails
- def assert_contains(collection, x, extra_msg = "")
- collection = [collection] unless collection.is_a?(Array)
- msg = "#{x.inspect} not found in #{collection.to_a.inspect} " + extra_msg
- case x
- when Regexp: assert(collection.detect { |e| e =~ x }, msg)
- else assert(collection.include?(x), msg)
- end
- end
-
- # Asserts that the given collection does not contain item x. If x is a regular expression, ensure that
- # none of the elements from the collection match x.
- def assert_does_not_contain(collection, x, extra_msg = "")
- collection = [collection] unless collection.is_a?(Array)
- msg = "#{x.inspect} found in #{collection.to_a.inspect} " + extra_msg
- case x
- when Regexp: assert(!collection.detect { |e| e =~ x }, msg)
- else assert(!collection.include?(x), msg)
- end
- end
-
- # Asserts that the given object can be saved
- #
- # assert_save User.new(params)
- def assert_save(obj)
- assert obj.save, "Errors: #{obj.errors.full_messages.join('; ')}"
- obj.reload
- end
-
- # Asserts that the given object is valid
- #
- # assert_save User.new(params)
- def assert_valid(obj)
- assert obj.valid?, "Errors: #{obj.errors.full_messages.join('; ')}"
- end
-
- # Asserts that the block uses ActionMailer to send emails
- #
- # assert_sends_email(2) { Mailer.deliver_messages }
- def assert_sends_email(num = 1, &blk)
- ActionMailer::Base.deliveries.clear
- blk.call
- msg = "Sent #{ActionMailer::Base.deliveries.size} emails, when #{num} expected:\n"
- ActionMailer::Base.deliveries.each { |m| msg << " '#{m.subject}' sent to #{m.to.to_sentence}\n" }
- assert(num == ActionMailer::Base.deliveries.size, msg)
- end
-
- # Asserts that the block does not send emails thorough ActionMailer
- #
- # assert_does_not_send_email { # do nothing }
- def assert_does_not_send_email(&blk)
- assert_sends_email 0, &blk
- end
-
- end
- end
-end
View
17 test/vendor/plugins/shoulda/lib/shoulda/private_helpers.rb
@@ -1,17 +0,0 @@
-module ThoughtBot # :nodoc:
- module Shoulda # :nodoc:
- module Private # :nodoc:
- def get_options!(args, *wanted)
- ret = []
- opts = (args.last.is_a?(Hash) ? args.pop : {})
- wanted.each {|w| ret << opts.delete(w)}
- raise ArgumentError, "Unsuported options given: #{opts.keys.join(', ')}" unless opts.keys.empty?
- return *ret
- end
-
- def model_class
- self.name.gsub(/Test$/, '').constantize
- end
- end
- end
-end
Please sign in to comment.
Something went wrong with that request. Please try again.