Merge remote branch 'rails/master'

This commit is contained in:
Xavier Noria 2010-06-10 22:00:55 +02:00
commit 6356066006
61 changed files with 950 additions and 514 deletions

View File

@ -32,7 +32,7 @@ if mri || RUBY_ENGINE == "rbx"
gem "sqlite3-ruby", "~> 1.3.0", :require => 'sqlite3'
group :db do
# gem "pg", ">= 0.9.0"
gem "pg", ">= 0.9.0"
gem "mysql", ">= 2.8.1"
end
elsif RUBY_ENGINE == "jruby"
@ -44,10 +44,6 @@ elsif RUBY_ENGINE == "jruby"
end
end
# AP
gem "RedCloth", ">= 4.2.2"
gem "bluecloth", ">= 2.0.7"
group :documentation do
gem 'rdoc', '2.1'
end

View File

@ -1,15 +1,9 @@
Rails 3.0.0 [Release Candidate] (unreleased)*
* Removed textilize, textilize_without_paragraph and markdown helpers. [Santiago Pastorino]
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
* Add shallow routes back to the new router [Diego Carrion]
resources :posts do
shallow do
resources :comments
end
end
You can now use comment_path for /comments/1 instead of post_comment_path for /posts/1/comments/1.
* Remove middleware laziness [José Valim]
* Make session stores rely on request.cookie_jar and change set_session semantics to return the cookie value instead of a boolean. [José Valim]

View File

@ -106,7 +106,7 @@ module AbstractController
@_action_name = action_name = action.to_s
unless action_name = method_for_action(action_name)
raise ActionNotFound, "The action '#{action}' could not be found"
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
end
@_response_body = nil

View File

@ -5,6 +5,7 @@ module AbstractController
included do
class_attribute :_view_paths
self._view_paths = ActionView::PathSet.new
self._view_paths.freeze
end
delegate :find_template, :template_exists?, :view_paths, :formats, :formats=,
@ -61,7 +62,7 @@ module AbstractController
# paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
# otherwise, process the parameter into a ViewPathSet.
def view_paths=(paths)
self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths)
self._view_paths = ActionView::Base.process_view_paths(paths)
self._view_paths.freeze
end
end

View File

@ -57,6 +57,7 @@ module ActionController #:nodoc:
def before(controller)
self.controller = controller
callback(:before) if controller.perform_caching
true # before method from sweeper should always return true
end
def after(controller)

View File

@ -8,7 +8,7 @@ module ActionController
included do
class_attribute :hidden_actions
self.hidden_actions = Set.new
self.hidden_actions = Set.new.freeze
end
private
@ -25,7 +25,7 @@ module ActionController
# ==== Parameters
# *args<#to_s>:: A list of actions
def hide_action(*args)
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s))
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
end
def inherited(klass)
@ -41,7 +41,7 @@ module ActionController
# Overrides AbstractController::Base#action_methods to remove any methods
# that are listed as hidden methods.
def action_methods
@action_methods ||= Set.new(super.reject {|name| hidden_actions.include?(name)})
@action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) })
end
end
end

View File

@ -135,7 +135,7 @@ module ActionDispatch
ActiveSupport::Deprecation.silence do
message = "\n#{exception.class} (#{exception.message}):\n"
message << exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
message << exception.backtrace.join("\n ")
message << " " << application_trace(exception).join("\n ")
logger.fatal("#{message}\n\n")
end
end

View File

@ -350,10 +350,6 @@ module ActionDispatch
scope(:constraints => constraints) { yield }
end
def shallow
scope(:shallow => true) { yield }
end
def defaults(defaults = {})
scope(:defaults => defaults) { yield }
end
@ -378,21 +374,12 @@ module ActionDispatch
@scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
end
def merge_shallow_scope(parent, child)
parent or child
end
def merge_path_scope(parent, child)
parent_path = (@scope[:shallow] and child.eql?(':id')) ? parent.split('/').last : parent
Mapper.normalize_path "#{parent_path}/#{child}"
Mapper.normalize_path("#{parent}/#{child}")
end
def merge_name_prefix_scope(parent, child)
if @scope[:shallow]
child
else
parent ? "#{parent}_#{child}" : child
end
parent ? "#{parent}_#{child}" : child
end
def merge_module_scope(parent, child)
@ -535,10 +522,6 @@ module ActionDispatch
options["#{singular}_id".to_sym] = id_constraint if id_constraint?
options
end
def shallow?
options[:shallow]
end
end
class SingletonResource < Resource #:nodoc:
@ -620,12 +603,8 @@ module ActionDispatch
resource = Resource.new(resources.pop, options)
scope(:path => resource.path, :controller => resource.controller, :shallow => resource.shallow?) do
scope(:path => resource.path, :controller => resource.controller) do
with_scope_level(:resources, resource) do
if @scope[:shallow] && @scope[:name_prefix]
@scope[:path] = "/#{@scope[:name_prefix].pluralize}/:#{@scope[:name_prefix]}_id/#{resource.path}"
end
yield if block_given?
with_scope_level(:collection) do
@ -639,8 +618,6 @@ module ActionDispatch
with_scope_level(:member) do
scope(':id') do
scope(resource.options) do
@scope[:name_prefix] = nil if @scope[:shallow]
get :show if resource.actions.include?(:show)
put :update if resource.actions.include?(:update)
delete :destroy if resource.actions.include?(:destroy)
@ -702,6 +679,14 @@ module ActionDispatch
end
end
def namespace(path)
if resource_scope?
nested { super }
else
super
end
end
def match(*args)
options = args.extract_options!

View File

@ -158,7 +158,6 @@ module ActionView #:nodoc:
end
include Helpers, Rendering, Partials, Layouts, ::ERB::Util, Context
extend ActiveSupport::Memoizable
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
@ -170,9 +169,6 @@ module ActionView #:nodoc:
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
class_attribute :helpers
remove_method :helpers
attr_reader :helpers
class_attribute :_router
class << self
@ -201,20 +197,21 @@ module ActionView #:nodoc:
end
def self.process_view_paths(value)
return value.dup if value.is_a?(PathSet)
ActionView::PathSet.new(Array.wrap(value))
value.is_a?(PathSet) ?
value.dup : ActionView::PathSet.new(Array.wrap(value))
end
def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
@config = nil
@assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) }
@helpers = self.class.helpers || Module.new
self.assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) }
self.helpers = self.class.helpers || Module.new
if @_controller = controller
@_request = controller.request if controller.respond_to?(:request)
end
@_config = ActiveSupport::InheritableOptions.new(controller.config) if controller && controller.respond_to?(:config)
config = controller && controller.respond_to?(:config) ? controller.config : {}
@_config = ActiveSupport::InheritableOptions.new(config)
@_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
@output_buffer = nil
@ -228,12 +225,6 @@ module ActionView #:nodoc:
@controller_path ||= controller && controller.controller_path
end
def punctuate_body!(part)
flush_output_buffer
response.body_parts << part
nil
end
ActiveSupport.run_load_hooks(:action_view, self)
end
end

View File

@ -220,89 +220,6 @@ module ActionView
end * "\n"
end
# Returns the text with all the Textile[http://www.textism.com/tools/textile] codes turned into HTML tags.
#
# You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
# <i>This method is only available if RedCloth[http://redcloth.org/] is available</i>.
#
# ==== Examples
# textilize("*This is Textile!* Rejoice!")
# # => "<p><strong>This is Textile!</strong> Rejoice!</p>"
#
# textilize("I _love_ ROR(Ruby on Rails)!")
# # => "<p>I <em>love</em> <acronym title="Ruby on Rails">ROR</acronym>!</p>"
#
# textilize("h2. Textile makes markup -easy- simple!")
# # => "<h2>Textile makes markup <del>easy</del> simple!</h2>"
#
# textilize("Visit the Rails website "here":http://www.rubyonrails.org/.)
# # => "<p>Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>.</p>"
#
# textilize("This is worded <strong>strongly</strong>")
# # => "<p>This is worded <strong>strongly</strong></p>"
#
# textilize("This is worded <strong>strongly</strong>", :filter_html)
# # => "<p>This is worded &lt;strong&gt;strongly&lt;/strong&gt;</p>"
#
def textilize(text, *options)
options ||= [:hard_breaks]
text = sanitize(text) unless text.html_safe? || options.delete(:safe)
if text.blank?
""
else
textilized = RedCloth.new(text, options)
textilized.to_html
end.html_safe
end
# Returns the text with all the Textile codes turned into HTML tags,
# but without the bounding <p> tag that RedCloth adds.
#
# You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
# <i>This method is only available if RedCloth[http://redcloth.org/] is available</i>.
#
# ==== Examples
# textilize_without_paragraph("*This is Textile!* Rejoice!")
# # => "<strong>This is Textile!</strong> Rejoice!"
#
# textilize_without_paragraph("I _love_ ROR(Ruby on Rails)!")
# # => "I <em>love</em> <acronym title="Ruby on Rails">ROR</acronym>!"
#
# textilize_without_paragraph("h2. Textile makes markup -easy- simple!")
# # => "<h2>Textile makes markup <del>easy</del> simple!</h2>"
#
# textilize_without_paragraph("Visit the Rails website "here":http://www.rubyonrails.org/.)
# # => "Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>."
def textilize_without_paragraph(text, *options)
textiled = textilize(text, *options)
if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
if textiled[-4..-1] == "</p>" then textiled = textiled[0..-5] end
return textiled
end
# Returns the text with all the Markdown codes turned into HTML tags.
# <i>This method requires BlueCloth[http://www.deveiate.org/projects/BlueCloth]
# to be available</i>.
#
# ==== Examples
# markdown("We are using __Markdown__ now!")
# # => "<p>We are using <strong>Markdown</strong> now!</p>"
#
# markdown("We like to _write_ `code`, not just _read_ it!")
# # => "<p>We like to <em>write</em> <code>code</code>, not just <em>read</em> it!</p>"
#
# markdown("The [Markdown website](http://daringfireball.net/projects/markdown/) has more information.")
# # => "<p>The <a href="http://daringfireball.net/projects/markdown/">Markdown website</a>
# # has more information.</p>"
#
# markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")')
# # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
def markdown(text, *options)
text = sanitize(text) unless text.html_safe? || options.delete(:safe)
(text.blank? ? "" : BlueCloth.new(text).to_html).html_safe
end
# Returns +text+ transformed into HTML using simple formatting rules.
# Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
# paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is

View File

@ -131,12 +131,14 @@ module ActionView
end
def _view
view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller)
view.singleton_class.send :include, _helpers
view.singleton_class.send :include, @controller._router.url_helpers
view.singleton_class.send :delegate, :alert, :notice, :to => "request.flash"
view.output_buffer = self.output_buffer
view
@_view ||= begin
view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller)
view.singleton_class.send :include, _helpers
view.singleton_class.send :include, @controller._router.url_helpers
view.singleton_class.send :delegate, :alert, :notice, :to => "request.flash"
view.output_buffer = self.output_buffer
view
end
end
EXCLUDE_IVARS = %w{

View File

@ -24,6 +24,9 @@ require 'action_view/testing/resolvers'
require 'action_dispatch'
require 'active_support/dependencies'
require 'active_model'
require 'active_record'
require 'action_controller/caching'
require 'action_controller/caching/sweeping'
begin
require 'ruby-debug'

View File

@ -90,6 +90,7 @@ class RecordIdentifierController < ActionController::Base
end
class ControllerClassTests < ActiveSupport::TestCase
def test_controller_path
assert_equal 'empty', EmptyController.controller_path
assert_equal EmptyController.controller_path, EmptyController.new.controller_path
@ -166,7 +167,15 @@ class PerformActionTest < ActionController::TestCase
rescue_action_in_public!
end
def test_process_should_be_precise
use_controller EmptyController
exception = assert_raise AbstractController::ActionNotFound do
get :non_existent
end
assert_equal exception.message, "The action 'non_existent' could not be found for EmptyController"
end
def test_get_on_priv_should_show_selector
use_controller MethodMissingController
get :shouldnt_be_called

View File

@ -445,6 +445,23 @@ class FilterTest < ActionController::TestCase
end
class ::AppSweeper < ActionController::Caching::Sweeper; end
class SweeperTestController < ActionController::Base
cache_sweeper :app_sweeper
def show
render :text => 'hello world'
end
end
def test_sweeper_should_not_block_rendering
response = test_process(SweeperTestController)
assert_equal 'hello world', response.body
end
def test_before_method_of_sweeper_should_always_return_true
sweeper = ActionController::Caching::Sweeper.send(:new)
assert sweeper.before(TestController.new)
end
def test_non_yielding_around_filters_not_returning_false_do_not_raise
controller = NonYieldingAroundFilterController.new
controller.instance_variable_set "@filter_return_value", true

View File

@ -34,33 +34,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
resources :users do
shallow do
resources :photos do
resources :types do
member do
post :preview
end
collection do
delete :erase
end
end
end
end
end
shallow do
resources :teams do
resources :players
end
resources :countries do
resources :cities do
resources :places
end
end
end
match 'account/logout' => redirect("/logout"), :as => :logout_redirect
match 'account/login', :to => redirect("/login")
@ -171,6 +144,16 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :sheep
resources :clients do
namespace :google do
resource :account do
namespace :secret do
resource :info
end
end
end
end
match 'sprockets.js' => ::TestRoutingMapper::SprocketsApp
match 'people/:id/update', :to => 'people#update', :as => :update_person
@ -779,18 +762,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
def test_shallow_routes
with_test_routes do
assert_equal '/photos/4', photo_path(4)
assert_equal '/types/10/edit', edit_type_path(10)
assert_equal '/types/5/preview', preview_type_path(5)
assert_equal '/photos/2/types', photo_types_path(2)
assert_equal '/cities/1/places', url_for(:controller => :places, :action => :index, :city_id => 1, :only_path => true)
assert_equal '/teams/new', url_for(:controller => :teams, :action => :new, :only_path => true)
assert_equal '/photos/11/types/erase', url_for(:controller => :types, :action => :erase, :photo_id => 11, :only_path => true)
end
end
def test_update_project_person
with_test_routes do
get '/projects/1/people/2/update'
@ -852,6 +823,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/account/admin/subscription', account_admin_subscription_path
end
end
def test_namespace_nested_in_resources
with_test_routes do
get '/clients/1/google/account'
assert_equal '/clients/1/google/account', client_google_account_path(1)
assert_equal 'google/accounts#show', @response.body
get '/clients/1/google/account/secret/info'
assert_equal '/clients/1/google/account/secret/info', client_google_account_secret_info_path(1)
assert_equal 'google/secret/infos#show', @response.body
end
end
def test_articles_with_id
with_test_routes do

View File

@ -1,25 +0,0 @@
require 'abstract_unit'
class BodyPartsTest < ActionController::TestCase
RENDERINGS = [Object.new, Object.new, Object.new]
class TestController < ActionController::Base
def response_body() "" end
def index
RENDERINGS.each do |rendering|
view_context.punctuate_body! rendering
end
end
end
tests TestController
def test_body_parts
get :index
# TestProcess buffers body_parts into body
# TODO: Rewrite test w/o going through process
assert_equal RENDERINGS, @response.body_parts
assert_equal RENDERINGS.join, @response.body
end
end

View File

@ -37,6 +37,10 @@ module ActionView
include SharedTests
test_case = self
test "memoizes the _view" do
assert_same _view, _view
end
test "works without testing a helper module" do
assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy'))
end

View File

@ -1,17 +1,6 @@
# encoding: us-ascii
require 'abstract_unit'
require 'testing_sandbox'
begin
require 'redcloth'
rescue LoadError
$stderr.puts "Skipping textilize tests. `gem install RedCloth` to enable."
end
begin
require 'bluecloth'
rescue LoadError
$stderr.puts "Skipping markdown tests. 'gem install bluecloth' to enable."
end
class TextHelperTest < ActionView::TestCase
tests ActionView::Helpers::TextHelper
@ -665,101 +654,4 @@ class TextHelperTest < ActionView::TestCase
assert_equal("red", cycle("red", "blue"))
assert_equal(%w{Specialized Fuji Giant}, @cycles)
end
# TODO test textilize_without_paragraph and markdown
if defined? RedCloth
def test_textilize_should_be_html_safe
assert textilize("*This is Textile!* Rejoice!").html_safe?
end
def test_textilize
assert_equal("<p><strong>This is Textile!</strong> Rejoice!</p>", textilize("*This is Textile!* Rejoice!"))
end
def test_textilize_with_blank
assert_equal("", textilize(""))
end
def test_textilize_with_options
assert_equal("<p>This is worded &lt;strong&gt;strongly&lt;/strong&gt;</p>", textilize("This is worded <strong>strongly</strong>", :filter_html))
end
def test_textilize_should_sanitize_unsafe_input
assert_equal("<p>This is worded <strong>strongly</strong></p>", textilize("This is worded <strong>strongly</strong><script>code!</script>"))
end
def test_textilize_should_not_sanitize_input_if_safe_option
assert_equal("<p>This is worded <strong>strongly</strong><script>code!</script></p>", textilize("This is worded <strong>strongly</strong><script>code!</script>", :safe))
end
def test_textilize_should_not_sanitize_safe_input
assert_equal("<p>This is worded <strong>strongly</strong><script>code!</script></p>", textilize("This is worded <strong>strongly</strong><script>code!</script>".html_safe))
end
def test_textilize_with_hard_breaks
assert_equal("<p>This is one scary world.<br />\n True.</p>", textilize("This is one scary world.\n True."))
end
def test_textilize_without_paragraph_should_be_html_safe
textilize_without_paragraph("*This is Textile!* Rejoice!").html_safe?
end
def test_textilize_without_paragraph
assert_equal("<strong>This is Textile!</strong> Rejoice!", textilize_without_paragraph("*This is Textile!* Rejoice!"))
end
def test_textilize_without_paragraph_with_blank
assert_equal("", textilize_without_paragraph(""))
end
def test_textilize_without_paragraph_with_options
assert_equal("This is worded &lt;strong&gt;strongly&lt;/strong&gt;", textilize_without_paragraph("This is worded <strong>strongly</strong>", :filter_html))
end
def test_textilize_without_paragraph_should_sanitize_unsafe_input
assert_equal("This is worded <strong>strongly</strong>", textilize_without_paragraph("This is worded <strong>strongly</strong><script>code!</script>"))
end
def test_textilize_without_paragraph_should_not_sanitize_input_if_safe_option
assert_equal("This is worded <strong>strongly</strong><script>code!</script>", textilize_without_paragraph("This is worded <strong>strongly</strong><script>code!</script>", :safe))
end
def test_textilize_without_paragraph_should_not_sanitize_safe_input
assert_equal("This is worded <strong>strongly</strong><script>code!</script>", textilize_without_paragraph("This is worded <strong>strongly</strong><script>code!</script>".html_safe))
end
def test_textilize_without_paragraph_with_hard_breaks
assert_equal("This is one scary world.<br />\n True.", textilize_without_paragraph("This is one scary world.\n True."))
end
end
if defined? BlueCloth
def test_markdown_should_be_html_safe
assert markdown("We are using __Markdown__ now!").html_safe?
end
def test_markdown
assert_equal("<p>We are using <strong>Markdown</strong> now!</p>", markdown("We are using __Markdown__ now!"))
end
def test_markdown_with_blank
assert_equal("", markdown(""))
end
def test_markdown_should_sanitize_unsafe_input
assert_equal("<p>This is worded <strong>strongly</strong></p>", markdown("This is worded <strong>strongly</strong><script>code!</script>"))
end
def test_markdown_should_not_sanitize_input_if_safe_option
assert_equal("<p>This is worded <strong>strongly</strong><script>code!</script></p>", markdown("This is worded <strong>strongly</strong><script>code!</script>", :safe))
end
def test_markdown_should_not_sanitize_safe_input
assert_equal("<p>This is worded <strong>strongly</strong><script>code!</script></p>", markdown("This is worded <strong>strongly</strong><script>code!</script>".html_safe))
end
def test_markdown_with_hard_breaks
assert_equal("<p>This is one scary world.</p>\n\n<p>True.</p>", markdown("This is one scary world.\n\nTrue."))
end
end
end

View File

@ -50,9 +50,8 @@ module ActiveModel
extend HelperMethods
include HelperMethods
define_callbacks :validate, :scope => :name
attr_accessor :validation_context
define_callbacks :validate, :scope => :name
class_attribute :_validators
self._validators = Hash.new { |h,k| h[k] = [] }
@ -128,8 +127,7 @@ module ActiveModel
set_callback(:validate, *args, &block)
end
# List all validators that being used to validate the model using +validates_with+
# method.
# List all validators that being used to validate the model using +validates_with+ method.
def validators
_validators.values.flatten.uniq
end
@ -139,9 +137,17 @@ module ActiveModel
_validators[attribute.to_sym]
end
# Check if method is an attribute method or not.
def attribute_method?(attribute)
method_defined?(attribute)
end
# Copy validators on inheritance.
def inherited(base)
dup = _validators.dup
base._validators = dup.each { |k, v| dup[k] = v.dup }
super
end
end
# Returns the Errors object that holds all information about attribute error messages.

View File

@ -1,5 +1,7 @@
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
* Fixed that ActiveRecord::Base.compute_type would swallow NoMethodError #4751 [Andrew Bloomgarden, Andrew White]
* Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik]
Example:
@ -12,6 +14,8 @@
* find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune]
* New callbacks: after_commit and after_rollback. Do expensive operations like image thumbnailing after_commit instead of after_save. #2991 [Brian Durand]
* Serialized attributes are not converted to YAML if they are any of the formats that can be serialized to XML (like Hash, Array and Strings). [José Valim]
* Destroy uses optimistic locking. If lock_version on the record you're destroying doesn't match lock_version in the database, a StaleObjectError is raised. #1966 [Curtis Hawthorne]

View File

@ -388,7 +388,7 @@ module ActiveRecord
begin
if !loaded?
if @target.is_a?(Array) && @target.any?
@target = find_target + @target.find_all {|t| t.new_record? }
@target = find_target.map { |f| i = @target.index(f); i ? @target.delete_at(i) : f } + @target
else
@target = find_target
end

View File

@ -1219,7 +1219,9 @@ module ActiveRecord #:nodoc:
begin
constant = candidate.constantize
return constant if candidate == constant.to_s
rescue NameError
rescue NameError => e
# We don't want to swallow NoMethodError < NameError errors
raise e unless e.instance_of?(NameError)
rescue ArgumentError
end
end

View File

@ -122,6 +122,8 @@ module ActiveRecord
requires_new = options[:requires_new] || !last_transaction_joinable
transaction_open = false
@_current_transaction_records ||= []
begin
if block_given?
if requires_new || open_transactions == 0
@ -132,6 +134,7 @@ module ActiveRecord
end
increment_open_transactions
transaction_open = true
@_current_transaction_records.push([])
end
yield
end
@ -141,8 +144,10 @@ module ActiveRecord
decrement_open_transactions
if open_transactions == 0
rollback_db_transaction
rollback_transaction_records(true)
else
rollback_to_savepoint
rollback_transaction_records(false)
end
end
raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
@ -157,20 +162,35 @@ module ActiveRecord
begin
if open_transactions == 0
commit_db_transaction
commit_transaction_records
else
release_savepoint
save_point_records = @_current_transaction_records.pop
unless save_point_records.blank?
@_current_transaction_records.push([]) if @_current_transaction_records.empty?
@_current_transaction_records.last.concat(save_point_records)
end
end
rescue Exception => database_transaction_rollback
if open_transactions == 0
rollback_db_transaction
rollback_transaction_records(true)
else
rollback_to_savepoint
rollback_transaction_records(false)
end
raise
end
end
end
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
# can be called.
def add_transaction_record(record)
last_batch = @_current_transaction_records.last
last_batch << record if last_batch
end
# Begins the transaction (and turns off auto-committing).
def begin_db_transaction() end
@ -268,6 +288,42 @@ module ActiveRecord
limit.to_i
end
end
# Send a rollback message to all records after they have been rolled back. If rollback
# is false, only rollback records since the last save point.
def rollback_transaction_records(rollback) #:nodoc
if rollback
records = @_current_transaction_records.flatten
@_current_transaction_records.clear
else
records = @_current_transaction_records.pop
end
unless records.blank?
records.uniq.each do |record|
begin
record.rolledback!(rollback)
rescue Exception => e
record.logger.error(e) if record.respond_to?(:logger)
end
end
end
end
# Send a commit message to all records after they have been committed.
def commit_transaction_records #:nodoc
records = @_current_transaction_records.flatten
@_current_transaction_records.clear
unless records.blank?
records.uniq.each do |record|
begin
record.committed!
rescue Exception => e
record.logger.error(e) if record.respond_to?(:logger)
end
end
end
end
end
end
end

View File

@ -787,16 +787,14 @@ class Fixture #:nodoc:
end
def key_list
columns = @fixture.keys.collect{ |column_name| @connection.quote_column_name(column_name) }
columns.join(", ")
@fixture.keys.map { |column_name| @connection.quote_column_name(column_name) }.join(', ')
end
def value_list
list = @fixture.inject([]) do |fixtures, (key, value)|
col = model_class.columns_hash[key] if model_class.respond_to?(:ancestors) && model_class.ancestors.include?(ActiveRecord::Base)
fixtures << @connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
end
list * ', '
cols = (model_class && model_class < ActiveRecord::Base) ? model_class.columns_hash : {}
@fixture.map do |key, value|
@connection.quote(value, cols[key]).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
end.join(', ')
end
def find

View File

@ -88,7 +88,7 @@ module ActiveRecord
#
class Observer < ActiveModel::Observer
class_attribute :observed_methods
self.observed_methods = []
self.observed_methods = [].freeze
def initialize
super
@ -97,7 +97,11 @@ module ActiveRecord
def self.method_added(method)
method = method.to_sym
observed_methods << method if ActiveRecord::Callbacks::CALLBACKS.include?(method)
if ActiveRecord::Callbacks::CALLBACKS.include?(method)
self.observed_methods += [method]
self.observed_methods.freeze
end
end
protected

View File

@ -5,7 +5,7 @@ namespace :db do
end
namespace :create do
desc 'Create all the local databases defined in config/database.yml'
# desc 'Create all the local databases defined in config/database.yml'
task :all => :load_config do
ActiveRecord::Base.configurations.each_value do |config|
# Skip entries that don't have a database key, such as the first entry here:
@ -26,7 +26,7 @@ namespace :db do
end
end
desc 'Create the database defined in config/database.yml for the current Rails.env - also makes test database if in development mode'
desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)'
task :create => :load_config do
# Make the test database at the same time as the development one, if it exists
if Rails.env.development? && ActiveRecord::Base.configurations['test']
@ -100,7 +100,7 @@ namespace :db do
end
namespace :drop do
desc 'Drops all the local databases defined in config/database.yml'
# desc 'Drops all the local databases defined in config/database.yml'
task :all => :load_config do
ActiveRecord::Base.configurations.each_value do |config|
# Skip entries that don't have a database key
@ -115,7 +115,7 @@ namespace :db do
end
end
desc 'Drops the database for the current Rails.env'
desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)'
task :drop => :load_config do
config = ActiveRecord::Base.configurations[Rails.env || 'development']
begin
@ -134,7 +134,7 @@ namespace :db do
end
desc "Migrate the database through scripts in db/migrate and update db/schema.rb by invoking db:schema:dump. Target specific version with VERSION=x. Turn off output with VERBOSE=false."
desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
task :migrate => :environment do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
@ -142,7 +142,7 @@ namespace :db do
end
namespace :migrate do
desc 'Rollbacks the database one migration and re migrate up. If you want to rollback more than one step, define STEP=x. Target specific version with VERSION=x.'
# desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
task :redo => :environment do
if ENV["VERSION"]
Rake::Task["db:migrate:down"].invoke
@ -153,10 +153,10 @@ namespace :db do
end
end
desc 'Resets your database using your migrations for the current environment'
# desc 'Resets your database using your migrations for the current environment'
task :reset => ["db:drop", "db:create", "db:migrate"]
desc 'Runs the "up" for a given migration VERSION.'
# desc 'Runs the "up" for a given migration VERSION.'
task :up => :environment do
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
raise "VERSION is required" unless version
@ -164,7 +164,7 @@ namespace :db do
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
desc 'Runs the "down" for a given migration VERSION.'
# desc 'Runs the "down" for a given migration VERSION.'
task :down => :environment do
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
raise "VERSION is required" unless version
@ -173,24 +173,24 @@ namespace :db do
end
end
desc 'Rolls the schema back to the previous version. Specify the number of steps with STEP=n'
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
task :rollback => :environment do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.rollback('db/migrate/', step)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
desc 'Pushes the schema to the next version. Specify the number of steps with STEP=n'
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
task :forward => :environment do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.forward('db/migrate/', step)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
# desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
task :reset => [ 'db:drop', 'db:setup' ]
desc "Retrieves the charset for the current environment's database"
# desc "Retrieves the charset for the current environment's database"
task :charset => :environment do
config = ActiveRecord::Base.configurations[Rails.env || 'development']
case config['adapter']
@ -208,7 +208,7 @@ namespace :db do
end
end
desc "Retrieves the collation for the current environment's database"
# desc "Retrieves the collation for the current environment's database"
task :collation => :environment do
config = ActiveRecord::Base.configurations[Rails.env || 'development']
case config['adapter']
@ -225,7 +225,7 @@ namespace :db do
puts "Current version: #{ActiveRecord::Migrator.current_version}"
end
desc "Raises an error if there are pending migrations"
# desc "Raises an error if there are pending migrations"
task :abort_if_pending_migrations => :environment do
if defined? ActiveRecord
pending_migrations = ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations
@ -240,7 +240,7 @@ namespace :db do
end
end
desc 'Create the database, load the schema, and initialize with the seed data'
desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)'
task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ]
desc 'Load the seed data from db/seeds.rb'
@ -263,7 +263,7 @@ namespace :db do
end
end
desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
# desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
task :identify => :environment do
require 'active_record/fixtures'
@ -347,17 +347,17 @@ namespace :db do
end
namespace :test do
desc "Recreate the test database from the current schema.rb"
# desc "Recreate the test database from the current schema.rb"
task :load => 'db:test:purge' do
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
ActiveRecord::Schema.verbose = false
Rake::Task["db:schema:load"].invoke
end
desc "Recreate the test database from the current environment's database schema"
# desc "Recreate the test database from the current environment's database schema"
task :clone => %w(db:schema:dump db:test:load)
desc "Recreate the test databases from the development structure"
# desc "Recreate the test databases from the development structure"
task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
abcs = ActiveRecord::Base.configurations
case abcs["test"]["adapter"]
@ -391,7 +391,7 @@ namespace :db do
end
end
desc "Empty the test database"
# desc "Empty the test database"
task :purge => :environment do
abcs = ActiveRecord::Base.configurations
case abcs["test"]["adapter"]
@ -422,7 +422,7 @@ namespace :db do
end
end
desc 'Check for pending migrations and load the test schema'
# desc 'Check for pending migrations and load the test schema'
task :prepare => 'db:abort_if_pending_migrations' do
if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]].invoke
@ -431,7 +431,7 @@ namespace :db do
end
namespace :sessions do
desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
# desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
task :create => :environment do
raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
require 'rails/generators'
@ -440,7 +440,7 @@ namespace :db do
Rails::Generators::SessionMigrationGenerator.start [ ENV["MIGRATION"] || "add_sessions_table" ]
end
desc "Clear the sessions table"
# desc "Clear the sessions table"
task :clear => :environment do
ActiveRecord::Base.connection.execute "DELETE FROM #{session_table_name}"
end

View File

@ -8,6 +8,10 @@ module ActiveRecord
class TransactionError < ActiveRecordError # :nodoc:
end
included do
define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name]
end
# Transactions are protective blocks where SQL statements are only permanent
# if they can all succeed as one atomic action. The classic example is a
# transfer between two accounts where you can only have a deposit if the
@ -72,7 +76,7 @@ module ActiveRecord
#
# Both +save+ and +destroy+ come wrapped in a transaction that ensures
# that whatever you do in validations or callbacks will happen under its
# protected cover. So you can use validations to check for values that
# protected cover. So you can use validations to check for values that
# the transaction depends on or you can raise exceptions in the callbacks
# to rollback, including <tt>after_*</tt> callbacks.
#
@ -158,6 +162,21 @@ module ActiveRecord
# http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
# for more information about savepoints.
#
# === Callbacks
#
# There are two types of callbacks associated with committing and rolling back transactions:
# +after_commit+ and +after_rollback+.
#
# +after_commit+ callbacks are called on every record saved or destroyed within a
# transaction immediately after the transaction is committed. +after_rollback+ callbacks
# are called on every record saved or destroyed within a transaction immediately after the
# transaction or savepoint is rolled back.
#
# These callbacks are useful for interacting with other systems since you will be guaranteed
# that the callback is only executed when the database is in a permanent state. For example,
# +after_commit+ is a good spot to put in a hook to clearing a cache since clearing it from
# within a transaction could trigger the cache to be regenerated before the database is updated.
#
# === Caveats
#
# If you're on MySQL, then do not use DDL operations in nested transactions
@ -182,6 +201,24 @@ module ActiveRecord
# See the ConnectionAdapters::DatabaseStatements#transaction API docs.
connection.transaction(options, &block)
end
def after_commit(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:on]
options[:if] = Array.wrap(options[:if])
options[:if] << "transaction_include_action?(:#{options[:on]})"
end
set_callback(:commit, :after, *args, &block)
end
def after_rollback(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:on]
options[:if] = Array.wrap(options[:if])
options[:if] << "transaction_include_action?(:#{options[:on]})"
end
set_callback(:rollback, :after, *args, &block)
end
end
# See ActiveRecord::Transactions::ClassMethods for detailed documentation.
@ -205,19 +242,36 @@ module ActiveRecord
# Reset id and @new_record if the transaction rolls back.
def rollback_active_record_state!
id_present = has_attribute?(self.class.primary_key)
previous_id = id
previous_new_record = new_record?
remember_transaction_record_state
yield
rescue Exception
@new_record = previous_new_record
if id_present
self.id = previous_id
else
@attributes.delete(self.class.primary_key)
@attributes_cache.delete(self.class.primary_key)
end
restore_transaction_record_state
raise
ensure
clear_transaction_record_state
end
# Call the after_commit callbacks
def committed! #:nodoc:
_run_commit_callbacks
ensure
clear_transaction_record_state
end
# Call the after rollback callbacks. The restore_state argument indicates if the record
# state should be rolled back to the beginning or just to the last savepoint.
def rolledback!(force_restore_state = false) #:nodoc:
_run_rollback_callbacks
ensure
restore_transaction_record_state(force_restore_state)
end
# Add the record to the current transaction so that the :after_rollback and :after_commit callbacks
# can be called.
def add_to_transaction
if self.class.connection.add_transaction_record(self)
remember_transaction_record_state
end
end
# Executes +method+ within a transaction and captures its return value as a
@ -229,10 +283,71 @@ module ActiveRecord
def with_transaction_returning_status
status = nil
self.class.transaction do
add_to_transaction
status = yield
raise ActiveRecord::Rollback unless status
end
status
end
protected
# Save the new record state and id of a record so it can be restored later if a transaction fails.
def remember_transaction_record_state #:nodoc
@_start_transaction_state ||= {}
unless @_start_transaction_state.include?(:new_record)
@_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
@_start_transaction_state[:new_record] = @new_record
end
unless @_start_transaction_state.include?(:destroyed)
@_start_transaction_state[:destroyed] = @destroyed
end
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
end
# Clear the new record state and id of a record.
def clear_transaction_record_state #:nodoc
if defined?(@_start_transaction_state)
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1
end
end
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
def restore_transaction_record_state(force = false) #:nodoc
if defined?(@_start_transaction_state)
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
if @_start_transaction_state[:level] < 1
restore_state = remove_instance_variable(:@_start_transaction_state)
if restore_state
@new_record = restore_state[:new_record]
@destroyed = restore_state[:destroyed]
if restore_state[:id]
self.id = restore_state[:id]
else
@attributes.delete(self.class.primary_key)
@attributes_cache.delete(self.class.primary_key)
end
end
end
end
end
# Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
def transaction_record_state(state) #:nodoc
@_start_transaction_state[state] if defined?(@_start_transaction_state)
end
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
def transaction_include_action?(action) #:nodoc
case action
when :create
transaction_record_state(:new_record)
when :destroy
destroyed?
when :update
!(transaction_record_state(:new_record) || destroyed?)
end
end
end
end

View File

@ -4,6 +4,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase
def setup
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
alias_method :execute_without_stub, :execute
remove_method :execute
def execute(sql, name = nil) return sql end
end
end
@ -66,7 +67,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase
assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people')
end
def test_add_timestamps
def test_add_timestamps
with_real_execute do
begin
ActiveRecord::Base.connection.create_table :delete_me do |t|
@ -79,8 +80,8 @@ class ActiveSchemaTest < ActiveRecord::TestCase
end
end
end
def test_remove_timestamps
def test_remove_timestamps
with_real_execute do
begin
ActiveRecord::Base.connection.create_table :delete_me do |t|
@ -106,6 +107,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase
ensure
#before finishing, we restore the alias to the mock-up method
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
remove_method :execute
alias_method :execute, :execute_with_stub
end
end

View File

@ -4,6 +4,7 @@ class PostgresqlActiveSchemaTest < Test::Unit::TestCase
def setup
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
alias_method :real_execute, :execute
remove_method :execute
def execute(sql, name = nil) sql end
end
end

View File

@ -41,9 +41,9 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_grafts_stashed_associations_to_correct_parent
assert_nothing_raised do
Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').all
Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('primary_contacts_people_2.id').all
end
assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').first
assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('primary_contacts_people_2.id').first
end
def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations

View File

@ -2334,6 +2334,23 @@ class BasicsTest < ActiveRecord::TestCase
assert !Minimalistic.new.freeze.dup.frozen?
end
def test_compute_type_success
assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author')
end
def test_compute_type_nonexistent_constant
assert_raises NameError do
ActiveRecord::Base.send :compute_type, 'NonexistentModel'
end
end
def test_compute_type_no_method_error
String.any_instance.stubs(:constantize).raises(NoMethodError)
assert_raises NoMethodError do
ActiveRecord::Base.send :compute_type, 'InvalidModel'
end
end
protected
def with_env_tz(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz

View File

@ -1019,7 +1019,7 @@ if ActiveRecord::Base.connection.supports_migrations?
# This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
# precision/scale explicitly left out. By the SQL standard, numbers
# assigned to this field should be truncated but that's seldom respected.
if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
if current_adapter?(:PostgreSQLAdapter)
# - PostgreSQL changes the SQL spec on columns declared simply as
# "decimal" to something more useful: instead of being given a scale
# of 0, they take on the compile-time limit for precision and scale,

View File

@ -466,6 +466,20 @@ module NestedAttributesOnACollectionAssociationTests
assert_equal 'Grace OMalley', @child_1.reload.name
end
def test_should_not_overwrite_unsaved_updates_when_loading_association
@pirate.reload
@pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
@pirate.send(@association_name).send(:load_target)
assert_equal 'Grace OMalley', @pirate.send(@association_name).target.find { |r| r.id == @child_1.id }.name
end
def test_should_preserve_order_when_not_overwriting_unsaved_updates
@pirate.reload
@pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
@pirate.send(@association_name).send(:load_target)
assert_equal @pirate.send(@association_name).target.first.id, @child_1.id
end
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
@child_1.stubs(:id).returns('ABC1X')
@child_2.stubs(:id).returns('ABC2X')

View File

@ -0,0 +1,240 @@
require "cases/helper"
require 'models/topic'
require 'models/reply'
class TransactionCallbacksTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
fixtures :topics
class TopicWithCallbacks < ActiveRecord::Base
set_table_name :topics
after_commit{|record| record.send(:do_after_commit, nil)}
after_commit(:on => :create){|record| record.send(:do_after_commit, :create)}
after_commit(:on => :update){|record| record.send(:do_after_commit, :update)}
after_commit(:on => :destroy){|record| record.send(:do_after_commit, :destroy)}
after_rollback{|record| record.send(:do_after_rollback, nil)}
after_rollback(:on => :create){|record| record.send(:do_after_rollback, :create)}
after_rollback(:on => :update){|record| record.send(:do_after_rollback, :update)}
after_rollback(:on => :destroy){|record| record.send(:do_after_rollback, :destroy)}
def history
@history ||= []
end
def after_commit_block(on = nil, &block)
@after_commit ||= {}
@after_commit[on] ||= []
@after_commit[on] << block
end
def after_rollback_block(on = nil, &block)
@after_rollback ||= {}
@after_rollback[on] ||= []
@after_rollback[on] << block
end
def do_after_commit(on)
blocks = @after_commit[on] if defined?(@after_commit)
blocks.each{|b| b.call(self)} if blocks
end
def do_after_rollback(on)
blocks = @after_rollback[on] if defined?(@after_rollback)
blocks.each{|b| b.call(self)} if blocks
end
end
def setup
@first, @second = TopicWithCallbacks.find(1, 3).sort_by { |t| t.id }
end
def test_call_after_commit_after_transaction_commits
@first.after_commit_block{|r| r.history << :after_commit}
@first.after_rollback_block{|r| r.history << :after_rollback}
@first.save!
assert_equal [:after_commit], @first.history
end
def test_only_call_after_commit_on_update_after_transaction_commits_for_existing_record
@first.after_commit_block(:create){|r| r.history << :commit_on_create}
@first.after_commit_block(:update){|r| r.history << :commit_on_update}
@first.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
@first.after_rollback_block(:create){|r| r.history << :rollback_on_create}
@first.after_rollback_block(:update){|r| r.history << :rollback_on_update}
@first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
@first.save!
assert_equal [:commit_on_update], @first.history
end
def test_only_call_after_commit_on_destroy_after_transaction_commits_for_destroyed_record
@first.after_commit_block(:create){|r| r.history << :commit_on_create}
@first.after_commit_block(:update){|r| r.history << :commit_on_update}
@first.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
@first.after_rollback_block(:create){|r| r.history << :rollback_on_create}
@first.after_rollback_block(:update){|r| r.history << :rollback_on_update}
@first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
@first.destroy
assert_equal [:commit_on_destroy], @first.history
end
def test_only_call_after_commit_on_create_after_transaction_commits_for_new_record
@new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Date.today)
@new_record.after_commit_block(:create){|r| r.history << :commit_on_create}
@new_record.after_commit_block(:update){|r| r.history << :commit_on_update}
@new_record.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
@new_record.after_rollback_block(:create){|r| r.history << :rollback_on_create}
@new_record.after_rollback_block(:update){|r| r.history << :rollback_on_update}
@new_record.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
@new_record.save!
assert_equal [:commit_on_create], @new_record.history
end
def test_call_after_rollback_after_transaction_rollsback
@first.after_commit_block{|r| r.history << :after_commit}
@first.after_rollback_block{|r| r.history << :after_rollback}
Topic.transaction do
@first.save!
raise ActiveRecord::Rollback
end
assert_equal [:after_rollback], @first.history
end
def test_only_call_after_rollback_on_update_after_transaction_rollsback_for_existing_record
@first.after_commit_block(:create){|r| r.history << :commit_on_create}
@first.after_commit_block(:update){|r| r.history << :commit_on_update}
@first.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
@first.after_rollback_block(:create){|r| r.history << :rollback_on_create}
@first.after_rollback_block(:update){|r| r.history << :rollback_on_update}
@first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
Topic.transaction do
@first.save!
raise ActiveRecord::Rollback
end
assert_equal [:rollback_on_update], @first.history
end
def test_only_call_after_rollback_on_destroy_after_transaction_rollsback_for_destroyed_record
@first.after_commit_block(:create){|r| r.history << :commit_on_create}
@first.after_commit_block(:update){|r| r.history << :commit_on_update}
@first.after_commit_block(:destroy){|r| r.history << :commit_on_update}
@first.after_rollback_block(:create){|r| r.history << :rollback_on_create}
@first.after_rollback_block(:update){|r| r.history << :rollback_on_update}
@first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
Topic.transaction do
@first.destroy
raise ActiveRecord::Rollback
end
assert_equal [:rollback_on_destroy], @first.history
end
def test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record
@new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Date.today)
@new_record.after_commit_block(:create){|r| r.history << :commit_on_create}
@new_record.after_commit_block(:update){|r| r.history << :commit_on_update}
@new_record.after_commit_block(:destroy){|r| r.history << :commit_on_destroy}
@new_record.after_rollback_block(:create){|r| r.history << :rollback_on_create}
@new_record.after_rollback_block(:update){|r| r.history << :rollback_on_update}
@new_record.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy}
Topic.transaction do
@new_record.save!
raise ActiveRecord::Rollback
end
assert_equal [:rollback_on_create], @new_record.history
end
def test_call_after_rollback_when_commit_fails
@first.connection.class.send(:alias_method, :real_method_commit_db_transaction, :commit_db_transaction)
begin
@first.connection.class.class_eval do
def commit_db_transaction; raise "boom!"; end
end
@first.after_commit_block{|r| r.history << :after_commit}
@first.after_rollback_block{|r| r.history << :after_rollback}
assert !@first.save rescue nil
assert_equal [:after_rollback], @first.history
ensure
@first.connection.class.send(:remove_method, :commit_db_transaction)
@first.connection.class.send(:alias_method, :commit_db_transaction, :real_method_commit_db_transaction)
end
end
def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint
def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
def @first.commits(i=0); @commits ||= 0; @commits += i if i; end
@first.after_rollback_block{|r| r.rollbacks(1)}
@first.after_commit_block{|r| r.commits(1)}
def @second.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
def @second.commits(i=0); @commits ||= 0; @commits += i if i; end
@second.after_rollback_block{|r| r.rollbacks(1)}
@second.after_commit_block{|r| r.commits(1)}
Topic.transaction do
@first.save!
Topic.transaction(:requires_new => true) do
@second.save!
raise ActiveRecord::Rollback
end
end
assert_equal 1, @first.commits
assert_equal 0, @first.rollbacks
assert_equal 0, @second.commits
assert_equal 1, @second.rollbacks
end
def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint_when_release_savepoint_fails
def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
def @first.commits(i=0); @commits ||= 0; @commits += i if i; end
@first.after_rollback_block{|r| r.rollbacks(1)}
@first.after_commit_block{|r| r.commits(1)}
Topic.transaction do
@first.save
Topic.transaction(:requires_new => true) do
@first.save!
raise ActiveRecord::Rollback
end
Topic.transaction(:requires_new => true) do
@first.save!
raise ActiveRecord::Rollback
end
end
assert_equal 1, @first.commits
assert_equal 2, @first.rollbacks
end
def test_after_transaction_callbacks_should_not_raise_errors
def @first.last_after_transaction_error=(e); @last_transaction_error = e; end
def @first.last_after_transaction_error; @last_transaction_error; end
@first.after_commit_block{|r| r.last_after_transaction_error = :commit; raise "fail!";}
@first.after_rollback_block{|r| r.last_after_transaction_error = :rollback; raise "fail!";}
@first.save!
assert_equal :commit, @first.last_after_transaction_error
Topic.transaction do
@first.save!
raise ActiveRecord::Rollback
end
assert_equal :rollback, @first.last_after_transaction_error
end
end

View File

@ -320,6 +320,33 @@ class TransactionTest < ActiveRecord::TestCase
end
end
def test_restore_active_record_state_for_all_records_in_a_transaction
topic_1 = Topic.new(:title => 'test_1')
topic_2 = Topic.new(:title => 'test_2')
Topic.transaction do
assert topic_1.save
assert topic_2.save
@first.save
@second.destroy
assert_equal false, topic_1.new_record?
assert_not_nil topic_1.id
assert_equal false, topic_2.new_record?
assert_not_nil topic_2.id
assert_equal false, @first.new_record?
assert_not_nil @first.id
assert_equal true, @second.destroyed?
raise ActiveRecord::Rollback
end
assert_equal true, topic_1.new_record?
assert_nil topic_1.id
assert_equal true, topic_2.new_record?
assert_nil topic_2.id
assert_equal false, @first.new_record?
assert_not_nil @first.id
assert_equal false, @second.destroyed?
end
if current_adapter?(:PostgreSQLAdapter) && defined?(PGconn::PQTRANS_IDLE)
def test_outside_transaction_works
assert Topic.connection.outside_transaction?
@ -382,6 +409,12 @@ class TransactionTest < ActiveRecord::TestCase
end
private
def define_callback_method(callback_method)
define_method(callback_method) do
self.history << [callback_method, :method]
end
end
def add_exception_raising_after_save_callback_to_topic
Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1
remove_method(:after_save_for_transaction)

View File

@ -9,6 +9,8 @@ require 'models/guid'
require 'models/owner'
require 'models/pet'
require 'models/event'
require 'models/parrot'
require 'models/company'
class ProtectedPerson < ActiveRecord::Base
set_table_name 'people'
@ -189,4 +191,12 @@ class ValidationsTest < ActiveRecord::TestCase
end
assert_equal ["always invalid", "invalid on update"], p.errors[:name]
end
def test_validators
assert_equal 1, Parrot.validators.size
assert_equal 1, Company.validators.size
assert_equal 1, Parrot.validators_on(:name).size
assert_equal 1, Company.validators_on(:name).size
end
end

View File

@ -1,7 +1,7 @@
class Pirate < ActiveRecord::Base
belongs_to :parrot, :validate => true
belongs_to :non_validated_parrot, :class_name => 'Parrot'
has_and_belongs_to_many :parrots, :validate => true
has_and_belongs_to_many :parrots, :validate => true, :order => 'parrots.id ASC'
has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot'
has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot",
:before_add => :log_before_add,
@ -21,7 +21,7 @@ class Pirate < ActiveRecord::Base
has_one :ship
has_one :update_only_ship, :class_name => 'Ship'
has_one :non_validated_ship, :class_name => 'Ship'
has_many :birds
has_many :birds, :order => 'birds.id ASC'
has_many :birds_with_method_callbacks, :class_name => "Bird",
:before_add => :log_before_add,
:after_add => :log_after_add,

View File

@ -25,5 +25,5 @@ Gem::Specification.new do |s|
s.add_dependency('activeresource', version)
s.add_dependency('actionmailer', version)
s.add_dependency('railties', version)
s.add_dependency('bundler', '>= 0.9.19')
s.add_dependency('bundler', '>= 0.9.26')
end

View File

@ -1,3 +1,9 @@
*Rails 3.0.0 [Release Candidate] (unreleased)*
* Abort generation/booting on Ruby 1.9.1. [fxn]
* Made the rails command work even when you're in a subdirectory [Chad Fowler]
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
* Version bump

View File

@ -32,11 +32,11 @@ h3. Upgrading to Rails 3
If you're upgrading an existing application, it's a great idea to have good test coverage before going in. You should also first upgrade to Rails 2.3.5 and make sure your application still runs as expected before attempting to update to Rails 3. Then take heed of the following changes:
h4. Rails 3 requires Ruby 1.8.7+
h4. Rails 3 requires at least Ruby 1.8.7
Rails 3.0 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. Rails 3.0 is also compatible with Ruby 1.9.2.
TIP: Note that Ruby 1.8.7 p248 and p249 has marshaling bugs that crash Rails 3.0.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing.
TIP: Note that Ruby 1.8.7 p248 and p249 has marshaling bugs that crash Rails 3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing.
h4. Rails Application object

View File

@ -17,7 +17,7 @@ This guide is designed for beginners who want to get started with a Rails applic
* The "Ruby":http://www.ruby-lang.org/en/downloads language version 1.8.7 or higher
TIP: Note that Ruby 1.8.7 p248 and p249 has marshaling bugs that crash Rails 3.0.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing.
TIP: Note that Ruby 1.8.7 p248 and p249 has marshaling bugs that crash Rails 3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing.
* The "RubyGems":http://rubyforge.org/frs/?group_id=126 packaging system
* A working installation of the "SQLite3 Database":http://www.sqlite.org

View File

@ -1,23 +1,5 @@
require 'rbconfig'
module Rails
module ScriptRailsLoader
RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
SCRIPT_RAILS = File.join('script', 'rails')
def self.exec_script_rails!
cwd = Dir.pwd
exec RUBY, SCRIPT_RAILS, *ARGV if File.exists?(SCRIPT_RAILS)
Dir.chdir("..") do
# Recurse in a chdir block: if the search fails we want to be sure
# the application is generated in the original working directory.
exec_script_rails! unless cwd == Dir.pwd
end
rescue SystemCallError
# could not chdir, no problem just return
end
end
end
require 'rails/script_rails_loader'
Rails::ScriptRailsLoader.exec_script_rails!

View File

@ -1,3 +1,5 @@
require 'active_support/time'
module Rails
module Generators
class GeneratedAttribute
@ -13,7 +15,6 @@ module Rails
when :time then :time_select
when :datetime, :timestamp then :datetime_select
when :date then :date_select
when :string then :text_field
when :text then :text_area
when :boolean then :check_box
else

View File

@ -29,7 +29,7 @@ link:files/vendor/rails/actionpack/README.html.
== Getting Started
1. At the command prompt, create a new Rails application:
<tt>rails myapp</tt> (where <tt>myapp</tt> is the application name)
<tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
2. Change directory to <tt>myapp</tt> and start the web server:
<tt>cd myapp; rails server</tt> (run with --help for options)
@ -44,31 +44,6 @@ the following resources handy:
* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
== Web Servers
By default, Rails will try to use Mongrel if it's installed when started with
<tt>rails server</tt>, otherwise Rails will use WEBrick, the web server that
ships with Ruby.
Mongrel is a Ruby-based web server with a C component (which requires
compilation) that is suitable for development. If you have Ruby Gems installed,
getting up and running with mongrel is as easy as:
<tt>sudo gem install mongrel</tt>.
You can find more info at: http://mongrel.rubyforge.org
You can alternatively run Rails applications with other Ruby web servers, e.g.,
{Thin}[http://code.macournoyer.com/thin/], {Ebb}[http://ebb.rubyforge.org/], and
Apache with {mod_rails}[http://www.modrails.com/]. However, <tt>rails server</tt>
doesn't search for or start them.
For production use, often a web/proxy server, e.g., {Apache}[http://apache.org],
{Nginx}[http://nginx.net/], {LiteSpeed}[http://litespeedtech.com/],
{Lighttpd}[http://www.lighttpd.net/], or {IIS}[http://www.iis.net/], is deployed
as the front end server with the chosen Ruby web server running in the back end
and receiving the proxied requests via one of several protocols (HTTP, CGI, FCGI).
== Debugging Rails
Sometimes your application goes wrong. Fortunately there are a lot of tools that

View File

@ -1,4 +1,3 @@
class ApplicationController < ActionController::Base
protect_from_forgery
layout 'application'
end

View File

@ -210,23 +210,6 @@
<div id="page">
<div id="sidebar">
<ul id="sidebar-items">
<li>
<form id="search" action="http://www.google.com/search" method="get" onSubmit="prepend();">
<input type="hidden" name="hl" value="en" />
<input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
<input type="submit" value="Search" /> the Rails site
</form>
</li>
<li>
<h3>Join the community</h3>
<ul class="links">
<li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
<li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
<li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
</ul>
</li>
<li>
<h3>Browse the documentation</h3>
<ul class="links">

View File

@ -189,18 +189,23 @@ module Rails
end
alias :assert_method :assert_instance_method
# Asserts the given field name gets translated to an attribute type
# properly.
# Asserts the given attribute type gets translated to a field type
# properly:
#
# assert_field_type :date, :date_select
#
def assert_field_type(name, attribute_type)
assert_equal(
Rails::Generators::GeneratedAttribute.new('test', name.to_s).field_type,
attribute_type
)
def assert_field_type(attribute_type, field_type)
assert_equal(field_type, create_generated_attribute(attribute_type).field_type)
end
# Asserts the given attribute type gets a proper default value:
#
# assert_field_type :string, "MyString"
#
def assert_field_default_value(attribute_type, value)
assert_equal(value, create_generated_attribute(attribute_type).default)
end
# Runs the generator configured for this class. The first argument is an array like
# command line arguments:
#
@ -226,6 +231,15 @@ module Rails
@generator ||= self.generator_class.new(args, options, config.reverse_merge(:destination_root => destination_root))
end
# Create a Rails::Generators::GeneratedAttribute by supplying the
# attribute type and, optionally, the attribute name:
#
# create_generated_attribute(:string, 'name')
#
def create_generated_attribute(attribute_type, name = 'test')
Rails::Generators::GeneratedAttribute.new(name, attribute_type.to_s)
end
protected
def destination_root_is_set? #:nodoc:

View File

@ -7,4 +7,10 @@ if ruby_release < min_release
You're running #{ruby_release}; please upgrade to continue.
end_message
elsif RUBY_VERSION == '1.9.1'
abort <<-EOS
Rails 3 does not work with Ruby 1.9.1. Please upgrade to 1.9.2.
EOS
end

View File

@ -0,0 +1,28 @@
require 'pathname'
module Rails
module ScriptRailsLoader
RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
SCRIPT_RAILS = File.join('script', 'rails')
def self.exec_script_rails!
cwd = Dir.pwd
exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application?
Dir.chdir("..") do
# Recurse in a chdir block: if the search fails we want to be sure
# the application is generated in the original working directory.
exec_script_rails! unless cwd == Dir.pwd
end
rescue SystemCallError
# could not chdir, no problem just return
end
def self.in_rails_application?
File.exists?(SCRIPT_RAILS) || in_rails_application_subdirectory?
end
def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd))
File.exists?(File.join(path, SCRIPT_RAILS)) || !path.root? && in_rails_application_subdirectory?(path.parent)
end
end
end

View File

@ -1,13 +1,13 @@
require 'rails/source_annotation_extractor'
desc "Enumerate all annotations"
desc "Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)"
task :notes do
SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", :tag => true
end
namespace :notes do
["OPTIMIZE", "FIXME", "TODO"].each do |annotation|
desc "Enumerate all #{annotation} annotations"
# desc "Enumerate all #{annotation} annotations"
task annotation.downcase.intern do
SourceAnnotationExtractor.enumerate annotation
end

View File

@ -1,13 +1,43 @@
require 'rake/rdoctask'
# Monkey-patch to remove redoc'ing and clobber descriptions to cut down on rake -T noise
class RDocTaskWithoutDescriptions < Rake::RDocTask
def define
task rdoc_task_name
task rerdoc_task_name => [clobber_task_name, rdoc_task_name]
task clobber_task_name do
rm_r rdoc_dir rescue nil
end
task :clobber => [clobber_task_name]
directory @rdoc_dir
task rdoc_task_name => [rdoc_target]
file rdoc_target => @rdoc_files + [Rake.application.rakefile] do
rm_r @rdoc_dir rescue nil
@before_running_rdoc.call if @before_running_rdoc
args = option_list + @rdoc_files
if @external
argstring = args.join(' ')
sh %{ruby -Ivendor vendor/rd #{argstring}}
else
require 'rdoc/rdoc'
RDoc::RDoc.new.document(args)
end
end
self
end
end
namespace :doc do
def gem_path(gem_name)
path = $LOAD_PATH.grep(/#{gem_name}[\w.-]*\/lib$/).first
yield File.dirname(path) if path
end
desc "Generate documentation for the application. Set custom template with TEMPLATE=/path/to/rdoc/template.rb or title with TITLE=\"Custom Title\""
Rake::RDocTask.new("app") { |rdoc|
RDocTaskWithoutDescriptions.new("app") { |rdoc|
rdoc.rdoc_dir = 'doc/app'
rdoc.template = ENV['template'] if ENV['template']
rdoc.title = ENV['title'] || "Rails Application Documentation"
@ -17,9 +47,10 @@ namespace :doc do
rdoc.rdoc_files.include('app/**/*.rb')
rdoc.rdoc_files.include('lib/**/*.rb')
}
Rake::Task['doc:app'].comment = "Generate docs for the app -- also availble doc:rails, doc:guides, doc:plugins (options: TEMPLATE=/rdoc-template.rb, TITLE=\"Custom Title\")"
desc 'Generate documentation for the Rails framework.'
Rake::RDocTask.new("rails") { |rdoc|
# desc 'Generate documentation for the Rails framework.'
RDocTaskWithoutDescriptions.new("rails") { |rdoc|
rdoc.rdoc_dir = 'doc/api'
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
rdoc.title = "Rails Framework Documentation"
@ -71,15 +102,15 @@ namespace :doc do
plugins = FileList['vendor/plugins/**'].collect { |plugin| File.basename(plugin) }
desc "Generate documentation for all installed plugins"
# desc "Generate documentation for all installed plugins"
task :plugins => plugins.collect { |plugin| "doc:plugins:#{plugin}" }
desc "Remove plugin documentation"
# desc "Remove plugin documentation"
task :clobber_plugins do
rm_rf 'doc/plugins' rescue nil
end
desc "Generate Rails guides"
# desc "Generate Rails Guides"
task :guides do
# FIXME: Reaching outside lib directory is a bad idea
require File.expand_path('../../../../guides/rails_guides', __FILE__)
@ -89,7 +120,7 @@ namespace :doc do
namespace :plugins do
# Define doc tasks for each plugin
plugins.each do |plugin|
desc "Generate documentation for the #{plugin} plugin"
# desc "Generate documentation for the #{plugin} plugin"
task(plugin => :environment) do
plugin_base = "vendor/plugins/#{plugin}"
options = []

View File

@ -1,22 +1,22 @@
namespace :rails do
namespace :freeze do
desc "The rails:freeze:gems is deprecated, please use bundle install instead"
# desc "The rails:freeze:gems is deprecated, please use bundle install instead"
task :gems do
abort "The rails:freeze:gems is deprecated, please use bundle install instead"
end
desc 'The freeze:edge command has been deprecated, specify the path setting in your app Gemfile instead and bundle install'
# desc 'The freeze:edge command has been deprecated, specify the path setting in your app Gemfile instead and bundle install'
task :edge do
abort 'The freeze:edge command has been deprecated, specify the path setting in your app Gemfile instead and bundle install'
end
end
desc 'The unfreeze command has been deprecated, please use bundler commands instead'
# desc 'The unfreeze command has been deprecated, please use bundler commands instead'
task :unfreeze do
abort 'The unfreeze command has been deprecated, please use bundler commands instead'
end
desc "Update both configs, scripts and public/javascripts from Rails"
desc "Update both configs and public/javascripts from Rails (or use just update:javascripts or update:configs)"
task :update => [ "update:configs", "update:javascripts", "update:scripts", "update:application_controller" ]
desc "Applies the template supplied by LOCATION=/path/to/template"
@ -31,7 +31,7 @@ namespace :rails do
end
namespace :templates do
desc "Copy all the templates from rails to the application directory for customization. Already existing local copies will be overwritten"
# desc "Copy all the templates from rails to the application directory for customization. Already existing local copies will be overwritten"
task :copy do
generators_lib = File.expand_path("../../generators", __FILE__)
project_templates = "#{Rails.root}/lib/templates"
@ -69,23 +69,23 @@ namespace :rails do
end
end
desc "Update config/boot.rb from your current rails install"
# desc "Update config/boot.rb from your current rails install"
task :configs do
invoke_from_app_generator :create_boot_file
invoke_from_app_generator :create_config_files
end
desc "Update Prototype javascripts from your current rails install"
# desc "Update Prototype javascripts from your current rails install"
task :javascripts do
invoke_from_app_generator :create_prototype_files
end
desc "Adds new scripts to the application script/ directory"
# desc "Adds new scripts to the application script/ directory"
task :scripts do
invoke_from_app_generator :create_script_files
end
desc "Rename application.rb to application_controller.rb"
# desc "Rename application.rb to application_controller.rb"
task :application_controller do
old_style = Rails.root + '/app/controllers/application.rb'
new_style = Rails.root + '/app/controllers/application_controller.rb'

View File

@ -7,30 +7,30 @@ task :rails_env do
end
end
desc 'Generate a crytographically secure secret key. This is typically used to generate a secret for cookie sessions.'
desc 'Generate a crytographically secure secret key (this is typically used to generate a secret for cookie sessions).'
task :secret do
require 'active_support/secure_random'
puts ActiveSupport::SecureRandom.hex(64)
end
desc 'Explain the current environment'
desc 'List versions of all Rails frameworks and the environment'
task :about do
puts Rails::Info
end
namespace :time do
namespace :zones do
desc 'Displays names of all time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6'
desc 'Displays all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET parameter, e.g., OFFSET=-6'
task :all do
build_time_zone_list(:all)
end
desc 'Displays names of US time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6'
# desc 'Displays names of US time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6'
task :us do
build_time_zone_list(:us_zones)
end
desc 'Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time'
# desc 'Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time'
task :local do
require 'active_support'
require 'active_support/time'

View File

@ -1,5 +1,5 @@
namespace :tmp do
desc "Clear session, cache, and socket files from tmp/"
desc "Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp:sockets:clear)"
task :clear => [ "tmp:sessions:clear", "tmp:cache:clear", "tmp:sockets:clear"]
desc "Creates tmp directories for sessions, cache, sockets, and pids"
@ -8,28 +8,28 @@ namespace :tmp do
end
namespace :sessions do
desc "Clears all files in tmp/sessions"
# desc "Clears all files in tmp/sessions"
task :clear do
FileUtils.rm(Dir['tmp/sessions/[^.]*'])
end
end
namespace :cache do
desc "Clears all files and directories in tmp/cache"
# desc "Clears all files and directories in tmp/cache"
task :clear do
FileUtils.rm_rf(Dir['tmp/cache/[^.]*'])
end
end
namespace :sockets do
desc "Clears all files in tmp/sockets"
# desc "Clears all files in tmp/sockets"
task :clear do
FileUtils.rm(Dir['tmp/sockets/[^.]*'])
end
end
namespace :pids do
desc "Clears all files in tmp/pids"
# desc "Clears all files in tmp/pids"
task :clear do
FileUtils.rm(Dir['tmp/pids/[^.]*'])
end

View File

@ -1,5 +1,35 @@
require 'rake/testtask'
# Monkey-patch to silence the description from Rake::TestTask to cut down on rake -T noise
class TestTaskWithoutDescription < Rake::TestTask
# Create the tasks defined by this task lib.
def define
lib_path = @libs.join(File::PATH_SEPARATOR)
task @name do
run_code = ''
RakeFileUtils.verbose(@verbose) do
run_code =
case @loader
when :direct
"-e 'ARGV.each{|f| load f}'"
when :testrb
"-S testrb #{fix}"
when :rake
rake_loader
end
@ruby_opts.unshift( "-I\"#{lib_path}\"" )
@ruby_opts.unshift( "-w" ) if @warning
ruby @ruby_opts.join(" ") +
" \"#{run_code}\" " +
file_list.collect { |fn| "\"#{fn}\"" }.join(' ') +
" #{option_list}"
end
end
self
end
end
TEST_CHANGES_SINCE = Time.now - 600
# Look up tests for recently modified sources.
@ -40,7 +70,7 @@ module Kernel
end
end
desc 'Run all unit, functional and integration tests'
desc 'Runs test:unit, test:functional, test:integration together (also available: test:benchmark, test:profile, test:plugins)'
task :test do
errors = %w(test:units test:functionals test:integration).collect do |task|
begin
@ -92,38 +122,33 @@ namespace :test do
end
Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion and Git)"
Rake::TestTask.new(:units => "test:prepare") do |t|
TestTaskWithoutDescription.new(:units => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/unit/**/*_test.rb'
end
Rake::Task['test:units'].comment = "Run the unit tests in test/unit"
Rake::TestTask.new(:functionals => "test:prepare") do |t|
TestTaskWithoutDescription.new(:functionals => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/functional/**/*_test.rb'
end
Rake::Task['test:functionals'].comment = "Run the functional tests in test/functional"
Rake::TestTask.new(:integration => "test:prepare") do |t|
TestTaskWithoutDescription.new(:integration => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/integration/**/*_test.rb'
end
Rake::Task['test:integration'].comment = "Run the integration tests in test/integration"
Rake::TestTask.new(:benchmark => 'test:prepare') do |t|
TestTaskWithoutDescription.new(:benchmark => 'test:prepare') do |t|
t.libs << 'test'
t.pattern = 'test/performance/**/*_test.rb'
t.options = '-- --benchmark'
end
Rake::Task['test:benchmark'].comment = 'Benchmark the performance tests'
Rake::TestTask.new(:profile => 'test:prepare') do |t|
TestTaskWithoutDescription.new(:profile => 'test:prepare') do |t|
t.libs << 'test'
t.pattern = 'test/performance/**/*_test.rb'
end
Rake::Task['test:profile'].comment = 'Profile the performance tests'
Rake::TestTask.new(:plugins => :environment) do |t|
TestTaskWithoutDescription.new(:plugins => :environment) do |t|
t.libs << "test"
if ENV['PLUGIN']
@ -132,5 +157,4 @@ namespace :test do
t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb'
end
end
Rake::Task['test:plugins'].comment = "Run the plugin tests in vendor/plugins/*/**/test (or specify with PLUGIN=name)"
end

View File

@ -64,6 +64,7 @@ module ApplicationTests
# Initialize the application
require "#{app_path}/config/environment"
require "rails/generators"
Rails::Generators.configure!
assert_equal :rspec, Rails::Generators.options[:rails][:test_framework]
assert_equal "-w", Rails::Generators.aliases[:rails][:test_framework]

View File

@ -9,9 +9,11 @@ module ApplicationTests
boot_rails
end
test "rails initializes with ruby 1.8.7 or later" do
test "rails initializes with ruby 1.8.7 or later, except for 1.9.1" do
if RUBY_VERSION < '1.8.7'
assert_rails_does_not_boot
elsif RUBY_VERSION == '1.9.1'
assert_rails_does_not_boot
else
assert_rails_boots
end

View File

@ -1,4 +1,5 @@
require 'isolation/abstract_unit'
require 'stringio'
module ApplicationTests
class MiddlewareTest < Test::Unit::TestCase
@ -163,6 +164,25 @@ module ApplicationTests
assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "4.2.42.42,1.1.1.1")
end
test "show exceptions middleware filter backtrace before logging" do
my_middleware = Struct.new(:app) do
def call(env)
raise "Failure"
end
end
make_basic_app do |app|
app.config.middleware.use my_middleware
end
stringio = StringIO.new
Rails.logger = Logger.new(stringio)
env = Rack::MockRequest.env_for("/")
Rails.application.call(env)
assert_no_match(/action_dispatch/, stringio.string)
end
private
def boot!

View File

@ -59,7 +59,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_application_controller_and_layout_files
run_generator
assert_file "app/controllers/application_controller.rb", /layout 'application'/
assert_file "app/views/layouts/application.html.erb", /stylesheet_link_tag :all/
assert_no_file "public/stylesheets/application.css"
end
@ -272,4 +271,4 @@ protected
def action(*args, &block)
silence(:stdout){ generator.send(*args, &block) }
end
end
end

View File

@ -5,36 +5,107 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
include GeneratorsTestHelper
def test_field_type_returns_text_field
%w(integer float decimal string).each do |name|
assert_field_type name, :text_field
%w(integer float decimal string).each do |attribute_type|
assert_field_type attribute_type, :text_field
end
end
def test_field_type_returns_datetime_select
%w(datetime timestamp).each do |name|
assert_field_type name, :datetime_select
%w(datetime timestamp).each do |attribute_type|
assert_field_type attribute_type, :datetime_select
end
end
def test_field_type_returns_time_select
assert_field_type 'time', :time_select
assert_field_type :time, :time_select
end
def test_field_type_returns_date_select
assert_field_type 'date', :date_select
assert_field_type :date, :date_select
end
def test_field_type_returns_text_area
assert_field_type 'text', :text_area
assert_field_type :text, :text_area
end
def test_field_type_returns_check_box
assert_field_type 'boolean', :check_box
assert_field_type :boolean, :check_box
end
def test_field_type_with_unknown_type_returns_text_field
%w(foo bar baz).each do |name|
assert_field_type name, :text_field
%w(foo bar baz).each do |attribute_type|
assert_field_type attribute_type, :text_field
end
end
def test_default_value_is_integer
assert_field_default_value :integer, 1
end
def test_default_value_is_float
assert_field_default_value :float, 1.5
end
def test_default_value_is_decimal
assert_field_default_value :decimal, '9.99'
end
def test_default_value_is_datetime
%w(datetime timestamp time).each do |attribute_type|
assert_field_default_value attribute_type, Time.now.to_s(:db)
end
end
def test_default_value_is_date
assert_field_default_value :date, Date.today.to_s(:db)
end
def test_default_value_is_string
assert_field_default_value :string, 'MyString'
end
def test_default_value_is_text
assert_field_default_value :text, 'MyText'
end
def test_default_value_is_boolean
assert_field_default_value :boolean, false
end
def test_default_value_is_nil
%w(references belongs_to).each do |attribute_type|
assert_field_default_value attribute_type, nil
end
end
def test_default_value_is_empty_string
%w(foo bar baz).each do |attribute_type|
assert_field_default_value attribute_type, ''
end
end
def test_human_name
assert_equal(
'Full name',
create_generated_attribute(:string, 'full_name').human_name
)
end
def test_reference_is_true
%w(references belongs_to).each do |attribute_type|
assert_equal(
true,
create_generated_attribute(attribute_type).reference?
)
end
end
def test_reference_is_false
%w(foo bar baz).each do |attribute_type|
assert_equal(
false,
create_generated_attribute(attribute_type).reference?
)
end
end
end

View File

@ -0,0 +1,22 @@
require 'abstract_unit'
require 'rails/script_rails_loader'
class ScriptRailsLoaderTest < ActiveSupport::TestCase
test "is in a rails application if script/rails exists" do
File.stubs(:exists?).returns(true)
assert Rails::ScriptRailsLoader.in_rails_application?
end
test "is in a rails application if parent directory has script/rails" do
File.stubs(:exists?).with("/foo/bar/script/rails").returns(false)
File.stubs(:exists?).with("/foo/script/rails").returns(true)
assert Rails::ScriptRailsLoader.in_rails_application_subdirectory?(Pathname.new("/foo/bar"))
end
test "is not in a rails application if at the root directory and doesn't have script/rails" do
Pathname.any_instance.stubs(:root?).returns true
assert !Rails::ScriptRailsLoader.in_rails_application?
end
end