Merge branch 'master' into docrails_master

This commit is contained in:
wycats
2010-04-10 17:22:52 -04:00
111 changed files with 1336 additions and 1916 deletions

View File

@@ -3,7 +3,7 @@ module ActionMailer
MAJOR = 3
MINOR = 0
TINY = 0
BUILD = "beta2"
BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end

View File

@@ -1,4 +1,21 @@
*Rails 3.0.0 [Edge] (pending)*
*Rails 3.0.0 [beta 3] (pending)*
* Removed verify method in controllers. [JV]
It's now available as a plugin at http://github.com/rails/verification
* Removed input, form, error_messages_for and error_message_on from views. [JV]
It's now available as a plugin at http://github.com/rails/dynamic_form
* Routes can be scoped by controller module. [Jeremy Kemper]
# /session => Auth::SessionsController
scope :module => 'auth' do
resource :session
end
* Added #favicon_link_tag, it uses #image_path so in particular the favicon gets an asset ID [fxn]
* Fixed that default locale templates should be used if the current locale template is missing [DHH]
* Added all the new HTML5 form types as individual form tag methods (search, url, number, etc) #3646 [Stephen Celis]

View File

@@ -33,7 +33,6 @@ module ActionController
autoload :Streaming
autoload :Testing
autoload :UrlFor
autoload :Verification
end
autoload :Dispatcher, 'action_controller/deprecated/dispatcher'

View File

@@ -30,7 +30,6 @@ module ActionController
Cookies,
Flash,
Verification,
RequestForgeryProtection,
Streaming,
RecordIdentifier,

View File

@@ -38,22 +38,22 @@ module ActionController #:nodoc:
extend ActiveSupport::Concern
included do
@@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
##
# :singleton-method:
# The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
# For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
# this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
# web server to look in the new location for cached files.
@@page_cache_directory = ''
cattr_accessor :page_cache_directory
@@page_cache_extension = '.html'
##
# :singleton-method:
# Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
# order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
# If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
# extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
@@page_cache_extension = '.html'
cattr_accessor :page_cache_extension
end

View File

@@ -6,15 +6,6 @@ module ActionController
deprecated_config_writer(option, message)
end
# This method has been moved to ActionDispatch::Request.filter_parameters
def filter_parameter_logging(*args, &block)
ActiveSupport::Deprecation.warn("Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead", caller)
filter = Rails.application.config.filter_parameters
filter.concat(args)
filter << block if block
filter
end
def deprecated_config_reader(option, message = nil)
message ||= "Reading #{option} directly from ActionController::Base is deprecated. " \
"Please read it from config.#{option}"
@@ -136,6 +127,25 @@ module ActionController
end
end
module DeprecatedBehavior
# This method has been moved to ActionDispatch::Request.filter_parameters
def filter_parameter_logging(*args, &block)
ActiveSupport::Deprecation.warn("Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead", caller)
filter = Rails.application.config.filter_parameters
filter.concat(args)
filter << block if block
filter
end
# This was moved to a plugin
def verify(*args)
ActiveSupport::Deprecation.warn "verify was removed from Rails and is now available as a plugin. " <<
"Please install it with `rails plugin install git://github.com/rails/verification.git`.", caller
end
end
extend DeprecatedBehavior
deprecated_config_writer :session_store
deprecated_config_writer :session_options
deprecated_config_accessor :relative_url_root, "relative_url_root is ineffective. Please stop using it"

View File

@@ -32,8 +32,6 @@ module ActionController
def rescue_action(env)
raise env["action_dispatch.rescue.exception"]
end
self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
end
# For old tests

View File

@@ -135,7 +135,6 @@ module ActionController #:nodoc:
def to_format
default_render
rescue ActionView::MissingTemplate => e
raise unless resourceful?
api_behavior(e)
end
@@ -154,6 +153,8 @@ module ActionController #:nodoc:
# This is the common behavior for "API" requests, like :xml and :json.
def api_behavior(error)
raise error unless resourceful?
if get?
display resource
elsif has_errors?

View File

@@ -1,130 +0,0 @@
module ActionController #:nodoc:
module Verification #:nodoc:
extend ActiveSupport::Concern
include AbstractController::Callbacks, Flash, Rendering
# This module provides a class-level method for specifying that certain
# actions are guarded against being called without certain prerequisites
# being met. This is essentially a special kind of before_filter.
#
# An action may be guarded against being invoked without certain request
# parameters being set, or without certain session values existing.
#
# When a verification is violated, values may be inserted into the flash, and
# a specified redirection is triggered. If no specific action is configured,
# verification failures will by default result in a 400 Bad Request response.
#
# Usage:
#
# class GlobalController < ActionController::Base
# # Prevent the #update_settings action from being invoked unless
# # the 'admin_privileges' request parameter exists. The
# # settings action will be redirected to in current controller
# # if verification fails.
# verify :params => "admin_privileges", :only => :update_post,
# :redirect_to => { :action => "settings" }
#
# # Disallow a post from being updated if there was no information
# # submitted with the post, and if there is no active post in the
# # session, and if there is no "note" key in the flash. The route
# # named category_url will be redirected to if verification fails.
#
# verify :params => "post", :session => "post", "flash" => "note",
# :only => :update_post,
# :add_flash => { "alert" => "Failed to create your message" },
# :redirect_to => :category_url
#
# Note that these prerequisites are not business rules. They do not examine
# the content of the session or the parameters. That level of validation should
# be encapsulated by your domain model or helper methods in the controller.
module ClassMethods
# Verify the given actions so that if certain prerequisites are not met,
# the user is redirected to a different action. The +options+ parameter
# is a hash consisting of the following key/value pairs:
#
# <tt>:params</tt>::
# a single key or an array of keys that must be in the <tt>params</tt>
# hash in order for the action(s) to be safely called.
# <tt>:session</tt>::
# a single key or an array of keys that must be in the <tt>session</tt>
# in order for the action(s) to be safely called.
# <tt>:flash</tt>::
# a single key or an array of keys that must be in the flash in order
# for the action(s) to be safely called.
# <tt>:method</tt>::
# a single key or an array of keys--any one of which must match the
# current request method in order for the action(s) to be safely called.
# (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for
# example.)
# <tt>:xhr</tt>::
# true/false option to ensure that the request is coming from an Ajax
# call or not.
# <tt>:add_flash</tt>::
# a hash of name/value pairs that should be merged into the session's
# flash if the prerequisites cannot be satisfied.
# <tt>:add_headers</tt>::
# a hash of name/value pairs that should be merged into the response's
# headers hash if the prerequisites cannot be satisfied.
# <tt>:redirect_to</tt>::
# the redirection parameters to be used when redirecting if the
# prerequisites cannot be satisfied. You can redirect either to named
# route or to the action in some controller.
# <tt>:render</tt>::
# the render parameters to be used when the prerequisites cannot be satisfied.
# <tt>:only</tt>::
# only apply this verification to the actions specified in the associated
# array (may also be a single value).
# <tt>:except</tt>::
# do not apply this verification to the actions specified in the associated
# array (may also be a single value).
def verify(options={})
before_filter :only => options[:only], :except => options[:except] do
verify_action options
end
end
end
private
def verify_action(options) #:nodoc:
if prereqs_invalid?(options)
flash.update(options[:add_flash]) if options[:add_flash]
response.headers.merge!(options[:add_headers]) if options[:add_headers]
apply_remaining_actions(options) unless performed?
end
end
def prereqs_invalid?(options) # :nodoc:
verify_presence_of_keys_in_hash_flash_or_params(options) ||
verify_method(options) ||
verify_request_xhr_status(options)
end
def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
[*options[:params] ].find { |v| v && params[v.to_sym].nil? } ||
[*options[:session]].find { |v| session[v].nil? } ||
[*options[:flash] ].find { |v| flash[v].nil? }
end
def verify_method(options) # :nodoc:
[*options[:method]].all? { |v| request.method_symbol != v.to_sym } if options[:method]
end
def verify_request_xhr_status(options) # :nodoc:
request.xhr? != options[:xhr] unless options[:xhr].nil?
end
def apply_redirect_to(redirect_to_option) # :nodoc:
(redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option
end
def apply_remaining_actions(options) # :nodoc:
case
when options[:render] ; render(options[:render])
when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to]))
else head(:bad_request)
end
end
end
end

View File

@@ -120,7 +120,7 @@ module ActionController
end
%w(edit new).each do |action|
module_eval <<-EOT, __FILE__, __LINE__
module_eval <<-EOT, __FILE__, __LINE__ + 1
def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
polymorphic_url( # polymorphic_url(
record_or_hash, # record_or_hash,

View File

@@ -44,6 +44,12 @@ module ActionController
ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger }
end
initializer "action_controller.page_cache_directory" do
ActiveSupport.on_load(:action_controller) do
self.page_cache_directory = Rails.public_path
end
end
initializer "action_controller.set_configs" do |app|
paths = app.config.paths
ac = app.config.action_controller

View File

@@ -284,6 +284,8 @@ module ActionController
include ActionDispatch::TestProcess
include ActionController::TemplateAssertions
attr_reader :response, :request
# Executes a request simulating GET HTTP method and set/volley the response
def get(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "GET")

View File

@@ -36,17 +36,16 @@ module ActionDispatch
when Proc
strategy.call(request.raw_post)
when :xml_simple, :xml_node
request.body.size == 0 ? {} : Hash.from_xml(request.raw_post).with_indifferent_access
data = Hash.from_xml(request.body) || {}
request.body.rewind if request.body.respond_to?(:rewind)
data.with_indifferent_access
when :yaml
YAML.load(request.raw_post)
when :json
if request.body.size == 0
{}
else
data = ActiveSupport::JSON.decode(request.raw_post)
data = {:_json => data} unless data.is_a?(Hash)
data.with_indifferent_access
end
data = ActiveSupport::JSON.decode(request.body)
request.body.rewind if request.body.respond_to?(:rewind)
data = {:_json => data} unless data.is_a?(Hash)
data.with_indifferent_access
else
false
end
@@ -76,4 +75,4 @@ module ActionDispatch
defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
end
end
end
end

View File

@@ -6,7 +6,7 @@
<% end %>
<%
clean_params = @request.parameters.clone
clean_params = @request.filtered_parameters.clone
clean_params.delete("action")
clean_params.delete("controller")

View File

@@ -324,7 +324,8 @@ module ActionDispatch
end
def namespace(path)
scope(path.to_s, :name_prefix => path.to_s, :controller_namespace => path.to_s) { yield }
path = path.to_s
scope(:path => path, :name_prefix => path, :module => path) { yield }
end
def constraints(constraints = {})
@@ -363,15 +364,15 @@ module ActionDispatch
parent ? "#{parent}_#{child}" : child
end
def merge_controller_namespace_scope(parent, child)
def merge_module_scope(parent, child)
parent ? "#{parent}/#{child}" : child
end
def merge_controller_scope(parent, child)
@scope[:controller_namespace] ? "#{@scope[:controller_namespace]}/#{child}" : child
@scope[:module] ? "#{@scope[:module]}/#{child}" : child
end
def merge_resources_path_names_scope(parent, child)
def merge_path_names_scope(parent, child)
merge_options_scope(parent, child)
end
@@ -520,7 +521,7 @@ module ActionDispatch
def initialize(*args) #:nodoc:
super
@scope[:resources_path_names] = @set.resources_path_names
@scope[:path_names] = @set.resources_path_names
end
def resource(*resources, &block)
@@ -636,7 +637,7 @@ module ActionDispatch
return self
end
resources_path_names = options.delete(:path_names)
path_names = options.delete(:path_names)
if args.first.is_a?(Symbol)
action = args.first
@@ -653,7 +654,7 @@ module ActionDispatch
end
else
with_exclusive_name_prefix(action) do
return match("#{action_path(action, resources_path_names)}(.:format)", options.reverse_merge(:to => action))
return match("#{action_path(action, path_names)}(.:format)", options.reverse_merge(:to => action))
end
end
end
@@ -681,7 +682,7 @@ module ActionDispatch
private
def action_path(name, path_names = nil)
path_names ||= @scope[:resources_path_names]
path_names ||= @scope[:path_names]
path_names[name.to_sym] || name.to_s
end
@@ -692,7 +693,7 @@ module ActionDispatch
end
if path_names = options.delete(:path_names)
scope(:resources_path_names => path_names) do
scope(:path_names => path_names) do
send(method, resources.pop, options, &block)
end
return true

View File

@@ -3,7 +3,7 @@ module ActionPack
MAJOR = 3
MINOR = 0
TINY = 0
BUILD = "beta2"
BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end

View File

@@ -1,8 +1,6 @@
require 'cgi'
require 'action_view/helpers/form_helper'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/object/blank'
module ActionView
@@ -14,252 +12,29 @@ module ActionView
end
module Helpers
# The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the +form+
# method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This
# is a great way of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form.
# In that case, it's better to use the +input+ method and the specialized +form+ methods in link:classes/ActionView/Helpers/FormHelper.html
module ActiveModelHelper
# Returns a default input tag for the type of object returned by the method. For example, if <tt>@post</tt>
# has an attribute +title+ mapped to a +VARCHAR+ column that holds "Hello World":
#
# input("post", "title")
# # => <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
def input(record_name, method, options = {})
InstanceTag.new(record_name, method, self).to_tag(options)
end
# Returns an entire form with all needed input tags for a specified Active Record object. For example, if <tt>@post</tt>
# has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then
#
# form("post")
#
# would yield a form like the following (modulus formatting):
#
# <form action='/posts/create' method='post'>
# <p>
# <label for="post_title">Title</label><br />
# <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
# </p>
# <p>
# <label for="post_body">Body</label><br />
# <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea>
# </p>
# <input name="commit" type="submit" value="Create" />
# </form>
#
# It's possible to specialize the form builder by using a different action name and by supplying another
# block renderer. For example, if <tt>@entry</tt> has an attribute +message+ of type +VARCHAR+ then
#
# form("entry",
# :action => "sign",
# :input_block => Proc.new { |record, column|
# "#{column.human_name}: #{input(record, column.name)}<br />"
# })
#
# would yield a form like the following (modulus formatting):
#
# <form action="/entries/sign" method="post">
# Message:
# <input id="entry_message" name="entry[message]" size="30" type="text" /><br />
# <input name="commit" type="submit" value="Sign" />
# </form>
#
# It's also possible to add additional content to the form by giving it a block, such as:
#
# form("entry", :action => "sign") do |form|
# form << content_tag("b", "Department")
# form << collection_select("department", "id", @departments, "id", "name")
# end
#
# The following options are available:
#
# * <tt>:action</tt> - The action used when submitting the form (default: +create+ if a new record, otherwise +update+).
# * <tt>:input_block</tt> - Specialize the output using a different block, see above.
# * <tt>:method</tt> - The method used when submitting the form (default: +post+).
# * <tt>:multipart</tt> - Whether to change the enctype of the form to "multipart/form-data", used when uploading a file (default: +false+).
# * <tt>:submit_value</tt> - The text of the submit button (default: "Create" if a new record, otherwise "Update").
def form(record_name, options = {})
record = instance_variable_get("@#{record_name}")
record = convert_to_model(record)
options = options.symbolize_keys
options[:action] ||= record.persisted? ? "update" : "create"
action = url_for(:action => options[:action], :id => record)
submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
contents.safe_concat hidden_field(record_name, :id) if record.persisted?
contents.safe_concat all_input_tags(record, record_name, options)
yield contents if block_given?
contents.safe_concat submit_tag(submit_value)
contents.safe_concat('</form>')
end
# Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
# This error message is wrapped in a <tt>DIV</tt> tag by default or with <tt>:html_tag</tt> if specified,
# which can be extended to include a <tt>:prepend_text</tt> and/or <tt>:append_text</tt> (to properly explain
# the error), and a <tt>:css_class</tt> to style it accordingly. +object+ should either be the name of an
# instance variable or the actual object. The method can be passed in either as a string or a symbol.
# As an example, let's say you have a model <tt>@post</tt> that has an error message on the +title+ attribute:
#
# <%= error_message_on "post", "title" %>
# # => <div class="formError">can't be empty</div>
#
# <%= error_message_on @post, :title %>
# # => <div class="formError">can't be empty</div>
#
# <%= error_message_on "post", "title",
# :prepend_text => "Title simply ",
# :append_text => " (or it won't work).",
# :html_tag => "span",
# :css_class => "inputError" %>
# # => <span class="inputError">Title simply can't be empty (or it won't work).</span>
def error_message_on(object, method, *args)
options = args.extract_options!
unless args.empty?
ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' +
'prepend_text, append_text, html_tag, and css_class arguments', caller)
options[:prepend_text] = args[0] || ''
options[:append_text] = args[1] || ''
options[:html_tag] = args[2] || 'div'
options[:css_class] = args[3] || 'formError'
end
options.reverse_merge!(:prepend_text => '', :append_text => '', :html_tag => 'div', :css_class => 'formError')
object = convert_to_model(object)
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
(errors = obj.errors[method]).presence
content_tag(options[:html_tag],
(options[:prepend_text].html_safe << errors.first).safe_concat(options[:append_text]),
:class => options[:css_class]
)
else
''
end
end
# Returns a string with a <tt>DIV</tt> containing all of the error messages for the objects located as instance variables by the names
# given. If more than one object is specified, the errors for the objects are displayed in the order that the object names are
# provided.
#
# This <tt>DIV</tt> can be tailored by the following options:
#
# * <tt>:header_tag</tt> - Used for the header of the error div (default: "h2").
# * <tt>:id</tt> - The id of the error div (default: "errorExplanation").
# * <tt>:class</tt> - The class of the error div (default: "errorExplanation").
# * <tt>:object</tt> - The object (or array of objects) for which to display errors,
# if you need to escape the instance variable convention.
# * <tt>:object_name</tt> - The object name to use in the header, or any text that you prefer.
# If <tt>:object_name</tt> is not set, the name of the first object will be used.
# * <tt>:header_message</tt> - The message in the header of the error div. Pass +nil+
# or an empty string to avoid the header message altogether. (Default: "X errors
# prohibited this object from being saved").
# * <tt>:message</tt> - The explanation message after the header message and before
# the error list. Pass +nil+ or an empty string to avoid the explanation message
# altogether. (Default: "There were problems with the following fields:").
#
# To specify the display for one object, you simply provide its name as a parameter.
# For example, for the <tt>@user</tt> model:
#
# error_messages_for 'user'
#
# You can also supply an object:
#
# error_messages_for @user
#
# This will use the last part of the model name in the presentation. For instance, if
# this is a MyKlass::User object, this will use "user" as the name in the String. This
# is taken from MyKlass::User.model_name.human, which can be overridden.
#
# To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
# will be the name used in the header message:
#
# error_messages_for 'user_common', 'user', :object_name => 'user'
#
# You can also use a number of objects, which will have the same naming semantics
# as a single object.
#
# error_messages_for @user, @post
#
# If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual
# object (or array of objects to use):
#
# error_messages_for 'user', :object => @question.user
#
# NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what
# you need is significantly different from the default presentation, it makes plenty of sense to access the <tt>object.errors</tt>
# instance yourself and set it up. View the source of this method to see how easy it is.
def error_messages_for(*params)
options = params.extract_options!.symbolize_keys
objects = Array.wrap(options.delete(:object) || params).map do |object|
object = instance_variable_get("@#{object}") unless object.respond_to?(:to_model)
object = convert_to_model(object)
if object.class.respond_to?(:model_name)
options[:object_name] ||= object.class.model_name.human.downcase
%w(input form error_messages_for error_message_on).each do |method|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args)
ActiveSupport::Deprecation.warn "#{method} was removed from Rails and is now available as a plugin. " <<
"Please install it with `rails plugin install git://github.com/rails/dynamic_form.git`.", caller
end
object
end
objects.compact!
count = objects.inject(0) {|sum, object| sum + object.errors.count }
unless count.zero?
html = {}
[:id, :class].each do |key|
if options.include?(key)
value = options[key]
html[key] = value unless value.blank?
else
html[key] = 'errorExplanation'
end
end
options[:object_name] ||= params.first
I18n.with_options :locale => options[:locale], :scope => [:errors, :template] do |locale|
header_message = if options.include?(:header_message)
options[:header_message]
else
locale.t :header, :count => count, :model => options[:object_name].to_s.gsub('_', ' ')
end
message = options.include?(:message) ? options[:message] : locale.t(:body)
error_messages = objects.sum do |object|
object.errors.full_messages.map do |msg|
content_tag(:li, msg)
end
end.join.html_safe
contents = ''
contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
contents << content_tag(:p, message) unless message.blank?
contents << content_tag(:ul, error_messages)
content_tag(:div, contents.html_safe, html)
end
else
''
end
RUBY
end
private
def all_input_tags(record, record_name, options)
input_block = options[:input_block] || default_input_block
record.class.content_columns.collect{ |column| input_block.call(record_name, column) }.join("\n")
end
def default_input_block
Proc.new { |record, column| %(<p><label for="#{record}_#{column.name}">#{column.human_name}</label><br />#{input(record, column.name)}</p>) }
end
end
module ActiveRecordInstanceTag
module ActiveModelFormBuilder
%w(error_messages error_message_on).each do |method|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args)
ActiveSupport::Deprecation.warn "f.#{method} was removed from Rails and is now available as a plugin. " <<
"Please install it with `rails plugin install git://github.com/rails/dynamic_form.git`.", caller
end
RUBY
end
end
module ActiveModelInstanceTag
def object
@active_model_object ||= begin
object = super
@@ -267,26 +42,6 @@ module ActionView
end
end
def to_tag(options = {})
case column_type
when :string
field_type = @method_name.include?("password") ? "password" : "text"
to_input_field_tag(field_type, options)
when :text
to_text_area_tag(options)
when :integer, :float, :decimal
to_input_field_tag("text", options)
when :date
to_date_select_tag(options)
when :datetime, :timestamp
to_datetime_select_tag(options)
when :time
to_time_select_tag(options)
when :boolean
to_boolean_select_tag(options)
end
end
%w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth|
module_eval "def #{meth}(*) error_wrapping(super) end"
end
@@ -302,14 +57,14 @@ module ActionView
def error_message
object.errors[@method_name]
end
end
def column_type
object.send(:column_for_attribute, @method_name).type
end
class FormBuilder
include ActiveModelFormBuilder
end
class InstanceTag
include ActiveRecordInstanceTag
include ActiveModelInstanceTag
end
end
end

View File

@@ -501,16 +501,53 @@ module ActionView
end
end
# Web browsers cache favicons. If you just throw a <tt>favicon.ico</tt> into the document
# root of your application and it changes later, clients that have it in their cache
# won't see the update. Using this helper prevents that because it appends an asset ID:
#
# <%= favicon_link_tag %>
#
# generates
#
# <link href="/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" />
#
# You may specify a different file in the first argument:
#
# <%= favicon_link_tag 'favicon.ico' %>
#
# That's passed to +path_to_image+ as is, so it gives
#
# <link href="/images/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" />
#
# The helper accepts an additional options hash where you can override "rel" and "type".
#
# For example, Mobile Safari looks for a different LINK tag, pointing to an image that
# will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad.
# The following call would generate such a tag:
#
# <%= favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png' %>
#
def favicon_link_tag(source='/favicon.ico', options={})
tag('link', {
:rel => 'shortcut icon',
:type => 'image/vnd.microsoft.icon',
:href => path_to_image(source)
}.merge(options.symbolize_keys))
end
# Computes the path to an image asset in the public images directory.
# Full paths from the document root will be passed through.
# Used internally by +image_tag+ to build the image path.
# Used internally by +image_tag+ to build the image path:
#
# ==== Examples
# image_path("edit") # => /images/edit
# image_path("edit.png") # => /images/edit.png
# image_path("icons/edit.png") # => /images/icons/edit.png
# image_path("/icons/edit.png") # => /icons/edit.png
# image_path("http://www.railsapplication.com/img/edit.png") # => http://www.railsapplication.com/img/edit.png
# image_path("edit") # => "/images/edit"
# image_path("edit.png") # => "/images/edit.png"
# image_path("icons/edit.png") # => "/images/icons/edit.png"
# image_path("/icons/edit.png") # => "/icons/edit.png"
# image_path("http://www.railsapplication.com/img/edit.png") # => "http://www.railsapplication.com/img/edit.png"
#
# If you have images as application resources this method may conflict with their named routes.
# The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
# plugin authors are encouraged to do so.
def image_path(source)
compute_public_path(source, 'images')
end
@@ -590,7 +627,7 @@ module ActionView
end
if mouseover = options.delete(:mouseover)
options[:onmouseover] = "this.src='#{image_path(mouseover)}'"
options[:onmouseover] = "this.src='#{path_to_image(mouseover)}'"
options[:onmouseout] = "this.src='#{src}'"
end

View File

@@ -1105,7 +1105,7 @@ module ActionView
end
(field_helpers - %w(label check_box radio_button fields_for hidden_field)).each do |selector|
src, file, line = <<-end_src, __FILE__, __LINE__ + 1
src, line = <<-end_src, __LINE__ + 1
def #{selector}(method, options = {}) # def text_field(method, options = {})
@template.send( # @template.send(
#{selector.inspect}, # "text_field",
@@ -1114,7 +1114,7 @@ module ActionView
objectify_options(options)) # objectify_options(options))
end # end
end_src
class_eval src, file, line
class_eval src, __FILE__, line
end
def fields_for(record_or_name_or_array, *args, &block)
@@ -1169,14 +1169,6 @@ module ActionView
@template.hidden_field(@object_name, method, objectify_options(options))
end
def error_message_on(method, *args)
@template.error_message_on(@object, method, *args)
end
def error_messages(options = {})
@template.error_messages_for(@object_name, objectify_options(options))
end
# Add the submit button for the given form. When no value is given, it checks
# if the object is a new resource or not to create the proper label:
#

View File

@@ -217,8 +217,6 @@ module ActionView
InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
end
# Return select and option tags for the given object and method, using
# #time_zone_options_for_select to generate the list of option tags.
#

View File

@@ -199,8 +199,8 @@ module ActionView
# file_field_tag 'attachment'
# # => <input id="attachment" name="attachment" type="file" />
#
# file_field_tag 'avatar', :class => 'profile-input'
# # => <input class="profile-input" id="avatar" name="avatar" type="file" />
# file_field_tag 'avatar', :class => 'profile_input'
# # => <input class="profile_input" id="avatar" name="avatar" type="file" />
#
# file_field_tag 'picture', :disabled => true
# # => <input disabled="disabled" id="picture" name="picture" type="file" />
@@ -244,8 +244,8 @@ module ActionView
# password_field_tag 'confirm_pass', nil, :disabled => true
# # => <input disabled="disabled" id="confirm_pass" name="confirm_pass" type="password" />
#
# password_field_tag 'pin', '1234', :maxlength => 4, :size => 6, :class => "pin-input"
# # => <input class="pin-input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
# password_field_tag 'pin', '1234', :maxlength => 4, :size => 6, :class => "pin_input"
# # => <input class="pin_input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
def password_field_tag(name = "password", value = nil, options = {})
text_field_tag(name, value, options.update("type" => "password"))
end
@@ -374,8 +374,8 @@ module ActionView
# submit_tag nil, :class => "form_submit"
# # => <input class="form_submit" name="commit" type="submit" />
#
# submit_tag "Edit", :disable_with => "Editing...", :class => "edit-button"
# # => <input class="edit-button" data-disable_with="Editing..."
# submit_tag "Edit", :disable_with => "Editing...", :class => "edit_button"
# # => <input class="edit_button" data-disable_with="Editing..."
# # name="commit" type="submit" value="Edit" />
#
# submit_tag "Save", :confirm => "Are you sure?"
@@ -386,7 +386,7 @@ module ActionView
options.stringify_keys!
if disable_with = options.delete("disable_with")
options["data-disable-with"] = disable_with if disable_with
options["data-disable-with"] = disable_with
end
if confirm = options.delete("confirm")
@@ -398,7 +398,7 @@ module ActionView
# Displays an image which when clicked will submit the form.
#
# <tt>source</tt> is passed to AssetTagHelper#image_path
# <tt>source</tt> is passed to AssetTagHelper#path_to_image
#
# ==== Options
# * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
@@ -414,11 +414,11 @@ module ActionView
# image_submit_tag("purchase.png", :disabled => true)
# # => <input disabled="disabled" src="/images/purchase.png" type="image" />
#
# image_submit_tag("search.png", :class => 'search-button')
# # => <input class="search-button" src="/images/search.png" type="image" />
# image_submit_tag("search.png", :class => 'search_button')
# # => <input class="search_button" src="/images/search.png" type="image" />
#
# image_submit_tag("agree.png", :disabled => true, :class => "agree-disagree-button")
# # => <input class="agree-disagree-button" disabled="disabled" src="/images/agree.png" type="image" />
# image_submit_tag("agree.png", :disabled => true, :class => "agree_disagree_button")
# # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
def image_submit_tag(source, options = {})
options.stringify_keys!

View File

@@ -265,7 +265,7 @@ module ActionView
# using the +link_to+ method with the <tt>:method</tt> modifier as described in
# the +link_to+ documentation.
#
# The generated form element has a class name of <tt>button-to</tt>
# The generated form element has a class name of <tt>button_to</tt>
# to allow styling of the form itself and its children. You can control
# the form submission and input element behavior using +html_options+.
# This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
@@ -289,14 +289,14 @@ module ActionView
#
# ==== Examples
# <%= button_to "New", :action => "new" %>
# # => "<form method="post" action="/controller/new" class="button-to">
# # => "<form method="post" action="/controller/new" class="button_to">
# # <div><input value="New" type="submit" /></div>
# # </form>"
#
#
# <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
# :confirm => "Are you sure?", :method => :delete %>
# # => "<form method="post" action="/images/delete/1" class="button-to">
# # => "<form method="post" action="/images/delete/1" class="button_to">
# # <div>
# # <input type="hidden" name="_method" value="delete" />
# # <input data-confirm='Are you sure?' value="Delete" type="submit" />
@@ -306,7 +306,7 @@ module ActionView
#
# <%= button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?',
# :method => "delete", :remote => true, :disable_with => 'loading...') %>
# # => "<form class='button-to' method='post' action='http://www.example.com' data-remote='true'>
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
# # <div>
# # <input name='_method' value='delete' type='hidden' />
# # <input value='Destroy' type='submit' disable_with='loading...' data-confirm='Are you sure?' />
@@ -338,7 +338,7 @@ module ActionView
html_options.merge!("type" => "submit", "value" => name)
("<form method=\"#{form_method}\" action=\"#{escape_once url}\" #{"data-remote=\"true\"" if remote} class=\"button-to\"><div>" +
("<form method=\"#{form_method}\" action=\"#{escape_once url}\" #{"data-remote=\"true\"" if remote} class=\"button_to\"><div>" +
method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe
end

View File

@@ -141,14 +141,6 @@
minute: "Minute"
second: "Seconds"
errors:
template:
header:
one: "1 error prohibited this {{model}} from being saved"
other: "{{count}} errors prohibited this {{model}} from being saved"
# The variable :count is also available
body: "There were problems with the following fields:"
helpers:
select:
# Default value for :prompt => true in FormOptionsHelper

View File

@@ -38,7 +38,7 @@ module ActionView
end
register_detail(:formats) { Mime::SET.symbols }
register_detail(:locale) { [I18n.locale] }
register_detail(:locale) { [I18n.locale, I18n.default_locale] }
class DetailsKey #:nodoc:
alias :eql? :equal?
@@ -160,7 +160,7 @@ module ActionView
config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config
config.locale = value
end
super(I18n.locale)
super(_locale_defaults)
end
# Update the details keys by merging the given hash into the current

View File

@@ -48,7 +48,7 @@ module ActionView
#
def _layout_for(name = nil, &block) #:nodoc:
if !block || name
@_content_for[name || :layout]
@_content_for[name || :layout].html_safe
else
capture(&block)
end

View File

@@ -0,0 +1,22 @@
require 'abstract_unit'
class LocalizedController < ActionController::Base
def hello_world
end
end
class LocalizedTemplatesTest < ActionController::TestCase
tests LocalizedController
def test_localized_template_is_used
I18n.locale = :de
get :hello_world
assert_equal "Gutten Tag", @response.body
end
def test_default_locale_template_is_used_when_locale_is_missing
I18n.locale = :dk
get :hello_world
assert_equal "Hello World", @response.body
end
end

View File

@@ -189,6 +189,12 @@ XML
assert_equal Hash.new, @request.session.to_hash
end
def test_response_and_request_have_nice_accessors
process :no_op
assert_equal @response, response
assert_equal @request, request
end
def test_process_with_request_uri_with_no_params
process :test_uri
assert_equal "/test_test/test/test_uri", @response.body

View File

@@ -1,270 +0,0 @@
require 'abstract_unit'
class VerificationTest < ActionController::TestCase
class TestController < ActionController::Base
verify :only => :guarded_one, :params => "one",
:add_flash => { :error => 'unguarded' },
:redirect_to => { :action => "unguarded" }
verify :only => :guarded_two, :params => %w( one two ),
:redirect_to => { :action => "unguarded" }
verify :only => :guarded_with_flash, :params => "one",
:add_flash => { :notice => "prereqs failed" },
:redirect_to => { :action => "unguarded" }
verify :only => :guarded_in_session, :session => "one",
:redirect_to => { :action => "unguarded" }
verify :only => [:multi_one, :multi_two], :session => %w( one two ),
:redirect_to => { :action => "unguarded" }
verify :only => :guarded_by_method, :method => :post,
:redirect_to => { :action => "unguarded" }
verify :only => :guarded_by_xhr, :xhr => true,
:redirect_to => { :action => "unguarded" }
verify :only => :guarded_by_not_xhr, :xhr => false,
:redirect_to => { :action => "unguarded" }
before_filter :unconditional_redirect, :only => :two_redirects
verify :only => :two_redirects, :method => :post,
:redirect_to => { :action => "unguarded" }
verify :only => :must_be_post, :method => :post, :render => { :status => 405, :text => "Must be post" }, :add_headers => { "Allow" => "POST" }
verify :only => :guarded_one_for_named_route_test, :params => "one",
:redirect_to => :foo_url
verify :only => :no_default_action, :params => "santa"
verify :only => :guarded_with_back, :method => :post,
:redirect_to => :back
def guarded_one
render :text => "#{params[:one]}"
end
def guarded_one_for_named_route_test
render :text => "#{params[:one]}"
end
def guarded_with_flash
render :text => "#{params[:one]}"
end
def guarded_two
render :text => "#{params[:one]}:#{params[:two]}"
end
def guarded_in_session
render :text => "#{session["one"]}"
end
def multi_one
render :text => "#{session["one"]}:#{session["two"]}"
end
def multi_two
render :text => "#{session["two"]}:#{session["one"]}"
end
def guarded_by_method
render :text => "#{request.method.downcase}"
end
def guarded_by_xhr
render :text => "#{request.xhr?}"
end
def guarded_by_not_xhr
render :text => "#{request.xhr?}"
end
def unguarded
render :text => "#{params[:one]}"
end
def two_redirects
render :nothing => true
end
def must_be_post
render :text => "Was a post!"
end
def guarded_with_back
render :text => "#{params[:one]}"
end
def no_default_action
# Will never run
end
protected
def unconditional_redirect
redirect_to :action => "unguarded"
end
end
tests TestController
def test_using_symbol_back_with_no_referrer
assert_raise(ActionController::RedirectBackError) { get :guarded_with_back }
end
def test_using_symbol_back_redirects_to_referrer
@request.env["HTTP_REFERER"] = "/foo"
get :guarded_with_back
assert_redirected_to '/foo'
end
def test_no_deprecation_warning_for_named_route
assert_not_deprecated do
with_routing do |set|
set.draw do |map|
match 'foo', :to => 'test#foo', :as => :foo
match 'verification_test/:action', :to => ::VerificationTest::TestController
end
get :guarded_one_for_named_route_test, :two => "not one"
assert_redirected_to '/foo'
end
end
end
def test_guarded_one_with_prereqs
get :guarded_one, :one => "here"
assert_equal "here", @response.body
end
def test_guarded_one_without_prereqs
get :guarded_one
assert_redirected_to :action => "unguarded"
assert_equal 'unguarded', flash[:error]
end
def test_guarded_with_flash_with_prereqs
get :guarded_with_flash, :one => "here"
assert_equal "here", @response.body
assert flash.empty?
end
def test_guarded_with_flash_without_prereqs
get :guarded_with_flash
assert_redirected_to :action => "unguarded"
assert_equal "prereqs failed", flash[:notice]
end
def test_guarded_two_with_prereqs
get :guarded_two, :one => "here", :two => "there"
assert_equal "here:there", @response.body
end
def test_guarded_two_without_prereqs_one
get :guarded_two, :two => "there"
assert_redirected_to :action => "unguarded"
end
def test_guarded_two_without_prereqs_two
get :guarded_two, :one => "here"
assert_redirected_to :action => "unguarded"
end
def test_guarded_two_without_prereqs_both
get :guarded_two
assert_redirected_to :action => "unguarded"
end
def test_unguarded_with_params
get :unguarded, :one => "here"
assert_equal "here", @response.body
end
def test_unguarded_without_params
get :unguarded
assert @response.body.blank?
end
def test_guarded_in_session_with_prereqs
get :guarded_in_session, {}, "one" => "here"
assert_equal "here", @response.body
end
def test_guarded_in_session_without_prereqs
get :guarded_in_session
assert_redirected_to :action => "unguarded"
end
def test_multi_one_with_prereqs
get :multi_one, {}, "one" => "here", "two" => "there"
assert_equal "here:there", @response.body
end
def test_multi_one_without_prereqs
get :multi_one
assert_redirected_to :action => "unguarded"
end
def test_multi_two_with_prereqs
get :multi_two, {}, "one" => "here", "two" => "there"
assert_equal "there:here", @response.body
end
def test_multi_two_without_prereqs
get :multi_two
assert_redirected_to :action => "unguarded"
end
def test_guarded_by_method_with_prereqs
post :guarded_by_method
assert_equal "post", @response.body
end
def test_guarded_by_method_without_prereqs
get :guarded_by_method
assert_redirected_to :action => "unguarded"
end
def test_guarded_by_xhr_with_prereqs
xhr :post, :guarded_by_xhr
assert_equal "true", @response.body
end
def test_guarded_by_xhr_without_prereqs
get :guarded_by_xhr
assert_redirected_to :action => "unguarded"
end
def test_guarded_by_not_xhr_with_prereqs
get :guarded_by_not_xhr
assert_equal "false", @response.body
end
def test_guarded_by_not_xhr_without_prereqs
xhr :post, :guarded_by_not_xhr
assert_redirected_to :action => "unguarded"
end
def test_guarded_post_and_calls_render_succeeds
post :must_be_post
assert_equal "Was a post!", @response.body
end
def test_default_failure_should_be_a_bad_request
post :no_default_action
assert_response :bad_request
end
def test_guarded_post_and_calls_render_fails_and_sets_allow_header
get :must_be_post
assert_response 405
assert_equal "Must be post", @response.body
assert_equal "POST", @response.headers["Allow"]
end
def test_second_redirect
assert_nothing_raised { get :two_redirects }
end
end

View File

@@ -186,6 +186,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
resource :dashboard, :constraints => { :ip => /192\.168\.1\.\d{1,3}/ }
scope :module => 'api' do
resource :token
end
scope :path => 'api' do
resource :me
match '/' => 'mes#index'
end
end
end
@@ -942,6 +951,25 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
def test_module_scope
with_test_routes do
get '/token'
assert_equal 'api/tokens#show', @response.body
assert_equal '/token', token_path
end
end
def test_path_scope
with_test_routes do
get '/api/me'
assert_equal 'mes#show', @response.body
assert_equal '/api/me', me_path
get '/api'
assert_equal 'mes#index', @response.body
end
end
private
def with_test_routes
yield

View File

@@ -106,4 +106,13 @@ class ShowExceptionsTest < ActionController::IntegrationTest
assert_response 405
assert_match /ActionController::MethodNotAllowed/, body
end
test "does not show filtered parameters" do
@app = DevelopmentApp
get "/", {"foo"=>"bar"}, {'action_dispatch.show_exceptions' => true,
'action_dispatch.parameter_filter' => [:foo]}
assert_response 500
assert_match "&quot;foo&quot;=&gt;&quot;[FILTERED]&quot;", body
end
end

View File

@@ -0,0 +1 @@
Gutten Tag

View File

@@ -0,0 +1 @@
Hello World

View File

@@ -0,0 +1 @@
<%= "текст" %>

View File

@@ -0,0 +1,2 @@
<%# encoding: utf-8 -%>
<%= "текст" %>

View File

@@ -1,4 +1,4 @@
Русский текст
Русский <%= render :partial => 'test/utf8_partial' %>
<%= "日".encoding %>
<%= @output_buffer.encoding %>
<%= __ENCODING__ %>

View File

@@ -1,5 +1,5 @@
<%# encoding: utf-8 -%>
Русский текст
Русский <%= render :partial => 'test/utf8_partial_magic' %>
<%= "日".encoding %>
<%= @output_buffer.encoding %>
<%= __ENCODING__ %>

View File

@@ -0,0 +1,5 @@
<%# encoding: utf-8 -%>
Русский <%= render :partial => 'test/utf8_partial' %>
<%= "日".encoding %>
<%= @output_buffer.encoding %>
<%= __ENCODING__ %>

View File

@@ -1,42 +0,0 @@
require 'abstract_unit'
class ActiveModelHelperI18nTest < Test::Unit::TestCase
include ActionView::Context
include ActionView::Helpers::ActiveModelHelper
attr_reader :request
def setup
@object = stub :errors => stub(:count => 1, :full_messages => ['full_messages'])
@object.stubs :to_model => @object
@object.stubs :class => stub(:model_name => stub(:human => ""))
@object_name = 'book_seller'
@object_name_without_underscore = 'book seller'
stubs(:content_tag).returns 'content_tag'
I18n.stubs(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').returns "1 error prohibited this from being saved"
I18n.stubs(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).returns 'There were problems with the following fields:'
end
def test_error_messages_for_given_a_header_option_it_does_not_translate_header_message
I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').never
error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en')
end
def test_error_messages_for_given_no_header_option_it_translates_header_message
I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').returns 'header message'
error_messages_for(:object => @object, :locale => 'en')
end
def test_error_messages_for_given_a_message_option_it_does_not_translate_message
I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).never
error_messages_for(:object => @object, :message => 'message', :locale => 'en')
end
def test_error_messages_for_given_no_message_option_it_translates_message
I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).returns 'There were problems with the following fields:'
error_messages_for(:object => @object, :locale => 'en')
end
end

File diff suppressed because one or more lines are too long

View File

@@ -157,6 +157,14 @@ class AssetTagHelperTest < ActionView::TestCase
%(image_tag("mouse.png", :mouseover => image_path("mouse_over.png"))) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />)
}
FaviconLinkToTag = {
%(favicon_link_tag) => %(<link href="/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
%(favicon_link_tag 'favicon.ico') => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
%(favicon_link_tag 'favicon.ico', :rel => 'foo') => %(<link href="/images/favicon.ico" rel="foo" type="image/vnd.microsoft.icon" />),
%(favicon_link_tag 'favicon.ico', :rel => 'foo', :type => 'bar') => %(<link href="/images/favicon.ico" rel="foo" type="bar" />),
%(favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png') => %(<link href="/images/mb-icon.png" rel="apple-touch-icon" type="image/png" />)
}
VideoPathToTag = {
%(video_path("xml")) => %(/videos/xml),
%(video_path("xml.ogg")) => %(/videos/xml.ogg),
@@ -331,6 +339,10 @@ class AssetTagHelperTest < ActionView::TestCase
ImageLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
def test_favicon_link_tag
FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
def test_image_tag_windows_behaviour
old_asset_id, ENV["RAILS_ASSET_ID"] = ENV["RAILS_ASSET_ID"], "1"
# This simulates the behaviour of File#exist? on windows when testing a file ending in "."

View File

@@ -1314,12 +1314,12 @@ class FormHelperTest < ActionView::TestCase
class LabelledFormBuilder < ActionView::Helpers::FormBuilder
(field_helpers - %w(hidden_field)).each do |selector|
src = <<-END_SRC
src, line = <<-END_SRC, __LINE__ + 1
def #{selector}(field, *args, &proc)
("<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>").html_safe
end
END_SRC
class_eval src, __FILE__, __LINE__
class_eval src, __FILE__, line
end
end
@@ -1366,43 +1366,6 @@ class FormHelperTest < ActionView::TestCase
ActionView::Base.default_form_builder = old_default_form_builder
end
def test_default_form_builder_with_active_record_helpers
assert_deprecated do
form_for(:post, @post) do |f|
concat f.error_message_on('author_name')
concat f.error_messages
end
end
expected = %(<form action='http://www.example.com' method='post'>) +
%(<div class='formError'>can't be empty</div>) +
%(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>) +
%(</form>)
assert_dom_equal expected, output_buffer
end
def test_default_form_builder_no_instance_variable
post = @post
@post = nil
assert_deprecated do
form_for(:post, post) do |f|
concat f.error_message_on('author_name')
concat f.error_messages
end
end
expected = %(<form action='http://www.example.com' method='post'>) +
%(<div class='formError'>can't be empty</div>) +
%(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>) +
%(</form>)
assert_dom_equal expected, output_buffer
end
def test_fields_for_with_labelled_builder
output_buffer = fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
concat f.text_field(:title)

View File

@@ -245,31 +245,6 @@ module RenderTestCases
assert_equal %(\n<title>title</title>\n\n),
@view.render(:file => "test/layout_render_file.erb")
end
if '1.9'.respond_to?(:force_encoding)
def test_render_utf8_template_with_magic_comment
with_external_encoding Encoding::ASCII_8BIT do
result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield")
assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result
assert_equal Encoding::UTF_8, result.encoding
end
end
def test_render_utf8_template_with_default_external_encoding
with_external_encoding Encoding::UTF_8 do
result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield")
assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result
assert_equal Encoding::UTF_8, result.encoding
end
end
def with_external_encoding(encoding)
old, Encoding.default_external = Encoding.default_external, encoding
yield
ensure
Encoding.default_external = old
end
end
end
class CachedViewRenderTest < ActiveSupport::TestCase
@@ -302,4 +277,51 @@ class LazyViewRenderTest < ActiveSupport::TestCase
def teardown
GC.start
end
if '1.9'.respond_to?(:force_encoding)
def test_render_utf8_template_with_magic_comment
with_external_encoding Encoding::ASCII_8BIT do
result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield")
assert_equal Encoding::UTF_8, result.encoding
assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result
end
end
def test_render_utf8_template_with_default_external_encoding
with_external_encoding Encoding::UTF_8 do
result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield")
assert_equal Encoding::UTF_8, result.encoding
assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result
end
end
def test_render_utf8_template_with_incompatible_external_encoding
with_external_encoding Encoding::SJIS do
begin
result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield")
flunk 'Should have raised incompatible encoding error'
rescue ActionView::Template::Error => error
assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message
end
end
end
def test_render_utf8_template_with_partial_with_incompatible_encoding
with_external_encoding Encoding::SJIS do
begin
result = @view.render(:file => "test/utf8_magic_with_bare_partial.html.erb", :layouts => "layouts/yield")
flunk 'Should have raised incompatible encoding error'
rescue ActionView::Template::Error => error
assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message
end
end
end
def with_external_encoding(encoding)
old, Encoding.default_external = Encoding.default_external, encoding
yield
ensure
Encoding.default_external = old
end
end
end

View File

@@ -74,56 +74,56 @@ class UrlHelperTest < ActiveSupport::TestCase
# todo: missing test cases
def test_button_to_with_straight_url
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
end
def test_button_to_with_query
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
end
def test_button_to_with_escaped_query
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2")
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2")
end
def test_button_to_with_query_and_no_name
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com?q1=v1&amp;q2=v2\" class=\"button-to\"><div><input type=\"submit\" value=\"http://www.example.com?q1=v1&amp;q2=v2\" /></div></form>", button_to(nil, "http://www.example.com?q1=v1&q2=v2")
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com?q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"http://www.example.com?q1=v1&amp;q2=v2\" /></div></form>", button_to(nil, "http://www.example.com?q1=v1&q2=v2")
end
def test_button_to_with_javascript_confirm
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
)
end
def test_button_to_with_remote_and_javascript_confirm
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?")
)
end
def test_button_to_enabled_disabled
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :disabled => false)
)
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input disabled=\"disabled\" type=\"submit\" value=\"Hello\" /></div></form>",
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input disabled=\"disabled\" type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :disabled => true)
)
end
def test_button_to_with_method_delete
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"hidden\" name=\"_method\" value=\"delete\" /><input type=\"submit\" value=\"Hello\" /></div></form>",
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"hidden\" name=\"_method\" value=\"delete\" /><input type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :method => :delete)
)
end
def test_button_to_with_method_get
assert_dom_equal(
"<form method=\"get\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
"<form method=\"get\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :method => :get)
)
end

View File

@@ -221,7 +221,7 @@ module ActiveModel
def alias_attribute(new_name, old_name)
attribute_method_matchers.each do |matcher|
module_eval <<-STR, __FILE__, __LINE__+1
module_eval <<-STR, __FILE__, __LINE__ + 1
def #{matcher.method_name(new_name)}(*args)
send(:#{matcher.method_name(old_name)}, *args)
end
@@ -265,7 +265,7 @@ module ActiveModel
else
method_name = matcher.method_name(attr_name)
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__+1
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
if method_defined?(:#{method_name})
undef :#{method_name}
end

View File

@@ -105,7 +105,7 @@ module ActiveModel
end
def _define_before_model_callback(klass, callback) #:nodoc:
klass.class_eval <<-CALLBACK, __FILE__, __LINE__
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.before_#{callback}(*args, &block)
set_callback(:#{callback}, :before, *args, &block)
end
@@ -113,7 +113,7 @@ module ActiveModel
end
def _define_around_model_callback(klass, callback) #:nodoc:
klass.class_eval <<-CALLBACK, __FILE__, __LINE__
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.around_#{callback}(*args, &block)
set_callback(:#{callback}, :around, *args, &block)
end
@@ -121,7 +121,7 @@ module ActiveModel
end
def _define_after_model_callback(klass, callback) #:nodoc:
klass.class_eval <<-CALLBACK, __FILE__, __LINE__
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.after_#{callback}(*args, &block)
options = args.extract_options!
options[:prepend] = true

View File

@@ -142,6 +142,11 @@ module ActiveModel
to_a.size
end
# Returns true if there are any errors, false if not.
def empty?
all? { |k, v| v && v.empty? }
end
# Returns an xml formatted representation of the Errors hash.
#
# p.errors.add(:name, "can't be blank")

View File

@@ -3,7 +3,7 @@ module ActiveModel
MAJOR = 3
MINOR = 0
TINY = 0
BUILD = "beta2"
BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end

View File

@@ -97,6 +97,12 @@ class ValidationsTest < ActiveModel::TestCase
assert_equal 2, r.errors.count
end
def test_errors_empty_after_errors_on_check
t = Topic.new
assert t.errors[:id].empty?
assert t.errors.empty?
end
def test_validates_each
hits = 0
Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|

View File

@@ -50,6 +50,7 @@ end
t.libs << "test" << connection_path
t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort
t.verbose = true
t.warning = true
}
task "isolated_test_#{adapter}" do

View File

@@ -105,6 +105,7 @@ module ActiveRecord
def reset
reset_target!
reset_named_scopes_cache!
@loaded = false
end
@@ -146,7 +147,7 @@ module ActiveRecord
# has_many :books
# end
#
# Author.find(:first).books.transaction do
# Author.first.books.transaction do
# # same effect as calling Book.transaction
# end
def transaction(*args)
@@ -162,6 +163,7 @@ module ActiveRecord
load_target
delete(@target)
reset_target!
reset_named_scopes_cache!
end
# Calculate sum using SQL, not Enumerable
@@ -250,6 +252,7 @@ module ActiveRecord
load_target
destroy(@target)
reset_target!
reset_named_scopes_cache!
end
def create(attrs = {})
@@ -406,8 +409,8 @@ module ActiveRecord
super
end
elsif @reflection.klass.scopes[method]
@_scopes ||= {}
@_scopes[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
@_named_scopes_cache ||= {}
@_named_scopes_cache[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
else
with_scope(construct_scope) do
if block_given?
@@ -428,6 +431,10 @@ module ActiveRecord
@target = Array.new
end
def reset_named_scopes_cache!
@_named_scopes_cache = {}
end
def find_target
records =
if @reflection.options[:finder_sql]

View File

@@ -52,6 +52,8 @@ module ActiveRecord
@changed_attributes.delete(attr) unless field_changed?(attr, old, value)
else
old = clone_attribute_value(:read_attribute, attr)
# Save Time objects as TimeWithZone if time_zone_aware_attributes == true
old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
@changed_attributes[attr] = old if field_changed?(attr, old, value)
end
@@ -84,6 +86,10 @@ module ActiveRecord
old != value
end
def clone_with_time_zone_conversion_attribute?(attr, old)
old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
end
end
end
end

View File

@@ -17,7 +17,7 @@ module ActiveRecord
# This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
def define_method_attribute(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
method_body = <<-EOV
method_body, line = <<-EOV, __LINE__ + 1
def #{attr_name}(reload = false)
cached = @attributes_cache['#{attr_name}']
return cached if cached && !reload
@@ -25,7 +25,7 @@ module ActiveRecord
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
end
EOV
generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
generated_attribute_methods.module_eval(method_body, __FILE__, line)
else
super
end
@@ -35,7 +35,7 @@ module ActiveRecord
# This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
def define_method_attribute=(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
method_body = <<-EOV
method_body, line = <<-EOV, __LINE__ + 1
def #{attr_name}=(time)
unless time.acts_like?(:time)
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
@@ -44,7 +44,7 @@ module ActiveRecord
write_attribute(:#{attr_name}, time)
end
EOV
generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
generated_attribute_methods.module_eval(method_body, __FILE__, line)
else
super
end

View File

@@ -56,15 +56,15 @@ module ActiveRecord #:nodoc:
#
# class User < ActiveRecord::Base
# def self.authenticate_unsafely(user_name, password)
# find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'")
# where("user_name = '#{user_name}' AND password = '#{password}'").first
# end
#
# def self.authenticate_safely(user_name, password)
# find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
# where("user_name = ? AND password = ?", user_name, password).first
# end
#
# def self.authenticate_safely_simply(user_name, password)
# find(:first, :conditions => { :user_name => user_name, :password => password })
# where(:user_name => user_name, :password => password).first
# end
# end
#
@@ -77,30 +77,30 @@ module ActiveRecord #:nodoc:
# question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing
# the question marks with symbols and supplying a hash with values for the matching symbol keys:
#
# Company.find(:first, :conditions => [
# Company.where(
# "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
# { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
# ])
# ).first
#
# Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
# operator. For instance:
#
# Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 })
# Student.find(:all, :conditions => params[:student])
# Student.where(:first_name => "Harvey", :status => 1)
# Student.where(params[:student])
#
# A range may be used in the hash to use the SQL BETWEEN operator:
#
# Student.find(:all, :conditions => { :grade => 9..12 })
# Student.where(:grade => 9..12)
#
# An array may be used in the hash to use the SQL IN operator:
#
# Student.find(:all, :conditions => { :grade => [9,11,12] })
# Student.where(:grade => [9,11,12])
#
# When joining tables, nested hashes or keys written in the form 'table_name.column_name' can be used to qualify the table name of a
# particular condition. For instance:
#
# Student.find(:all, :conditions => { :schools => { :type => 'public' }}, :joins => :schools)
# Student.find(:all, :conditions => { 'schools.type' => 'public' }, :joins => :schools)
# Student.joins(:schools).where(:schools => { :type => 'public' })
# Student.joins(:schools).where('schools.type' => 'public' )
#
# == Overwriting default accessors
#
@@ -153,18 +153,18 @@ module ActiveRecord #:nodoc:
# Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
# appending the name of an attribute to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt>, so you get finders like <tt>Person.find_by_user_name</tt>,
# <tt>Person.find_all_by_last_name</tt>, and <tt>Payment.find_by_transaction_id</tt>. So instead of writing
# <tt>Person.find(:first, :conditions => ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
# And instead of writing <tt>Person.find(:all, :conditions => ["last_name = ?", last_name])</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
# <tt>Person.where(:user_name => user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
# And instead of writing <tt>Person.where(:last_name => last_name).all</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
#
# It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
# <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
# <tt>Person.find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt>, you just do
# <tt>Person.where(:user_name => user_name, :password => password).first</tt>, you just do
# <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
#
# It's even possible to use all the additional parameters to find. For example, the full interface for <tt>Payment.find_all_by_amount</tt>
# is actually <tt>Payment.find_all_by_amount(amount, options)</tt>. And the full interface to <tt>Person.find_by_user_name</tt> is
# actually <tt>Person.find_by_user_name(user_name, options)</tt>. So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
# Also you may call <tt>Payment.find_last_by_amount(amount, options)</tt> returning the last record matching that amount and options.
# It's even possible to call these dynamic finder methods on relations and named scopes. For example :
#
# Payment.order("created_on").find_all_by_amount(50)
# Payment.pending.find_last_by_amount(100)
#
# The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
# <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won't be set unless they are given in a block. For example:
@@ -224,7 +224,7 @@ module ActiveRecord #:nodoc:
# class PriorityClient < Client; end
#
# When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in the companies table with type = "Firm". You can then
# fetch this row again using <tt>Company.find(:first, "name = '37signals'")</tt> and it will return a Firm object.
# fetch this row again using <tt>Company.where(:name => '37signals').first</tt> and it will return a Firm object.
#
# If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just
# like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
@@ -1117,10 +1117,6 @@ module ActiveRecord #:nodoc:
# It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+
# is actually <tt>find_all_by_amount(amount, options)</tt>.
#
# Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that
# are turned into scoped(:conditions => ["user_name = ?", user_name]) and scoped(:conditions => ["user_name = ? AND password = ?", user_name, password])
# respectively.
#
# Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future
# attempts to use it do not run through method_missing.
def method_missing(method_id, *arguments, &block)
@@ -1138,7 +1134,7 @@ module ActiveRecord #:nodoc:
attribute_names = match.attribute_names
super unless all_attributes_exists?(attribute_names)
if match.scope?
self.class_eval %{
self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
options = args.extract_options! # options = args.extract_options!
attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
@@ -1147,7 +1143,7 @@ module ActiveRecord #:nodoc:
#
scoped(:conditions => attributes) # scoped(:conditions => attributes)
end # end
}, __FILE__, __LINE__
METHOD
send(method_id, *arguments)
end
else
@@ -1196,12 +1192,12 @@ module ActiveRecord #:nodoc:
protected
# Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
# method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
# <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. <tt>:create</tt> parameters are an attributes hash.
# method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
# <tt>:create</tt> parameters are an attributes hash.
#
# class Article < ActiveRecord::Base
# def self.create_with_scope
# with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
# with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
# find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
# a = create(1)
# a.blog_id # => 1
@@ -1210,20 +1206,20 @@ module ActiveRecord #:nodoc:
# end
#
# In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
# <tt>:conditions</tt>, <tt>:include</tt>, and <tt>:joins</tt> options in <tt>:find</tt>, which are merged.
# <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
#
# <tt>:joins</tt> options are uniqued so multiple scopes can join in the same table without table aliasing
# <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
# problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
# array of strings format for your joins.
#
# class Article < ActiveRecord::Base
# def self.find_with_scope
# with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
# with_scope(:find => { :limit => 10 }) do
# find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
# with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
# with_scope(:find => limit(10)) do
# all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
# end
# with_scope(:find => { :conditions => "author_id = 3" }) do
# find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
# with_scope(:find => where(:author_id => 3)) do
# all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
# end
# end
# end
@@ -1233,9 +1229,9 @@ module ActiveRecord #:nodoc:
#
# class Article < ActiveRecord::Base
# def self.find_with_exclusive_scope
# with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
# with_exclusive_scope(:find => { :limit => 10 })
# find(:all) # => SELECT * from articles LIMIT 10
# with_scope(:find => where(:blog_id => 1).limit(1)) do
# with_exclusive_scope(:find => limit(10))
# all # => SELECT * from articles LIMIT 10
# end
# end
# end
@@ -1318,9 +1314,9 @@ module ActiveRecord #:nodoc:
modularized_name = type_name_with_module(type_name)
silence_warnings do
begin
class_eval(modularized_name, __FILE__, __LINE__)
class_eval(modularized_name, __FILE__)
rescue NameError
class_eval(type_name, __FILE__, __LINE__)
class_eval(type_name, __FILE__)
end
end
end

View File

@@ -15,7 +15,7 @@ module ActiveRecord
def dirties_query_cache(base, *method_names)
method_names.each do |method_name|
base.class_eval <<-end_code, __FILE__, __LINE__
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
def #{method_name}_with_query_dirty(*args) # def update_with_query_dirty(*args)
clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
#{method_name}_without_query_dirty(*args) # update_without_query_dirty(*args)

View File

@@ -658,27 +658,6 @@ module ActiveRecord
end
end
# Creates a schema for the given user
#
# Example:
# create_schema('products', 'postgres')
def create_schema(schema_name, pg_username)
execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
end
# Drops a schema
#
# Example:
# drop_schema('products')
def drop_schema(schema_name)
execute("DROP SCHEMA \"#{schema_name}\"")
end
# Returns an array of all schemas in the database
def all_schemas
query('SELECT schema_name FROM information_schema.schemata').flatten
end
# Returns the list of all tables in the schema search path or a specified schema.
def tables(name = nil)
query(<<-SQL, name).map { |row| row[0] }

View File

@@ -84,15 +84,9 @@ namespace :db do
end
end
when 'postgresql'
@encoding = config['encoding'] || ENV['CHARSET'] || 'utf8'
schema_search_path = config['schema_search_path'] || 'public'
first_in_schema_search_path = schema_search_path.split(',').first.strip
@encoding = config[:encoding] || ENV['CHARSET'] || 'utf8'
begin
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
unless ActiveRecord::Base.connection.all_schemas.include?(first_in_schema_search_path)
ActiveRecord::Base.connection.create_schema(first_in_schema_search_path, config['username'])
$stderr.puts "Schema #{first_in_schema_search_path} has been created."
end
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
ActiveRecord::Base.establish_connection(config)
rescue
@@ -452,6 +446,8 @@ namespace :db do
end
end
task 'test:prepare' => 'db:test:prepare'
def drop_database(config)
case config['adapter']
when 'mysql'

View File

@@ -3,7 +3,7 @@ module ActiveRecord
MAJOR = 3
MINOR = 0
TINY = 0
BUILD = "beta2"
BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end

View File

@@ -17,13 +17,6 @@ class PostgresqlActiveSchemaTest < Test::Unit::TestCase
assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
end
def test_create_schema
assert_equal %(CREATE SCHEMA "rizwan" AUTHORIZATION "postgres"), create_schema(:rizwan, :postgres)
end
def test_drop_schema
assert_equal %(DROP SCHEMA "rizwan"), drop_schema(:rizwan)
end
private
def method_missing(method_symbol, *arguments)
ActiveRecord::Base.connection.send(method_symbol, *arguments)

View File

@@ -81,12 +81,6 @@ class AdapterTest < ActiveRecord::TestCase
def test_encoding
assert_not_nil @connection.encoding
end
def test_all_schemas
@connection.create_schema(:test_schema, :postgres)
assert @connection.all_schemas.include?('test_schema')
@connection.drop_schema(:test_schema)
end
end
def test_table_alias

View File

@@ -35,11 +35,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_belongs_to_with_primary_key_joins_on_correct_column
sql = Client.joins(:firm_with_primary_key).to_sql
if current_adapter?(:MysqlAdapter)
assert_no_match /`firm_with_primary_keys_companies`\.`id`/, sql
assert_match /`firm_with_primary_keys_companies`\.`name`/, sql
assert_no_match(/`firm_with_primary_keys_companies`\.`id`/, sql)
assert_match(/`firm_with_primary_keys_companies`\.`name`/, sql)
else
assert_no_match /"firm_with_primary_keys_companies"\."id"/, sql
assert_match /"firm_with_primary_keys_companies"\."name"/, sql
assert_no_match(/"firm_with_primary_keys_companies"\."id"/, sql)
assert_match(/"firm_with_primary_keys_companies"\."name"/, sql)
end
end

View File

@@ -54,6 +54,89 @@ class DirtyTest < ActiveRecord::TestCase
assert_nil pirate.catchphrase_change
end
def test_time_attributes_changes_with_time_zone
in_time_zone 'Paris' do
target = Class.new(ActiveRecord::Base)
target.table_name = 'pirates'
# New record - no changes.
pirate = target.new
assert !pirate.created_on_changed?
assert_nil pirate.created_on_change
# Saved - no changes.
pirate.catchphrase = 'arrrr, time zone!!'
pirate.save!
assert !pirate.created_on_changed?
assert_nil pirate.created_on_change
# Change created_on.
old_created_on = pirate.created_on
pirate.created_on = Time.now - 1.day
assert pirate.created_on_changed?
assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was
assert_equal old_created_on, pirate.created_on_was
end
end
def test_time_attributes_changes_without_time_zone_by_skip
in_time_zone 'Paris' do
target = Class.new(ActiveRecord::Base)
target.table_name = 'pirates'
target.skip_time_zone_conversion_for_attributes = [:created_on]
# New record - no changes.
pirate = target.new
assert !pirate.created_on_changed?
assert_nil pirate.created_on_change
# Saved - no changes.
pirate.catchphrase = 'arrrr, time zone!!'
pirate.save!
assert !pirate.created_on_changed?
assert_nil pirate.created_on_change
# Change created_on.
old_created_on = pirate.created_on
pirate.created_on = Time.now + 1.day
assert pirate.created_on_changed?
# kind_of does not work because
# ActiveSupport::TimeWithZone.name == 'Time'
assert_equal Time, pirate.created_on_was.class
assert_equal old_created_on, pirate.created_on_was
end
end
def test_time_attributes_changes_without_time_zone
target = Class.new(ActiveRecord::Base)
target.table_name = 'pirates'
target.time_zone_aware_attributes = false
# New record - no changes.
pirate = target.new
assert !pirate.created_on_changed?
assert_nil pirate.created_on_change
# Saved - no changes.
pirate.catchphrase = 'arrrr, time zone!!'
pirate.save!
assert !pirate.created_on_changed?
assert_nil pirate.created_on_change
# Change created_on.
old_created_on = pirate.created_on
pirate.created_on = Time.now + 1.day
assert pirate.created_on_changed?
# kind_of does not work because
# ActiveSupport::TimeWithZone.name == 'Time'
assert_equal Time, pirate.created_on_was.class
assert_equal old_created_on, pirate.created_on_was
end
def test_aliased_attribute_changes
# the actual attribute here is name, title is an
# alias setup via alias_attribute
@@ -406,4 +489,16 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal %w(parrot_id), pirate.changed
assert_nil pirate.parrot_id_was
end
def in_time_zone(zone)
old_zone = Time.zone
old_tz = ActiveRecord::Base.time_zone_aware_attributes
Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
yield
ensure
Time.zone = old_zone
ActiveRecord::Base.time_zone_aware_attributes = old_tz
end
end

View File

@@ -1136,21 +1136,6 @@ if ActiveRecord::Base.connection.supports_migrations?
load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
end
def test_migrator_interleaved_migrations
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
assert_nothing_raised do
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
end
Person.reset_column_information
assert Person.column_methods_hash.include?(:last_name)
assert_nothing_raised do
ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
end
end
def test_migrator_db_has_no_schema_migrations_table
# Oracle adapter raises error if semicolon is present as last character
if current_adapter?(:OracleAdapter)
@@ -1362,16 +1347,6 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
def test_migration_should_be_run_without_logger
previous_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = nil
assert_nothing_raised do
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
end
ensure
ActiveRecord::Base.logger = previous_logger
end
protected
def with_env_tz(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz
@@ -1457,6 +1432,45 @@ if ActiveRecord::Base.connection.supports_migrations?
end # SexyMigrationsTest
class MigrationLoggerTest < ActiveRecord::TestCase
def setup
Object.send(:remove_const, :InnocentJointable)
end
def test_migration_should_be_run_without_logger
previous_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = nil
assert_nothing_raised do
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
end
ensure
ActiveRecord::Base.logger = previous_logger
end
end
class InterleavedMigrationsTest < ActiveRecord::TestCase
def setup
Object.send(:remove_const, :PeopleHaveLastNames)
end
def test_migrator_interleaved_migrations
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
assert_nothing_raised do
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
end
Person.reset_column_information
assert Person.column_methods_hash.include?(:last_name)
Object.send(:remove_const, :PeopleHaveLastNames)
Object.send(:remove_const, :InnocentJointable)
assert_nothing_raised do
ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
end
end
end
class ChangeTableMigrationsTest < ActiveRecord::TestCase
def setup
@connection = Person.connection

View File

@@ -422,6 +422,16 @@ class NamedScopeTest < ActiveRecord::TestCase
post.comments.containing_the_letter_e.all # force load
assert_no_queries { post.comments.containing_the_letter_e.all }
end
def test_named_scopes_are_reset_on_association_reload
post = posts(:welcome)
[:destroy_all, :reset, :delete_all].each do |method|
before = post.comments.containing_the_letter_e
post.comments.send(method)
assert before.object_id != post.comments.containing_the_letter_e.object_id, "AssociationCollection##{method} should reset the named scopes cache"
end
end
end
class DynamicScopeMatchTest < ActiveRecord::TestCase

View File

@@ -130,10 +130,20 @@ class NilXmlSerializationTest < ActiveRecord::TestCase
end
class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
fixtures :authors, :posts
fixtures :authors, :posts, :projects
# to_xml used to mess with the hash the user provided which
# caused the builder to be reused. This meant the document kept
# getting appended to.
def test_modules
projects = MyApplication::Business::Project.all
xml = projects.to_xml
root = projects.first.class.to_s.underscore.pluralize.tr('/','_').dasherize
assert_match "<#{root} type=\"array\">", xml
assert_match "</#{root}>", xml
end
def test_passing_hash_shouldnt_reuse_builder
options = {:include=>:posts}
david = authors(:david)

View File

@@ -588,11 +588,11 @@ module ActiveResource
@prefix_parameters = nil
# Redefine the new methods.
code = <<-end_code
code, line = <<-end_code, __LINE__ + 1
def prefix_source() "#{value}" end
def prefix(options={}) "#{prefix_call}" end
end_code
silence_warnings { instance_eval code, __FILE__, __LINE__ }
silence_warnings { instance_eval code, __FILE__, line }
rescue
logger.error "Couldn't set prefix: #{$!}\n #{code}" if logger
raise

View File

@@ -57,7 +57,7 @@ module ActiveResource
# def post(path, request_headers = {}, body = nil, status = 200, response_headers = {})
# @responses[Request.new(:post, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
# end
module_eval <<-EOE, __FILE__, __LINE__
module_eval <<-EOE, __FILE__, __LINE__ + 1
def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {})
@responses << [Request.new(:#{method}, path, nil, request_headers), Response.new(body || "", status, response_headers)]
end
@@ -125,7 +125,7 @@ module ActiveResource
# self.class.requests << request
# self.class.responses.assoc(request).try(:second) || raise(InvalidRequestError.new("No response recorded for #{request}"))
# end
module_eval <<-EOE, __FILE__, __LINE__
module_eval <<-EOE, __FILE__, __LINE__ + 1
def #{method}(path, #{'body, ' if has_body}headers)
request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers)
self.class.requests << request

View File

@@ -3,7 +3,7 @@ module ActiveResource
MAJOR = 3
MINOR = 0
TINY = 0
BUILD = "beta2"
BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end

View File

@@ -1,3 +1,8 @@
*Rails 3.0.0 [beta 3] (pending)*
* Speed up and add Ruby 1.9 support for ActiveSupport::Multibyte::Chars#tidy_bytes. #4350 [Norman Clarke]
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
* Reduced load time by deferring configuration of classes using

View File

@@ -4,7 +4,9 @@ module ActiveSupport
# context, so only the relevant lines are included.
#
# If you need to reconfigure an existing BacktraceCleaner, like the one in Rails, to show as much as possible, you can always
# call BacktraceCleaner#remove_silencers!
# call BacktraceCleaner#remove_silencers! Also, if you need to reconfigure an existing BacktraceCleaner so that it does not
# filter or modify the paths of any lines of the backtrace, you can call BacktraceCleaner#remove_filters! These two methods
# will give you a completely untouched backtrace.
#
# Example:
#
@@ -60,6 +62,10 @@ module ActiveSupport
@silencers = []
end
def remove_filters!
@filters = []
end
private
def filter(backtrace)
@filters.each do |f|

View File

@@ -64,7 +64,7 @@ module ActiveSupport
@data.get(key, raw?(options))
end
rescue MemCache::MemCacheError => e
logger.error("MemCacheError (#{e}): #{e.message}")
logger.error("MemCacheError (#{e}): #{e.message}") if logger
nil
end
@@ -85,7 +85,7 @@ module ActiveSupport
response == Response::STORED
end
rescue MemCache::MemCacheError => e
logger.error("MemCacheError (#{e}): #{e.message}")
logger.error("MemCacheError (#{e}): #{e.message}") if logger
false
end
@@ -95,7 +95,7 @@ module ActiveSupport
response == Response::DELETED
end
rescue MemCache::MemCacheError => e
logger.error("MemCacheError (#{e}): #{e.message}")
logger.error("MemCacheError (#{e}): #{e.message}") if logger
false
end

View File

@@ -18,7 +18,7 @@ module ActiveSupport
def middleware
@middleware ||= begin
klass = Class.new
klass.class_eval(<<-EOS, __FILE__, __LINE__)
klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def initialize(app)
@app = app
end

View File

@@ -387,7 +387,7 @@ module ActiveSupport
send("_update_#{symbol}_superclass_callbacks")
body = send("_#{symbol}_callbacks").compile(nil)
body, line = <<-RUBY_EVAL, __LINE__
body, line = <<-RUBY_EVAL, __LINE__ + 1
def _run_#{symbol}_callbacks(key = nil, &blk)
if self.class.send("_update_#{symbol}_superclass_callbacks")
self.class.__define_runner(#{symbol.inspect})

View File

@@ -131,7 +131,7 @@ class Array
require 'builder' unless defined?(Builder)
options = options.dup
options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.name)) : "records"
options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.name)).tr('/', '_') : "records"
options[:children] ||= options[:root].singularize
options[:indent] ||= 2
options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])

View File

@@ -61,7 +61,7 @@ class Module
# e.subject = "Megastars"
# e.title # => "Megastars"
def alias_attribute(new_name, old_name)
module_eval <<-STR, __FILE__, __LINE__+1
module_eval <<-STR, __FILE__, __LINE__ + 1
def #{new_name}; self.#{old_name}; end # def subject; self.title; end
def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end

View File

@@ -21,7 +21,7 @@ class Module
def attr_accessor_with_default(sym, default = nil, &block)
raise 'Default value or block required' unless !default.nil? || block
define_method(sym, block_given? ? block : Proc.new { default })
module_eval(<<-EVAL, __FILE__, __LINE__)
module_eval(<<-EVAL, __FILE__, __LINE__ + 1)
def #{sym}=(value) # def age=(value)
class << self; attr_reader :#{sym} end # class << self; attr_reader :age end
@#{sym} = value # @age = value

View File

@@ -126,7 +126,7 @@ class Module
%(raise "#{self}##{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
end
module_eval(<<-EOS, file, line)
module_eval(<<-EOS, file, line - 5)
if instance_methods(false).map(&:to_s).include?("#{prefix}#{method}")
remove_possible_method("#{prefix}#{method}")
end

View File

@@ -28,7 +28,7 @@ class Module
raise ArgumentError, "#{method} is already synchronized. Double synchronization is not currently supported."
end
module_eval(<<-EOS, __FILE__, __LINE__)
module_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) # def expire_with_synchronization(*args, &block)
#{with}.synchronize do # @@lock.synchronize do
#{aliased_method}_without_synchronization#{punctuation}(*args, &block) # expire_without_synchronization(*args, &block)

View File

@@ -19,7 +19,7 @@ module ActiveSupport #:nodoc:
# bad.explicit_checking_method "T".mb_chars.downcase.to_s
#
# The default Chars implementation assumes that the encoding of the string is UTF-8, if you want to handle different
# encodings you can write your own multibyte string handler and configure it through
# encodings you can write your own multibyte string handler and configure it through
# ActiveSupport::Multibyte.proxy_class.
#
# class CharsForUTF32
@@ -458,8 +458,10 @@ module ActiveSupport #:nodoc:
end
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
def tidy_bytes
chars(self.class.tidy_bytes(@wrapped_string))
#
# Passing +true+ will forcibly tidy all bytes, assuming that the string's encoding is entirely CP1252 or ISO-8859-1.
def tidy_bytes(force = false)
chars(self.class.tidy_bytes(@wrapped_string, force))
end
%w(lstrip rstrip strip reverse upcase downcase tidy_bytes capitalize).each do |method|
@@ -528,7 +530,7 @@ module ActiveSupport #:nodoc:
unpacked << codepoints[marker..pos-1]
marker = pos
end
end
end
unpacked
end
@@ -644,33 +646,80 @@ module ActiveSupport #:nodoc:
codepoints
end
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
def tidy_bytes(string)
string.split(//u).map do |c|
c.force_encoding(Encoding::ASCII) if c.respond_to?(:force_encoding)
def tidy_byte(byte)
if byte < 160
[UCD.cp1252[byte] || byte].pack("U").unpack("C*")
elsif byte < 192
[194, byte]
else
[195, byte - 64]
end
end
private :tidy_byte
if !ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'].match(c)
n = c.unpack('C')[0]
n < 128 ? n.chr :
n < 160 ? [UCD.cp1252[n] || n].pack('U') :
n < 192 ? "\xC2" + n.chr : "\xC3" + (n-64).chr
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
#
# Passing +true+ will forcibly tidy all bytes, assuming that the string's encoding is entirely CP-1252 or ISO-8859-1.
def tidy_bytes(string, force = false)
if force
return string.unpack("C*").map do |b|
tidy_byte(b)
end.flatten.compact.pack("C*").unpack("U*").pack("U*")
end
bytes = string.unpack("C*")
conts_expected = 0
last_lead = 0
bytes.each_index do |i|
byte = bytes[i]
is_ascii = byte < 128
is_cont = byte > 127 && byte < 192
is_lead = byte > 191 && byte < 245
is_unused = byte > 240
is_restricted = byte > 244
# Impossible or highly unlikely byte? Clean it.
if is_unused || is_restricted
bytes[i] = tidy_byte(byte)
elsif is_cont
# Not expecting contination byte? Clean up. Otherwise, now expect one less.
conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1
else
c
if conts_expected > 0
# Expected continuation, but got ASCII or leading? Clean backwards up to
# the leading byte.
(1..(i - last_lead)).each {|j| bytes[i - j] = tidy_byte(bytes[i - j])}
conts_expected = 0
end
if is_lead
# Final byte is leading? Clean it.
if i == bytes.length - 1
bytes[i] = tidy_byte(bytes.last)
else
# Valid leading byte? Expect continuations determined by position of
# first zero bit, with max of 3.
conts_expected = byte < 224 ? 1 : byte < 240 ? 2 : 3
last_lead = i
end
end
end
end.join
end
bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*")
end
end
protected
def translate_offset(byte_offset) #:nodoc:
return nil if byte_offset.nil?
return 0 if @wrapped_string == ''
if @wrapped_string.respond_to?(:force_encoding)
@wrapped_string = @wrapped_string.dup.force_encoding(Encoding::ASCII_8BIT)
end
begin
@wrapped_string[0...byte_offset].unpack('U*').length
rescue ArgumentError => e

View File

@@ -1,13 +1,28 @@
require 'yaml'
YAML.add_builtin_type("omap") do |type, val|
ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)]
end
# OrderedHash is namespaced to prevent conflicts with other implementations
module ActiveSupport
# Hash is ordered in Ruby 1.9!
if RUBY_VERSION >= '1.9'
class OrderedHash < ::Hash #:nodoc:
class OrderedHash < ::Hash #:nodoc:
def to_yaml_type
"!tag:yaml.org,2002:omap"
end
else
class OrderedHash < Hash #:nodoc:
def to_yaml(opts = {})
YAML.quick_emit(self, opts) do |out|
out.seq(taguri, to_yaml_style) do |seq|
each do |k, v|
seq.add(k => v)
end
end
end
end
# Hash is ordered in Ruby 1.9!
if RUBY_VERSION < '1.9'
def initialize(*args, &block)
super
@keys = []
@@ -55,7 +70,7 @@ module ActiveSupport
end
super
end
def delete_if
super
sync_keys!
@@ -134,31 +149,10 @@ module ActiveSupport
"#<OrderedHash #{super}>"
end
private
def sync_keys!
@keys.delete_if {|k| !has_key?(k)}
end
end
end
class OrderedHash #:nodoc:
def to_yaml_type
"!tag:yaml.org,2002:omap"
end
def to_yaml(opts = {})
YAML.quick_emit(self, opts) do |out|
out.seq(taguri, to_yaml_style) do |seq|
each do |k, v|
seq.add(k => v)
end
private
def sync_keys!
@keys.delete_if {|k| !has_key?(k)}
end
end
end
end
YAML.add_builtin_type("omap") do |type, val|
ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)]
end
end

View File

@@ -3,7 +3,7 @@ module ActiveSupport
MAJOR = 3
MINOR = 0
TINY = 0
BUILD = "beta2"
BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end

View File

@@ -9,6 +9,11 @@ class BacktraceCleanerFilterTest < ActiveSupport::TestCase
test "backtrace should not contain prefix when it has been filtered out" do
assert_equal "/my/class.rb", @bc.clean([ "/my/prefix/my/class.rb" ]).first
end
test "backtrace cleaner should allow removing filters" do
@bc.remove_filters!
assert_equal "/my/prefix/my/class.rb", @bc.clean(["/my/prefix/my/class.rb"]).first
end
test "backtrace should contain unaltered lines if they dont match a filter" do
assert_equal "/my/other_prefix/my/class.rb", @bc.clean([ "/my/other_prefix/my/class.rb" ]).first
@@ -44,4 +49,4 @@ class BacktraceCleanerFilterAndSilencerTest < ActiveSupport::TestCase
test "backtrace should not silence lines that has first had their silence hook filtered out" do
assert_equal [ "/class.rb" ], @bc.clean([ "/mongrel/class.rb" ])
end
end
end

View File

@@ -107,7 +107,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
# Ruby 1.9 only supports basic whitespace
@whitespace = "\n\t ".force_encoding(Encoding::UTF_8)
end
@byte_order_mark = [65279].pack('U')
end
@@ -468,14 +468,6 @@ end
class MultibyteCharsExtrasTest < Test::Unit::TestCase
include MultibyteTestHelpers
if RUBY_VERSION >= '1.9'
def test_tidy_bytes_is_broken_on_1_9_0
assert_raise(ArgumentError) do
assert_equal_codepoints [0xfffd].pack('U'), chars("\xef\xbf\xbd").tidy_bytes
end
end
end
def test_upcase_should_be_unicode_aware
assert_equal "АБВГД\0F", chars("аБвгд\0f").upcase
assert_equal 'こにちわ', chars('こにちわ').upcase
@@ -504,7 +496,7 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
def test_limit_should_work_on_a_multibyte_string
example = chars(UNICODE_STRING)
bytesize = UNICODE_STRING.respond_to?(:bytesize) ? UNICODE_STRING.bytesize : UNICODE_STRING.size
assert_equal UNICODE_STRING, example.limit(bytesize)
assert_equal '', example.limit(0)
assert_equal '', example.limit(1)
@@ -531,7 +523,7 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
assert example.limit(limit).to_s.length <= limit
end
end
def test_composition_exclusion_is_set_up_properly
# Normalization of DEVANAGARI LETTER QA breaks when composition exclusion isn't used correctly
qa = [0x915, 0x93c].pack('U*')
@@ -607,28 +599,57 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
end
def test_tidy_bytes_should_tidy_bytes
single_byte_cases = {
"\x21" => "!", # Valid ASCII byte, low
"\x41" => "A", # Valid ASCII byte, mid
"\x7E" => "~", # Valid ASCII byte, high
"\x80" => "", # Continuation byte, low (cp125)
"\x94" => "", # Continuation byte, mid (cp125)
"\x9F" => "Ÿ", # Continuation byte, high (cp125)
"\xC0" => "À", # Overlong encoding, start of 2-byte sequence, but codepoint < 128
"\xC1" => "Á", # Overlong encoding, start of 2-byte sequence, but codepoint < 128
"\xC2" => "Â", # Start of 2-byte sequence, low
"\xC8" => "È", # Start of 2-byte sequence, mid
"\xDF" => "ß", # Start of 2-byte sequence, high
"\xE0" => "à", # Start of 3-byte sequence, low
"\xE8" => "è", # Start of 3-byte sequence, mid
"\xEF" => "ï", # Start of 3-byte sequence, high
"\xF0" => "ð", # Start of 4-byte sequence
"\xF1" => "ñ", # Unused byte
"\xFF" => "ÿ", # Restricted byte
"\x00" => "\x00" # null char
}
single_byte_cases.each do |bad, good|
assert_equal good, chars(bad).tidy_bytes.to_s
assert_equal "#{good}#{good}", chars("#{bad}#{bad}").tidy_bytes
assert_equal "#{good}#{good}#{good}", chars("#{bad}#{bad}#{bad}").tidy_bytes
assert_equal "#{good}a", chars("#{bad}a").tidy_bytes
assert_equal "#{good}á", chars("#{bad}á").tidy_bytes
assert_equal "a#{good}a", chars("a#{bad}a").tidy_bytes
assert_equal "á#{good}á", chars("á#{bad}á").tidy_bytes
assert_equal "a#{good}", chars("a#{bad}").tidy_bytes
assert_equal "á#{good}", chars("á#{bad}").tidy_bytes
end
byte_string = "\270\236\010\210\245"
tidy_string = [0xb8, 0x17e, 0x8, 0x2c6, 0xa5].pack('U*')
ascii_padding = 'aa'
utf8_padding = 'éé'
assert_equal_codepoints tidy_string, chars(byte_string).tidy_bytes
assert_equal_codepoints ascii_padding.dup.insert(1, tidy_string),
chars(ascii_padding.dup.insert(1, byte_string)).tidy_bytes
assert_equal_codepoints utf8_padding.dup.insert(2, tidy_string),
chars(utf8_padding.dup.insert(2, byte_string)).tidy_bytes
assert_nothing_raised { chars(byte_string).tidy_bytes.to_s.unpack('U*') }
assert_equal_codepoints "\xC3\xA7", chars("\xE7").tidy_bytes # iso_8859_1: small c cedilla
assert_equal_codepoints "\xE2\x80\x9C", chars("\x93").tidy_bytes # win_1252: left smart quote
assert_equal_codepoints "\xE2\x82\xAC", chars("\x80").tidy_bytes # win_1252: euro
assert_equal_codepoints "\x00", chars("\x00").tidy_bytes # null char
assert_equal_codepoints [0xfffd].pack('U'), chars("\xef\xbf\xbd").tidy_bytes # invalid char
rescue ArgumentError => e
raise e if RUBY_VERSION < '1.9'
# UTF-8 leading byte followed by too few continuation bytes
assert_equal_codepoints "\xc3\xb0\xc2\xa5\xc2\xa4\x21", chars("\xf0\xa5\xa4\x21").tidy_bytes
end
def test_tidy_bytes_should_forcibly_tidy_bytes_if_specified
byte_string = "\xF0\xA5\xA4\xA4" # valid as both CP-1252 and UTF-8, but with different interpretations.
assert_not_equal "𥤤", chars(byte_string).tidy_bytes
# Forcible conversion to UTF-8
assert_equal "𥤤", chars(byte_string).tidy_bytes(true)
end
private
def string_from_classes(classes)

View File

@@ -1,5 +1,3 @@
* A new application now comes with a layout and a stylesheet. [JV]
* Renamed config.cookie_secret to config.secret_token and pass it as env key. [JV]
*Rails 3.0.0 [beta 2] (April 1st, 2010)*

View File

@@ -437,7 +437,7 @@ div.code_container, div.important, div.caution, div.warning, div.note, div.info
/* Remove bottom margin of paragraphs in special boxes, otherwise they get a
spurious blank area below with the box background. */
div.important p, div.caution p, div.warning p, div.note p, div.info p {
margin-bottom: 0px;
margin-bottom: 1em;
}
/* Edge Badge

File diff suppressed because it is too large Load Diff

View File

@@ -79,6 +79,10 @@ module Rails
@_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
end
def env=(environment)
@_env = ActiveSupport::StringInquirer.new(environment)
end
def cache
RAILS_CACHE
end
@@ -88,11 +92,12 @@ module Rails
end
def public_path
@@public_path ||= self.root ? File.join(self.root, "public") : "public"
application && application.paths.public.to_a.first
end
def public_path=(path)
@@public_path = path
ActiveSupport::Deprecation.warn "Setting Rails.public_path= is deprecated. " <<
"Please set paths.public = in config/application.rb instead.", caller
end
end
end

View File

@@ -6,21 +6,29 @@ module Rails
include ::Rails::Configuration::Deprecated
attr_accessor :allow_concurrency, :cache_classes, :cache_store,
:secret_token, :consider_all_requests_local, :dependency_loading,
:encoding, :consider_all_requests_local, :dependency_loading,
:filter_parameters, :log_level, :logger, :metals,
:plugins, :preload_frameworks, :reload_engines, :reload_plugins,
:serve_static_assets, :time_zone, :whiny_nils
:secret_token, :serve_static_assets, :time_zone, :whiny_nils
def initialize(*)
super
@allow_concurrency = false
@filter_parameters = []
@dependency_loading = true
@allow_concurrency = false
@consider_all_requests_local = false
@encoding = "utf-8"
@filter_parameters = []
@dependency_loading = true
@serve_static_assets = true
@time_zone = "UTC"
@consider_all_requests_local = true
@session_store = :cookie_store
@session_options = {}
@time_zone = "UTC"
end
def encoding=(value)
@encoding = value
if defined?(Encoding) && Encoding.respond_to?(:default_external=)
Encoding.default_external = value
end
end
def middleware
@@ -129,7 +137,7 @@ module Rails
def default_middleware_stack
ActionDispatch::MiddlewareStack.new.tap do |middleware|
middleware.use('::ActionDispatch::Static', lambda { Rails.public_path }, :if => lambda { serve_static_assets })
middleware.use('::ActionDispatch::Static', lambda { paths.public.to_a.first }, :if => lambda { serve_static_assets })
middleware.use('::Rack::Lock', :if => lambda { !allow_concurrency })
middleware.use('::Rack::Runtime')
middleware.use('::Rails::Rack::Logger')

View File

@@ -43,7 +43,7 @@ module Rails
end
def load_once_paths
@eager_load_paths ||= paths.load_once
@load_once_paths ||= paths.load_once
end
def load_paths

View File

@@ -26,6 +26,7 @@ module Rails
:orm => '-o',
:resource_controller => '-c',
:scaffold_controller => '-c',
:stylesheets => '-y',
:template_engine => '-e',
:test_framework => '-t'
},
@@ -50,6 +51,7 @@ module Rails
:resource_controller => :controller,
:scaffold_controller => :scaffold_controller,
:singleton => false,
:stylesheets => true,
:test_framework => nil,
:template_engine => :erb
},

View File

@@ -1,5 +1,14 @@
<%%= form_for(@<%= singular_name %>) do |f| %>
<%%= f.error_messages %>
<%% if @<%= singular_name %>.errors.any? %>
<div id="errorExplanation">
<h2><%%= pluralize(@<%= singular_name %>.errors.count, "error") %> prohibited this <%= singular_name %> from being saved:</h2>
<ul>
<%% @<%= singular_name %>.errors.full_messages.each do |msg| %>
<li><%%= msg %></li>
<%% end %>
</ul>
</div>
<%% end %>
<% for attribute in attributes -%>
<div class="field">

View File

@@ -1,3 +1,5 @@
<p class="notice"><%%= notice %></p>
<% for attribute in attributes -%>
<p>
<b><%= attribute.human_name %>:</b>

View File

@@ -78,7 +78,7 @@ module Rails::Generators
end
def create_app_files
directory "app"
directory 'app'
end
def create_config_files
@@ -137,7 +137,7 @@ module Rails::Generators
end
def create_public_stylesheets_files
directory "public/stylesheets"
empty_directory_with_gitkeep "public/stylesheets"
end
def create_prototype_files

View File

@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title><%= controller_name.humanize %>: <%= action_name %></title>
<%= stylesheet_link_tag 'application' %>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
</head>
<body>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%=raw yield %>
</body>
</html>

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title><%= app_const_base %></title>
<%%= stylesheet_link_tag :all %>
<%%= javascript_include_tag :defaults %>
<%%= csrf_meta_tag %>
</head>
<body>
<%%= yield %>
</body>
</html>

View File

@@ -46,7 +46,10 @@ module <%= app_const_base %>
# g.test_framework :test_unit, :fixture => true
# end
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters << :password
config.filter_parameters += [:password]
end
end

View File

@@ -1,15 +1,8 @@
document.observe("dom:loaded", function() {
var authToken = $$('meta[name=csrf-token]').first().readAttribute('content'),
authParam = $$('meta[name=csrf-param]').first().readAttribute('content'),
formTemplate = '<form method="#{method}" action="#{action}">\
#{realmethod}<input name="#{param}" value="#{token}" type="hidden">\
</form>',
realmethodTemplate = '<input name="_method" value="#{method}" type="hidden">';
function handleRemote(element) {
var method, url, params;
if (element.tagName.toLowerCase() == 'form') {
if (element.tagName.toLowerCase() === 'form') {
method = element.readAttribute('method') || 'post';
url = element.readAttribute('action');
params = element.serialize(true);
@@ -39,65 +32,81 @@ document.observe("dom:loaded", function() {
element.fire("ajax:after");
}
function handleMethod(element) {
var method, url, token_name, token;
method = element.readAttribute('data-method');
url = element.readAttribute('href');
csrf_param = $$('meta[name=csrf-param]').first();
csrf_token = $$('meta[name=csrf-token]').first();
var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
element.parentNode.appendChild(form);
if (method != 'post') {
var field = new Element('input', { type: 'hidden', name: '_method', value: method });
form.appendChild(field);
}
if (csrf_param) {
var param = csrf_param.readAttribute('content');
var token = csrf_token.readAttribute('content');
var field = new Element('input', { type: 'hidden', name: param, value: token });
form.appendChild(field);
}
form.submit();
}
$(document.body).observe("click", function(event) {
var message = event.element().readAttribute('data-confirm');
var message = event.findElement().readAttribute('data-confirm');
if (message && !confirm(message)) {
event.stop();
return false;
}
var element = event.findElement("a[data-remote=true]");
var element = event.findElement("a[data-remote]");
if (element) {
handleRemote(element);
event.stop();
return true;
}
var element = event.findElement("a[data-method]");
if (element && element.readAttribute('data-remote') != 'true') {
var method = element.readAttribute('data-method'),
piggyback = method.toLowerCase() != 'post',
formHTML = formTemplate.interpolate({
method: 'POST',
realmethod: piggyback ? realmethodTemplate.interpolate({ method: method }) : '',
action: element.readAttribute('href'),
token: authToken,
param: authParam
});
var form = new Element('div').update(formHTML).down().hide();
this.insert({ bottom: form });
form.submit();
if (element) {
handleMethod(element);
event.stop();
return true;
}
});
// TODO: I don't think submit bubbles in IE
$(document.body).observe("submit", function(event) {
var message = event.element().readAttribute('data-confirm');
var element = event.findElement(),
message = element.readAttribute('data-confirm');
if (message && !confirm(message)) {
event.stop();
return false;
}
var inputs = event.element().select("input[type=submit][data-disable-with]");
var inputs = element.select("input[type=submit][data-disable-with]");
inputs.each(function(input) {
input.disabled = true;
input.writeAttribute('data-original-value', input.value);
input.value = input.readAttribute('data-disable-with');
});
var element = event.findElement("form[data-remote=true]");
var element = event.findElement("form[data-remote]");
if (element) {
handleRemote(element);
event.stop();
}
});
$(document.body).observe("ajax:complete", function(event) {
var element = event.element();
$(document.body).observe("ajax:after", function(event) {
var element = event.findElement();
if (element.tagName.toLowerCase() == 'form') {
if (element.tagName.toLowerCase() === 'form') {
var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
inputs.each(function(input) {
input.value = input.readAttribute('data-original-value');
@@ -106,4 +115,4 @@ document.observe("dom:loaded", function() {
});
}
});
});
});

View File

@@ -3,11 +3,13 @@ require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
<% unless options[:skip_activerecord] -%>
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
fixtures :all
<% end -%>
# Add more helper methods to be used by all tests here...
end

View File

@@ -7,6 +7,7 @@ module Rails
remove_class_option :actions
hook_for :scaffold_controller, :required => true
hook_for :stylesheets
end
end
end

View File

@@ -0,0 +1,5 @@
Description:
Copies scaffold stylesheets to public/stylesheets/.
Examples:
`rails generate stylesheets`

Some files were not shown because too many files have changed in this diff Show More