mirror of https://github.com/rails/rails
Merge remote branch 'rails/master'
This commit is contained in:
commit
6356066006
6
Gemfile
6
Gemfile
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <strong>strongly</strong></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
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 <strong>strongly</strong></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 <strong>strongly</strong>", 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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
class ApplicationController < ActionController::Base
|
||||
protect_from_forgery
|
||||
layout 'application'
|
||||
end
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue