mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Merge commit 'mainstream/master'
Conflicts: actionpack/lib/action_controller/request.rb actionpack/lib/action_controller/resources.rb
This commit is contained in:
@@ -216,7 +216,7 @@ module ActionMailer #:nodoc:
|
||||
# * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
|
||||
# * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
|
||||
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
|
||||
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the authentication type here.
|
||||
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the authentication type here.
|
||||
# This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.
|
||||
#
|
||||
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
|
||||
@@ -233,10 +233,10 @@ module ActionMailer #:nodoc:
|
||||
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with <tt>delivery_method :test</tt>. Most useful
|
||||
# for unit and functional testing.
|
||||
#
|
||||
# * <tt>default_charset</tt> - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
|
||||
# * <tt>default_charset</tt> - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
|
||||
# pick a different charset from inside a method with +charset+.
|
||||
# * <tt>default_content_type</tt> - The default content type used for the main part of the message. Defaults to "text/plain". You
|
||||
# can also pick a different content type from inside a method with +content_type+.
|
||||
# can also pick a different content type from inside a method with +content_type+.
|
||||
# * <tt>default_mime_version</tt> - The default mime version used for the message. Defaults to <tt>1.0</tt>. You
|
||||
# can also pick a different value from inside a method with +mime_version+.
|
||||
# * <tt>default_implicit_parts_order</tt> - When a message is built implicitly (i.e. multiple parts are assembled from templates
|
||||
@@ -253,9 +253,6 @@ module ActionMailer #:nodoc:
|
||||
class_inheritable_accessor :view_paths
|
||||
cattr_accessor :logger
|
||||
|
||||
cattr_accessor :template_extensions
|
||||
@@template_extensions = ['erb', 'builder', 'rhtml', 'rxml']
|
||||
|
||||
@@smtp_settings = {
|
||||
:address => "localhost",
|
||||
:port => 25,
|
||||
@@ -414,15 +411,10 @@ module ActionMailer #:nodoc:
|
||||
new.deliver!(mail)
|
||||
end
|
||||
|
||||
# Register a template extension so mailer templates written in a
|
||||
# templating language other than rhtml or rxml are supported.
|
||||
# To use this, include in your template-language plugin's init
|
||||
# code or on a per-application basis, this can be invoked from
|
||||
# <tt>config/environment.rb</tt>:
|
||||
#
|
||||
# ActionMailer::Base.register_template_extension('haml')
|
||||
def register_template_extension(extension)
|
||||
template_extensions << extension
|
||||
ActiveSupport::Deprecation.warn(
|
||||
"ActionMailer::Base.register_template_extension has been deprecated." +
|
||||
"Use ActionView::Base.register_template_extension instead", caller)
|
||||
end
|
||||
|
||||
def template_root
|
||||
@@ -455,16 +447,18 @@ module ActionMailer #:nodoc:
|
||||
# "the_template_file.text.html.erb", etc.). Only do this if parts
|
||||
# have not already been specified manually.
|
||||
if @parts.empty?
|
||||
templates = Dir.glob("#{template_path}/#{@template}.*")
|
||||
templates.each do |path|
|
||||
basename = File.basename(path)
|
||||
template_regex = Regexp.new("^([^\\\.]+)\\\.([^\\\.]+\\\.[^\\\.]+)\\\.(" + template_extensions.join('|') + ")$")
|
||||
next unless md = template_regex.match(basename)
|
||||
template_name = basename
|
||||
content_type = md.captures[1].gsub('.', '/')
|
||||
@parts << Part.new(:content_type => content_type,
|
||||
:disposition => "inline", :charset => charset,
|
||||
:body => render_message(template_name, @body))
|
||||
Dir.glob("#{template_path}/#{@template}.*").each do |path|
|
||||
template = template_root["#{mailer_name}/#{File.basename(path)}"]
|
||||
|
||||
# Skip unless template has a multipart format
|
||||
next unless template.multipart?
|
||||
|
||||
@parts << Part.new(
|
||||
:content_type => template.content_type,
|
||||
:disposition => "inline",
|
||||
:charset => charset,
|
||||
:body => render_message(template, @body)
|
||||
)
|
||||
end
|
||||
unless @parts.empty?
|
||||
@content_type = "multipart/alternative"
|
||||
@@ -477,7 +471,7 @@ module ActionMailer #:nodoc:
|
||||
# normal template exists (or if there were no implicit parts) we render
|
||||
# it.
|
||||
template_exists = @parts.empty?
|
||||
template_exists ||= Dir.glob("#{template_path}/#{@template}.*").any? { |i| File.basename(i).split(".").length == 2 }
|
||||
template_exists ||= template_root["#{mailer_name}/#{@template}"]
|
||||
@body = render_message(@template, @body) if template_exists
|
||||
|
||||
# Finally, if there are other message parts and a textual body exists,
|
||||
@@ -538,7 +532,7 @@ module ActionMailer #:nodoc:
|
||||
|
||||
def render(opts)
|
||||
body = opts.delete(:body)
|
||||
if opts[:file] && opts[:file] !~ /\//
|
||||
if opts[:file] && (opts[:file] !~ /\// && !opts[:file].respond_to?(:render))
|
||||
opts[:file] = "#{mailer_name}/#{opts[:file]}"
|
||||
end
|
||||
initialize_template_class(body).render(opts)
|
||||
|
||||
@@ -219,7 +219,7 @@ class TestMailer < ActionMailer::Base
|
||||
end
|
||||
attachment :content_type => "application/octet-stream",:filename => "test.txt", :body => "test abcdefghijklmnopqstuvwxyz"
|
||||
end
|
||||
|
||||
|
||||
def nested_multipart_with_body(recipient)
|
||||
recipients recipient
|
||||
subject "nested multipart with body"
|
||||
@@ -321,7 +321,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
assert_nothing_raised { created = TestMailer.create_nested_multipart(@recipient)}
|
||||
assert_equal 2,created.parts.size
|
||||
assert_equal 2,created.parts.first.parts.size
|
||||
|
||||
|
||||
assert_equal "multipart/mixed", created.content_type
|
||||
assert_equal "multipart/alternative", created.parts.first.content_type
|
||||
assert_equal "bar", created.parts.first.header['foo'].to_s
|
||||
@@ -366,7 +366,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
assert_not_nil ActionMailer::Base.deliveries.first
|
||||
assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
|
||||
end
|
||||
|
||||
|
||||
def test_custom_template
|
||||
expected = new_mail
|
||||
expected.to = @recipient
|
||||
@@ -382,7 +382,6 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_custom_templating_extension
|
||||
#
|
||||
# N.b., custom_templating_extension.text.plain.haml is expected to be in fixtures/test_mailer directory
|
||||
expected = new_mail
|
||||
expected.to = @recipient
|
||||
@@ -390,18 +389,10 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
expected.body = "Hello there, \n\nMr. #{@recipient}"
|
||||
expected.from = "system@loudthinking.com"
|
||||
expected.date = Time.local(2004, 12, 12)
|
||||
|
||||
|
||||
# Stub the render method so no alternative renderers need be present.
|
||||
ActionView::Base.any_instance.stubs(:render).returns("Hello there, \n\nMr. #{@recipient}")
|
||||
|
||||
# If the template is not registered, there should be no parts.
|
||||
created = nil
|
||||
assert_nothing_raised { created = TestMailer.create_custom_templating_extension(@recipient) }
|
||||
assert_not_nil created
|
||||
assert_equal 0, created.parts.length
|
||||
|
||||
ActionMailer::Base.register_template_extension('haml')
|
||||
|
||||
|
||||
# Now that the template is registered, there should be one part. The text/plain part.
|
||||
created = nil
|
||||
assert_nothing_raised { created = TestMailer.create_custom_templating_extension(@recipient) }
|
||||
@@ -428,7 +419,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
assert_not_nil ActionMailer::Base.deliveries.first
|
||||
assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
|
||||
end
|
||||
|
||||
|
||||
def test_cc_bcc
|
||||
expected = new_mail
|
||||
expected.to = @recipient
|
||||
@@ -550,7 +541,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
TestMailer.deliver_signed_up(@recipient)
|
||||
assert_equal 1, ActionMailer::Base.deliveries.size
|
||||
end
|
||||
|
||||
|
||||
def test_doesnt_raise_errors_when_raise_delivery_errors_is_false
|
||||
ActionMailer::Base.raise_delivery_errors = false
|
||||
TestMailer.any_instance.expects(:perform_delivery_test).raises(Exception)
|
||||
@@ -670,7 +661,7 @@ EOF
|
||||
assert_not_nil ActionMailer::Base.deliveries.first
|
||||
assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
|
||||
end
|
||||
|
||||
|
||||
def test_utf8_body_is_not_quoted
|
||||
@recipient = "Foo áëô îü <extended@example.net>"
|
||||
expected = new_mail "utf-8"
|
||||
@@ -760,7 +751,7 @@ EOF
|
||||
mail = TestMailer.create_multipart_with_mime_version(@recipient)
|
||||
assert_equal "1.1", mail.mime_version
|
||||
end
|
||||
|
||||
|
||||
def test_multipart_with_utf8_subject
|
||||
mail = TestMailer.create_multipart_with_utf8_subject(@recipient)
|
||||
assert_match(/\nSubject: =\?utf-8\?Q\?Foo_.*?\?=/, mail.encoded)
|
||||
@@ -825,7 +816,7 @@ EOF
|
||||
mail = TestMailer.create_implicitly_multipart_example(@recipient, 'iso-8859-1')
|
||||
|
||||
assert_equal "multipart/alternative", mail.header['content-type'].body
|
||||
|
||||
|
||||
assert_equal 'iso-8859-1', mail.parts[0].sub_header("content-type", "charset")
|
||||
assert_equal 'iso-8859-1', mail.parts[1].sub_header("content-type", "charset")
|
||||
assert_equal 'iso-8859-1', mail.parts[2].sub_header("content-type", "charset")
|
||||
@@ -852,7 +843,7 @@ EOF
|
||||
assert_equal "line #1\nline #2\nline #3\nline #4\n\n", mail.parts[0].body
|
||||
assert_equal "<p>line #1</p>\n<p>line #2</p>\n<p>line #3</p>\n<p>line #4</p>\n\n", mail.parts[1].body
|
||||
end
|
||||
|
||||
|
||||
def test_headers_removed_on_smtp_delivery
|
||||
ActionMailer::Base.delivery_method = :smtp
|
||||
TestMailer.deliver_cc_bcc(@recipient)
|
||||
|
||||
@@ -7,8 +7,14 @@
|
||||
* Update Prototype to 1.6.0.2 #599 [Patrick Joyce]
|
||||
|
||||
* Conditional GET utility methods. [Jeremy Kemper]
|
||||
* etag!([:admin, post, current_user]) sets the ETag response header and returns head(:not_modified) if it matches the If-None-Match request header.
|
||||
* last_modified!(post.updated_at) sets Last-Modified and returns head(:not_modified) if it's no later than If-Modified-Since.
|
||||
response.last_modified = @post.updated_at
|
||||
response.etag = [:admin, @post, current_user]
|
||||
|
||||
if request.fresh?(response)
|
||||
head :not_modified
|
||||
else
|
||||
# render ...
|
||||
end
|
||||
|
||||
* All 2xx requests are considered successful [Josh Peek]
|
||||
|
||||
|
||||
@@ -87,11 +87,11 @@ module ActionController
|
||||
#
|
||||
def assert_template(expected = nil, message=nil)
|
||||
clean_backtrace do
|
||||
rendered = @response.rendered_template
|
||||
rendered = @response.rendered_template.to_s
|
||||
msg = build_message(message, "expecting <?> but rendering with <?>", expected, rendered)
|
||||
assert_block(msg) do
|
||||
if expected.nil?
|
||||
@response.rendered_template.nil?
|
||||
@response.rendered_template.blank?
|
||||
else
|
||||
rendered.to_s.match(expected)
|
||||
end
|
||||
|
||||
@@ -428,11 +428,7 @@ module ActionController #:nodoc:
|
||||
# By default, all methods defined in ActionController::Base and included modules are hidden.
|
||||
# More methods can be hidden using <tt>hide_actions</tt>.
|
||||
def hidden_actions
|
||||
unless read_inheritable_attribute(:hidden_actions)
|
||||
write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods.map { |m| m.to_s })
|
||||
end
|
||||
|
||||
read_inheritable_attribute(:hidden_actions)
|
||||
read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, [])
|
||||
end
|
||||
|
||||
# Hide each of the given methods from being callable as actions.
|
||||
@@ -1199,7 +1195,7 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
def perform_action
|
||||
if self.class.action_methods.include?(action_name)
|
||||
if action_methods.include?(action_name)
|
||||
send(action_name)
|
||||
default_render unless performed?
|
||||
elsif respond_to? :method_missing
|
||||
@@ -1208,7 +1204,7 @@ module ActionController #:nodoc:
|
||||
elsif template_exists? && template_public?
|
||||
default_render
|
||||
else
|
||||
raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.to_a.sort.to_sentence}", caller
|
||||
raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1234,7 +1230,15 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
def self.action_methods
|
||||
@action_methods ||= Set.new(public_instance_methods.map { |m| m.to_s }) - hidden_actions
|
||||
@action_methods ||=
|
||||
# All public instance methods of this class, including ancestors
|
||||
public_instance_methods(true).map { |m| m.to_s }.to_set -
|
||||
# Except for public instance methods of Base and its ancestors
|
||||
Base.public_instance_methods(true).map { |m| m.to_s } +
|
||||
# Be sure to include shadowed public instance methods of this class
|
||||
public_instance_methods(false).map { |m| m.to_s } -
|
||||
# And always exclude explicitly hidden actions
|
||||
hidden_actions
|
||||
end
|
||||
|
||||
def add_variables_to_assigns
|
||||
|
||||
@@ -43,7 +43,7 @@ module ActionController #:nodoc:
|
||||
:session_path => "/", # available to all paths in app
|
||||
:session_key => "_session_id",
|
||||
:cookie_only => true
|
||||
} unless const_defined?(:DEFAULT_SESSION_OPTIONS)
|
||||
}
|
||||
|
||||
def initialize(cgi, session_options = {})
|
||||
@cgi = cgi
|
||||
@@ -61,53 +61,14 @@ module ActionController #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
# The request body is an IO input stream. If the RAW_POST_DATA environment
|
||||
# variable is already set, wrap it in a StringIO.
|
||||
def body
|
||||
if raw_post = env['RAW_POST_DATA']
|
||||
raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
|
||||
StringIO.new(raw_post)
|
||||
else
|
||||
@cgi.stdinput
|
||||
end
|
||||
end
|
||||
|
||||
def query_parameters
|
||||
@query_parameters ||= self.class.parse_query_parameters(query_string)
|
||||
end
|
||||
|
||||
def request_parameters
|
||||
@request_parameters ||= parse_formatted_request_parameters
|
||||
def body_stream #:nodoc:
|
||||
@cgi.stdinput
|
||||
end
|
||||
|
||||
def cookies
|
||||
@cgi.cookies.freeze
|
||||
end
|
||||
|
||||
def host_with_port_without_standard_port_handling
|
||||
if forwarded = env["HTTP_X_FORWARDED_HOST"]
|
||||
forwarded.split(/,\s?/).last
|
||||
elsif http_host = env['HTTP_HOST']
|
||||
http_host
|
||||
elsif server_name = env['SERVER_NAME']
|
||||
server_name
|
||||
else
|
||||
"#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
|
||||
end
|
||||
end
|
||||
|
||||
def host
|
||||
host_with_port_without_standard_port_handling.sub(/:\d+$/, '')
|
||||
end
|
||||
|
||||
def port
|
||||
if host_with_port_without_standard_port_handling =~ /:(\d+)$/
|
||||
$1.to_i
|
||||
else
|
||||
standard_port
|
||||
end
|
||||
end
|
||||
|
||||
def session
|
||||
unless defined?(@session)
|
||||
if @session_options == false
|
||||
|
||||
@@ -109,16 +109,17 @@ module ActionController #:nodoc:
|
||||
update_options! options
|
||||
end
|
||||
|
||||
# override these to return true in appropriate subclass
|
||||
def before?
|
||||
self.class == BeforeFilter
|
||||
false
|
||||
end
|
||||
|
||||
def after?
|
||||
self.class == AfterFilter
|
||||
false
|
||||
end
|
||||
|
||||
def around?
|
||||
self.class == AroundFilter
|
||||
false
|
||||
end
|
||||
|
||||
# Make sets of strings from :only/:except options
|
||||
@@ -170,6 +171,10 @@ module ActionController #:nodoc:
|
||||
:around
|
||||
end
|
||||
|
||||
def around?
|
||||
true
|
||||
end
|
||||
|
||||
def call(controller, &block)
|
||||
if should_run_callback?(controller)
|
||||
method = filter_responds_to_before_and_after? ? around_proc : self.method
|
||||
@@ -212,6 +217,10 @@ module ActionController #:nodoc:
|
||||
:before
|
||||
end
|
||||
|
||||
def before?
|
||||
true
|
||||
end
|
||||
|
||||
def call(controller, &block)
|
||||
super
|
||||
if controller.send!(:performed?)
|
||||
@@ -224,6 +233,10 @@ module ActionController #:nodoc:
|
||||
def type
|
||||
:after
|
||||
end
|
||||
|
||||
def after?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
require 'active_support/memoizable'
|
||||
|
||||
module ActionController
|
||||
module Http
|
||||
class Headers < ::Hash
|
||||
|
||||
def initialize(constructor = {})
|
||||
if constructor.is_a?(Hash)
|
||||
extend ActiveSupport::Memoizable
|
||||
|
||||
def initialize(*args)
|
||||
if args.size == 1 && args[0].is_a?(Hash)
|
||||
super()
|
||||
update(constructor)
|
||||
update(args[0])
|
||||
else
|
||||
super(constructor)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def [](header_name)
|
||||
if include?(header_name)
|
||||
super
|
||||
super
|
||||
else
|
||||
super(normalize_header(header_name))
|
||||
super(env_name(header_name))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
private
|
||||
# Takes an HTTP header name and returns it in the
|
||||
# format
|
||||
def normalize_header(header_name)
|
||||
# Converts a HTTP header name to an environment variable name.
|
||||
def env_name(header_name)
|
||||
"HTTP_#{header_name.upcase.gsub(/-/, '_')}"
|
||||
end
|
||||
memoize :env_name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@ require 'action_controller/session/cookie_store'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
class RackRequest < AbstractRequest #:nodoc:
|
||||
attr_accessor :env, :session_options
|
||||
attr_accessor :session_options
|
||||
attr_reader :cgi
|
||||
|
||||
class SessionFixationAttempt < StandardError #:nodoc:
|
||||
@@ -15,7 +15,7 @@ module ActionController #:nodoc:
|
||||
:session_path => "/", # available to all paths in app
|
||||
:session_key => "_session_id",
|
||||
:cookie_only => true
|
||||
} unless const_defined?(:DEFAULT_SESSION_OPTIONS)
|
||||
}
|
||||
|
||||
def initialize(env, session_options = DEFAULT_SESSION_OPTIONS)
|
||||
@session_options = session_options
|
||||
@@ -30,35 +30,21 @@ module ActionController #:nodoc:
|
||||
SERVER_NAME SERVER_PROTOCOL
|
||||
|
||||
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
|
||||
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
|
||||
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
|
||||
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
|
||||
define_method(env.sub(/^HTTP_/n, '').downcase) do
|
||||
@env[env]
|
||||
end
|
||||
end
|
||||
|
||||
# The request body is an IO input stream. If the RAW_POST_DATA environment
|
||||
# variable is already set, wrap it in a StringIO.
|
||||
def body
|
||||
if raw_post = env['RAW_POST_DATA']
|
||||
StringIO.new(raw_post)
|
||||
else
|
||||
@env['rack.input']
|
||||
end
|
||||
def body_stream #:nodoc:
|
||||
@env['rack.input']
|
||||
end
|
||||
|
||||
def key?(key)
|
||||
@env.key?(key)
|
||||
end
|
||||
|
||||
def query_parameters
|
||||
@query_parameters ||= self.class.parse_query_parameters(query_string)
|
||||
end
|
||||
|
||||
def request_parameters
|
||||
@request_parameters ||= parse_formatted_request_parameters
|
||||
end
|
||||
|
||||
def cookies
|
||||
return {} unless @env["HTTP_COOKIE"]
|
||||
|
||||
@@ -70,34 +56,6 @@ module ActionController #:nodoc:
|
||||
@env["rack.request.cookie_hash"]
|
||||
end
|
||||
|
||||
def host_with_port_without_standard_port_handling
|
||||
if forwarded = @env["HTTP_X_FORWARDED_HOST"]
|
||||
forwarded.split(/,\s?/).last
|
||||
elsif http_host = @env['HTTP_HOST']
|
||||
http_host
|
||||
elsif server_name = @env['SERVER_NAME']
|
||||
server_name
|
||||
else
|
||||
"#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
|
||||
end
|
||||
end
|
||||
|
||||
def host
|
||||
host_with_port_without_standard_port_handling.sub(/:\d+$/, '')
|
||||
end
|
||||
|
||||
def port
|
||||
if host_with_port_without_standard_port_handling =~ /:(\d+)$/
|
||||
$1.to_i
|
||||
else
|
||||
standard_port
|
||||
end
|
||||
end
|
||||
|
||||
def remote_addr
|
||||
@env['REMOTE_ADDR']
|
||||
end
|
||||
|
||||
def server_port
|
||||
@env['SERVER_PORT'].to_i
|
||||
end
|
||||
|
||||
@@ -2,35 +2,35 @@ require 'tempfile'
|
||||
require 'stringio'
|
||||
require 'strscan'
|
||||
|
||||
module ActionController
|
||||
# HTTP methods which are accepted by default.
|
||||
ACCEPTED_HTTP_METHODS = Set.new(%w( get head put post delete options ))
|
||||
require 'active_support/memoizable'
|
||||
|
||||
module ActionController
|
||||
# CgiRequest and TestRequest provide concrete implementations.
|
||||
class AbstractRequest
|
||||
extend ActiveSupport::Memoizable
|
||||
|
||||
def self.relative_url_root=(*args)
|
||||
ActiveSupport::Deprecation.warn(
|
||||
"ActionController::AbstractRequest.relative_url_root= has been renamed." +
|
||||
"You can now set it with config.action_controller.relative_url_root=", caller)
|
||||
end
|
||||
|
||||
# The hash of CGI-like environment variables for this request, such as
|
||||
#
|
||||
# { 'SERVER_PROTOCOL' => 'HTTP/1.1', 'HTTP_ACCEPT_LANGUAGE' => 'en-us', ... }
|
||||
HTTP_METHODS = %w(get head put post delete options)
|
||||
HTTP_METHOD_LOOKUP = HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h }
|
||||
|
||||
# The hash of environment variables for this request,
|
||||
# such as { 'RAILS_ENV' => 'production' }.
|
||||
attr_reader :env
|
||||
|
||||
# The true HTTP request \method as a lowercase symbol, such as <tt>:get</tt>.
|
||||
# UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS.
|
||||
def request_method
|
||||
@request_method ||= begin
|
||||
method = ((@env['REQUEST_METHOD'] == 'POST' && !parameters[:_method].blank?) ? parameters[:_method].to_s : @env['REQUEST_METHOD']).downcase
|
||||
if ACCEPTED_HTTP_METHODS.include?(method)
|
||||
method.to_sym
|
||||
else
|
||||
raise UnknownHttpMethod, "#{method}, accepted HTTP methods are #{ACCEPTED_HTTP_METHODS.to_a.to_sentence}"
|
||||
end
|
||||
end
|
||||
method = @env['REQUEST_METHOD']
|
||||
method = parameters[:_method] if method == 'POST' && !parameters[:_method].blank?
|
||||
|
||||
HTTP_METHOD_LOOKUP[method] || raise(UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}")
|
||||
end
|
||||
memoize :request_method
|
||||
|
||||
# The HTTP request \method as a lowercase symbol, such as <tt>:get</tt>.
|
||||
# Note, HEAD is returned as <tt>:get</tt> since the two are functionally
|
||||
@@ -69,34 +69,60 @@ module ActionController
|
||||
#
|
||||
# request.headers["Content-Type"] # => "text/plain"
|
||||
def headers
|
||||
@headers ||= ActionController::Http::Headers.new(@env)
|
||||
ActionController::Http::Headers.new(@env)
|
||||
end
|
||||
memoize :headers
|
||||
|
||||
# Returns the content length of the request as an integer.
|
||||
def content_length
|
||||
@content_length ||= env['CONTENT_LENGTH'].to_i
|
||||
@env['CONTENT_LENGTH'].to_i
|
||||
end
|
||||
memoize :content_length
|
||||
|
||||
# The MIME type of the HTTP request, such as Mime::XML.
|
||||
#
|
||||
# For backward compatibility, the post \format is extracted from the
|
||||
# X-Post-Data-Format HTTP header if present.
|
||||
def content_type
|
||||
@content_type ||= Mime::Type.lookup(content_type_without_parameters)
|
||||
Mime::Type.lookup(content_type_without_parameters)
|
||||
end
|
||||
memoize :content_type
|
||||
|
||||
# Returns the accepted MIME type for the request.
|
||||
def accepts
|
||||
@accepts ||=
|
||||
begin
|
||||
header = @env['HTTP_ACCEPT'].to_s.strip
|
||||
header = @env['HTTP_ACCEPT'].to_s.strip
|
||||
|
||||
if header.empty?
|
||||
[content_type, Mime::ALL].compact
|
||||
else
|
||||
Mime::Type.parse(header)
|
||||
end
|
||||
end
|
||||
if header.empty?
|
||||
[content_type, Mime::ALL].compact
|
||||
else
|
||||
Mime::Type.parse(header)
|
||||
end
|
||||
end
|
||||
memoize :accepts
|
||||
|
||||
def if_modified_since
|
||||
if since = env['HTTP_IF_MODIFIED_SINCE']
|
||||
Time.rfc2822(since)
|
||||
end
|
||||
end
|
||||
memoize :if_modified_since
|
||||
|
||||
def if_none_match
|
||||
env['HTTP_IF_NONE_MATCH']
|
||||
end
|
||||
|
||||
def not_modified?(modified_at)
|
||||
if_modified_since && modified_at && if_modified_since >= modified_at
|
||||
end
|
||||
|
||||
def etag_matches?(etag)
|
||||
if_none_match && if_none_match == etag
|
||||
end
|
||||
|
||||
# Check response freshness (Last-Modified and ETag) against request
|
||||
# If-Modified-Since and If-None-Match conditions.
|
||||
def fresh?(response)
|
||||
not_modified?(response.last_modified) || etag_matches?(response.etag)
|
||||
end
|
||||
|
||||
# Returns the Mime type for the \format used in the request.
|
||||
@@ -105,7 +131,7 @@ module ActionController
|
||||
# GET /posts/5.xhtml | request.format => Mime::HTML
|
||||
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
|
||||
def format
|
||||
@format ||= begin
|
||||
@format ||=
|
||||
if parameters[:format]
|
||||
Mime::Type.lookup_by_extension(parameters[:format])
|
||||
elsif ActionController::Base.use_accept_header
|
||||
@@ -115,7 +141,6 @@ module ActionController
|
||||
else
|
||||
Mime::Type.lookup_by_extension("html")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -203,22 +228,26 @@ EOM
|
||||
|
||||
@env['REMOTE_ADDR']
|
||||
end
|
||||
memoize :remote_ip
|
||||
|
||||
# Returns the lowercase name of the HTTP server software.
|
||||
def server_software
|
||||
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
|
||||
end
|
||||
memoize :server_software
|
||||
|
||||
|
||||
# Returns the complete URL used for this request.
|
||||
def url
|
||||
protocol + host_with_port + request_uri
|
||||
end
|
||||
memoize :url
|
||||
|
||||
# Returns 'https://' if this is an SSL request and 'http://' otherwise.
|
||||
def protocol
|
||||
ssl? ? 'https://' : 'http://'
|
||||
end
|
||||
memoize :protocol
|
||||
|
||||
# Is this an SSL request?
|
||||
def ssl?
|
||||
@@ -226,19 +255,36 @@ EOM
|
||||
end
|
||||
|
||||
# Returns the \host for this request, such as "example.com".
|
||||
def host
|
||||
def raw_host_with_port
|
||||
if forwarded = env["HTTP_X_FORWARDED_HOST"]
|
||||
forwarded.split(/,\s?/).last
|
||||
else
|
||||
env['HTTP_HOST'] || env['SERVER_NAME'] || "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the host for this request, such as example.com.
|
||||
def host
|
||||
raw_host_with_port.sub(/:\d+$/, '')
|
||||
end
|
||||
memoize :host
|
||||
|
||||
# Returns a \host:\port string for this request, such as "example.com" or
|
||||
# "example.com:8080".
|
||||
def host_with_port
|
||||
@host_with_port ||= host + port_string
|
||||
"#{host}#{port_string}"
|
||||
end
|
||||
memoize :host_with_port
|
||||
|
||||
# Returns the port number of this request as an integer.
|
||||
def port
|
||||
@port_as_int ||= @env['SERVER_PORT'].to_i
|
||||
if raw_host_with_port =~ /:(\d+)$/
|
||||
$1.to_i
|
||||
else
|
||||
standard_port
|
||||
end
|
||||
end
|
||||
memoize :port
|
||||
|
||||
# Returns the standard \port number for this request's protocol.
|
||||
def standard_port
|
||||
@@ -251,7 +297,7 @@ EOM
|
||||
# Returns a \port suffix like ":8080" if the \port number of this request
|
||||
# is not the default HTTP \port 80 or HTTPS \port 443.
|
||||
def port_string
|
||||
(port == standard_port) ? '' : ":#{port}"
|
||||
port == standard_port ? '' : ":#{port}"
|
||||
end
|
||||
|
||||
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
|
||||
@@ -280,6 +326,7 @@ EOM
|
||||
@env['QUERY_STRING'] || ''
|
||||
end
|
||||
end
|
||||
memoize :query_string
|
||||
|
||||
# Returns the request URI, accounting for server idiosyncrasies.
|
||||
# WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
|
||||
@@ -289,21 +336,23 @@ EOM
|
||||
(%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
|
||||
else
|
||||
# Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
|
||||
script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
|
||||
uri = @env['PATH_INFO']
|
||||
uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil?
|
||||
unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty?
|
||||
uri << '?' << env_qs
|
||||
uri = @env['PATH_INFO'].to_s
|
||||
|
||||
if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
|
||||
uri = uri.sub(/#{script_filename}\//, '')
|
||||
end
|
||||
|
||||
if uri.nil?
|
||||
env_qs = @env['QUERY_STRING'].to_s
|
||||
uri += "?#{env_qs}" unless env_qs.empty?
|
||||
|
||||
if uri.blank?
|
||||
@env.delete('REQUEST_URI')
|
||||
uri
|
||||
else
|
||||
@env['REQUEST_URI'] = uri
|
||||
end
|
||||
end
|
||||
end
|
||||
memoize :request_uri
|
||||
|
||||
# Returns the interpreted \path to requested resource after all the installation
|
||||
# directory of this application was taken into account.
|
||||
@@ -314,6 +363,7 @@ EOM
|
||||
path.sub!(%r/^#{ActionController::Base.relative_url_root}/, '')
|
||||
path || ''
|
||||
end
|
||||
memoize :path
|
||||
|
||||
# Read the request \body. This is useful for web services that need to
|
||||
# work with raw requests directly.
|
||||
@@ -350,19 +400,41 @@ EOM
|
||||
@path_parameters ||= {}
|
||||
end
|
||||
|
||||
# The request body is an IO input stream. If the RAW_POST_DATA environment
|
||||
# variable is already set, wrap it in a StringIO.
|
||||
def body
|
||||
if raw_post = env['RAW_POST_DATA']
|
||||
raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
|
||||
StringIO.new(raw_post)
|
||||
else
|
||||
body_stream
|
||||
end
|
||||
end
|
||||
|
||||
def remote_addr
|
||||
@env['REMOTE_ADDR']
|
||||
end
|
||||
|
||||
def referrer
|
||||
@env['HTTP_REFERER']
|
||||
end
|
||||
alias referer referrer
|
||||
|
||||
|
||||
def query_parameters
|
||||
@query_parameters ||= self.class.parse_query_parameters(query_string)
|
||||
end
|
||||
|
||||
def request_parameters
|
||||
@request_parameters ||= parse_formatted_request_parameters
|
||||
end
|
||||
|
||||
|
||||
#--
|
||||
# Must be implemented in the concrete request
|
||||
#++
|
||||
|
||||
# The request \body as an IO input stream.
|
||||
def body
|
||||
end
|
||||
|
||||
def query_parameters #:nodoc:
|
||||
end
|
||||
|
||||
def request_parameters #:nodoc:
|
||||
def body_stream #:nodoc:
|
||||
end
|
||||
|
||||
def cookies #:nodoc:
|
||||
@@ -389,8 +461,9 @@ EOM
|
||||
|
||||
# The raw content type string with its parameters stripped off.
|
||||
def content_type_without_parameters
|
||||
@content_type_without_parameters ||= self.class.extract_content_type_without_parameters(content_type_with_parameters)
|
||||
self.class.extract_content_type_without_parameters(content_type_with_parameters)
|
||||
end
|
||||
memoize :content_type_without_parameters
|
||||
|
||||
private
|
||||
def content_type_from_legacy_post_data_format_header
|
||||
|
||||
@@ -481,8 +481,7 @@ module ActionController
|
||||
resource.collection_methods.each do |method, actions|
|
||||
actions.each do |action|
|
||||
action_options = action_options_for(action, resource, method)
|
||||
map.named_route("#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options)
|
||||
map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}.:format", action_options)
|
||||
map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -495,18 +494,15 @@ module ActionController
|
||||
index_route_name << "_index"
|
||||
end
|
||||
|
||||
map.named_route(index_route_name, resource.path, index_action_options)
|
||||
map.named_route("formatted_#{index_route_name}", "#{resource.path}.:format", index_action_options)
|
||||
map_named_routes(map, index_route_name, resource.path, index_action_options)
|
||||
|
||||
create_action_options = action_options_for("create", resource)
|
||||
map.connect(resource.path, create_action_options)
|
||||
map.connect("#{resource.path}.:format", create_action_options)
|
||||
map_unnamed_routes(map, resource.path, create_action_options)
|
||||
end
|
||||
|
||||
def map_default_singleton_actions(map, resource)
|
||||
create_action_options = action_options_for("create", resource)
|
||||
map.connect(resource.path, create_action_options)
|
||||
map.connect("#{resource.path}.:format", create_action_options)
|
||||
map_unnamed_routes(map, resource.path, create_action_options)
|
||||
end
|
||||
|
||||
def map_new_actions(map, resource)
|
||||
@@ -514,11 +510,9 @@ module ActionController
|
||||
actions.each do |action|
|
||||
action_options = action_options_for(action, resource, method)
|
||||
if action == :new
|
||||
map.named_route("new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options)
|
||||
map.named_route("formatted_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}.:format", action_options)
|
||||
map_named_routes(map, "new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options)
|
||||
else
|
||||
map.named_route("#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options)
|
||||
map.named_route("formatted_#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}.:format", action_options)
|
||||
map_named_routes(map, "#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -532,22 +526,28 @@ module ActionController
|
||||
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
|
||||
action_path ||= Base.resources_path_names[action] || action
|
||||
|
||||
map.named_route("#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options)
|
||||
map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}.:format",action_options)
|
||||
map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options)
|
||||
end
|
||||
end
|
||||
|
||||
show_action_options = action_options_for("show", resource)
|
||||
map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)
|
||||
map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", show_action_options)
|
||||
map_named_routes(map, "#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)
|
||||
|
||||
update_action_options = action_options_for("update", resource)
|
||||
map.connect(resource.member_path, update_action_options)
|
||||
map.connect("#{resource.member_path}.:format", update_action_options)
|
||||
map_unnamed_routes(map, resource.member_path, update_action_options)
|
||||
|
||||
destroy_action_options = action_options_for("destroy", resource)
|
||||
map.connect(resource.member_path, destroy_action_options)
|
||||
map.connect("#{resource.member_path}.:format", destroy_action_options)
|
||||
map_unnamed_routes(map, resource.member_path, destroy_action_options)
|
||||
end
|
||||
|
||||
def map_unnamed_routes(map, path_without_format, options)
|
||||
map.connect(path_without_format, options)
|
||||
map.connect("#{path_without_format}.:format", options)
|
||||
end
|
||||
|
||||
def map_named_routes(map, name, path_without_format, options)
|
||||
map.named_route(name, path_without_format, options)
|
||||
map.named_route("formatted_#{name}", "#{path_without_format}.:format", options)
|
||||
end
|
||||
|
||||
def add_conditions_for(conditions, method)
|
||||
@@ -574,4 +574,4 @@ end
|
||||
|
||||
class ActionController::Routing::RouteSet::Mapper
|
||||
include ActionController::Resources
|
||||
end
|
||||
end
|
||||
@@ -37,12 +37,20 @@ module ActionController # :nodoc:
|
||||
attr_accessor :body
|
||||
# The headers of the response, as a Hash. It maps header names to header values.
|
||||
attr_accessor :headers
|
||||
attr_accessor :session, :cookies, :assigns, :template, :redirected_to, :redirected_to_method_params, :layout
|
||||
attr_accessor :session, :cookies, :assigns, :template, :layout
|
||||
attr_accessor :redirected_to, :redirected_to_method_params
|
||||
|
||||
def initialize
|
||||
@body, @headers, @session, @assigns = "", DEFAULT_HEADERS.merge("cookie" => []), [], []
|
||||
end
|
||||
|
||||
def status; headers['Status'] end
|
||||
def status=(status) headers['Status'] = status end
|
||||
|
||||
def location; headers['Location'] end
|
||||
def location=(url) headers['Location'] = url end
|
||||
|
||||
|
||||
# Sets the HTTP response's content MIME type. For example, in the controller
|
||||
# you could write this:
|
||||
#
|
||||
@@ -70,11 +78,23 @@ module ActionController # :nodoc:
|
||||
charset.blank? ? nil : charset.strip.split("=")[1]
|
||||
end
|
||||
|
||||
def redirect(to_url, response_status)
|
||||
self.headers["Status"] = response_status
|
||||
self.headers["Location"] = to_url
|
||||
def last_modified
|
||||
Time.rfc2822(headers['Last-Modified'])
|
||||
end
|
||||
|
||||
self.body = "<html><body>You are being <a href=\"#{to_url}\">redirected</a>.</body></html>"
|
||||
def last_modified=(utc_time)
|
||||
headers['Last-Modified'] = utc_time.httpdate
|
||||
end
|
||||
|
||||
def etag; headers['ETag'] end
|
||||
def etag=(etag)
|
||||
headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
|
||||
end
|
||||
|
||||
def redirect(url, status)
|
||||
self.status = status
|
||||
self.location = url
|
||||
self.body = "<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>"
|
||||
end
|
||||
|
||||
def prepare!
|
||||
@@ -83,38 +103,20 @@ module ActionController # :nodoc:
|
||||
set_content_length!
|
||||
end
|
||||
|
||||
# Sets the Last-Modified response header. Returns whether it's older than
|
||||
# the If-Modified-Since request header.
|
||||
def last_modified!(utc_time)
|
||||
headers['Last-Modified'] ||= utc_time.httpdate
|
||||
if request && since = request.headers['HTTP_IF_MODIFIED_SINCE']
|
||||
utc_time <= Time.rfc2822(since)
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the ETag response header. Returns whether it matches the
|
||||
# If-None-Match request header.
|
||||
def etag!(tag)
|
||||
headers['ETag'] ||= %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(tag))}")
|
||||
if request && request.headers['HTTP_IF_NONE_MATCH'] == headers['ETag']
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def handle_conditional_get!
|
||||
if nonempty_ok_response?
|
||||
set_conditional_cache_control!
|
||||
|
||||
if etag!(body)
|
||||
headers['Status'] = '304 Not Modified'
|
||||
self.etag ||= body
|
||||
if request && request.etag_matches?(etag)
|
||||
self.status = '304 Not Modified'
|
||||
self.body = ''
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def nonempty_ok_response?
|
||||
status = headers['Status']
|
||||
ok = !status || status[0..2] == '200'
|
||||
ok && body.is_a?(String) && !body.empty?
|
||||
end
|
||||
|
||||
@@ -2,7 +2,8 @@ module ActionController
|
||||
module Routing
|
||||
class Segment #:nodoc:
|
||||
RESERVED_PCHAR = ':@&=+$,;'
|
||||
UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze
|
||||
SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
|
||||
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
|
||||
|
||||
# TODO: Convert :is_optional accessor to read only
|
||||
attr_accessor :is_optional
|
||||
|
||||
@@ -129,7 +129,7 @@ class CGI::Session::CookieStore
|
||||
private
|
||||
# Marshal a session hash into safe cookie data. Include an integrity hash.
|
||||
def marshal(session)
|
||||
data = ActiveSupport::Base64.encode64(Marshal.dump(session)).chop
|
||||
data = ActiveSupport::Base64.encode64s(Marshal.dump(session))
|
||||
"#{data}--#{generate_digest(data)}"
|
||||
end
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ module ActionController #:nodoc:
|
||||
|
||||
class TestRequest < AbstractRequest #:nodoc:
|
||||
attr_accessor :cookies, :session_options
|
||||
attr_accessor :query_parameters, :request_parameters, :path, :session, :env
|
||||
attr_accessor :query_parameters, :request_parameters, :path, :session
|
||||
attr_accessor :host, :user_agent
|
||||
|
||||
def initialize(query_parameters = nil, request_parameters = nil, session = nil)
|
||||
@@ -42,7 +42,7 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
# Wraps raw_post in a StringIO.
|
||||
def body
|
||||
def body_stream #:nodoc:
|
||||
StringIO.new(raw_post)
|
||||
end
|
||||
|
||||
@@ -54,7 +54,7 @@ module ActionController #:nodoc:
|
||||
|
||||
def port=(number)
|
||||
@env["SERVER_PORT"] = number.to_i
|
||||
@port_as_int = nil
|
||||
port(true)
|
||||
end
|
||||
|
||||
def action=(action_name)
|
||||
@@ -68,6 +68,8 @@ module ActionController #:nodoc:
|
||||
@env["REQUEST_URI"] = value
|
||||
@request_uri = nil
|
||||
@path = nil
|
||||
request_uri(true)
|
||||
path(true)
|
||||
end
|
||||
|
||||
def request_uri=(uri)
|
||||
@@ -77,21 +79,26 @@ module ActionController #:nodoc:
|
||||
|
||||
def accept=(mime_types)
|
||||
@env["HTTP_ACCEPT"] = Array(mime_types).collect { |mime_types| mime_types.to_s }.join(",")
|
||||
accepts(true)
|
||||
end
|
||||
|
||||
def if_modified_since=(last_modified)
|
||||
@env["HTTP_IF_MODIFIED_SINCE"] = last_modified
|
||||
end
|
||||
|
||||
def if_none_match=(etag)
|
||||
@env["HTTP_IF_NONE_MATCH"] = etag
|
||||
end
|
||||
|
||||
def remote_addr=(addr)
|
||||
@env['REMOTE_ADDR'] = addr
|
||||
end
|
||||
|
||||
def remote_addr
|
||||
@env['REMOTE_ADDR']
|
||||
end
|
||||
|
||||
def request_uri
|
||||
def request_uri(*args)
|
||||
@request_uri || super
|
||||
end
|
||||
|
||||
def path
|
||||
def path(*args)
|
||||
@path || super
|
||||
end
|
||||
|
||||
@@ -113,17 +120,13 @@ module ActionController #:nodoc:
|
||||
end
|
||||
end
|
||||
@parameters = nil # reset TestRequest#parameters to use the new path_parameters
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def recycle!
|
||||
self.request_parameters = {}
|
||||
self.query_parameters = {}
|
||||
self.path_parameters = {}
|
||||
@request_method, @accepts, @content_type = nil, nil, nil
|
||||
end
|
||||
|
||||
def referer
|
||||
@env["HTTP_REFERER"]
|
||||
unmemoize_all
|
||||
end
|
||||
|
||||
private
|
||||
@@ -448,10 +451,13 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
def method_missing(selector, *args)
|
||||
return @controller.send!(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
|
||||
return super
|
||||
if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
|
||||
@controller.send(selector, *args)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Shortcut for <tt>ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + path, type)</tt>:
|
||||
#
|
||||
# post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
|
||||
|
||||
@@ -300,6 +300,8 @@ module ActionView #:nodoc:
|
||||
# # => 'users/legacy.rhtml'
|
||||
#
|
||||
def pick_template(template_path)
|
||||
return template_path if template_path.respond_to?(:render)
|
||||
|
||||
path = template_path.sub(/^\//, '')
|
||||
if m = path.match(/(.*)\.(\w+)$/)
|
||||
template_file_name, template_file_extension = m[1], m[2]
|
||||
@@ -343,7 +345,8 @@ module ActionView #:nodoc:
|
||||
ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller)
|
||||
end
|
||||
|
||||
if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/")
|
||||
if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) &&
|
||||
template_path.is_a?(String) && !template_path.include?("/")
|
||||
raise ActionViewError, <<-END_ERROR
|
||||
Due to changes in ActionMailer, you need to provide the mailer_name along with the template name.
|
||||
|
||||
|
||||
@@ -463,7 +463,7 @@ module ActionView
|
||||
end
|
||||
|
||||
private
|
||||
COMPUTED_PUBLIC_PATHS = ActiveSupport::Cache::MemoryStore.new.silence!.threadsafe!
|
||||
COMPUTED_PUBLIC_PATHS = ActiveSupport::Cache::MemoryStore.new.silence!
|
||||
|
||||
# Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
|
||||
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
|
||||
@@ -618,6 +618,11 @@ module ActionView
|
||||
def write_asset_file_contents(joined_asset_path, asset_paths)
|
||||
FileUtils.mkdir_p(File.dirname(joined_asset_path))
|
||||
File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }
|
||||
|
||||
# Set mtime to the latest of the combined files to allow for
|
||||
# consistent ETag without a shared filesystem.
|
||||
mt = asset_paths.map { |p| File.mtime(File.join(ASSETS_DIR, p)) }.max
|
||||
File.utime(mt, mt, joined_asset_path)
|
||||
end
|
||||
|
||||
def collect_asset_files(*path)
|
||||
|
||||
@@ -13,9 +13,6 @@ module ActionView
|
||||
# the select_month method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead of
|
||||
# "date[month]".
|
||||
module DateHelper
|
||||
include ActionView::Helpers::TagHelper
|
||||
DEFAULT_PREFIX = 'date' unless const_defined?('DEFAULT_PREFIX')
|
||||
|
||||
# Reports the approximate distance in time between two Time or Date objects or integers as seconds.
|
||||
# Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs
|
||||
# Distances are reported based on the following table:
|
||||
@@ -52,7 +49,7 @@ module ActionView
|
||||
# distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute
|
||||
# distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
|
||||
# distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
|
||||
# distance_of_time_in_words(from_time, from_time + 4.years + 15.days + 30.minutes + 5.seconds) # => over 4 years
|
||||
# distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => over 4 years
|
||||
#
|
||||
# to_time = Time.now + 6.years + 19.days
|
||||
# distance_of_time_in_words(from_time, to_time, true) # => over 6 years
|
||||
@@ -109,19 +106,36 @@ module ActionView
|
||||
alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
|
||||
|
||||
# Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
|
||||
# attribute (identified by +method+) on an object assigned to the template (identified by +object+). It's
|
||||
# possible to tailor the selects through the +options+ hash, which accepts all the keys that each of the
|
||||
# individual select builders do (like <tt>:use_month_numbers</tt> for select_month) as well as a range of discard
|
||||
# options. The discard options are <tt>:discard_year</tt>, <tt>:discard_month</tt> and <tt>:discard_day</tt>. Set
|
||||
# to true, they'll drop the respective select. Discarding the month select will also automatically discard the
|
||||
# day select. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with an
|
||||
# array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. Symbols may be omitted
|
||||
# and the respective select is not included.
|
||||
# attribute (identified by +method+) on an object assigned to the template (identified by +object+). You can
|
||||
# the output in the +options+ hash.
|
||||
#
|
||||
# Pass the <tt>:default</tt> option to set the default date. Use a Time object or a Hash of <tt>:year</tt>,
|
||||
# <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt>, and <tt>:second</tt>.
|
||||
#
|
||||
# Passing <tt>:disabled => true</tt> as part of the +options+ will make elements inaccessible for change.
|
||||
# ==== Options
|
||||
# * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
|
||||
# "2" instead of "February").
|
||||
# * <tt>:use_short_month</tt> - Set to true if you want to use the abbreviated month name instead of the full
|
||||
# name (e.g. "Feb" instead of "February").
|
||||
# * <tt>:add_month_number</tt> - Set to true if you want to show both, the month's number and name (e.g.
|
||||
# "2 - February" instead of "February").
|
||||
# * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
|
||||
# Note: You can also use Rails' new i18n functionality for this.
|
||||
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
|
||||
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
|
||||
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
|
||||
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
|
||||
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
|
||||
# first of the given month in order to not create invalid dates like 31 February.
|
||||
# * <tt>:discard_month</tt> - Set to true if you don't want to show a month select. This includes the month
|
||||
# as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
|
||||
# * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
|
||||
# as a hidden field instead of showing a select field.
|
||||
# * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> do
|
||||
# customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
|
||||
# select will not be shown (like when you set <tt>:discard_xxx => true</tt>. Defaults to the order defined in
|
||||
# the respective locale (e.g. [:year, :month, :day] in the en-US locale that ships with Rails).
|
||||
# * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
|
||||
# dates.
|
||||
# * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
|
||||
# * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
|
||||
#
|
||||
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
|
||||
#
|
||||
@@ -165,9 +179,9 @@ module ActionView
|
||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_date_select_tag(options, html_options)
|
||||
end
|
||||
|
||||
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a specified
|
||||
# time-based attribute (identified by +method+) on an object assigned to the template (identified by +object+).
|
||||
# You can include the seconds with <tt>:include_seconds</tt>.
|
||||
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
|
||||
# specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
|
||||
# +object+). You can include the seconds with <tt>:include_seconds</tt>.
|
||||
#
|
||||
# This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
|
||||
# <tt>:ignore_date</tt> is set to +true+.
|
||||
@@ -178,7 +192,8 @@ module ActionView
|
||||
# # Creates a time select tag that, when POSTed, will be stored in the post variable in the sunrise attribute
|
||||
# time_select("post", "sunrise")
|
||||
#
|
||||
# # Creates a time select tag that, when POSTed, will be stored in the order variable in the submitted attribute
|
||||
# # Creates a time select tag that, when POSTed, will be stored in the order variable in the submitted
|
||||
# # attribute
|
||||
# time_select("order", "submitted")
|
||||
#
|
||||
# # Creates a time select tag that, when POSTed, will be stored in the mail variable in the sent_at attribute
|
||||
@@ -210,7 +225,8 @@ module ActionView
|
||||
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
|
||||
#
|
||||
# ==== Examples
|
||||
# # Generates a datetime select that, when POSTed, will be stored in the post variable in the written_on attribute
|
||||
# # Generates a datetime select that, when POSTed, will be stored in the post variable in the written_on
|
||||
# # attribute
|
||||
# datetime_select("post", "written_on")
|
||||
#
|
||||
# # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
|
||||
@@ -230,12 +246,12 @@ module ActionView
|
||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_datetime_select_tag(options, html_options)
|
||||
end
|
||||
|
||||
# Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the +datetime+.
|
||||
# It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
|
||||
# symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not supply a Symbol,
|
||||
# it will be appended onto the <tt>:order</tt> passed in. You can also add <tt>:date_separator</tt>,
|
||||
# <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to control visual display of
|
||||
# the elements.
|
||||
# Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the
|
||||
# +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
|
||||
# an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
|
||||
# supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
|
||||
# <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
|
||||
# control visual display of the elements.
|
||||
#
|
||||
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
|
||||
#
|
||||
@@ -270,14 +286,13 @@ module ActionView
|
||||
# select_datetime(my_date_time, :prefix => 'payday')
|
||||
#
|
||||
def select_datetime(datetime = Time.current, options = {}, html_options = {})
|
||||
separator = options[:datetime_separator] || ''
|
||||
select_date(datetime, options, html_options) + separator + select_time(datetime, options, html_options)
|
||||
DateTimeSelector.new(datetime, options, html_options).select_datetime
|
||||
end
|
||||
|
||||
# Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
|
||||
# It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
|
||||
# symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not supply a Symbol, it
|
||||
# will be appended onto the <tt>:order</tt> passed in.
|
||||
# symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not supply a Symbol,
|
||||
# it will be appended onto the <tt>:order</tt> passed in.
|
||||
#
|
||||
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
|
||||
#
|
||||
@@ -307,12 +322,7 @@ module ActionView
|
||||
# select_date(my_date, :prefix => 'payday')
|
||||
#
|
||||
def select_date(date = Date.current, options = {}, html_options = {})
|
||||
options.reverse_merge!(:order => [], :date_separator => '')
|
||||
[:year, :month, :day].each { |o| options[:order].push(o) unless options[:order].include?(o) }
|
||||
|
||||
options[:order].inject([]) { |s, o|
|
||||
s << self.send("select_#{o}", date, options, html_options)
|
||||
}.join(options[:date_separator])
|
||||
DateTimeSelector.new(date, options, html_options).select_date
|
||||
end
|
||||
|
||||
# Returns a set of html select-tags (one for hour and minute)
|
||||
@@ -343,9 +353,7 @@ module ActionView
|
||||
# select_time(my_time, :time_separator => ':', :include_seconds => true)
|
||||
#
|
||||
def select_time(datetime = Time.current, options = {}, html_options = {})
|
||||
separator = options[:time_separator] || ''
|
||||
select_hour(datetime, options, html_options) + separator + select_minute(datetime, options, html_options) +
|
||||
(options[:include_seconds] ? separator + select_second(datetime, options, html_options) : '')
|
||||
DateTimeSelector.new(datetime, options, html_options).select_time
|
||||
end
|
||||
|
||||
# Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
|
||||
@@ -366,15 +374,12 @@ module ActionView
|
||||
# select_second(my_time, :field_name => 'interval')
|
||||
#
|
||||
def select_second(datetime, options = {}, html_options = {})
|
||||
val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.sec) : ''
|
||||
options[:use_hidden] ?
|
||||
(options[:include_seconds] ? _date_hidden_html(options[:field_name] || 'second', val, options) : '') :
|
||||
_date_select_html(options[:field_name] || 'second', _date_build_options(val), options, html_options)
|
||||
DateTimeSelector.new(datetime, options, html_options).select_second
|
||||
end
|
||||
|
||||
# Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
|
||||
# Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute selected
|
||||
# The <tt>minute</tt> can also be substituted for a minute number.
|
||||
# Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
|
||||
# selected. The <tt>minute</tt> can also be substituted for a minute number.
|
||||
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
|
||||
#
|
||||
# ==== Examples
|
||||
@@ -391,11 +396,7 @@ module ActionView
|
||||
# select_minute(my_time, :field_name => 'stride')
|
||||
#
|
||||
def select_minute(datetime, options = {}, html_options = {})
|
||||
val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.min) : ''
|
||||
options[:use_hidden] ?
|
||||
_date_hidden_html(options[:field_name] || 'minute', val, options) :
|
||||
_date_select_html(options[:field_name] || 'minute',
|
||||
_date_build_options(val, :step => options[:minute_step]), options, html_options)
|
||||
DateTimeSelector.new(datetime, options, html_options).select_minute
|
||||
end
|
||||
|
||||
# Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
|
||||
@@ -416,9 +417,7 @@ module ActionView
|
||||
# select_minute(my_time, :field_name => 'stride')
|
||||
#
|
||||
def select_hour(datetime, options = {}, html_options = {})
|
||||
val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.hour) : ''
|
||||
options[:use_hidden] ? _date_hidden_html(options[:field_name] || 'hour', val, options) :
|
||||
_date_select_html(options[:field_name] || 'hour', _date_build_options(val, :end => 23), options, html_options)
|
||||
DateTimeSelector.new(datetime, options, html_options).select_hour
|
||||
end
|
||||
|
||||
# Returns a select tag with options for each of the days 1 through 31 with the current day selected.
|
||||
@@ -439,11 +438,7 @@ module ActionView
|
||||
# select_day(my_time, :field_name => 'due')
|
||||
#
|
||||
def select_day(date, options = {}, html_options = {})
|
||||
val = date ? (date.kind_of?(Fixnum) ? date : date.day) : ''
|
||||
options[:use_hidden] ? _date_hidden_html(options[:field_name] || 'day', val, options) :
|
||||
_date_select_html(options[:field_name] || 'day',
|
||||
_date_build_options(val, :start => 1, :end => 31, :leading_zeros => false),
|
||||
options, html_options)
|
||||
DateTimeSelector.new(date, options, html_options).select_day
|
||||
end
|
||||
|
||||
# Returns a select tag with options for each of the months January through December with the current month
|
||||
@@ -481,36 +476,7 @@ module ActionView
|
||||
# select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
|
||||
#
|
||||
def select_month(date, options = {}, html_options = {})
|
||||
locale = options[:locale]
|
||||
|
||||
val = date ? (date.kind_of?(Fixnum) ? date : date.month) : ''
|
||||
if options[:use_hidden]
|
||||
_date_hidden_html(options[:field_name] || 'month', val, options)
|
||||
else
|
||||
month_options = []
|
||||
month_names = options[:use_month_names] || begin
|
||||
key = options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
|
||||
I18n.translate key, :locale => locale
|
||||
end
|
||||
month_names.unshift(nil) if month_names.size < 13
|
||||
|
||||
1.upto(12) do |month_number|
|
||||
month_name = if options[:use_month_numbers]
|
||||
month_number
|
||||
elsif options[:add_month_numbers]
|
||||
month_number.to_s + ' - ' + month_names[month_number]
|
||||
else
|
||||
month_names[month_number]
|
||||
end
|
||||
|
||||
month_options << ((val == month_number) ?
|
||||
content_tag(:option, month_name, :value => month_number, :selected => "selected") :
|
||||
content_tag(:option, month_name, :value => month_number)
|
||||
)
|
||||
month_options << "\n"
|
||||
end
|
||||
_date_select_html(options[:field_name] || 'month', month_options.join, options, html_options)
|
||||
end
|
||||
DateTimeSelector.new(date, options, html_options).select_month
|
||||
end
|
||||
|
||||
# Returns a select tag with options for each of the five years on each side of the current, which is selected.
|
||||
@@ -537,158 +503,369 @@ module ActionView
|
||||
# select_year(2006, :start_year => 2000, :end_year => 2010)
|
||||
#
|
||||
def select_year(date, options = {}, html_options = {})
|
||||
if !date || date == 0
|
||||
val = ''
|
||||
middle_year = Date.today.year
|
||||
elsif date.kind_of?(Fixnum)
|
||||
val = middle_year = date
|
||||
DateTimeSelector.new(date, options, html_options).select_year
|
||||
end
|
||||
end
|
||||
|
||||
class DateTimeSelector #:nodoc:
|
||||
extend ActiveSupport::Memoizable
|
||||
include ActionView::Helpers::TagHelper
|
||||
|
||||
DEFAULT_PREFIX = 'date'.freeze unless const_defined?('DEFAULT_PREFIX')
|
||||
POSITION = {
|
||||
:year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
|
||||
}.freeze unless const_defined?('POSITION')
|
||||
|
||||
def initialize(datetime, options = {}, html_options = {})
|
||||
@options = options.dup
|
||||
@html_options = html_options.dup
|
||||
@datetime = datetime
|
||||
end
|
||||
|
||||
def select_datetime
|
||||
# TODO: Remove tag conditional
|
||||
# Ideally we could just join select_date and select_date for the tag case
|
||||
if @options[:tag] && @options[:ignore_date]
|
||||
select_time
|
||||
elsif @options[:tag]
|
||||
order = date_order.dup
|
||||
order -= [:hour, :minute, :second]
|
||||
|
||||
@options[:discard_year] ||= true unless order.include?(:year)
|
||||
@options[:discard_month] ||= true unless order.include?(:month)
|
||||
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
|
||||
@options[:discard_minute] ||= true if @options[:discard_hour]
|
||||
@options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
|
||||
|
||||
# If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
|
||||
# valid (otherwise it could be 31 and february wouldn't be a valid date)
|
||||
if @options[:discard_day] && !@options[:discard_month]
|
||||
@datetime = @datetime.change(:day => 1)
|
||||
end
|
||||
|
||||
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
|
||||
order += [:hour, :minute, :second] unless @options[:discard_hour]
|
||||
|
||||
build_selects_from_types(order)
|
||||
else
|
||||
val = middle_year = date.year
|
||||
"#{select_date}#{@options[:datetime_separator]}#{select_time}"
|
||||
end
|
||||
end
|
||||
|
||||
def select_date
|
||||
order = date_order.dup
|
||||
|
||||
# TODO: Remove tag conditional
|
||||
if @options[:tag]
|
||||
@options[:discard_hour] = true
|
||||
@options[:discard_minute] = true
|
||||
@options[:discard_second] = true
|
||||
|
||||
@options[:discard_year] ||= true unless order.include?(:year)
|
||||
@options[:discard_month] ||= true unless order.include?(:month)
|
||||
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
|
||||
|
||||
# If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
|
||||
# valid (otherwise it could be 31 and february wouldn't be a valid date)
|
||||
if @options[:discard_day] && !@options[:discard_month]
|
||||
@datetime = @datetime.change(:day => 1)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:use_hidden]
|
||||
_date_hidden_html(options[:field_name] || 'year', val, options)
|
||||
else
|
||||
options[:start_year] ||= middle_year - 5
|
||||
options[:end_year] ||= middle_year + 5
|
||||
step = options[:start_year] < options[:end_year] ? 1 : -1
|
||||
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
|
||||
|
||||
_date_select_html(options[:field_name] || 'year',
|
||||
_date_build_options(val,
|
||||
:start => options[:start_year],
|
||||
:end => options[:end_year],
|
||||
:step => step,
|
||||
:leading_zeros => false
|
||||
), options, html_options)
|
||||
build_selects_from_types(order)
|
||||
end
|
||||
|
||||
def select_time
|
||||
order = []
|
||||
|
||||
# TODO: Remove tag conditional
|
||||
if @options[:tag]
|
||||
@options[:discard_month] = true
|
||||
@options[:discard_year] = true
|
||||
@options[:discard_day] = true
|
||||
@options[:discard_second] ||= true unless @options[:include_seconds]
|
||||
|
||||
order += [:year, :month, :day] unless @options[:ignore_date]
|
||||
end
|
||||
|
||||
order += [:hour, :minute]
|
||||
order << :second if @options[:include_seconds]
|
||||
|
||||
build_selects_from_types(order)
|
||||
end
|
||||
|
||||
def select_second
|
||||
if @options[:use_hidden] || @options[:discard_second]
|
||||
build_hidden(:second, sec) if @options[:include_seconds]
|
||||
else
|
||||
build_options_and_select(:second, sec)
|
||||
end
|
||||
end
|
||||
|
||||
def select_minute
|
||||
if @options[:use_hidden] || @options[:discard_minute]
|
||||
build_hidden(:minute, min)
|
||||
else
|
||||
build_options_and_select(:minute, min, :step => @options[:minute_step])
|
||||
end
|
||||
end
|
||||
|
||||
def select_hour
|
||||
if @options[:use_hidden] || @options[:discard_hour]
|
||||
build_hidden(:hour, hour)
|
||||
else
|
||||
build_options_and_select(:hour, hour, :end => 23)
|
||||
end
|
||||
end
|
||||
|
||||
def select_day
|
||||
if @options[:use_hidden] || @options[:discard_day]
|
||||
build_hidden(:day, day)
|
||||
else
|
||||
build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false)
|
||||
end
|
||||
end
|
||||
|
||||
def select_month
|
||||
if @options[:use_hidden] || @options[:discard_month]
|
||||
build_hidden(:month, month)
|
||||
else
|
||||
month_options = []
|
||||
1.upto(12) do |month_number|
|
||||
options = { :value => month_number }
|
||||
options[:selected] = "selected" if month == month_number
|
||||
month_options << content_tag(:option, month_name(month_number), options) + "\n"
|
||||
end
|
||||
build_select(:month, month_options.join)
|
||||
end
|
||||
end
|
||||
|
||||
def select_year
|
||||
if !@datetime || @datetime == 0
|
||||
val = ''
|
||||
middle_year = Date.today.year
|
||||
else
|
||||
val = middle_year = year
|
||||
end
|
||||
|
||||
if @options[:use_hidden] || @options[:discard_year]
|
||||
build_hidden(:year, val)
|
||||
else
|
||||
options = {}
|
||||
options[:start] = @options[:start_year] || middle_year - 5
|
||||
options[:end] = @options[:end_year] || middle_year + 5
|
||||
options[:step] = options[:start] < options[:end] ? 1 : -1
|
||||
options[:leading_zeros] = false
|
||||
|
||||
build_options_and_select(:year, val, options)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def _date_build_options(selected, options={})
|
||||
options.reverse_merge!(:start => 0, :end => 59, :step => 1, :leading_zeros => true)
|
||||
%w( sec min hour day month year ).each do |method|
|
||||
define_method(method) do
|
||||
@datetime.kind_of?(Fixnum) ? @datetime : @datetime.send(method) if @datetime
|
||||
end
|
||||
end
|
||||
|
||||
# Returns translated month names, but also ensures that a custom month
|
||||
# name array has a leading nil element
|
||||
def month_names
|
||||
month_names = @options[:use_month_names] || translated_month_names
|
||||
month_names.unshift(nil) if month_names.size < 13
|
||||
month_names
|
||||
end
|
||||
memoize :month_names
|
||||
|
||||
# Returns translated month names
|
||||
# => [nil, "January", "February", "March",
|
||||
# "April", "May", "June", "July",
|
||||
# "August", "September", "October",
|
||||
# "November", "December"]
|
||||
#
|
||||
# If :use_short_month option is set
|
||||
# => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
# "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
||||
def translated_month_names
|
||||
begin
|
||||
key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
|
||||
I18n.translate(key, :locale => @options[:locale])
|
||||
end
|
||||
end
|
||||
|
||||
# Lookup month name for number
|
||||
# month_name(1) => "January"
|
||||
#
|
||||
# If :use_month_numbers option is passed
|
||||
# month_name(1) => 1
|
||||
#
|
||||
# If :add_month_numbers option is passed
|
||||
# month_name(1) => "1 - January"
|
||||
def month_name(number)
|
||||
if @options[:use_month_numbers]
|
||||
number
|
||||
elsif @options[:add_month_numbers]
|
||||
"#{number} - #{month_names[number]}"
|
||||
else
|
||||
month_names[number]
|
||||
end
|
||||
end
|
||||
|
||||
def date_order
|
||||
@options[:order] || translated_date_order
|
||||
end
|
||||
memoize :date_order
|
||||
|
||||
def translated_date_order
|
||||
begin
|
||||
I18n.translate(:'date.order', :locale => @options[:locale]) || []
|
||||
end
|
||||
end
|
||||
|
||||
# Build full select tag from date type and options
|
||||
def build_options_and_select(type, selected, options = {})
|
||||
build_select(type, build_options(selected, options))
|
||||
end
|
||||
|
||||
# Build select option html from date value and options
|
||||
# build_options(15, :start => 1, :end => 31)
|
||||
# => "<option value="1">1</option>
|
||||
# <option value=\"2\">2</option>
|
||||
# <option value=\"3\">3</option>..."
|
||||
def build_options(selected, options = {})
|
||||
start = options.delete(:start) || 0
|
||||
stop = options.delete(:end) || 59
|
||||
step = options.delete(:step) || 1
|
||||
leading_zeros = options.delete(:leading_zeros).nil? ? true : false
|
||||
|
||||
select_options = []
|
||||
(options[:start] || 0).step((options[:end] || 59), options[:step] || 1) do |i|
|
||||
value = options[:leading_zeros] ? sprintf("%02d", i) : i
|
||||
start.step(stop, step) do |i|
|
||||
value = leading_zeros ? sprintf("%02d", i) : i
|
||||
tag_options = { :value => value }
|
||||
tag_options[:selected] = "selected" if selected == i
|
||||
|
||||
select_options << content_tag(:option, value, tag_options)
|
||||
end
|
||||
select_options.join("\n") + "\n"
|
||||
end
|
||||
|
||||
def _date_select_html(type, html_options, options, select_tag_options = {})
|
||||
_date_name_and_id_from_options(options, type)
|
||||
select_options = {:id => options[:id], :name => options[:name]}
|
||||
select_options.merge!(:disabled => 'disabled') if options[:disabled]
|
||||
select_options.merge!(select_tag_options) unless select_tag_options.empty?
|
||||
# Builds select tag from date type and html select options
|
||||
# build_select(:month, "<option value="1">January</option>...")
|
||||
# => "<select id="post_written_on_2i" name="post[written_on(2i)]">
|
||||
# <option value="1">January</option>...
|
||||
# </select>"
|
||||
def build_select(type, select_options_as_html)
|
||||
select_options = {
|
||||
:id => input_id_from_type(type),
|
||||
:name => input_name_from_type(type)
|
||||
}.merge(@html_options)
|
||||
select_options.merge!(:disabled => 'disabled') if @options[:disabled]
|
||||
|
||||
select_html = "\n"
|
||||
select_html << content_tag(:option, '', :value => '') + "\n" if options[:include_blank]
|
||||
select_html << html_options.to_s
|
||||
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
|
||||
select_html << select_options_as_html.to_s
|
||||
|
||||
content_tag(:select, select_html, select_options) + "\n"
|
||||
end
|
||||
|
||||
def _date_hidden_html(type, value, options)
|
||||
_date_name_and_id_from_options(options, type)
|
||||
hidden_html = tag(:input, :type => "hidden", :id => options[:id], :name => options[:name], :value => value) + "\n"
|
||||
# Builds hidden input tag for date part and value
|
||||
# build_hidden(:year, 2008)
|
||||
# => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
|
||||
def build_hidden(type, value)
|
||||
tag(:input, {
|
||||
:type => "hidden",
|
||||
:id => input_id_from_type(type),
|
||||
:name => input_name_from_type(type),
|
||||
:value => value
|
||||
}) + "\n"
|
||||
end
|
||||
|
||||
def _date_name_and_id_from_options(options, type)
|
||||
options[:name] = (options[:prefix] || DEFAULT_PREFIX) + (options[:discard_type] ? '' : "[#{type}]")
|
||||
options[:id] = options[:name].gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
|
||||
# Returns the name attribute for the input tag
|
||||
# => post[written_on(1i)]
|
||||
def input_name_from_type(type)
|
||||
prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
|
||||
prefix += "[#{@options[:index]}]" if @options[:index]
|
||||
|
||||
field_name = @options[:field_name] || type
|
||||
if @options[:include_position]
|
||||
field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
|
||||
end
|
||||
|
||||
@options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
|
||||
end
|
||||
|
||||
# Returns the id attribute for the input tag
|
||||
# => "post_written_on_1i"
|
||||
def input_id_from_type(type)
|
||||
input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
|
||||
end
|
||||
|
||||
# Given an ordering of datetime components, create the selection html
|
||||
# and join them with their appropriate seperators
|
||||
def build_selects_from_types(order)
|
||||
select = ''
|
||||
order.reverse.each do |type|
|
||||
separator = separator(type) unless type == order.first # don't add on last field
|
||||
select.insert(0, separator.to_s + send("select_#{type}").to_s)
|
||||
end
|
||||
select
|
||||
end
|
||||
|
||||
# Returns the separator for a given datetime component
|
||||
def separator(type)
|
||||
case type
|
||||
when :month, :day
|
||||
@options[:date_separator]
|
||||
when :hour
|
||||
(@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
|
||||
when :minute
|
||||
@options[:time_separator]
|
||||
when :second
|
||||
@options[:include_seconds] ? @options[:time_separator] : ""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class InstanceTag #:nodoc:
|
||||
include DateHelper
|
||||
|
||||
def to_date_select_tag(options = {}, html_options = {})
|
||||
date_or_time_select(options.merge(:discard_hour => true), html_options)
|
||||
datetime_selector(options, html_options).select_date
|
||||
end
|
||||
|
||||
def to_time_select_tag(options = {}, html_options = {})
|
||||
date_or_time_select(options.merge(:discard_year => true, :discard_month => true), html_options)
|
||||
datetime_selector(options, html_options).select_time
|
||||
end
|
||||
|
||||
def to_datetime_select_tag(options = {}, html_options = {})
|
||||
date_or_time_select(options, html_options)
|
||||
datetime_selector(options, html_options).select_datetime
|
||||
end
|
||||
|
||||
private
|
||||
def date_or_time_select(options, html_options = {})
|
||||
locale = options[:locale]
|
||||
def datetime_selector(options, html_options)
|
||||
datetime = value(object) || default_datetime(options)
|
||||
|
||||
defaults = { :discard_type => true }
|
||||
options = defaults.merge(options)
|
||||
datetime = value(object)
|
||||
datetime ||= default_time_from_options(options[:default]) unless options[:include_blank]
|
||||
options = options.dup
|
||||
options[:field_name] = @method_name
|
||||
options[:include_position] = true
|
||||
options[:prefix] ||= @object_name
|
||||
options[:index] ||= @auto_index
|
||||
options[:datetime_separator] ||= ' — '
|
||||
options[:time_separator] ||= ' : '
|
||||
|
||||
position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }
|
||||
|
||||
order = options[:order] ||= I18n.translate(:'date.order', :locale => locale)
|
||||
|
||||
# Discard explicit and implicit by not being included in the :order
|
||||
discard = {}
|
||||
discard[:year] = true if options[:discard_year] or !order.include?(:year)
|
||||
discard[:month] = true if options[:discard_month] or !order.include?(:month)
|
||||
discard[:day] = true if options[:discard_day] or discard[:month] or !order.include?(:day)
|
||||
discard[:hour] = true if options[:discard_hour]
|
||||
discard[:minute] = true if options[:discard_minute] or discard[:hour]
|
||||
discard[:second] = true unless options[:include_seconds] && !discard[:minute]
|
||||
|
||||
# If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are valid
|
||||
# (otherwise it could be 31 and february wouldn't be a valid date)
|
||||
if datetime && discard[:day] && !discard[:month]
|
||||
datetime = datetime.change(:day => 1)
|
||||
end
|
||||
|
||||
# Maintain valid dates by including hidden fields for discarded elements
|
||||
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
|
||||
|
||||
# Ensure proper ordering of :hour, :minute and :second
|
||||
[:hour, :minute, :second].each { |o| order.delete(o); order.push(o) }
|
||||
|
||||
date_or_time_select = ''
|
||||
order.reverse.each do |param|
|
||||
# Send hidden fields for discarded elements once output has started
|
||||
# This ensures AR can reconstruct valid dates using ParseDate
|
||||
next if discard[param] && (date_or_time_select.empty? || options[:ignore_date])
|
||||
|
||||
date_or_time_select.insert(0,
|
||||
self.send("select_#{param}",
|
||||
datetime,
|
||||
options_with_prefix(position[param], options.merge(:use_hidden => discard[param])),
|
||||
html_options))
|
||||
date_or_time_select.insert(0,
|
||||
case param
|
||||
when :hour then (discard[:year] && discard[:day] ? "" : " — ")
|
||||
when :minute then " : "
|
||||
when :second then options[:include_seconds] ? " : " : ""
|
||||
else ""
|
||||
end)
|
||||
end
|
||||
|
||||
date_or_time_select
|
||||
DateTimeSelector.new(datetime, options.merge(:tag => true), html_options)
|
||||
end
|
||||
|
||||
def options_with_prefix(position, options)
|
||||
prefix = "#{@object_name}"
|
||||
if options[:index]
|
||||
prefix << "[#{options[:index]}]"
|
||||
elsif @auto_index
|
||||
prefix << "[#{@auto_index}]"
|
||||
end
|
||||
options.merge(:prefix => "#{prefix}[#{@method_name}(#{position}i)]")
|
||||
end
|
||||
def default_datetime(options)
|
||||
return if options[:include_blank]
|
||||
|
||||
def default_time_from_options(default)
|
||||
case default
|
||||
case options[:default]
|
||||
when nil
|
||||
Time.current
|
||||
when Date, Time
|
||||
default
|
||||
options[:default]
|
||||
else
|
||||
default = options[:default].dup
|
||||
|
||||
# Rename :minute and :second to :min and :sec
|
||||
default[:min] ||= default[:minute]
|
||||
default[:sec] ||= default[:second]
|
||||
@@ -699,8 +876,11 @@ module ActionView
|
||||
default[key] ||= time.send(key)
|
||||
end
|
||||
|
||||
Time.utc_time(default[:year], default[:month], default[:day], default[:hour], default[:min], default[:sec])
|
||||
end
|
||||
Time.utc_time(
|
||||
default[:year], default[:month], default[:day],
|
||||
default[:hour], default[:min], default[:sec]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -71,9 +71,9 @@ module ActionView
|
||||
def number_to_currency(number, options = {})
|
||||
options.symbolize_keys!
|
||||
|
||||
defaults, currency = I18n.translate([:'number.format', :'number.currency.format'],
|
||||
:locale => options[:locale]) || [{},{}]
|
||||
defaults = defaults.merge(currency)
|
||||
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
||||
currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {}
|
||||
defaults = defaults.merge(currency)
|
||||
|
||||
precision = options[:precision] || defaults[:precision]
|
||||
unit = options[:unit] || defaults[:unit]
|
||||
@@ -109,9 +109,9 @@ module ActionView
|
||||
def number_to_percentage(number, options = {})
|
||||
options.symbolize_keys!
|
||||
|
||||
defaults, percentage = I18n.translate([:'number.format', :'number.percentage.format'],
|
||||
:locale => options[:locale]) || [{},{}]
|
||||
defaults = defaults.merge(percentage)
|
||||
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
||||
percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :raise => true) rescue {}
|
||||
defaults = defaults.merge(percentage)
|
||||
|
||||
precision = options[:precision] || defaults[:precision]
|
||||
separator = options[:separator] || defaults[:separator]
|
||||
@@ -151,7 +151,7 @@ module ActionView
|
||||
options = args.extract_options!
|
||||
options.symbolize_keys!
|
||||
|
||||
defaults = I18n.translate(:'number.format', :locale => options[:locale]) || {}
|
||||
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
||||
|
||||
unless args.empty?
|
||||
ActiveSupport::Deprecation.warn('number_with_delimiter takes an option hash ' +
|
||||
@@ -195,9 +195,10 @@ module ActionView
|
||||
options = args.extract_options!
|
||||
options.symbolize_keys!
|
||||
|
||||
defaults, precision_defaults = I18n.translate([:'number.format', :'number.precision.format'],
|
||||
:locale => options[:locale]) || [{},{}]
|
||||
defaults = defaults.merge(precision_defaults)
|
||||
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
||||
precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale],
|
||||
:raise => true) rescue {}
|
||||
defaults = defaults.merge(precision_defaults)
|
||||
|
||||
unless args.empty?
|
||||
ActiveSupport::Deprecation.warn('number_with_precision takes an option hash ' +
|
||||
@@ -209,12 +210,14 @@ module ActionView
|
||||
separator ||= (options[:separator] || defaults[:separator])
|
||||
delimiter ||= (options[:delimiter] || defaults[:delimiter])
|
||||
|
||||
rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision
|
||||
number_with_delimiter("%01.#{precision}f" % rounded_number,
|
||||
:separator => separator,
|
||||
:delimiter => delimiter)
|
||||
rescue
|
||||
number
|
||||
begin
|
||||
rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision
|
||||
number_with_delimiter("%01.#{precision}f" % rounded_number,
|
||||
:separator => separator,
|
||||
:delimiter => delimiter)
|
||||
rescue
|
||||
number
|
||||
end
|
||||
end
|
||||
|
||||
STORAGE_UNITS = %w( Bytes KB MB GB TB ).freeze
|
||||
@@ -251,8 +254,8 @@ module ActionView
|
||||
options = args.extract_options!
|
||||
options.symbolize_keys!
|
||||
|
||||
defaults, human = I18n.translate([:'number.format', :'number.human.format'],
|
||||
:locale => options[:locale]) || [{},{}]
|
||||
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
||||
human = I18n.translate(:'number.human.format', :locale => options[:locale], :raise => true) rescue {}
|
||||
defaults = defaults.merge(human)
|
||||
|
||||
unless args.empty?
|
||||
@@ -272,13 +275,16 @@ module ActionView
|
||||
number /= 1024 ** exponent
|
||||
unit = STORAGE_UNITS[exponent]
|
||||
|
||||
number_with_precision(number,
|
||||
:precision => precision,
|
||||
:separator => separator,
|
||||
:delimiter => delimiter
|
||||
).sub(/(\d)(#{Regexp.escape(separator)}[1-9]*)?0+\z/, '\1') + " #{unit}"
|
||||
rescue
|
||||
number
|
||||
begin
|
||||
escaped_separator = Regexp.escape(separator)
|
||||
number_with_precision(number,
|
||||
:precision => precision,
|
||||
:separator => separator,
|
||||
:delimiter => delimiter
|
||||
).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '') + " #{unit}"
|
||||
rescue
|
||||
number
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -558,7 +558,7 @@ module ActionView
|
||||
[-\w]+ # subdomain or domain
|
||||
(?:\.[-\w]+)* # remaining subdomains or domain
|
||||
(?::\d+)? # port
|
||||
(?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$])))*)* # path
|
||||
(?:/(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))*)* # path
|
||||
(?:\?[\w\+@%&=.;-]+)? # query string
|
||||
(?:\#[\w\-]*)? # trailing anchor
|
||||
)
|
||||
|
||||
@@ -442,7 +442,7 @@ module ActionView
|
||||
# # => <a href="mailto:me@domain.com">me@domain.com</a>
|
||||
#
|
||||
# mail_to "me@domain.com", "My email", :encode => "javascript"
|
||||
# # => <script type="text/javascript">eval(unescape('%64%6f%63...%6d%65%6e'))</script>
|
||||
# # => <script type="text/javascript">eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
|
||||
#
|
||||
# mail_to "me@domain.com", "My email", :encode => "hex"
|
||||
# # => <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
|
||||
@@ -476,7 +476,7 @@ module ActionView
|
||||
"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)
|
||||
end
|
||||
"<script type=\"#{Mime::JS}\">eval(unescape('#{string}'))</script>"
|
||||
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>"
|
||||
elsif encode == "hex"
|
||||
email_address_encoded = ''
|
||||
email_address_obfuscated.each_byte do |c|
|
||||
|
||||
@@ -146,7 +146,7 @@ module ActionView
|
||||
|
||||
def find_partial_path(partial_path)
|
||||
if partial_path.include?('/')
|
||||
"#{File.dirname(partial_path)}/_#{File.basename(partial_path)}"
|
||||
File.join(File.dirname(partial_path), "_#{File.basename(partial_path)}")
|
||||
elsif respond_to?(:controller)
|
||||
"#{controller.class.controller_path}/_#{partial_path}"
|
||||
else
|
||||
|
||||
@@ -31,10 +31,10 @@ module ActionView
|
||||
|
||||
view.send(:evaluate_assigns)
|
||||
view.send(:set_controller_content_type, mime_type) if respond_to?(:mime_type)
|
||||
view.send(:execute, method(local_assigns), local_assigns)
|
||||
view.send(:execute, method_name(local_assigns), local_assigns)
|
||||
end
|
||||
|
||||
def method(local_assigns)
|
||||
def method_name(local_assigns)
|
||||
if local_assigns && local_assigns.any?
|
||||
local_assigns_keys = "locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
|
||||
end
|
||||
@@ -44,7 +44,7 @@ module ActionView
|
||||
private
|
||||
# Compile and evaluate the template's code (if necessary)
|
||||
def compile(local_assigns)
|
||||
render_symbol = method(local_assigns)
|
||||
render_symbol = method_name(local_assigns)
|
||||
|
||||
@@mutex.synchronize do
|
||||
if recompile?(render_symbol)
|
||||
|
||||
@@ -22,6 +22,14 @@ module ActionView #:nodoc:
|
||||
end
|
||||
memoize :format_and_extension
|
||||
|
||||
def multipart?
|
||||
format && format.include?('.')
|
||||
end
|
||||
|
||||
def content_type
|
||||
format.gsub('.', '/')
|
||||
end
|
||||
|
||||
def mime_type
|
||||
Mime::Type.lookup_by_extension(format) if format
|
||||
end
|
||||
@@ -84,7 +92,7 @@ module ActionView #:nodoc:
|
||||
# [base_path, name, format, extension]
|
||||
def split(file)
|
||||
if m = file.match(/^(.*\/)?([^\.]+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/)
|
||||
if m[5] # Mulipart formats
|
||||
if m[5] # Multipart formats
|
||||
[m[1], m[2], "#{m[3]}.#{m[4]}", m[5]]
|
||||
elsif m[4] # Single format
|
||||
[m[1], m[2], m[3], m[4]]
|
||||
|
||||
@@ -17,6 +17,8 @@ unless defined?(ActionMailer)
|
||||
end
|
||||
end
|
||||
|
||||
ActionMailer::Base.template_root = FIXTURE_LOAD_PATH
|
||||
|
||||
class AssertSelectTest < Test::Unit::TestCase
|
||||
class AssertSelectController < ActionController::Base
|
||||
def response_with=(content)
|
||||
@@ -69,11 +71,10 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
ActionMailer::Base.deliveries = []
|
||||
end
|
||||
|
||||
|
||||
def teardown
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
|
||||
def assert_failure(message, &block)
|
||||
e = assert_raises(AssertionFailedError, &block)
|
||||
assert_match(message, e.message) if Regexp === message
|
||||
@@ -91,7 +92,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
assert_failure(/Expected at least 1 element matching \"p\", found 0/) { assert_select "p" }
|
||||
end
|
||||
|
||||
|
||||
def test_equality_true_false
|
||||
render_html %Q{<div id="1"></div><div id="2"></div>}
|
||||
assert_nothing_raised { assert_select "div" }
|
||||
@@ -102,7 +102,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
assert_nothing_raised { assert_select "p", false }
|
||||
end
|
||||
|
||||
|
||||
def test_equality_string_and_regexp
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_nothing_raised { assert_select "div", "foo" }
|
||||
@@ -116,7 +115,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
assert_raises(AssertionFailedError) { assert_select "p", :text=>/foobar/ }
|
||||
end
|
||||
|
||||
|
||||
def test_equality_of_html
|
||||
render_html %Q{<p>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</p>}
|
||||
text = "\"This is not a big problem,\" he said."
|
||||
@@ -135,7 +133,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
assert_raises(AssertionFailedError) { assert_select "pre", :html=>text }
|
||||
end
|
||||
|
||||
|
||||
def test_counts
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_nothing_raised { assert_select "div", 2 }
|
||||
@@ -166,7 +163,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_substitution_values
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_select "div#?", /\d+/ do |elements|
|
||||
@@ -181,7 +177,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_nested_assert_select
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_select "div" do |elements|
|
||||
@@ -200,7 +195,7 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
assert_select "#3", false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
assert_failure(/Expected at least 1 element matching \"#4\", found 0\./) do
|
||||
assert_select "div" do
|
||||
assert_select "#4"
|
||||
@@ -208,7 +203,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_assert_select_text_match
|
||||
render_html %Q{<div id="1"><span>foo</span></div><div id="2"><span>bar</span></div>}
|
||||
assert_select "div" do
|
||||
@@ -225,7 +219,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# With single result.
|
||||
def test_assert_select_from_rjs_with_single_result
|
||||
render_rjs do |page|
|
||||
@@ -255,19 +248,16 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Test css_select.
|
||||
#
|
||||
|
||||
|
||||
def test_css_select
|
||||
render_html %Q{<div id="1"></div><div id="2"></div>}
|
||||
assert 2, css_select("div").size
|
||||
assert 0, css_select("p").size
|
||||
end
|
||||
|
||||
|
||||
def test_nested_css_select
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_select "div#?", /\d+/ do |elements|
|
||||
@@ -286,7 +276,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# With one result.
|
||||
def test_css_select_from_rjs_with_single_result
|
||||
render_rjs do |page|
|
||||
@@ -309,12 +298,10 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
assert_equal 1, css_select("#2").size
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Test assert_select_rjs.
|
||||
#
|
||||
|
||||
|
||||
# Test that we can pick up all statements in the result.
|
||||
def test_assert_select_rjs_picks_up_all_statements
|
||||
render_rjs do |page|
|
||||
@@ -381,7 +368,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
assert_raises(AssertionFailedError) { assert_select_rjs "test4" }
|
||||
end
|
||||
|
||||
|
||||
def test_assert_select_rjs_for_replace
|
||||
render_rjs do |page|
|
||||
page.replace "test1", "<div id=\"1\">foo</div>"
|
||||
@@ -479,7 +465,7 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Simple hide
|
||||
def test_assert_select_rjs_for_hide
|
||||
render_rjs do |page|
|
||||
@@ -500,7 +486,7 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Simple toggle
|
||||
def test_assert_select_rjs_for_toggle
|
||||
render_rjs do |page|
|
||||
@@ -521,7 +507,7 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Non-positioned insert.
|
||||
def test_assert_select_rjs_for_nonpositioned_insert
|
||||
render_rjs do |page|
|
||||
@@ -568,7 +554,7 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
assert_select "div", 4
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Simple selection from a single result.
|
||||
def test_nested_assert_select_rjs_with_single_result
|
||||
render_rjs do |page|
|
||||
@@ -600,7 +586,6 @@ class AssertSelectTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_feed_item_encoded
|
||||
render_xml <<-EOF
|
||||
<rss version="2.0">
|
||||
@@ -654,7 +639,6 @@ EOF
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Test assert_select_email
|
||||
#
|
||||
@@ -670,7 +654,6 @@ EOF
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def render_html(html)
|
||||
@controller.response_with = html
|
||||
|
||||
@@ -109,7 +109,7 @@ class PageCachingTest < Test::Unit::TestCase
|
||||
|
||||
uses_mocha("should_cache_ok_at_custom_path") do
|
||||
def test_should_cache_ok_at_custom_path
|
||||
@request.expects(:path).returns("/index.html")
|
||||
@request.stubs(:path).returns("/index.html")
|
||||
get :ok
|
||||
assert_response :ok
|
||||
assert File.exist?("#{FILE_STORE_PATH}/index.html")
|
||||
|
||||
@@ -75,7 +75,7 @@ class CgiRequestTest < BaseCgiTest
|
||||
assert_equal "rubyonrails.org:8080", @request.host_with_port
|
||||
|
||||
@request_hash['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
|
||||
assert_equal "www.secondhost.org", @request.host
|
||||
assert_equal "www.secondhost.org", @request.host(true)
|
||||
end
|
||||
|
||||
def test_http_host_with_default_port_overrides_server_port
|
||||
|
||||
@@ -128,23 +128,23 @@ class AcceptBasedContentTypeTest < ActionController::TestCase
|
||||
|
||||
|
||||
def test_render_default_content_types_for_respond_to
|
||||
@request.env["HTTP_ACCEPT"] = Mime::HTML.to_s
|
||||
@request.accept = Mime::HTML.to_s
|
||||
get :render_default_content_types_for_respond_to
|
||||
assert_equal Mime::HTML, @response.content_type
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = Mime::JS.to_s
|
||||
@request.accept = Mime::JS.to_s
|
||||
get :render_default_content_types_for_respond_to
|
||||
assert_equal Mime::JS, @response.content_type
|
||||
end
|
||||
|
||||
def test_render_default_content_types_for_respond_to_with_template
|
||||
@request.env["HTTP_ACCEPT"] = Mime::XML.to_s
|
||||
@request.accept = Mime::XML.to_s
|
||||
get :render_default_content_types_for_respond_to
|
||||
assert_equal Mime::XML, @response.content_type
|
||||
end
|
||||
|
||||
def test_render_default_content_types_for_respond_to_with_overwrite
|
||||
@request.env["HTTP_ACCEPT"] = Mime::RSS.to_s
|
||||
@request.accept = Mime::RSS.to_s
|
||||
get :render_default_content_types_for_respond_to
|
||||
assert_equal Mime::XML, @response.content_type
|
||||
end
|
||||
|
||||
@@ -177,7 +177,7 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_html
|
||||
@request.env["HTTP_ACCEPT"] = "text/html"
|
||||
@request.accept = "text/html"
|
||||
get :js_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
@@ -189,7 +189,7 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_all
|
||||
@request.env["HTTP_ACCEPT"] = "*/*"
|
||||
@request.accept = "*/*"
|
||||
get :js_or_html
|
||||
assert_equal 'HTML', @response.body # js is not part of all
|
||||
|
||||
@@ -201,13 +201,13 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_xml
|
||||
@request.env["HTTP_ACCEPT"] = "application/xml"
|
||||
@request.accept = "application/xml"
|
||||
get :html_xml_or_rss
|
||||
assert_equal 'XML', @response.body
|
||||
end
|
||||
|
||||
def test_js_or_html
|
||||
@request.env["HTTP_ACCEPT"] = "text/javascript, text/html"
|
||||
@request.accept = "text/javascript, text/html"
|
||||
get :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
|
||||
@@ -232,7 +232,7 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
'JSON' => %w(application/json text/x-json)
|
||||
}.each do |body, content_types|
|
||||
content_types.each do |content_type|
|
||||
@request.env['HTTP_ACCEPT'] = content_type
|
||||
@request.accept = content_type
|
||||
get :json_or_yaml
|
||||
assert_equal body, @response.body
|
||||
end
|
||||
@@ -240,7 +240,7 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_js_or_anything
|
||||
@request.env["HTTP_ACCEPT"] = "text/javascript, */*"
|
||||
@request.accept = "text/javascript, */*"
|
||||
get :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
|
||||
@@ -252,34 +252,34 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_using_defaults
|
||||
@request.env["HTTP_ACCEPT"] = "*/*"
|
||||
@request.accept = "*/*"
|
||||
get :using_defaults
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 'Hello world!', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "text/javascript"
|
||||
@request.accept = "text/javascript"
|
||||
get :using_defaults
|
||||
assert_equal "text/javascript", @response.content_type
|
||||
assert_equal '$("body").visualEffect("highlight");', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "application/xml"
|
||||
@request.accept = "application/xml"
|
||||
get :using_defaults
|
||||
assert_equal "application/xml", @response.content_type
|
||||
assert_equal "<p>Hello world!</p>\n", @response.body
|
||||
end
|
||||
|
||||
def test_using_defaults_with_type_list
|
||||
@request.env["HTTP_ACCEPT"] = "*/*"
|
||||
@request.accept = "*/*"
|
||||
get :using_defaults_with_type_list
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 'Hello world!', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "text/javascript"
|
||||
@request.accept = "text/javascript"
|
||||
get :using_defaults_with_type_list
|
||||
assert_equal "text/javascript", @response.content_type
|
||||
assert_equal '$("body").visualEffect("highlight");', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "application/xml"
|
||||
@request.accept = "application/xml"
|
||||
get :using_defaults_with_type_list
|
||||
assert_equal "application/xml", @response.content_type
|
||||
assert_equal "<p>Hello world!</p>\n", @response.body
|
||||
@@ -298,55 +298,55 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_synonyms
|
||||
@request.env["HTTP_ACCEPT"] = "application/javascript"
|
||||
@request.accept = "application/javascript"
|
||||
get :js_or_html
|
||||
assert_equal 'JS', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "application/x-xml"
|
||||
@request.accept = "application/x-xml"
|
||||
get :html_xml_or_rss
|
||||
assert_equal "XML", @response.body
|
||||
end
|
||||
|
||||
def test_custom_types
|
||||
@request.env["HTTP_ACCEPT"] = "application/crazy-xml"
|
||||
@request.accept = "application/crazy-xml"
|
||||
get :custom_type_handling
|
||||
assert_equal "application/crazy-xml", @response.content_type
|
||||
assert_equal 'Crazy XML', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "text/html"
|
||||
@request.accept = "text/html"
|
||||
get :custom_type_handling
|
||||
assert_equal "text/html", @response.content_type
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_xhtml_alias
|
||||
@request.env["HTTP_ACCEPT"] = "application/xhtml+xml,application/xml"
|
||||
@request.accept = "application/xhtml+xml,application/xml"
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_firefox_simulation
|
||||
@request.env["HTTP_ACCEPT"] = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
|
||||
@request.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
|
||||
get :html_or_xml
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any
|
||||
@request.env["HTTP_ACCEPT"] = "*/*"
|
||||
@request.accept = "*/*"
|
||||
get :handle_any
|
||||
assert_equal 'HTML', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "text/javascript"
|
||||
@request.accept = "text/javascript"
|
||||
get :handle_any
|
||||
assert_equal 'Either JS or XML', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "text/xml"
|
||||
@request.accept = "text/xml"
|
||||
get :handle_any
|
||||
assert_equal 'Either JS or XML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any
|
||||
@request.env["HTTP_ACCEPT"] = "*/*"
|
||||
@request.accept = "*/*"
|
||||
get :handle_any_any
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
@@ -357,31 +357,31 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_handle_any_any_explicit_html
|
||||
@request.env["HTTP_ACCEPT"] = "text/html"
|
||||
@request.accept = "text/html"
|
||||
get :handle_any_any
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_javascript
|
||||
@request.env["HTTP_ACCEPT"] = "text/javascript"
|
||||
@request.accept = "text/javascript"
|
||||
get :handle_any_any
|
||||
assert_equal 'Whatever you ask for, I got it', @response.body
|
||||
end
|
||||
|
||||
def test_handle_any_any_xml
|
||||
@request.env["HTTP_ACCEPT"] = "text/xml"
|
||||
@request.accept = "text/xml"
|
||||
get :handle_any_any
|
||||
assert_equal 'Whatever you ask for, I got it', @response.body
|
||||
end
|
||||
|
||||
def test_rjs_type_skips_layout
|
||||
@request.env["HTTP_ACCEPT"] = "text/javascript"
|
||||
@request.accept = "text/javascript"
|
||||
get :all_types_with_layout
|
||||
assert_equal 'RJS for all_types_with_layout', @response.body
|
||||
end
|
||||
|
||||
def test_html_type_with_layout
|
||||
@request.env["HTTP_ACCEPT"] = "text/html"
|
||||
@request.accept = "text/html"
|
||||
get :all_types_with_layout
|
||||
assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body
|
||||
end
|
||||
@@ -460,7 +460,7 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_format_with_custom_response_type_and_request_headers
|
||||
@request.env["HTTP_ACCEPT"] = "text/iphone"
|
||||
@request.accept = "text/iphone"
|
||||
get :iphone_with_html_response_type
|
||||
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
|
||||
assert_equal "text/html", @response.content_type
|
||||
@@ -470,7 +470,7 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||
get :iphone_with_html_response_type_without_layout
|
||||
assert_equal '<html><div id="html_missing">Hello future from Firefox!</div></html>', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "text/iphone"
|
||||
@request.accept = "text/iphone"
|
||||
assert_raises(ActionView::MissingTemplate) { get :iphone_with_html_response_type_without_layout }
|
||||
end
|
||||
end
|
||||
@@ -522,7 +522,7 @@ class MimeControllerLayoutsTest < Test::Unit::TestCase
|
||||
get :index
|
||||
assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "text/iphone"
|
||||
@request.accept = "text/iphone"
|
||||
get :index
|
||||
assert_equal 'Hello iPhone', @response.body
|
||||
end
|
||||
@@ -533,7 +533,7 @@ class MimeControllerLayoutsTest < Test::Unit::TestCase
|
||||
get :index
|
||||
assert_equal 'Super Firefox', @response.body
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = "text/iphone"
|
||||
@request.accept = "text/iphone"
|
||||
get :index
|
||||
assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
|
||||
end
|
||||
|
||||
@@ -136,6 +136,10 @@ class NewRenderTestController < ActionController::Base
|
||||
render :partial => "partial_only", :layout => true
|
||||
end
|
||||
|
||||
def partial_with_counter
|
||||
render :partial => "counter", :locals => { :counter_counter => 5 }
|
||||
end
|
||||
|
||||
def partial_with_locals
|
||||
render :partial => "customer", :locals => { :customer => Customer.new("david") }
|
||||
end
|
||||
@@ -741,6 +745,11 @@ EOS
|
||||
assert_equal "<title>Talking to the layout</title>\nAction was here!", @response.body
|
||||
end
|
||||
|
||||
def test_partial_with_counter
|
||||
get :partial_with_counter
|
||||
assert_equal "5", @response.body
|
||||
end
|
||||
|
||||
def test_partials_list
|
||||
get :partials_list
|
||||
assert_equal "goodbyeHello: davidHello: marygoodbye\n", @response.body
|
||||
|
||||
@@ -64,58 +64,61 @@ end
|
||||
|
||||
class RackRequestTest < BaseRackTest
|
||||
def test_proxy_request
|
||||
assert_equal 'glu.ttono.us', @request.host_with_port
|
||||
assert_equal 'glu.ttono.us', @request.host_with_port(true)
|
||||
end
|
||||
|
||||
def test_http_host
|
||||
@env.delete "HTTP_X_FORWARDED_HOST"
|
||||
@env['HTTP_HOST'] = "rubyonrails.org:8080"
|
||||
assert_equal "rubyonrails.org:8080", @request.host_with_port
|
||||
assert_equal "rubyonrails.org", @request.host(true)
|
||||
assert_equal "rubyonrails.org:8080", @request.host_with_port(true)
|
||||
|
||||
@env['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
|
||||
assert_equal "www.secondhost.org", @request.host
|
||||
assert_equal "www.secondhost.org", @request.host(true)
|
||||
end
|
||||
|
||||
def test_http_host_with_default_port_overrides_server_port
|
||||
@env.delete "HTTP_X_FORWARDED_HOST"
|
||||
@env['HTTP_HOST'] = "rubyonrails.org"
|
||||
assert_equal "rubyonrails.org", @request.host_with_port
|
||||
assert_equal "rubyonrails.org", @request.host_with_port(true)
|
||||
end
|
||||
|
||||
def test_host_with_port_defaults_to_server_name_if_no_host_headers
|
||||
@env.delete "HTTP_X_FORWARDED_HOST"
|
||||
@env.delete "HTTP_HOST"
|
||||
assert_equal "glu.ttono.us:8007", @request.host_with_port
|
||||
assert_equal "glu.ttono.us:8007", @request.host_with_port(true)
|
||||
end
|
||||
|
||||
def test_host_with_port_falls_back_to_server_addr_if_necessary
|
||||
@env.delete "HTTP_X_FORWARDED_HOST"
|
||||
@env.delete "HTTP_HOST"
|
||||
@env.delete "SERVER_NAME"
|
||||
assert_equal "207.7.108.53:8007", @request.host_with_port
|
||||
assert_equal "207.7.108.53", @request.host(true)
|
||||
assert_equal 8007, @request.port(true)
|
||||
assert_equal "207.7.108.53:8007", @request.host_with_port(true)
|
||||
end
|
||||
|
||||
def test_host_with_port_if_http_standard_port_is_specified
|
||||
@env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80"
|
||||
assert_equal "glu.ttono.us", @request.host_with_port
|
||||
assert_equal "glu.ttono.us", @request.host_with_port(true)
|
||||
end
|
||||
|
||||
def test_host_with_port_if_https_standard_port_is_specified
|
||||
@env['HTTP_X_FORWARDED_PROTO'] = "https"
|
||||
@env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443"
|
||||
assert_equal "glu.ttono.us", @request.host_with_port
|
||||
assert_equal "glu.ttono.us", @request.host_with_port(true)
|
||||
end
|
||||
|
||||
def test_host_if_ipv6_reference
|
||||
@env.delete "HTTP_X_FORWARDED_HOST"
|
||||
@env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]"
|
||||
assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
|
||||
assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host(true)
|
||||
end
|
||||
|
||||
def test_host_if_ipv6_reference_with_port
|
||||
@env.delete "HTTP_X_FORWARDED_HOST"
|
||||
@env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008"
|
||||
assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
|
||||
assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host(true)
|
||||
end
|
||||
|
||||
def test_cgi_environment_variables
|
||||
|
||||
@@ -15,9 +15,14 @@ class TestController < ActionController::Base
|
||||
end
|
||||
|
||||
def conditional_hello
|
||||
etag! [:foo, 123]
|
||||
last_modified! Time.now.utc.beginning_of_day
|
||||
render :action => 'hello_world' unless performed?
|
||||
response.last_modified = Time.now.utc.beginning_of_day
|
||||
response.etag = [:foo, 123]
|
||||
|
||||
if request.fresh?(response)
|
||||
head :not_modified
|
||||
else
|
||||
render :action => 'hello_world'
|
||||
end
|
||||
end
|
||||
|
||||
def render_hello_world
|
||||
@@ -428,7 +433,7 @@ class RenderTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_should_render_formatted_html_erb_template_with_faulty_accepts_header
|
||||
@request.env["HTTP_ACCEPT"] = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appliction/x-shockwave-flash, */*"
|
||||
@request.accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appliction/x-shockwave-flash, */*"
|
||||
get :formatted_xml_erb
|
||||
assert_equal '<test>passed formatted html erb</test>', @response.body
|
||||
end
|
||||
@@ -490,16 +495,16 @@ class EtagRenderTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_render_against_etag_request_should_304_when_match
|
||||
@request.headers["HTTP_IF_NONE_MATCH"] = etag_for("hello david")
|
||||
@request.if_none_match = etag_for("hello david")
|
||||
get :render_hello_world_from_variable
|
||||
assert_equal "304 Not Modified", @response.headers['Status']
|
||||
assert_equal "304 Not Modified", @response.status
|
||||
assert @response.body.empty?
|
||||
end
|
||||
|
||||
def test_render_against_etag_request_should_200_when_no_match
|
||||
@request.headers["HTTP_IF_NONE_MATCH"] = etag_for("hello somewhere else")
|
||||
@request.if_none_match = etag_for("hello somewhere else")
|
||||
get :render_hello_world_from_variable
|
||||
assert_equal "200 OK", @response.headers['Status']
|
||||
assert_equal "200 OK", @response.status
|
||||
assert !@response.body.empty?
|
||||
end
|
||||
|
||||
@@ -508,13 +513,13 @@ class EtagRenderTest < Test::Unit::TestCase
|
||||
expected_etag = etag_for('hello david')
|
||||
assert_equal expected_etag, @response.headers['ETag']
|
||||
|
||||
@request.headers["HTTP_IF_NONE_MATCH"] = expected_etag
|
||||
@request.if_none_match = expected_etag
|
||||
get :render_hello_world_from_variable
|
||||
assert_equal "304 Not Modified", @response.headers['Status']
|
||||
assert_equal "304 Not Modified", @response.status
|
||||
|
||||
@request.headers["HTTP_IF_NONE_MATCH"] = "\"diftag\""
|
||||
@request.if_none_match = "\"diftag\""
|
||||
get :render_hello_world_from_variable
|
||||
assert_equal "200 OK", @response.headers['Status']
|
||||
assert_equal "200 OK", @response.status
|
||||
end
|
||||
|
||||
def render_with_404_shouldnt_have_etag
|
||||
@@ -557,17 +562,17 @@ class LastModifiedRenderTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_request_not_modified
|
||||
@request.headers["HTTP_IF_MODIFIED_SINCE"] = @last_modified
|
||||
@request.if_modified_since = @last_modified
|
||||
get :conditional_hello
|
||||
assert_equal "304 Not Modified", @response.headers['Status']
|
||||
assert_equal "304 Not Modified", @response.status
|
||||
assert @response.body.blank?, @response.body
|
||||
assert_equal @last_modified, @response.headers['Last-Modified']
|
||||
end
|
||||
|
||||
def test_request_modified
|
||||
@request.headers["HTTP_IF_MODIFIED_SINCE"] = 'Thu, 16 Jul 2008 00:00:00 GMT'
|
||||
@request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
|
||||
get :conditional_hello
|
||||
assert_equal "200 OK", @response.headers['Status']
|
||||
assert_equal "200 OK", @response.status
|
||||
assert !@response.body.blank?
|
||||
assert_equal @last_modified, @response.headers['Last-Modified']
|
||||
end
|
||||
|
||||
@@ -15,57 +15,57 @@ class RequestTest < Test::Unit::TestCase
|
||||
assert_equal '0.0.0.0', @request.remote_ip
|
||||
|
||||
@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(true)
|
||||
|
||||
@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(true)
|
||||
|
||||
@request.remote_addr = '192.168.0.1'
|
||||
assert_equal '2.3.4.5', @request.remote_ip
|
||||
assert_equal '2.3.4.5', @request.remote_ip(true)
|
||||
@request.env.delete 'HTTP_CLIENT_IP'
|
||||
|
||||
@request.remote_addr = '1.2.3.4'
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
|
||||
assert_equal '1.2.3.4', @request.remote_ip
|
||||
assert_equal '1.2.3.4', @request.remote_ip(true)
|
||||
|
||||
@request.remote_addr = '127.0.0.1'
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
assert_equal '3.4.5.6', @request.remote_ip(true)
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,3.4.5.6'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
assert_equal '3.4.5.6', @request.remote_ip(true)
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '172.16.0.1,3.4.5.6'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
assert_equal '3.4.5.6', @request.remote_ip(true)
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '192.168.0.1,3.4.5.6'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
assert_equal '3.4.5.6', @request.remote_ip(true)
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1,3.4.5.6'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
assert_equal '3.4.5.6', @request.remote_ip(true)
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1, 10.0.0.1, 3.4.5.6'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
assert_equal '3.4.5.6', @request.remote_ip(true)
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
assert_equal '3.4.5.6', @request.remote_ip(true)
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1'
|
||||
assert_equal 'unknown', @request.remote_ip
|
||||
assert_equal 'unknown', @request.remote_ip(true)
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
|
||||
assert_equal '3.4.5.6', @request.remote_ip
|
||||
assert_equal '3.4.5.6', @request.remote_ip(true)
|
||||
|
||||
@request.env['HTTP_CLIENT_IP'] = '8.8.8.8'
|
||||
e = assert_raises(ActionController::ActionControllerError) {
|
||||
@request.remote_ip
|
||||
@request.remote_ip(true)
|
||||
}
|
||||
assert_match /IP spoofing attack/, 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
|
||||
|
||||
@request.env['HTTP_X_FORWARDED_FOR'] = '8.8.8.8, 9.9.9.9'
|
||||
assert_equal '8.8.8.8', @request.remote_ip
|
||||
assert_equal '8.8.8.8', @request.remote_ip(true)
|
||||
|
||||
@request.env.delete 'HTTP_CLIENT_IP'
|
||||
@request.env.delete 'HTTP_X_FORWARDED_FOR'
|
||||
@@ -168,58 +168,58 @@ class RequestTest < Test::Unit::TestCase
|
||||
ActionController::Base.relative_url_root = nil
|
||||
|
||||
# The following tests are for when REQUEST_URI is not supplied (as in IIS)
|
||||
@request.set_REQUEST_URI nil
|
||||
@request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1"
|
||||
@request.env['SCRIPT_NAME'] = nil #"/path/dispatch.rb"
|
||||
@request.set_REQUEST_URI nil
|
||||
assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
|
||||
assert_equal "/path/of/some/uri", @request.path
|
||||
|
||||
ActionController::Base.relative_url_root = '/path'
|
||||
@request.set_REQUEST_URI nil
|
||||
@request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1"
|
||||
@request.env['SCRIPT_NAME'] = "/path/dispatch.rb"
|
||||
assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
|
||||
assert_equal "/of/some/uri", @request.path
|
||||
@request.set_REQUEST_URI nil
|
||||
assert_equal "/path/of/some/uri?mapped=1", @request.request_uri(true)
|
||||
assert_equal "/of/some/uri", @request.path(true)
|
||||
ActionController::Base.relative_url_root = nil
|
||||
|
||||
@request.set_REQUEST_URI nil
|
||||
@request.env['PATH_INFO'] = "/path/of/some/uri"
|
||||
@request.env['SCRIPT_NAME'] = nil
|
||||
@request.set_REQUEST_URI nil
|
||||
assert_equal "/path/of/some/uri", @request.request_uri
|
||||
assert_equal "/path/of/some/uri", @request.path
|
||||
|
||||
@request.set_REQUEST_URI nil
|
||||
@request.env['PATH_INFO'] = "/"
|
||||
@request.set_REQUEST_URI nil
|
||||
assert_equal "/", @request.request_uri
|
||||
assert_equal "/", @request.path
|
||||
|
||||
@request.set_REQUEST_URI nil
|
||||
@request.env['PATH_INFO'] = "/?m=b"
|
||||
@request.set_REQUEST_URI nil
|
||||
assert_equal "/?m=b", @request.request_uri
|
||||
assert_equal "/", @request.path
|
||||
|
||||
@request.set_REQUEST_URI nil
|
||||
@request.env['PATH_INFO'] = "/"
|
||||
@request.env['SCRIPT_NAME'] = "/dispatch.cgi"
|
||||
@request.set_REQUEST_URI nil
|
||||
assert_equal "/", @request.request_uri
|
||||
assert_equal "/", @request.path
|
||||
|
||||
ActionController::Base.relative_url_root = '/hieraki'
|
||||
@request.set_REQUEST_URI nil
|
||||
@request.env['PATH_INFO'] = "/hieraki/"
|
||||
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
|
||||
@request.set_REQUEST_URI nil
|
||||
assert_equal "/hieraki/", @request.request_uri
|
||||
assert_equal "/", @request.path
|
||||
ActionController::Base.relative_url_root = nil
|
||||
|
||||
@request.set_REQUEST_URI '/hieraki/dispatch.cgi'
|
||||
ActionController::Base.relative_url_root = '/hieraki'
|
||||
assert_equal "/dispatch.cgi", @request.path
|
||||
assert_equal "/dispatch.cgi", @request.path(true)
|
||||
ActionController::Base.relative_url_root = nil
|
||||
|
||||
@request.set_REQUEST_URI '/hieraki/dispatch.cgi'
|
||||
ActionController::Base.relative_url_root = '/foo'
|
||||
assert_equal "/hieraki/dispatch.cgi", @request.path
|
||||
assert_equal "/hieraki/dispatch.cgi", @request.path(true)
|
||||
ActionController::Base.relative_url_root = nil
|
||||
|
||||
# This test ensures that Rails uses REQUEST_URI over PATH_INFO
|
||||
@@ -227,8 +227,8 @@ class RequestTest < Test::Unit::TestCase
|
||||
@request.env['REQUEST_URI'] = "/some/path"
|
||||
@request.env['PATH_INFO'] = "/another/path"
|
||||
@request.env['SCRIPT_NAME'] = "/dispatch.cgi"
|
||||
assert_equal "/some/path", @request.request_uri
|
||||
assert_equal "/some/path", @request.path
|
||||
assert_equal "/some/path", @request.request_uri(true)
|
||||
assert_equal "/some/path", @request.path(true)
|
||||
end
|
||||
|
||||
def test_host_with_default_port
|
||||
@@ -244,13 +244,13 @@ class RequestTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_server_software
|
||||
assert_equal nil, @request.server_software
|
||||
assert_equal nil, @request.server_software(true)
|
||||
|
||||
@request.env['SERVER_SOFTWARE'] = 'Apache3.422'
|
||||
assert_equal 'apache', @request.server_software
|
||||
assert_equal 'apache', @request.server_software(true)
|
||||
|
||||
@request.env['SERVER_SOFTWARE'] = 'lighttpd(1.1.4)'
|
||||
assert_equal 'lighttpd', @request.server_software
|
||||
assert_equal 'lighttpd', @request.server_software(true)
|
||||
end
|
||||
|
||||
def test_xml_http_request
|
||||
@@ -280,44 +280,44 @@ class RequestTest < Test::Unit::TestCase
|
||||
|
||||
def test_symbolized_request_methods
|
||||
[:get, :post, :put, :delete].each do |method|
|
||||
set_request_method_to method
|
||||
self.request_method = method
|
||||
assert_equal method, @request.method
|
||||
end
|
||||
end
|
||||
|
||||
def test_invalid_http_method_raises_exception
|
||||
set_request_method_to :random_method
|
||||
assert_raises(ActionController::UnknownHttpMethod) do
|
||||
@request.method
|
||||
self.request_method = :random_method
|
||||
end
|
||||
end
|
||||
|
||||
def test_allow_method_hacking_on_post
|
||||
set_request_method_to :post
|
||||
self.request_method = :post
|
||||
[:get, :head, :options, :put, :post, :delete].each do |method|
|
||||
@request.instance_eval { @parameters = { :_method => method } ; @request_method = nil }
|
||||
@request.instance_eval { @parameters = { :_method => method.to_s } ; @request_method = nil }
|
||||
@request.request_method(true)
|
||||
assert_equal(method == :head ? :get : method, @request.method)
|
||||
end
|
||||
end
|
||||
|
||||
def test_invalid_method_hacking_on_post_raises_exception
|
||||
set_request_method_to :post
|
||||
self.request_method = :post
|
||||
@request.instance_eval { @parameters = { :_method => :random_method } ; @request_method = nil }
|
||||
assert_raises(ActionController::UnknownHttpMethod) do
|
||||
@request.method
|
||||
@request.request_method(true)
|
||||
end
|
||||
end
|
||||
|
||||
def test_restrict_method_hacking
|
||||
@request.instance_eval { @parameters = { :_method => 'put' } }
|
||||
[:get, :put, :delete].each do |method|
|
||||
set_request_method_to method
|
||||
self.request_method = method
|
||||
assert_equal method, @request.method
|
||||
end
|
||||
end
|
||||
|
||||
def test_head_masquarading_as_get
|
||||
set_request_method_to :head
|
||||
def test_head_masquerading_as_get
|
||||
self.request_method = :head
|
||||
assert_equal :get, @request.method
|
||||
assert @request.get?
|
||||
assert @request.head?
|
||||
@@ -339,9 +339,16 @@ class RequestTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_nil_format
|
||||
@request.instance_eval { @parameters = { :format => nil } }
|
||||
ActionController::Base.use_accept_header, old =
|
||||
false, ActionController::Base.use_accept_header
|
||||
|
||||
@request.instance_eval { @parameters = {} }
|
||||
@request.env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
|
||||
assert @request.xhr?
|
||||
assert_equal Mime::JS, @request.format
|
||||
|
||||
ensure
|
||||
ActionController::Base.use_accept_header = old
|
||||
end
|
||||
|
||||
def test_content_type
|
||||
@@ -384,9 +391,9 @@ class RequestTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
protected
|
||||
def set_request_method_to(method)
|
||||
def request_method=(method)
|
||||
@request.env['REQUEST_METHOD'] = method.to_s.upcase
|
||||
@request.instance_eval { @request_method = nil }
|
||||
@request.request_method(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
1
actionpack/test/fixtures/_top_level_partial.html.erb
vendored
Normal file
1
actionpack/test/fixtures/_top_level_partial.html.erb
vendored
Normal file
@@ -0,0 +1 @@
|
||||
top level partial html
|
||||
1
actionpack/test/fixtures/_top_level_partial_only.erb
vendored
Normal file
1
actionpack/test/fixtures/_top_level_partial_only.erb
vendored
Normal file
@@ -0,0 +1 @@
|
||||
top level partial
|
||||
1
actionpack/test/fixtures/test/_counter.html.erb
vendored
Normal file
1
actionpack/test/fixtures/test/_counter.html.erb
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<%= counter_counter %>
|
||||
@@ -425,7 +425,8 @@ class AssetTagHelperTest < ActionView::TestCase
|
||||
stylesheet_link_tag(:all, :cache => true)
|
||||
)
|
||||
|
||||
assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
|
||||
expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max
|
||||
assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
|
||||
|
||||
assert_dom_equal(
|
||||
%(<link href="http://a0.example.com/stylesheets/money.css" media="screen" rel="stylesheet" type="text/css" />),
|
||||
|
||||
@@ -3,22 +3,22 @@ require 'abstract_unit'
|
||||
class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase
|
||||
include ActionView::Helpers::DateHelper
|
||||
attr_reader :request
|
||||
|
||||
|
||||
def setup
|
||||
@from = Time.mktime(2004, 6, 6, 21, 45, 0)
|
||||
end
|
||||
|
||||
|
||||
uses_mocha 'date_helper_distance_of_time_in_words_i18n_test' do
|
||||
# distance_of_time_in_words
|
||||
|
||||
def test_distance_of_time_in_words_calls_i18n
|
||||
{ # with include_seconds
|
||||
[2.seconds, true] => [:'less_than_x_seconds', 5],
|
||||
[9.seconds, true] => [:'less_than_x_seconds', 10],
|
||||
[19.seconds, true] => [:'less_than_x_seconds', 20],
|
||||
[30.seconds, true] => [:'half_a_minute', nil],
|
||||
[59.seconds, true] => [:'less_than_x_minutes', 1],
|
||||
[60.seconds, true] => [:'x_minutes', 1],
|
||||
[2.seconds, true] => [:'less_than_x_seconds', 5],
|
||||
[9.seconds, true] => [:'less_than_x_seconds', 10],
|
||||
[19.seconds, true] => [:'less_than_x_seconds', 20],
|
||||
[30.seconds, true] => [:'half_a_minute', nil],
|
||||
[59.seconds, true] => [:'less_than_x_minutes', 1],
|
||||
[60.seconds, true] => [:'x_minutes', 1],
|
||||
|
||||
# without include_seconds
|
||||
[29.seconds, false] => [:'less_than_x_minutes', 1],
|
||||
@@ -38,7 +38,7 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase
|
||||
|
||||
def assert_distance_of_time_in_words_translates_key(passed, expected)
|
||||
diff, include_seconds = *passed
|
||||
key, count = *expected
|
||||
key, count = *expected
|
||||
to = @from + diff
|
||||
|
||||
options = {:locale => 'en-US', :scope => :'datetime.distance_in_words'}
|
||||
@@ -49,11 +49,11 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class DateHelperSelectTagsI18nTests < Test::Unit::TestCase
|
||||
include ActionView::Helpers::DateHelper
|
||||
attr_reader :request
|
||||
|
||||
|
||||
uses_mocha 'date_helper_select_tags_i18n_tests' do
|
||||
def setup
|
||||
I18n.stubs(:translate).with(:'date.month_names', :locale => 'en-US').returns Date::MONTHNAMES
|
||||
|
||||
@@ -557,11 +557,8 @@ class DateHelperTest < ActionView::TestCase
|
||||
end
|
||||
|
||||
def test_select_date_with_incomplete_order
|
||||
expected = %(<select id="date_first_day" name="date[first][day]">\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">15</option>\n<option value="16" selected="selected">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"
|
||||
|
||||
expected << %(<select id="date_first_year" name="date[first][year]">\n)
|
||||
# NOTE: modified this test because of minimal API change
|
||||
expected = %(<select id="date_first_year" name="date[first][year]">\n)
|
||||
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
|
||||
expected << "</select>\n"
|
||||
|
||||
@@ -569,6 +566,10 @@ class DateHelperTest < ActionView::TestCase
|
||||
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">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">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="date_first_day" name="date[first][day]">\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">15</option>\n<option value="16" selected="selected">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, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :order => [:day])
|
||||
end
|
||||
|
||||
@@ -909,6 +910,10 @@ class DateHelperTest < ActionView::TestCase
|
||||
assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { :datetime_separator => "—", :date_separator => "/", :time_separator => ":", :start_year => 2003, :end_year => 2005, :prefix => "date[first]"}, :class => 'selector')
|
||||
end
|
||||
|
||||
def test_select_datetime_should_work_with_date
|
||||
assert_nothing_raised { select_datetime(Date.today) }
|
||||
end
|
||||
|
||||
def test_select_time
|
||||
expected = %(<select id="date_hour" name="date[hour]">\n)
|
||||
expected << %(<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" selected="selected">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)
|
||||
@@ -986,31 +991,8 @@ class DateHelperTest < ActionView::TestCase
|
||||
assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {:include_seconds => false}, :class => 'selector')
|
||||
end
|
||||
|
||||
uses_mocha 'TestDatetimeAndTimeSelectUseTimeCurrentAsDefault' do
|
||||
def test_select_datetime_uses_time_current_as_default
|
||||
time = stub(:year => 2004, :month => 6, :day => 15, :hour => 16, :min => 35, :sec => 0)
|
||||
Time.expects(:current).returns time
|
||||
expects(:select_date).with(time, anything, anything).returns('')
|
||||
expects(:select_time).with(time, anything, anything).returns('')
|
||||
select_datetime
|
||||
end
|
||||
|
||||
def test_select_time_uses_time_current_as_default
|
||||
time = stub(:year => 2004, :month => 6, :day => 15, :hour => 16, :min => 35, :sec => 0)
|
||||
Time.expects(:current).returns time
|
||||
expects(:select_hour).with(time, anything, anything).returns('')
|
||||
expects(:select_minute).with(time, anything, anything).returns('')
|
||||
select_time
|
||||
end
|
||||
|
||||
def test_select_date_uses_date_current_as_default
|
||||
date = stub(:year => 2004, :month => 6, :day => 15)
|
||||
Date.expects(:current).returns date
|
||||
expects(:select_year).with(date, anything, anything).returns('')
|
||||
expects(:select_month).with(date, anything, anything).returns('')
|
||||
expects(:select_day).with(date, anything, anything).returns('')
|
||||
select_date
|
||||
end
|
||||
def test_select_time_should_work_with_date
|
||||
assert_nothing_raised { select_time(Date.today) }
|
||||
end
|
||||
|
||||
def test_date_select
|
||||
@@ -1231,6 +1213,30 @@ class DateHelperTest < ActionView::TestCase
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_date_select_with_separator
|
||||
@post = Post.new
|
||||
@post.written_on = Date.new(2004, 6, 15)
|
||||
|
||||
expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\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 << " / "
|
||||
|
||||
expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\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 << " / "
|
||||
|
||||
expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\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, date_select("post", "written_on", { :date_separator => " / " })
|
||||
end
|
||||
|
||||
def test_time_select
|
||||
@post = Post.new
|
||||
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
|
||||
@@ -1330,6 +1336,33 @@ class DateHelperTest < ActionView::TestCase
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_time_select_with_separator
|
||||
@post = Post.new
|
||||
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
|
||||
|
||||
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)]">\n)
|
||||
0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
|
||||
expected << "</select>\n"
|
||||
|
||||
expected << " - "
|
||||
|
||||
expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
|
||||
0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
|
||||
expected << "</select>\n"
|
||||
|
||||
expected << " - "
|
||||
|
||||
expected << %(<select id="post_written_on_6i" name="post[written_on(6i)]">\n)
|
||||
0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
|
||||
expected << "</select>\n"
|
||||
|
||||
assert_dom_equal expected, time_select("post", "written_on", { :time_separator => " - ", :include_seconds => true })
|
||||
end
|
||||
|
||||
def test_datetime_select
|
||||
@post = Post.new
|
||||
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
|
||||
@@ -1412,6 +1445,47 @@ class DateHelperTest < ActionView::TestCase
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_datetime_select_with_separators
|
||||
@post = Post.new
|
||||
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
|
||||
|
||||
expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\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 << " / "
|
||||
|
||||
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\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 << " / "
|
||||
|
||||
expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\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"
|
||||
|
||||
expected << " , "
|
||||
|
||||
expected << %(<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n)
|
||||
0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
|
||||
expected << "</select>\n"
|
||||
|
||||
expected << " - "
|
||||
|
||||
expected << %(<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n)
|
||||
0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
|
||||
expected << "</select>\n"
|
||||
|
||||
expected << " - "
|
||||
|
||||
expected << %(<select id="post_updated_at_6i" name="post[updated_at(6i)]">\n)
|
||||
0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
|
||||
expected << "</select>\n"
|
||||
|
||||
assert_dom_equal expected, datetime_select("post", "updated_at", { :date_separator => " / ", :datetime_separator => " , ", :time_separator => " - ", :include_seconds => true })
|
||||
end
|
||||
|
||||
def test_date_select_with_zero_value_and_no_start_year
|
||||
expected = %(<select id="date_first_year" name="date[first][year]">\n)
|
||||
(Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
|
||||
@@ -1814,26 +1888,151 @@ class DateHelperTest < ActionView::TestCase
|
||||
assert_dom_equal expected, datetime_select("post", "updated_at", {}, :class => 'selector')
|
||||
end
|
||||
|
||||
uses_mocha 'TestInstanceTagDefaultTimeFromOptions' do
|
||||
def test_instance_tag_default_time_from_options_uses_time_current_as_default_when_hash_passed_as_arg
|
||||
dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3)
|
||||
Time.expects(:current).returns Time.now
|
||||
dummy_instance_tag.send!(:default_time_from_options, :hour => 2)
|
||||
end
|
||||
def test_date_select_should_not_change_passed_options_hash
|
||||
@post = Post.new
|
||||
@post.updated_at = Time.local(2008, 7, 16, 23, 30)
|
||||
|
||||
def test_instance_tag_default_time_from_options_respects_hash_arg_settings_when_time_falls_in_system_local_dst_spring_gap
|
||||
with_env_tz('US/Central') do
|
||||
dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3)
|
||||
Time.stubs(:now).returns Time.local(2006, 4, 2, 1)
|
||||
assert_equal 2, dummy_instance_tag.send!(:default_time_from_options, :hour => 2).hour
|
||||
end
|
||||
end
|
||||
options = {
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}
|
||||
date_select(@post, :updated_at, options)
|
||||
|
||||
def test_instance_tag_default_time_from_options_handles_far_future_date
|
||||
dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3)
|
||||
time = dummy_instance_tag.send!(:default_time_from_options, :year => 2050, :month => 2, :day => 10, :hour => 15, :min => 30, :sec => 45)
|
||||
assert_equal 2050, time.year
|
||||
end
|
||||
# note: the literal hash is intentional to show that the actual options hash isn't modified
|
||||
# don't change this!
|
||||
assert_equal({
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}, options)
|
||||
end
|
||||
|
||||
def test_datetime_select_should_not_change_passed_options_hash
|
||||
@post = Post.new
|
||||
@post.updated_at = Time.local(2008, 7, 16, 23, 30)
|
||||
|
||||
options = {
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}
|
||||
datetime_select(@post, :updated_at, options)
|
||||
|
||||
# note: the literal hash is intentional to show that the actual options hash isn't modified
|
||||
# don't change this!
|
||||
assert_equal({
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}, options)
|
||||
end
|
||||
|
||||
def test_time_select_should_not_change_passed_options_hash
|
||||
@post = Post.new
|
||||
@post.updated_at = Time.local(2008, 7, 16, 23, 30)
|
||||
|
||||
options = {
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}
|
||||
time_select(@post, :updated_at, options)
|
||||
|
||||
# note: the literal hash is intentional to show that the actual options hash isn't modified
|
||||
# don't change this!
|
||||
assert_equal({
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}, options)
|
||||
end
|
||||
|
||||
def test_select_date_should_not_change_passed_options_hash
|
||||
options = {
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}
|
||||
select_date(Date.today, options)
|
||||
|
||||
# note: the literal hash is intentional to show that the actual options hash isn't modified
|
||||
# don't change this!
|
||||
assert_equal({
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}, options)
|
||||
end
|
||||
|
||||
def test_select_datetime_should_not_change_passed_options_hash
|
||||
options = {
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}
|
||||
select_datetime(Time.now, options)
|
||||
|
||||
# note: the literal hash is intentional to show that the actual options hash isn't modified
|
||||
# don't change this!
|
||||
assert_equal({
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}, options)
|
||||
end
|
||||
|
||||
def test_select_time_should_not_change_passed_options_hash
|
||||
options = {
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}
|
||||
select_time(Time.now, options)
|
||||
|
||||
# note: the literal hash is intentional to show that the actual options hash isn't modified
|
||||
# don't change this!
|
||||
assert_equal({
|
||||
:order => [ :year, :month, :day ],
|
||||
:default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
|
||||
:discard_type => false,
|
||||
:include_blank => false,
|
||||
:ignore_date => false,
|
||||
:include_seconds => true
|
||||
}, options)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
@@ -18,35 +18,35 @@ class NumberHelperI18nTests < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_number_to_currency_translates_currency_formats
|
||||
I18n.expects(:translate).with(
|
||||
[:'number.format', :'number.currency.format'], :locale => 'en-US'
|
||||
).returns([@number_defaults, @currency_defaults])
|
||||
I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
|
||||
I18n.expects(:translate).with(:'number.currency.format', :locale => 'en-US',
|
||||
:raise => true).returns(@currency_defaults)
|
||||
number_to_currency(1, :locale => 'en-US')
|
||||
end
|
||||
|
||||
def test_number_with_precision_translates_number_formats
|
||||
I18n.expects(:translate).with(
|
||||
[:'number.format', :'number.precision.format'], :locale => 'en-US'
|
||||
).returns([@number_defaults, @precision_defaults])
|
||||
I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
|
||||
I18n.expects(:translate).with(:'number.precision.format', :locale => 'en-US',
|
||||
:raise => true).returns(@precision_defaults)
|
||||
number_with_precision(1, :locale => 'en-US')
|
||||
end
|
||||
|
||||
def test_number_with_delimiter_translates_number_formats
|
||||
I18n.expects(:translate).with(:'number.format', :locale => 'en-US').returns(@number_defaults)
|
||||
I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
|
||||
number_with_delimiter(1, :locale => 'en-US')
|
||||
end
|
||||
|
||||
def test_number_to_percentage_translates_number_formats
|
||||
I18n.expects(:translate).with(
|
||||
[:'number.format', :'number.percentage.format'], :locale => 'en-US'
|
||||
).returns([@number_defaults, @percentage_defaults])
|
||||
I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
|
||||
I18n.expects(:translate).with(:'number.percentage.format', :locale => 'en-US',
|
||||
:raise => true).returns(@percentage_defaults)
|
||||
number_to_percentage(1, :locale => 'en-US')
|
||||
end
|
||||
|
||||
def test_number_to_human_size_translates_human_formats
|
||||
I18n.expects(:translate).with(
|
||||
[:'number.format', :'number.human.format'], :locale => 'en-US'
|
||||
).returns([@number_defaults, @human_defaults])
|
||||
I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
|
||||
I18n.expects(:translate).with(:'number.human.format', :locale => 'en-US',
|
||||
:raise => true).returns(@human_defaults)
|
||||
# can't be called with 1 because this directly returns without calling I18n.translate
|
||||
number_to_human_size(1025, :locale => 'en-US')
|
||||
end
|
||||
|
||||
@@ -19,6 +19,10 @@ class ViewRenderTest < Test::Unit::TestCase
|
||||
assert_equal "Hello world!", @view.render("test/hello_world")
|
||||
end
|
||||
|
||||
def test_render_file_at_top_level
|
||||
assert_equal 'Elastica', @view.render('/shared')
|
||||
end
|
||||
|
||||
def test_render_file_with_full_path
|
||||
template_path = File.join(File.dirname(__FILE__), '../fixtures/test/hello_world.erb')
|
||||
assert_equal "Hello world!", @view.render(:file => template_path)
|
||||
@@ -47,6 +51,24 @@ class ViewRenderTest < Test::Unit::TestCase
|
||||
assert_equal "only partial", @view.render(:partial => "test/partial_only")
|
||||
end
|
||||
|
||||
def test_render_partial_with_format
|
||||
assert_equal 'partial html', @view.render(:partial => 'test/partial')
|
||||
end
|
||||
|
||||
def test_render_partial_at_top_level
|
||||
# file fixtures/_top_level_partial_only.erb (not fixtures/test)
|
||||
assert_equal 'top level partial', @view.render(:partial => '/top_level_partial_only')
|
||||
end
|
||||
|
||||
def test_render_partial_with_format_at_top_level
|
||||
# file fixtures/_top_level_partial.html.erb (not fixtures/test, with format extension)
|
||||
assert_equal 'top level partial html', @view.render(:partial => '/top_level_partial')
|
||||
end
|
||||
|
||||
def test_render_partial_with_locals
|
||||
assert_equal "5", @view.render(:partial => "test/counter", :locals => { :counter_counter => 5 })
|
||||
end
|
||||
|
||||
def test_render_partial_with_errors
|
||||
assert_raise(ActionView::TemplateError) { @view.render(:partial => "test/raise") }
|
||||
end
|
||||
@@ -54,14 +76,14 @@ class ViewRenderTest < Test::Unit::TestCase
|
||||
def test_render_partial_collection
|
||||
assert_equal "Hello: davidHello: mary", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), Customer.new("mary") ])
|
||||
end
|
||||
|
||||
|
||||
def test_render_partial_collection_as
|
||||
assert_equal "david david davidmary mary mary",
|
||||
assert_equal "david david davidmary mary mary",
|
||||
@view.render(:partial => "test/customer_with_var", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :customer)
|
||||
end
|
||||
|
||||
|
||||
def test_render_partial_collection_without_as
|
||||
assert_equal "local_inspector,local_inspector_counter,object",
|
||||
assert_equal "local_inspector,local_inspector_counter,object",
|
||||
@view.render(:partial => "test/local_inspector", :collection => [ Customer.new("mary") ])
|
||||
end
|
||||
|
||||
|
||||
@@ -277,7 +277,11 @@ class UrlHelperTest < ActionView::TestCase
|
||||
end
|
||||
|
||||
def test_mail_to_with_javascript
|
||||
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")
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%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")
|
||||
end
|
||||
|
||||
def test_mail_to_with_javascript_unicode
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%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%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%22%3e%c3%ba%6e%69%63%6f%64%65%3c%2f%61%3e%27%29%3b'))</script>", mail_to("unicode@example.com", "únicode", :encode => "javascript")
|
||||
end
|
||||
|
||||
def test_mail_with_options
|
||||
@@ -301,8 +305,8 @@ class UrlHelperTest < ActionView::TestCase
|
||||
assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">me(at)domain.com</a>", mail_to("me@domain.com", nil, :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 "<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)")
|
||||
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%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(decodeURIComponent('%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
|
||||
|
||||
def protect_against_forgery?
|
||||
|
||||
@@ -344,7 +344,7 @@ module ActiveRecord
|
||||
callback(:before_add, record)
|
||||
yield(record) if block_given?
|
||||
@target ||= [] unless loaded?
|
||||
@target << record
|
||||
@target << record unless @reflection.options[:uniq] && @target.include?(record)
|
||||
callback(:after_add, record)
|
||||
record
|
||||
end
|
||||
|
||||
@@ -2599,7 +2599,7 @@ module ActiveRecord #:nodoc:
|
||||
removed_attributes = attributes.keys - safe_attributes.keys
|
||||
|
||||
if removed_attributes.any?
|
||||
logger.debug "WARNING: Can't mass-assign these protected attributes: #{removed_attributes.join(', ')}"
|
||||
log_protected_attribute_removal(removed_attributes)
|
||||
end
|
||||
|
||||
safe_attributes
|
||||
@@ -2614,6 +2614,10 @@ module ActiveRecord #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
def log_protected_attribute_removal(*attributes)
|
||||
logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}"
|
||||
end
|
||||
|
||||
# The primary key and inheritance column can never be set by mass-assignment for security reasons.
|
||||
def attributes_protected_by_default
|
||||
default = [ self.class.primary_key, self.class.inheritance_column ]
|
||||
@@ -2627,8 +2631,15 @@ module ActiveRecord #:nodoc:
|
||||
quoted = {}
|
||||
connection = self.class.connection
|
||||
attribute_names.each do |name|
|
||||
if column = column_for_attribute(name)
|
||||
quoted[name] = connection.quote(read_attribute(name), column) unless !include_primary_key && column.primary
|
||||
if (column = column_for_attribute(name)) && (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
|
||||
include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
|
||||
|
||||
@@ -211,7 +211,7 @@ module ActiveRecord
|
||||
|
||||
sql << " ORDER BY #{options[:order]} " if options[:order]
|
||||
add_limit!(sql, options, scope)
|
||||
sql << ')' if use_workaround
|
||||
sql << ') AS #{aggregate_alias}_subquery' if use_workaround
|
||||
sql
|
||||
end
|
||||
|
||||
|
||||
@@ -134,7 +134,9 @@ module ActiveRecord
|
||||
|
||||
def update_with_dirty
|
||||
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
|
||||
update_without_dirty
|
||||
end
|
||||
|
||||
@@ -349,6 +349,27 @@ module ActiveRecord
|
||||
end
|
||||
end
|
||||
|
||||
# MigrationProxy is used to defer loading of the actual migration classes
|
||||
# until they are needed
|
||||
class MigrationProxy
|
||||
|
||||
attr_accessor :name, :version, :filename
|
||||
|
||||
delegate :migrate, :announce, :write, :to=>:migration
|
||||
|
||||
private
|
||||
|
||||
def migration
|
||||
@migration ||= load_migration
|
||||
end
|
||||
|
||||
def load_migration
|
||||
load(filename)
|
||||
name.constantize
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Migrator#:nodoc:
|
||||
class << self
|
||||
def migrate(migrations_path, target_version = nil)
|
||||
@@ -437,7 +458,7 @@ module ActiveRecord
|
||||
runnable.pop if down? && !target.nil?
|
||||
|
||||
runnable.each do |migration|
|
||||
Base.logger.info "Migrating to #{migration} (#{migration.version})"
|
||||
Base.logger.info "Migrating to #{migration.name} (#{migration.version})"
|
||||
|
||||
# On our way up, we skip migrating the ones we've already migrated
|
||||
# On our way down, we skip reverting the ones we've never migrated
|
||||
@@ -470,11 +491,10 @@ module ActiveRecord
|
||||
raise DuplicateMigrationNameError.new(name.camelize)
|
||||
end
|
||||
|
||||
load(file)
|
||||
|
||||
klasses << returning(name.camelize.constantize) do |klass|
|
||||
class << klass; attr_accessor :version end
|
||||
klass.version = version
|
||||
klasses << returning(MigrationProxy.new) do |migration|
|
||||
migration.name = name.camelize
|
||||
migration.version = version
|
||||
migration.filename = file
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ module ActiveRecord
|
||||
attr_reader :proxy_scope, :proxy_options
|
||||
|
||||
[].methods.each do |m|
|
||||
unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last|empty?|any?)/
|
||||
unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last|empty?|any?|respond_to?)/
|
||||
delegate m, :to => :proxy_found
|
||||
end
|
||||
end
|
||||
@@ -140,6 +140,10 @@ module ActiveRecord
|
||||
@found ? @found.empty? : count.zero?
|
||||
end
|
||||
|
||||
def respond_to?(method)
|
||||
super || @proxy_scope.respond_to?(method)
|
||||
end
|
||||
|
||||
def any?
|
||||
if block_given?
|
||||
proxy_found.any? { |*block_args| yield(*block_args) }
|
||||
|
||||
@@ -9,7 +9,7 @@ require 'models/topic'
|
||||
require 'models/reply'
|
||||
|
||||
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
|
||||
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
|
||||
|
||||
@@ -21,7 +21,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
||||
fixtures :posts, :comments, :authors, :categories, :categories_posts,
|
||||
:companies, :accounts, :tags, :taggings, :people, :readers,
|
||||
:owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
|
||||
:developers, :projects
|
||||
:developers, :projects, :developers_projects
|
||||
|
||||
def test_loading_with_one_association
|
||||
posts = Post.find(:all, :include => :comments)
|
||||
|
||||
@@ -70,7 +70,7 @@ end
|
||||
|
||||
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||
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
|
||||
david = Developer.find(1)
|
||||
@@ -299,6 +299,17 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||
assert_equal 3, projects(:active_record, :reload).developers.size
|
||||
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
|
||||
david = Developer.find(1)
|
||||
active_record = Project.find(1)
|
||||
|
||||
@@ -14,7 +14,7 @@ require 'models/reader'
|
||||
class HasManyAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :categories, :companies, :developers, :projects,
|
||||
:developers_projects, :topics, :authors, :comments, :author_addresses,
|
||||
:people, :posts
|
||||
:people, :posts, :readers
|
||||
|
||||
def setup
|
||||
Client.destroyed_client_ids.clear
|
||||
|
||||
@@ -76,7 +76,7 @@ class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class BasicsTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations
|
||||
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories
|
||||
|
||||
def test_table_exists
|
||||
assert !NonExistentTable.table_exists?
|
||||
@@ -1361,6 +1361,12 @@ class BasicsTest < ActiveRecord::TestCase
|
||||
assert_equal(myobj, topic.content)
|
||||
end
|
||||
|
||||
def test_serialized_time_attribute
|
||||
myobj = Time.local(2008,1,1,1,0)
|
||||
topic = Topic.create("content" => myobj).reload
|
||||
assert_equal(myobj, topic.content)
|
||||
end
|
||||
|
||||
def test_nil_serialized_attribute_with_class_constraint
|
||||
myobj = MyObject.new('value1', 'value2')
|
||||
topic = Topic.new
|
||||
|
||||
@@ -191,6 +191,18 @@ class DirtyTest < ActiveRecord::TestCase
|
||||
assert !pirate.changed?
|
||||
end
|
||||
|
||||
def test_save_should_store_serialized_attributes_even_with_partial_updates
|
||||
with_partial_updates(Topic) do
|
||||
topic = Topic.create!(:content => {:a => "a"})
|
||||
topic.content[:b] = "b"
|
||||
#assert topic.changed? # Known bug, will fail
|
||||
topic.save!
|
||||
assert_equal "b", topic.content[:b]
|
||||
topic.reload
|
||||
assert_equal "b", topic.content[:b]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_partial_updates(klass, on = true)
|
||||
old = klass.partial_updates?
|
||||
|
||||
@@ -74,7 +74,7 @@ class MultiObserver < ActiveRecord::Observer
|
||||
end
|
||||
|
||||
class LifecycleTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :developers
|
||||
fixtures :topics, :developers, :minimalistics
|
||||
|
||||
def test_before_destroy
|
||||
original_count = Topic.count
|
||||
|
||||
@@ -6,7 +6,7 @@ require 'models/post'
|
||||
require 'models/category'
|
||||
|
||||
class MethodScopingTest < ActiveRecord::TestCase
|
||||
fixtures :developers, :projects, :comments, :posts
|
||||
fixtures :developers, :projects, :comments, :posts, :developers_projects
|
||||
|
||||
def test_set_conditions
|
||||
Developer.with_scope(:find => { :conditions => 'just a test...' }) do
|
||||
|
||||
@@ -922,6 +922,26 @@ if ActiveRecord::Base.connection.supports_migrations?
|
||||
migrations[0].name == 'innocent_jointable'
|
||||
end
|
||||
|
||||
def test_only_loads_pending_migrations
|
||||
# migrate up to 1
|
||||
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
|
||||
|
||||
# now unload the migrations that have been defined
|
||||
PeopleHaveLastNames.unloadable
|
||||
ActiveSupport::Dependencies.remove_unloadable_constants!
|
||||
|
||||
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
|
||||
|
||||
assert !defined? PeopleHaveLastNames
|
||||
|
||||
%w(WeNeedReminders, InnocentJointable).each do |migration|
|
||||
assert defined? migration
|
||||
end
|
||||
|
||||
ensure
|
||||
load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
|
||||
end
|
||||
|
||||
def test_migrator_interleaved_migrations
|
||||
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ class NamedScopeTest < ActiveRecord::TestCase
|
||||
assert_equal Topic.average(:replies_count), Topic.base.average(:replies_count)
|
||||
end
|
||||
|
||||
def test_scope_should_respond_to_own_methods_and_methods_of_the_proxy
|
||||
assert Topic.approved.respond_to?(:proxy_found)
|
||||
assert Topic.approved.respond_to?(:count)
|
||||
assert Topic.approved.respond_to?(:length)
|
||||
end
|
||||
|
||||
def test_subclasses_inherit_scopes
|
||||
assert Topic.scopes.include?(:base)
|
||||
|
||||
@@ -186,9 +192,10 @@ class NamedScopeTest < ActiveRecord::TestCase
|
||||
|
||||
def test_any_should_not_load_results
|
||||
topics = Topic.base
|
||||
assert_queries(1) do
|
||||
topics.expects(:empty?).returns(true)
|
||||
assert !topics.any?
|
||||
assert_queries(2) do
|
||||
topics.any? # use count query
|
||||
topics.collect # force load
|
||||
topics.any? # use loaded (no query)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ end
|
||||
uses_mocha 'QueryCacheExpiryTest' do
|
||||
|
||||
class QueryCacheExpiryTest < ActiveRecord::TestCase
|
||||
fixtures :tasks
|
||||
fixtures :tasks, :posts, :categories, :categories_posts
|
||||
|
||||
def test_find
|
||||
Task.connection.expects(:clear_query_cache).times(1)
|
||||
|
||||
@@ -39,10 +39,6 @@ module ActiveSupport
|
||||
class Store
|
||||
cattr_accessor :logger
|
||||
|
||||
def threadsafe!
|
||||
extend ThreadSafety
|
||||
end
|
||||
|
||||
def silence!
|
||||
@silence = true
|
||||
self
|
||||
@@ -115,20 +111,6 @@ module ActiveSupport
|
||||
logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@silence && !@logger_off
|
||||
end
|
||||
end
|
||||
|
||||
module ThreadSafety #:nodoc:
|
||||
def self.extended(object) #:nodoc:
|
||||
object.instance_variable_set(:@mutex, Mutex.new)
|
||||
end
|
||||
|
||||
%w(read write delete delete_matched exist? increment decrement).each do |method|
|
||||
module_eval <<-EOS, __FILE__, __LINE__
|
||||
def #{method}(*args)
|
||||
@mutex.synchronize { super }
|
||||
end
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
module ActiveSupport
|
||||
module Cache
|
||||
class CompressedMemCacheStore < MemCacheStore
|
||||
def read(name, options = {})
|
||||
if value = super(name, options.merge(:raw => true))
|
||||
def read(name, options = nil)
|
||||
if value = super(name, (options || {}).merge(:raw => true))
|
||||
Marshal.load(ActiveSupport::Gzip.decompress(value))
|
||||
end
|
||||
end
|
||||
|
||||
def write(name, value, options = {})
|
||||
super(name, ActiveSupport::Gzip.compress(Marshal.dump(value)), options.merge(:raw => true))
|
||||
def write(name, value, options = nil)
|
||||
super(name, ActiveSupport::Gzip.compress(Marshal.dump(value)), (options || {}).merge(:raw => true))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,13 +9,13 @@ module ActiveSupport
|
||||
|
||||
def read(name, options = nil)
|
||||
super
|
||||
File.open(real_file_path(name), 'rb') { |f| f.read } rescue nil
|
||||
File.open(real_file_path(name), 'rb') { |f| Marshal.load(f) } rescue nil
|
||||
end
|
||||
|
||||
def write(name, value, options = nil)
|
||||
super
|
||||
ensure_cache_path(File.dirname(real_file_path(name)))
|
||||
File.open(real_file_path(name), "wb+") { |f| f.write(value) }
|
||||
File.atomic_write(real_file_path(name), cache_path) { |f| Marshal.dump(value, f) }
|
||||
rescue => e
|
||||
RAILS_DEFAULT_LOGGER.error "Couldn't create cache directory: #{name} (#{e.message})" if RAILS_DEFAULT_LOGGER
|
||||
end
|
||||
|
||||
@@ -3,6 +3,13 @@ module ActiveSupport
|
||||
class MemoryStore < Store
|
||||
def initialize
|
||||
@data = {}
|
||||
@mutex = Mutex.new
|
||||
end
|
||||
|
||||
def fetch(key, options = {})
|
||||
@mutex.synchronize do
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def read(name, options = nil)
|
||||
@@ -16,23 +23,32 @@ module ActiveSupport
|
||||
end
|
||||
|
||||
def delete(name, options = nil)
|
||||
super
|
||||
@data.delete(name)
|
||||
end
|
||||
|
||||
def delete_matched(matcher, options = nil)
|
||||
super
|
||||
@data.delete_if { |k,v| k =~ matcher }
|
||||
end
|
||||
|
||||
def exist?(name,options = nil)
|
||||
super
|
||||
@data.has_key?(name)
|
||||
end
|
||||
|
||||
def increment(key, amount = 1)
|
||||
@mutex.synchronize do
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def decrement(key, amount = 1)
|
||||
@mutex.synchronize do
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def clear
|
||||
@data.clear
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
require 'tempfile'
|
||||
require 'active_support/core_ext/file/atomic'
|
||||
|
||||
# Write to a file atomically. Useful for situations where you don't
|
||||
# want other processes or threads to see half-written files.
|
||||
#
|
||||
# File.atomic_write("important.file") do |file|
|
||||
# file.write("hello")
|
||||
# end
|
||||
#
|
||||
# If your temp directory is not on the same filesystem as the file you're
|
||||
# trying to write, you can provide a different temporary directory.
|
||||
#
|
||||
# File.atomic_write("/data/something.important", "/data/tmp") do |f|
|
||||
# file.write("hello")
|
||||
# end
|
||||
def File.atomic_write(file_name, temp_dir = Dir.tmpdir)
|
||||
temp_file = Tempfile.new(File.basename(file_name), temp_dir)
|
||||
yield temp_file
|
||||
temp_file.close
|
||||
File.rename(temp_file.path, file_name)
|
||||
end
|
||||
class File #:nodoc:
|
||||
extend ActiveSupport::CoreExtensions::File::Atomic
|
||||
end
|
||||
|
||||
46
activesupport/lib/active_support/core_ext/file/atomic.rb
Normal file
46
activesupport/lib/active_support/core_ext/file/atomic.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
require 'tempfile'
|
||||
|
||||
module ActiveSupport #:nodoc:
|
||||
module CoreExtensions #:nodoc:
|
||||
module File #:nodoc:
|
||||
module Atomic
|
||||
# Write to a file atomically. Useful for situations where you don't
|
||||
# want other processes or threads to see half-written files.
|
||||
#
|
||||
# File.atomic_write("important.file") do |file|
|
||||
# file.write("hello")
|
||||
# end
|
||||
#
|
||||
# If your temp directory is not on the same filesystem as the file you're
|
||||
# trying to write, you can provide a different temporary directory.
|
||||
#
|
||||
# File.atomic_write("/data/something.important", "/data/tmp") do |f|
|
||||
# file.write("hello")
|
||||
# end
|
||||
def atomic_write(file_name, temp_dir = Dir.tmpdir)
|
||||
temp_file = Tempfile.new(basename(file_name), temp_dir)
|
||||
yield temp_file
|
||||
temp_file.close
|
||||
|
||||
begin
|
||||
# Get original file permissions
|
||||
old_stat = stat(file_name)
|
||||
rescue Errno::ENOENT
|
||||
# No old permissions, write a temp file to determine the defaults
|
||||
check_name = ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}"
|
||||
new(check_name, "w")
|
||||
old_stat = stat(check_name)
|
||||
unlink(check_name)
|
||||
end
|
||||
|
||||
# Overwrite original file with temp file
|
||||
rename(temp_file.path, file_name)
|
||||
|
||||
# Set correct permissions on new file
|
||||
chown(old_stat.uid, old_stat.gid, file_name)
|
||||
chmod(old_stat.mode, file_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -291,11 +291,14 @@ module ActiveSupport
|
||||
# NameError is raised when the name is not in CamelCase or the constant is
|
||||
# unknown.
|
||||
def constantize(camel_cased_word)
|
||||
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
|
||||
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
|
||||
end
|
||||
names = camel_cased_word.split('::')
|
||||
names.shift if names.empty? || names.first.empty?
|
||||
|
||||
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
||||
constant = Object
|
||||
names.each do |name|
|
||||
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
||||
end
|
||||
constant
|
||||
end
|
||||
|
||||
# Turns a number into an ordinal string used to denote the position in an
|
||||
@@ -326,4 +329,4 @@ require 'active_support/inflections'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
unless String.included_modules.include?(ActiveSupport::CoreExtensions::String::Inflections)
|
||||
String.send :include, ActiveSupport::CoreExtensions::String::Inflections
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,18 +10,37 @@ module ActiveSupport
|
||||
end
|
||||
|
||||
def freeze_with_memoizable
|
||||
methods.each do |method|
|
||||
__send__($1) if method.to_s =~ /^_unmemoized_(.*)/
|
||||
end unless frozen?
|
||||
|
||||
memoize_all unless frozen?
|
||||
freeze_without_memoizable
|
||||
end
|
||||
|
||||
def memoize_all
|
||||
methods.each do |m|
|
||||
if m.to_s =~ /^_unmemoized_(.*)/
|
||||
if method(m).arity == 0
|
||||
__send__($1)
|
||||
else
|
||||
ivar = :"@_memoized_#{$1}"
|
||||
instance_variable_set(ivar, {})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unmemoize_all
|
||||
methods.each do |m|
|
||||
if m.to_s =~ /^_unmemoized_(.*)/
|
||||
ivar = :"@_memoized_#{$1}"
|
||||
instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def memoize(*symbols)
|
||||
symbols.each do |symbol|
|
||||
original_method = "_unmemoized_#{symbol}"
|
||||
memoized_ivar = "@_memoized_#{symbol}"
|
||||
original_method = :"_unmemoized_#{symbol}"
|
||||
memoized_ivar = :"@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}"
|
||||
|
||||
class_eval <<-EOS, __FILE__, __LINE__
|
||||
include Freezable
|
||||
@@ -29,14 +48,27 @@ module ActiveSupport
|
||||
raise "Already memoized #{symbol}" if method_defined?(:#{original_method})
|
||||
alias #{original_method} #{symbol}
|
||||
|
||||
def #{symbol}(*args)
|
||||
#{memoized_ivar} ||= {}
|
||||
reload = args.pop if args.last == true || args.last == :reload
|
||||
if instance_method(:#{symbol}).arity == 0
|
||||
def #{symbol}(reload = false)
|
||||
if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty?
|
||||
#{memoized_ivar} = [#{original_method}.freeze]
|
||||
end
|
||||
#{memoized_ivar}[0]
|
||||
end
|
||||
else
|
||||
def #{symbol}(*args)
|
||||
#{memoized_ivar} ||= {} unless frozen?
|
||||
reload = args.pop if args.last == true || args.last == :reload
|
||||
|
||||
if !reload && #{memoized_ivar} && #{memoized_ivar}.has_key?(args)
|
||||
#{memoized_ivar}[args]
|
||||
else
|
||||
#{memoized_ivar}[args] = #{original_method}(*args).freeze
|
||||
if #{memoized_ivar}
|
||||
if !reload && #{memoized_ivar}.has_key?(args)
|
||||
#{memoized_ivar}[args]
|
||||
elsif #{memoized_ivar}
|
||||
#{memoized_ivar}[args] = #{original_method}(*args).freeze
|
||||
end
|
||||
else
|
||||
#{original_method}(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
EOS
|
||||
|
||||
@@ -71,69 +71,29 @@ uses_mocha 'high-level cache store tests' do
|
||||
end
|
||||
end
|
||||
|
||||
class ThreadSafetyCacheStoreTest < Test::Unit::TestCase
|
||||
class FileStoreTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@cache = ActiveSupport::Cache.lookup_store(:memory_store).threadsafe!
|
||||
@cache = ActiveSupport::Cache.lookup_store(:file_store, Dir.pwd)
|
||||
end
|
||||
|
||||
def test_should_read_and_write_strings
|
||||
@cache.write('foo', 'bar')
|
||||
|
||||
# No way to have mocha proxy to the original method
|
||||
@mutex = @cache.instance_variable_get(:@mutex)
|
||||
@mutex.instance_eval %(
|
||||
def calls; @calls; end
|
||||
def synchronize
|
||||
@calls ||= 0
|
||||
@calls += 1
|
||||
yield
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
def test_read_is_synchronized
|
||||
assert_equal 'bar', @cache.read('foo')
|
||||
assert_equal 1, @mutex.calls
|
||||
ensure
|
||||
File.delete("foo.cache")
|
||||
end
|
||||
|
||||
def test_write_is_synchronized
|
||||
@cache.write('foo', 'baz')
|
||||
assert_equal 'baz', @cache.read('foo')
|
||||
assert_equal 2, @mutex.calls
|
||||
def test_should_read_and_write_hash
|
||||
@cache.write('foo', {:a => "b"})
|
||||
assert_equal({:a => "b"}, @cache.read('foo'))
|
||||
ensure
|
||||
File.delete("foo.cache")
|
||||
end
|
||||
|
||||
def test_delete_is_synchronized
|
||||
assert_equal 'bar', @cache.read('foo')
|
||||
@cache.delete('foo')
|
||||
def test_should_read_and_write_nil
|
||||
@cache.write('foo', nil)
|
||||
assert_equal nil, @cache.read('foo')
|
||||
assert_equal 3, @mutex.calls
|
||||
end
|
||||
|
||||
def test_delete_matched_is_synchronized
|
||||
assert_equal 'bar', @cache.read('foo')
|
||||
@cache.delete_matched(/foo/)
|
||||
assert_equal nil, @cache.read('foo')
|
||||
assert_equal 3, @mutex.calls
|
||||
end
|
||||
|
||||
def test_fetch_is_synchronized
|
||||
assert_equal 'bar', @cache.fetch('foo') { 'baz' }
|
||||
assert_equal 'fu', @cache.fetch('bar') { 'fu' }
|
||||
assert_equal 3, @mutex.calls
|
||||
end
|
||||
|
||||
def test_exist_is_synchronized
|
||||
assert @cache.exist?('foo')
|
||||
assert !@cache.exist?('bar')
|
||||
assert_equal 2, @mutex.calls
|
||||
end
|
||||
|
||||
def test_increment_is_synchronized
|
||||
@cache.write('foo_count', 1)
|
||||
assert_equal 2, @cache.increment('foo_count')
|
||||
assert_equal 4, @mutex.calls
|
||||
end
|
||||
|
||||
def test_decrement_is_synchronized
|
||||
@cache.write('foo_count', 1)
|
||||
assert_equal 0, @cache.decrement('foo_count')
|
||||
assert_equal 4, @mutex.calls
|
||||
ensure
|
||||
File.delete("foo.cache")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
class AtomicWriteTest < Test::Unit::TestCase
|
||||
|
||||
def test_atomic_write_without_errors
|
||||
contents = "Atomic Text"
|
||||
contents = "Atomic Text"
|
||||
File.atomic_write(file_name, Dir.pwd) do |file|
|
||||
file.write(contents)
|
||||
assert !File.exist?(file_name)
|
||||
@@ -13,7 +12,7 @@ class AtomicWriteTest < Test::Unit::TestCase
|
||||
ensure
|
||||
File.unlink(file_name) rescue nil
|
||||
end
|
||||
|
||||
|
||||
def test_atomic_write_doesnt_write_when_block_raises
|
||||
File.atomic_write(file_name) do |file|
|
||||
file.write("testing")
|
||||
@@ -22,8 +21,47 @@ class AtomicWriteTest < Test::Unit::TestCase
|
||||
rescue
|
||||
assert !File.exist?(file_name)
|
||||
end
|
||||
|
||||
def file_name
|
||||
"atomic.file"
|
||||
|
||||
def test_atomic_write_preserves_file_permissions
|
||||
contents = "Atomic Text"
|
||||
File.open(file_name, "w", 0755) do |file|
|
||||
file.write(contents)
|
||||
assert File.exist?(file_name)
|
||||
end
|
||||
assert File.exist?(file_name)
|
||||
assert_equal 0100755, file_mode
|
||||
assert_equal contents, File.read(file_name)
|
||||
|
||||
File.atomic_write(file_name, Dir.pwd) do |file|
|
||||
file.write(contents)
|
||||
assert File.exist?(file_name)
|
||||
end
|
||||
assert File.exist?(file_name)
|
||||
assert_equal 0100755, file_mode
|
||||
assert_equal contents, File.read(file_name)
|
||||
ensure
|
||||
File.unlink(file_name) rescue nil
|
||||
end
|
||||
|
||||
def test_atomic_write_preserves_default_file_permissions
|
||||
contents = "Atomic Text"
|
||||
File.atomic_write(file_name, Dir.pwd) do |file|
|
||||
file.write(contents)
|
||||
assert !File.exist?(file_name)
|
||||
end
|
||||
assert File.exist?(file_name)
|
||||
assert_equal 0100666 ^ File.umask, file_mode
|
||||
assert_equal contents, File.read(file_name)
|
||||
ensure
|
||||
File.unlink(file_name) rescue nil
|
||||
end
|
||||
|
||||
private
|
||||
def file_name
|
||||
"atomic.file"
|
||||
end
|
||||
|
||||
def file_mode
|
||||
File.stat(file_name).mode
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,6 +16,16 @@ uses_mocha 'Memoizable' do
|
||||
"Josh"
|
||||
end
|
||||
|
||||
def name?
|
||||
true
|
||||
end
|
||||
memoize :name?
|
||||
|
||||
def update(name)
|
||||
"Joshua"
|
||||
end
|
||||
memoize :update
|
||||
|
||||
def age
|
||||
@age_calls += 1
|
||||
nil
|
||||
@@ -88,6 +98,10 @@ uses_mocha 'Memoizable' do
|
||||
assert_equal 1, @person.name_calls
|
||||
end
|
||||
|
||||
def test_memoization_with_punctuation
|
||||
assert_equal true, @person.name?
|
||||
end
|
||||
|
||||
def test_memoization_with_nil_value
|
||||
assert_equal nil, @person.age
|
||||
assert_equal 1, @person.age_calls
|
||||
@@ -96,6 +110,11 @@ uses_mocha 'Memoizable' do
|
||||
assert_equal 1, @person.age_calls
|
||||
end
|
||||
|
||||
def test_memorized_results_are_immutable
|
||||
assert_equal "Josh", @person.name
|
||||
assert_raise(ActiveSupport::FrozenObjectError) { @person.name.gsub!("Josh", "Gosh") }
|
||||
end
|
||||
|
||||
def test_reloadable
|
||||
counter = @calculator.counter
|
||||
assert_equal 1, @calculator.counter
|
||||
@@ -105,6 +124,21 @@ uses_mocha 'Memoizable' do
|
||||
assert_equal 3, @calculator.counter
|
||||
end
|
||||
|
||||
def test_unmemoize_all
|
||||
assert_equal 1, @calculator.counter
|
||||
|
||||
assert @calculator.instance_variable_get(:@_memoized_counter).any?
|
||||
@calculator.unmemoize_all
|
||||
assert @calculator.instance_variable_get(:@_memoized_counter).empty?
|
||||
|
||||
assert_equal 2, @calculator.counter
|
||||
end
|
||||
|
||||
def test_memoize_all
|
||||
@calculator.memoize_all
|
||||
assert @calculator.instance_variable_defined?(:@_memoized_counter)
|
||||
end
|
||||
|
||||
def test_memoization_cache_is_different_for_each_instance
|
||||
assert_equal 1, @calculator.counter
|
||||
assert_equal 2, @calculator.counter(:reload)
|
||||
@@ -114,6 +148,7 @@ uses_mocha 'Memoizable' do
|
||||
def test_memoized_is_not_affected_by_freeze
|
||||
@person.freeze
|
||||
assert_equal "Josh", @person.name
|
||||
assert_equal "Joshua", @person.update("Joshua")
|
||||
end
|
||||
|
||||
def test_memoization_with_args
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
*Edge*
|
||||
|
||||
* Added config.threadsafe! to toggle allow concurrency settings and disable the dependency loader [Josh Peek]
|
||||
|
||||
* Turn cache_classes on by default [Josh Peek]
|
||||
|
||||
* Added configurable eager load paths. Defaults to app/models, app/controllers, and app/helpers [Josh Peek]
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
# Code is not reloaded between requests
|
||||
config.cache_classes = true
|
||||
|
||||
# Enable threaded mode
|
||||
# config.threadsafe!
|
||||
|
||||
# Use a different logger for distributed setups
|
||||
# config.logger = SyslogLogger.new
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ if code_or_file.nil?
|
||||
$stderr.puts "Run '#{$0} -h' for help."
|
||||
exit 1
|
||||
elsif File.exist?(code_or_file)
|
||||
eval(File.read(code_or_file))
|
||||
eval(File.read(code_or_file), nil, code_or_file)
|
||||
else
|
||||
eval(code_or_file)
|
||||
end
|
||||
|
||||
@@ -340,9 +340,11 @@ Run `rake gems:install` to install the missing gems.
|
||||
end
|
||||
|
||||
def load_view_paths
|
||||
ActionView::PathSet::Path.eager_load_templates! if configuration.cache_classes
|
||||
ActionMailer::Base.template_root.load if configuration.frameworks.include?(:action_mailer)
|
||||
ActionController::Base.view_paths.load if configuration.frameworks.include?(:action_controller)
|
||||
if configuration.frameworks.include?(:action_view)
|
||||
ActionView::PathSet::Path.eager_load_templates! if configuration.cache_classes
|
||||
ActionController::Base.view_paths.load if configuration.frameworks.include?(:action_controller)
|
||||
ActionMailer::Base.template_root.load if configuration.frameworks.include?(:action_mailer)
|
||||
end
|
||||
end
|
||||
|
||||
# Eager load application classes
|
||||
@@ -440,9 +442,11 @@ Run `rake gems:install` to install the missing gems.
|
||||
# paths have already been set, it is not changed, otherwise it is
|
||||
# set to use Configuration#view_path.
|
||||
def initialize_framework_views
|
||||
view_path = ActionView::PathSet::Path.new(configuration.view_path, false)
|
||||
ActionMailer::Base.template_root ||= view_path if configuration.frameworks.include?(:action_mailer)
|
||||
ActionController::Base.view_paths = view_path if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.empty?
|
||||
if configuration.frameworks.include?(:action_view)
|
||||
view_path = ActionView::PathSet::Path.new(configuration.view_path, false)
|
||||
ActionMailer::Base.template_root ||= view_path if configuration.frameworks.include?(:action_mailer)
|
||||
ActionController::Base.view_paths = view_path if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.empty?
|
||||
end
|
||||
end
|
||||
|
||||
# If Action Controller is not one of the loaded frameworks (Configuration#frameworks)
|
||||
@@ -688,13 +692,17 @@ Run `rake gems:install` to install the missing gems.
|
||||
# You can add gems with the #gem method.
|
||||
attr_accessor :gems
|
||||
|
||||
# Adds a single Gem dependency to the rails application.
|
||||
# Adds a single Gem dependency to the rails application. By default, it will require
|
||||
# the library with the same name as the gem. Use :lib to specify a different name.
|
||||
#
|
||||
# # gem 'aws-s3', '>= 0.4.0'
|
||||
# # require 'aws/s3'
|
||||
# config.gem 'aws-s3', :lib => 'aws/s3', :version => '>= 0.4.0', \
|
||||
# :source => "http://code.whytheluckystiff.net"
|
||||
#
|
||||
# To require a library be installed, but not attempt to load it, pass :lib => false
|
||||
#
|
||||
# config.gem 'qrp', :version => '0.4.1', :lib => false
|
||||
def gem(name, options = {})
|
||||
@gems << Rails::GemDependency.new(name, options)
|
||||
end
|
||||
@@ -764,6 +772,17 @@ Run `rake gems:install` to install the missing gems.
|
||||
::RAILS_ROOT.replace @root_path
|
||||
end
|
||||
|
||||
# Enable threaded mode. Allows concurrent requests to controller actions and
|
||||
# multiple database connections. Also disables automatic dependency loading
|
||||
# after boot
|
||||
def threadsafe!
|
||||
self.cache_classes = true
|
||||
self.dependency_loading = false
|
||||
self.active_record.allow_concurrency = true
|
||||
self.action_controller.allow_concurrency = true
|
||||
self
|
||||
end
|
||||
|
||||
# Loads and returns the contents of the #database_configuration_file. The
|
||||
# contents of the file are processed via ERB before being sent through
|
||||
# YAML::load.
|
||||
|
||||
@@ -58,7 +58,7 @@ module Rails
|
||||
|
||||
def load
|
||||
return if @loaded || @load_paths_added == false
|
||||
require(@lib || @name)
|
||||
require(@lib || @name) unless @lib == false
|
||||
@loaded = true
|
||||
rescue LoadError
|
||||
puts $!.to_s
|
||||
|
||||
@@ -182,11 +182,11 @@ namespace :db do
|
||||
end
|
||||
|
||||
namespace :fixtures do
|
||||
desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z."
|
||||
desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
|
||||
task :load => :environment do
|
||||
require 'active_record/fixtures'
|
||||
ActiveRecord::Base.establish_connection(Rails.env)
|
||||
base_dir = File.join(Rails.root, 'test', 'fixtures')
|
||||
base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures')
|
||||
fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir
|
||||
|
||||
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file|
|
||||
@@ -194,7 +194,7 @@ namespace :db do
|
||||
end
|
||||
end
|
||||
|
||||
desc "Search for a fixture given a LABEL or ID."
|
||||
desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
|
||||
task :identify => :environment do
|
||||
require "active_record/fixtures"
|
||||
|
||||
@@ -203,7 +203,8 @@ namespace :db do
|
||||
|
||||
puts %Q(The fixture ID for "#{label}" is #{Fixtures.identify(label)}.) if label
|
||||
|
||||
Dir["#{RAILS_ROOT}/test/fixtures/**/*.yml"].each do |file|
|
||||
base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures')
|
||||
Dir["#{base_dir}/**/*.yml"].each do |file|
|
||||
if data = YAML::load(ERB.new(IO.read(file)).result)
|
||||
data.keys.each do |key|
|
||||
key_id = Fixtures.identify(key)
|
||||
|
||||
@@ -11,6 +11,7 @@ uses_mocha "Plugin Tests" do
|
||||
@gem_with_source = Rails::GemDependency.new "hpricot", :source => "http://code.whytheluckystiff.net"
|
||||
@gem_with_version = Rails::GemDependency.new "hpricot", :version => "= 0.6"
|
||||
@gem_with_lib = Rails::GemDependency.new "aws-s3", :lib => "aws/s3"
|
||||
@gem_without_load = Rails::GemDependency.new "hpricot", :lib => false
|
||||
end
|
||||
|
||||
def test_configuration_adds_gem_dependency
|
||||
@@ -62,5 +63,13 @@ uses_mocha "Plugin Tests" do
|
||||
@gem_with_lib.add_load_paths
|
||||
@gem_with_lib.load
|
||||
end
|
||||
|
||||
def test_gem_without_lib_loading
|
||||
@gem_without_load.expects(:gem).with(@gem_without_load.name)
|
||||
@gem_without_load.expects(:require).with(@gem_without_load.lib).never
|
||||
@gem_without_load.add_load_paths
|
||||
@gem_without_load.load
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user