Rails 2.1.1
Among other things, a security fix.
This commit is contained in:
parent
d2c4c8737c
commit
d4f97345db
354 changed files with 21027 additions and 3072 deletions
5
vendor/rails/actionmailer/CHANGELOG
vendored
5
vendor/rails/actionmailer/CHANGELOG
vendored
|
@ -1,3 +1,8 @@
|
||||||
|
*2.1.1 (September 4th, 2008)*
|
||||||
|
|
||||||
|
* Included in Rails 2.1.1
|
||||||
|
|
||||||
|
|
||||||
*2.1.0 (May 31st, 2008)*
|
*2.1.0 (May 31st, 2008)*
|
||||||
|
|
||||||
* Fixed that a return-path header would be ignored #7572 [joost]
|
* Fixed that a return-path header would be ignored #7572 [joost]
|
||||||
|
|
11
vendor/rails/actionmailer/Rakefile
vendored
11
vendor/rails/actionmailer/Rakefile
vendored
|
@ -5,6 +5,8 @@ require 'rake/rdoctask'
|
||||||
require 'rake/packagetask'
|
require 'rake/packagetask'
|
||||||
require 'rake/gempackagetask'
|
require 'rake/gempackagetask'
|
||||||
require 'rake/contrib/sshpublisher'
|
require 'rake/contrib/sshpublisher'
|
||||||
|
require 'rake/contrib/rubyforgepublisher'
|
||||||
|
|
||||||
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
|
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
|
||||||
|
|
||||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||||
|
@ -35,7 +37,7 @@ Rake::RDocTask.new { |rdoc|
|
||||||
rdoc.title = "Action Mailer -- Easy email delivery and testing"
|
rdoc.title = "Action Mailer -- Easy email delivery and testing"
|
||||||
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
||||||
rdoc.options << '--charset' << 'utf-8'
|
rdoc.options << '--charset' << 'utf-8'
|
||||||
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
|
||||||
rdoc.rdoc_files.include('README', 'CHANGELOG')
|
rdoc.rdoc_files.include('README', 'CHANGELOG')
|
||||||
rdoc.rdoc_files.include('lib/action_mailer.rb')
|
rdoc.rdoc_files.include('lib/action_mailer.rb')
|
||||||
rdoc.rdoc_files.include('lib/action_mailer/*.rb')
|
rdoc.rdoc_files.include('lib/action_mailer/*.rb')
|
||||||
|
@ -55,7 +57,7 @@ spec = Gem::Specification.new do |s|
|
||||||
s.rubyforge_project = "actionmailer"
|
s.rubyforge_project = "actionmailer"
|
||||||
s.homepage = "http://www.rubyonrails.org"
|
s.homepage = "http://www.rubyonrails.org"
|
||||||
|
|
||||||
s.add_dependency('actionpack', '= 2.1.0' + PKG_BUILD)
|
s.add_dependency('actionpack', '= 2.1.1' + PKG_BUILD)
|
||||||
|
|
||||||
s.has_rdoc = true
|
s.has_rdoc = true
|
||||||
s.requirements << 'none'
|
s.requirements << 'none'
|
||||||
|
@ -76,12 +78,13 @@ end
|
||||||
|
|
||||||
desc "Publish the API documentation"
|
desc "Publish the API documentation"
|
||||||
task :pgem => [:package] do
|
task :pgem => [:package] do
|
||||||
Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
Rake::SshFilePublisher.new("david@greed.loudthinking.com", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
||||||
|
`ssh david@greed.loudthinking.com '/u/sites/gems/gemupdate.sh'`
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Publish the API documentation"
|
desc "Publish the API documentation"
|
||||||
task :pdoc => [:rdoc] do
|
task :pdoc => [:rdoc] do
|
||||||
Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/am", "doc").upload
|
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/am", "doc").upload
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Publish the release files to RubyForge."
|
desc "Publish the release files to RubyForge."
|
||||||
|
|
|
@ -530,7 +530,7 @@ module ActionMailer #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_message(method_name, body)
|
def render_message(method_name, body)
|
||||||
render :file => method_name, :body => body
|
render :file => method_name, :body => body, :use_full_path => true
|
||||||
end
|
end
|
||||||
|
|
||||||
def render(opts)
|
def render(opts)
|
||||||
|
@ -538,6 +538,7 @@ module ActionMailer #:nodoc:
|
||||||
if opts[:file] && opts[:file] !~ /\//
|
if opts[:file] && opts[:file] !~ /\//
|
||||||
opts[:file] = "#{mailer_name}/#{opts[:file]}"
|
opts[:file] = "#{mailer_name}/#{opts[:file]}"
|
||||||
end
|
end
|
||||||
|
opts[:use_full_path] = true
|
||||||
initialize_template_class(body).render(opts)
|
initialize_template_class(body).render(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ module ActionMailer
|
||||||
module VERSION #:nodoc:
|
module VERSION #:nodoc:
|
||||||
MAJOR = 2
|
MAJOR = 2
|
||||||
MINOR = 1
|
MINOR = 1
|
||||||
TINY = 0
|
TINY = 1
|
||||||
|
|
||||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||||
end
|
end
|
||||||
|
|
19
vendor/rails/actionmailer/test/abstract_unit.rb
vendored
19
vendor/rails/actionmailer/test/abstract_unit.rb
vendored
|
@ -30,13 +30,20 @@ class Net::SMTP
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Wrap tests that use Mocha and skip if unavailable.
|
def uses_gem(gem_name, test_name, version = '> 0')
|
||||||
def uses_mocha(test_name)
|
require 'rubygems'
|
||||||
gem 'mocha', ">=0.5"
|
gem gem_name.to_s, version
|
||||||
require 'stubba'
|
require gem_name.to_s
|
||||||
yield
|
yield
|
||||||
rescue Gem::LoadError
|
rescue LoadError
|
||||||
$stderr.puts "Skipping #{test_name} tests (Mocha >= 0.5 is required). `gem install mocha` and try again."
|
$stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again."
|
||||||
|
end
|
||||||
|
|
||||||
|
# Wrap tests that use Mocha and skip if unavailable.
|
||||||
|
unless defined? uses_mocha
|
||||||
|
def uses_mocha(test_name, &block)
|
||||||
|
uses_gem('mocha', test_name, '>= 0.5.5', &block)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_delivery_method(delivery_method)
|
def set_delivery_method(delivery_method)
|
||||||
|
|
17
vendor/rails/actionpack/CHANGELOG
vendored
17
vendor/rails/actionpack/CHANGELOG
vendored
|
@ -1,3 +1,20 @@
|
||||||
|
*2.1.1 (September 4th, 2008)*
|
||||||
|
|
||||||
|
* All 2xx requests are considered successful [Josh Peek]
|
||||||
|
|
||||||
|
* Deprecate the limited follow_redirect in functional tests. If you wish to follow redirects, use integration tests. [Michael Koziarski]
|
||||||
|
|
||||||
|
* Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run [DHH]
|
||||||
|
|
||||||
|
* Deprecate define_javascript_functions, javascript_include_tag and friends are much better [Michael Koziarski]
|
||||||
|
|
||||||
|
* Fix polymorphic_url with singleton resources. #461 [Tammer Saleh]
|
||||||
|
|
||||||
|
* Deprecate ActionView::Base.erb_variable. Use the concat helper method instead of appending to it directly. [Jeremy Kemper]
|
||||||
|
|
||||||
|
* Fixed Request#remote_ip to only raise hell if the HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR doesn't match (not just if they're both present) [Mark Imbriaco, Bradford Folkens]
|
||||||
|
|
||||||
|
|
||||||
*2.1.0 (May 31st, 2008)*
|
*2.1.0 (May 31st, 2008)*
|
||||||
|
|
||||||
* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing]
|
* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing]
|
||||||
|
|
16
vendor/rails/actionpack/Rakefile
vendored
16
vendor/rails/actionpack/Rakefile
vendored
|
@ -5,6 +5,8 @@ require 'rake/rdoctask'
|
||||||
require 'rake/packagetask'
|
require 'rake/packagetask'
|
||||||
require 'rake/gempackagetask'
|
require 'rake/gempackagetask'
|
||||||
require 'rake/contrib/sshpublisher'
|
require 'rake/contrib/sshpublisher'
|
||||||
|
require 'rake/contrib/rubyforgepublisher'
|
||||||
|
|
||||||
require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
|
require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
|
||||||
|
|
||||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||||
|
@ -49,12 +51,14 @@ Rake::RDocTask.new { |rdoc|
|
||||||
rdoc.title = "Action Pack -- On rails from request to response"
|
rdoc.title = "Action Pack -- On rails from request to response"
|
||||||
rdoc.options << '--line-numbers' << '--inline-source'
|
rdoc.options << '--line-numbers' << '--inline-source'
|
||||||
rdoc.options << '--charset' << 'utf-8'
|
rdoc.options << '--charset' << 'utf-8'
|
||||||
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
|
||||||
if ENV['DOC_FILES']
|
if ENV['DOC_FILES']
|
||||||
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
||||||
else
|
else
|
||||||
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
|
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
|
||||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
rdoc.rdoc_files.include(Dir['lib/**/*.rb'] -
|
||||||
|
Dir['lib/*/vendor/**/*.rb'])
|
||||||
|
rdoc.rdoc_files.exclude('lib/actionpack.rb')
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +80,7 @@ spec = Gem::Specification.new do |s|
|
||||||
s.has_rdoc = true
|
s.has_rdoc = true
|
||||||
s.requirements << 'none'
|
s.requirements << 'none'
|
||||||
|
|
||||||
s.add_dependency('activesupport', '= 2.1.0' + PKG_BUILD)
|
s.add_dependency('activesupport', '= 2.1.1' + PKG_BUILD)
|
||||||
|
|
||||||
s.require_path = 'lib'
|
s.require_path = 'lib'
|
||||||
s.autorequire = 'action_controller'
|
s.autorequire = 'action_controller'
|
||||||
|
@ -132,13 +136,13 @@ task :update_js => [ :update_scriptaculous ]
|
||||||
|
|
||||||
desc "Publish the API documentation"
|
desc "Publish the API documentation"
|
||||||
task :pgem => [:package] do
|
task :pgem => [:package] do
|
||||||
Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
Rake::SshFilePublisher.new("david@greed.loudthinking.com", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
||||||
`ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'`
|
`ssh david@greed.loudthinking.com '/u/sites/gems/gemupdate.sh'`
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Publish the API documentation"
|
desc "Publish the API documentation"
|
||||||
task :pdoc => [:rdoc] do
|
task :pdoc => [:rdoc] do
|
||||||
Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/ap", "doc").upload
|
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ap", "doc").upload
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Publish the release files to RubyForge."
|
desc "Publish the release files to RubyForge."
|
||||||
|
|
|
@ -97,7 +97,7 @@ module ActionController
|
||||||
value['controller'] = value['controller'].to_s
|
value['controller'] = value['controller'].to_s
|
||||||
if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/')
|
if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/')
|
||||||
new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)
|
new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)
|
||||||
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path)
|
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path) && @response.redirected_to.is_a?(Hash)
|
||||||
end
|
end
|
||||||
value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash
|
value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash
|
||||||
end
|
end
|
||||||
|
|
|
@ -398,47 +398,31 @@ module ActionController
|
||||||
# # The same, but shorter.
|
# # The same, but shorter.
|
||||||
# assert_select "ol>li", 4
|
# assert_select "ol>li", 4
|
||||||
def assert_select_rjs(*args, &block)
|
def assert_select_rjs(*args, &block)
|
||||||
rjs_type = nil
|
rjs_type = args.first.is_a?(Symbol) ? args.shift : nil
|
||||||
arg = args.shift
|
id = args.first.is_a?(String) ? args.shift : nil
|
||||||
|
|
||||||
# If the first argument is a symbol, it's the type of RJS statement we're looking
|
# If the first argument is a symbol, it's the type of RJS statement we're looking
|
||||||
# for (update, replace, insertion, etc). Otherwise, we're looking for just about
|
# for (update, replace, insertion, etc). Otherwise, we're looking for just about
|
||||||
# any RJS statement.
|
# any RJS statement.
|
||||||
if arg.is_a?(Symbol)
|
if rjs_type
|
||||||
rjs_type = arg
|
|
||||||
|
|
||||||
if rjs_type == :insert
|
if rjs_type == :insert
|
||||||
arg = args.shift
|
position = args.shift
|
||||||
insertion = "insert_#{arg}".to_sym
|
insertion = "insert_#{position}".to_sym
|
||||||
raise ArgumentError, "Unknown RJS insertion type #{arg}" unless RJS_STATEMENTS[insertion]
|
raise ArgumentError, "Unknown RJS insertion type #{position}" unless RJS_STATEMENTS[insertion]
|
||||||
statement = "(#{RJS_STATEMENTS[insertion]})"
|
statement = "(#{RJS_STATEMENTS[insertion]})"
|
||||||
else
|
else
|
||||||
raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type]
|
raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type]
|
||||||
statement = "(#{RJS_STATEMENTS[rjs_type]})"
|
statement = "(#{RJS_STATEMENTS[rjs_type]})"
|
||||||
end
|
end
|
||||||
arg = args.shift
|
|
||||||
else
|
else
|
||||||
statement = "#{RJS_STATEMENTS[:any]}"
|
statement = "#{RJS_STATEMENTS[:any]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Next argument we're looking for is the element identifier. If missing, we pick
|
# Next argument we're looking for is the element identifier. If missing, we pick
|
||||||
# any element.
|
# any element, otherwise we replace it in the statement.
|
||||||
if arg.is_a?(String)
|
pattern = Regexp.new(
|
||||||
id = Regexp.quote(arg)
|
id ? statement.gsub(RJS_ANY_ID, "\"#{id}\"") : statement
|
||||||
arg = args.shift
|
)
|
||||||
else
|
|
||||||
id = "[^\"]*"
|
|
||||||
end
|
|
||||||
|
|
||||||
pattern =
|
|
||||||
case rjs_type
|
|
||||||
when :chained_replace, :chained_replace_html
|
|
||||||
Regexp.new("\\$\\(\"#{id}\"\\)#{statement}\\(#{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
|
|
||||||
when :remove, :show, :hide, :toggle
|
|
||||||
Regexp.new("#{statement}\\(\"#{id}\"\\)")
|
|
||||||
else
|
|
||||||
Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Duplicate the body since the next step involves destroying it.
|
# Duplicate the body since the next step involves destroying it.
|
||||||
matches = nil
|
matches = nil
|
||||||
|
@ -447,7 +431,7 @@ module ActionController
|
||||||
matches = @response.body.match(pattern)
|
matches = @response.body.match(pattern)
|
||||||
else
|
else
|
||||||
@response.body.gsub(pattern) do |match|
|
@response.body.gsub(pattern) do |match|
|
||||||
html = unescape_rjs($2)
|
html = unescape_rjs(match)
|
||||||
matches ||= []
|
matches ||= []
|
||||||
matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
|
matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
|
||||||
""
|
""
|
||||||
|
@ -577,27 +561,23 @@ module ActionController
|
||||||
|
|
||||||
protected
|
protected
|
||||||
unless const_defined?(:RJS_STATEMENTS)
|
unless const_defined?(:RJS_STATEMENTS)
|
||||||
|
RJS_PATTERN_HTML = "\"((\\\\\"|[^\"])*)\""
|
||||||
|
RJS_ANY_ID = "\"([^\"])*\""
|
||||||
RJS_STATEMENTS = {
|
RJS_STATEMENTS = {
|
||||||
:replace => /Element\.replace/,
|
:chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)",
|
||||||
:replace_html => /Element\.update/,
|
:chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)",
|
||||||
:chained_replace => /\.replace/,
|
:replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
|
||||||
:chained_replace_html => /\.update/,
|
:replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)"
|
||||||
:remove => /Element\.remove/,
|
|
||||||
:show => /Element\.show/,
|
|
||||||
:hide => /Element\.hide/,
|
|
||||||
:toggle => /Element\.toggle/
|
|
||||||
}
|
}
|
||||||
RJS_INSERTIONS = [:top, :bottom, :before, :after]
|
[:remove, :show, :hide, :toggle].each do |action|
|
||||||
RJS_INSERTIONS.each do |insertion|
|
RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)"
|
||||||
RJS_STATEMENTS["insert_#{insertion}".to_sym] = Regexp.new(Regexp.quote("new Insertion.#{insertion.to_s.camelize}"))
|
|
||||||
end
|
end
|
||||||
|
RJS_INSERTIONS = ["top", "bottom", "before", "after"]
|
||||||
|
RJS_INSERTIONS.each do |insertion|
|
||||||
|
RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)"
|
||||||
|
end
|
||||||
|
RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)"
|
||||||
RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
|
RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
|
||||||
RJS_STATEMENTS[:insert_html] = Regexp.new(RJS_INSERTIONS.collect do |insertion|
|
|
||||||
Regexp.quote("new Insertion.#{insertion.to_s.camelize}")
|
|
||||||
end.join('|'))
|
|
||||||
RJS_PATTERN_HTML = /"((\\"|[^"])*)"/
|
|
||||||
RJS_PATTERN_EVERYTHING = Regexp.new("#{RJS_STATEMENTS[:any]}\\(\"([^\"]*)\", #{RJS_PATTERN_HTML}\\)",
|
|
||||||
Regexp::MULTILINE)
|
|
||||||
RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
|
RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -611,8 +591,8 @@ module ActionController
|
||||||
root = HTML::Node.new(nil)
|
root = HTML::Node.new(nil)
|
||||||
|
|
||||||
while true
|
while true
|
||||||
next if body.sub!(RJS_PATTERN_EVERYTHING) do |match|
|
next if body.sub!(RJS_STATEMENTS[:any]) do |match|
|
||||||
html = unescape_rjs($3)
|
html = unescape_rjs(match)
|
||||||
matches = HTML::Document.new(html).root.children.select { |n| n.tag? }
|
matches = HTML::Document.new(html).root.children.select { |n| n.tag? }
|
||||||
root.children.concat matches
|
root.children.concat matches
|
||||||
""
|
""
|
||||||
|
|
|
@ -613,8 +613,9 @@ module ActionController #:nodoc:
|
||||||
#
|
#
|
||||||
# This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>
|
# This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>
|
||||||
# would have slashed-off the path components after the changed action.
|
# would have slashed-off the path components after the changed action.
|
||||||
def url_for(options = nil) #:doc:
|
def url_for(options = {})
|
||||||
case options || {}
|
options ||= {}
|
||||||
|
case options
|
||||||
when String
|
when String
|
||||||
options
|
options
|
||||||
when Hash
|
when Hash
|
||||||
|
@ -743,6 +744,9 @@ module ActionController #:nodoc:
|
||||||
# # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
|
# # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
|
||||||
# render :template => "weblog/show"
|
# render :template => "weblog/show"
|
||||||
#
|
#
|
||||||
|
# # Renders the template with a local variable
|
||||||
|
# render :template => "weblog/show", :locals => {:customer => Customer.new}
|
||||||
|
#
|
||||||
# === Rendering a file
|
# === Rendering a file
|
||||||
#
|
#
|
||||||
# File rendering works just like action rendering except that it takes a filesystem path. By default, the path
|
# File rendering works just like action rendering except that it takes a filesystem path. By default, the path
|
||||||
|
@ -865,7 +869,7 @@ module ActionController #:nodoc:
|
||||||
render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
|
render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
|
||||||
|
|
||||||
elsif template = options[:template]
|
elsif template = options[:template]
|
||||||
render_for_file(template, options[:status], true)
|
render_for_file(template, options[:status], true, options[:locals] || {})
|
||||||
|
|
||||||
elsif inline = options[:inline]
|
elsif inline = options[:inline]
|
||||||
add_variables_to_assigns
|
add_variables_to_assigns
|
||||||
|
@ -1147,7 +1151,7 @@ module ActionController #:nodoc:
|
||||||
|
|
||||||
def log_processing
|
def log_processing
|
||||||
if logger && logger.info?
|
if logger && logger.info?
|
||||||
logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
|
logger.info "\n\nProcessing #{self.class.name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
|
||||||
logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
|
logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
|
||||||
logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
|
logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -135,7 +135,7 @@ module ActionController
|
||||||
# be reloaded on the next request without restarting the server.
|
# be reloaded on the next request without restarting the server.
|
||||||
def cleanup_application
|
def cleanup_application
|
||||||
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
|
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
|
||||||
Dependencies.clear
|
ActiveSupport::Dependencies.clear
|
||||||
ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
|
ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,200 @@ module ActionController #:nodoc:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
|
||||||
|
def append_filter_to_chain(filters, filter_type, &block)
|
||||||
|
pos = find_filter_append_position(filters, filter_type)
|
||||||
|
update_filter_chain(filters, filter_type, pos, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepend_filter_to_chain(filters, filter_type, &block)
|
||||||
|
pos = find_filter_prepend_position(filters, filter_type)
|
||||||
|
update_filter_chain(filters, filter_type, pos, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_filters(filters, filter_type, &block)
|
||||||
|
filters, conditions = extract_options(filters, &block)
|
||||||
|
filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
|
||||||
|
filters
|
||||||
|
end
|
||||||
|
|
||||||
|
def skip_filter_in_chain(*filters, &test)
|
||||||
|
filters, conditions = extract_options(filters)
|
||||||
|
filters.each do |filter|
|
||||||
|
if callback = find(filter) then delete(callback) end
|
||||||
|
end if conditions.empty?
|
||||||
|
update_filter_in_chain(filters, :skip => conditions, &test)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def update_filter_chain(filters, filter_type, pos, &block)
|
||||||
|
new_filters = create_filters(filters, filter_type, &block)
|
||||||
|
insert(pos, new_filters).flatten!
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_filter_append_position(filters, filter_type)
|
||||||
|
# appending an after filter puts it at the end of the call chain
|
||||||
|
# before and around filters go before the first after filter in the chain
|
||||||
|
unless filter_type == :after
|
||||||
|
each_with_index do |f,i|
|
||||||
|
return i if f.after?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_filter_prepend_position(filters, filter_type)
|
||||||
|
# prepending a before or around filter puts it at the front of the call chain
|
||||||
|
# after filters go before the first after filter in the chain
|
||||||
|
if filter_type == :after
|
||||||
|
each_with_index do |f,i|
|
||||||
|
return i if f.after?
|
||||||
|
end
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_or_create_filter(filter, filter_type, options = {})
|
||||||
|
update_filter_in_chain([filter], options)
|
||||||
|
|
||||||
|
if found_filter = find(filter) { |f| f.type == filter_type }
|
||||||
|
found_filter
|
||||||
|
else
|
||||||
|
filter_kind = case
|
||||||
|
when filter.respond_to?(:before) && filter_type == :before
|
||||||
|
:before
|
||||||
|
when filter.respond_to?(:after) && filter_type == :after
|
||||||
|
:after
|
||||||
|
else
|
||||||
|
:filter
|
||||||
|
end
|
||||||
|
|
||||||
|
case filter_type
|
||||||
|
when :before
|
||||||
|
BeforeFilter.new(filter_kind, filter, options)
|
||||||
|
when :after
|
||||||
|
AfterFilter.new(filter_kind, filter, options)
|
||||||
|
else
|
||||||
|
AroundFilter.new(filter_kind, filter, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_filter_in_chain(filters, options, &test)
|
||||||
|
filters.map! { |f| block_given? ? find(f, &test) : find(f) }
|
||||||
|
filters.compact!
|
||||||
|
|
||||||
|
map! do |filter|
|
||||||
|
if filters.include?(filter)
|
||||||
|
new_filter = filter.dup
|
||||||
|
new_filter.options.merge!(options)
|
||||||
|
new_filter
|
||||||
|
else
|
||||||
|
filter
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
|
||||||
|
def before?
|
||||||
|
self.class == BeforeFilter
|
||||||
|
end
|
||||||
|
|
||||||
|
def after?
|
||||||
|
self.class == AfterFilter
|
||||||
|
end
|
||||||
|
|
||||||
|
def around?
|
||||||
|
self.class == AroundFilter
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def should_not_skip?(controller)
|
||||||
|
if options[:skip]
|
||||||
|
!included_in_action?(controller, options[:skip])
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def included_in_action?(controller, options)
|
||||||
|
if options[:only]
|
||||||
|
Array(options[:only]).map(&:to_s).include?(controller.action_name)
|
||||||
|
elsif options[:except]
|
||||||
|
!Array(options[:except]).map(&:to_s).include?(controller.action_name)
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_run_callback?(controller)
|
||||||
|
should_not_skip?(controller) && included_in_action?(controller, options) && super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AroundFilter < Filter #:nodoc:
|
||||||
|
def type
|
||||||
|
:around
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(controller, &block)
|
||||||
|
if should_run_callback?(controller)
|
||||||
|
method = filter_responds_to_before_and_after? ? around_proc : self.method
|
||||||
|
|
||||||
|
# For around_filter do |controller, action|
|
||||||
|
if method.is_a?(Proc) && method.arity == 2
|
||||||
|
evaluate_method(method, controller, block)
|
||||||
|
else
|
||||||
|
evaluate_method(method, controller, &block)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
block.call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def filter_responds_to_before_and_after?
|
||||||
|
method.respond_to?(:before) && method.respond_to?(:after)
|
||||||
|
end
|
||||||
|
|
||||||
|
def around_proc
|
||||||
|
Proc.new do |controller, action|
|
||||||
|
method.before(controller)
|
||||||
|
|
||||||
|
if controller.send!(:performed?)
|
||||||
|
controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
action.call
|
||||||
|
ensure
|
||||||
|
method.after(controller)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class BeforeFilter < Filter #:nodoc:
|
||||||
|
def type
|
||||||
|
:before
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(controller, &block)
|
||||||
|
super
|
||||||
|
if controller.send!(:performed?)
|
||||||
|
controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AfterFilter < Filter #:nodoc:
|
||||||
|
def type
|
||||||
|
:after
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
|
# Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
|
||||||
# authentication, caching, or auditing before the intended action is performed. Or to do localization or output
|
# authentication, caching, or auditing before the intended action is performed. Or to do localization or output
|
||||||
# compression after the action has been performed. Filters have access to the request, response, and all the instance
|
# compression after the action has been performed. Filters have access to the request, response, and all the instance
|
||||||
|
@ -245,201 +439,6 @@ module ActionController #:nodoc:
|
||||||
# filter and controller action will not be run. If +before+ renders or redirects,
|
# filter and controller action will not be run. If +before+ renders or redirects,
|
||||||
# the second half of +around+ and will still run but +after+ and the
|
# the second half of +around+ and will still run but +after+ and the
|
||||||
# action will not. If +around+ fails to yield, +after+ will not be run.
|
# action will not. If +around+ fails to yield, +after+ will not be run.
|
||||||
|
|
||||||
class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
|
|
||||||
def append_filter_to_chain(filters, filter_type, &block)
|
|
||||||
pos = find_filter_append_position(filters, filter_type)
|
|
||||||
update_filter_chain(filters, filter_type, pos, &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def prepend_filter_to_chain(filters, filter_type, &block)
|
|
||||||
pos = find_filter_prepend_position(filters, filter_type)
|
|
||||||
update_filter_chain(filters, filter_type, pos, &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_filters(filters, filter_type, &block)
|
|
||||||
filters, conditions = extract_options(filters, &block)
|
|
||||||
filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
|
|
||||||
filters
|
|
||||||
end
|
|
||||||
|
|
||||||
def skip_filter_in_chain(*filters, &test)
|
|
||||||
filters, conditions = extract_options(filters)
|
|
||||||
filters.each do |filter|
|
|
||||||
if callback = find(filter) then delete(callback) end
|
|
||||||
end if conditions.empty?
|
|
||||||
update_filter_in_chain(filters, :skip => conditions, &test)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def update_filter_chain(filters, filter_type, pos, &block)
|
|
||||||
new_filters = create_filters(filters, filter_type, &block)
|
|
||||||
insert(pos, new_filters).flatten!
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_filter_append_position(filters, filter_type)
|
|
||||||
# appending an after filter puts it at the end of the call chain
|
|
||||||
# before and around filters go before the first after filter in the chain
|
|
||||||
unless filter_type == :after
|
|
||||||
each_with_index do |f,i|
|
|
||||||
return i if f.after?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_filter_prepend_position(filters, filter_type)
|
|
||||||
# prepending a before or around filter puts it at the front of the call chain
|
|
||||||
# after filters go before the first after filter in the chain
|
|
||||||
if filter_type == :after
|
|
||||||
each_with_index do |f,i|
|
|
||||||
return i if f.after?
|
|
||||||
end
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_or_create_filter(filter, filter_type, options = {})
|
|
||||||
update_filter_in_chain([filter], options)
|
|
||||||
|
|
||||||
if found_filter = find(filter) { |f| f.type == filter_type }
|
|
||||||
found_filter
|
|
||||||
else
|
|
||||||
filter_kind = case
|
|
||||||
when filter.respond_to?(:before) && filter_type == :before
|
|
||||||
:before
|
|
||||||
when filter.respond_to?(:after) && filter_type == :after
|
|
||||||
:after
|
|
||||||
else
|
|
||||||
:filter
|
|
||||||
end
|
|
||||||
|
|
||||||
case filter_type
|
|
||||||
when :before
|
|
||||||
BeforeFilter.new(filter_kind, filter, options)
|
|
||||||
when :after
|
|
||||||
AfterFilter.new(filter_kind, filter, options)
|
|
||||||
else
|
|
||||||
AroundFilter.new(filter_kind, filter, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_filter_in_chain(filters, options, &test)
|
|
||||||
filters.map! { |f| block_given? ? find(f, &test) : find(f) }
|
|
||||||
filters.compact!
|
|
||||||
|
|
||||||
map! do |filter|
|
|
||||||
if filters.include?(filter)
|
|
||||||
new_filter = filter.dup
|
|
||||||
new_filter.options.merge!(options)
|
|
||||||
new_filter
|
|
||||||
else
|
|
||||||
filter
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
|
|
||||||
def before?
|
|
||||||
self.class == BeforeFilter
|
|
||||||
end
|
|
||||||
|
|
||||||
def after?
|
|
||||||
self.class == AfterFilter
|
|
||||||
end
|
|
||||||
|
|
||||||
def around?
|
|
||||||
self.class == AroundFilter
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def should_not_skip?(controller)
|
|
||||||
if options[:skip]
|
|
||||||
!included_in_action?(controller, options[:skip])
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def included_in_action?(controller, options)
|
|
||||||
if options[:only]
|
|
||||||
Array(options[:only]).map(&:to_s).include?(controller.action_name)
|
|
||||||
elsif options[:except]
|
|
||||||
!Array(options[:except]).map(&:to_s).include?(controller.action_name)
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def should_run_callback?(controller)
|
|
||||||
should_not_skip?(controller) && included_in_action?(controller, options) && super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class AroundFilter < Filter #:nodoc:
|
|
||||||
def type
|
|
||||||
:around
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(controller, &block)
|
|
||||||
if should_run_callback?(controller)
|
|
||||||
method = filter_responds_to_before_and_after? ? around_proc : self.method
|
|
||||||
|
|
||||||
# For around_filter do |controller, action|
|
|
||||||
if method.is_a?(Proc) && method.arity == 2
|
|
||||||
evaluate_method(method, controller, block)
|
|
||||||
else
|
|
||||||
evaluate_method(method, controller, &block)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
block.call
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def filter_responds_to_before_and_after?
|
|
||||||
method.respond_to?(:before) && method.respond_to?(:after)
|
|
||||||
end
|
|
||||||
|
|
||||||
def around_proc
|
|
||||||
Proc.new do |controller, action|
|
|
||||||
method.before(controller)
|
|
||||||
|
|
||||||
if controller.send!(:performed?)
|
|
||||||
controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
action.call
|
|
||||||
ensure
|
|
||||||
method.after(controller)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class BeforeFilter < Filter #:nodoc:
|
|
||||||
def type
|
|
||||||
:before
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(controller, &block)
|
|
||||||
super
|
|
||||||
if controller.send!(:performed?)
|
|
||||||
controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class AfterFilter < Filter #:nodoc:
|
|
||||||
def type
|
|
||||||
:after
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
# The passed <tt>filters</tt> will be appended to the filter_chain and
|
# The passed <tt>filters</tt> will be appended to the filter_chain and
|
||||||
# will execute before the action on this controller is performed.
|
# will execute before the action on this controller is performed.
|
||||||
|
|
|
@ -48,6 +48,9 @@ module ActionController
|
||||||
#
|
#
|
||||||
# # calls post_url(post)
|
# # calls post_url(post)
|
||||||
# polymorphic_url(post) # => "http://example.com/posts/1"
|
# polymorphic_url(post) # => "http://example.com/posts/1"
|
||||||
|
# polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
|
||||||
|
# polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
|
||||||
|
# polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
|
||||||
#
|
#
|
||||||
# ==== Options
|
# ==== Options
|
||||||
#
|
#
|
||||||
|
@ -83,8 +86,6 @@ module ActionController
|
||||||
else [ record_or_hash_or_array ]
|
else [ record_or_hash_or_array ]
|
||||||
end
|
end
|
||||||
|
|
||||||
args << format if format
|
|
||||||
|
|
||||||
inflection =
|
inflection =
|
||||||
case
|
case
|
||||||
when options[:action].to_s == "new"
|
when options[:action].to_s == "new"
|
||||||
|
@ -97,6 +98,9 @@ module ActionController
|
||||||
:singular
|
:singular
|
||||||
end
|
end
|
||||||
|
|
||||||
|
args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
|
||||||
|
args << format if format
|
||||||
|
|
||||||
named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
|
named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
|
||||||
send!(named_route, *args)
|
send!(named_route, *args)
|
||||||
end
|
end
|
||||||
|
@ -136,11 +140,19 @@ module ActionController
|
||||||
else
|
else
|
||||||
record = records.pop
|
record = records.pop
|
||||||
route = records.inject("") do |string, parent|
|
route = records.inject("") do |string, parent|
|
||||||
|
if parent.is_a?(Symbol) || parent.is_a?(String)
|
||||||
|
string << "#{parent}_"
|
||||||
|
else
|
||||||
string << "#{RecordIdentifier.send!("singular_class_name", parent)}_"
|
string << "#{RecordIdentifier.send!("singular_class_name", parent)}_"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if record.is_a?(Symbol) || record.is_a?(String)
|
||||||
|
route << "#{record}_"
|
||||||
|
else
|
||||||
route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_"
|
route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_"
|
||||||
|
end
|
||||||
|
|
||||||
action_prefix(options) + namespace + route + routing_type(options).to_s
|
action_prefix(options) + namespace + route + routing_type(options).to_s
|
||||||
end
|
end
|
||||||
|
@ -163,16 +175,17 @@ module ActionController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Remove the first symbols from the array and return the url prefix
|
||||||
|
# implied by those symbols.
|
||||||
def extract_namespace(record_or_hash_or_array)
|
def extract_namespace(record_or_hash_or_array)
|
||||||
returning "" do |namespace|
|
return "" unless record_or_hash_or_array.is_a?(Array)
|
||||||
if record_or_hash_or_array.is_a?(Array)
|
|
||||||
record_or_hash_or_array.delete_if do |record_or_namespace|
|
namespace_keys = []
|
||||||
if record_or_namespace.is_a?(String) || record_or_namespace.is_a?(Symbol)
|
while (key = record_or_hash_or_array.first) && key.is_a?(String) || key.is_a?(Symbol)
|
||||||
namespace << "#{record_or_namespace}_"
|
namespace_keys << record_or_hash_or_array.shift
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
namespace_keys.map {|k| "#{k}_"}.join
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,18 +31,21 @@ module ActionController
|
||||||
module RecordIdentifier
|
module RecordIdentifier
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
|
JOIN = '_'.freeze
|
||||||
|
NEW = 'new'.freeze
|
||||||
|
|
||||||
# Returns plural/singular for a record or class. Example:
|
# Returns plural/singular for a record or class. Example:
|
||||||
#
|
#
|
||||||
# partial_path(post) # => "posts/post"
|
# partial_path(post) # => "posts/post"
|
||||||
# partial_path(Person) # => "people/person"
|
# partial_path(Person) # => "people/person"
|
||||||
# partial_path(Person, "admin/games") # => "admin/people/person"
|
# partial_path(Person, "admin/games") # => "admin/people/person"
|
||||||
def partial_path(record_or_class, controller_path = nil)
|
def partial_path(record_or_class, controller_path = nil)
|
||||||
klass = class_from_record_or_class(record_or_class)
|
name = model_name_from_record_or_class(record_or_class)
|
||||||
|
|
||||||
if controller_path && controller_path.include?("/")
|
if controller_path && controller_path.include?("/")
|
||||||
"#{File.dirname(controller_path)}/#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
|
"#{File.dirname(controller_path)}/#{name.partial_path}"
|
||||||
else
|
else
|
||||||
"#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
|
name.partial_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -56,21 +59,25 @@ module ActionController
|
||||||
# dom_class(post, :edit) # => "edit_post"
|
# dom_class(post, :edit) # => "edit_post"
|
||||||
# dom_class(Person, :edit) # => "edit_person"
|
# dom_class(Person, :edit) # => "edit_person"
|
||||||
def dom_class(record_or_class, prefix = nil)
|
def dom_class(record_or_class, prefix = nil)
|
||||||
[ prefix, singular_class_name(record_or_class) ].compact * '_'
|
singular = singular_class_name(record_or_class)
|
||||||
|
prefix ? "#{prefix}#{JOIN}#{singular}" : singular
|
||||||
end
|
end
|
||||||
|
|
||||||
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
|
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
|
||||||
# If no id is found, prefix with "new_" instead. Examples:
|
# If no id is found, prefix with "new_" instead. Examples:
|
||||||
#
|
#
|
||||||
# dom_id(Post.new(:id => 45)) # => "post_45"
|
# dom_id(Post.find(45)) # => "post_45"
|
||||||
# dom_id(Post.new) # => "new_post"
|
# dom_id(Post.new) # => "new_post"
|
||||||
#
|
#
|
||||||
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
|
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
|
||||||
#
|
#
|
||||||
# dom_id(Post.new(:id => 45), :edit) # => "edit_post_45"
|
# dom_id(Post.find(45), :edit) # => "edit_post_45"
|
||||||
def dom_id(record, prefix = nil)
|
def dom_id(record, prefix = nil)
|
||||||
prefix ||= 'new' unless record.id
|
if record_id = record.id
|
||||||
[ prefix, singular_class_name(record), record.id ].compact * '_'
|
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
|
||||||
|
else
|
||||||
|
dom_class(record, prefix || NEW)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the plural class name of a record or class. Examples:
|
# Returns the plural class name of a record or class. Examples:
|
||||||
|
@ -78,7 +85,7 @@ module ActionController
|
||||||
# plural_class_name(post) # => "posts"
|
# plural_class_name(post) # => "posts"
|
||||||
# plural_class_name(Highrise::Person) # => "highrise_people"
|
# plural_class_name(Highrise::Person) # => "highrise_people"
|
||||||
def plural_class_name(record_or_class)
|
def plural_class_name(record_or_class)
|
||||||
singular_class_name(record_or_class).pluralize
|
model_name_from_record_or_class(record_or_class).plural
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the singular class name of a record or class. Examples:
|
# Returns the singular class name of a record or class. Examples:
|
||||||
|
@ -86,12 +93,12 @@ module ActionController
|
||||||
# singular_class_name(post) # => "post"
|
# singular_class_name(post) # => "post"
|
||||||
# singular_class_name(Highrise::Person) # => "highrise_person"
|
# singular_class_name(Highrise::Person) # => "highrise_person"
|
||||||
def singular_class_name(record_or_class)
|
def singular_class_name(record_or_class)
|
||||||
class_from_record_or_class(record_or_class).name.underscore.tr('/', '_')
|
model_name_from_record_or_class(record_or_class).singular
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def class_from_record_or_class(record_or_class)
|
def model_name_from_record_or_class(record_or_class)
|
||||||
record_or_class.is_a?(Class) ? record_or_class : record_or_class.class
|
(record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -134,14 +134,17 @@ module ActionController
|
||||||
# REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma-
|
# REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma-
|
||||||
# delimited list in the case of multiple chained proxies; the last
|
# delimited list in the case of multiple chained proxies; the last
|
||||||
# address which is not trusted is the originating IP.
|
# address which is not trusted is the originating IP.
|
||||||
|
|
||||||
def remote_ip
|
def remote_ip
|
||||||
if TRUSTED_PROXIES !~ @env['REMOTE_ADDR']
|
remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].split(',').collect(&:strip)
|
||||||
return @env['REMOTE_ADDR']
|
|
||||||
|
unless remote_addr_list.blank?
|
||||||
|
not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES}
|
||||||
|
return not_trusted_addrs.first unless not_trusted_addrs.empty?
|
||||||
end
|
end
|
||||||
|
remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',')
|
||||||
|
|
||||||
if @env.include? 'HTTP_CLIENT_IP'
|
if @env.include? 'HTTP_CLIENT_IP'
|
||||||
if @env.include? 'HTTP_X_FORWARDED_FOR'
|
if remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP'])
|
||||||
# We don't know which came from the proxy, and which from the user
|
# We don't know which came from the proxy, and which from the user
|
||||||
raise ActionControllerError.new(<<EOM)
|
raise ActionControllerError.new(<<EOM)
|
||||||
IP spoofing attack?!
|
IP spoofing attack?!
|
||||||
|
@ -149,11 +152,11 @@ HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
|
||||||
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
|
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
|
||||||
EOM
|
EOM
|
||||||
end
|
end
|
||||||
|
|
||||||
return @env['HTTP_CLIENT_IP']
|
return @env['HTTP_CLIENT_IP']
|
||||||
end
|
end
|
||||||
|
|
||||||
if @env.include? 'HTTP_X_FORWARDED_FOR' then
|
if remote_ips
|
||||||
remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',')
|
|
||||||
while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip
|
while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip
|
||||||
remote_ips.pop
|
remote_ips.pop
|
||||||
end
|
end
|
||||||
|
|
|
@ -88,6 +88,10 @@ module ActionController
|
||||||
#
|
#
|
||||||
# map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' }
|
# map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' }
|
||||||
#
|
#
|
||||||
|
# Note: The default routes, as provided by the Rails generator, make all actions in every
|
||||||
|
# controller accessible via GET requests. You should consider removing them or commenting
|
||||||
|
# them out if you're using named routes and resources.
|
||||||
|
#
|
||||||
# == Named routes
|
# == Named routes
|
||||||
#
|
#
|
||||||
# Routes can be named with the syntax <tt>map.name_of_route options</tt>,
|
# Routes can be named with the syntax <tt>map.name_of_route options</tt>,
|
||||||
|
@ -369,7 +373,7 @@ module ActionController
|
||||||
|
|
||||||
Routes = RouteSet.new
|
Routes = RouteSet.new
|
||||||
|
|
||||||
::Inflector.module_eval do
|
ActiveSupport::Inflector.module_eval do
|
||||||
# Ensures that routes are reloaded when Rails inflections are updated.
|
# Ensures that routes are reloaded when Rails inflections are updated.
|
||||||
def inflections_with_route_reloading(&block)
|
def inflections_with_route_reloading(&block)
|
||||||
returning(inflections_without_route_reloading(&block)) {
|
returning(inflections_without_route_reloading(&block)) {
|
||||||
|
|
|
@ -67,10 +67,9 @@ module ActionController
|
||||||
options = options.dup
|
options = options.dup
|
||||||
|
|
||||||
if options[:namespace]
|
if options[:namespace]
|
||||||
options[:controller] = "#{options[:path_prefix]}/#{options[:controller]}"
|
options[:controller] = "#{options.delete(:namespace).sub(/\/$/, '')}/#{options[:controller]}"
|
||||||
options.delete(:path_prefix)
|
options.delete(:path_prefix)
|
||||||
options.delete(:name_prefix)
|
options.delete(:name_prefix)
|
||||||
options.delete(:namespace)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
requirements = (options.delete(:requirements) || {}).dup
|
requirements = (options.delete(:requirements) || {}).dup
|
||||||
|
|
|
@ -248,7 +248,7 @@ module ActionController
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_value
|
def extract_value
|
||||||
"#{local_name} = hash[:#{key}] && hash[:#{key}].collect { |path_component| CGI.escape(path_component.to_param, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
|
"#{local_name} = hash[:#{key}] && Array(hash[:#{key}]).collect { |path_component| CGI.escape(path_component.to_param, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def default
|
def default
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<html>
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<title>Action Controller: Exception caught</title>
|
<title>Action Controller: Exception caught</title>
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -171,7 +171,7 @@ module ActionController #:nodoc:
|
||||||
|
|
||||||
# Was the response successful?
|
# Was the response successful?
|
||||||
def success?
|
def success?
|
||||||
response_code == 200
|
(200..299).include?(response_code)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Was the URL not found?
|
# Was the URL not found?
|
||||||
|
@ -333,7 +333,7 @@ module ActionController #:nodoc:
|
||||||
attr_reader :original_filename
|
attr_reader :original_filename
|
||||||
|
|
||||||
# The content type of the "uploaded" file
|
# The content type of the "uploaded" file
|
||||||
attr_reader :content_type
|
attr_accessor :content_type
|
||||||
|
|
||||||
def initialize(path, content_type = Mime::TEXT, binary = false)
|
def initialize(path, content_type = Mime::TEXT, binary = false)
|
||||||
raise "#{path} file does not exist" unless File.exist?(path)
|
raise "#{path} file does not exist" unless File.exist?(path)
|
||||||
|
@ -413,6 +413,8 @@ module ActionController #:nodoc:
|
||||||
get(@response.redirected_to.delete(:action), @response.redirected_to.stringify_keys)
|
get(@response.redirected_to.delete(:action), @response.redirected_to.stringify_keys)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
deprecate :follow_redirect => "If you wish to follow redirects, you should use integration tests"
|
||||||
|
|
||||||
def assigns(key = nil)
|
def assigns(key = nil)
|
||||||
if key.nil?
|
if key.nil?
|
||||||
@response.template.assigns
|
@response.template.assigns
|
||||||
|
|
|
@ -17,7 +17,7 @@ module HTML #:nodoc:
|
||||||
@root = Node.new(nil)
|
@root = Node.new(nil)
|
||||||
node_stack = [ @root ]
|
node_stack = [ @root ]
|
||||||
while token = tokenizer.next
|
while token = tokenizer.next
|
||||||
node = Node.parse(node_stack.last, tokenizer.line, tokenizer.position, token)
|
node = Node.parse(node_stack.last, tokenizer.line, tokenizer.position, token, strict)
|
||||||
|
|
||||||
node_stack.last.children << node unless node.tag? && node.closing == :close
|
node_stack.last.children << node unless node.tag? && node.closing == :close
|
||||||
if node.tag?
|
if node.tag?
|
||||||
|
|
|
@ -116,7 +116,7 @@ module ActionController #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
def apply_redirect_to(redirect_to_option) # :nodoc:
|
def apply_redirect_to(redirect_to_option) # :nodoc:
|
||||||
redirect_to_option.is_a?(Symbol) ? self.send!(redirect_to_option) : redirect_to_option
|
(redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.send!(redirect_to_option) : redirect_to_option
|
||||||
end
|
end
|
||||||
|
|
||||||
def apply_remaining_actions(options) # :nodoc:
|
def apply_remaining_actions(options) # :nodoc:
|
||||||
|
|
|
@ -2,7 +2,7 @@ module ActionPack #:nodoc:
|
||||||
module VERSION #:nodoc:
|
module VERSION #:nodoc:
|
||||||
MAJOR = 2
|
MAJOR = 2
|
||||||
MINOR = 1
|
MINOR = 1
|
||||||
TINY = 0
|
TINY = 1
|
||||||
|
|
||||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||||
end
|
end
|
||||||
|
|
|
@ -181,6 +181,9 @@ module ActionView #:nodoc:
|
||||||
|
|
||||||
@@erb_variable = '_erbout'
|
@@erb_variable = '_erbout'
|
||||||
cattr_accessor :erb_variable
|
cattr_accessor :erb_variable
|
||||||
|
class << self
|
||||||
|
deprecate :erb_variable= => 'The erb variable will no longer be configurable. Use the concat helper method instead of appending to it directly.'
|
||||||
|
end
|
||||||
|
|
||||||
attr_internal :request
|
attr_internal :request
|
||||||
|
|
||||||
|
@ -253,6 +256,7 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
||||||
elsif options == :update
|
elsif options == :update
|
||||||
update_page(&block)
|
update_page(&block)
|
||||||
elsif options.is_a?(Hash)
|
elsif options.is_a?(Hash)
|
||||||
|
use_full_path = options[:use_full_path]
|
||||||
options = options.reverse_merge(:locals => {}, :use_full_path => true)
|
options = options.reverse_merge(:locals => {}, :use_full_path => true)
|
||||||
|
|
||||||
if partial_layout = options.delete(:layout)
|
if partial_layout = options.delete(:layout)
|
||||||
|
@ -266,7 +270,7 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elsif options[:file]
|
elsif options[:file]
|
||||||
render_file(options[:file], options[:use_full_path], options[:locals])
|
render_file(options[:file], use_full_path || false, options[:locals])
|
||||||
elsif options[:partial] && options[:collection]
|
elsif options[:partial] && options[:collection]
|
||||||
render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals])
|
render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals])
|
||||||
elsif options[:partial]
|
elsif options[:partial]
|
||||||
|
|
|
@ -485,9 +485,14 @@ module ActionView
|
||||||
source = "#{@controller.request.relative_url_root}#{source}"
|
source = "#{@controller.request.relative_url_root}#{source}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
source = rewrite_asset_path(source)
|
|
||||||
|
|
||||||
if include_host
|
rewrite_asset_path(source)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
source = ActionView::Base.computed_public_paths[cache_key]
|
||||||
|
|
||||||
|
if include_host && source !~ %r{^[-a-z]+://}
|
||||||
host = compute_asset_host(source)
|
host = compute_asset_host(source)
|
||||||
|
|
||||||
if has_request && !host.blank? && host !~ %r{^[-a-z]+://}
|
if has_request && !host.blank? && host !~ %r{^[-a-z]+://}
|
||||||
|
@ -499,8 +504,6 @@ module ActionView
|
||||||
source
|
source
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Pick an asset host for this source. Returns +nil+ if no host is set,
|
# Pick an asset host for this source. Returns +nil+ if no host is set,
|
||||||
# the host if no wildcard is set, the host interpolated with the
|
# the host if no wildcard is set, the host interpolated with the
|
||||||
|
|
|
@ -696,15 +696,15 @@ module ActionView
|
||||||
|
|
||||||
class FormBuilder
|
class FormBuilder
|
||||||
def date_select(method, options = {}, html_options = {})
|
def date_select(method, options = {}, html_options = {})
|
||||||
@template.date_select(@object_name, method, options.merge(:object => @object))
|
@template.date_select(@object_name, method, options.merge(:object => @object), html_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def time_select(method, options = {}, html_options = {})
|
def time_select(method, options = {}, html_options = {})
|
||||||
@template.time_select(@object_name, method, options.merge(:object => @object))
|
@template.time_select(@object_name, method, options.merge(:object => @object), html_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def datetime_select(method, options = {}, html_options = {})
|
def datetime_select(method, options = {}, html_options = {})
|
||||||
@template.datetime_select(@object_name, method, options.merge(:object => @object))
|
@template.datetime_select(@object_name, method, options.merge(:object => @object), html_options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -601,7 +601,11 @@ module ActionView
|
||||||
end
|
end
|
||||||
|
|
||||||
def object
|
def object
|
||||||
@object || (@template_object.instance_variable_get("@#{@object_name}") rescue nil)
|
@object || @template_object.instance_variable_get("@#{@object_name}")
|
||||||
|
rescue NameError
|
||||||
|
# As @object_name may contain the nested syntax (item[subobject]) we
|
||||||
|
# need to fallback to nil.
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def value(object)
|
def value(object)
|
||||||
|
|
|
@ -304,7 +304,7 @@ module ActionView
|
||||||
#
|
#
|
||||||
# NOTE: Only the option tags are returned, you have to wrap this call in
|
# NOTE: Only the option tags are returned, you have to wrap this call in
|
||||||
# a regular HTML select tag.
|
# a regular HTML select tag.
|
||||||
def time_zone_options_for_select(selected = nil, priority_zones = nil, model = TimeZone)
|
def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
|
||||||
zone_options = ""
|
zone_options = ""
|
||||||
|
|
||||||
zones = model.all
|
zones = model.all
|
||||||
|
@ -417,7 +417,7 @@ module ActionView
|
||||||
value = value(object)
|
value = value(object)
|
||||||
content_tag("select",
|
content_tag("select",
|
||||||
add_options(
|
add_options(
|
||||||
time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || TimeZone),
|
time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone),
|
||||||
options, value
|
options, value
|
||||||
), html_options
|
), html_options
|
||||||
)
|
)
|
||||||
|
|
|
@ -129,7 +129,7 @@ module ActionView
|
||||||
# label_tag 'name', nil, :class => 'small_label'
|
# label_tag 'name', nil, :class => 'small_label'
|
||||||
# # => <label for="name" class="small_label">Name</label>
|
# # => <label for="name" class="small_label">Name</label>
|
||||||
def label_tag(name, text = nil, options = {})
|
def label_tag(name, text = nil, options = {})
|
||||||
content_tag :label, text || name.humanize, { "for" => name }.update(options.stringify_keys)
|
content_tag :label, text || name.to_s.humanize, { "for" => name }.update(options.stringify_keys)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
|
# Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
|
||||||
|
@ -348,11 +348,13 @@ module ActionView
|
||||||
options.stringify_keys!
|
options.stringify_keys!
|
||||||
|
|
||||||
if disable_with = options.delete("disable_with")
|
if disable_with = options.delete("disable_with")
|
||||||
|
disable_with = "this.value='#{disable_with}'"
|
||||||
|
disable_with << ";#{options.delete('onclick')}" if options['onclick']
|
||||||
|
|
||||||
options["onclick"] = [
|
options["onclick"] = [
|
||||||
"this.setAttribute('originalValue', this.value)",
|
"this.setAttribute('originalValue', this.value)",
|
||||||
"this.disabled=true",
|
"this.disabled=true",
|
||||||
"this.value='#{disable_with}'",
|
disable_with,
|
||||||
"#{options["onclick"]}",
|
|
||||||
"result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit())",
|
"result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit())",
|
||||||
"if (result == false) { this.value = this.getAttribute('originalValue'); this.disabled = false }",
|
"if (result == false) { this.value = this.getAttribute('originalValue'); this.disabled = false }",
|
||||||
"return result;",
|
"return result;",
|
||||||
|
|
|
@ -80,10 +80,9 @@ module ActionView
|
||||||
# return false;">Show me more</a>
|
# return false;">Show me more</a>
|
||||||
#
|
#
|
||||||
def link_to_function(name, *args, &block)
|
def link_to_function(name, *args, &block)
|
||||||
html_options = args.extract_options!
|
html_options = args.extract_options!.symbolize_keys
|
||||||
function = args[0] || ''
|
function = args[0] || ''
|
||||||
|
|
||||||
html_options.symbolize_keys!
|
|
||||||
function = update_page(&block) if block_given?
|
function = update_page(&block) if block_given?
|
||||||
content_tag(
|
content_tag(
|
||||||
"a", name,
|
"a", name,
|
||||||
|
@ -111,10 +110,9 @@ module ActionView
|
||||||
# page[:details].visual_effect :toggle_slide
|
# page[:details].visual_effect :toggle_slide
|
||||||
# end
|
# end
|
||||||
def button_to_function(name, *args, &block)
|
def button_to_function(name, *args, &block)
|
||||||
html_options = args.extract_options!
|
html_options = args.extract_options!.symbolize_keys
|
||||||
function = args[0] || ''
|
function = args[0] || ''
|
||||||
|
|
||||||
html_options.symbolize_keys!
|
|
||||||
function = update_page(&block) if block_given?
|
function = update_page(&block) if block_given?
|
||||||
tag(:input, html_options.merge({
|
tag(:input, html_options.merge({
|
||||||
:type => "button", :value => name,
|
:type => "button", :value => name,
|
||||||
|
@ -147,6 +145,8 @@ module ActionView
|
||||||
javascript << '</script>'
|
javascript << '</script>'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
deprecate :define_javascript_functions=>"use javascript_include_tag instead"
|
||||||
|
|
||||||
# Escape carrier returns and single and double quotes for JavaScript segments.
|
# Escape carrier returns and single and double quotes for JavaScript segments.
|
||||||
def escape_javascript(javascript)
|
def escape_javascript(javascript)
|
||||||
(javascript || '').gsub('\\','\0\0').gsub('</','<\/').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
|
(javascript || '').gsub('\\','\0\0').gsub('</','<\/').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
|
||||||
|
|
|
@ -111,7 +111,7 @@ module ActionView
|
||||||
(100..599).to_a)
|
(100..599).to_a)
|
||||||
AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,
|
AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,
|
||||||
:asynchronous, :method, :insertion, :position,
|
:asynchronous, :method, :insertion, :position,
|
||||||
:form, :with, :update, :script ]).merge(CALLBACKS)
|
:form, :with, :update, :script, :type ]).merge(CALLBACKS)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a link to a remote action defined by <tt>options[:url]</tt>
|
# Returns a link to a remote action defined by <tt>options[:url]</tt>
|
||||||
|
@ -603,7 +603,7 @@ module ActionView
|
||||||
# Example:
|
# Example:
|
||||||
#
|
#
|
||||||
# # Generates:
|
# # Generates:
|
||||||
# # new Insertion.Bottom("list", "<li>Some item</li>");
|
# # new Element.insert("list", { bottom: <li>Some item</li>" });
|
||||||
# # new Effect.Highlight("list");
|
# # new Effect.Highlight("list");
|
||||||
# # ["status-indicator", "cancel-link"].each(Element.hide);
|
# # ["status-indicator", "cancel-link"].each(Element.hide);
|
||||||
# update_page do |page|
|
# update_page do |page|
|
||||||
|
@ -736,16 +736,16 @@ module ActionView
|
||||||
#
|
#
|
||||||
# # Insert the rendered 'navigation' partial just before the DOM
|
# # Insert the rendered 'navigation' partial just before the DOM
|
||||||
# # element with ID 'content'.
|
# # element with ID 'content'.
|
||||||
# # Generates: new Insertion.Before("content", "-- Contents of 'navigation' partial --");
|
# # Generates: Element.insert("content", { before: "-- Contents of 'navigation' partial --" });
|
||||||
# page.insert_html :before, 'content', :partial => 'navigation'
|
# page.insert_html :before, 'content', :partial => 'navigation'
|
||||||
#
|
#
|
||||||
# # Add a list item to the bottom of the <ul> with ID 'list'.
|
# # Add a list item to the bottom of the <ul> with ID 'list'.
|
||||||
# # Generates: new Insertion.Bottom("list", "<li>Last item</li>");
|
# # Generates: Element.insert("list", { bottom: "<li>Last item</li>" });
|
||||||
# page.insert_html :bottom, 'list', '<li>Last item</li>'
|
# page.insert_html :bottom, 'list', '<li>Last item</li>'
|
||||||
#
|
#
|
||||||
def insert_html(position, id, *options_for_render)
|
def insert_html(position, id, *options_for_render)
|
||||||
insertion = position.to_s.camelize
|
content = javascript_object_for(render(*options_for_render))
|
||||||
call "new Insertion.#{insertion}", id, render(*options_for_render)
|
record "Element.insert(\"#{id}\", { #{position.to_s.downcase}: #{content} });"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Replaces the inner HTML of the DOM element with the given +id+.
|
# Replaces the inner HTML of the DOM element with the given +id+.
|
||||||
|
@ -1039,7 +1039,7 @@ module ActionView
|
||||||
|
|
||||||
js_options['asynchronous'] = options[:type] != :synchronous
|
js_options['asynchronous'] = options[:type] != :synchronous
|
||||||
js_options['method'] = method_option_to_s(options[:method]) if options[:method]
|
js_options['method'] = method_option_to_s(options[:method]) if options[:method]
|
||||||
js_options['insertion'] = "Insertion.#{options[:position].to_s.camelize}" if options[:position]
|
js_options['insertion'] = "'#{options[:position].to_s.downcase}'" if options[:position]
|
||||||
js_options['evalScripts'] = options[:script].nil? || options[:script]
|
js_options['evalScripts'] = options[:script].nil? || options[:script]
|
||||||
|
|
||||||
if options[:form]
|
if options[:form]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
require 'cgi'
|
require 'cgi'
|
||||||
require 'erb'
|
require 'erb'
|
||||||
|
require 'set'
|
||||||
|
|
||||||
module ActionView
|
module ActionView
|
||||||
module Helpers #:nodoc:
|
module Helpers #:nodoc:
|
||||||
|
@ -8,7 +9,8 @@ module ActionView
|
||||||
module TagHelper
|
module TagHelper
|
||||||
include ERB::Util
|
include ERB::Util
|
||||||
|
|
||||||
BOOLEAN_ATTRIBUTES = Set.new(%w(disabled readonly multiple))
|
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple).to_set
|
||||||
|
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
|
||||||
|
|
||||||
# Returns an empty HTML tag of type +name+ which by default is XHTML
|
# Returns an empty HTML tag of type +name+ which by default is XHTML
|
||||||
# compliant. Set +open+ to true to create an open tag compatible
|
# compliant. Set +open+ to true to create an open tag compatible
|
||||||
|
@ -37,7 +39,7 @@ module ActionView
|
||||||
# tag("img", { :src => "open & shut.png" }, false, false)
|
# tag("img", { :src => "open & shut.png" }, false, false)
|
||||||
# # => <img src="open & shut.png" />
|
# # => <img src="open & shut.png" />
|
||||||
def tag(name, options = nil, open = false, escape = true)
|
def tag(name, options = nil, open = false, escape = true)
|
||||||
"<#{name}#{tag_options(options, escape) if options}" + (open ? ">" : " />")
|
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
|
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
|
||||||
|
@ -114,7 +116,6 @@ module ActionView
|
||||||
if escape
|
if escape
|
||||||
options.each do |key, value|
|
options.each do |key, value|
|
||||||
next unless value
|
next unless value
|
||||||
key = key.to_s
|
|
||||||
value = BOOLEAN_ATTRIBUTES.include?(key) ? key : escape_once(value)
|
value = BOOLEAN_ATTRIBUTES.include?(key) ? key : escape_once(value)
|
||||||
attrs << %(#{key}="#{value}")
|
attrs << %(#{key}="#{value}")
|
||||||
end
|
end
|
||||||
|
|
|
@ -464,7 +464,7 @@ module ActionView
|
||||||
[-\w]+ # subdomain or domain
|
[-\w]+ # subdomain or domain
|
||||||
(?:\.[-\w]+)* # remaining subdomains or domain
|
(?:\.[-\w]+)* # remaining subdomains or domain
|
||||||
(?::\d+)? # port
|
(?::\d+)? # port
|
||||||
(?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:][^\s$]))+)?)* # path
|
(?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))+)?)* # path
|
||||||
(?:\?[\w\+@%&=.;-]+)? # query string
|
(?:\?[\w\+@%&=.;-]+)? # query string
|
||||||
(?:\#[\w\-]*)? # trailing anchor
|
(?:\#[\w\-]*)? # trailing anchor
|
||||||
)
|
)
|
||||||
|
|
|
@ -63,17 +63,15 @@ module ActionView
|
||||||
# # calls @workshop.to_s
|
# # calls @workshop.to_s
|
||||||
# # => /workshops/5
|
# # => /workshops/5
|
||||||
def url_for(options = {})
|
def url_for(options = {})
|
||||||
|
options ||= {}
|
||||||
case options
|
case options
|
||||||
when Hash
|
when Hash
|
||||||
show_path = options[:host].nil? ? true : false
|
options = { :only_path => options[:host].nil? }.update(options.symbolize_keys)
|
||||||
options = { :only_path => show_path }.update(options.symbolize_keys)
|
|
||||||
escape = options.key?(:escape) ? options.delete(:escape) : true
|
escape = options.key?(:escape) ? options.delete(:escape) : true
|
||||||
url = @controller.send(:url_for, options)
|
url = @controller.send(:url_for, options)
|
||||||
when String
|
when String
|
||||||
escape = true
|
escape = true
|
||||||
url = options
|
url = options
|
||||||
when NilClass
|
|
||||||
url = @controller.send(:url_for, nil)
|
|
||||||
else
|
else
|
||||||
escape = false
|
escape = false
|
||||||
url = polymorphic_path(options)
|
url = polymorphic_path(options)
|
||||||
|
@ -444,7 +442,7 @@ module ActionView
|
||||||
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
|
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
|
||||||
|
|
||||||
if encode == "javascript"
|
if encode == "javascript"
|
||||||
"document.write('#{content_tag("a", name || email_address, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
|
"document.write('#{content_tag("a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
|
||||||
string << sprintf("%%%x", c)
|
string << sprintf("%%%x", c)
|
||||||
end
|
end
|
||||||
"<script type=\"#{Mime::JS}\">eval(unescape('#{string}'))</script>"
|
"<script type=\"#{Mime::JS}\">eval(unescape('#{string}'))</script>"
|
||||||
|
|
|
@ -22,10 +22,10 @@ module ActionView #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_member(object)
|
def render_member(object)
|
||||||
@locals[@counter_name] += 1
|
|
||||||
@locals[:object] = @locals[@variable_name] = object
|
@locals[:object] = @locals[@variable_name] = object
|
||||||
|
|
||||||
template = render_template
|
template = render_template
|
||||||
|
@locals[@counter_name] += 1
|
||||||
@locals.delete(@variable_name)
|
@locals.delete(@variable_name)
|
||||||
@locals.delete(:object)
|
@locals.delete(:object)
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,9 @@ class AssertResponseWithUnexpectedErrorController < ActionController::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class UserController < ActionController::Base
|
||||||
|
end
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class InnerModuleController < ActionController::Base
|
class InnerModuleController < ActionController::Base
|
||||||
def index
|
def index
|
||||||
|
@ -174,7 +177,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
|
||||||
# let's get this party started
|
# let's get this party started
|
||||||
def setup
|
def setup
|
||||||
ActionController::Routing::Routes.reload
|
ActionController::Routing::Routes.reload
|
||||||
ActionController::Routing.use_controllers!(%w(action_pack_assertions admin/inner_module content admin/user))
|
ActionController::Routing.use_controllers!(%w(action_pack_assertions admin/inner_module user content admin/user))
|
||||||
@controller = ActionPackAssertionsController.new
|
@controller = ActionPackAssertionsController.new
|
||||||
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
||||||
end
|
end
|
||||||
|
@ -277,11 +280,25 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
@controller = Admin::InnerModuleController.new
|
@controller = Admin::InnerModuleController.new
|
||||||
process :redirect_to_top_level_named_route
|
process :redirect_to_top_level_named_route
|
||||||
# passes -> assert_redirected_to "http://test.host/action_pack_assertions/foo"
|
# assert_redirected_to "http://test.host/action_pack_assertions/foo" would pass because of exact match early return
|
||||||
assert_redirected_to "/action_pack_assertions/foo"
|
assert_redirected_to "/action_pack_assertions/foo"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_assert_redirected_to_top_level_named_route_with_same_controller_name_in_both_namespaces
|
||||||
|
with_routing do |set|
|
||||||
|
set.draw do |map|
|
||||||
|
# this controller exists in the admin namespace as well which is the only difference from previous test
|
||||||
|
map.top_level '/user/:id', :controller => 'user', :action => 'index'
|
||||||
|
map.connect ':controller/:action/:id'
|
||||||
|
end
|
||||||
|
@controller = Admin::InnerModuleController.new
|
||||||
|
process :redirect_to_top_level_named_route
|
||||||
|
# assert_redirected_to top_level_url('foo') would pass because of exact match early return
|
||||||
|
assert_redirected_to top_level_path('foo')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# -- standard request/response object testing --------------------------------
|
# -- standard request/response object testing --------------------------------
|
||||||
|
|
||||||
# make sure that the template objects exist
|
# make sure that the template objects exist
|
||||||
|
@ -406,7 +423,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
|
||||||
process :redirect_to_action
|
process :redirect_to_action
|
||||||
assert_redirected_to :action => "flash_me"
|
assert_redirected_to :action => "flash_me"
|
||||||
|
|
||||||
follow_redirect
|
assert_deprecated { follow_redirect }
|
||||||
assert_equal 1, @request.parameters["id"].to_i
|
assert_equal 1, @request.parameters["id"].to_i
|
||||||
|
|
||||||
assert "Inconceivable!", @response.body
|
assert "Inconceivable!", @response.body
|
||||||
|
@ -416,7 +433,9 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
|
||||||
process :redirect_to_controller
|
process :redirect_to_controller
|
||||||
assert_redirected_to :controller => "elsewhere", :action => "flash_me"
|
assert_redirected_to :controller => "elsewhere", :action => "flash_me"
|
||||||
|
|
||||||
assert_raises(RuntimeError, "Can't follow redirects outside of current controller (elsewhere)") { follow_redirect }
|
assert_raises(RuntimeError, "Can't follow redirects outside of current controller (elsewhere)") do
|
||||||
|
assert_deprecated { follow_redirect }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_assert_redirection_fails_with_incorrect_controller
|
def test_assert_redirection_fails_with_incorrect_controller
|
||||||
|
|
|
@ -569,6 +569,11 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_assert_select_rjs_raise_errors
|
||||||
|
assert_raises(ArgumentError) { assert_select_rjs(:destroy) }
|
||||||
|
assert_raises(ArgumentError) { assert_select_rjs(:insert, :left) }
|
||||||
|
end
|
||||||
|
|
||||||
# Simple selection from a single result.
|
# Simple selection from a single result.
|
||||||
def test_nested_assert_select_rjs_with_single_result
|
def test_nested_assert_select_rjs_with_single_result
|
||||||
render_rjs do |page|
|
render_rjs do |page|
|
||||||
|
|
|
@ -7,6 +7,7 @@ module Submodule
|
||||||
end
|
end
|
||||||
class ContainedNonEmptyController < ActionController::Base
|
class ContainedNonEmptyController < ActionController::Base
|
||||||
def public_action
|
def public_action
|
||||||
|
render :nothing => true
|
||||||
end
|
end
|
||||||
|
|
||||||
hide_action :hidden_action
|
hide_action :hidden_action
|
||||||
|
@ -105,6 +106,18 @@ end
|
||||||
|
|
||||||
|
|
||||||
class PerformActionTest < Test::Unit::TestCase
|
class PerformActionTest < Test::Unit::TestCase
|
||||||
|
class MockLogger
|
||||||
|
attr_reader :logged
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@logged = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(method, *args)
|
||||||
|
@logged << args.first
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def use_controller(controller_class)
|
def use_controller(controller_class)
|
||||||
@controller = controller_class.new
|
@controller = controller_class.new
|
||||||
|
|
||||||
|
@ -142,6 +155,13 @@ class PerformActionTest < Test::Unit::TestCase
|
||||||
get :another_hidden_action
|
get :another_hidden_action
|
||||||
assert_response 404
|
assert_response 404
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_namespaced_action_should_log_module_name
|
||||||
|
use_controller Submodule::ContainedNonEmptyController
|
||||||
|
@controller.logger = MockLogger.new
|
||||||
|
get :public_action
|
||||||
|
assert_match /Processing\sSubmodule::ContainedNonEmptyController#public_action/, @controller.logger.logged[1]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class DefaultUrlOptionsTest < Test::Unit::TestCase
|
class DefaultUrlOptionsTest < Test::Unit::TestCase
|
||||||
|
@ -169,6 +189,22 @@ class DefaultUrlOptionsTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class EmptyUrlOptionsTest < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
@controller = NonEmptyController.new
|
||||||
|
|
||||||
|
@request = ActionController::TestRequest.new
|
||||||
|
@response = ActionController::TestResponse.new
|
||||||
|
|
||||||
|
@request.host = 'www.example.com'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ensure_url_for_works_as_expected_when_called_with_no_options_if_default_url_options_is_not_set
|
||||||
|
get :public_action
|
||||||
|
assert_equal "http://www.example.com/non_empty/public_action", @controller.url_for
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class EnsureNamedRoutesWorksTicket22BugTest < Test::Unit::TestCase
|
class EnsureNamedRoutesWorksTicket22BugTest < Test::Unit::TestCase
|
||||||
def test_named_routes_still_work
|
def test_named_routes_still_work
|
||||||
ActionController::Routing::Routes.draw do |map|
|
ActionController::Routing::Routes.draw do |map|
|
||||||
|
|
|
@ -27,14 +27,14 @@ class DispatcherTest < Test::Unit::TestCase
|
||||||
|
|
||||||
def test_clears_dependencies_after_dispatch_if_in_loading_mode
|
def test_clears_dependencies_after_dispatch_if_in_loading_mode
|
||||||
ActionController::Routing::Routes.expects(:reload).once
|
ActionController::Routing::Routes.expects(:reload).once
|
||||||
Dependencies.expects(:clear).once
|
ActiveSupport::Dependencies.expects(:clear).once
|
||||||
|
|
||||||
dispatch(@output, false)
|
dispatch(@output, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode
|
def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode
|
||||||
ActionController::Routing::Routes.expects(:reload).never
|
ActionController::Routing::Routes.expects(:reload).never
|
||||||
Dependencies.expects(:clear).never
|
ActiveSupport::Dependencies.expects(:clear).never
|
||||||
|
|
||||||
dispatch
|
dispatch
|
||||||
end
|
end
|
||||||
|
|
|
@ -120,4 +120,29 @@ HTML
|
||||||
assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => "")
|
assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => "")
|
||||||
assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => nil)
|
assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_parse_invalid_document
|
||||||
|
assert_nothing_raised do
|
||||||
|
doc = HTML::Document.new("<html>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</html>")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_invalid_document_raises_exception_when_strict
|
||||||
|
assert_raises RuntimeError do
|
||||||
|
doc = HTML::Document.new("<html>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</html>", true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,7 +28,7 @@ class SessionUploadTest < ActionController::IntegrationTest
|
||||||
# end
|
# end
|
||||||
def test_post_with_upload
|
def test_post_with_upload
|
||||||
uses_mocha "test_post_with_upload" do
|
uses_mocha "test_post_with_upload" do
|
||||||
Dependencies.stubs(:load?).returns(false)
|
ActiveSupport::Dependencies.stubs(:load?).returns(false)
|
||||||
with_routing do |set|
|
with_routing do |set|
|
||||||
set.draw do |map|
|
set.draw do |map|
|
||||||
map.update 'update', :controller => "upload_test", :action => "update", :method => :post
|
map.update 'update', :controller => "upload_test", :action => "update", :method => :post
|
||||||
|
|
|
@ -69,6 +69,11 @@ class NewRenderTestController < ActionController::Base
|
||||||
render :file => path
|
render :file => path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render_file_from_template
|
||||||
|
@secret = 'in the sauce'
|
||||||
|
@path = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb'))
|
||||||
|
end
|
||||||
|
|
||||||
def render_file_with_locals
|
def render_file_with_locals
|
||||||
path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.erb')
|
path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.erb')
|
||||||
render :file => path, :locals => {:secret => 'in the sauce'}
|
render :file => path, :locals => {:secret => 'in the sauce'}
|
||||||
|
@ -259,6 +264,10 @@ class NewRenderTestController < ActionController::Base
|
||||||
render :template => "test/hello_world"
|
render :template => "test/hello_world"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render_with_explicit_template_with_locals
|
||||||
|
render :template => "test/render_file_with_locals", :locals => { :secret => 'area51' }
|
||||||
|
end
|
||||||
|
|
||||||
def double_render
|
def double_render
|
||||||
render :text => "hello"
|
render :text => "hello"
|
||||||
render :text => "world"
|
render :text => "world"
|
||||||
|
@ -532,6 +541,11 @@ class NewRenderTest < Test::Unit::TestCase
|
||||||
assert_equal "The secret is in the sauce\n", @response.body
|
assert_equal "The secret is in the sauce\n", @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_file_from_template
|
||||||
|
get :render_file_from_template
|
||||||
|
assert_equal "The secret is in the sauce\n", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
def test_attempt_to_access_object_method
|
def test_attempt_to_access_object_method
|
||||||
assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
|
assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
|
||||||
end
|
end
|
||||||
|
@ -742,7 +756,7 @@ EOS
|
||||||
|
|
||||||
def test_partial_collection_with_counter
|
def test_partial_collection_with_counter
|
||||||
get :partial_collection_with_counter
|
get :partial_collection_with_counter
|
||||||
assert_equal "david1mary2", @response.body
|
assert_equal "david0mary1", @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_partial_collection_with_locals
|
def test_partial_collection_with_locals
|
||||||
|
@ -762,7 +776,7 @@ EOS
|
||||||
|
|
||||||
def test_partial_collection_shorthand_with_different_types_of_records
|
def test_partial_collection_shorthand_with_different_types_of_records
|
||||||
get :partial_collection_shorthand_with_different_types_of_records
|
get :partial_collection_shorthand_with_different_types_of_records
|
||||||
assert_equal "Bonjour bad customer: mark1Bonjour good customer: craig2Bonjour bad customer: john3Bonjour good customer: zach4Bonjour good customer: brandon5Bonjour bad customer: dan6", @response.body
|
assert_equal "Bonjour bad customer: mark0Bonjour good customer: craig1Bonjour bad customer: john2Bonjour good customer: zach3Bonjour good customer: brandon4Bonjour bad customer: dan5", @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_empty_partial_collection
|
def test_empty_partial_collection
|
||||||
|
@ -801,6 +815,11 @@ EOS
|
||||||
assert_equal "world", assigns["hello"]
|
assert_equal "world", assigns["hello"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_template_with_locals
|
||||||
|
get :render_with_explicit_template_with_locals
|
||||||
|
assert_equal "The secret is area51\n", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
def test_update_page
|
def test_update_page
|
||||||
get :update_page
|
get :update_page
|
||||||
assert_template nil
|
assert_template nil
|
||||||
|
|
|
@ -118,6 +118,39 @@ uses_mocha 'polymorphic URL helpers' do
|
||||||
polymorphic_url([:site, :admin, @article, @response, @tag])
|
polymorphic_url([:site, :admin, @article, @response, @tag])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_nesting_with_array_ending_in_singleton_resource
|
||||||
|
expects(:article_response_url).with(@article)
|
||||||
|
polymorphic_url([@article, :response])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_nesting_with_array_containing_singleton_resource
|
||||||
|
@tag = Tag.new
|
||||||
|
@tag.save
|
||||||
|
expects(:article_response_tag_url).with(@article, @tag)
|
||||||
|
polymorphic_url([@article, :response, @tag])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_nesting_with_array_containing_namespace_and_singleton_resource
|
||||||
|
@tag = Tag.new
|
||||||
|
@tag.save
|
||||||
|
expects(:admin_article_response_tag_url).with(@article, @tag)
|
||||||
|
polymorphic_url([:admin, @article, :response, @tag])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_nesting_with_array_containing_singleton_resource_and_format
|
||||||
|
@tag = Tag.new
|
||||||
|
@tag.save
|
||||||
|
expects(:formatted_article_response_tag_url).with(@article, @tag, :pdf)
|
||||||
|
formatted_polymorphic_url([@article, :response, @tag, :pdf])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_nesting_with_array_containing_singleton_resource_and_format_option
|
||||||
|
@tag = Tag.new
|
||||||
|
@tag.save
|
||||||
|
expects(:article_response_tag_url).with(@article, @tag, :pdf)
|
||||||
|
polymorphic_url([@article, :response, @tag], :format => :pdf)
|
||||||
|
end
|
||||||
|
|
||||||
# TODO: Needs to be updated to correctly know about whether the object is in a hash or not
|
# TODO: Needs to be updated to correctly know about whether the object is in a hash or not
|
||||||
def xtest_with_hash
|
def xtest_with_hash
|
||||||
expects(:article_url).with(@article)
|
expects(:article_url).with(@article)
|
||||||
|
|
|
@ -103,7 +103,7 @@ class TestController < ActionController::Base
|
||||||
def render_line_offset
|
def render_line_offset
|
||||||
begin
|
begin
|
||||||
render :inline => '<% raise %>', :locals => {:foo => 'bar'}
|
render :inline => '<% raise %>', :locals => {:foo => 'bar'}
|
||||||
rescue => exc
|
rescue RuntimeError => exc
|
||||||
end
|
end
|
||||||
line = exc.backtrace.first
|
line = exc.backtrace.first
|
||||||
render :text => line
|
render :text => line
|
||||||
|
|
|
@ -12,6 +12,9 @@ class RequestTest < Test::Unit::TestCase
|
||||||
@request.remote_addr = '1.2.3.4'
|
@request.remote_addr = '1.2.3.4'
|
||||||
assert_equal '1.2.3.4', @request.remote_ip
|
assert_equal '1.2.3.4', @request.remote_ip
|
||||||
|
|
||||||
|
@request.remote_addr = '1.2.3.4,3.4.5.6'
|
||||||
|
assert_equal '1.2.3.4', @request.remote_ip
|
||||||
|
|
||||||
@request.env['HTTP_CLIENT_IP'] = '2.3.4.5'
|
@request.env['HTTP_CLIENT_IP'] = '2.3.4.5'
|
||||||
assert_equal '1.2.3.4', @request.remote_ip
|
assert_equal '1.2.3.4', @request.remote_ip
|
||||||
|
|
||||||
|
@ -59,6 +62,9 @@ class RequestTest < Test::Unit::TestCase
|
||||||
assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message
|
assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message
|
||||||
assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message
|
assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message
|
||||||
|
|
||||||
|
@request.env['HTTP_X_FORWARDED_FOR'] = '8.8.8.8, 9.9.9.9'
|
||||||
|
assert_equal '8.8.8.8', @request.remote_ip
|
||||||
|
|
||||||
@request.env.delete 'HTTP_CLIENT_IP'
|
@request.env.delete 'HTTP_CLIENT_IP'
|
||||||
@request.env.delete 'HTTP_X_FORWARDED_FOR'
|
@request.env.delete 'HTTP_X_FORWARDED_FOR'
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,15 +28,13 @@ module Backoffice
|
||||||
end
|
end
|
||||||
|
|
||||||
class ResourcesTest < Test::Unit::TestCase
|
class ResourcesTest < Test::Unit::TestCase
|
||||||
|
|
||||||
|
|
||||||
# The assertions in these tests are incompatible with the hash method
|
# The assertions in these tests are incompatible with the hash method
|
||||||
# optimisation. This could indicate user level problems
|
# optimisation. This could indicate user level problems
|
||||||
def setup
|
def setup
|
||||||
ActionController::Base.optimise_named_routes = false
|
ActionController::Base.optimise_named_routes = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def tear_down
|
def teardown
|
||||||
ActionController::Base.optimise_named_routes = true
|
ActionController::Base.optimise_named_routes = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1983,6 +1983,26 @@ class RouteSetTest < Test::Unit::TestCase
|
||||||
Object.send(:remove_const, :Api)
|
Object.send(:remove_const, :Api)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_namespace_with_path_prefix
|
||||||
|
Object.const_set(:Api, Module.new { |m| m.const_set(:ProductsController, Class.new) })
|
||||||
|
|
||||||
|
set.draw do |map|
|
||||||
|
|
||||||
|
map.namespace 'api', :path_prefix => 'prefix' do |api|
|
||||||
|
api.route 'inventory', :controller => "products", :action => 'inventory'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
request.path = "/prefix/inventory"
|
||||||
|
request.method = :get
|
||||||
|
assert_nothing_raised { set.recognize(request) }
|
||||||
|
assert_equal("api/products", request.path_parameters[:controller])
|
||||||
|
assert_equal("inventory", request.path_parameters[:action])
|
||||||
|
ensure
|
||||||
|
Object.send(:remove_const, :Api)
|
||||||
|
end
|
||||||
|
|
||||||
def test_generate_finds_best_fit
|
def test_generate_finds_best_fit
|
||||||
set.draw do |map|
|
set.draw do |map|
|
||||||
map.connect "/people", :controller => "people", :action => "index"
|
map.connect "/people", :controller => "people", :action => "index"
|
||||||
|
@ -2392,10 +2412,10 @@ uses_mocha 'route loading' do
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_adding_inflections_forces_reload
|
def test_adding_inflections_forces_reload
|
||||||
Inflector::Inflections.instance.expects(:uncountable).with('equipment')
|
ActiveSupport::Inflector::Inflections.instance.expects(:uncountable).with('equipment')
|
||||||
routes.expects(:reload!)
|
routes.expects(:reload!)
|
||||||
|
|
||||||
Inflector.inflections { |inflect| inflect.uncountable('equipment') }
|
ActiveSupport::Inflector.inflections { |inflect| inflect.uncountable('equipment') }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_load_with_configuration
|
def test_load_with_configuration
|
||||||
|
|
|
@ -531,6 +531,11 @@ XML
|
||||||
assert_equal content_type, file.content_type
|
assert_equal content_type, file.content_type
|
||||||
assert_equal file.path, file.local_path
|
assert_equal file.path, file.local_path
|
||||||
assert_equal expected, file.read
|
assert_equal expected, file.read
|
||||||
|
|
||||||
|
new_content_type = "new content_type"
|
||||||
|
file.content_type = new_content_type
|
||||||
|
assert_equal new_content_type, file.content_type
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_test_uploaded_file_with_binary
|
def test_test_uploaded_file_with_binary
|
||||||
|
@ -571,16 +576,20 @@ XML
|
||||||
get :redirect_to_same_controller
|
get :redirect_to_same_controller
|
||||||
assert_response :redirect
|
assert_response :redirect
|
||||||
assert_redirected_to :controller => 'test_test/test', :action => 'test_uri', :id => 5
|
assert_redirected_to :controller => 'test_test/test', :action => 'test_uri', :id => 5
|
||||||
|
assert_deprecated 'follow_redirect' do
|
||||||
assert_nothing_raised { follow_redirect }
|
assert_nothing_raised { follow_redirect }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_assert_follow_redirect_to_different_controller
|
def test_assert_follow_redirect_to_different_controller
|
||||||
with_foo_routing do |set|
|
with_foo_routing do |set|
|
||||||
get :redirect_to_different_controller
|
get :redirect_to_different_controller
|
||||||
assert_response :redirect
|
assert_response :redirect
|
||||||
assert_redirected_to :controller => 'fail', :id => 5
|
assert_redirected_to :controller => 'fail', :id => 5
|
||||||
assert_raise(RuntimeError) { follow_redirect }
|
assert_raise(RuntimeError) do
|
||||||
|
assert_deprecated { follow_redirect }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,9 @@ class VerificationTest < Test::Unit::TestCase
|
||||||
|
|
||||||
verify :only => :no_default_action, :params => "santa"
|
verify :only => :no_default_action, :params => "santa"
|
||||||
|
|
||||||
|
verify :only => :guarded_with_back, :method => :post,
|
||||||
|
:redirect_to => :back
|
||||||
|
|
||||||
def guarded_one
|
def guarded_one
|
||||||
render :text => "#{params[:one]}"
|
render :text => "#{params[:one]}"
|
||||||
end
|
end
|
||||||
|
@ -91,6 +94,10 @@ class VerificationTest < Test::Unit::TestCase
|
||||||
render :text => "Was a post!"
|
render :text => "Was a post!"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def guarded_with_back
|
||||||
|
render :text => "#{params[:one]}"
|
||||||
|
end
|
||||||
|
|
||||||
def no_default_action
|
def no_default_action
|
||||||
# Will never run
|
# Will never run
|
||||||
end
|
end
|
||||||
|
@ -110,6 +117,16 @@ class VerificationTest < Test::Unit::TestCase
|
||||||
ActionController::Routing::Routes.add_named_route :foo, '/foo', :controller => 'test', :action => 'foo'
|
ActionController::Routing::Routes.add_named_route :foo, '/foo', :controller => 'test', :action => 'foo'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_using_symbol_back_with_no_referrer
|
||||||
|
assert_raise(ActionController::RedirectBackError) { get :guarded_with_back }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_using_symbol_back_redirects_to_referrer
|
||||||
|
@request.env["HTTP_REFERER"] = "/foo"
|
||||||
|
get :guarded_with_back
|
||||||
|
assert_redirected_to '/foo'
|
||||||
|
end
|
||||||
|
|
||||||
def test_no_deprecation_warning_for_named_route
|
def test_no_deprecation_warning_for_named_route
|
||||||
assert_not_deprecated do
|
assert_not_deprecated do
|
||||||
get :guarded_one_for_named_route_test, :two => "not one"
|
get :guarded_one_for_named_route_test, :two => "not one"
|
||||||
|
|
1
vendor/rails/actionpack/test/fixtures/test/render_file_from_template.html.erb
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/test/render_file_from_template.html.erb
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<%= render :file => @path %>
|
|
@ -1157,6 +1157,32 @@ class DateHelperTest < ActionView::TestCase
|
||||||
assert_dom_equal expected, date_select("post", "written_on", {}, :class => 'selector')
|
assert_dom_equal expected, date_select("post", "written_on", {}, :class => 'selector')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_date_select_with_html_options_within_fields_for
|
||||||
|
@post = Post.new
|
||||||
|
@post.written_on = Date.new(2004, 6, 15)
|
||||||
|
|
||||||
|
_erbout = ''
|
||||||
|
|
||||||
|
fields_for :post, @post do |f|
|
||||||
|
_erbout.concat f.date_select(:written_on, {}, :class => 'selector')
|
||||||
|
end
|
||||||
|
|
||||||
|
expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n}
|
||||||
|
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
|
||||||
|
expected << "</select>\n"
|
||||||
|
|
||||||
|
expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]" class="selector">\n}
|
||||||
|
expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
|
||||||
|
expected << "</select>\n"
|
||||||
|
|
||||||
|
expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]" class="selector">\n}
|
||||||
|
expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
|
||||||
|
|
||||||
|
expected << "</select>\n"
|
||||||
|
|
||||||
|
assert_dom_equal expected, _erbout
|
||||||
|
end
|
||||||
|
|
||||||
def test_time_select
|
def test_time_select
|
||||||
@post = Post.new
|
@post = Post.new
|
||||||
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
|
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
|
||||||
|
@ -1218,6 +1244,31 @@ class DateHelperTest < ActionView::TestCase
|
||||||
assert_dom_equal expected, time_select("post", "written_on", {}, :class => 'selector')
|
assert_dom_equal expected, time_select("post", "written_on", {}, :class => 'selector')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_time_select_with_html_options_within_fields_for
|
||||||
|
@post = Post.new
|
||||||
|
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
|
||||||
|
|
||||||
|
_erbout = ''
|
||||||
|
|
||||||
|
fields_for :post, @post do |f|
|
||||||
|
_erbout.concat f.time_select(:written_on, {}, :class => 'selector')
|
||||||
|
end
|
||||||
|
|
||||||
|
expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
|
||||||
|
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
|
||||||
|
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
|
||||||
|
|
||||||
|
expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]" class="selector">\n)
|
||||||
|
0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
|
||||||
|
expected << "</select>\n"
|
||||||
|
expected << " : "
|
||||||
|
expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]" class="selector">\n)
|
||||||
|
0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
|
||||||
|
expected << "</select>\n"
|
||||||
|
|
||||||
|
assert_dom_equal expected, _erbout
|
||||||
|
end
|
||||||
|
|
||||||
def test_datetime_select
|
def test_datetime_select
|
||||||
@post = Post.new
|
@post = Post.new
|
||||||
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
|
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
|
||||||
|
@ -1283,23 +1334,23 @@ class DateHelperTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_datetime_select_within_fields_for
|
def test_datetime_select_with_html_options_within_fields_for
|
||||||
@post = Post.new
|
@post = Post.new
|
||||||
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
|
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
|
||||||
|
|
||||||
_erbout = ''
|
_erbout = ''
|
||||||
|
|
||||||
fields_for :post, @post do |f|
|
fields_for :post, @post do |f|
|
||||||
_erbout.concat f.datetime_select(:updated_at)
|
_erbout.concat f.datetime_select(:updated_at, {}, :class => 'selector')
|
||||||
end
|
end
|
||||||
|
|
||||||
expected = "<select id='post_updated_at_1i' name='post[updated_at(1i)]'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n"
|
expected = "<select id='post_updated_at_1i' name='post[updated_at(1i)]' class='selector'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n"
|
||||||
expected << "<select id='post_updated_at_2i' name='post[updated_at(2i)]'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n"
|
expected << "<select id='post_updated_at_2i' name='post[updated_at(2i)]' class='selector'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n"
|
||||||
expected << "<select id='post_updated_at_3i' name='post[updated_at(3i)]'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n"
|
expected << "<select id='post_updated_at_3i' name='post[updated_at(3i)]' class='selector'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n"
|
||||||
expected << " — <select id='post_updated_at_4i' name='post[updated_at(4i)]'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option selected='selected' value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n</select>\n"
|
expected << " — <select id='post_updated_at_4i' name='post[updated_at(4i)]' class='selector'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option selected='selected' value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n</select>\n"
|
||||||
expected << " : <select id='post_updated_at_5i' name='post[updated_at(5i)]'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n<option value='32'>32</option>\n<option value='33'>33</option>\n<option value='34'>34</option>\n<option selected='selected' value='35'>35</option>\n<option value='36'>36</option>\n<option value='37'>37</option>\n<option value='38'>38</option>\n<option value='39'>39</option>\n<option value='40'>40</option>\n<option value='41'>41</option>\n<option value='42'>42</option>\n<option value='43'>43</option>\n<option value='44'>44</option>\n<option value='45'>45</option>\n<option value='46'>46</option>\n<option value='47'>47</option>\n<option value='48'>48</option>\n<option value='49'>49</option>\n<option value='50'>50</option>\n<option value='51'>51</option>\n<option value='52'>52</option>\n<option value='53'>53</option>\n<option value='54'>54</option>\n<option value='55'>55</option>\n<option value='56'>56</option>\n<option value='57'>57</option>\n<option value='58'>58</option>\n<option value='59'>59</option>\n</select>\n"
|
expected << " : <select id='post_updated_at_5i' name='post[updated_at(5i)]' class='selector'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n<option value='32'>32</option>\n<option value='33'>33</option>\n<option value='34'>34</option>\n<option selected='selected' value='35'>35</option>\n<option value='36'>36</option>\n<option value='37'>37</option>\n<option value='38'>38</option>\n<option value='39'>39</option>\n<option value='40'>40</option>\n<option value='41'>41</option>\n<option value='42'>42</option>\n<option value='43'>43</option>\n<option value='44'>44</option>\n<option value='45'>45</option>\n<option value='46'>46</option>\n<option value='47'>47</option>\n<option value='48'>48</option>\n<option value='49'>49</option>\n<option value='50'>50</option>\n<option value='51'>51</option>\n<option value='52'>52</option>\n<option value='53'>53</option>\n<option value='54'>54</option>\n<option value='55'>55</option>\n<option value='56'>56</option>\n<option value='57'>57</option>\n<option value='58'>58</option>\n<option value='59'>59</option>\n</select>\n"
|
||||||
|
|
||||||
assert_dom_equal(expected, _erbout)
|
assert_dom_equal expected, _erbout
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_date_select_with_zero_value_and_no_start_year
|
def test_date_select_with_zero_value_and_no_start_year
|
||||||
|
|
9
vendor/rails/actionpack/test/template/deprecated_erb_variable_test.rb
vendored
Normal file
9
vendor/rails/actionpack/test/template/deprecated_erb_variable_test.rb
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
require 'abstract_unit'
|
||||||
|
|
||||||
|
class DeprecatedErbVariableTest < ActionView::TestCase
|
||||||
|
def test_setting_erb_variable_warns
|
||||||
|
assert_deprecated 'erb_variable' do
|
||||||
|
ActionView::Base.erb_variable = '_erbout'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,27 +1,8 @@
|
||||||
require 'abstract_unit'
|
require 'abstract_unit'
|
||||||
|
|
||||||
class MockTimeZone
|
TZInfo::Timezone.cattr_reader :loaded_zones
|
||||||
attr_reader :name
|
|
||||||
|
|
||||||
def initialize( name )
|
|
||||||
@name = name
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.all
|
|
||||||
[ "A", "B", "C", "D", "E" ].map { |s| new s }
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==( z )
|
|
||||||
z && @name == z.name
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
@name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ActionView::Helpers::FormOptionsHelper::TimeZone = MockTimeZone
|
|
||||||
|
|
||||||
|
uses_mocha "FormOptionsHelperTest" do
|
||||||
class FormOptionsHelperTest < ActionView::TestCase
|
class FormOptionsHelperTest < ActionView::TestCase
|
||||||
tests ActionView::Helpers::FormOptionsHelper
|
tests ActionView::Helpers::FormOptionsHelper
|
||||||
|
|
||||||
|
@ -33,6 +14,15 @@ class FormOptionsHelperTest < ActionView::TestCase
|
||||||
Album = Struct.new('Album', :id, :title, :genre)
|
Album = Struct.new('Album', :id, :title, :genre)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@fake_timezones = %w(A B C D E).inject([]) do |zones, id|
|
||||||
|
tz = TZInfo::Timezone.loaded_zones[id] = stub(:name => id, :to_s => id)
|
||||||
|
ActiveSupport::TimeZone.stubs(:[]).with(id).returns(tz)
|
||||||
|
zones << tz
|
||||||
|
end
|
||||||
|
ActiveSupport::TimeZone.stubs(:all).returns(@fake_timezones)
|
||||||
|
end
|
||||||
|
|
||||||
def test_collection_options
|
def test_collection_options
|
||||||
@posts = [
|
@posts = [
|
||||||
Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
|
Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
|
||||||
|
@ -183,7 +173,7 @@ class FormOptionsHelperTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_time_zone_options_with_priority_zones
|
def test_time_zone_options_with_priority_zones
|
||||||
zones = [ TimeZone.new( "B" ), TimeZone.new( "E" ) ]
|
zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
|
||||||
opts = time_zone_options_for_select( nil, zones )
|
opts = time_zone_options_for_select( nil, zones )
|
||||||
assert_dom_equal "<option value=\"B\">B</option>\n" +
|
assert_dom_equal "<option value=\"B\">B</option>\n" +
|
||||||
"<option value=\"E\">E</option>" +
|
"<option value=\"E\">E</option>" +
|
||||||
|
@ -195,7 +185,7 @@ class FormOptionsHelperTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_time_zone_options_with_selected_priority_zones
|
def test_time_zone_options_with_selected_priority_zones
|
||||||
zones = [ TimeZone.new( "B" ), TimeZone.new( "E" ) ]
|
zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
|
||||||
opts = time_zone_options_for_select( "E", zones )
|
opts = time_zone_options_for_select( "E", zones )
|
||||||
assert_dom_equal "<option value=\"B\">B</option>\n" +
|
assert_dom_equal "<option value=\"B\">B</option>\n" +
|
||||||
"<option value=\"E\" selected=\"selected\">E</option>" +
|
"<option value=\"E\" selected=\"selected\">E</option>" +
|
||||||
|
@ -207,7 +197,7 @@ class FormOptionsHelperTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_time_zone_options_with_unselected_priority_zones
|
def test_time_zone_options_with_unselected_priority_zones
|
||||||
zones = [ TimeZone.new( "B" ), TimeZone.new( "E" ) ]
|
zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
|
||||||
opts = time_zone_options_for_select( "C", zones )
|
opts = time_zone_options_for_select( "C", zones )
|
||||||
assert_dom_equal "<option value=\"B\">B</option>\n" +
|
assert_dom_equal "<option value=\"B\">B</option>\n" +
|
||||||
"<option value=\"E\">E</option>" +
|
"<option value=\"E\">E</option>" +
|
||||||
|
@ -1293,7 +1283,7 @@ COUNTRIES
|
||||||
|
|
||||||
def test_time_zone_select_with_priority_zones
|
def test_time_zone_select_with_priority_zones
|
||||||
@firm = Firm.new("D")
|
@firm = Firm.new("D")
|
||||||
zones = [ TimeZone.new("A"), TimeZone.new("D") ]
|
zones = [ ActiveSupport::TimeZone.new("A"), ActiveSupport::TimeZone.new("D") ]
|
||||||
html = time_zone_select("firm", "time_zone", zones )
|
html = time_zone_select("firm", "time_zone", zones )
|
||||||
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
|
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
|
||||||
"<option value=\"A\">A</option>\n" +
|
"<option value=\"A\">A</option>\n" +
|
||||||
|
@ -1334,3 +1324,4 @@ COUNTRIES
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -190,6 +190,12 @@ class FormTagHelperTest < ActionView::TestCase
|
||||||
assert_dom_equal expected, actual
|
assert_dom_equal expected, actual
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_label_tag_with_symbol
|
||||||
|
actual = label_tag :title
|
||||||
|
expected = %(<label for="title">Title</label>)
|
||||||
|
assert_dom_equal expected, actual
|
||||||
|
end
|
||||||
|
|
||||||
def test_label_tag_with_text
|
def test_label_tag_with_text
|
||||||
actual = label_tag "title", "My Title"
|
actual = label_tag "title", "My Title"
|
||||||
expected = %(<label for="title">My Title</label>)
|
expected = %(<label for="title">My Title</label>)
|
||||||
|
@ -222,6 +228,13 @@ class FormTagHelperTest < ActionView::TestCase
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_submit_tag_with_no_onclick_options
|
||||||
|
assert_dom_equal(
|
||||||
|
%(<input name='commit' type='submit' value='Save' onclick="this.setAttribute('originalValue', this.value);this.disabled=true;this.value='Saving...';result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());if (result == false) { this.value = this.getAttribute('originalValue'); this.disabled = false };return result;" />),
|
||||||
|
submit_tag("Save", :disable_with => "Saving...")
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def test_submit_tag_with_confirmation
|
def test_submit_tag_with_confirmation
|
||||||
assert_dom_equal(
|
assert_dom_equal(
|
||||||
%(<input name='commit' type='submit' value='Save' onclick="return confirm('Are you sure?');"/>),
|
%(<input name='commit' type='submit' value='Save' onclick="return confirm('Are you sure?');"/>),
|
||||||
|
|
|
@ -4,11 +4,14 @@ class JavaScriptHelperTest < ActionView::TestCase
|
||||||
tests ActionView::Helpers::JavaScriptHelper
|
tests ActionView::Helpers::JavaScriptHelper
|
||||||
|
|
||||||
def test_define_javascript_functions
|
def test_define_javascript_functions
|
||||||
|
assert_deprecated(/javascript_include_tag/) do
|
||||||
# check if prototype.js is included first
|
# check if prototype.js is included first
|
||||||
assert_not_nil define_javascript_functions.split("\n")[1].match(/Prototype JavaScript framework/)
|
src = define_javascript_functions
|
||||||
|
assert_not_nil src.split("\n")[1].match(/Prototype JavaScript framework/)
|
||||||
|
|
||||||
# check that scriptaculous.js is not in here, only needed if loaded remotely
|
# check that scriptaculous.js is not in here, only needed if loaded remotely
|
||||||
assert_nil define_javascript_functions.split("\n")[1].match(/var Scriptaculous = \{/)
|
assert_nil src.split("\n")[1].match(/var Scriptaculous = \{/)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_escape_javascript
|
def test_escape_javascript
|
||||||
|
|
|
@ -77,6 +77,10 @@ class PrototypeHelperTest < PrototypeHelperBaseTest
|
||||||
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot" })
|
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot" })
|
||||||
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
|
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
|
||||||
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
|
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
|
||||||
|
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:false, evalScripts:true}); return false;\">Remote outauthor</a>),
|
||||||
|
link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :type => :synchronous)
|
||||||
|
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, insertion:'bottom'}); return false;\">Remote outauthor</a>),
|
||||||
|
link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :position => :bottom)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_link_to_remote_html_options
|
def test_link_to_remote_html_options
|
||||||
|
@ -288,13 +292,13 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_insert_html_with_string
|
def test_insert_html_with_string
|
||||||
assert_equal 'new Insertion.Top("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");',
|
assert_equal 'Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });',
|
||||||
@generator.insert_html(:top, 'element', '<p>This is a test</p>')
|
@generator.insert_html(:top, 'element', '<p>This is a test</p>')
|
||||||
assert_equal 'new Insertion.Bottom("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");',
|
assert_equal 'Element.insert("element", { bottom: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
|
||||||
@generator.insert_html(:bottom, 'element', '<p>This is a test</p>')
|
@generator.insert_html(:bottom, 'element', '<p>This is a test</p>')
|
||||||
assert_equal 'new Insertion.Before("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");',
|
assert_equal 'Element.insert("element", { before: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
|
||||||
@generator.insert_html(:before, 'element', '<p>This is a test</p>')
|
@generator.insert_html(:before, 'element', '<p>This is a test</p>')
|
||||||
assert_equal 'new Insertion.After("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");',
|
assert_equal 'Element.insert("element", { after: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
|
||||||
@generator.insert_html(:after, 'element', '<p>This is a test</p>')
|
@generator.insert_html(:after, 'element', '<p>This is a test</p>')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -362,8 +366,8 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest
|
||||||
@generator.replace_html('baz', '<p>This is a test</p>')
|
@generator.replace_html('baz', '<p>This is a test</p>')
|
||||||
|
|
||||||
assert_equal <<-EOS.chomp, @generator.to_s
|
assert_equal <<-EOS.chomp, @generator.to_s
|
||||||
new Insertion.Top("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
|
Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
|
||||||
new Insertion.Bottom("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
|
Element.insert("element", { bottom: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
|
||||||
["foo", "bar"].each(Element.remove);
|
["foo", "bar"].each(Element.remove);
|
||||||
Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
|
Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
|
||||||
EOS
|
EOS
|
||||||
|
@ -425,6 +429,8 @@ Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
|
||||||
def test_sortable
|
def test_sortable
|
||||||
assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("blah")})}});),
|
assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("blah")})}});),
|
||||||
@generator.sortable('blah', :url => { :action => "order" })
|
@generator.sortable('blah', :url => { :action => "order" })
|
||||||
|
assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:Sortable.serialize("blah")})}});),
|
||||||
|
@generator.sortable('blah', :url => { :action => "order" }, :type => :synchronous)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_draggable
|
def test_draggable
|
||||||
|
@ -435,6 +441,8 @@ Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
|
||||||
def test_drop_receiving
|
def test_drop_receiving
|
||||||
assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});),
|
assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});),
|
||||||
@generator.drop_receiving('blah', :url => { :action => "order" })
|
@generator.drop_receiving('blah', :url => { :action => "order" })
|
||||||
|
assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});),
|
||||||
|
@generator.drop_receiving('blah', :url => { :action => "order" }, :type => :synchronous)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_collection_first_and_last
|
def test_collection_first_and_last
|
||||||
|
|
|
@ -187,6 +187,7 @@ class TextHelperTest < ActionView::TestCase
|
||||||
http://www.mail-archive.com/rails@lists.rubyonrails.org/
|
http://www.mail-archive.com/rails@lists.rubyonrails.org/
|
||||||
http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1
|
http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1
|
||||||
http://en.wikipedia.org/wiki/Sprite_(computer_graphics)
|
http://en.wikipedia.org/wiki/Sprite_(computer_graphics)
|
||||||
|
http://en.wikipedia.org/wiki/Texas_hold'em
|
||||||
)
|
)
|
||||||
|
|
||||||
urls.each do |url|
|
urls.each do |url|
|
||||||
|
|
|
@ -284,6 +284,7 @@ class UrlHelperTest < ActionView::TestCase
|
||||||
assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")
|
assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")
|
||||||
assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">me(at)domain(dot)com</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
|
assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">me(at)domain(dot)com</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
|
||||||
assert_dom_equal "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
|
assert_dom_equal "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
|
||||||
|
assert_dom_equal "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
|
||||||
end
|
end
|
||||||
|
|
||||||
def protect_against_forgery?
|
def protect_against_forgery?
|
||||||
|
@ -305,6 +306,10 @@ class UrlHelperWithControllerTest < ActionView::TestCase
|
||||||
render :inline => "<%= show_named_route_#{params[:kind]} %>"
|
render :inline => "<%= show_named_route_#{params[:kind]} %>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def nil_url_for
|
||||||
|
render :inline => '<%= url_for(nil) %>'
|
||||||
|
end
|
||||||
|
|
||||||
def rescue_action(e) raise e end
|
def rescue_action(e) raise e end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -321,7 +326,7 @@ class UrlHelperWithControllerTest < ActionView::TestCase
|
||||||
assert_equal '/url_helper_with_controller/show_url_for', @response.body
|
assert_equal '/url_helper_with_controller/show_url_for', @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_named_route_shows_host_and_path
|
def test_named_route_url_shows_host_and_path
|
||||||
with_url_helper_routing do
|
with_url_helper_routing do
|
||||||
get :show_named_route, :kind => 'url'
|
get :show_named_route, :kind => 'url'
|
||||||
assert_equal 'http://test.host/url_helper_with_controller/show_named_route', @response.body
|
assert_equal 'http://test.host/url_helper_with_controller/show_named_route', @response.body
|
||||||
|
@ -335,6 +340,11 @@ class UrlHelperWithControllerTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_url_for_nil_returns_current_path
|
||||||
|
get :nil_url_for
|
||||||
|
assert_equal '/url_helper_with_controller/nil_url_for', @response.body
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
def with_url_helper_routing
|
def with_url_helper_routing
|
||||||
with_routing do |set|
|
with_routing do |set|
|
||||||
|
|
2
vendor/rails/activemodel/Rakefile
vendored
2
vendor/rails/activemodel/Rakefile
vendored
|
@ -10,7 +10,7 @@ Rake::RDocTask.new { |rdoc|
|
||||||
rdoc.title = "Active Model"
|
rdoc.title = "Active Model"
|
||||||
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
||||||
rdoc.options << '--charset' << 'utf-8'
|
rdoc.options << '--charset' << 'utf-8'
|
||||||
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
|
||||||
rdoc.rdoc_files.include('README', 'CHANGES')
|
rdoc.rdoc_files.include('README', 'CHANGES')
|
||||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||||
}
|
}
|
34
vendor/rails/activerecord/CHANGELOG
vendored
34
vendor/rails/activerecord/CHANGELOG
vendored
|
@ -1,3 +1,37 @@
|
||||||
|
*2.1.1 (September 4th, 2008)*
|
||||||
|
|
||||||
|
* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
|
||||||
|
|
||||||
|
* Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334]
|
||||||
|
|
||||||
|
* change_column_default preserves the not-null constraint. #617 [Tarmo Tänav]
|
||||||
|
|
||||||
|
* Add :tokenizer option to validates_length_of to specify how to split up the attribute string. #507. [David Lowenfels] Example :
|
||||||
|
|
||||||
|
# Ensure essay contains at least 100 words.
|
||||||
|
validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
|
||||||
|
|
||||||
|
* Always treat integer :limit as byte length. #420 [Tarmo Tänav]
|
||||||
|
|
||||||
|
* Partial updates don't update lock_version if nothing changed. #426 [Daniel Morrison]
|
||||||
|
|
||||||
|
* Fix column collision with named_scope and :joins. #46 [Duncan Beevers, Mark Catley]
|
||||||
|
|
||||||
|
* db:migrate:down and :up update schema_migrations. #369 [Michael Raidel, RaceCondition]
|
||||||
|
|
||||||
|
* PostgreSQL: support :conditions => [':foo::integer', { :foo => 1 }] without treating the ::integer typecast as a bind variable. [Tarmo Tänav]
|
||||||
|
|
||||||
|
* MySQL: rename_column preserves column defaults. #466 [Diego Algorta]
|
||||||
|
|
||||||
|
* Add :from option to calculations. #397 [Ben Munat]
|
||||||
|
|
||||||
|
* Add :validate option to associations to enable/disable the automatic validation of associated models. Resolves #301. [Jan De Poorter]
|
||||||
|
|
||||||
|
* PostgreSQL: use 'INSERT ... RETURNING id' for 8.2 and later. [Jeremy Kemper]
|
||||||
|
|
||||||
|
* Added SQL escaping for :limit and :offset in MySQL [Jonathan Wiess]
|
||||||
|
|
||||||
|
|
||||||
*2.1.0 (May 31st, 2008)*
|
*2.1.0 (May 31st, 2008)*
|
||||||
|
|
||||||
* Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [rick]
|
* Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [rick]
|
||||||
|
|
11
vendor/rails/activerecord/Rakefile
vendored
11
vendor/rails/activerecord/Rakefile
vendored
|
@ -5,6 +5,7 @@ require 'rake/rdoctask'
|
||||||
require 'rake/packagetask'
|
require 'rake/packagetask'
|
||||||
require 'rake/gempackagetask'
|
require 'rake/gempackagetask'
|
||||||
require 'rake/contrib/sshpublisher'
|
require 'rake/contrib/sshpublisher'
|
||||||
|
require 'rake/contrib/rubyforgepublisher'
|
||||||
|
|
||||||
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
|
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
|
||||||
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
|
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
|
||||||
|
@ -141,7 +142,7 @@ Rake::RDocTask.new { |rdoc|
|
||||||
rdoc.title = "Active Record -- Object-relation mapping put on rails"
|
rdoc.title = "Active Record -- Object-relation mapping put on rails"
|
||||||
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
||||||
rdoc.options << '--charset' << 'utf-8'
|
rdoc.options << '--charset' << 'utf-8'
|
||||||
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
|
||||||
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
|
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
|
||||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||||
rdoc.rdoc_files.exclude('lib/active_record/vendor/*')
|
rdoc.rdoc_files.exclude('lib/active_record/vendor/*')
|
||||||
|
@ -171,7 +172,7 @@ spec = Gem::Specification.new do |s|
|
||||||
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||||
end
|
end
|
||||||
|
|
||||||
s.add_dependency('activesupport', '= 2.1.0' + PKG_BUILD)
|
s.add_dependency('activesupport', '= 2.1.1' + PKG_BUILD)
|
||||||
|
|
||||||
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
|
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
|
||||||
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
|
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
|
||||||
|
@ -225,13 +226,13 @@ end
|
||||||
|
|
||||||
desc "Publish the beta gem"
|
desc "Publish the beta gem"
|
||||||
task :pgem => [:package] do
|
task :pgem => [:package] do
|
||||||
Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
Rake::SshFilePublisher.new("david@greed.loudthinking.com", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
||||||
`ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'`
|
`ssh david@greed.loudthinking.com '/u/sites/gems/gemupdate.sh'`
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Publish the API documentation"
|
desc "Publish the API documentation"
|
||||||
task :pdoc => [:rdoc] do
|
task :pdoc => [:rdoc] do
|
||||||
Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/ar", "doc").upload
|
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Publish the release files to RubyForge."
|
desc "Publish the release files to RubyForge."
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
$:.unshift(File.dirname(__FILE__)) unless
|
$:.unshift(File.dirname(__FILE__)) unless
|
||||||
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
||||||
|
|
||||||
unless defined? ActiveSupport
|
|
||||||
active_support_path = File.dirname(__FILE__) + "/../../activesupport/lib"
|
active_support_path = File.dirname(__FILE__) + "/../../activesupport/lib"
|
||||||
if File.exist?(active_support_path)
|
if File.exist?(active_support_path)
|
||||||
$:.unshift active_support_path
|
$:.unshift active_support_path
|
||||||
|
@ -34,7 +33,6 @@ unless defined? ActiveSupport
|
||||||
gem 'activesupport'
|
gem 'activesupport'
|
||||||
require 'active_support'
|
require 'active_support'
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
require 'active_record/base'
|
require 'active_record/base'
|
||||||
require 'active_record/named_scope'
|
require 'active_record/named_scope'
|
||||||
|
|
|
@ -51,9 +51,7 @@ module ActiveRecord
|
||||||
|
|
||||||
def add_preloaded_record_to_collection(parent_records, reflection_name, associated_record)
|
def add_preloaded_record_to_collection(parent_records, reflection_name, associated_record)
|
||||||
parent_records.each do |parent_record|
|
parent_records.each do |parent_record|
|
||||||
association_proxy = parent_record.send(reflection_name)
|
parent_record.send("set_#{reflection_name}_target", associated_record)
|
||||||
association_proxy.loaded
|
|
||||||
association_proxy.target = associated_record
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -103,17 +101,17 @@ module ActiveRecord
|
||||||
associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
|
associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
|
||||||
:include => options[:include],
|
:include => options[:include],
|
||||||
:joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} as t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}",
|
:joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} as t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}",
|
||||||
:select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as _parent_record_id",
|
:select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id",
|
||||||
:order => options[:order])
|
:order => options[:order])
|
||||||
|
|
||||||
set_association_collection_records(id_to_record_map, reflection.name, associated_records, '_parent_record_id')
|
set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id')
|
||||||
end
|
end
|
||||||
|
|
||||||
def preload_has_one_association(records, reflection, preload_options={})
|
def preload_has_one_association(records, reflection, preload_options={})
|
||||||
id_to_record_map, ids = construct_id_map(records)
|
id_to_record_map, ids = construct_id_map(records)
|
||||||
options = reflection.options
|
options = reflection.options
|
||||||
|
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
|
||||||
if options[:through]
|
if options[:through]
|
||||||
records.each {|record| record.send(reflection.name) && record.send(reflection.name).loaded}
|
|
||||||
through_records = preload_through_records(records, reflection, options[:through])
|
through_records = preload_through_records(records, reflection, options[:through])
|
||||||
through_reflection = reflections[options[:through]]
|
through_reflection = reflections[options[:through]]
|
||||||
through_primary_key = through_reflection.primary_key_name
|
through_primary_key = through_reflection.primary_key_name
|
||||||
|
@ -126,8 +124,6 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
|
|
||||||
|
|
||||||
set_association_single_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options), reflection.primary_key_name)
|
set_association_single_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options), reflection.primary_key_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -188,7 +184,6 @@ module ActiveRecord
|
||||||
through_records
|
through_records
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME: quoting
|
|
||||||
def preload_belongs_to_association(records, reflection, preload_options={})
|
def preload_belongs_to_association(records, reflection, preload_options={})
|
||||||
options = reflection.options
|
options = reflection.options
|
||||||
primary_key_name = reflection.primary_key_name
|
primary_key_name = reflection.primary_key_name
|
||||||
|
@ -227,9 +222,19 @@ module ActiveRecord
|
||||||
|
|
||||||
table_name = klass.quoted_table_name
|
table_name = klass.quoted_table_name
|
||||||
primary_key = klass.primary_key
|
primary_key = klass.primary_key
|
||||||
conditions = "#{table_name}.#{primary_key} IN (?)"
|
conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} IN (?)"
|
||||||
conditions << append_conditions(options, preload_options)
|
conditions << append_conditions(options, preload_options)
|
||||||
associated_records = klass.find(:all, :conditions => [conditions, id_map.keys.uniq],
|
column_type = klass.columns.detect{|c| c.name == primary_key}.type
|
||||||
|
ids = id_map.keys.uniq.map do |id|
|
||||||
|
if column_type == :integer
|
||||||
|
id.to_i
|
||||||
|
elsif column_type == :float
|
||||||
|
id.to_f
|
||||||
|
else
|
||||||
|
id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
associated_records = klass.find(:all, :conditions => [conditions, ids],
|
||||||
:include => options[:include],
|
:include => options[:include],
|
||||||
:select => options[:select],
|
:select => options[:select],
|
||||||
:joins => options[:joins],
|
:joins => options[:joins],
|
||||||
|
@ -243,7 +248,7 @@ module ActiveRecord
|
||||||
table_name = reflection.klass.quoted_table_name
|
table_name = reflection.klass.quoted_table_name
|
||||||
|
|
||||||
if interface = reflection.options[:as]
|
if interface = reflection.options[:as]
|
||||||
conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} IN (?) and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.name.demodulize}'"
|
conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} IN (?) and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
|
||||||
else
|
else
|
||||||
foreign_key = reflection.primary_key_name
|
foreign_key = reflection.primary_key_name
|
||||||
conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} IN (?)"
|
conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} IN (?)"
|
||||||
|
|
|
@ -690,6 +690,7 @@ module ActiveRecord
|
||||||
# association is a polymorphic +belongs_to+.
|
# association is a polymorphic +belongs_to+.
|
||||||
# * <tt>:uniq</tt> - If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
|
# * <tt>:uniq</tt> - If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
|
||||||
# * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
|
# * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
|
||||||
|
# * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. true by default.
|
||||||
#
|
#
|
||||||
# Option examples:
|
# Option examples:
|
||||||
# has_many :comments, :order => "posted_on"
|
# has_many :comments, :order => "posted_on"
|
||||||
|
@ -710,6 +711,7 @@ module ActiveRecord
|
||||||
|
|
||||||
configure_dependency_for_has_many(reflection)
|
configure_dependency_for_has_many(reflection)
|
||||||
|
|
||||||
|
add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
|
||||||
add_multiple_associated_save_callbacks(reflection.name)
|
add_multiple_associated_save_callbacks(reflection.name)
|
||||||
add_association_callbacks(reflection.name, reflection.options)
|
add_association_callbacks(reflection.name, reflection.options)
|
||||||
|
|
||||||
|
@ -769,6 +771,7 @@ module ActiveRecord
|
||||||
# * <tt>:source_type</tt> - Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
|
# * <tt>:source_type</tt> - Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
|
||||||
# association is a polymorphic +belongs_to+.
|
# association is a polymorphic +belongs_to+.
|
||||||
# * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
|
# * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
|
||||||
|
# * <tt>:validate</tt> - If false, don't validate the associated object when saving the parent object. +false+ by default.
|
||||||
#
|
#
|
||||||
# Option examples:
|
# Option examples:
|
||||||
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
|
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
|
||||||
|
@ -799,7 +802,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
after_save method_name
|
after_save method_name
|
||||||
|
|
||||||
add_single_associated_save_callbacks(reflection.name)
|
add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
|
||||||
association_accessor_methods(reflection, HasOneAssociation)
|
association_accessor_methods(reflection, HasOneAssociation)
|
||||||
association_constructor_method(:build, reflection, HasOneAssociation)
|
association_constructor_method(:build, reflection, HasOneAssociation)
|
||||||
association_constructor_method(:create, reflection, HasOneAssociation)
|
association_constructor_method(:create, reflection, HasOneAssociation)
|
||||||
|
@ -857,6 +860,7 @@ module ActiveRecord
|
||||||
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
|
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
|
||||||
# to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
|
# to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
|
||||||
# * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
|
# * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
|
||||||
|
# * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. +false+ by default.
|
||||||
#
|
#
|
||||||
# Option examples:
|
# Option examples:
|
||||||
# belongs_to :firm, :foreign_key => "client_of"
|
# belongs_to :firm, :foreign_key => "client_of"
|
||||||
|
@ -937,6 +941,8 @@ module ActiveRecord
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
|
||||||
|
|
||||||
configure_dependency_for_belongs_to(reflection)
|
configure_dependency_for_belongs_to(reflection)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1025,6 +1031,7 @@ module ActiveRecord
|
||||||
# * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
|
# * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
|
||||||
# but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
|
# but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
|
||||||
# * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
|
# * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
|
||||||
|
# * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. +true+ by default.
|
||||||
#
|
#
|
||||||
# Option examples:
|
# Option examples:
|
||||||
# has_and_belongs_to_many :projects
|
# has_and_belongs_to_many :projects
|
||||||
|
@ -1037,6 +1044,7 @@ module ActiveRecord
|
||||||
def has_and_belongs_to_many(association_id, options = {}, &extension)
|
def has_and_belongs_to_many(association_id, options = {}, &extension)
|
||||||
reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
|
reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
|
||||||
|
|
||||||
|
add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
|
||||||
add_multiple_associated_save_callbacks(reflection.name)
|
add_multiple_associated_save_callbacks(reflection.name)
|
||||||
collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
|
collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
|
||||||
|
|
||||||
|
@ -1104,10 +1112,9 @@ module ActiveRecord
|
||||||
self.send(reflection.name, new_value)
|
self.send(reflection.name, new_value)
|
||||||
else
|
else
|
||||||
association.replace(new_value)
|
association.replace(new_value)
|
||||||
end
|
|
||||||
|
|
||||||
instance_variable_set(ivar, new_value.nil? ? nil : association)
|
instance_variable_set(ivar, new_value.nil? ? nil : association)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
define_method("set_#{reflection.name}_target") do |target|
|
define_method("set_#{reflection.name}_target") do |target|
|
||||||
return if target.nil? and association_proxy_class == BelongsToAssociation
|
return if target.nil? and association_proxy_class == BelongsToAssociation
|
||||||
|
@ -1157,7 +1164,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_single_associated_save_callbacks(association_name)
|
def add_single_associated_validation_callbacks(association_name)
|
||||||
method_name = "validate_associated_records_for_#{association_name}".to_sym
|
method_name = "validate_associated_records_for_#{association_name}".to_sym
|
||||||
define_method(method_name) do
|
define_method(method_name) do
|
||||||
association = instance_variable_get("@#{association_name}")
|
association = instance_variable_get("@#{association_name}")
|
||||||
|
@ -1169,7 +1176,7 @@ module ActiveRecord
|
||||||
validate method_name
|
validate method_name
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_multiple_associated_save_callbacks(association_name)
|
def add_multiple_associated_validation_callbacks(association_name)
|
||||||
method_name = "validate_associated_records_for_#{association_name}".to_sym
|
method_name = "validate_associated_records_for_#{association_name}".to_sym
|
||||||
ivar = "@#{association_name}"
|
ivar = "@#{association_name}"
|
||||||
|
|
||||||
|
@ -1190,6 +1197,10 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
validate method_name
|
validate method_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_multiple_associated_save_callbacks(association_name)
|
||||||
|
ivar = "@#{association_name}"
|
||||||
|
|
||||||
method_name = "before_save_associated_records_for_#{association_name}".to_sym
|
method_name = "before_save_associated_records_for_#{association_name}".to_sym
|
||||||
define_method(method_name) do
|
define_method(method_name) do
|
||||||
|
@ -1211,7 +1222,6 @@ module ActiveRecord
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank?
|
records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank?
|
||||||
|
|
||||||
# reconstruct the SQL queries now that we know the owner's id
|
# reconstruct the SQL queries now that we know the owner's id
|
||||||
|
@ -1343,7 +1353,8 @@ module ActiveRecord
|
||||||
:uniq,
|
:uniq,
|
||||||
:finder_sql, :counter_sql,
|
:finder_sql, :counter_sql,
|
||||||
:before_add, :after_add, :before_remove, :after_remove,
|
:before_add, :after_add, :before_remove, :after_remove,
|
||||||
:extend, :readonly
|
:extend, :readonly,
|
||||||
|
:validate
|
||||||
)
|
)
|
||||||
|
|
||||||
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
|
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
|
||||||
|
@ -1353,7 +1364,7 @@ module ActiveRecord
|
||||||
|
|
||||||
def create_has_one_reflection(association_id, options)
|
def create_has_one_reflection(association_id, options)
|
||||||
options.assert_valid_keys(
|
options.assert_valid_keys(
|
||||||
:class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly
|
:class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly, :validate
|
||||||
)
|
)
|
||||||
|
|
||||||
create_reflection(:has_one, association_id, options, self)
|
create_reflection(:has_one, association_id, options, self)
|
||||||
|
@ -1361,7 +1372,7 @@ module ActiveRecord
|
||||||
|
|
||||||
def create_has_one_through_reflection(association_id, options)
|
def create_has_one_through_reflection(association_id, options)
|
||||||
options.assert_valid_keys(
|
options.assert_valid_keys(
|
||||||
:class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type
|
:class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type, :validate
|
||||||
)
|
)
|
||||||
create_reflection(:has_one, association_id, options, self)
|
create_reflection(:has_one, association_id, options, self)
|
||||||
end
|
end
|
||||||
|
@ -1369,7 +1380,7 @@ module ActiveRecord
|
||||||
def create_belongs_to_reflection(association_id, options)
|
def create_belongs_to_reflection(association_id, options)
|
||||||
options.assert_valid_keys(
|
options.assert_valid_keys(
|
||||||
:class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent,
|
:class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent,
|
||||||
:counter_cache, :extend, :polymorphic, :readonly
|
:counter_cache, :extend, :polymorphic, :readonly, :validate
|
||||||
)
|
)
|
||||||
|
|
||||||
reflection = create_reflection(:belongs_to, association_id, options, self)
|
reflection = create_reflection(:belongs_to, association_id, options, self)
|
||||||
|
@ -1388,7 +1399,8 @@ module ActiveRecord
|
||||||
:uniq,
|
:uniq,
|
||||||
:finder_sql, :delete_sql, :insert_sql,
|
:finder_sql, :delete_sql, :insert_sql,
|
||||||
:before_add, :after_add, :before_remove, :after_remove,
|
:before_add, :after_add, :before_remove, :after_remove,
|
||||||
:extend, :readonly
|
:extend, :readonly,
|
||||||
|
:validate
|
||||||
)
|
)
|
||||||
|
|
||||||
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
|
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
|
||||||
|
@ -1465,10 +1477,15 @@ module ActiveRecord
|
||||||
join_dependency.joins_for_table_name(table)
|
join_dependency.joins_for_table_name(table)
|
||||||
}.flatten.compact.uniq
|
}.flatten.compact.uniq
|
||||||
|
|
||||||
|
order = options[:order]
|
||||||
|
if scoped_order = (scope && scope[:order])
|
||||||
|
order = order ? "#{order}, #{scoped_order}" : scoped_order
|
||||||
|
end
|
||||||
|
|
||||||
is_distinct = !options[:joins].blank? || include_eager_conditions?(options, tables_from_conditions) || include_eager_order?(options, tables_from_order)
|
is_distinct = !options[:joins].blank? || include_eager_conditions?(options, tables_from_conditions) || include_eager_order?(options, tables_from_order)
|
||||||
sql = "SELECT "
|
sql = "SELECT "
|
||||||
if is_distinct
|
if is_distinct
|
||||||
sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", options[:order])
|
sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", order)
|
||||||
else
|
else
|
||||||
sql << primary_key
|
sql << primary_key
|
||||||
end
|
end
|
||||||
|
@ -1482,8 +1499,8 @@ module ActiveRecord
|
||||||
add_conditions!(sql, options[:conditions], scope)
|
add_conditions!(sql, options[:conditions], scope)
|
||||||
add_group!(sql, options[:group], scope)
|
add_group!(sql, options[:group], scope)
|
||||||
|
|
||||||
if options[:order] && is_distinct
|
if order && is_distinct
|
||||||
connection.add_order_by_for_association_limiting!(sql, options)
|
connection.add_order_by_for_association_limiting!(sql, :order => order)
|
||||||
else
|
else
|
||||||
add_order!(sql, options[:order], scope)
|
add_order!(sql, options[:order], scope)
|
||||||
end
|
end
|
||||||
|
@ -1502,19 +1519,19 @@ module ActiveRecord
|
||||||
else all << cond
|
else all << cond
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
conditions.join(' ').scan(/([\.\w]+).?\./).flatten
|
conditions.join(' ').scan(/([\.a-zA-Z_]+).?\./).flatten
|
||||||
end
|
end
|
||||||
|
|
||||||
def order_tables(options)
|
def order_tables(options)
|
||||||
order = options[:order]
|
order = [options[:order], scope(:find, :order) ].join(", ")
|
||||||
return [] unless order && order.is_a?(String)
|
return [] unless order && order.is_a?(String)
|
||||||
order.scan(/([\.\w]+).?\./).flatten
|
order.scan(/([\.a-zA-Z_]+).?\./).flatten
|
||||||
end
|
end
|
||||||
|
|
||||||
def selects_tables(options)
|
def selects_tables(options)
|
||||||
select = options[:select]
|
select = options[:select]
|
||||||
return [] unless select && select.is_a?(String)
|
return [] unless select && select.is_a?(String)
|
||||||
select.scan(/"?([\.\w]+)"?.?\./).flatten
|
select.scan(/"?([\.a-zA-Z_]+)"?.?\./).flatten
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks if the conditions reference a table other than the current model table
|
# Checks if the conditions reference a table other than the current model table
|
||||||
|
@ -1638,7 +1655,9 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def join_for_table_name(table_name)
|
def join_for_table_name(table_name)
|
||||||
@joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil
|
join = (@joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first) rescue nil
|
||||||
|
return join unless join.nil?
|
||||||
|
@joins.select{|j|j.is_a?(JoinAssociation) && j.aliased_join_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def joins_for_table_name(table_name)
|
def joins_for_table_name(table_name)
|
||||||
|
@ -1714,6 +1733,7 @@ module ActiveRecord
|
||||||
collection.target.push(association)
|
collection.target.push(association)
|
||||||
when :has_one
|
when :has_one
|
||||||
return if record.id.to_s != join.parent.record_id(row).to_s
|
return if record.id.to_s != join.parent.record_id(row).to_s
|
||||||
|
return if record.instance_variable_defined?("@#{join.reflection.name}")
|
||||||
association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
|
association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
|
||||||
record.send("set_#{join.reflection.name}_target", association)
|
record.send("set_#{join.reflection.name}_target", association)
|
||||||
when :belongs_to
|
when :belongs_to
|
||||||
|
@ -1795,7 +1815,7 @@ module ActiveRecord
|
||||||
@aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
|
@aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
|
||||||
end
|
end
|
||||||
|
|
||||||
if reflection.macro == :has_many && reflection.options[:through]
|
if [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through]
|
||||||
@aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join")
|
@aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1819,7 +1839,7 @@ module ActiveRecord
|
||||||
]
|
]
|
||||||
when :has_many, :has_one
|
when :has_many, :has_one
|
||||||
case
|
case
|
||||||
when reflection.macro == :has_many && reflection.options[:through]
|
when reflection.options[:through]
|
||||||
through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
|
through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
|
||||||
|
|
||||||
jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
|
jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
|
||||||
|
@ -1855,7 +1875,7 @@ module ActiveRecord
|
||||||
jt_sti_extra = " AND %s.%s = %s" % [
|
jt_sti_extra = " AND %s.%s = %s" % [
|
||||||
connection.quote_table_name(aliased_join_table_name),
|
connection.quote_table_name(aliased_join_table_name),
|
||||||
connection.quote_column_name(through_reflection.active_record.inheritance_column),
|
connection.quote_column_name(through_reflection.active_record.inheritance_column),
|
||||||
through_reflection.klass.quote_value(through_reflection.klass.name.demodulize)]
|
through_reflection.klass.quote_value(through_reflection.klass.sti_name)]
|
||||||
end
|
end
|
||||||
when :belongs_to
|
when :belongs_to
|
||||||
first_key = primary_key
|
first_key = primary_key
|
||||||
|
@ -1920,10 +1940,8 @@ module ActiveRecord
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
end || ''
|
end || ''
|
||||||
join << %(AND %s.%s = %s ) % [
|
join << %(AND %s) % [
|
||||||
connection.quote_table_name(aliased_table_name),
|
klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record?
|
||||||
connection.quote_column_name(klass.inheritance_column),
|
|
||||||
klass.quote_value(klass.name.demodulize)] unless klass.descends_from_active_record?
|
|
||||||
|
|
||||||
[through_reflection, reflection].each do |ref|
|
[through_reflection, reflection].each do |ref|
|
||||||
join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions]
|
join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions]
|
||||||
|
|
|
@ -78,11 +78,14 @@ module ActiveRecord
|
||||||
@loaded = false
|
@loaded = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def build(attributes = {})
|
def build(attributes = {}, &block)
|
||||||
if attributes.is_a?(Array)
|
if attributes.is_a?(Array)
|
||||||
attributes.collect { |attr| build(attr) }
|
attributes.collect { |attr| build(attr, &block) }
|
||||||
else
|
else
|
||||||
build_record(attributes) { |record| set_belongs_to_association_for(record) }
|
build_record(attributes) do |record|
|
||||||
|
block.call(record) if block_given?
|
||||||
|
set_belongs_to_association_for(record)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -187,7 +190,7 @@ module ActiveRecord
|
||||||
if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
|
if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
|
||||||
@target.size
|
@target.size
|
||||||
elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
|
elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
|
||||||
unsaved_records = Array(@target.detect { |r| r.new_record? })
|
unsaved_records = @target.select { |r| r.new_record? }
|
||||||
unsaved_records.size + count_records
|
unsaved_records.size + count_records
|
||||||
else
|
else
|
||||||
count_records
|
count_records
|
||||||
|
@ -335,7 +338,7 @@ module ActiveRecord
|
||||||
callback(:before_add, record)
|
callback(:before_add, record)
|
||||||
yield(record) if block_given?
|
yield(record) if block_given?
|
||||||
@target ||= [] unless loaded?
|
@target ||= [] unless loaded?
|
||||||
@target << record
|
@target << record unless @reflection.options[:uniq] && @target.include?(record)
|
||||||
callback(:after_add, record)
|
callback(:after_add, record)
|
||||||
record
|
record
|
||||||
end
|
end
|
||||||
|
|
|
@ -69,8 +69,8 @@ module ActiveRecord
|
||||||
@target
|
@target
|
||||||
end
|
end
|
||||||
|
|
||||||
def respond_to?(symbol, include_priv = false)
|
def respond_to?(*args)
|
||||||
proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv))
|
proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Explicitly proxy === because the instance method removal above
|
# Explicitly proxy === because the instance method removal above
|
||||||
|
@ -131,10 +131,6 @@ module ActiveRecord
|
||||||
records.map { |record| record.quoted_id }.join(',')
|
records.map { |record| record.quoted_id }.join(',')
|
||||||
end
|
end
|
||||||
|
|
||||||
def interpolate_sql_options!(options, *keys)
|
|
||||||
keys.each { |key| options[key] &&= interpolate_sql(options[key]) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def interpolate_sql(sql, record = nil)
|
def interpolate_sql(sql, record = nil)
|
||||||
@owner.send(:interpolate_sql, sql, record)
|
@owner.send(:interpolate_sql, sql, record)
|
||||||
end
|
end
|
||||||
|
|
|
@ -70,10 +70,8 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def construct_sql
|
def construct_sql
|
||||||
interpolate_sql_options!(@reflection.options, :finder_sql)
|
|
||||||
|
|
||||||
if @reflection.options[:finder_sql]
|
if @reflection.options[:finder_sql]
|
||||||
@finder_sql = @reflection.options[:finder_sql]
|
@finder_sql = interpolate_sql(@reflection.options[:finder_sql])
|
||||||
else
|
else
|
||||||
@finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} "
|
@finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} "
|
||||||
@finder_sql << " AND (#{conditions})" if conditions
|
@finder_sql << " AND (#{conditions})" if conditions
|
||||||
|
@ -87,6 +85,7 @@ module ActiveRecord
|
||||||
:joins => @join_sql,
|
:joins => @join_sql,
|
||||||
:readonly => false,
|
:readonly => false,
|
||||||
:order => @reflection.options[:order],
|
:order => @reflection.options[:order],
|
||||||
|
:include => @reflection.options[:include],
|
||||||
:limit => @reflection.options[:limit] } }
|
:limit => @reflection.options[:limit] } }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,16 @@ module ActiveRecord
|
||||||
@finder_sql + " AND (#{sanitize_sql(options[:conditions])})"
|
@finder_sql + " AND (#{sanitize_sql(options[:conditions])})"
|
||||||
options[:include] ||= @reflection.options[:include]
|
options[:include] ||= @reflection.options[:include]
|
||||||
|
|
||||||
@reflection.klass.count(column_name, options)
|
value = @reflection.klass.count(column_name, options)
|
||||||
|
|
||||||
|
limit = @reflection.options[:limit]
|
||||||
|
offset = @reflection.options[:offset]
|
||||||
|
|
||||||
|
if limit || offset
|
||||||
|
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,7 +37,10 @@ module ActiveRecord
|
||||||
@reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
|
@reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
|
||||||
end
|
end
|
||||||
|
|
||||||
@target = [] and loaded if count == 0
|
# If there's nothing in the database and @target has no new records
|
||||||
|
# we are certain the current target is an empty array. This is a
|
||||||
|
# documented side-effect of the method that may avoid an extra SELECT.
|
||||||
|
@target ||= [] and loaded if count == 0
|
||||||
|
|
||||||
if @reflection.options[:limit]
|
if @reflection.options[:limit]
|
||||||
count = [ @reflection.options[:limit], count ].min
|
count = [ @reflection.options[:limit], count ].min
|
||||||
|
@ -100,7 +112,7 @@ module ActiveRecord
|
||||||
create_scoping = {}
|
create_scoping = {}
|
||||||
set_belongs_to_association_for(create_scoping)
|
set_belongs_to_association_for(create_scoping)
|
||||||
{
|
{
|
||||||
:find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit] },
|
:find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit], :include => @reflection.options[:include]},
|
||||||
:create => create_scoping
|
:create => create_scoping
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -237,7 +237,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_sti_condition
|
def build_sti_condition
|
||||||
"#{@reflection.through_reflection.quoted_table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.sti_name)}"
|
@reflection.through_reflection.klass.send(:type_condition)
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :sql_conditions, :conditions
|
alias_method :sql_conditions, :conditions
|
||||||
|
|
|
@ -21,8 +21,8 @@ module ActiveRecord
|
||||||
def replace(obj, dont_save = false)
|
def replace(obj, dont_save = false)
|
||||||
load_target
|
load_target
|
||||||
|
|
||||||
unless @target.nil?
|
unless @target.nil? || @target == obj
|
||||||
if dependent? && !dont_save && @target != obj
|
if dependent? && !dont_save
|
||||||
@target.destroy unless @target.new_record?
|
@target.destroy unless @target.new_record?
|
||||||
@owner.clear_association_cache
|
@owner.clear_association_cache
|
||||||
else
|
else
|
||||||
|
|
|
@ -23,6 +23,10 @@ module ActiveRecord
|
||||||
def find_target
|
def find_target
|
||||||
super.first
|
super.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reset_target!
|
||||||
|
@target = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -372,7 +372,7 @@ module ActiveRecord #:nodoc:
|
||||||
def self.reset_subclasses #:nodoc:
|
def self.reset_subclasses #:nodoc:
|
||||||
nonreloadables = []
|
nonreloadables = []
|
||||||
subclasses.each do |klass|
|
subclasses.each do |klass|
|
||||||
unless Dependencies.autoloaded? klass
|
unless ActiveSupport::Dependencies.autoloaded? klass
|
||||||
nonreloadables << klass
|
nonreloadables << klass
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
@ -439,6 +439,10 @@ module ActiveRecord #:nodoc:
|
||||||
cattr_accessor :schema_format , :instance_writer => false
|
cattr_accessor :schema_format , :instance_writer => false
|
||||||
@@schema_format = :ruby
|
@@schema_format = :ruby
|
||||||
|
|
||||||
|
# Specify whether or not to use timestamps for migration numbers
|
||||||
|
cattr_accessor :timestamped_migrations , :instance_writer => false
|
||||||
|
@@timestamped_migrations = true
|
||||||
|
|
||||||
# Determine whether to store the full constant name including namespace when using STI
|
# Determine whether to store the full constant name including namespace when using STI
|
||||||
superclass_delegating_accessor :store_full_sti_class
|
superclass_delegating_accessor :store_full_sti_class
|
||||||
self.store_full_sti_class = false
|
self.store_full_sti_class = false
|
||||||
|
@ -828,7 +832,7 @@ module ActiveRecord #:nodoc:
|
||||||
def update_counters(id, counters)
|
def update_counters(id, counters)
|
||||||
updates = counters.inject([]) { |list, (counter_name, increment)|
|
updates = counters.inject([]) { |list, (counter_name, increment)|
|
||||||
sign = increment < 0 ? "-" : "+"
|
sign = increment < 0 ? "-" : "+"
|
||||||
list << "#{connection.quote_column_name(counter_name)} = #{connection.quote_column_name(counter_name)} #{sign} #{increment.abs}"
|
list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}"
|
||||||
}.join(", ")
|
}.join(", ")
|
||||||
update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
|
update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
|
||||||
end
|
end
|
||||||
|
@ -1465,7 +1469,7 @@ module ActiveRecord #:nodoc:
|
||||||
|
|
||||||
def construct_finder_sql(options)
|
def construct_finder_sql(options)
|
||||||
scope = scope(:find)
|
scope = scope(:find)
|
||||||
sql = "SELECT #{options[:select] || (scope && scope[:select]) || (options[:joins] && quoted_table_name + '.*') || '*'} "
|
sql = "SELECT #{options[:select] || (scope && scope[:select]) || ((options[:joins] || (scope && scope[:joins])) && quoted_table_name + '.*') || '*'} "
|
||||||
sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
|
sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
|
||||||
|
|
||||||
add_joins!(sql, options, scope)
|
add_joins!(sql, options, scope)
|
||||||
|
@ -1577,10 +1581,11 @@ module ActiveRecord #:nodoc:
|
||||||
sql << "WHERE #{merged_conditions} " unless merged_conditions.blank?
|
sql << "WHERE #{merged_conditions} " unless merged_conditions.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
def type_condition
|
def type_condition(table_alias=nil)
|
||||||
|
quoted_table_alias = self.connection.quote_table_name(table_alias || table_name)
|
||||||
quoted_inheritance_column = connection.quote_column_name(inheritance_column)
|
quoted_inheritance_column = connection.quote_column_name(inheritance_column)
|
||||||
type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass|
|
type_condition = subclasses.inject("#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass|
|
||||||
condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{subclass.sti_name}' "
|
condition << "OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' "
|
||||||
end
|
end
|
||||||
|
|
||||||
" (#{type_condition}) "
|
" (#{type_condition}) "
|
||||||
|
@ -1717,7 +1722,7 @@ module ActiveRecord #:nodoc:
|
||||||
def attribute_condition(argument)
|
def attribute_condition(argument)
|
||||||
case argument
|
case argument
|
||||||
when nil then "IS ?"
|
when nil then "IS ?"
|
||||||
when Array, ActiveRecord::Associations::AssociationCollection then "IN (?)"
|
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)"
|
||||||
when Range then "BETWEEN ? AND ?"
|
when Range then "BETWEEN ? AND ?"
|
||||||
else "= ?"
|
else "= ?"
|
||||||
end
|
end
|
||||||
|
@ -2053,9 +2058,10 @@ module ActiveRecord #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
|
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
|
||||||
statement.gsub(/:([a-zA-Z]\w*)/) do
|
statement.gsub(/(:?):([a-zA-Z]\w*)/) do
|
||||||
match = $1.to_sym
|
if $1 == ':' # skip postgresql casts
|
||||||
if bind_vars.include?(match)
|
$& # return the whole match
|
||||||
|
elsif bind_vars.include?(match = $2.to_sym)
|
||||||
quote_bound_value(bind_vars[match])
|
quote_bound_value(bind_vars[match])
|
||||||
else
|
else
|
||||||
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
||||||
|
@ -2064,13 +2070,18 @@ module ActiveRecord #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
def expand_range_bind_variables(bind_vars) #:nodoc:
|
def expand_range_bind_variables(bind_vars) #:nodoc:
|
||||||
bind_vars.sum do |var|
|
expanded = []
|
||||||
|
|
||||||
|
bind_vars.each do |var|
|
||||||
if var.is_a?(Range)
|
if var.is_a?(Range)
|
||||||
[var.first, var.last]
|
expanded << var.first
|
||||||
|
expanded << var.last
|
||||||
else
|
else
|
||||||
[var]
|
expanded << var
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
expanded
|
||||||
end
|
end
|
||||||
|
|
||||||
def quote_bound_value(value) #:nodoc:
|
def quote_bound_value(value) #:nodoc:
|
||||||
|
@ -2572,8 +2583,15 @@ module ActiveRecord #:nodoc:
|
||||||
quoted = {}
|
quoted = {}
|
||||||
connection = self.class.connection
|
connection = self.class.connection
|
||||||
attribute_names.each do |name|
|
attribute_names.each do |name|
|
||||||
if column = column_for_attribute(name)
|
if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
|
||||||
quoted[name] = connection.quote(read_attribute(name), column) unless !include_primary_key && column.primary
|
value = read_attribute(name)
|
||||||
|
|
||||||
|
# We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
|
||||||
|
if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))
|
||||||
|
value = value.to_yaml
|
||||||
|
end
|
||||||
|
|
||||||
|
quoted[name] = connection.quote(value, column)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
|
include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module ActiveRecord
|
module ActiveRecord
|
||||||
module Calculations #:nodoc:
|
module Calculations #:nodoc:
|
||||||
CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include]
|
CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from]
|
||||||
def self.included(base)
|
def self.included(base)
|
||||||
base.extend(ClassMethods)
|
base.extend(ClassMethods)
|
||||||
end
|
end
|
||||||
|
@ -27,6 +27,8 @@ module ActiveRecord
|
||||||
# * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not
|
# * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not
|
||||||
# include the joined columns.
|
# include the joined columns.
|
||||||
# * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
|
# * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
|
||||||
|
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
|
||||||
|
# of a database view).
|
||||||
#
|
#
|
||||||
# Examples for counting all:
|
# Examples for counting all:
|
||||||
# Person.count # returns the total count of all people
|
# Person.count # returns the total count of all people
|
||||||
|
@ -71,7 +73,7 @@ module ActiveRecord
|
||||||
#
|
#
|
||||||
# Person.sum('age')
|
# Person.sum('age')
|
||||||
def sum(column_name, options = {})
|
def sum(column_name, options = {})
|
||||||
calculate(:sum, column_name, options) || 0
|
calculate(:sum, column_name, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
|
# This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
|
||||||
|
@ -178,13 +180,23 @@ module ActiveRecord
|
||||||
sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
|
sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
|
||||||
|
|
||||||
sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
|
sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
|
||||||
|
if options[:from]
|
||||||
|
sql << " FROM #{options[:from]} "
|
||||||
|
else
|
||||||
sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround
|
sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround
|
||||||
sql << " FROM #{connection.quote_table_name(table_name)} "
|
sql << " FROM #{connection.quote_table_name(table_name)} "
|
||||||
|
end
|
||||||
|
|
||||||
|
joins = ""
|
||||||
|
add_joins!(joins, options, scope)
|
||||||
|
|
||||||
if merged_includes.any?
|
if merged_includes.any?
|
||||||
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, options[:joins])
|
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, joins)
|
||||||
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
||||||
end
|
end
|
||||||
add_joins!(sql, options, scope)
|
|
||||||
|
sql << joins unless joins.blank?
|
||||||
|
|
||||||
add_conditions!(sql, options[:conditions], scope)
|
add_conditions!(sql, options[:conditions], scope)
|
||||||
add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
|
add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
|
||||||
|
|
||||||
|
@ -205,7 +217,7 @@ module ActiveRecord
|
||||||
|
|
||||||
sql << " ORDER BY #{options[:order]} " if options[:order]
|
sql << " ORDER BY #{options[:order]} " if options[:order]
|
||||||
add_limit!(sql, options, scope)
|
add_limit!(sql, options, scope)
|
||||||
sql << ')' if use_workaround
|
sql << ') AS #{aggregate_alias}_subquery' if use_workaround
|
||||||
sql
|
sql
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -266,6 +278,7 @@ module ActiveRecord
|
||||||
operation = operation.to_s.downcase
|
operation = operation.to_s.downcase
|
||||||
case operation
|
case operation
|
||||||
when 'count' then value.to_i
|
when 'count' then value.to_i
|
||||||
|
when 'sum' then value =~ /\./ ? value.to_f : value.to_i
|
||||||
when 'avg' then value && value.to_f
|
when 'avg' then value && value.to_f
|
||||||
else column ? column.type_cast(value) : value
|
else column ? column.type_cast(value) : value
|
||||||
end
|
end
|
||||||
|
|
|
@ -257,7 +257,10 @@ module ActiveRecord
|
||||||
|
|
||||||
def to_sql
|
def to_sql
|
||||||
column_sql = "#{base.quote_column_name(name)} #{sql_type}"
|
column_sql = "#{base.quote_column_name(name)} #{sql_type}"
|
||||||
add_column_options!(column_sql, :null => null, :default => default) unless type.to_sym == :primary_key
|
column_options = {}
|
||||||
|
column_options[:null] = null unless null.nil?
|
||||||
|
column_options[:default] = default unless default.nil?
|
||||||
|
add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
|
||||||
column_sql
|
column_sql
|
||||||
end
|
end
|
||||||
alias to_s :to_sql
|
alias to_s :to_sql
|
||||||
|
@ -304,8 +307,7 @@ module ActiveRecord
|
||||||
#
|
#
|
||||||
# Available options are (none of these exists by default):
|
# Available options are (none of these exists by default):
|
||||||
# * <tt>:limit</tt> -
|
# * <tt>:limit</tt> -
|
||||||
# Requests a maximum column length (<tt>:string</tt>, <tt>:text</tt>,
|
# Requests a maximum column length. This is number of characters for <tt>:string</tt> and <tt>:text</tt> columns and number of bytes for :binary and :integer columns.
|
||||||
# <tt>:binary</tt> or <tt>:integer</tt> columns only)
|
|
||||||
# * <tt>:default</tt> -
|
# * <tt>:default</tt> -
|
||||||
# The column's default value. Use nil for NULL.
|
# The column's default value. Use nil for NULL.
|
||||||
# * <tt>:null</tt> -
|
# * <tt>:null</tt> -
|
||||||
|
@ -442,9 +444,10 @@ module ActiveRecord
|
||||||
|
|
||||||
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
|
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
|
||||||
# <tt>:updated_at</tt> to the table.
|
# <tt>:updated_at</tt> to the table.
|
||||||
def timestamps
|
def timestamps(*args)
|
||||||
column(:created_at, :datetime)
|
options = args.extract_options!
|
||||||
column(:updated_at, :datetime)
|
column(:created_at, :datetime, options)
|
||||||
|
column(:updated_at, :datetime, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def references(*args)
|
def references(*args)
|
||||||
|
|
|
@ -331,15 +331,26 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def assume_migrated_upto_version(version)
|
def assume_migrated_upto_version(version)
|
||||||
|
version = version.to_i
|
||||||
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
|
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
|
||||||
|
|
||||||
migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
|
migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
|
||||||
versions = Dir['db/migrate/[0-9]*_*.rb'].map do |filename|
|
versions = Dir['db/migrate/[0-9]*_*.rb'].map do |filename|
|
||||||
filename.split('/').last.split('_').first.to_i
|
filename.split('/').last.split('_').first.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" unless migrated.include?(version.to_i)
|
unless migrated.include?(version)
|
||||||
(versions - migrated).select { |v| v < version.to_i }.each do |v|
|
execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
|
||||||
|
end
|
||||||
|
|
||||||
|
inserted = Set.new
|
||||||
|
(versions - migrated).each do |v|
|
||||||
|
if inserted.include?(v)
|
||||||
|
raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
|
||||||
|
elsif v < version
|
||||||
execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
|
execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
|
||||||
|
inserted << v
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -372,13 +383,9 @@ module ActiveRecord
|
||||||
|
|
||||||
def add_column_options!(sql, options) #:nodoc:
|
def add_column_options!(sql, options) #:nodoc:
|
||||||
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
|
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
|
||||||
# must explcitly check for :null to allow change_column to work on migrations
|
# must explicitly check for :null to allow change_column to work on migrations
|
||||||
if options.has_key? :null
|
|
||||||
if options[:null] == false
|
if options[:null] == false
|
||||||
sql << " NOT NULL"
|
sql << " NOT NULL"
|
||||||
else
|
|
||||||
sql << " NULL"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -50,10 +50,7 @@ module ActiveRecord
|
||||||
rescue LoadError => cannot_require_mysql
|
rescue LoadError => cannot_require_mysql
|
||||||
# Use the bundled Ruby/MySQL driver if no driver is already in place
|
# Use the bundled Ruby/MySQL driver if no driver is already in place
|
||||||
begin
|
begin
|
||||||
ActiveRecord::Base.logger.info(
|
ActiveSupport::Deprecation.warn "You're using the Ruby-based MySQL library that ships with Rails. This library will be REMOVED FROM RAILS 2.2. Please switch to the offical mysql gem: `gem install mysql`", caller
|
||||||
"WARNING: You're using the Ruby-based MySQL library that ships with Rails. This library is not suited for production. " +
|
|
||||||
"Please install the C-based MySQL library instead (gem install mysql)."
|
|
||||||
) if ActiveRecord::Base.logger
|
|
||||||
|
|
||||||
require 'active_record/vendor/mysql'
|
require 'active_record/vendor/mysql'
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
|
@ -113,7 +110,8 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_limit(sql_type)
|
def extract_limit(sql_type)
|
||||||
if sql_type =~ /blob|text/i
|
case sql_type
|
||||||
|
when /blob|text/i
|
||||||
case sql_type
|
case sql_type
|
||||||
when /tiny/i
|
when /tiny/i
|
||||||
255
|
255
|
||||||
|
@ -124,6 +122,11 @@ module ActiveRecord
|
||||||
else
|
else
|
||||||
super # we could return 65535 here, but we leave it undecorated by default
|
super # we could return 65535 here, but we leave it undecorated by default
|
||||||
end
|
end
|
||||||
|
when /^bigint/i; 8
|
||||||
|
when /^int/i; 4
|
||||||
|
when /^mediumint/i; 3
|
||||||
|
when /^smallint/i; 2
|
||||||
|
when /^tinyint/i; 1
|
||||||
else
|
else
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
@ -193,10 +196,10 @@ module ActiveRecord
|
||||||
|
|
||||||
def native_database_types #:nodoc:
|
def native_database_types #:nodoc:
|
||||||
{
|
{
|
||||||
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
|
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
|
||||||
:string => { :name => "varchar", :limit => 255 },
|
:string => { :name => "varchar", :limit => 255 },
|
||||||
:text => { :name => "text" },
|
:text => { :name => "text" },
|
||||||
:integer => { :name => "int"},
|
:integer => { :name => "int", :limit => 4 },
|
||||||
:float => { :name => "float" },
|
:float => { :name => "float" },
|
||||||
:decimal => { :name => "decimal" },
|
:decimal => { :name => "decimal" },
|
||||||
:datetime => { :name => "datetime" },
|
:datetime => { :name => "datetime" },
|
||||||
|
@ -336,10 +339,11 @@ module ActiveRecord
|
||||||
|
|
||||||
def add_limit_offset!(sql, options) #:nodoc:
|
def add_limit_offset!(sql, options) #:nodoc:
|
||||||
if limit = options[:limit]
|
if limit = options[:limit]
|
||||||
|
limit = sanitize_limit(limit)
|
||||||
unless offset = options[:offset]
|
unless offset = options[:offset]
|
||||||
sql << " LIMIT #{limit}"
|
sql << " LIMIT #{limit}"
|
||||||
else
|
else
|
||||||
sql << " LIMIT #{offset}, #{limit}"
|
sql << " LIMIT #{offset.to_i}, #{limit}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -439,18 +443,29 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def change_column_default(table_name, column_name, default) #:nodoc:
|
def change_column_default(table_name, column_name, default) #:nodoc:
|
||||||
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
column = column_for(table_name, column_name)
|
||||||
|
change_column table_name, column_name, column.sql_type, :default => default
|
||||||
|
end
|
||||||
|
|
||||||
execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
|
def change_column_null(table_name, column_name, null, default = nil)
|
||||||
|
column = column_for(table_name, column_name)
|
||||||
|
|
||||||
|
unless null || default.nil?
|
||||||
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
||||||
|
end
|
||||||
|
|
||||||
|
change_column table_name, column_name, column.sql_type, :null => null
|
||||||
end
|
end
|
||||||
|
|
||||||
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
||||||
|
column = column_for(table_name, column_name)
|
||||||
|
|
||||||
unless options_include_default?(options)
|
unless options_include_default?(options)
|
||||||
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
|
||||||
options[:default] = column.default
|
options[:default] = column.default
|
||||||
else
|
|
||||||
raise "No such column: #{table_name}.#{column_name}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unless options.has_key?(:null)
|
||||||
|
options[:null] = column.null
|
||||||
end
|
end
|
||||||
|
|
||||||
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
||||||
|
@ -459,8 +474,17 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
||||||
|
options = {}
|
||||||
|
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
||||||
|
options[:default] = column.default
|
||||||
|
options[:null] = column.null
|
||||||
|
else
|
||||||
|
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
||||||
|
end
|
||||||
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
||||||
execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
||||||
|
add_column_options!(rename_column_sql, options)
|
||||||
|
execute(rename_column_sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Maps logical Rails types to MySQL-specific data types.
|
# Maps logical Rails types to MySQL-specific data types.
|
||||||
|
@ -468,14 +492,12 @@ module ActiveRecord
|
||||||
return super unless type.to_s == 'integer'
|
return super unless type.to_s == 'integer'
|
||||||
|
|
||||||
case limit
|
case limit
|
||||||
when 0..3
|
when 1; 'tinyint'
|
||||||
"smallint(#{limit})"
|
when 2; 'smallint'
|
||||||
when 4..8
|
when 3; 'mediumint'
|
||||||
"int(#{limit})"
|
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
||||||
when 9..20
|
when 5..8; 'bigint'
|
||||||
"bigint(#{limit})"
|
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
||||||
else
|
|
||||||
'int(11)'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -525,6 +547,13 @@ module ActiveRecord
|
||||||
def version
|
def version
|
||||||
@version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
@version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def column_for(table_name, column_name)
|
||||||
|
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
|
||||||
|
raise "No such column: #{table_name}.#{column_name}"
|
||||||
|
end
|
||||||
|
column
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,8 +23,8 @@ module ActiveRecord
|
||||||
config = config.symbolize_keys
|
config = config.symbolize_keys
|
||||||
host = config[:host]
|
host = config[:host]
|
||||||
port = config[:port] || 5432
|
port = config[:port] || 5432
|
||||||
username = config[:username].to_s
|
username = config[:username].to_s if config[:username]
|
||||||
password = config[:password].to_s
|
password = config[:password].to_s if config[:password]
|
||||||
|
|
||||||
if config.has_key?(:database)
|
if config.has_key?(:database)
|
||||||
database = config[:database]
|
database = config[:database]
|
||||||
|
@ -47,6 +47,14 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
def extract_limit(sql_type)
|
||||||
|
case sql_type
|
||||||
|
when /^bigint/i; 8
|
||||||
|
when /^smallint/i; 2
|
||||||
|
else super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Extracts the scale from PostgreSQL-specific data types.
|
# Extracts the scale from PostgreSQL-specific data types.
|
||||||
def extract_scale(sql_type)
|
def extract_scale(sql_type)
|
||||||
# Money type has a fixed scale of 2.
|
# Money type has a fixed scale of 2.
|
||||||
|
@ -174,8 +182,8 @@ module ActiveRecord
|
||||||
def self.extract_value_from_default(default)
|
def self.extract_value_from_default(default)
|
||||||
case default
|
case default
|
||||||
# Numeric types
|
# Numeric types
|
||||||
when /\A-?\d+(\.\d*)?\z/
|
when /\A\(?(-?\d+(\.\d*)?\)?)\z/
|
||||||
default
|
$1
|
||||||
# Character types
|
# Character types
|
||||||
when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
|
when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
|
||||||
$1
|
$1
|
||||||
|
@ -319,6 +327,10 @@ module ActiveRecord
|
||||||
has_support
|
has_support
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def supports_insert_with_returning?
|
||||||
|
postgresql_version >= 80200
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the configured supported identifier length supported by PostgreSQL,
|
# Returns the configured supported identifier length supported by PostgreSQL,
|
||||||
# or report the default of 63 on PostgreSQL 7.x.
|
# or report the default of 63 on PostgreSQL 7.x.
|
||||||
def table_alias_length
|
def table_alias_length
|
||||||
|
@ -360,7 +372,7 @@ module ActiveRecord
|
||||||
# There are some incorrectly compiled postgres drivers out there
|
# There are some incorrectly compiled postgres drivers out there
|
||||||
# that don't define PGconn.escape.
|
# that don't define PGconn.escape.
|
||||||
self.class.instance_eval do
|
self.class.instance_eval do
|
||||||
undef_method(:quote_string)
|
remove_method(:quote_string)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
quote_string(s)
|
quote_string(s)
|
||||||
|
@ -411,8 +423,34 @@ module ActiveRecord
|
||||||
|
|
||||||
# Executes an INSERT query and returns the new record's ID
|
# Executes an INSERT query and returns the new record's ID
|
||||||
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
||||||
|
# Extract the table from the insert sql. Yuck.
|
||||||
table = sql.split(" ", 4)[2].gsub('"', '')
|
table = sql.split(" ", 4)[2].gsub('"', '')
|
||||||
super || pk && last_insert_id(table, sequence_name || default_sequence_name(table, pk))
|
|
||||||
|
# Try an insert with 'returning id' if available (PG >= 8.2)
|
||||||
|
if supports_insert_with_returning?
|
||||||
|
pk, sequence_name = *pk_and_sequence_for(table) unless pk
|
||||||
|
if pk
|
||||||
|
id = select_value("#{sql} RETURNING #{quote_column_name(pk)}")
|
||||||
|
clear_query_cache
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Otherwise, insert then grab last_insert_id.
|
||||||
|
if insert_id = super
|
||||||
|
insert_id
|
||||||
|
else
|
||||||
|
# If neither pk nor sequence name is given, look them up.
|
||||||
|
unless pk || sequence_name
|
||||||
|
pk, sequence_name = *pk_and_sequence_for(table)
|
||||||
|
end
|
||||||
|
|
||||||
|
# If a pk is given, fallback to default sequence name.
|
||||||
|
# Don't fetch last insert id for a table without a pk.
|
||||||
|
if pk && sequence_name ||= default_sequence_name(table, pk)
|
||||||
|
last_insert_id(table, sequence_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# create a 2D array representing the result set
|
# create a 2D array representing the result set
|
||||||
|
@ -492,13 +530,13 @@ module ActiveRecord
|
||||||
option_string = options.symbolize_keys.sum do |key, value|
|
option_string = options.symbolize_keys.sum do |key, value|
|
||||||
case key
|
case key
|
||||||
when :owner
|
when :owner
|
||||||
" OWNER = '#{value}'"
|
" OWNER = \"#{value}\""
|
||||||
when :template
|
when :template
|
||||||
" TEMPLATE = #{value}"
|
" TEMPLATE = \"#{value}\""
|
||||||
when :encoding
|
when :encoding
|
||||||
" ENCODING = '#{value}'"
|
" ENCODING = '#{value}'"
|
||||||
when :tablespace
|
when :tablespace
|
||||||
" TABLESPACE = #{value}"
|
" TABLESPACE = \"#{value}\""
|
||||||
when :connection_limit
|
when :connection_limit
|
||||||
" CONNECTION LIMIT = #{value}"
|
" CONNECTION LIMIT = #{value}"
|
||||||
else
|
else
|
||||||
|
@ -506,7 +544,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
execute "CREATE DATABASE #{name}#{option_string}"
|
execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Drops a PostgreSQL database
|
# Drops a PostgreSQL database
|
||||||
|
@ -514,7 +552,15 @@ module ActiveRecord
|
||||||
# Example:
|
# Example:
|
||||||
# drop_database 'matt_development'
|
# drop_database 'matt_development'
|
||||||
def drop_database(name) #:nodoc:
|
def drop_database(name) #:nodoc:
|
||||||
execute "DROP DATABASE IF EXISTS #{name}"
|
if postgresql_version >= 80200
|
||||||
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
execute "DROP DATABASE #{quote_table_name(name)}"
|
||||||
|
rescue ActiveRecord::StatementInvalid
|
||||||
|
@logger.warn "#{name} database doesn't exist." if @logger
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -676,7 +722,7 @@ module ActiveRecord
|
||||||
|
|
||||||
# Renames a table.
|
# Renames a table.
|
||||||
def rename_table(name, new_name)
|
def rename_table(name, new_name)
|
||||||
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
|
execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Adds a new column to the named table.
|
# Adds a new column to the named table.
|
||||||
|
@ -698,7 +744,8 @@ module ActiveRecord
|
||||||
|
|
||||||
begin
|
begin
|
||||||
execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
||||||
rescue ActiveRecord::StatementInvalid
|
rescue ActiveRecord::StatementInvalid => e
|
||||||
|
raise e if postgresql_version > 80000
|
||||||
# This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
|
# This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
|
||||||
begin
|
begin
|
||||||
begin_db_transaction
|
begin_db_transaction
|
||||||
|
@ -743,12 +790,11 @@ module ActiveRecord
|
||||||
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
||||||
return super unless type.to_s == 'integer'
|
return super unless type.to_s == 'integer'
|
||||||
|
|
||||||
if limit.nil? || limit == 4
|
case limit
|
||||||
'integer'
|
when 1..2; 'smallint'
|
||||||
elsif limit < 4
|
when 3..4, nil; 'integer'
|
||||||
'smallint'
|
when 5..8; 'bigint'
|
||||||
else
|
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
|
||||||
'bigint'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -238,6 +238,15 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def change_column_null(table_name, column_name, null, default = nil)
|
||||||
|
unless null || default.nil?
|
||||||
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
||||||
|
end
|
||||||
|
alter_table(table_name) do |definition|
|
||||||
|
definition[column_name].null = null
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
||||||
alter_table(table_name) do |definition|
|
alter_table(table_name) do |definition|
|
||||||
include_default = options_include_default?(options)
|
include_default = options_include_default?(options)
|
||||||
|
@ -251,6 +260,9 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
||||||
|
unless columns(table_name).detect{|c| c.name == column_name.to_s }
|
||||||
|
raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
|
||||||
|
end
|
||||||
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
|
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,10 @@ module ActiveRecord
|
||||||
attr = attr.to_s
|
attr = attr.to_s
|
||||||
|
|
||||||
# The attribute already has an unsaved change.
|
# The attribute already has an unsaved change.
|
||||||
unless changed_attributes.include?(attr)
|
if changed_attributes.include?(attr)
|
||||||
|
old = changed_attributes[attr]
|
||||||
|
changed_attributes.delete(attr) unless field_changed?(attr, old, value)
|
||||||
|
else
|
||||||
old = clone_attribute_value(:read_attribute, attr)
|
old = clone_attribute_value(:read_attribute, attr)
|
||||||
changed_attributes[attr] = old if field_changed?(attr, old, value)
|
changed_attributes[attr] = old if field_changed?(attr, old, value)
|
||||||
end
|
end
|
||||||
|
@ -134,7 +137,9 @@ module ActiveRecord
|
||||||
|
|
||||||
def update_with_dirty
|
def update_with_dirty
|
||||||
if partial_updates?
|
if partial_updates?
|
||||||
update_without_dirty(changed)
|
# Serialized attributes should always be written in case they've been
|
||||||
|
# changed in place.
|
||||||
|
update_without_dirty(changed | self.class.serialized_attributes.keys)
|
||||||
else
|
else
|
||||||
update_without_dirty
|
update_without_dirty
|
||||||
end
|
end
|
||||||
|
@ -142,9 +147,11 @@ module ActiveRecord
|
||||||
|
|
||||||
def field_changed?(attr, old, value)
|
def field_changed?(attr, old, value)
|
||||||
if column = column_for_attribute(attr)
|
if column = column_for_attribute(attr)
|
||||||
if column.type == :integer && column.null && old.nil?
|
if column.type == :integer && column.null && (old.nil? || old == 0)
|
||||||
# For nullable integer columns, NULL gets stored in database for blank (i.e. '') values.
|
# For nullable integer columns, NULL gets stored in database for blank (i.e. '') values.
|
||||||
# Hence we don't record it as a change if the value changes from nil to ''.
|
# Hence we don't record it as a change if the value changes from nil to ''.
|
||||||
|
# If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
|
||||||
|
# be typecast back to 0 (''.to_i => 0)
|
||||||
value = nil if value.blank?
|
value = nil if value.blank?
|
||||||
else
|
else
|
||||||
value = column.type_cast(value)
|
value = column.type_cast(value)
|
||||||
|
|
|
@ -68,6 +68,7 @@ module ActiveRecord
|
||||||
|
|
||||||
def update_with_lock(attribute_names = @attributes.keys) #:nodoc:
|
def update_with_lock(attribute_names = @attributes.keys) #:nodoc:
|
||||||
return update_without_lock(attribute_names) unless locking_enabled?
|
return update_without_lock(attribute_names) unless locking_enabled?
|
||||||
|
return 0 if attribute_names.empty?
|
||||||
|
|
||||||
lock_col = self.class.locking_column
|
lock_col = self.class.locking_column
|
||||||
previous_value = send(lock_col).to_i
|
previous_value = send(lock_col).to_i
|
||||||
|
|
|
@ -238,6 +238,22 @@ module ActiveRecord
|
||||||
# lower than the current schema version: when migrating up, those
|
# lower than the current schema version: when migrating up, those
|
||||||
# never-applied "interleaved" migrations will be automatically applied, and
|
# never-applied "interleaved" migrations will be automatically applied, and
|
||||||
# when migrating down, never-applied "interleaved" migrations will be skipped.
|
# when migrating down, never-applied "interleaved" migrations will be skipped.
|
||||||
|
#
|
||||||
|
# == Timestamped Migrations
|
||||||
|
#
|
||||||
|
# By default, Rails generates migrations that look like:
|
||||||
|
#
|
||||||
|
# 20080717013526_your_migration_name.rb
|
||||||
|
#
|
||||||
|
# The prefix is a generation timestamp (in UTC).
|
||||||
|
#
|
||||||
|
# If you'd prefer to use numeric prefixes, you can turn timestamped migrations
|
||||||
|
# off by setting:
|
||||||
|
#
|
||||||
|
# config.active_record.timestamped_migrations = false
|
||||||
|
#
|
||||||
|
# In environment.rb.
|
||||||
|
#
|
||||||
class Migration
|
class Migration
|
||||||
@@verbose = true
|
@@verbose = true
|
||||||
cattr_accessor :verbose
|
cattr_accessor :verbose
|
||||||
|
@ -369,11 +385,17 @@ module ActiveRecord
|
||||||
Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
|
Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_all_versions
|
||||||
|
Base.connection.select_values("SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).sort
|
||||||
|
end
|
||||||
|
|
||||||
def current_version
|
def current_version
|
||||||
version = Base.connection.select_values(
|
sm_table = schema_migrations_table_name
|
||||||
"SELECT version FROM #{schema_migrations_table_name}"
|
if Base.connection.table_exists?(sm_table)
|
||||||
).map(&:to_i).max rescue nil
|
get_all_versions.max || 0
|
||||||
version || 0
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def proper_table_name(name)
|
def proper_table_name(name)
|
||||||
|
@ -389,7 +411,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_version
|
def current_version
|
||||||
self.class.current_version
|
migrated.last || 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_migration
|
def current_migration
|
||||||
|
@ -399,7 +421,10 @@ module ActiveRecord
|
||||||
def run
|
def run
|
||||||
target = migrations.detect { |m| m.version == @target_version }
|
target = migrations.detect { |m| m.version == @target_version }
|
||||||
raise UnknownMigrationVersionError.new(@target_version) if target.nil?
|
raise UnknownMigrationVersionError.new(@target_version) if target.nil?
|
||||||
|
unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
|
||||||
target.migrate(@direction)
|
target.migrate(@direction)
|
||||||
|
record_version_state_after_migrating(target.version)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def migrate
|
def migrate
|
||||||
|
@ -470,17 +495,19 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def migrated
|
def migrated
|
||||||
sm_table = self.class.schema_migrations_table_name
|
@migrated_versions ||= self.class.get_all_versions
|
||||||
Base.connection.select_values("SELECT version FROM #{sm_table}").map(&:to_i).sort
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def record_version_state_after_migrating(version)
|
def record_version_state_after_migrating(version)
|
||||||
sm_table = self.class.schema_migrations_table_name
|
sm_table = self.class.schema_migrations_table_name
|
||||||
|
|
||||||
|
@migrated_versions ||= []
|
||||||
if down?
|
if down?
|
||||||
|
@migrated_versions.delete(version.to_i)
|
||||||
Base.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'")
|
Base.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'")
|
||||||
else
|
else
|
||||||
|
@migrated_versions.push(version.to_i).sort!
|
||||||
Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')")
|
Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -82,6 +82,7 @@ module ActiveRecord
|
||||||
# expected_options = { :conditions => { :colored => 'red' } }
|
# expected_options = { :conditions => { :colored => 'red' } }
|
||||||
# assert_equal expected_options, Shirt.colored('red').proxy_options
|
# assert_equal expected_options, Shirt.colored('red').proxy_options
|
||||||
def named_scope(name, options = {}, &block)
|
def named_scope(name, options = {}, &block)
|
||||||
|
name = name.to_sym
|
||||||
scopes[name] = lambda do |parent_scope, *args|
|
scopes[name] = lambda do |parent_scope, *args|
|
||||||
Scope.new(parent_scope, case options
|
Scope.new(parent_scope, case options
|
||||||
when Hash
|
when Hash
|
||||||
|
@ -102,7 +103,7 @@ module ActiveRecord
|
||||||
attr_reader :proxy_scope, :proxy_options
|
attr_reader :proxy_scope, :proxy_options
|
||||||
|
|
||||||
[].methods.each do |m|
|
[].methods.each do |m|
|
||||||
unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last|empty?)/
|
unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|^find$|count|sum|average|maximum|minimum|paginate|first|last|empty\?|respond_to\?)/
|
||||||
delegate m, :to => :proxy_found
|
delegate m, :to => :proxy_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -139,6 +140,10 @@ module ActiveRecord
|
||||||
@found ? @found.empty? : count.zero?
|
@found ? @found.empty? : count.zero?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def respond_to?(method, include_private = false)
|
||||||
|
super || @proxy_scope.respond_to?(method, include_private)
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
def proxy_found
|
def proxy_found
|
||||||
@found || load_found
|
@found || load_found
|
||||||
|
|
|
@ -189,7 +189,9 @@ module ActiveRecord
|
||||||
|
|
||||||
def add_observer!(klass)
|
def add_observer!(klass)
|
||||||
klass.add_observer(self)
|
klass.add_observer(self)
|
||||||
klass.class_eval 'def after_find() end' unless klass.respond_to?(:after_find)
|
if respond_to?(:after_find) && !klass.method_defined?(:after_find)
|
||||||
|
klass.class_eval 'def after_find() end'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,11 +22,22 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_queries(num = 1)
|
def assert_sql(*patterns_to_match)
|
||||||
$query_count = 0
|
$queries_executed = []
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
|
failed_patterns = []
|
||||||
|
patterns_to_match.each do |pattern|
|
||||||
|
failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
|
||||||
|
end
|
||||||
|
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found."
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_queries(num = 1)
|
||||||
|
$queries_executed = []
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed."
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_no_queries(&block)
|
def assert_no_queries(&block)
|
||||||
|
|
|
@ -482,6 +482,7 @@ module ActiveRecord
|
||||||
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
|
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
|
||||||
# validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least %d character"
|
# validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least %d character"
|
||||||
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with %d characters... don't play me."
|
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with %d characters... don't play me."
|
||||||
|
# validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# Configuration options:
|
# Configuration options:
|
||||||
|
@ -492,7 +493,6 @@ module ActiveRecord
|
||||||
# * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
|
# * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
|
||||||
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
|
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
|
||||||
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
|
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
|
||||||
#
|
|
||||||
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)").
|
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)").
|
||||||
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)").
|
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)").
|
||||||
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %d characters)").
|
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %d characters)").
|
||||||
|
@ -504,12 +504,16 @@ module ActiveRecord
|
||||||
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
||||||
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
||||||
# method, proc or string should return or evaluate to a true or false value.
|
# method, proc or string should return or evaluate to a true or false value.
|
||||||
|
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to
|
||||||
|
# count words as in above example.)
|
||||||
|
# Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
|
||||||
def validates_length_of(*attrs)
|
def validates_length_of(*attrs)
|
||||||
# Merge given options with defaults.
|
# Merge given options with defaults.
|
||||||
options = {
|
options = {
|
||||||
:too_long => ActiveRecord::Errors.default_error_messages[:too_long],
|
:too_long => ActiveRecord::Errors.default_error_messages[:too_long],
|
||||||
:too_short => ActiveRecord::Errors.default_error_messages[:too_short],
|
:too_short => ActiveRecord::Errors.default_error_messages[:too_short],
|
||||||
:wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length]
|
:wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length],
|
||||||
|
:tokenizer => lambda {|value| value.split(//)}
|
||||||
}.merge(DEFAULT_VALIDATION_OPTIONS)
|
}.merge(DEFAULT_VALIDATION_OPTIONS)
|
||||||
options.update(attrs.extract_options!.symbolize_keys)
|
options.update(attrs.extract_options!.symbolize_keys)
|
||||||
|
|
||||||
|
@ -536,7 +540,7 @@ module ActiveRecord
|
||||||
too_long = options[:too_long] % option_value.end
|
too_long = options[:too_long] % option_value.end
|
||||||
|
|
||||||
validates_each(attrs, options) do |record, attr, value|
|
validates_each(attrs, options) do |record, attr, value|
|
||||||
value = value.split(//) if value.kind_of?(String)
|
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
||||||
if value.nil? or value.size < option_value.begin
|
if value.nil? or value.size < option_value.begin
|
||||||
record.errors.add(attr, too_short)
|
record.errors.add(attr, too_short)
|
||||||
elsif value.size > option_value.end
|
elsif value.size > option_value.end
|
||||||
|
@ -553,7 +557,7 @@ module ActiveRecord
|
||||||
message = (options[:message] || options[message_options[option]]) % option_value
|
message = (options[:message] || options[message_options[option]]) % option_value
|
||||||
|
|
||||||
validates_each(attrs, options) do |record, attr, value|
|
validates_each(attrs, options) do |record, attr, value|
|
||||||
value = value.split(//) if value.kind_of?(String)
|
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
||||||
record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
|
record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -614,14 +618,20 @@ module ActiveRecord
|
||||||
# class (which has a database table to query from).
|
# class (which has a database table to query from).
|
||||||
finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
|
finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
|
||||||
|
|
||||||
if value.nil? || (configuration[:case_sensitive] || !finder_class.columns_hash[attr_name.to_s].text?)
|
is_text_column = finder_class.columns_hash[attr_name.to_s].text?
|
||||||
|
|
||||||
|
if !value.nil? && is_text_column
|
||||||
|
value = value.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
if value.nil? || (configuration[:case_sensitive] || !is_text_column)
|
||||||
condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}"
|
condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}"
|
||||||
condition_params = [value]
|
condition_params = [value]
|
||||||
else
|
else
|
||||||
# sqlite has case sensitive SELECT query, while MySQL/Postgresql don't.
|
# sqlite has case sensitive SELECT query, while MySQL/Postgresql don't.
|
||||||
# Hence, this is needed only for sqlite.
|
# Hence, this is needed only for sqlite.
|
||||||
condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}"
|
condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}"
|
||||||
condition_params = [value.downcase]
|
condition_params = [value.chars.downcase]
|
||||||
end
|
end
|
||||||
|
|
||||||
if scope = configuration[:scope]
|
if scope = configuration[:scope]
|
||||||
|
@ -851,7 +861,7 @@ module ActiveRecord
|
||||||
raw_value = raw_value.to_i
|
raw_value = raw_value.to_i
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
raw_value = Kernel.Float(raw_value.to_s)
|
raw_value = Kernel.Float(raw_value)
|
||||||
rescue ArgumentError, TypeError
|
rescue ArgumentError, TypeError
|
||||||
record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
|
record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
|
||||||
next
|
next
|
||||||
|
|
|
@ -2,7 +2,7 @@ module ActiveRecord
|
||||||
module VERSION #:nodoc:
|
module VERSION #:nodoc:
|
||||||
MAJOR = 2
|
MAJOR = 2
|
||||||
MINOR = 1
|
MINOR = 1
|
||||||
TINY = 0
|
TINY = 1
|
||||||
|
|
||||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,8 +13,8 @@ class PostgresqlActiveSchemaTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_create_database_with_encoding
|
def test_create_database_with_encoding
|
||||||
assert_equal "CREATE DATABASE matt ENCODING = 'utf8'", create_database(:matt)
|
assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt)
|
||||||
assert_equal "CREATE DATABASE aimonetti ENCODING = 'latin1'", create_database(:aimonetti, :encoding => :latin1)
|
assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -118,7 +118,7 @@ class AdapterTest < ActiveRecord::TestCase
|
||||||
sql_inject = "1, 7 procedure help()"
|
sql_inject = "1, 7 procedure help()"
|
||||||
if current_adapter?(:MysqlAdapter)
|
if current_adapter?(:MysqlAdapter)
|
||||||
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
|
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
|
||||||
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
|
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=> '1 ; DROP TABLE USERS', :offset=>7)
|
||||||
else
|
else
|
||||||
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
|
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
|
||||||
assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
|
assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
|
||||||
|
|
|
@ -409,4 +409,23 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||||
sponsor.sponsorable = new_member
|
sponsor.sponsorable = new_member
|
||||||
assert_equal nil, sponsor.sponsorable_id
|
assert_equal nil, sponsor.sponsorable_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_save_fails_for_invalid_belongs_to
|
||||||
|
assert log = AuditLog.create(:developer_id=>0,:message=>"")
|
||||||
|
|
||||||
|
log.developer = Developer.new
|
||||||
|
assert !log.developer.valid?
|
||||||
|
assert !log.valid?
|
||||||
|
assert !log.save
|
||||||
|
assert_equal "is invalid", log.errors.on("developer")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_save_succeeds_for_invalid_belongs_to_with_validate_false
|
||||||
|
assert log = AuditLog.create(:developer_id=>0,:message=>"")
|
||||||
|
|
||||||
|
log.unvalidated_developer = Developer.new
|
||||||
|
assert !log.unvalidated_developer.valid?
|
||||||
|
assert log.valid?
|
||||||
|
assert log.save
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,7 @@ require 'models/topic'
|
||||||
require 'models/reply'
|
require 'models/reply'
|
||||||
|
|
||||||
class CascadedEagerLoadingTest < ActiveRecord::TestCase
|
class CascadedEagerLoadingTest < ActiveRecord::TestCase
|
||||||
fixtures :authors, :mixins, :companies, :posts, :topics
|
fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments, :categorizations
|
||||||
|
|
||||||
def test_eager_association_loading_with_cascaded_two_levels
|
def test_eager_association_loading_with_cascaded_two_levels
|
||||||
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
|
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
|
||||||
|
@ -68,6 +68,18 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_eager_association_loading_with_has_many_sti_and_subclasses
|
||||||
|
silly = SillyReply.new(:title => "gaga", :content => "boo-boo", :parent_id => 1)
|
||||||
|
silly.parent_id = 1
|
||||||
|
assert silly.save
|
||||||
|
|
||||||
|
topics = Topic.find(:all, :include => :replies, :order => 'topics.id, replies_topics.id')
|
||||||
|
assert_no_queries do
|
||||||
|
assert_equal 2, topics[0].replies.size
|
||||||
|
assert_equal 0, topics[1].replies.size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_eager_association_loading_with_belongs_to_sti
|
def test_eager_association_loading_with_belongs_to_sti
|
||||||
replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
|
replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
|
||||||
assert replies.include?(topics(:second))
|
assert replies.include?(topics(:second))
|
||||||
|
|
36
vendor/rails/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
vendored
Normal file
36
vendor/rails/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
require 'cases/helper'
|
||||||
|
require 'models/post'
|
||||||
|
require 'models/tagging'
|
||||||
|
|
||||||
|
module Namespaced
|
||||||
|
class Post < ActiveRecord::Base
|
||||||
|
set_table_name 'posts'
|
||||||
|
has_one :tagging, :as => :taggable, :class_name => 'Tagging'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
|
||||||
|
|
||||||
|
def setup
|
||||||
|
generate_test_objects
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_test_objects
|
||||||
|
post = Namespaced::Post.create( :title => 'Great stuff', :body => 'This is not', :author_id => 1 )
|
||||||
|
tagging = Tagging.create( :taggable => post )
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_class_names
|
||||||
|
old = ActiveRecord::Base.store_full_sti_class
|
||||||
|
|
||||||
|
ActiveRecord::Base.store_full_sti_class = false
|
||||||
|
post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
|
||||||
|
assert_nil post.tagging
|
||||||
|
|
||||||
|
ActiveRecord::Base.store_full_sti_class = true
|
||||||
|
post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
|
||||||
|
assert_equal 'Tagging', post.tagging.class.name
|
||||||
|
ensure
|
||||||
|
ActiveRecord::Base.store_full_sti_class = old
|
||||||
|
end
|
||||||
|
end
|
|
@ -14,11 +14,14 @@ require 'models/job'
|
||||||
require 'models/subscriber'
|
require 'models/subscriber'
|
||||||
require 'models/subscription'
|
require 'models/subscription'
|
||||||
require 'models/book'
|
require 'models/book'
|
||||||
|
require 'models/developer'
|
||||||
|
require 'models/project'
|
||||||
|
|
||||||
class EagerAssociationTest < ActiveRecord::TestCase
|
class EagerAssociationTest < ActiveRecord::TestCase
|
||||||
fixtures :posts, :comments, :authors, :categories, :categories_posts,
|
fixtures :posts, :comments, :authors, :categories, :categories_posts,
|
||||||
:companies, :accounts, :tags, :taggings, :people, :readers,
|
:companies, :accounts, :tags, :taggings, :people, :readers,
|
||||||
:owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books
|
:owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
|
||||||
|
:developers, :projects, :developers_projects
|
||||||
|
|
||||||
def test_loading_with_one_association
|
def test_loading_with_one_association
|
||||||
posts = Post.find(:all, :include => :comments)
|
posts = Post.find(:all, :include => :comments)
|
||||||
|
@ -35,6 +38,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
||||||
assert_equal Post.find(1).last_comment, post.last_comment
|
assert_equal Post.find(1).last_comment, post.last_comment
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_loading_with_one_association_with_non_preload
|
||||||
|
posts = Post.find(:all, :include => :last_comment, :order => 'comments.id DESC')
|
||||||
|
post = posts.find { |p| p.id == 1 }
|
||||||
|
assert_equal Post.find(1).last_comment, post.last_comment
|
||||||
|
end
|
||||||
|
|
||||||
def test_loading_conditions_with_or
|
def test_loading_conditions_with_or
|
||||||
posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
|
posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
|
||||||
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
|
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
|
||||||
|
@ -556,6 +565,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
||||||
assert_nothing_raised { Post.find(:all, :include => 'comments') }
|
assert_nothing_raised { Post.find(:all, :include => 'comments') }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_eager_with_floating_point_numbers
|
||||||
|
assert_queries(2) do
|
||||||
|
# Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query
|
||||||
|
Comment.find :all, :conditions => "123.456 = 123.456", :include => :post
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_preconfigured_includes_with_belongs_to
|
def test_preconfigured_includes_with_belongs_to
|
||||||
author = posts(:welcome).author_with_posts
|
author = posts(:welcome).author_with_posts
|
||||||
assert_no_queries {assert_equal 5, author.posts.size}
|
assert_no_queries {assert_equal 5, author.posts.size}
|
||||||
|
@ -609,4 +625,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
||||||
Comment.find :all, :include => :post
|
Comment.find :all, :include => :post
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_conditions_on_join_table_with_include_and_limit
|
||||||
|
assert_equal 3, Developer.find(:all, :include => 'projects', :conditions => 'developers_projects.access_level = 1', :limit => 5).size
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_order_on_join_table_with_include_and_limit
|
||||||
|
assert_equal 5, Developer.find(:all, :include => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).size
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -70,7 +70,7 @@ end
|
||||||
|
|
||||||
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||||
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
|
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
|
||||||
:parrots, :pirates, :treasures, :price_estimates
|
:parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
|
||||||
|
|
||||||
def test_has_and_belongs_to_many
|
def test_has_and_belongs_to_many
|
||||||
david = Developer.find(1)
|
david = Developer.find(1)
|
||||||
|
@ -299,6 +299,17 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||||
assert_equal 3, projects(:active_record, :reload).developers.size
|
assert_equal 3, projects(:active_record, :reload).developers.size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_uniq_option_prevents_duplicate_push
|
||||||
|
project = projects(:active_record)
|
||||||
|
project.developers << developers(:jamis)
|
||||||
|
project.developers << developers(:david)
|
||||||
|
assert_equal 3, project.developers.size
|
||||||
|
|
||||||
|
project.developers << developers(:david)
|
||||||
|
project.developers << developers(:jamis)
|
||||||
|
assert_equal 3, project.developers.size
|
||||||
|
end
|
||||||
|
|
||||||
def test_deleting
|
def test_deleting
|
||||||
david = Developer.find(1)
|
david = Developer.find(1)
|
||||||
active_record = Project.find(1)
|
active_record = Project.find(1)
|
||||||
|
@ -439,6 +450,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||||
assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
|
assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations
|
||||||
|
# interpolate once:
|
||||||
|
assert_equal [developers(:david), developers(:jamis), developers(:poor_jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation"
|
||||||
|
# interpolate again, for a different project id
|
||||||
|
assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation"
|
||||||
|
end
|
||||||
|
|
||||||
def test_find_in_association_with_custom_finder_sql_and_string_id
|
def test_find_in_association_with_custom_finder_sql_and_string_id
|
||||||
assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
|
assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
|
||||||
end
|
end
|
||||||
|
@ -629,8 +647,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||||
developer.save
|
developer.save
|
||||||
developer.reload
|
developer.reload
|
||||||
assert_equal 2, developer.projects.length
|
assert_equal 2, developer.projects.length
|
||||||
assert_equal projects(:active_record), developer.projects[0]
|
assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
|
||||||
assert_equal projects(:action_controller), developer.projects[1]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_assign_ids_ignoring_blanks
|
def test_assign_ids_ignoring_blanks
|
||||||
|
@ -639,8 +656,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||||
developer.save
|
developer.save
|
||||||
developer.reload
|
developer.reload
|
||||||
assert_equal 2, developer.projects.length
|
assert_equal 2, developer.projects.length
|
||||||
assert_equal projects(:active_record), developer.projects[0]
|
assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
|
||||||
assert_equal projects(:action_controller), developer.projects[1]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_select_limited_ids_list
|
def test_select_limited_ids_list
|
||||||
|
@ -681,4 +697,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||||
assert_equal developer, project.developers.find(:first)
|
assert_equal developer, project.developers.find(:first)
|
||||||
assert_equal project, developer.projects.find(:first)
|
assert_equal project, developer.projects.find(:first)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_dynamic_find_should_respect_association_include
|
||||||
|
# SQL error in sort clause if :include is not included
|
||||||
|
# due to Unknown column 'authors.id'
|
||||||
|
assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title('Welcome to the weblog')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,7 @@ require 'models/reader'
|
||||||
class HasManyAssociationsTest < ActiveRecord::TestCase
|
class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||||
fixtures :accounts, :categories, :companies, :developers, :projects,
|
fixtures :accounts, :categories, :companies, :developers, :projects,
|
||||||
:developers_projects, :topics, :authors, :comments, :author_addresses,
|
:developers_projects, :topics, :authors, :comments, :author_addresses,
|
||||||
:people, :posts
|
:people, :posts, :readers
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
Client.destroyed_client_ids.clear
|
Client.destroyed_client_ids.clear
|
||||||
|
@ -37,15 +37,21 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_counting_with_single_conditions
|
def test_counting_with_single_conditions
|
||||||
assert_equal 2, Firm.find(:first).plain_clients.count(:conditions => '1=1')
|
assert_equal 1, Firm.find(:first).plain_clients.count(:conditions => ['name=?', "Microsoft"])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_counting_with_single_hash
|
def test_counting_with_single_hash
|
||||||
assert_equal 2, Firm.find(:first).plain_clients.count(:conditions => '1=1')
|
assert_equal 1, Firm.find(:first).plain_clients.count(:conditions => {:name => "Microsoft"})
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_counting_with_column_name_and_hash
|
def test_counting_with_column_name_and_hash
|
||||||
assert_equal 2, Firm.find(:first).plain_clients.count(:all, :conditions => '1=1')
|
assert_equal 2, Firm.find(:first).plain_clients.count(:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_counting_with_association_limit
|
||||||
|
firm = companies(:first_firm)
|
||||||
|
assert_equal firm.limited_clients.length, firm.limited_clients.size
|
||||||
|
assert_equal firm.limited_clients.length, firm.limited_clients.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_finding
|
def test_finding
|
||||||
|
@ -342,6 +348,34 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||||
assert new_firm.new_record?
|
assert new_firm.new_record?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_invalid_adding_with_validate_false
|
||||||
|
firm = Firm.find(:first)
|
||||||
|
client = Client.new
|
||||||
|
firm.unvalidated_clients_of_firm << client
|
||||||
|
|
||||||
|
assert firm.valid?
|
||||||
|
assert !client.valid?
|
||||||
|
assert firm.save
|
||||||
|
assert client.new_record?
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_valid_adding_with_validate_false
|
||||||
|
no_of_clients = Client.count
|
||||||
|
|
||||||
|
firm = Firm.find(:first)
|
||||||
|
client = Client.new("name" => "Apple")
|
||||||
|
|
||||||
|
assert firm.valid?
|
||||||
|
assert client.valid?
|
||||||
|
assert client.new_record?
|
||||||
|
|
||||||
|
firm.unvalidated_clients_of_firm << client
|
||||||
|
|
||||||
|
assert firm.save
|
||||||
|
assert !client.new_record?
|
||||||
|
assert_equal no_of_clients+1, Client.count
|
||||||
|
end
|
||||||
|
|
||||||
def test_build
|
def test_build
|
||||||
company = companies(:first_firm)
|
company = companies(:first_firm)
|
||||||
new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
|
new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
|
||||||
|
@ -356,6 +390,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||||
assert_equal 2, company.clients_of_firm(true).size
|
assert_equal 2, company.clients_of_firm(true).size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_collection_size_after_building
|
||||||
|
company = companies(:first_firm) # company already has one client
|
||||||
|
company.clients_of_firm.build("name" => "Another Client")
|
||||||
|
company.clients_of_firm.build("name" => "Yet Another Client")
|
||||||
|
assert_equal 3, company.clients_of_firm.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_collection_size_twice_for_regressions
|
||||||
|
post = posts(:thinking)
|
||||||
|
assert_equal 0, post.readers.size
|
||||||
|
# This test needs a post that has no readers, we assert it to ensure it holds,
|
||||||
|
# but need to reload the post because the very call to #size hides the bug.
|
||||||
|
post.reload
|
||||||
|
post.readers.build
|
||||||
|
size1 = post.readers.size
|
||||||
|
size2 = post.readers.size
|
||||||
|
assert_equal size1, size2
|
||||||
|
end
|
||||||
|
|
||||||
def test_build_many
|
def test_build_many
|
||||||
company = companies(:first_firm)
|
company = companies(:first_firm)
|
||||||
new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
|
new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
|
||||||
|
@ -386,6 +439,37 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||||
assert_equal 2, first_topic.replies.to_ary.size
|
assert_equal 2, first_topic.replies.to_ary.size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_build_via_block
|
||||||
|
company = companies(:first_firm)
|
||||||
|
new_client = assert_no_queries { company.clients_of_firm.build {|client| client.name = "Another Client" } }
|
||||||
|
assert !company.clients_of_firm.loaded?
|
||||||
|
|
||||||
|
assert_equal "Another Client", new_client.name
|
||||||
|
assert new_client.new_record?
|
||||||
|
assert_equal new_client, company.clients_of_firm.last
|
||||||
|
company.name += '-changed'
|
||||||
|
assert_queries(2) { assert company.save }
|
||||||
|
assert !new_client.new_record?
|
||||||
|
assert_equal 2, company.clients_of_firm(true).size
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_build_many_via_block
|
||||||
|
company = companies(:first_firm)
|
||||||
|
new_clients = assert_no_queries do
|
||||||
|
company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
|
||||||
|
client.name = "changed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal 2, new_clients.size
|
||||||
|
assert_equal "changed", new_clients.first.name
|
||||||
|
assert_equal "changed", new_clients.last.name
|
||||||
|
|
||||||
|
company.name += '-changed'
|
||||||
|
assert_queries(3) { assert company.save }
|
||||||
|
assert_equal 3, company.clients_of_firm(true).size
|
||||||
|
end
|
||||||
|
|
||||||
def test_create_without_loading_association
|
def test_create_without_loading_association
|
||||||
first_firm = companies(:first_firm)
|
first_firm = companies(:first_firm)
|
||||||
Firm.column_names
|
Firm.column_names
|
||||||
|
@ -929,4 +1013,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||||
assert firm.clients.loaded?
|
assert firm.clients.loaded?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_joins_with_namespaced_model_should_use_correct_type
|
||||||
|
old = ActiveRecord::Base.store_full_sti_class
|
||||||
|
ActiveRecord::Base.store_full_sti_class = true
|
||||||
|
|
||||||
|
firm = Namespaced::Firm.create({ :name => 'Some Company' })
|
||||||
|
firm.clients.create({ :name => 'Some Client' })
|
||||||
|
|
||||||
|
stats = Namespaced::Firm.find(firm.id, {
|
||||||
|
:select => "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
|
||||||
|
:joins => :clients,
|
||||||
|
:group => "#{Namespaced::Firm.table_name}.id"
|
||||||
|
})
|
||||||
|
assert_equal 1, stats.num_clients.to_i
|
||||||
|
|
||||||
|
ensure
|
||||||
|
ActiveRecord::Base.store_full_sti_class = old
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -187,4 +187,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
||||||
post.people_with_callbacks.clear
|
post.people_with_callbacks.clear
|
||||||
assert_equal (%w(Michael David Julian Roger) * 2).sort, log.last(8).collect(&:last).sort
|
assert_equal (%w(Michael David Julian Roger) * 2).sort, log.last(8).collect(&:last).sort
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_dynamic_find_should_respect_association_include
|
||||||
|
# SQL error in sort clause if :include is not included
|
||||||
|
# due to Unknown column 'comments.id'
|
||||||
|
assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title('Welcome to the weblog')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_count_with_include_should_alias_join_table
|
||||||
|
assert_equal 2, people(:michael).posts.count(:include => :readers)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -72,6 +72,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
||||||
assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
|
assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_natural_assignment_to_already_associated_record
|
||||||
|
company = companies(:first_firm)
|
||||||
|
account = accounts(:signals37)
|
||||||
|
assert_equal company.account, account
|
||||||
|
company.account = account
|
||||||
|
company.reload
|
||||||
|
account.reload
|
||||||
|
assert_equal company.account, account
|
||||||
|
end
|
||||||
|
|
||||||
def test_assignment_without_replacement
|
def test_assignment_without_replacement
|
||||||
apple = Firm.create("name" => "Apple")
|
apple = Firm.create("name" => "Apple")
|
||||||
citibank = Account.create("credit_limit" => 10)
|
citibank = Account.create("credit_limit" => 10)
|
||||||
|
@ -275,6 +285,18 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
||||||
assert_equal "is invalid", firm.errors.on("account")
|
assert_equal "is invalid", firm.errors.on("account")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def test_save_succeeds_for_invalid_has_one_with_validate_false
|
||||||
|
firm = Firm.find(:first)
|
||||||
|
assert firm.valid?
|
||||||
|
|
||||||
|
firm.unvalidated_account = Account.new
|
||||||
|
|
||||||
|
assert !firm.unvalidated_account.valid?
|
||||||
|
assert firm.valid?
|
||||||
|
assert firm.save
|
||||||
|
end
|
||||||
|
|
||||||
def test_assignment_before_either_saved
|
def test_assignment_before_either_saved
|
||||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||||
firm.account = a = Account.new("credit_limit" => 1000)
|
firm.account = a = Account.new("credit_limit" => 1000)
|
||||||
|
|
|
@ -50,13 +50,17 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_has_one_through_eager_loading
|
def test_has_one_through_eager_loading
|
||||||
members = Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"])
|
members = assert_queries(3) do #base table, through table, clubs table
|
||||||
|
Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"])
|
||||||
|
end
|
||||||
assert_equal 1, members.size
|
assert_equal 1, members.size
|
||||||
assert_not_nil assert_no_queries {members[0].club}
|
assert_not_nil assert_no_queries {members[0].club}
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_has_one_through_eager_loading_through_polymorphic
|
def test_has_one_through_eager_loading_through_polymorphic
|
||||||
members = Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"])
|
members = assert_queries(3) do #base table, through table, clubs table
|
||||||
|
Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"])
|
||||||
|
end
|
||||||
assert_equal 1, members.size
|
assert_equal 1, members.size
|
||||||
assert_not_nil assert_no_queries {members[0].sponsor_club}
|
assert_not_nil assert_no_queries {members[0].sponsor_club}
|
||||||
end
|
end
|
||||||
|
@ -71,4 +75,39 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
||||||
assert_not_nil assert_no_queries {clubs[0].sponsored_member}
|
assert_not_nil assert_no_queries {clubs[0].sponsored_member}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_has_one_through_nonpreload_eagerloading
|
||||||
|
members = assert_queries(1) do
|
||||||
|
Member.find(:all, :include => :club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
|
||||||
|
end
|
||||||
|
assert_equal 1, members.size
|
||||||
|
assert_not_nil assert_no_queries {members[0].club}
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_has_one_through_nonpreload_eager_loading_through_polymorphic
|
||||||
|
members = assert_queries(1) do
|
||||||
|
Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
|
||||||
|
end
|
||||||
|
assert_equal 1, members.size
|
||||||
|
assert_not_nil assert_no_queries {members[0].sponsor_club}
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
|
||||||
|
Sponsor.new(:sponsor_club => clubs(:crazy_club), :sponsorable => members(:groucho)).save!
|
||||||
|
members = assert_queries(1) do
|
||||||
|
Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC') #force fallback
|
||||||
|
end
|
||||||
|
assert_equal 1, members.size
|
||||||
|
assert_not_nil assert_no_queries { members[0].sponsor_club }
|
||||||
|
assert_equal clubs(:crazy_club), members[0].sponsor_club
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_uninitialized_has_one_through_should_return_nil_for_unsaved_record
|
||||||
|
assert_nil Member.new.club
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_assigning_association_correctly_assigns_target
|
||||||
|
new_member = Member.create(:name => "Chris")
|
||||||
|
new_member.club = new_club = Club.create(:name => "LRUG")
|
||||||
|
assert_equal new_club, new_member.club.target
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -694,6 +694,13 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
|
||||||
assert ! david.categories.include?(category)
|
assert ! david.categories.include?(category)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_has_many_through_goes_through_all_sti_classes
|
||||||
|
sub_sti_post = SubStiPost.create!(:title => 'test', :body => 'test', :author_id => 1)
|
||||||
|
new_comment = sub_sti_post.comments.create(:body => 'test')
|
||||||
|
|
||||||
|
assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
# create dynamic Post models to allow different dependency options
|
# create dynamic Post models to allow different dependency options
|
||||||
def find_post_with_dependency(post_id, association, association_name, dependency)
|
def find_post_with_dependency(post_id, association, association_name, dependency)
|
||||||
|
|
|
@ -27,7 +27,7 @@ require 'models/sponsor'
|
||||||
|
|
||||||
class AssociationsTest < ActiveRecord::TestCase
|
class AssociationsTest < ActiveRecord::TestCase
|
||||||
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
|
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
|
||||||
:computers
|
:computers, :people, :readers
|
||||||
|
|
||||||
def test_include_with_order_works
|
def test_include_with_order_works
|
||||||
assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
|
assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
|
||||||
|
@ -45,7 +45,7 @@ class AssociationsTest < ActiveRecord::TestCase
|
||||||
assert_equal [], person.readers.find(:all)
|
assert_equal [], person.readers.find(:all)
|
||||||
person.save!
|
person.save!
|
||||||
reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
|
reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
|
||||||
assert_equal [reader], person.readers.find(:all)
|
assert person.readers.find(reader.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_force_reload
|
def test_force_reload
|
||||||
|
|
|
@ -145,7 +145,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
||||||
record[:written_on] = utc_time
|
record[:written_on] = utc_time
|
||||||
assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time
|
assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time
|
||||||
assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone
|
assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone
|
||||||
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
|
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
|
||||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly
|
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -156,7 +156,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
||||||
record = @target.new
|
record = @target.new
|
||||||
record.written_on = utc_time
|
record.written_on = utc_time
|
||||||
assert_equal utc_time, record.written_on
|
assert_equal utc_time, record.written_on
|
||||||
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
||||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -168,7 +168,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
||||||
record = @target.new
|
record = @target.new
|
||||||
record.written_on = cst_time
|
record.written_on = cst_time
|
||||||
assert_equal utc_time, record.written_on
|
assert_equal utc_time, record.written_on
|
||||||
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
||||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -181,7 +181,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
||||||
record = @target.new
|
record = @target.new
|
||||||
record.written_on = time_string
|
record.written_on = time_string
|
||||||
assert_equal Time.zone.parse(time_string), record.written_on
|
assert_equal Time.zone.parse(time_string), record.written_on
|
||||||
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
||||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -202,7 +202,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
||||||
record = @target.new
|
record = @target.new
|
||||||
record.written_on = time_string
|
record.written_on = time_string
|
||||||
assert_equal Time.zone.parse(time_string), record.written_on
|
assert_equal Time.zone.parse(time_string), record.written_on
|
||||||
assert_equal TimeZone[timezone_offset], record.written_on.time_zone
|
assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone
|
||||||
assert_equal Time.utc(2008, 1, 1), record.written_on.time
|
assert_equal Time.utc(2008, 1, 1), record.written_on.time
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -214,7 +214,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
||||||
record = @target.new
|
record = @target.new
|
||||||
record.written_on = utc_time.in_time_zone
|
record.written_on = utc_time.in_time_zone
|
||||||
assert_equal utc_time, record.written_on
|
assert_equal utc_time, record.written_on
|
||||||
assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
||||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -228,7 +228,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
||||||
old_zone = Time.zone
|
old_zone = Time.zone
|
||||||
old_tz = ActiveRecord::Base.time_zone_aware_attributes
|
old_tz = ActiveRecord::Base.time_zone_aware_attributes
|
||||||
|
|
||||||
Time.zone = zone ? TimeZone[zone] : nil
|
Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
|
||||||
ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
|
ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue