提交 aab2f0b3 编写于 作者: J Jeremy Kemper

Merge branch 'master' of git@github.com:rails/rails

*Edge*
* Allow polymorphic_url helper to take url options. #880 [Tarmo Tänav]
* Switched integration test runner to use Rack processor instead of CGI [Josh Peek]
* Made AbstractRequest.if_modified_sense return nil if the header could not be parsed [Jamis Buck]
* Added back ActionController::Base.allow_concurrency flag [Josh Peek]
......
......@@ -44,7 +44,7 @@ def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, o
def to_prepare(identifier = nil, &block)
@prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
@prepare_dispatch_callbacks | callback
@prepare_dispatch_callbacks.replace_or_append!(callback)
end
# If the block raises, send status code as a last-ditch response.
......
......@@ -228,21 +228,6 @@ def url_for(options)
end
private
class StubCGI < CGI #:nodoc:
attr_accessor :stdinput, :stdoutput, :env_table
def initialize(env, stdinput = nil)
self.env_table = env
self.stdoutput = StringIO.new
super
stdinput.set_encoding(Encoding::BINARY) if stdinput.respond_to?(:set_encoding)
stdinput.force_encoding(Encoding::BINARY) if stdinput.respond_to?(:force_encoding)
@stdinput = stdinput.is_a?(IO) ? stdinput : StringIO.new(stdinput || '')
end
end
# Tailors the session based on the given URI, setting the HTTPS value
# and the hostname.
def interpret_uri(path)
......@@ -290,9 +275,8 @@ def process(method, path, parameters = nil, headers = nil)
ActionController::Base.clear_last_instantiation!
cgi = StubCGI.new(env, data)
ActionController::Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, cgi.stdoutput)
@result = cgi.stdoutput.string
env['rack.input'] = data.is_a?(IO) ? data : StringIO.new(data || '')
@status, @headers, result_body = ActionController::Dispatcher.new.call(env)
@request_count += 1
@controller = ActionController::Base.last_instantiation
......@@ -306,32 +290,34 @@ def process(method, path, parameters = nil, headers = nil)
@html_document = nil
parse_result
return status
rescue MultiPartNeededException
boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
status = process(method, path, multipart_body(parameters, boundary), (headers || {}).merge({"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"}))
return status
end
# Inject status back in for backwords compatibility with CGI
@headers['Status'] = @status
# Parses the result of the response and extracts the various values,
# like cookies, status, headers, etc.
def parse_result
response_headers, result_body = @result.split(/\r\n\r\n/, 2)
@status, @status_message = @status.split(/ /)
@status = @status.to_i
@headers = Hash.new { |h,k| h[k] = [] }
response_headers.to_s.each_line do |line|
key, value = line.strip.split(/:\s*/, 2)
@headers[key.downcase] << value
cgi_headers = Hash.new { |h,k| h[k] = [] }
@headers.each do |key, value|
cgi_headers[key.downcase] << value
end
cgi_headers['set-cookie'] = cgi_headers['set-cookie'].first
@headers = cgi_headers
(@headers['set-cookie'] || [] ).each do |string|
name, value = string.match(/^([^=]*)=([^;]*);/)[1,2]
@response.headers['cookie'] ||= []
(@headers['set-cookie'] || []).each do |cookie|
name, value = cookie.match(/^([^=]*)=([^;]*);/)[1,2]
@cookies[name] = value
# Fake CGI cookie header
# DEPRECATE: Use response.headers["Set-Cookie"] instead
@response.headers['cookie'] << CGI::Cookie::new("name" => name, "value" => value)
end
@status, @status_message = @headers["status"].first.to_s.split(/ /)
@status = @status.to_i
return status
rescue MultiPartNeededException
boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
status = process(method, path, multipart_body(parameters, boundary), (headers || {}).merge({"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"}))
return status
end
# Encode the cookies hash in a format suitable for passing to a
......@@ -344,13 +330,15 @@ def encode_cookies
# Get a temporary URL writer object
def generic_url_rewriter
cgi = StubCGI.new('REQUEST_METHOD' => "GET",
'QUERY_STRING' => "",
"REQUEST_URI" => "/",
"HTTP_HOST" => host,
"SERVER_PORT" => https? ? "443" : "80",
"HTTPS" => https? ? "on" : "off")
ActionController::UrlRewriter.new(ActionController::CgiRequest.new(cgi), {})
env = {
'REQUEST_METHOD' => "GET",
'QUERY_STRING' => "",
"REQUEST_URI" => "/",
"HTTP_HOST" => host,
"SERVER_PORT" => https? ? "443" : "80",
"HTTPS" => https? ? "on" : "off"
}
ActionController::UrlRewriter.new(ActionController::RackRequest.new(env), {})
end
def name_with_prefix(prefix, name)
......
......@@ -102,6 +102,12 @@ def polymorphic_url(record_or_hash_or_array, options = {})
args << format if format
named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
url_options = options.except(:action, :routing_type, :format)
unless url_options.empty?
args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
end
send!(named_route, *args)
end
......@@ -114,19 +120,19 @@ def polymorphic_path(record_or_hash_or_array, options = {})
%w(edit new formatted).each do |action|
module_eval <<-EOT, __FILE__, __LINE__
def #{action}_polymorphic_url(record_or_hash)
polymorphic_url(record_or_hash, :action => "#{action}")
def #{action}_polymorphic_url(record_or_hash, options = {})
polymorphic_url(record_or_hash, options.merge(:action => "#{action}"))
end
def #{action}_polymorphic_path(record_or_hash)
polymorphic_url(record_or_hash, :action => "#{action}", :routing_type => :path)
def #{action}_polymorphic_path(record_or_hash, options = {})
polymorphic_url(record_or_hash, options.merge(:action => "#{action}", :routing_type => :path))
end
EOT
end
private
def action_prefix(options)
options[:action] ? "#{options[:action]}_" : ""
options[:action] ? "#{options[:action]}_" : options[:format] ? "formatted_" : ""
end
def routing_type(options)
......
......@@ -17,7 +17,7 @@ module AtomFeedHelper
# # GET /posts.atom
# def index
# @posts = Post.find(:all)
#
#
# respond_to do |format|
# format.html
# format.atom
......@@ -29,12 +29,12 @@ module AtomFeedHelper
# atom_feed do |feed|
# feed.title("My great blog!")
# feed.updated((@posts.first.created_at))
#
#
# for post in @posts
# feed.entry(post) do |entry|
# entry.title(post.title)
# entry.content(post.body, :type => 'html')
#
#
# entry.author do |author|
# author.name("DHH")
# end
......@@ -47,8 +47,9 @@ module AtomFeedHelper
# * <tt>:language</tt>: Defaults to "en-US".
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
# * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
# created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
# * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}"
# * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
# created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
# 2005 is used (as an "I don't care" value).
#
# Other namespaces can be added to the root element:
......@@ -81,7 +82,7 @@ def atom_feed(options = {}, &block)
else
options[:schema_date] = "2005" # The Atom spec copyright date
end
xml = options[:xml] || eval("xml", block.binding)
xml.instruct!
......@@ -89,10 +90,10 @@ def atom_feed(options = {}, &block)
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
xml.feed(feed_opts) do
xml.id("tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
yield AtomFeedBuilder.new(xml, self, options)
end
end
......@@ -102,7 +103,7 @@ class AtomFeedBuilder
def initialize(xml, view, feed_options = {})
@xml, @view, @feed_options = xml, view, feed_options
end
# Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
def updated(date_or_time = nil)
@xml.updated((date_or_time || Time.now.utc).xmlschema)
......@@ -115,9 +116,10 @@ def updated(date_or_time = nil)
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
# * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
# * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
def entry(record, options = {})
@xml.entry do
@xml.id("tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
@xml.entry do
@xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
if options[:published] || (record.respond_to?(:created_at) && record.created_at)
@xml.published((options[:published] || record.created_at).xmlschema)
......
module ActionView #:nodoc:
class PathSet < ActiveSupport::TypedArray #:nodoc:
class PathSet < Array #:nodoc:
def self.type_cast(obj)
if obj.is_a?(String)
if Base.warn_cache_misses && defined?(Rails) && Rails.initialized?
......@@ -15,6 +15,30 @@ def self.type_cast(obj)
end
end
def initialize(*args)
super(*args).map! { |obj| self.class.type_cast(obj) }
end
def <<(obj)
super(self.class.type_cast(obj))
end
def concat(array)
super(array.map! { |obj| self.class.type_cast(obj) })
end
def insert(index, obj)
super(index, self.class.type_cast(obj))
end
def push(*objs)
super(*objs.map { |obj| self.class.type_cast(obj) })
end
def unshift(*objs)
super(*objs.map { |obj| self.class.type_cast(obj) })
end
class Path #:nodoc:
def self.eager_load_templates!
@eager_load_templates = true
......
......@@ -327,9 +327,12 @@ def test_cookie_monster
assert_equal ["410 Gone"], headers["status"]
assert_response 410
assert_response :gone
assert_equal nil, response.headers["Set-Cookie"]
assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], response.headers["Set-Cookie"]
assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], headers['set-cookie']
assert_equal [[], ["chocolate"]], response.headers["cookie"]
assert_equal [
CGI::Cookie::new("name" => "cookie_1", "value" => ""),
CGI::Cookie::new("name" => "cookie_3", "value" => "chocolate")
], response.headers["cookie"]
assert_equal [], headers["cookie"]
assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies)
assert_equal "Gone", response.body
......
......@@ -60,6 +60,18 @@ def test_url_helper_prefixed_with_edit
edit_polymorphic_url(@article)
end
def test_url_helper_prefixed_with_edit_with_url_options
@article.save
expects(:edit_article_url).with(@article, :param1 => '10')
edit_polymorphic_url(@article, :param1 => '10')
end
def test_url_helper_with_url_options
@article.save
expects(:article_url).with(@article, :param1 => '10')
polymorphic_url(@article, :param1 => '10')
end
def test_formatted_url_helper
expects(:formatted_article_url).with(@article, :pdf)
formatted_polymorphic_url([@article, :pdf])
......@@ -67,10 +79,16 @@ def test_formatted_url_helper
def test_format_option
@article.save
expects(:article_url).with(@article, :pdf)
expects(:formatted_article_url).with(@article, :pdf)
polymorphic_url(@article, :format => :pdf)
end
def test_format_option_with_url_options
@article.save
expects(:formatted_article_url).with(@article, :pdf, :param1 => '10')
polymorphic_url(@article, :format => :pdf, :param1 => '10')
end
def test_id_and_format_option
@article.save
expects(:article_url).with(:id => @article, :format => :pdf)
......@@ -147,7 +165,7 @@ def test_nesting_with_array_containing_singleton_resource_and_format
def test_nesting_with_array_containing_singleton_resource_and_format_option
@tag = Tag.new
@tag.save
expects(:article_response_tag_url).with(@article, @tag, :pdf)
expects(:formatted_article_response_tag_url).with(@article, @tag, :pdf)
polymorphic_url([@article, :response, @tag], :format => :pdf)
end
......
......@@ -67,6 +67,24 @@ class ScrollsController < ActionController::Base
entry.content(scroll.body, :type => 'html')
entry.tag!('app:edited', Time.now)
entry.author do |author|
author.name("DHH")
end
end
end
end
EOT
FEEDS["feed_with_overridden_ids"] = <<-EOT
atom_feed({:id => 'tag:test.rubyonrails.org,2008:test/'}) do |feed|
feed.title("My great blog!")
feed.updated((@scrolls.first.created_at))
for scroll in @scrolls
feed.entry(scroll, :id => "tag:test.rubyonrails.org,2008:"+scroll.id.to_s) do |entry|
entry.title(scroll.title)
entry.content(scroll.body, :type => 'html')
entry.tag!('app:edited', Time.now)
entry.author do |author|
author.name("DHH")
end
......@@ -79,7 +97,7 @@ def index
Scroll.new(1, "1", "Hello One", "Something <i>COOL!</i>", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)),
Scroll.new(2, "2", "Hello Two", "Something Boring", Time.utc(2007, 12, 12, 15)),
]
render :inline => FEEDS[params[:id]], :type => :builder
end
......@@ -98,21 +116,21 @@ def setup
@request.host = "www.nextangle.com"
end
def test_feed_should_use_default_language_if_none_is_given
with_restful_routing(:scrolls) do
get :index, :id => "defaults"
assert_match %r{xml:lang="en-US"}, @response.body
end
end
def test_feed_should_include_two_entries
with_restful_routing(:scrolls) do
get :index, :id => "defaults"
assert_select "entry", 2
end
end
def test_entry_should_only_use_published_if_created_at_is_present
with_restful_routing(:scrolls) do
get :index, :id => "defaults"
......@@ -167,7 +185,16 @@ def test_feed_should_include_atomPub_namespace
end
end
private
def test_feed_should_allow_overriding_ids
with_restful_routing(:scrolls) do
get :index, :id => "feed_with_overridden_ids"
assert_select "id", :text => "tag:test.rubyonrails.org,2008:test/"
assert_select "entry id", :text => "tag:test.rubyonrails.org,2008:1"
assert_select "entry id", :text => "tag:test.rubyonrails.org,2008:2"
end
end
private
def with_restful_routing(resources)
with_routing do |set|
set.draw do |map|
......
......@@ -1679,19 +1679,19 @@ def conditions_tables(options)
else all << cond
end
end
conditions.join(' ').scan(/([\.\w]+).?\./).flatten
conditions.join(' ').scan(/([\.a-zA-Z_]+).?\./).flatten
end
def order_tables(options)
order = [options[:order], scope(:find, :order) ].join(", ")
return [] unless order && order.is_a?(String)
order.scan(/([\.\w]+).?\./).flatten
order.scan(/([\.a-zA-Z_]+).?\./).flatten
end
def selects_tables(options)
select = options[:select]
return [] unless select && select.is_a?(String)
select.scan(/"?([\.\w]+)"?.?\./).flatten
select.scan(/"?([\.a-zA-Z_]+)"?.?\./).flatten
end
# Checks if the conditions reference a table other than the current model table
......
......@@ -35,8 +35,11 @@ def count_records
else
@reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
end
@target = [] and loaded if count == 0
# If there's nothing in the database and @target has no new records
# we are certain the current target is an empty array. This is a
# documented side-effect of the method that may avoid an extra SELECT.
@target ||= [] and loaded if count == 0
if @reflection.options[:limit]
count = [ @reflection.options[:limit], count ].min
......
......@@ -21,8 +21,8 @@ def build(attrs = {}, replace_existing = true)
def replace(obj, dont_save = false)
load_target
unless @target.nil?
if dependent? && !dont_save && @target != obj
unless @target.nil? || @target == obj
if dependent? && !dont_save
@target.destroy unless @target.new_record?
@owner.clear_association_cache
else
......
......@@ -1750,7 +1750,7 @@ def all_attributes_exists?(attribute_names)
def attribute_condition(argument)
case argument
when nil then "IS ?"
when Array, ActiveRecord::Associations::AssociationCollection then "IN (?)"
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)"
when Range then "BETWEEN ? AND ?"
else "= ?"
end
......@@ -2572,11 +2572,14 @@ def ensure_proper_type
end
def convert_number_column_value(value)
case value
when FalseClass; 0
when TrueClass; 1
when ''; nil
else value
if value == false
0
elsif value == true
1
elsif value.is_a?(String) && value.blank?
nil
else
value
end
end
......
......@@ -138,7 +138,11 @@ def string_to_dummy_time(string)
# convert something to a boolean
def value_to_boolean(value)
TRUE_VALUES.include?(value)
if value.blank?
nil
else
TRUE_VALUES.include?(value)
end
end
# convert something to a BigDecimal
......
......@@ -103,7 +103,7 @@ class Scope
attr_reader :proxy_scope, :proxy_options
[].methods.each do |m|
unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last|empty?|any?|respond_to?)/
unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|^find$|count|sum|average|maximum|minimum|paginate|first|last|empty?|any?|respond_to?)/
delegate m, :to => :proxy_found
end
end
......@@ -140,8 +140,8 @@ def empty?
@found ? @found.empty? : count.zero?
end
def respond_to?(method)
super || @proxy_scope.respond_to?(method)
def respond_to?(method, include_private = false)
super || @proxy_scope.respond_to?(method, include_private)
end
def any?
......
require 'cases/helper'
module Remembered
def self.included(base)
base.extend ClassMethods
base.class_eval do
after_create :remember
protected
def remember; self.class.remembered << self; end
end
end
module ClassMethods
def remembered; @@remembered ||= []; end
def rand; @@remembered.rand; end
end
end
class ShapeExpression < ActiveRecord::Base
belongs_to :shape, :polymorphic => true
......@@ -8,26 +23,33 @@ class ShapeExpression < ActiveRecord::Base
class Circle < ActiveRecord::Base
has_many :shape_expressions, :as => :shape
include Remembered
end
class Square < ActiveRecord::Base
has_many :shape_expressions, :as => :shape
include Remembered
end
class Triangle < ActiveRecord::Base
has_many :shape_expressions, :as => :shape
include Remembered
end
class PaintColor < ActiveRecord::Base
has_many :shape_expressions, :as => :paint
belongs_to :non_poly, :foreign_key => "non_poly_one_id", :class_name => "NonPolyOne"
include Remembered
end
class PaintTexture < ActiveRecord::Base
has_many :shape_expressions, :as => :paint
belongs_to :non_poly, :foreign_key => "non_poly_two_id", :class_name => "NonPolyTwo"
include Remembered
end
class NonPolyOne < ActiveRecord::Base
has_many :paint_colors
include Remembered
end
class NonPolyTwo < ActiveRecord::Base
has_many :paint_textures
include Remembered
end
......@@ -49,23 +71,19 @@ def teardown
end
# meant to be supplied as an ID, never returns 0
def rand_simple
val = (NUM_SIMPLE_OBJS * rand).round
val == 0 ? 1 : val
end
def generate_test_object_graphs
1.upto(NUM_SIMPLE_OBJS) do
[Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!)
end
1.upto(NUM_SIMPLE_OBJS) do |i|
PaintColor.create!(:non_poly_one_id => rand_simple)
PaintTexture.create!(:non_poly_two_id => rand_simple)
1.upto(NUM_SIMPLE_OBJS) do
PaintColor.create!(:non_poly_one_id => NonPolyOne.rand.id)
PaintTexture.create!(:non_poly_two_id => NonPolyTwo.rand.id)
end
1.upto(NUM_SHAPE_EXPRESSIONS) do |i|
ShapeExpression.create!(:shape_type => [Circle, Square, Triangle].rand.to_s, :shape_id => rand_simple,
:paint_type => [PaintColor, PaintTexture].rand.to_s, :paint_id => rand_simple)
1.upto(NUM_SHAPE_EXPRESSIONS) do
shape_type = [Circle, Square, Triangle].rand
paint_type = [PaintColor, PaintTexture].rand
ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.rand.id,
:paint_type => paint_type.to_s, :paint_id => paint_type.rand.id)
end
end
......
......@@ -559,6 +559,13 @@ def test_eager_with_valid_association_as_string_not_symbol
assert_nothing_raised { Post.find(:all, :include => 'comments') }
end
def test_eager_with_floating_point_numbers
assert_queries(2) do
# Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query
Comment.find :all, :conditions => "123.456 = 123.456", :include => :post
end
end
def test_preconfigured_includes_with_belongs_to
author = posts(:welcome).author_with_posts
assert_no_queries {assert_equal 5, author.posts.size}
......
......@@ -395,6 +395,18 @@ def test_collection_size_after_building
assert_equal 3, company.clients_of_firm.size
end
def test_collection_size_twice_for_regressions
post = posts(:thinking)
assert_equal 0, post.readers.size
# This test needs a post that has no readers, we assert it to ensure it holds,
# but need to reload the post because the very call to #size hides the bug.
post.reload
post.readers.build
size1 = post.readers.size
size2 = post.readers.size
assert_equal size1, size2
end
def test_build_many
company = companies(:first_firm)
new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
......
......@@ -79,6 +79,16 @@ def test_natural_assignment_to_nil
assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
end
def test_natural_assignment_to_already_associated_record
company = companies(:first_firm)
account = accounts(:signals37)
assert_equal company.account, account
company.account = account
company.reload
account.reload
assert_equal company.account, account
end
def test_assignment_without_replacement
apple = Firm.create("name" => "Apple")
citibank = Account.create("credit_limit" => 10)
......
......@@ -4,6 +4,7 @@
require 'models/comment'
require 'models/reply'
require 'models/author'
require 'models/developer'
class NamedScopeTest < ActiveRecord::TestCase
fixtures :posts, :authors, :topics, :comments, :author_addresses
......@@ -51,6 +52,11 @@ def test_scope_should_respond_to_own_methods_and_methods_of_the_proxy
assert Topic.approved.respond_to?(:length)
end
def test_respond_to_respects_include_private_parameter
assert !Topic.approved.respond_to?(:load_found)
assert Topic.approved.respond_to?(:load_found, true)
end
def test_subclasses_inherit_scopes
assert Topic.scopes.include?(:base)
......@@ -238,4 +244,12 @@ def test_should_build_with_proxy_options_chained
assert topic.approved
assert_equal 'lifo', topic.author_name
end
def test_find_all_should_behave_like_select
assert_equal Topic.base.select(&:approved), Topic.base.find_all(&:approved)
end
def test_should_use_where_in_query_for_named_scope
assert_equal Developer.find_all_by_name('Jamis'), Developer.find_all_by_id(Developer.jamises)
end
end
......@@ -1420,8 +1420,8 @@ def test_default_validates_numericality_of
def test_validates_numericality_of_with_nil_allowed
Topic.validates_numericality_of :approved, :allow_nil => true
invalid!(BLANK + JUNK)
valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
invalid!(JUNK)
valid!(NIL + BLANK + FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
end
def test_validates_numericality_of_with_integer_only
......@@ -1434,8 +1434,8 @@ def test_validates_numericality_of_with_integer_only
def test_validates_numericality_of_with_integer_only_and_nil_allowed
Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
invalid!(BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY)
valid!(NIL + INTEGERS)
invalid!(JUNK + FLOATS + BIGDECIMAL + INFINITY)
valid!(NIL + BLANK + INTEGERS)
end
def test_validates_numericality_with_greater_than
......
......@@ -43,6 +43,8 @@ def find_least_recent
has_many :audit_logs
named_scope :jamises, :conditions => {:name => 'Jamis'}
validates_inclusion_of :salary, :in => 50000..200000
validates_length_of :name, :within => 3..20
......
......@@ -39,7 +39,6 @@
require 'active_support/dependencies'
require 'active_support/deprecation'
require 'active_support/typed_array'
require 'active_support/ordered_hash'
require 'active_support/ordered_options'
require 'active_support/option_merger'
......
......@@ -96,15 +96,12 @@ def run(object, options = {}, &terminator)
end
end
def |(chain)
if chain.is_a?(CallbackChain)
chain.each { |callback| self | callback }
# TODO: Decompose into more Array like behavior
def replace_or_append!(chain)
if index = index(chain)
self[index] = chain
else
if (found_callback = find(chain)) && (index = index(chain))
self[index] = chain
else
self << chain
end
self << chain
end
self
end
......@@ -157,6 +154,14 @@ def dup
self.class.new(@kind, @method, @options.dup)
end
def hash
if @identifier
@identifier.hash
else
@method.hash
end
end
def call(*args, &block)
evaluate_method(method, *args, &block) if should_run_callback?(*args)
rescue LocalJumpError
......
......@@ -35,3 +35,9 @@ def duplicable?
false
end
end
class Class #:nodoc:
def duplicable?
false
end
end
module ActiveSupport
class TypedArray < Array
def self.type_cast(obj)
obj
end
def initialize(*args)
super(*args).map! { |obj| self.class.type_cast(obj) }
end
def <<(obj)
super(self.class.type_cast(obj))
end
def concat(array)
super(array.map! { |obj| self.class.type_cast(obj) })
end
def insert(index, obj)
super(index, self.class.type_cast(obj))
end
def push(*objs)
super(*objs.map { |obj| self.class.type_cast(obj) })
end
def unshift(*objs)
super(*objs.map { |obj| self.class.type_cast(obj) })
end
end
end
......@@ -134,10 +134,10 @@ def test_find
assert_equal :bacon, @chain.find(:bacon).method
end
def test_union
assert_equal [:bacon, :lettuce, :tomato], (@chain | Callback.new(:make, :bacon)).map(&:method)
assert_equal [:bacon, :lettuce, :tomato, :turkey], (@chain | CallbackChain.build(:make, :bacon, :lettuce, :tomato, :turkey)).map(&:method)
assert_equal [:bacon, :lettuce, :tomato, :turkey, :mayo], (@chain | Callback.new(:make, :mayo)).map(&:method)
def test_replace_or_append
assert_equal [:bacon, :lettuce, :tomato], (@chain.replace_or_append!(Callback.new(:make, :bacon))).map(&:method)
assert_equal [:bacon, :lettuce, :tomato, :turkey], (@chain.replace_or_append!(Callback.new(:make, :turkey))).map(&:method)
assert_equal [:bacon, :lettuce, :tomato, :turkey, :mayo], (@chain.replace_or_append!(Callback.new(:make, :mayo))).map(&:method)
end
def test_delete
......
require 'abstract_unit'
class DuplicableTest < Test::Unit::TestCase
NO = [nil, false, true, :symbol, 1, 2.3, BigDecimal.new('4.56')]
NO = [nil, false, true, :symbol, 1, 2.3, BigDecimal.new('4.56'), Class.new]
YES = ['1', Object.new, /foo/, [], {}, Time.now]
def test_duplicable
......
require 'abstract_unit'
class TypedArrayTest < Test::Unit::TestCase
class StringArray < ActiveSupport::TypedArray
def self.type_cast(obj)
obj.to_s
end
end
def setup
@array = StringArray.new
end
def test_string_array_initialize
assert_equal ["1", "2", "3"], StringArray.new([1, "2", :"3"])
end
def test_string_array_append
@array << 1
@array << "2"
@array << :"3"
assert_equal ["1", "2", "3"], @array
end
def test_string_array_concat
@array.concat([1, "2"])
@array.concat([:"3"])
assert_equal ["1", "2", "3"], @array
end
def test_string_array_insert
@array.insert(0, 1)
@array.insert(1, "2")
@array.insert(2, :"3")
assert_equal ["1", "2", "3"], @array
end
def test_string_array_push
@array.push(1)
@array.push("2")
@array.push(:"3")
assert_equal ["1", "2", "3"], @array
end
def test_string_array_unshift
@array.unshift(:"3")
@array.unshift("2")
@array.unshift(1)
assert_equal ["1", "2", "3"], @array
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册