Merge commit 'rails/master'
This commit is contained in:
commit
824fa10f4d
3
.gitignore
vendored
3
.gitignore
vendored
@ -15,7 +15,6 @@ activesupport/test/fixtures/isolation_test
|
||||
railties/test/500.html
|
||||
railties/test/fixtures/tmp
|
||||
railties/test/initializer/root/log
|
||||
railties/doc/guides/html/images
|
||||
railties/doc/guides/html/stylesheets
|
||||
railties/doc
|
||||
railties/guides/output
|
||||
railties/tmp
|
||||
|
2
Gemfile
2
Gemfile
@ -11,6 +11,8 @@ group :mri do
|
||||
if RUBY_VERSION < '1.9'
|
||||
gem "system_timer"
|
||||
gem "ruby-debug", ">= 0.10.3"
|
||||
elsif RUBY_VERSION < '1.9.2'
|
||||
gem "ruby-debug19"
|
||||
end
|
||||
end
|
||||
|
||||
|
5
Rakefile
5
Rakefile
@ -72,8 +72,8 @@ Rake::RDocTask.new do |rdoc|
|
||||
rdoc.rdoc_files.include('railties/CHANGELOG')
|
||||
rdoc.rdoc_files.include('railties/MIT-LICENSE')
|
||||
rdoc.rdoc_files.include('railties/README')
|
||||
rdoc.rdoc_files.include('railties/lib/{*.rb,commands/*.rb,rails/*.rb,generators/*.rb}')
|
||||
rdoc.rdoc_files.exclude('railties/lib/vendor/*')
|
||||
rdoc.rdoc_files.include('railties/lib/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/*')
|
||||
|
||||
rdoc.rdoc_files.include('activerecord/README')
|
||||
rdoc.rdoc_files.include('activerecord/CHANGELOG')
|
||||
@ -88,6 +88,7 @@ Rake::RDocTask.new do |rdoc|
|
||||
rdoc.rdoc_files.include('actionpack/README')
|
||||
rdoc.rdoc_files.include('actionpack/CHANGELOG')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_dispatch/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_view/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('actionpack/lib/action_controller/vendor/*')
|
||||
|
||||
|
@ -20,6 +20,6 @@
|
||||
s.has_rdoc = true
|
||||
|
||||
s.add_dependency('actionpack', version)
|
||||
s.add_dependency('mail', '~> 2.1.3')
|
||||
s.add_dependency('mail', '~> 2.1.5.3')
|
||||
s.add_dependency('text-format', '~> 1.0.0')
|
||||
end
|
||||
|
@ -2,6 +2,7 @@
|
||||
require 'action_mailer/tmail_compat'
|
||||
require 'action_mailer/collector'
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionMailer #:nodoc:
|
||||
# Action Mailer allows you to send email from your application using a mailer model and views.
|
||||
@ -290,7 +291,7 @@ class Base < AbstractController::Base
|
||||
:parts_order => [ "text/plain", "text/enriched", "text/html" ]
|
||||
}.freeze
|
||||
|
||||
ActionMailer.run_base_hooks(self)
|
||||
ActiveSupport.run_load_hooks(:action_mailer, self)
|
||||
|
||||
class << self
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
require 'active_support/core_ext/object/try'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionMailer
|
||||
module OldApi #:nodoc:
|
||||
|
@ -6,18 +6,18 @@ class Railtie < Rails::Railtie
|
||||
config.action_mailer = ActiveSupport::OrderedOptions.new
|
||||
|
||||
initializer "action_mailer.url_for", :before => :load_environment_config do |app|
|
||||
ActionMailer.base_hook { include app.routes.url_helpers }
|
||||
ActiveSupport.on_load(:action_mailer) { include app.routes.url_helpers }
|
||||
end
|
||||
|
||||
require "action_mailer/railties/log_subscriber"
|
||||
log_subscriber :action_mailer, ActionMailer::Railties::LogSubscriber.new
|
||||
|
||||
initializer "action_mailer.logger" do
|
||||
ActionMailer.base_hook { self.logger ||= Rails.logger }
|
||||
ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger }
|
||||
end
|
||||
|
||||
initializer "action_mailer.set_configs" do |app|
|
||||
ActionMailer.base_hook do
|
||||
ActiveSupport.on_load(:action_mailer) do
|
||||
app.config.action_mailer.each do |k,v|
|
||||
send "#{k}=", v
|
||||
end
|
||||
|
@ -39,11 +39,18 @@ def attachment_with_content(hash = {})
|
||||
end
|
||||
|
||||
def attachment_with_hash
|
||||
attachments['invoice.jpg'] = { :data => "you smiling", :mime_type => "image/x-jpg",
|
||||
attachments['invoice.jpg'] = { :data => "\312\213\254\232)b",
|
||||
:mime_type => "image/x-jpg",
|
||||
:transfer_encoding => "base64" }
|
||||
mail
|
||||
end
|
||||
|
||||
def attachment_with_hash_default_encoding
|
||||
attachments['invoice.jpg'] = { :data => "\312\213\254\232)b",
|
||||
:mime_type => "image/x-jpg" }
|
||||
mail
|
||||
end
|
||||
|
||||
def implicit_multipart(hash = {})
|
||||
attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments)
|
||||
mail(hash)
|
||||
@ -137,7 +144,7 @@ def different_layout(layout_name='')
|
||||
:date => @time)
|
||||
assert_equal(['bcc@test.lindsaar.net'], email.bcc)
|
||||
assert_equal(['cc@test.lindsaar.net'], email.cc)
|
||||
assert_equal('multipart/mixed', email.content_type)
|
||||
assert_equal('multipart/mixed; charset=iso-8559-1', email.content_type)
|
||||
assert_equal('iso-8559-1', email.charset)
|
||||
assert_equal('2.0', email.mime_version)
|
||||
assert_equal(['reply-to@test.lindsaar.net'], email.reply_to)
|
||||
@ -206,6 +213,15 @@ def different_layout(layout_name='')
|
||||
assert_equal expected, email.attachments['invoice.jpg'].decoded
|
||||
end
|
||||
|
||||
test "attachment with hash using default mail encoding" do
|
||||
email = BaseMailer.attachment_with_hash_default_encoding
|
||||
assert_equal(1, email.attachments.length)
|
||||
assert_equal('invoice.jpg', email.attachments[0].filename)
|
||||
expected = "\312\213\254\232)b"
|
||||
expected.force_encoding(Encoding::BINARY) if '1.9'.respond_to?(:force_encoding)
|
||||
assert_equal expected, email.attachments['invoice.jpg'].decoded
|
||||
end
|
||||
|
||||
test "sets mime type to multipart/mixed when attachment is included" do
|
||||
email = BaseMailer.attachment_with_content
|
||||
assert_equal(1, email.attachments.length)
|
||||
|
@ -784,7 +784,7 @@ def test_utf8_body_is_not_quoted
|
||||
expected.date = Time.local 2004, 12, 12
|
||||
|
||||
created = TestMailer.utf8_body @recipient
|
||||
assert_match(/åœö blah/, created.encoded)
|
||||
assert_match(/åœö blah/, created.decoded)
|
||||
end
|
||||
|
||||
def test_multiple_utf8_recipients
|
||||
@ -1019,8 +1019,8 @@ def test_decode_message_with_unknown_charset
|
||||
|
||||
def test_empty_header_values_omitted
|
||||
result = TestMailer.unnamed_attachment(@recipient).encoded
|
||||
assert_match %r{Content-Type: application/octet-stream;}, result
|
||||
assert_match %r{Content-Disposition: attachment;}, result
|
||||
assert_match %r{Content-Type: application/octet-stream}, result
|
||||
assert_match %r{Content-Disposition: attachment}, result
|
||||
end
|
||||
|
||||
def test_headers_with_nonalpha_chars
|
||||
|
@ -64,8 +64,7 @@ def self.filter_parameter_logging(*args, &block)
|
||||
filter
|
||||
end
|
||||
|
||||
ActionController.run_base_hooks(self)
|
||||
|
||||
ActiveSupport.run_load_hooks(:action_controller, self)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -66,6 +66,18 @@ def ip_spoofing_check
|
||||
Rails.application.config.action_dispatch.ip_spoofing_check
|
||||
end
|
||||
|
||||
def cookie_verifier_secret=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret= is deprecated. " <<
|
||||
"Please configure it on your application with config.cookie_secret=", caller
|
||||
ActionController::Base.config.secret = value
|
||||
end
|
||||
|
||||
def cookie_verifier_secret
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret is deprecated. " <<
|
||||
"Please use ActionController::Base.config.secret instead.", caller
|
||||
ActionController::Base.config.secret
|
||||
end
|
||||
|
||||
def trusted_proxies=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies= is deprecated. " <<
|
||||
"Please configure it on your application with config.action_dispatch.trusted_proxies=", caller
|
||||
|
@ -6,7 +6,6 @@ module Cookies
|
||||
|
||||
included do
|
||||
helper_method :cookies
|
||||
cattr_accessor :cookie_verifier_secret
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -1,4 +1,5 @@
|
||||
require 'active_support/base64'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionController
|
||||
module HttpAuthentication
|
||||
|
@ -1,4 +1,5 @@
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionController
|
||||
def self.add_renderer(key, &block)
|
||||
|
@ -41,7 +41,7 @@ class Railtie < Rails::Railtie
|
||||
log_subscriber :action_controller, ActionController::Railties::LogSubscriber.new
|
||||
|
||||
initializer "action_controller.logger" do
|
||||
ActionController.base_hook { self.logger ||= Rails.logger }
|
||||
ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger }
|
||||
end
|
||||
|
||||
initializer "action_controller.set_configs" do |app|
|
||||
@ -53,23 +53,23 @@ class Railtie < Rails::Railtie
|
||||
ac.stylesheets_dir = paths.public.stylesheets.to_a.first
|
||||
ac.secret = app.config.cookie_secret
|
||||
|
||||
ActionController.base_hook do
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
self.config.merge!(ac)
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_controller.initialize_framework_caches" do
|
||||
ActionController.base_hook { self.cache_store ||= RAILS_CACHE }
|
||||
ActiveSupport.on_load(:action_controller) { self.cache_store ||= RAILS_CACHE }
|
||||
end
|
||||
|
||||
initializer "action_controller.set_helpers_path" do |app|
|
||||
ActionController.base_hook do
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
self.helpers_path = app.config.paths.app.helpers.to_a
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_controller.url_helpers" do |app|
|
||||
ActionController.base_hook do
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
extend ::ActionController::Railties::UrlHelpers.with(app.routes)
|
||||
end
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionController
|
||||
module Railties
|
||||
class LogSubscriber < Rails::LogSubscriber
|
||||
|
@ -1,11 +1,11 @@
|
||||
module ActionController
|
||||
module Railties
|
||||
module UrlHelpers
|
||||
def self.with(router)
|
||||
def self.with(routes)
|
||||
Module.new do
|
||||
define_method(:inherited) do |klass|
|
||||
super(klass)
|
||||
klass.send(:include, router.url_helpers)
|
||||
klass.send(:include, routes.url_helpers)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -76,8 +76,8 @@ def dom_id(record, prefix = nil)
|
||||
# method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
|
||||
# make sure yourself that your dom ids are valid, in case you overwrite this method.
|
||||
def record_key_for_dom_id(record)
|
||||
return record.id unless record.respond_to?(:to_model)
|
||||
key = record.to_model.to_key
|
||||
record = record.to_model if record.respond_to?(:to_model)
|
||||
key = record.to_key
|
||||
key ? sanitize_dom_id(key.join('_')) : key
|
||||
end
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
require 'rack/session/abstract/id'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionController
|
||||
module TemplateAssertions
|
||||
@ -117,9 +118,9 @@ def self.new_escaped(strings)
|
||||
end
|
||||
end
|
||||
|
||||
def assign_parameters(router, controller_path, action, parameters = {})
|
||||
def assign_parameters(routes, controller_path, action, parameters = {})
|
||||
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
|
||||
extra_keys = router.extra_keys(parameters)
|
||||
extra_keys = routes.extra_keys(parameters)
|
||||
non_path_parameters = get? ? query_parameters : request_parameters
|
||||
parameters.each do |key, value|
|
||||
if value.is_a? Fixnum
|
||||
@ -321,7 +322,7 @@ def xml_http_request(request_method, action, parameters = nil, session = nil, fl
|
||||
def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
|
||||
# Sanity check for required instance variables so we can give an
|
||||
# understandable error message.
|
||||
%w(@router @controller @request @response).each do |iv_name|
|
||||
%w(@routes @controller @request @response).each do |iv_name|
|
||||
if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
|
||||
raise "#{iv_name} is nil: make sure you set it in your test's setup method."
|
||||
end
|
||||
@ -337,7 +338,7 @@ def process(action, parameters = nil, session = nil, flash = nil, http_method =
|
||||
@request.env['REQUEST_METHOD'] = http_method
|
||||
|
||||
parameters ||= {}
|
||||
@request.assign_parameters(@router, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
|
||||
@request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
|
||||
|
||||
@request.session = ActionController::TestSession.new(session) unless session.nil?
|
||||
@request.session["flash"] = @request.flash.update(flash || {})
|
||||
@ -446,7 +447,7 @@ def build_request_uri(action, parameters)
|
||||
:relative_url_root => nil,
|
||||
:_path_segments => @request.symbolized_path_parameters)
|
||||
|
||||
url, query_string = @router.url_for(options).split("?", 2)
|
||||
url, query_string = @routes.url_for(options).split("?", 2)
|
||||
|
||||
@request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
|
||||
@request.env["PATH_INFO"] = url
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionDispatch
|
||||
module Http
|
||||
module Cache
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/hash/keys'
|
||||
require 'active_support/core_ext/object/duplicable'
|
||||
|
||||
module ActionDispatch
|
||||
module Http
|
||||
|
@ -5,7 +5,7 @@ module MimeNegotiation
|
||||
#
|
||||
# For backward compatibility, the post \format is extracted from the
|
||||
# X-Post-Data-Format HTTP header if present.
|
||||
def content_type
|
||||
def content_mime_type
|
||||
@env["action_dispatch.request.content_type"] ||= begin
|
||||
if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
|
||||
Mime::Type.lookup($1.strip.downcase)
|
||||
@ -15,13 +15,17 @@ def content_type
|
||||
end
|
||||
end
|
||||
|
||||
def content_type
|
||||
content_mime_type && content_mime_type.to_s
|
||||
end
|
||||
|
||||
# Returns the accepted MIME type for the request.
|
||||
def accepts
|
||||
@env["action_dispatch.request.accepts"] ||= begin
|
||||
header = @env['HTTP_ACCEPT'].to_s.strip
|
||||
|
||||
if header.empty?
|
||||
[content_type]
|
||||
[content_mime_type]
|
||||
else
|
||||
Mime::Type.parse(header)
|
||||
end
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'set'
|
||||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module Mime
|
||||
class Mimes < Array
|
||||
|
@ -96,11 +96,11 @@ def headers
|
||||
end
|
||||
|
||||
def forgery_whitelisted?
|
||||
method == :get || xhr? || content_type.nil? || !content_type.verify_request?
|
||||
method == :get || xhr? || content_mime_type.nil? || !content_mime_type.verify_request?
|
||||
end
|
||||
|
||||
def media_type
|
||||
content_type.to_s
|
||||
content_mime_type.to_s
|
||||
end
|
||||
|
||||
# Returns the content length of the request as an integer.
|
||||
@ -157,7 +157,7 @@ def body
|
||||
end
|
||||
|
||||
def form_data?
|
||||
FORM_DATA_MEDIA_TYPES.include?(content_type.to_s)
|
||||
FORM_DATA_MEDIA_TYPES.include?(content_mime_type.to_s)
|
||||
end
|
||||
|
||||
def body_stream #:nodoc:
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'digest/md5'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionDispatch # :nodoc:
|
||||
# Represents an HTTP response generated by a controller action. One can use
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionDispatch
|
||||
module Http
|
||||
module UploadedFile
|
||||
|
@ -168,12 +168,12 @@ def method_missing(method, *arguments, &block)
|
||||
|
||||
class SignedCookieJar < CookieJar #:nodoc:
|
||||
def initialize(parent_jar)
|
||||
unless ActionController::Base.cookie_verifier_secret
|
||||
raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies"
|
||||
unless ActionController::Base.config.secret
|
||||
raise "You must set ActionController::Base.config.secret"
|
||||
end
|
||||
|
||||
@parent_jar = parent_jar
|
||||
@verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.cookie_verifier_secret)
|
||||
@verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.config.secret)
|
||||
end
|
||||
|
||||
def [](name)
|
||||
|
@ -25,7 +25,9 @@ def parse_formatted_parameters(env)
|
||||
|
||||
return false if request.content_length.zero?
|
||||
|
||||
mime_type = content_type_from_legacy_post_data_format_header(env) || request.content_type
|
||||
mime_type = content_type_from_legacy_post_data_format_header(env) ||
|
||||
request.content_mime_type
|
||||
|
||||
strategy = @parsers[mime_type]
|
||||
|
||||
return false unless strategy
|
||||
@ -53,7 +55,7 @@ def parse_formatted_parameters(env)
|
||||
|
||||
raise
|
||||
{ "body" => request.raw_post,
|
||||
"content_type" => request.content_type,
|
||||
"content_type" => request.content_mime_type,
|
||||
"content_length" => request.content_length,
|
||||
"exception" => "#{e.message} (#{e.class})",
|
||||
"backtrace" => e.backtrace }
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'rack/utils'
|
||||
require 'rack/request'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionDispatch
|
||||
module Session
|
||||
|
@ -1,4 +1,5 @@
|
||||
require 'active_support/core_ext/hash/keys'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionDispatch
|
||||
module Session
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionDispatch
|
||||
module Routing
|
||||
class RouteSet
|
||||
|
@ -1,4 +1,5 @@
|
||||
require 'active_support/core_ext/hash/except'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionDispatch
|
||||
module Routing
|
||||
@ -258,6 +259,7 @@ def initialize(*args) #:nodoc:
|
||||
|
||||
def scope(*args)
|
||||
options = args.extract_options!
|
||||
options = options.dup
|
||||
|
||||
case args.first
|
||||
when String
|
||||
@ -423,8 +425,13 @@ def member_name
|
||||
singular
|
||||
end
|
||||
|
||||
# Checks for uncountable plurals, and appends "_index" if they're.
|
||||
def collection_name
|
||||
plural
|
||||
uncountable? ? "#{plural}_index" : plural
|
||||
end
|
||||
|
||||
def uncountable?
|
||||
singular == plural
|
||||
end
|
||||
|
||||
def name_for_action(action)
|
||||
@ -439,6 +446,32 @@ def name_for_action(action)
|
||||
def id_segment
|
||||
":#{singular}_id"
|
||||
end
|
||||
|
||||
def constraints
|
||||
options[:constraints] || {}
|
||||
end
|
||||
|
||||
def id_constraint?
|
||||
options[:id] && options[:id].is_a?(Regexp) || constraints[:id] && constraints[:id].is_a?(Regexp)
|
||||
end
|
||||
|
||||
def id_constraint
|
||||
options[:id] || constraints[:id]
|
||||
end
|
||||
|
||||
def collection_options
|
||||
(options || {}).dup.tap do |options|
|
||||
options.delete(:id)
|
||||
options[:constraints] = options[:constraints].dup if options[:constraints]
|
||||
options[:constraints].delete(:id) if options[:constraints].is_a?(Hash)
|
||||
end
|
||||
end
|
||||
|
||||
def nested_options
|
||||
options = { :name_prefix => member_name }
|
||||
options["#{singular}_id".to_sym] = id_constraint if id_constraint?
|
||||
options
|
||||
end
|
||||
end
|
||||
|
||||
class SingletonResource < Resource #:nodoc:
|
||||
@ -483,6 +516,7 @@ def resource(*resources, &block)
|
||||
yield if block_given?
|
||||
end
|
||||
|
||||
scope(resource.options) do
|
||||
get :show if resource.actions.include?(:show)
|
||||
post :create if resource.actions.include?(:create)
|
||||
put :update if resource.actions.include?(:update)
|
||||
@ -491,6 +525,7 @@ def resource(*resources, &block)
|
||||
get :edit, :as => resource.name if resource.actions.include?(:edit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
@ -509,13 +544,16 @@ def resources(*resources, &block)
|
||||
yield if block_given?
|
||||
|
||||
with_scope_level(:collection) do
|
||||
scope(resource.collection_options) do
|
||||
get :index if resource.actions.include?(:index)
|
||||
post :create if resource.actions.include?(:create)
|
||||
get :new, :as => resource.singular if resource.actions.include?(:new)
|
||||
end
|
||||
end
|
||||
|
||||
with_scope_level(:member) do
|
||||
scope(':id') do
|
||||
scope(resource.options) do
|
||||
get :show if resource.actions.include?(:show)
|
||||
put :update if resource.actions.include?(:update)
|
||||
delete :destroy if resource.actions.include?(:destroy)
|
||||
@ -524,6 +562,7 @@ def resources(*resources, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
@ -558,7 +597,7 @@ def nested
|
||||
end
|
||||
|
||||
with_scope_level(:nested) do
|
||||
scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name) do
|
||||
scope(parent_resource.id_segment, parent_resource.nested_options) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
@ -80,7 +80,7 @@ def assert_generates(expected_path, options, defaults={}, extras = {}, message=n
|
||||
expected_path = "/#{expected_path}" unless expected_path[0] == ?/
|
||||
# Load routes.rb if it hasn't been loaded.
|
||||
|
||||
generated_path, extra_keys = @router.generate_extras(options, defaults)
|
||||
generated_path, extra_keys = @routes.generate_extras(options, defaults)
|
||||
found_extras = options.reject {|k, v| ! extra_keys.include? k}
|
||||
|
||||
msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
|
||||
@ -125,7 +125,7 @@ def assert_routing(path, options, defaults={}, extras={}, message=nil)
|
||||
end
|
||||
|
||||
# A helper to make it easier to test different route configurations.
|
||||
# This method temporarily replaces @router
|
||||
# This method temporarily replaces @routes
|
||||
# with a new RouteSet instance.
|
||||
#
|
||||
# The new instance is yielded to the passed block. Typically the block
|
||||
@ -142,9 +142,9 @@ def assert_routing(path, options, defaults={}, extras={}, message=nil)
|
||||
# end
|
||||
#
|
||||
def with_routing
|
||||
old_routes, @router = @router, ActionDispatch::Routing::RouteSet.new
|
||||
old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
|
||||
old_controller, @controller = @controller, @controller.clone if @controller
|
||||
_router = @router
|
||||
_routes = @routes
|
||||
|
||||
# Unfortunately, there is currently an abstraction leak between AC::Base
|
||||
# and AV::Base which requires having the URL helpers in both AC and AV.
|
||||
@ -153,14 +153,14 @@ def with_routing
|
||||
#
|
||||
# TODO: Make this unnecessary
|
||||
if @controller
|
||||
@controller.singleton_class.send(:include, _router.url_helpers)
|
||||
@controller.singleton_class.send(:include, _routes.url_helpers)
|
||||
@controller.view_context_class = Class.new(@controller.view_context_class) do
|
||||
include _router.url_helpers
|
||||
include _routes.url_helpers
|
||||
end
|
||||
end
|
||||
yield @router
|
||||
yield @routes
|
||||
ensure
|
||||
@router = old_routes
|
||||
@routes = old_routes
|
||||
if @controller
|
||||
@controller = old_controller
|
||||
end
|
||||
@ -168,7 +168,7 @@ def with_routing
|
||||
|
||||
# ROUTES TODO: These assertions should really work in an integration context
|
||||
def method_missing(selector, *args, &block)
|
||||
if @controller && @router.named_routes.helpers.include?(selector)
|
||||
if @controller && @routes && @routes.named_routes.helpers.include?(selector)
|
||||
@controller.send(selector, *args, &block)
|
||||
else
|
||||
super
|
||||
@ -185,7 +185,7 @@ def recognized_request_for(path, request_method = nil)
|
||||
request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
|
||||
request.path = path
|
||||
|
||||
params = @router.recognize_path(path, { :method => request.method })
|
||||
params = @routes.recognize_path(path, { :method => request.method })
|
||||
request.path_parameters = params.with_indifferent_access
|
||||
|
||||
request
|
||||
|
@ -1,7 +1,7 @@
|
||||
require 'active_support/testing/performance'
|
||||
require 'active_support/testing/default'
|
||||
|
||||
if defined?(ActiveSupport::Testing::Performance)
|
||||
begin
|
||||
module ActionDispatch
|
||||
# An integration test that runs a code profiler on your test methods.
|
||||
# Profiling output for combinations of each test method, measurement, and
|
||||
@ -14,4 +14,6 @@ class PerformanceTest < ActionDispatch::IntegrationTest
|
||||
include ActiveSupport::Testing::Default
|
||||
end
|
||||
end
|
||||
rescue NameError
|
||||
$stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks."
|
||||
end
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionDispatch
|
||||
class TestRequest < Request
|
||||
DEFAULT_ENV = Rack::MockRequest.env_for('/')
|
||||
|
@ -173,7 +173,7 @@ class << self
|
||||
delegate :logger, :to => 'ActionController::Base', :allow_nil => true
|
||||
end
|
||||
|
||||
ActionView.run_base_hooks(self)
|
||||
ActiveSupport.run_load_hooks(:action_view, self)
|
||||
|
||||
attr_accessor :base_path, :assigns, :template_extension, :lookup_context
|
||||
attr_internal :captures, :request, :controller, :template, :config
|
||||
|
@ -6,7 +6,7 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
ActionView.base_hook do
|
||||
ActiveSupport.on_load(:action_view) do
|
||||
class ActionView::Base
|
||||
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
|
||||
cattr_accessor :field_error_proc
|
||||
@ -97,10 +97,10 @@ def form(record_name, options = {})
|
||||
end
|
||||
|
||||
# Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
|
||||
# This error message is wrapped in a <tt>DIV</tt> tag, which can be extended to include a <tt>:prepend_text</tt>
|
||||
# and/or <tt>:append_text</tt> (to properly explain the error), and a <tt>:css_class</tt> to style it
|
||||
# accordingly. +object+ should either be the name of an instance variable or the actual object. The method can be
|
||||
# passed in either as a string or a symbol.
|
||||
# This error message is wrapped in a <tt>DIV</tt> tag by default or with <tt>:html_tag</tt> if specified,
|
||||
# which can be extended to include a <tt>:prepend_text</tt> and/or <tt>:append_text</tt> (to properly explain
|
||||
# the error), and a <tt>:css_class</tt> to style it accordingly. +object+ should either be the name of an
|
||||
# instance variable or the actual object. The method can be passed in either as a string or a symbol.
|
||||
# As an example, let's say you have a model <tt>@post</tt> that has an error message on the +title+ attribute:
|
||||
#
|
||||
# <%= error_message_on "post", "title" %>
|
||||
@ -112,25 +112,28 @@ def form(record_name, options = {})
|
||||
# <%= error_message_on "post", "title",
|
||||
# :prepend_text => "Title simply ",
|
||||
# :append_text => " (or it won't work).",
|
||||
# :html_tag => "span",
|
||||
# :css_class => "inputError" %>
|
||||
# # => <span class="inputError">Title simply can't be empty (or it won't work).</span>
|
||||
def error_message_on(object, method, *args)
|
||||
options = args.extract_options!
|
||||
unless args.empty?
|
||||
ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' +
|
||||
'prepend_text, append_text, and css_class arguments', caller)
|
||||
'prepend_text, append_text, html_tag, and css_class arguments', caller)
|
||||
|
||||
options[:prepend_text] = args[0] || ''
|
||||
options[:append_text] = args[1] || ''
|
||||
options[:css_class] = args[2] || 'formError'
|
||||
options[:html_tag] = args[2] || 'div'
|
||||
options[:css_class] = args[3] || 'formError'
|
||||
end
|
||||
options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError')
|
||||
options.reverse_merge!(:prepend_text => '', :append_text => '', :html_tag => 'div', :css_class => 'formError')
|
||||
|
||||
object = convert_to_model(object)
|
||||
|
||||
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
|
||||
(errors = obj.errors[method]).presence
|
||||
content_tag("div",
|
||||
"#{options[:prepend_text]}#{ERB::Util.h(errors.first)}#{options[:append_text]}".html_safe,
|
||||
content_tag(options[:html_tag],
|
||||
(options[:prepend_text].html_safe << errors.first).safe_concat(options[:append_text]),
|
||||
:class => options[:css_class]
|
||||
)
|
||||
else
|
||||
|
@ -108,7 +108,7 @@ module Helpers #:nodoc:
|
||||
# "http://asset%d.example.com", "https://asset1.example.com"
|
||||
# )
|
||||
#
|
||||
# === Using asset timestamps
|
||||
# === Customizing the asset path
|
||||
#
|
||||
# By default, Rails appends asset's timestamps to all asset paths. This allows
|
||||
# you to set a cache-expiration date for the asset far into the future, but
|
||||
@ -133,6 +133,65 @@ module Helpers #:nodoc:
|
||||
# will request the same assets over and over again even thought they didn't
|
||||
# change. You can use something like Live HTTP Headers for Firefox to verify
|
||||
# that the cache is indeed working.
|
||||
#
|
||||
# This strategy works well enough for most server setups and requires the
|
||||
# least configuration, but if you deploy several application servers at
|
||||
# different times - say to handle a temporary spike in load - then the
|
||||
# asset time stamps will be out of sync. In a setup like this you may want
|
||||
# to set the way that asset paths are generated yourself.
|
||||
#
|
||||
# Altering the asset paths that Rails generates can be done in two ways.
|
||||
# The easiest is to define the RAILS_ASSET_ID environment variable. The
|
||||
# contents of this variable will always be used in preference to
|
||||
# calculated timestamps. A more complex but flexible way is to set
|
||||
# <tt>ActionController::Base.config.asset_path</tt> to a proc
|
||||
# that takes the unmodified asset path and returns the path needed for
|
||||
# your asset caching to work. Typically you'd do something like this in
|
||||
# <tt>config/environments/production.rb</tt>:
|
||||
#
|
||||
# # Normally you'd calculate RELEASE_NUMBER at startup.
|
||||
# RELEASE_NUMBER = 12345
|
||||
# config.action_controller.asset_path_template = proc { |asset_path|
|
||||
# "/release-#{RELEASE_NUMBER}#{asset_path}"
|
||||
# }
|
||||
#
|
||||
# This example would cause the following behaviour on all servers no
|
||||
# matter when they were deployed:
|
||||
#
|
||||
# image_tag("rails.png")
|
||||
# # => <img alt="Rails" src="/release-12345/images/rails.png" />
|
||||
# stylesheet_link_tag("application")
|
||||
# # => <link href="/release-12345/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
|
||||
#
|
||||
# Changing the asset_path does require that your web servers have
|
||||
# knowledge of the asset template paths that you rewrite to so it's not
|
||||
# suitable for out-of-the-box use. To use the example given above you
|
||||
# could use something like this in your Apache VirtualHost configuration:
|
||||
#
|
||||
# <LocationMatch "^/release-\d+/(images|javascripts|stylesheets)/.*$">
|
||||
# # Some browsers still send conditional-GET requests if there's a
|
||||
# # Last-Modified header or an ETag header even if they haven't
|
||||
# # reached the expiry date sent in the Expires header.
|
||||
# Header unset Last-Modified
|
||||
# Header unset ETag
|
||||
# FileETag None
|
||||
#
|
||||
# # Assets requested using a cache-busting filename should be served
|
||||
# # only once and then cached for a really long time. The HTTP/1.1
|
||||
# # spec frowns on hugely-long expiration times though and suggests
|
||||
# # that assets which never expire be served with an expiration date
|
||||
# # 1 year from access.
|
||||
# ExpiresActive On
|
||||
# ExpiresDefault "access plus 1 year"
|
||||
# </LocationMatch>
|
||||
#
|
||||
# # We use cached-busting location names with the far-future expires
|
||||
# # headers to ensure that if a file does change it can force a new
|
||||
# # request. The actual asset filenames are still the same though so we
|
||||
# # need to rewrite the location from the cache-busting location to the
|
||||
# # real asset location so that we can serve it.
|
||||
# RewriteEngine On
|
||||
# RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L]
|
||||
module AssetTagHelper
|
||||
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
|
||||
|
||||
@ -646,7 +705,7 @@ def compute_public_path(source, dir, ext = nil, include_host = true)
|
||||
|
||||
source += ".#{ext}" if rewrite_extension?(source, dir, ext)
|
||||
source = "/#{dir}/#{source}" unless source[0] == ?/
|
||||
source = rewrite_asset_path(source)
|
||||
source = rewrite_asset_path(source, config.asset_path)
|
||||
|
||||
has_request = controller.respond_to?(:request)
|
||||
if has_request && include_host && source !~ %r{^#{controller.config.relative_url_root}/}
|
||||
@ -710,7 +769,13 @@ def rails_asset_id(source)
|
||||
|
||||
# Break out the asset path rewrite in case plugins wish to put the asset id
|
||||
# someplace other than the query string.
|
||||
def rewrite_asset_path(source)
|
||||
def rewrite_asset_path(source, path = nil)
|
||||
if path && path.respond_to?(:call)
|
||||
return path.call(source)
|
||||
elsif path && path.is_a?(String)
|
||||
return path % [source]
|
||||
end
|
||||
|
||||
asset_id = rails_asset_id(source)
|
||||
if asset_id.blank?
|
||||
source
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
module Helpers
|
||||
# CaptureHelper exposes methods to let you extract generated markup which
|
||||
|
@ -4,6 +4,7 @@
|
||||
require 'action_view/helpers/form_tag_helper'
|
||||
require 'active_support/core_ext/class/inheritable_attributes'
|
||||
require 'active_support/core_ext/hash/slice'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
module Helpers
|
||||
@ -1220,7 +1221,7 @@ def nested_child_index(name)
|
||||
end
|
||||
end
|
||||
|
||||
ActionView.base_hook do
|
||||
ActiveSupport.on_load(:action_view) do
|
||||
class ActionView::Base
|
||||
cattr_accessor :default_form_builder
|
||||
@@default_form_builder = ::ActionView::Helpers::FormBuilder
|
||||
|
@ -1,6 +1,7 @@
|
||||
require 'cgi'
|
||||
require 'erb'
|
||||
require 'action_view/helpers/form_helper'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
module Helpers
|
||||
|
@ -1,6 +1,7 @@
|
||||
require 'cgi'
|
||||
require 'action_view/helpers/tag_helper'
|
||||
require 'active_support/core_ext/object/returning'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
module Helpers
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'active_support/core_ext/big_decimal/conversions'
|
||||
require 'active_support/core_ext/float/rounding'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
module Helpers #:nodoc:
|
||||
|
@ -1,6 +1,7 @@
|
||||
require 'set'
|
||||
require 'active_support/json'
|
||||
require 'active_support/core_ext/object/returning'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
module Helpers
|
||||
|
@ -1,3 +1,4 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'set'
|
||||
|
||||
module ActionView
|
||||
|
@ -1,3 +1,4 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'action_view/helpers/tag_helper'
|
||||
|
||||
module ActionView
|
||||
|
@ -10,14 +10,14 @@ class Railtie < Rails::Railtie
|
||||
|
||||
initializer "action_view.cache_asset_timestamps" do |app|
|
||||
unless app.config.cache_classes
|
||||
ActionView.base_hook do
|
||||
ActiveSupport.on_load(:action_view) do
|
||||
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_view.set_configs" do |app|
|
||||
ActionView.base_hook do
|
||||
ActiveSupport.on_load(:action_view) do
|
||||
app.config.action_view.each do |k,v|
|
||||
send "#{k}=", v
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
# There's also a convenience method for rendering sub templates within the current controller that depends on a
|
||||
# single object (we call this kind of sub templates for partials). It relies on the fact that partials should
|
||||
|
@ -35,12 +35,7 @@ def find_templates(name, prefix, partial, details)
|
||||
|
||||
def cached(key, prefix, name, partial)
|
||||
return yield unless key && caching?
|
||||
scope = @cached[key][prefix][name]
|
||||
if scope.key?(partial)
|
||||
scope[partial]
|
||||
else
|
||||
scope[partial] = yield
|
||||
end
|
||||
@cached[key][prefix][name][partial] ||= yield
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'action_controller/test_case'
|
||||
require 'action_view'
|
||||
|
||||
|
@ -95,7 +95,7 @@ class TestCase
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
|
||||
ActionController::IntegrationTest.app.router.draw do |map|
|
||||
ActionController::IntegrationTest.app.routes.draw do |map|
|
||||
# FIXME: match ':controller(/:action(/:id))'
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
@ -104,12 +104,11 @@ class TestCase
|
||||
end
|
||||
|
||||
class RoutedRackApp
|
||||
attr_reader :router
|
||||
alias routes router
|
||||
attr_reader :routes
|
||||
|
||||
def initialize(router, &blk)
|
||||
@router = router
|
||||
@stack = ActionDispatch::MiddlewareStack.new(&blk).build(@router)
|
||||
def initialize(routes, &blk)
|
||||
@routes = routes
|
||||
@stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@ -234,7 +233,7 @@ class TestCase
|
||||
# Must repeat the setup because AV::TestCase is a duplication
|
||||
# of AC::TestCase
|
||||
setup do
|
||||
@router = SharedTestRoutes
|
||||
@routes = SharedTestRoutes
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -250,7 +249,7 @@ class TestCase
|
||||
include ActionDispatch::TestProcess
|
||||
|
||||
setup do
|
||||
@router = SharedTestRoutes
|
||||
@routes = SharedTestRoutes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -400,7 +400,7 @@ def with_test_routes(options = {})
|
||||
map.resources :series
|
||||
end
|
||||
|
||||
self.class.send(:include, @router.url_helpers)
|
||||
self.class.send(:include, @routes.url_helpers)
|
||||
yield
|
||||
end
|
||||
end
|
||||
@ -422,7 +422,7 @@ def with_admin_test_routes(options = {})
|
||||
end
|
||||
end
|
||||
|
||||
self.class.send(:include, @router.url_helpers)
|
||||
self.class.send(:include, @routes.url_helpers)
|
||||
yield
|
||||
end
|
||||
end
|
||||
@ -441,7 +441,7 @@ def with_admin_and_site_test_routes(options = {})
|
||||
end
|
||||
end
|
||||
|
||||
self.class.send(:include, @router.url_helpers)
|
||||
self.class.send(:include, @routes.url_helpers)
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
@ -81,9 +81,9 @@ def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_def
|
||||
match '/', :to => 'posts#index', :as => :main
|
||||
end
|
||||
@params[:format] = 'rss'
|
||||
assert_equal '/posts.rss', @router.url_for(@params)
|
||||
assert_equal '/posts.rss', @routes.url_for(@params)
|
||||
@params[:format] = nil
|
||||
assert_equal '/', @router.url_for(@params)
|
||||
assert_equal '/', @routes.url_for(@params)
|
||||
end
|
||||
end
|
||||
|
||||
@ -518,7 +518,7 @@ def reset!
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
@controller = ActionCachingTestController.new
|
||||
@controller.singleton_class.send(:include, @router.url_helpers)
|
||||
@controller.singleton_class.send(:include, @routes.url_helpers)
|
||||
@request.host = 'hostname.com'
|
||||
end
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
ActionController::Base.cookie_verifier_secret = "thisISverySECRET123"
|
||||
ActionController::Base.config.secret = "thisISverySECRET123"
|
||||
|
||||
class CookieTest < ActionController::TestCase
|
||||
class TestController < ActionController::Base
|
||||
|
@ -430,3 +430,50 @@ def test_generate_url_without_controller
|
||||
assert_equal 'http://www.example.com/foo', url_for(:controller => "foo")
|
||||
end
|
||||
end
|
||||
|
||||
class ApplicationIntegrationTest < ActionController::IntegrationTest
|
||||
class TestController < ActionController::Base
|
||||
def index
|
||||
render :text => "index"
|
||||
end
|
||||
end
|
||||
|
||||
def self.call(env)
|
||||
routes.call(env)
|
||||
end
|
||||
|
||||
def self.routes
|
||||
@routes ||= ActionDispatch::Routing::RouteSet.new
|
||||
end
|
||||
|
||||
routes.draw do
|
||||
match 'foo', :to => 'application_integration_test/test#index', :as => :foo
|
||||
match 'bar', :to => 'application_integration_test/test#index', :as => :bar
|
||||
end
|
||||
|
||||
def app
|
||||
self.class
|
||||
end
|
||||
|
||||
test "includes route helpers" do
|
||||
assert_equal '/foo', foo_path
|
||||
assert_equal '/bar', bar_path
|
||||
end
|
||||
|
||||
test "route helpers after controller access" do
|
||||
get '/foo'
|
||||
assert_equal '/foo', foo_path
|
||||
|
||||
get '/bar'
|
||||
assert_equal '/bar', bar_path
|
||||
end
|
||||
|
||||
test "missing route helper before controller access" do
|
||||
assert_raise(NameError) { missing_path }
|
||||
end
|
||||
|
||||
test "missing route helper after controller access" do
|
||||
get '/foo'
|
||||
assert_raise(NameError) { missing_path }
|
||||
end
|
||||
end
|
||||
|
@ -126,7 +126,7 @@ def test_multiple_default_restful_routes
|
||||
|
||||
def test_with_custom_conditions
|
||||
with_restful_routing :messages, :conditions => { :subdomain => 'app' } do
|
||||
assert @router.recognize_path("/messages", :method => :get, :subdomain => 'app')
|
||||
assert @routes.recognize_path("/messages", :method => :get, :subdomain => 'app')
|
||||
end
|
||||
end
|
||||
|
||||
@ -395,7 +395,7 @@ def test_override_new_method
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get)
|
||||
assert_raise(ActionController::RoutingError) do
|
||||
@router.recognize_path("/messages/new", :method => :post)
|
||||
@routes.recognize_path("/messages/new", :method => :post)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -505,7 +505,7 @@ def test_shallow_nested_restful_routes_with_namespaces
|
||||
|
||||
def test_restful_routes_dont_generate_duplicates
|
||||
with_restful_routing :messages do
|
||||
routes = @router.routes
|
||||
routes = @routes.routes
|
||||
routes.each do |route|
|
||||
routes.each do |r|
|
||||
next if route === r # skip the comparison instance
|
||||
@ -1169,8 +1169,8 @@ def assert_restful_routes_for(controller_name, options = {})
|
||||
options[:shallow_options] = options[:options]
|
||||
end
|
||||
|
||||
new_action = @router.resources_path_names[:new] || "new"
|
||||
edit_action = @router.resources_path_names[:edit] || "edit"
|
||||
new_action = @routes.resources_path_names[:new] || "new"
|
||||
edit_action = @routes.resources_path_names[:edit] || "edit"
|
||||
|
||||
if options[:path_names]
|
||||
new_action = options[:path_names][:new] if options[:path_names][:new]
|
||||
@ -1237,7 +1237,7 @@ def assert_restful_named_routes_for(controller_name, singular_name = nil, option
|
||||
end
|
||||
|
||||
@controller = "#{options[:options][:controller].camelize}Controller".constantize.new
|
||||
@controller.singleton_class.send(:include, @router.url_helpers)
|
||||
@controller.singleton_class.send(:include, @routes.url_helpers)
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
get :index, options[:options]
|
||||
@ -1307,7 +1307,7 @@ def assert_singleton_routes_for(singleton_name, options = {})
|
||||
def assert_singleton_named_routes_for(singleton_name, options = {})
|
||||
(options[:options] ||= {})[:controller] ||= singleton_name.to_s.pluralize
|
||||
@controller = "#{options[:options][:controller].camelize}Controller".constantize.new
|
||||
@controller.singleton_class.send(:include, @router.url_helpers)
|
||||
@controller.singleton_class.send(:include, @routes.url_helpers)
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
get :show, options[:options]
|
||||
|
@ -478,8 +478,8 @@ def test_assert_realistic_path_parameters
|
||||
end
|
||||
|
||||
def test_with_routing_places_routes_back
|
||||
assert @router
|
||||
routes_id = @router.object_id
|
||||
assert @routes
|
||||
routes_id = @routes.object_id
|
||||
|
||||
begin
|
||||
with_routing { raise 'fail' }
|
||||
@ -487,8 +487,8 @@ def test_with_routing_places_routes_back
|
||||
rescue RuntimeError
|
||||
end
|
||||
|
||||
assert @router
|
||||
assert_equal routes_id, @router.object_id
|
||||
assert @routes
|
||||
assert_equal routes_id, @routes.object_id
|
||||
end
|
||||
|
||||
def test_remote_addr
|
||||
|
@ -10,8 +10,8 @@ def initialize(request)
|
||||
}
|
||||
end
|
||||
|
||||
def rewrite(router, options)
|
||||
router.url_for(@options.merge(options))
|
||||
def rewrite(routes, options)
|
||||
routes.url_for(@options.merge(options))
|
||||
end
|
||||
end
|
||||
|
||||
@ -23,63 +23,63 @@ def setup
|
||||
|
||||
def test_port
|
||||
assert_equal('http://test.host:1271/c/a/i',
|
||||
@rewriter.rewrite(@router, :controller => 'c', :action => 'a', :id => 'i', :port => 1271)
|
||||
@rewriter.rewrite(@routes, :controller => 'c', :action => 'a', :id => 'i', :port => 1271)
|
||||
)
|
||||
end
|
||||
|
||||
def test_protocol_with_and_without_separator
|
||||
assert_equal('https://test.host/c/a/i',
|
||||
@rewriter.rewrite(@router, :protocol => 'https', :controller => 'c', :action => 'a', :id => 'i')
|
||||
@rewriter.rewrite(@routes, :protocol => 'https', :controller => 'c', :action => 'a', :id => 'i')
|
||||
)
|
||||
|
||||
assert_equal('https://test.host/c/a/i',
|
||||
@rewriter.rewrite(@router, :protocol => 'https://', :controller => 'c', :action => 'a', :id => 'i')
|
||||
@rewriter.rewrite(@routes, :protocol => 'https://', :controller => 'c', :action => 'a', :id => 'i')
|
||||
)
|
||||
end
|
||||
|
||||
def test_user_name_and_password
|
||||
assert_equal(
|
||||
'http://david:secret@test.host/c/a/i',
|
||||
@rewriter.rewrite(@router, :user => "david", :password => "secret", :controller => 'c', :action => 'a', :id => 'i')
|
||||
@rewriter.rewrite(@routes, :user => "david", :password => "secret", :controller => 'c', :action => 'a', :id => 'i')
|
||||
)
|
||||
end
|
||||
|
||||
def test_user_name_and_password_with_escape_codes
|
||||
assert_equal(
|
||||
'http://openid.aol.com%2Fnextangler:one+two%3F@test.host/c/a/i',
|
||||
@rewriter.rewrite(@router, :user => "openid.aol.com/nextangler", :password => "one two?", :controller => 'c', :action => 'a', :id => 'i')
|
||||
@rewriter.rewrite(@routes, :user => "openid.aol.com/nextangler", :password => "one two?", :controller => 'c', :action => 'a', :id => 'i')
|
||||
)
|
||||
end
|
||||
|
||||
def test_anchor
|
||||
assert_equal(
|
||||
'http://test.host/c/a/i#anchor',
|
||||
@rewriter.rewrite(@router, :controller => 'c', :action => 'a', :id => 'i', :anchor => 'anchor')
|
||||
@rewriter.rewrite(@routes, :controller => 'c', :action => 'a', :id => 'i', :anchor => 'anchor')
|
||||
)
|
||||
end
|
||||
|
||||
def test_anchor_should_call_to_param
|
||||
assert_equal(
|
||||
'http://test.host/c/a/i#anchor',
|
||||
@rewriter.rewrite(@router, :controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anchor'))
|
||||
@rewriter.rewrite(@routes, :controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anchor'))
|
||||
)
|
||||
end
|
||||
|
||||
def test_anchor_should_be_cgi_escaped
|
||||
assert_equal(
|
||||
'http://test.host/c/a/i#anc%2Fhor',
|
||||
@rewriter.rewrite(@router, :controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anc/hor'))
|
||||
@rewriter.rewrite(@routes, :controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anc/hor'))
|
||||
)
|
||||
end
|
||||
|
||||
def test_trailing_slash
|
||||
options = {:controller => 'foo', :action => 'bar', :id => '3', :only_path => true}
|
||||
assert_equal '/foo/bar/3', @rewriter.rewrite(@router, options)
|
||||
assert_equal '/foo/bar/3?query=string', @rewriter.rewrite(@router, options.merge({:query => 'string'}))
|
||||
assert_equal '/foo/bar/3', @rewriter.rewrite(@routes, options)
|
||||
assert_equal '/foo/bar/3?query=string', @rewriter.rewrite(@routes, options.merge({:query => 'string'}))
|
||||
options.update({:trailing_slash => true})
|
||||
assert_equal '/foo/bar/3/', @rewriter.rewrite(@router, options)
|
||||
assert_equal '/foo/bar/3/', @rewriter.rewrite(@routes, options)
|
||||
options.update({:query => 'string'})
|
||||
assert_equal '/foo/bar/3/?query=string', @rewriter.rewrite(@router, options)
|
||||
assert_equal '/foo/bar/3/?query=string', @rewriter.rewrite(@routes, options)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -245,7 +245,7 @@ def test_typecast_as_yaml
|
||||
private
|
||||
def with_params_parsers(parsers = {})
|
||||
old_session = @integration_session
|
||||
@app = ActionDispatch::ParamsParser.new(app.router, parsers)
|
||||
@app = ActionDispatch::ParamsParser.new(app.routes, parsers)
|
||||
reset!
|
||||
yield
|
||||
ensure
|
||||
|
@ -122,7 +122,7 @@ class RackRequestTest < BaseRackTest
|
||||
test "cgi environment variables" do
|
||||
assert_equal "Basic", @request.auth_type
|
||||
assert_equal 0, @request.content_length
|
||||
assert_equal nil, @request.content_type
|
||||
assert_equal nil, @request.content_mime_type
|
||||
assert_equal "CGI/1.1", @request.gateway_interface
|
||||
assert_equal "*/*", @request.accept
|
||||
assert_equal "UTF-8", @request.accept_charset
|
||||
@ -177,12 +177,12 @@ class RackRequestParamsParsingTest < BaseRackTest
|
||||
class RackRequestContentTypeTest < BaseRackTest
|
||||
test "html content type verification" do
|
||||
@request.env['CONTENT_TYPE'] = Mime::HTML.to_s
|
||||
assert @request.content_type.verify_request?
|
||||
assert @request.content_mime_type.verify_request?
|
||||
end
|
||||
|
||||
test "xml content type verification" do
|
||||
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
|
||||
assert !@request.content_type.verify_request?
|
||||
assert !@request.content_mime_type.verify_request?
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -295,7 +295,7 @@ class RequestTest < ActiveSupport::TestCase
|
||||
|
||||
test "content type" do
|
||||
request = stub_request 'CONTENT_TYPE' => 'text/html'
|
||||
assert_equal Mime::HTML, request.content_type
|
||||
assert_equal Mime::HTML, request.content_mime_type
|
||||
end
|
||||
|
||||
test "can override format with parameter" do
|
||||
@ -310,17 +310,17 @@ class RequestTest < ActiveSupport::TestCase
|
||||
|
||||
test "no content type" do
|
||||
request = stub_request
|
||||
assert_equal nil, request.content_type
|
||||
assert_equal nil, request.content_mime_type
|
||||
end
|
||||
|
||||
test "content type is XML" do
|
||||
request = stub_request 'CONTENT_TYPE' => 'application/xml'
|
||||
assert_equal Mime::XML, request.content_type
|
||||
assert_equal Mime::XML, request.content_mime_type
|
||||
end
|
||||
|
||||
test "content type with charset" do
|
||||
request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8'
|
||||
assert_equal Mime::XML, request.content_type
|
||||
assert_equal Mime::XML, request.content_mime_type
|
||||
end
|
||||
|
||||
test "user agent" do
|
||||
|
@ -114,6 +114,8 @@ def self.matches?(request)
|
||||
resources :comments, :except => :destroy
|
||||
end
|
||||
|
||||
resources :sheep
|
||||
|
||||
match 'sprockets.js' => ::TestRoutingMapper::SprocketsApp
|
||||
|
||||
match 'people/:id/update', :to => 'people#update', :as => :update_person
|
||||
@ -171,6 +173,12 @@ def self.matches?(request)
|
||||
resources :descriptions
|
||||
root :to => 'projects#index'
|
||||
end
|
||||
|
||||
resources :products, :constraints => { :id => /\d{4}/ } do
|
||||
resources :images
|
||||
end
|
||||
|
||||
resource :dashboard, :constraints => { :ip => /192\.168\.1\.\d{1,3}/ }
|
||||
end
|
||||
end
|
||||
|
||||
@ -525,6 +533,23 @@ def test_resource_routes_with_only_and_except
|
||||
end
|
||||
end
|
||||
|
||||
def test_resource_with_slugs_in_ids
|
||||
with_test_routes do
|
||||
get '/posts/rails-rocks'
|
||||
assert_equal 'posts#show', @response.body
|
||||
assert_equal '/posts/rails-rocks', post_path(:id => 'rails-rocks')
|
||||
end
|
||||
end
|
||||
|
||||
def test_resources_for_uncountable_names
|
||||
with_test_routes do
|
||||
assert_equal '/sheep', sheep_index_path
|
||||
assert_equal '/sheep/1', sheep_path(1)
|
||||
assert_equal '/sheep/new', new_sheep_path
|
||||
assert_equal '/sheep/1/edit', edit_sheep_path(1)
|
||||
end
|
||||
end
|
||||
|
||||
def test_path_names
|
||||
with_test_routes do
|
||||
get '/es/projeto'
|
||||
@ -794,6 +819,26 @@ def test_default_params
|
||||
end
|
||||
end
|
||||
|
||||
def test_resource_constraints
|
||||
with_test_routes do
|
||||
assert_raise(ActionController::RoutingError) { get '/products/1' }
|
||||
get '/products'
|
||||
assert_equal 'products#index', @response.body
|
||||
get '/products/0001'
|
||||
assert_equal 'products#show', @response.body
|
||||
|
||||
assert_raise(ActionController::RoutingError) { get '/products/1/images' }
|
||||
get '/products/0001/images'
|
||||
assert_equal 'images#index', @response.body
|
||||
get '/products/0001/images/1'
|
||||
assert_equal 'images#show', @response.body
|
||||
|
||||
assert_raise(ActionController::RoutingError) { get '/dashboard', {}, {'REMOTE_ADDR' => '10.0.0.100'} }
|
||||
get '/dashboard', {}, {'REMOTE_ADDR' => '192.168.1.100'}
|
||||
assert_equal 'dashboards#show', @response.body
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_test_routes
|
||||
yield
|
||||
|
@ -266,6 +266,10 @@ def test_error_message_on_with_options_hash
|
||||
assert_dom_equal "<div class=\"differentError\">beforecan't be emptyafter</div>", error_message_on(:post, :author_name, :css_class => 'differentError', :prepend_text => 'before', :append_text => 'after')
|
||||
end
|
||||
|
||||
def test_error_message_on_with_tag_option_in_options_hash
|
||||
assert_dom_equal "<span class=\"differentError\">beforecan't be emptyafter</span>", error_message_on(:post, :author_name, :html_tag => "span", :css_class => 'differentError', :prepend_text => 'before', :append_text => 'after')
|
||||
end
|
||||
|
||||
def test_error_message_on_handles_empty_errors
|
||||
assert_equal "", error_message_on(@post, :tag)
|
||||
end
|
||||
|
@ -373,6 +373,22 @@ def test_timebased_asset_id
|
||||
assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
|
||||
end
|
||||
|
||||
def test_string_asset_id
|
||||
@controller.config.asset_path = "/assets.v12345%s"
|
||||
|
||||
expected_path = "/assets.v12345/images/rails.png"
|
||||
assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
|
||||
end
|
||||
|
||||
def test_proc_asset_id
|
||||
@controller.config.asset_path = Proc.new do |asset_path|
|
||||
"/assets.v12345#{asset_path}"
|
||||
end
|
||||
|
||||
expected_path = "/assets.v12345/images/rails.png"
|
||||
assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
|
||||
end
|
||||
|
||||
def test_timebased_asset_id_with_relative_url_root
|
||||
@controller.config.relative_url_root = "/collaboration/hieraki"
|
||||
expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s
|
||||
|
@ -114,7 +114,7 @@ def render_from_helper
|
||||
end
|
||||
|
||||
test "is able to use routes" do
|
||||
controller.request.assign_parameters(@router, 'foo', 'index')
|
||||
controller.request.assign_parameters(@routes, 'foo', 'index')
|
||||
assert_equal '/foo', url_for
|
||||
assert_equal '/bar', url_for(:controller => 'bar')
|
||||
end
|
||||
|
@ -1,4 +1,7 @@
|
||||
require 'active_model/attribute_methods'
|
||||
require 'active_support/concern'
|
||||
require 'active_support/hash_with_indifferent_access'
|
||||
require 'active_support/core_ext/object/duplicable'
|
||||
|
||||
module ActiveModel
|
||||
# <tt>ActiveModel::Dirty</tt> provides a way to track changes in your
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/ordered_hash'
|
||||
|
||||
module ActiveModel
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require "active_support/core_ext/module/anonymous"
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveModel #:nodoc:
|
||||
# A simple base class that can be used along with
|
||||
|
@ -1,9 +1,11 @@
|
||||
*Edge*
|
||||
*Rails 3.0.0 [Beta 2] (pending)*
|
||||
|
||||
* To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 [Andrew White]
|
||||
|
||||
* Silenced "SHOW FIELDS" and "SET SQL_AUTO_IS_NULL=0" statements from the MySQL driver to improve log signal to noise ration in development [DHH]
|
||||
|
||||
|
||||
*Rails 3.0 [Beta] (February 4th, 2010)*
|
||||
*Rails 3.0.0 [Beta 1] (February 4th, 2010)*
|
||||
|
||||
* PostgreSQLAdapter: set time_zone to UTC when Base.default_timezone == :utc so that Postgres doesn't incorrectly offset-adjust values inserted into TIMESTAMP WITH TIME ZONE columns. #3777 [Jack Christensen]
|
||||
|
||||
|
@ -111,10 +111,10 @@ module ConnectionAdapters
|
||||
|
||||
autoload :TestCase
|
||||
autoload :TestFixtures, 'active_record/fixtures'
|
||||
end
|
||||
|
||||
base_hook do
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
Arel::Table.engine = Arel::Sql::Engine.new(self)
|
||||
end
|
||||
end
|
||||
|
||||
I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
|
@ -159,6 +159,11 @@ def set_association_single_records(id_to_record_map, reflection_name, associated
|
||||
association_proxy.__send__(:set_inverse_instance, associated_record, mapped_record)
|
||||
end
|
||||
end
|
||||
|
||||
id_to_record_map.each do |id, records|
|
||||
next if seen_keys.include?(id.to_s)
|
||||
records.each {|record| record.send("set_#{reflection_name}_target", nil) }
|
||||
end
|
||||
end
|
||||
|
||||
# Given a collection of ActiveRecord objects, constructs a Hash which maps
|
||||
@ -324,7 +329,7 @@ def preload_belongs_to_association(records, reflection, preload_options={})
|
||||
klass = klass_name.constantize
|
||||
|
||||
table_name = klass.quoted_table_name
|
||||
primary_key = klass.primary_key
|
||||
primary_key = reflection.options[:primary_key] || klass.primary_key
|
||||
column_type = klass.columns.detect{|c| c.name == primary_key}.type
|
||||
|
||||
ids = id_map.keys.map do |id|
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/core_ext/enumerable'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
|
||||
@ -85,6 +86,15 @@ def initialize(reflection)
|
||||
end
|
||||
end
|
||||
|
||||
# This error is raised when trying to destroy a parent instance in a N:1, 1:1 assosications
|
||||
# (has_many, has_one) when there is at least 1 child assosociated instance.
|
||||
# ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
|
||||
class DeleteRestrictionError < ActiveRecordError #:nodoc:
|
||||
def initialize(reflection)
|
||||
super("Cannot delete record because of dependent #{reflection.name}")
|
||||
end
|
||||
end
|
||||
|
||||
# See ActiveRecord::Associations::ClassMethods for documentation.
|
||||
module Associations # :nodoc:
|
||||
extend ActiveSupport::Concern
|
||||
@ -830,6 +840,8 @@ module ClassMethods
|
||||
# objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
|
||||
# objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
|
||||
# the <tt>:through</tt> option.
|
||||
# the <tt>:through</tt> option. If set to <tt>:restrict</tt>
|
||||
# this object cannot be deleted if it has any associated object.
|
||||
# [:finder_sql]
|
||||
# Specify a complete SQL statement to fetch the association. This is a good way to go for complex
|
||||
# associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
|
||||
@ -1468,9 +1480,15 @@ def add_touch_callbacks(reflection, touch_attribute)
|
||||
|
||||
# Creates before_destroy callback methods that nullify, delete or destroy
|
||||
# has_many associated objects, according to the defined :dependent rule.
|
||||
# If the association is marked as :dependent => :restrict, create a callback
|
||||
# that prevents deleting entirely.
|
||||
#
|
||||
# See HasManyAssociation#delete_records. Dependent associations
|
||||
# delete children, otherwise foreign key is set to NULL.
|
||||
# See HasManyAssociation#delete_records. Dependent associations
|
||||
# delete children if the option is set to :destroy or :delete_all, set the
|
||||
# foreign key to NULL if the option is set to :nullify, and do not touch the
|
||||
# child records if the option is set to :restrict.
|
||||
#
|
||||
# The +extra_conditions+ parameter, which is not used within the main
|
||||
# Active Record codebase, is meant to allow plugins to define extra
|
||||
@ -1530,14 +1548,24 @@ def configure_dependency_for_has_many(reflection, extra_conditions = nil)
|
||||
%@#{dependent_conditions}@)
|
||||
end
|
||||
CALLBACK
|
||||
when :restrict
|
||||
method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
unless send(reflection.name).empty?
|
||||
raise DeleteRestrictionError.new(reflection)
|
||||
end
|
||||
end
|
||||
before_destroy method_name
|
||||
else
|
||||
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
|
||||
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, :nullify or :restrict (#{reflection.options[:dependent].inspect})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates before_destroy callback methods that nullify, delete or destroy
|
||||
# has_one associated objects, according to the defined :dependent rule.
|
||||
# If the association is marked as :dependent => :restrict, create a callback
|
||||
# that prevents deleting entirely.
|
||||
def configure_dependency_for_has_one(reflection)
|
||||
if reflection.options.include?(:dependent)
|
||||
name = reflection.options[:dependent]
|
||||
@ -1558,8 +1586,16 @@ def #{method_name}
|
||||
association.update_attribute(#{reflection.primary_key_name.inspect}, nil) if association
|
||||
end
|
||||
eoruby
|
||||
when :restrict
|
||||
method_name = "has_one_dependent_restrict_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
unless send(reflection.name).nil?
|
||||
raise DeleteRestrictionError.new(reflection)
|
||||
end
|
||||
end
|
||||
before_destroy method_name
|
||||
else
|
||||
raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})"
|
||||
raise ArgumentError, "The :dependent option expects either :destroy, :delete, :nullify or :restrict (#{reflection.options[:dependent].inspect})"
|
||||
end
|
||||
|
||||
before_destroy method_name
|
||||
@ -1995,7 +2031,7 @@ def association_join
|
||||
[aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])]
|
||||
end
|
||||
when :belongs_to
|
||||
[aliased_table[reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])]
|
||||
[aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])]
|
||||
end
|
||||
|
||||
unless klass.descends_from_active_record?
|
||||
|
@ -1,4 +1,5 @@
|
||||
require "active_record/associations/through_association_scope"
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module Associations
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module AttributeMethods
|
||||
module Dirty
|
||||
|
@ -3,6 +3,12 @@ module AttributeMethods
|
||||
module PrimaryKey
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# Returns this record's primary key value wrapped in an Array
|
||||
# or nil if the record is a new_record?
|
||||
def to_key
|
||||
new_record? ? nil : [ send(self.class.primary_key) ]
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
|
||||
# primary_key_prefix_type setting, though.
|
||||
@ -39,22 +45,6 @@ def set_primary_key(value = nil, &block)
|
||||
end
|
||||
alias :primary_key= :set_primary_key
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
# Returns this record's primary key value wrapped in an Array
|
||||
# or nil if the record is a new_record?
|
||||
# This is done to comply with the AMo interface that expects
|
||||
# every AMo compliant object to respond_to?(:to_key) and return
|
||||
# an Enumerable object from that call, or nil if new_record?
|
||||
# This method also takes custom primary keys specified via
|
||||
# the +set_primary_key+ into account.
|
||||
def to_key
|
||||
new_record? ? nil : [ self.primary_key ]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module AttributeMethods
|
||||
module Query
|
||||
|
@ -13,6 +13,8 @@
|
||||
require 'active_support/core_ext/string/behavior'
|
||||
require 'active_support/core_ext/object/singleton_class'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/core_ext/object/duplicable'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'arel'
|
||||
require 'active_record/errors'
|
||||
|
||||
@ -336,6 +338,9 @@ def self.reset_subclasses #:nodoc:
|
||||
# Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all
|
||||
# table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace
|
||||
# for tables in a shared database. By default, the prefix is the empty string.
|
||||
#
|
||||
# If you are organising your models within modules you can add a prefix to the models within a namespace by defining
|
||||
# a singleton method in the parent module called table_name_prefix which returns your chosen prefix.
|
||||
cattr_accessor :table_name_prefix, :instance_writer => false
|
||||
@@table_name_prefix = ""
|
||||
|
||||
@ -763,7 +768,7 @@ def reset_table_name #:nodoc:
|
||||
contained = contained.singularize if parent.pluralize_table_names
|
||||
contained << '_'
|
||||
end
|
||||
name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
|
||||
name = "#{full_table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
|
||||
end
|
||||
|
||||
@quoted_table_name = nil
|
||||
@ -771,6 +776,10 @@ def reset_table_name #:nodoc:
|
||||
name
|
||||
end
|
||||
|
||||
def full_table_name_prefix #:nodoc:
|
||||
(parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
|
||||
end
|
||||
|
||||
# Defines the column name for use with single table inheritance
|
||||
# -- can be set in subclasses like so: self.inheritance_column = "type_id"
|
||||
def inheritance_column
|
||||
@ -1288,7 +1297,7 @@ def subclasses #:nodoc:
|
||||
# <tt>options</tt> argument is the same as in find.
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# default_scope :order => 'last_name, first_name'
|
||||
# default_scope order('last_name, first_name')
|
||||
# end
|
||||
def default_scope(options = {})
|
||||
self.default_scoping << construct_finder_arel(options)
|
||||
@ -2214,6 +2223,7 @@ def object_from_yaml(string)
|
||||
extend QueryCache::ClassMethods
|
||||
extend ActiveSupport::Benchmarkable
|
||||
|
||||
include ActiveModel::Conversion
|
||||
include Validations
|
||||
include Locking::Optimistic, Locking::Pessimistic
|
||||
include AttributeMethods
|
||||
@ -2223,12 +2233,10 @@ def object_from_yaml(string)
|
||||
include AttributeMethods::Dirty
|
||||
include Callbacks, ActiveModel::Observing, Timestamp
|
||||
include Associations, AssociationPreload, NamedScope
|
||||
include ActiveModel::Conversion
|
||||
|
||||
# AutosaveAssociation needs to be included before Transactions, because we want
|
||||
# #save_with_autosave_associations to be wrapped inside a transaction.
|
||||
include AutosaveAssociation, NestedAttributes
|
||||
|
||||
include Aggregations, Transactions, Reflection, Serialization
|
||||
|
||||
NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
|
||||
@ -2237,4 +2245,4 @@ def object_from_yaml(string)
|
||||
|
||||
# TODO: Remove this and make it work with LAZY flag
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
ActiveRecord.run_base_hooks(ActiveRecord::Base)
|
||||
ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/duplicable'
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters # :nodoc:
|
||||
module QueryCache
|
||||
|
@ -1,3 +1,4 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'date'
|
||||
require 'set'
|
||||
require 'bigdecimal'
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
require 'active_support/core_ext/kernel/requires'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'set'
|
||||
|
||||
module MysqlCompat #:nodoc:
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
require 'active_support/core_ext/kernel/requires'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
begin
|
||||
require_library_or_gem 'pg'
|
||||
@ -299,7 +300,7 @@ def table_alias_length
|
||||
# QUOTING ==================================================
|
||||
|
||||
# Escapes binary strings for bytea input to the database.
|
||||
def escape_bytea(value)
|
||||
def escape_bytea(original_value)
|
||||
if @connection.respond_to?(:escape_bytea)
|
||||
self.class.instance_eval do
|
||||
define_method(:escape_bytea) do |value|
|
||||
@ -323,13 +324,13 @@ def escape_bytea(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
escape_bytea(value)
|
||||
escape_bytea(original_value)
|
||||
end
|
||||
|
||||
# Unescapes bytea output from a database to the binary string it represents.
|
||||
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
|
||||
# on escaped binary output from database drive.
|
||||
def unescape_bytea(value)
|
||||
def unescape_bytea(original_value)
|
||||
# In each case, check if the value actually is escaped PostgreSQL bytea output
|
||||
# or an unescaped Active Record attribute that was just written.
|
||||
if PGconn.respond_to?(:unescape_bytea)
|
||||
@ -369,7 +370,7 @@ def unescape_bytea(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
unescape_bytea(value)
|
||||
unescape_bytea(original_value)
|
||||
end
|
||||
|
||||
# Quotes PostgreSQL-specific data types for SQL input.
|
||||
@ -394,7 +395,7 @@ def quote(value, column = nil) #:nodoc:
|
||||
end
|
||||
|
||||
# Quotes strings for use in SQL input in the postgres driver for better performance.
|
||||
def quote_string(s) #:nodoc:
|
||||
def quote_string(original_value) #:nodoc:
|
||||
if @connection.respond_to?(:escape)
|
||||
self.class.instance_eval do
|
||||
define_method(:quote_string) do |s|
|
||||
@ -414,7 +415,7 @@ def quote_string(s) #:nodoc:
|
||||
remove_method(:quote_string)
|
||||
end
|
||||
end
|
||||
quote_string(s)
|
||||
quote_string(original_value)
|
||||
end
|
||||
|
||||
# Checks the following cases:
|
||||
|
@ -4,6 +4,7 @@
|
||||
require 'zlib'
|
||||
require 'active_support/dependencies'
|
||||
require 'active_support/core_ext/logger'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
if RUBY_VERSION < '1.9'
|
||||
module YAML #:nodoc:
|
||||
|
@ -1,6 +1,7 @@
|
||||
require 'active_support/core_ext/array'
|
||||
require 'active_support/core_ext/hash/except'
|
||||
require 'active_support/core_ext/object/singleton_class'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module NamedScope
|
||||
@ -101,7 +102,8 @@ def scope(name, options = {}, &block)
|
||||
name = name.to_sym
|
||||
|
||||
if !scopes[name] && respond_to?(name, true)
|
||||
raise ArgumentError, "Cannot define scope :#{name} because #{self.name}.#{name} method already exists."
|
||||
logger.warn "Creating scope :#{name}. " \
|
||||
"Overwriting existing method #{self.name}.#{name}."
|
||||
end
|
||||
|
||||
scopes[name] = lambda do |parent_scope, *args|
|
||||
@ -165,7 +167,14 @@ def last(*args)
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.respond_to?(:to_ary) ? to_a == other.to_a : false
|
||||
case other
|
||||
when Scope
|
||||
to_sql == other.to_sql
|
||||
when Relation
|
||||
other == self
|
||||
when Array
|
||||
to_a == other.to_a
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'active_support/core_ext/hash/except'
|
||||
require 'active_support/core_ext/object/try'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module NestedAttributes #:nodoc:
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
class QueryCache
|
||||
module ClassMethods
|
||||
|
@ -23,18 +23,18 @@ class Railtie < Rails::Railtie
|
||||
log_subscriber :active_record, ActiveRecord::Railties::LogSubscriber.new
|
||||
|
||||
initializer "active_record.initialize_timezone" do
|
||||
ActiveRecord.base_hook do
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
self.time_zone_aware_attributes = true
|
||||
self.default_timezone = :utc
|
||||
end
|
||||
end
|
||||
|
||||
initializer "active_record.logger" do
|
||||
ActiveRecord.base_hook { self.logger ||= ::Rails.logger }
|
||||
ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
|
||||
end
|
||||
|
||||
initializer "active_record.set_configs" do |app|
|
||||
ActiveRecord.base_hook do
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
app.config.active_record.each do |k,v|
|
||||
send "#{k}=", v
|
||||
end
|
||||
@ -44,7 +44,7 @@ class Railtie < Rails::Railtie
|
||||
# This sets the database configuration from Configuration#database_configuration
|
||||
# and then establishes the connection.
|
||||
initializer "active_record.initialize_database" do |app|
|
||||
ActiveRecord.base_hook do
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
self.configurations = app.config.database_configuration
|
||||
establish_connection
|
||||
end
|
||||
@ -53,7 +53,7 @@ class Railtie < Rails::Railtie
|
||||
# Expose database runtime to controller for logging.
|
||||
initializer "active_record.log_runtime" do |app|
|
||||
require "active_record/railties/controller_runtime"
|
||||
ActionController.base_hook do
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
include ActiveRecord::Railties::ControllerRuntime
|
||||
end
|
||||
end
|
||||
@ -71,9 +71,9 @@ class Railtie < Rails::Railtie
|
||||
end
|
||||
|
||||
initializer "active_record.load_observers" do
|
||||
ActiveRecord.base_hook { instantiate_observers }
|
||||
ActiveSupport.on_load(:active_record) { instantiate_observers }
|
||||
|
||||
ActiveRecord.base_hook do
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do
|
||||
ActiveRecord::Base.instantiate_observers
|
||||
end
|
||||
@ -81,7 +81,7 @@ class Railtie < Rails::Railtie
|
||||
end
|
||||
|
||||
initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
|
||||
ActiveRecord.base_hook do
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
unless app.config.cache_classes
|
||||
ActionDispatch::Callbacks.after do
|
||||
ActiveRecord::Base.reset_subclasses
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
class Relation
|
||||
JoinOperation = Struct.new(:relation, :join_class, :on)
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module Batches # :nodoc:
|
||||
# Yields each record that was found by the find +options+. The find is
|
||||
@ -48,6 +50,10 @@ def find_each(options = {})
|
||||
def find_in_batches(options = {})
|
||||
relation = self
|
||||
|
||||
if orders.present? || taken.present?
|
||||
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
|
||||
end
|
||||
|
||||
if (finder_options = options.except(:start, :batch_size)).present?
|
||||
raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
|
||||
raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present?
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module Calculations
|
||||
# Count operates using three different approaches.
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module FinderMethods
|
||||
# Find operates with four different retrieval approaches:
|
||||
|
@ -27,12 +27,7 @@ def build_from_hash(attributes, default_table)
|
||||
values = value.to_a
|
||||
attribute.in(values)
|
||||
when Range
|
||||
# TODO : Arel should handle ranges with excluded end.
|
||||
if value.exclude_end?
|
||||
[attribute.gteq(value.begin), attribute.lt(value.end)]
|
||||
else
|
||||
attribute.in(value)
|
||||
end
|
||||
else
|
||||
attribute.eq(value)
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module QueryMethods
|
||||
extend ActiveSupport::Concern
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
module SpawnMethods
|
||||
def merge(r)
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActiveRecord
|
||||
# Allows programmers to programmatically define a schema in a portable
|
||||
# DSL. This means you can define tables, indexes, etc. without using SQL
|
||||
|
@ -67,13 +67,11 @@ def mount_sql_and_params(klass, table_name, attribute, value) #:nodoc:
|
||||
|
||||
if value.nil? || (options[:case_sensitive] || !column.text?)
|
||||
sql = "#{sql_attribute} #{operator}"
|
||||
params = [value]
|
||||
else
|
||||
sql = "LOWER(#{sql_attribute}) #{operator}"
|
||||
params = [value.mb_chars.downcase]
|
||||
sql = "LOWER(#{sql_attribute}) = LOWER(?)"
|
||||
end
|
||||
|
||||
[sql, params]
|
||||
[sql, [value]]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -32,6 +32,17 @@ def test_belongs_to_with_primary_key
|
||||
assert_equal companies(:first_firm).name, client.firm_with_primary_key.name
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key_joins_on_correct_column
|
||||
sql = Client.joins(:firm_with_primary_key).to_sql
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
assert_no_match /`firm_with_primary_keys_companies`\.`id`/, sql
|
||||
assert_match /`firm_with_primary_keys_companies`\.`name`/, sql
|
||||
else
|
||||
assert_no_match /"firm_with_primary_keys_companies"\."id"/, sql
|
||||
assert_match /"firm_with_primary_keys_companies"\."name"/, sql
|
||||
end
|
||||
end
|
||||
|
||||
def test_proxy_assignment
|
||||
account = Account.find(1)
|
||||
assert_nothing_raised { account.firm = account.firm }
|
||||
@ -61,6 +72,13 @@ def test_natural_assignment_with_primary_key
|
||||
assert_equal apple.name, citibank.firm_name
|
||||
end
|
||||
|
||||
def test_eager_loading_with_primary_key
|
||||
apple = Firm.create("name" => "Apple")
|
||||
citibank = Client.create("name" => "Citibank", :firm_name => "Apple")
|
||||
citibank_result = Client.find(:first, :conditions => {:name => "Citibank"}, :include => :firm_with_primary_key)
|
||||
assert_not_nil citibank_result.instance_variable_get("@firm_with_primary_key")
|
||||
end
|
||||
|
||||
def test_no_unexpected_aliasing
|
||||
first_firm = companies(:first_firm)
|
||||
another_firm = companies(:another_firm)
|
||||
@ -439,9 +457,15 @@ def test_dependent_delete_and_destroy_with_belongs_to
|
||||
assert_equal [author_address.id], AuthorAddress.destroyed_author_address_ids
|
||||
end
|
||||
|
||||
def test_invalid_belongs_to_dependent_option_raises_exception
|
||||
def test_invalid_belongs_to_dependent_option_nullify_raises_exception
|
||||
assert_raise ArgumentError do
|
||||
Author.belongs_to :special_author_address, :dependent => :nullify
|
||||
end
|
||||
end
|
||||
|
||||
def test_invalid_belongs_to_dependent_option_restrict_raises_exception
|
||||
assert_raise ArgumentError do
|
||||
Author.belongs_to :special_author_address, :dependent => :restrict
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -833,4 +833,10 @@ def test_include_has_one_using_primary_key
|
||||
end
|
||||
end
|
||||
|
||||
def test_preloading_empty_polymorphic_parent
|
||||
t = Tagging.create!(:taggable_type => 'Post', :taggable_id => Post.maximum(:id) + 1, :tag => tags(:general))
|
||||
|
||||
assert_queries(2) { @tagging = Tagging.preload(:taggable).find(t.id) }
|
||||
assert_no_queries { assert ! @tagging.taggable }
|
||||
end
|
||||
end
|
||||
|
@ -836,6 +836,14 @@ def test_depends_and_nullify
|
||||
assert_equal num_accounts, Account.count
|
||||
end
|
||||
|
||||
def test_restrict
|
||||
firm = RestrictedFirm.new(:name => 'restrict')
|
||||
firm.save!
|
||||
child_firm = firm.companies.create(:name => 'child')
|
||||
assert !firm.companies.empty?
|
||||
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
|
||||
end
|
||||
|
||||
def test_included_in_collection
|
||||
assert companies(:first_firm).clients.include?(Client.find(2))
|
||||
end
|
||||
|
@ -177,7 +177,15 @@ def test_dependence_with_nil_associate
|
||||
assert_nothing_raised { firm.destroy }
|
||||
end
|
||||
|
||||
def test_succesful_build_association
|
||||
def test_dependence_with_restrict
|
||||
firm = RestrictedFirm.new(:name => 'restrict')
|
||||
firm.save!
|
||||
account = firm.create_account(:credit_limit => 10)
|
||||
assert !firm.account.nil?
|
||||
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
|
||||
end
|
||||
|
||||
def test_successful_build_association
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.save
|
||||
|
||||
|
@ -29,6 +29,16 @@ def test_each_should_raise_if_the_limit_is_set
|
||||
end
|
||||
end
|
||||
|
||||
def test_warn_if_limit_scope_is_set
|
||||
ActiveRecord::Base.logger.expects(:warn)
|
||||
Post.limit(1).find_each { |post| post }
|
||||
end
|
||||
|
||||
def test_warn_if_order_scope_is_set
|
||||
ActiveRecord::Base.logger.expects(:warn)
|
||||
Post.order("title").find_each { |post| post }
|
||||
end
|
||||
|
||||
def test_find_in_batches_should_return_batches
|
||||
assert_queries(Post.count + 1) do
|
||||
Post.find_in_batches(:batch_size => 1) do |batch|
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user