Merge branch 'master' into mrbrdo-fixserialization

* master: (142 commits)
  Use Colspan in th Tags
  Added test for link_to_unless to make sure the result consistency.
  Escape the string even when the condition of link_to_unless is not satisfied.
  Add CHANGELOG entry for #10969
  Use a case insensitive URI Regexp for #asset_path
  collection tags accept html attributes as the last element of collection
  Rewind StringIO instances before be parsed again
  Use xml instead already parsed xml
  Updated the doc for const_regexp [ci skip]
  Make test name descriptive and add reference to original regression commit
  fixture setup does not rely on `AR::Base.configurations`.
  regression test + mysql2 adapter raises correct error if conn is closed.
  cleanup, remove trailing whitespace from AR changelog
  'json' gem is no more required under JRuby
  fix typos
  Fix AS changelog [ci skip]
  Update the HTML boolean attributes per the HTML 5.1 spec
  Changing const_regexp to check for constant name.
  valid_app_const? -> valid_const?
  Add CHANGELOG entry for #10740
  ...
This commit is contained in:
Aaron Patterson 2013-06-17 10:47:08 -07:00
commit 252d11321f
153 changed files with 1158 additions and 572 deletions

@ -4,6 +4,8 @@ Ruby on Rails is a volunteer effort. We encourage you to pitch in. [Join the tea
* If you want to submit a patch, please read the [Contributing to Ruby on Rails](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html) guide.
* If you want to contribute to Rails documentation, please read the [Contributing to the Rails Documentation](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation) section of the aforementioned guide.
*We only accept bug reports and pull requests in GitHub*.
* If you have a question about how to use Ruby on Rails, please [ask the rubyonrails-talk mailing list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-talk).

@ -61,7 +61,6 @@ platforms :ruby do
end
platforms :jruby do
gem 'json'
gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.7'
group :db do

@ -1,3 +1,36 @@
* Use a case insensitive URI Regexp for #asset_path.
This fix a problem where the same asset path using different case are generating
different URIs.
Before:
image_tag("HTTP://google.com")
# => "<img alt=\"Google\" src=\"/assets/HTTP://google.com\" />"
image_tag("http://google.com")
# => "<img alt=\"Google\" src=\"http://google.com\" />"
After:
image_tag("HTTP://google.com")
# => "<img alt=\"Google\" src=\"HTTP://google.com\" />"
image_tag("http://google.com")
# => "<img alt=\"Google\" src=\"http://google.com\" />"
*David Celis*
* Element of the `collection_check_boxes` and `collection_radio_buttons` can
optionally contain html attributes as the last element of the array.
*Vasiliy Ermolovich*
* Update the HTML `BOOLEAN_ATTRIBUTES` in `ActionView::Helpers::TagHelper`
to conform to the latest HTML 5.1 spec. Add attributes `allowfullscreen`,
`default`, `inert`, `sortable`, `truespeed`, `typemustmatch`. Fix attribute
`seamless` (previously misspelled `seemless`).
*Alex Peattie*
* Fix an issue where partials with a number in the filename weren't being digested for cache dependencies.
*Bryan Ricker*

@ -77,7 +77,7 @@ def cookie_jar
# domain and subdomains.
#
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
# * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
# * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
# Default is +false+.
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
# only HTTP. Defaults to +false+.

@ -20,8 +20,7 @@ class Railtie < Rails::Railtie # :nodoc:
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
'X-XSS-Protection' => '1; mode=block',
'X-Content-Type-Options' => 'nosniff',
'X-UA-Compatible' => 'chrome=1'
'X-Content-Type-Options' => 'nosniff'
}
config.eager_load_namespaces << ActionDispatch

@ -105,7 +105,7 @@ module Helpers
# )
#
module AssetUrlHelper
URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}i
# Computes the path to asset in public directory. If :type
# options is set, a file extension will be appended and scoped

@ -12,8 +12,11 @@ module TagHelper
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
autoplay controls loop selected hidden scoped async
defer reversed ismap seemless muted required
autofocus novalidate formnovalidate open pubdate itemscope).to_set
defer reversed ismap seamless muted required
autofocus novalidate formnovalidate open pubdate
itemscope allowfullscreen default inert sortable
truespeed typemustmatch).to_set
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
PRE_CONTENT_STRINGS = {

@ -73,8 +73,9 @@ def render_collection #:nodoc:
value = value_for_collection(item, @value_method)
text = value_for_collection(item, @text_method)
default_html_options = default_html_options_for_collection(item, value)
additional_html_options = option_html_attributes(item)
yield item, value, text, default_html_options
yield item, value, text, default_html_options.merge(additional_html_options)
end.join.html_safe
end
end

@ -380,7 +380,7 @@ def link_to_unless(condition, name, options = {}, html_options = {}, &block)
if block_given?
block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
else
name
ERB::Util.html_escape(name)
end
else
link_to(name, options, html_options)

@ -267,7 +267,7 @@ def compile(view, mod) #:nodoc:
method_name = self.method_name
code = @handler.call(self)
# Make sure that the resulting String to be evalled is in the
# Make sure that the resulting String to be eval'd is in the
# encoding of the code
source = <<-end_src
def #{method_name}(local_assigns, output_buffer)

@ -182,8 +182,7 @@ def test_response_body_encoding
ActionDispatch::Response.default_headers = {
'X-Frame-Options' => 'DENY',
'X-Content-Type-Options' => 'nosniff',
'X-XSS-Protection' => '1;',
'X-UA-Compatible' => 'chrome=1'
'X-XSS-Protection' => '1;'
}
resp = ActionDispatch::Response.new.tap { |response|
response.body = 'Hello'
@ -193,7 +192,6 @@ def test_response_body_encoding
assert_equal('DENY', resp.headers['X-Frame-Options'])
assert_equal('nosniff', resp.headers['X-Content-Type-Options'])
assert_equal('1;', resp.headers['X-XSS-Protection'])
assert_equal('chrome=1', resp.headers['X-UA-Compatible'])
ensure
ActionDispatch::Response.default_headers = nil
end

@ -48,6 +48,9 @@ def url_for(*args)
%(asset_path("style.min")) => %(/style.min),
%(asset_path("style.min.css")) => %(/style.min.css),
%(asset_path("http://www.outside.com/image.jpg")) => %(http://www.outside.com/image.jpg),
%(asset_path("HTTP://www.outside.com/image.jpg")) => %(HTTP://www.outside.com/image.jpg),
%(asset_path("style", type: :stylesheet)) => %(/stylesheets/style.css),
%(asset_path("xmlhr", type: :javascript)) => %(/javascripts/xmlhr.js),
%(asset_path("xml.png", type: :image)) => %(/images/xml.png)
@ -445,8 +448,8 @@ def test_image_alt
[nil, '/', '/foo/bar/', 'foo/bar/'].each do |prefix|
assert_equal 'Rails', image_alt("#{prefix}rails.png")
assert_equal 'Rails', image_alt("#{prefix}rails-9c0a079bdd7701d7e729bd956823d153.png")
assert_equal 'Long file name with hyphens', image_alt("#{prefix}long-file-name-with-hyphens.png")
assert_equal 'Long file name with underscores', image_alt("#{prefix}long_file_name_with_underscores.png")
assert_equal 'Long file name with hyphens', image_alt("#{prefix}long-file-name-with-hyphens.png")
assert_equal 'Long file name with underscores', image_alt("#{prefix}long_file_name_with_underscores.png")
end
end

@ -45,7 +45,7 @@ def test_returns_empty_array_if_no_tracker_is_found
end
end
class ERBTrackerTest < MiniTest::Unit::TestCase
class ERBTrackerTest < Minitest::Test
def make_tracker(name, template)
ActionView::DependencyTracker::ERBTracker.new(name, template)
end

@ -76,6 +76,14 @@ def with_collection_check_boxes(*args, &block)
assert_select 'input[type=radio][value=false].special-radio#user_active_false'
end
test 'collection radio accepts html options as the last element of array' do
collection = [[1, true, {class: 'foo'}], [0, false, {class: 'bar'}]]
with_collection_radio_buttons :user, :active, collection, :second, :first
assert_select 'input[type=radio][value=true].foo#user_active_true'
assert_select 'input[type=radio][value=false].bar#user_active_false'
end
test 'collection radio does not wrap input inside the label' do
with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s
@ -192,6 +200,14 @@ def with_collection_check_boxes(*args, &block)
assert_select 'label[for=user_name_199]', '$1.99'
end
test 'collection check boxes accepts html options as the last element of array' do
collection = [[1, 'Category 1', {class: 'foo'}], [2, 'Category 2', {class: 'bar'}]]
with_collection_check_boxes :user, :active, collection, :first, :second
assert_select 'input[type=checkbox][value=1].foo'
assert_select 'input[type=checkbox][value=2].bar'
end
test 'collection check boxes accepts selected values as :checked option' do
collection = (1..3).map{|i| [i, "Category #{i}"] }
with_collection_check_boxes :user, :category_ids, collection, :first, :last, :checked => [1, 3]

@ -30,8 +30,8 @@ def test_tag_options_accepts_blank_option
end
def test_tag_options_converts_boolean_option
assert_equal '<p disabled="disabled" itemscope="itemscope" multiple="multiple" readonly="readonly" />',
tag("p", :disabled => true, :itemscope => true, :multiple => true, :readonly => true)
assert_dom_equal '<p disabled="disabled" itemscope="itemscope" multiple="multiple" readonly="readonly" allowfullscreen="allowfullscreen" seamless="seamless" typemustmatch="typemustmatch" sortable="sortable" default="default" inert="inert" truespeed="truespeed" />',
tag("p", :disabled => true, :itemscope => true, :multiple => true, :readonly => true, :allowfullscreen => true, :seamless => true, :typemustmatch => true, :sortable => true, :default => true, :inert => true, :truespeed => true)
end
def test_content_tag

@ -348,6 +348,11 @@ def test_link_to_unless
link_to_unless(true, "Showing", url_hash) {
"test"
}
assert_equal %{&lt;b&gt;Showing&lt;/b&gt;}, link_to_unless(true, "<b>Showing</b>", url_hash)
assert_equal %{<a href="/">&lt;b&gt;Showing&lt;/b&gt;</a>}, link_to_unless(false, "<b>Showing</b>", url_hash)
assert_equal %{<b>Showing</b>}, link_to_unless(true, "<b>Showing</b>".html_safe, url_hash)
assert_equal %{<a href="/"><b>Showing</b></a>}, link_to_unless(false, "<b>Showing</b>".html_safe, url_hash)
end
def test_link_to_if

@ -135,7 +135,10 @@ def _define_after_model_callback(klass, callback) #:nodoc:
klass.define_singleton_method("after_#{callback}") do |*args, &block|
options = args.extract_options!
options[:prepend] = true
options[:if] = Array(options[:if]) << "value != false"
conditional = ActiveSupport::Callbacks::Conditionals::Value.new { |v|
v != false
}
options[:if] = Array(options[:if]) << conditional
set_callback(:"#{callback}", :after, *(args << options), &block)
end
end

@ -142,23 +142,23 @@ def changed_attributes
@changed_attributes ||= {}
end
private
# Handle <tt>*_changed?</tt> for +method_missing+.
def attribute_changed?(attr)
changed_attributes.include?(attr)
end
# Handle <tt>*_changed?</tt> for +method_missing+.
def attribute_changed?(attr)
changed_attributes.include?(attr)
end
# Handle <tt>*_was</tt> for +method_missing+.
def attribute_was(attr)
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end
private
# Handle <tt>*_change</tt> for +method_missing+.
def attribute_change(attr)
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
end
# Handle <tt>*_was</tt> for +method_missing+.
def attribute_was(attr)
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end
# Handle <tt>*_will_change!</tt> for +method_missing+.
def attribute_will_change!(attr)
return if attribute_changed?(attr)

@ -109,7 +109,7 @@ def as_json(options = nil)
#
# def attributes=(hash)
# hash.each do |key, value|
# instance_variable_set("@#{key}", value)
# send("#{key}=", value)
# end
# end
#

@ -142,7 +142,9 @@ def validate(*args, &block)
if options.key?(:on)
options = options.dup
options[:if] = Array(options[:if])
options[:if].unshift("validation_context == :#{options[:on]}")
options[:if].unshift lambda { |o|
o.validation_context == options[:on]
}
end
args << options
set_callback(:validate, *args, &block)
@ -226,7 +228,6 @@ def clear_validators!
# Person.validators_on(:name)
# # => [
# # #<ActiveModel::Validations::PresenceValidator:0x007fe604914e60 @attributes=[:name], @options={}>,
# # #<ActiveModel::Validations::InclusionValidator:0x007fe603bb8780 @attributes=[:age], @options={in:0..99}>
# # ]
def validators_on(*attributes)
attributes.flat_map do |attribute|

@ -7,8 +7,12 @@ class RailtieTest < ActiveModel::TestCase
def setup
require 'active_model/railtie'
# Set a fake logger to avoid creating the log directory automatically
fake_logger = mock()
@app ||= Class.new(::Rails::Application) do
config.eager_load = false
config.logger = fake_logger
end
end

@ -1,3 +1,81 @@
* Fixture setup does no longer depend on `ActiveRecord::Base.configurations`.
This is relevant when `ENV["DATABASE_URL"]` is used in place of a `database.yml`.
*Yves Senn*
* Fix mysql2 adapter raises the correct exception when executing a query on a
closed connection.
*Yves Senn*
* Ambiguous reflections are on :through relationships are no longer supported.
For example, you need to change this:
class Author < ActiveRecord::Base
has_many :posts
has_many :taggings, :through => :posts
end
class Post < ActiveRecord::Base
has_one :tagging
has_many :taggings
end
class Tagging < ActiveRecord::Base
end
To this:
class Author < ActiveRecord::Base
has_many :posts
has_many :taggings, :through => :posts, :source => :tagging
end
class Post < ActiveRecord::Base
has_one :tagging
has_many :taggings
end
class Tagging < ActiveRecord::Base
end
* Remove column restrictions for `count`, let the database raise if the SQL is
invalid. The previous behavior was untested and surprising for the user.
Fixes #5554.
Example:
User.select("name, username").count
# Before => SELECT count(*) FROM users
# After => ActiveRecord::StatementInvalid
# you can still use `count(:all)` to perform a query unrelated to the
# selected columns
User.select("name, username").count(:all) # => SELECT count(*) FROM users
*Yves Senn*
* Rails now automatically detects inverse associations. If you do not set the
`:inverse_of` option on the association, then Active Record will guess the
inverse association based on heuristics.
Note that automatic inverse detection only works on `has_many`, `has_one`,
and `belongs_to` associations. Extra options on the associations will
also prevent the association's inverse from being found automatically.
The automatic guessing of the inverse association uses a heuristic based
on the name of the class, so it may not work for all associations,
especially the ones with non-standard names.
You can turn off the automatic detection of inverse associations by setting
the `:inverse_of` option to `false` like so:
class Taggable < ActiveRecord::Base
belongs_to :tag, inverse_of: false
end
*John Wang*
* Fix `add_column` with `array` option when using PostgreSQL. Fixes #10432
*Adam Anderson*
@ -19,7 +97,7 @@
*Yves Senn*
* Fix bug where tiny types are incorectly coerced as booleand when the length is more than 1.
* Fix bug where tiny types are incorrectly coerced as boolean when the length is more than 1.
Fixes #10620.
@ -102,8 +180,8 @@
*Olek Janiszewski*
* fixes bug introduced by #3329. Now, when autosaving associations,
deletions happen before inserts and saves. This prevents a 'duplicate
* fixes bug introduced by #3329. Now, when autosaving associations,
deletions happen before inserts and saves. This prevents a 'duplicate
unique value' database error that would occur if a record being created had
the same value on a unique indexed field as that of a record being destroyed.

@ -586,9 +586,10 @@ def association_instance_set(name, association)
# belongs_to :tag, inverse_of: :taggings
# end
#
# If you do not set the +:inverse_of+ record, the association will do its
# best to match itself up with the correct inverse. Automatic +:inverse_of+
# detection only works on +has_many+, +has_one+, and +belongs_to+ associations.
# If you do not set the <tt>:inverse_of</tt> record, the association will
# do its best to match itself up with the correct inverse. Automatic
# inverse detection only works on <tt>has_many</tt>, <tt>has_one</tt>, and
# <tt>belongs_to</tt> associations.
#
# Extra options on the associations, as defined in the
# <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> constant, will
@ -599,10 +600,10 @@ def association_instance_set(name, association)
# especially the ones with non-standard names.
#
# You can turn off the automatic detection of inverse associations by setting
# the +:automatic_inverse_of+ option to +false+ like so:
# the <tt>:inverse_of</tt> option to <tt>false</tt> like so:
#
# class Taggable < ActiveRecord::Base
# belongs_to :tag, automatic_inverse_of: false
# belongs_to :tag, inverse_of: false
# end
#
# == Nested \Associations

@ -200,13 +200,14 @@ def set_owner_attributes(record)
creation_attributes.each { |key, value| record[key] = value }
end
# Should be true if there is a foreign key present on the owner which
# Returns true if there is a foreign key present on the owner which
# references the target. This is used to determine whether we can load
# the target if the owner is currently a new record (and therefore
# without a key).
# without a key). If the owner is a new record then foreign_key must
# be present in order to load target.
#
# Currently implemented by belongs_to (vanilla and polymorphic) and
# has_one/has_many :through associations which go through a belongs_to
# has_one/has_many :through associations which go through a belongs_to.
def foreign_key_present?
false
end

@ -96,7 +96,7 @@ def add_constraints(scope)
item = eval_scope(klass, scope_chain_item)
if scope_chain_item == self.reflection.scope
scope.merge! item.except(:where, :includes)
scope.merge! item.except(:where, :includes, :bind)
end
scope.includes! item.includes_values

@ -111,13 +111,8 @@ def configure_dependency
)
end
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{macro}_dependent_for_#{name}
association(:#{name}).handle_dependency
end
CODE
model.before_destroy "#{macro}_dependent_for_#{name}"
n = name
model.before_destroy lambda { |o| o.association(n).handle_dependency }
end
def valid_dependent_options

@ -19,82 +19,116 @@ def build
reflection
end
def add_counter_cache_callbacks(reflection)
cache_column = reflection.counter_cache_column
foreign_key = reflection.foreign_key
def valid_dependent_options
[:destroy, :delete]
end
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
def belongs_to_counter_cache_after_create_for_#{name}
if record = #{name}
record.class.increment_counter(:#{cache_column}, record.id)
private
def add_counter_cache_methods(mixin)
return if mixin.method_defined? :belongs_to_counter_cache_after_create
mixin.class_eval do
def belongs_to_counter_cache_after_create(association, reflection)
if record = send(association.name)
cache_column = reflection.counter_cache_column
record.class.increment_counter(cache_column, record.id)
@_after_create_counter_called = true
end
end
def belongs_to_counter_cache_before_destroy_for_#{name}
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == #{foreign_key.to_sym.inspect}
record = #{name}
def belongs_to_counter_cache_before_destroy(association, reflection)
foreign_key = reflection.foreign_key.to_sym
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
record = send association.name
if record && !self.destroyed?
record.class.decrement_counter(:#{cache_column}, record.id)
cache_column = reflection.counter_cache_column
record.class.decrement_counter(cache_column, record.id)
end
end
end
def belongs_to_counter_cache_after_update_for_#{name}
def belongs_to_counter_cache_after_update(association, reflection)
foreign_key = reflection.foreign_key
cache_column = reflection.counter_cache_column
if (@_after_create_counter_called ||= false)
@_after_create_counter_called = false
elsif self.#{foreign_key}_changed? && !new_record? && defined?(#{name.to_s.camelize})
model = #{name.to_s.camelize}
foreign_key_was = self.#{foreign_key}_was
foreign_key = self.#{foreign_key}
elsif attribute_changed?(foreign_key) && !new_record? && association.constructable?
model = reflection.klass
foreign_key_was = attribute_was foreign_key
foreign_key = attribute foreign_key
if foreign_key && model.respond_to?(:increment_counter)
model.increment_counter(:#{cache_column}, foreign_key)
model.increment_counter(cache_column, foreign_key)
end
if foreign_key_was && model.respond_to?(:decrement_counter)
model.decrement_counter(:#{cache_column}, foreign_key_was)
model.decrement_counter(cache_column, foreign_key_was)
end
end
end
CODE
end
end
model.after_create "belongs_to_counter_cache_after_create_for_#{name}"
model.before_destroy "belongs_to_counter_cache_before_destroy_for_#{name}"
model.after_update "belongs_to_counter_cache_after_update_for_#{name}"
def add_counter_cache_callbacks(reflection)
cache_column = reflection.counter_cache_column
add_counter_cache_methods mixin
association = self
model.after_create lambda { |record|
record.belongs_to_counter_cache_after_create(association, reflection)
}
model.before_destroy lambda { |record|
record.belongs_to_counter_cache_before_destroy(association, reflection)
}
model.after_update lambda { |record|
record.belongs_to_counter_cache_after_update(association, reflection)
}
klass = reflection.class_name.safe_constantize
klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
end
def add_touch_callbacks(reflection)
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
def belongs_to_touch_after_save_or_destroy_for_#{name}
foreign_key_field = #{reflection.foreign_key.inspect}
old_foreign_id = attribute_was(foreign_key_field)
def self.touch_record(o, foreign_key, name, touch) # :nodoc:
old_foreign_id = o.attribute_was(foreign_key)
if old_foreign_id
klass = association(#{name.inspect}).klass
old_record = klass.find_by(klass.primary_key => old_foreign_id)
if old_foreign_id
klass = o.association(name).klass
old_record = klass.find_by(klass.primary_key => old_foreign_id)
if old_record
old_record.touch #{options[:touch].inspect if options[:touch] != true}
end
end
record = #{name}
unless record.nil? || record.new_record?
record.touch #{options[:touch].inspect if options[:touch] != true}
if old_record
if touch != true
old_record.touch touch
else
old_record.touch
end
end
CODE
end
model.after_save "belongs_to_touch_after_save_or_destroy_for_#{name}"
model.after_touch "belongs_to_touch_after_save_or_destroy_for_#{name}"
model.after_destroy "belongs_to_touch_after_save_or_destroy_for_#{name}"
record = o.send name
unless record.nil? || record.new_record?
if touch != true
record.touch touch
else
record.touch
end
end
end
def valid_dependent_options
[:destroy, :delete]
def add_touch_callbacks(reflection)
foreign_key = reflection.foreign_key
n = name
touch = options[:touch]
callback = lambda { |record|
BelongsTo.touch_record(record, foreign_key, n, touch)
}
model.after_save callback
model.after_touch callback
model.after_destroy callback
end
end
end

@ -5,7 +5,7 @@ def macro
end
def valid_options
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :automatic_inverse_of, :counter_cache]
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache]
end
def valid_dependent_options

@ -3,7 +3,7 @@
module ActiveRecord::Associations::Builder
class SingularAssociation < Association #:nodoc:
def valid_options
super + [:remote, :dependent, :counter_cache, :primary_key, :inverse_of, :automatic_inverse_of]
super + [:remote, :dependent, :counter_cache, :primary_key, :inverse_of]
end
def constructable?

@ -25,6 +25,9 @@ def primary_key?
end
end
class ChangeColumnDefinition < Struct.new(:column, :type, :options) #:nodoc:
end
# Represents the schema of an SQL table in an abstract way. This class
# provides methods for manipulating the schema representation.
#

@ -694,17 +694,6 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
end
end
def add_column_options!(sql, options) #:nodoc:
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
# must explicitly check for :null to allow change_column to work on migrations
if options[:null] == false
sql << " NOT NULL"
end
if options[:auto_increment] == true
sql << " AUTO_INCREMENT"
end
end
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
#
# distinct("posts.id", ["posts.created_at desc"])

@ -116,6 +116,12 @@ def accept(o)
send m, o
end
def visit_AddColumn(o)
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
sql = "ADD #{quote_column_name(o.name)} #{sql_type}"
add_column_options!(sql, column_options(o))
end
private
def visit_AlterTable(o)
@ -123,12 +129,6 @@ def visit_AlterTable(o)
sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
end
def visit_AddColumn(o)
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
sql = "ADD #{quote_column_name(o.name)} #{sql_type}"
add_column_options!(sql, column_options(o))
end
def visit_ColumnDefinition(o)
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
column_sql = "#{quote_column_name(o.name)} #{sql_type}"
@ -149,6 +149,8 @@ def column_options(o)
column_options[:null] = o.null unless o.null.nil?
column_options[:default] = o.default unless o.default.nil?
column_options[:column] = o
column_options[:first] = o.first
column_options[:after] = o.after
column_options
end
@ -164,9 +166,20 @@ def type_to_sql(type, limit, precision, scale)
@conn.type_to_sql type.to_sym, limit, precision, scale
end
def add_column_options!(column_sql, column_options)
@conn.add_column_options! column_sql, column_options
column_sql
def add_column_options!(sql, options)
sql << " DEFAULT #{@conn.quote(options[:default], options[:column])}" if options_include_default?(options)
# must explicitly check for :null to allow change_column to work on migrations
if options[:null] == false
sql << " NOT NULL"
end
if options[:auto_increment] == true
sql << " AUTO_INCREMENT"
end
sql
end
def options_include_default?(options)
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
end
end

@ -4,17 +4,26 @@ module ActiveRecord
module ConnectionAdapters
class AbstractMysqlAdapter < AbstractAdapter
class SchemaCreation < AbstractAdapter::SchemaCreation
private
def visit_AddColumn(o)
add_column_position!(super, o)
add_column_position!(super, column_options(o))
end
def add_column_position!(sql, column)
if column.first
private
def visit_ChangeColumnDefinition(o)
column = o.column
options = o.options
sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
add_column_options!(change_column_sql, options)
add_column_position!(change_column_sql, options)
end
def add_column_position!(sql, options)
if options[:first]
sql << " FIRST"
elsif column.after
sql << " AFTER #{quote_column_name(column.after)}"
elsif options[:after]
sql << " AFTER #{quote_column_name(options[:after])}"
end
sql
end
@ -661,10 +670,9 @@ def translate_exception(exception, message)
end
def add_column_sql(table_name, column_name, type, options = {})
add_column_sql = "ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
add_column_options!(add_column_sql, options)
add_column_position!(add_column_sql, options)
add_column_sql
td = create_table_definition table_name, options[:temporary], options[:options]
cd = td.new_column_definition(column_name, type, options)
schema_creation.visit_AddColumn cd
end
def change_column_sql(table_name, column_name, type, options = {})
@ -678,14 +686,12 @@ def change_column_sql(table_name, column_name, type, options = {})
options[:null] = column.null
end
change_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
add_column_options!(change_column_sql, options)
add_column_position!(change_column_sql, options)
change_column_sql
options[:name] = column.name
schema_creation.accept ChangeColumnDefinition.new column, type, options
end
def rename_column_sql(table_name, column_name, new_column_name)
options = {}
options = { name: new_column_name }
if column = columns(table_name).find { |c| c.name == column_name.to_s }
options[:default] = column.default
@ -696,9 +702,7 @@ def rename_column_sql(table_name, column_name, new_column_name)
end
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
add_column_options!(rename_column_sql, options)
rename_column_sql
schema_creation.accept ChangeColumnDefinition.new column, current_type, options
end
def remove_column_sql(table_name, column_name, type = nil, options = {})

@ -213,9 +213,11 @@ def select_rows(sql, name = nil)
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
# made since we established the connection
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
if @connection
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
# made since we established the connection
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
end
super
end

@ -50,7 +50,7 @@ def reset_counters(id, *counters)
# ==== Parameters
#
# * +id+ - The id of the object you wish to update a counter on or an Array of ids.
# * +counters+ - An Array of Hashes containing the names of the fields
# * +counters+ - A Hash containing the names of the fields
# to update as keys and the amount to update the field by as values.
#
# ==== Examples

@ -69,10 +69,6 @@ def initialize(message, original_exception = nil)
end
end
# Raised when SQL statement is invalid and the application gets a blank result.
class ThrowResult < ActiveRecordError
end
# Defunct wrapper class kept for compatibility.
# +StatementInvalid+ wraps the original exception now.
class WrappedDatabaseException < StatementInvalid

@ -841,8 +841,6 @@ def run_in_transaction?
end
def setup_fixtures
return if ActiveRecord::Base.configurations.blank?
if pre_loaded_fixtures && !use_transactional_fixtures
raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
end
@ -875,8 +873,6 @@ def setup_fixtures
end
def teardown_fixtures
return if ActiveRecord::Base.configurations.blank?
# Rollback changes if a transaction is active.
if run_in_transaction?
@fixture_connections.each do |connection|

@ -5,7 +5,7 @@ module Inheritance
extend ActiveSupport::Concern
included do
# Determine whether to store the full constant name including namespace when using STI
# Determines whether to store the full constant name including namespace when using STI.
class_attribute :store_full_sti_class, instance_writer: false
self.store_full_sti_class = true
end
@ -13,7 +13,7 @@ module Inheritance
module ClassMethods
# Determines if one of the attributes passed in is the inheritance column,
# and if the inheritance column is attr accessible, it initializes an
# instance of the given subclass instead of the base class
# instance of the given subclass instead of the base class.
def new(*args, &block)
if abstract_class? || self == Base
raise NotImplementedError, "#{self} is an abstract class and can not be instantiated."
@ -27,7 +27,8 @@ def new(*args, &block)
super
end
# True if this isn't a concrete subclass needing a STI type condition.
# Returns +true+ if this does not need STI type condition. Returns
# +false+ if STI type condition needs to be applied.
def descends_from_active_record?
if self == Base
false

@ -230,6 +230,10 @@ class TooManyRecords < ActiveRecordError
# validates_presence_of :member
# end
#
# Note that if you do not specify the <tt>inverse_of</tt> option, then
# Active Record will try to automatically guess the inverse association
# based on heuristics.
#
# For one-to-one nested associations, if you build the new (in-memory)
# child object yourself before assignment, then this module will not
# overwrite it, e.g.:
@ -302,14 +306,9 @@ def accepts_nested_attributes_for(*attr_names)
attr_names.each do |association_name|
if reflection = reflect_on_association(association_name)
reflection.options[:autosave] = true
reflection.autosave = true
add_autosave_association_callbacks(reflection)
# Clear cached values of any inverse associations found in the
# reflection and prevent the reflection from finding inverses
# automatically in the future.
reflection.remove_automatic_inverse_of!
nested_attributes_options = self.nested_attributes_options.dup
nested_attributes_options[association_name.to_sym] = options
self.nested_attributes_options = nested_attributes_options

@ -39,7 +39,7 @@ def many?
end
def to_sql
@to_sql ||= ""
""
end
def where_values_hash
@ -55,7 +55,11 @@ def sum(*)
end
def calculate(_operation, _column_name, _options = {})
nil
if _operation == :count
0
else
nil
end
end
def exists?(_id = false)

@ -324,7 +324,7 @@ db_namespace = namespace :db do
ActiveRecord::Schema.verbose = false
db_namespace["schema:load"].invoke
ensure
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Rails.env])
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
end
end

@ -5,7 +5,9 @@ module Reflection # :nodoc:
included do
class_attribute :reflections
class_attribute :aggregate_reflections
self.reflections = {}
self.aggregate_reflections = {}
end
# \Reflection enables to interrogate Active Record classes and objects
@ -27,13 +29,18 @@ def create_reflection(macro, name, scope, options, active_record)
reflection = klass.new(macro, name, scope, options, active_record)
self.reflections = self.reflections.merge(name => reflection)
if klass == AggregateReflection
self.aggregate_reflections = self.aggregate_reflections.merge(name => reflection)
else
self.reflections = self.reflections.merge(name => reflection)
end
reflection
end
# Returns an array of AggregateReflection objects for all the aggregations in the class.
def reflect_on_all_aggregations
reflections.values.grep(AggregateReflection)
aggregate_reflections.values
end
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
@ -41,8 +48,7 @@ def reflect_on_all_aggregations
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
#
def reflect_on_aggregation(aggregation)
reflection = reflections[aggregation]
reflection if reflection.is_a?(AggregateReflection)
aggregate_reflections[aggregation]
end
# Returns an array of AssociationReflection objects for all the
@ -56,7 +62,7 @@ def reflect_on_aggregation(aggregation)
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
#
def reflect_on_all_associations(macro = nil)
association_reflections = reflections.values.grep(AssociationReflection)
association_reflections = reflections.values
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
end
@ -66,8 +72,7 @@ def reflect_on_all_associations(macro = nil)
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
#
def reflect_on_association(association)
reflection = reflections[association]
reflection if reflection.is_a?(AssociationReflection)
reflections[association]
end
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
@ -118,6 +123,11 @@ def initialize(macro, name, scope, options, active_record)
name.to_s.pluralize : name.to_s
end
def autosave=(autosave)
@automatic_inverse_of = false
@options[:autosave] = autosave
end
# Returns the class for the macro.
#
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
@ -179,10 +189,14 @@ def klass
@klass ||= active_record.send(:compute_type, class_name)
end
attr_reader :type, :foreign_type
def initialize(*args)
super
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
@automatic_inverse_of = nil
@type = options[:as] && "#{options[:as]}_type"
@foreign_type = options[:foreign_type] || "#{name}_type"
end
# Returns a new, unsaved instance of the associated class. +attributes+ will
@ -192,11 +206,11 @@ def build_association(attributes, &block)
end
def table_name
@table_name ||= klass.table_name
klass.table_name
end
def quoted_table_name
@quoted_table_name ||= klass.quoted_table_name
klass.quoted_table_name
end
def join_table
@ -207,16 +221,8 @@ def foreign_key
@foreign_key ||= options[:foreign_key] || derive_foreign_key
end
def foreign_type
@foreign_type ||= options[:foreign_type] || "#{name}_type"
end
def type
@type ||= options[:as] && "#{options[:as]}_type"
end
def primary_key_column
@primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
klass.columns_hash[klass.primary_key]
end
def association_foreign_key
@ -240,14 +246,6 @@ def counter_cache_column
end
end
def columns(tbl_name)
@columns ||= klass.connection.columns(tbl_name)
end
def reset_column_information
@columns = nil
end
def check_validity!
check_validity_of_inverse!
@ -291,30 +289,13 @@ def scope_chain
alias :source_macro :macro
def has_inverse?
@options[:inverse_of] || find_inverse_of_automatically
inverse_name
end
def inverse_of
@inverse_of ||= if options[:inverse_of]
klass.reflect_on_association(options[:inverse_of])
else
find_inverse_of_automatically
end
end
return unless inverse_name
# Clears the cached value of +@inverse_of+ on this object. This will
# not remove the :inverse_of option however, so future calls on the
# +inverse_of+ will have to recompute the inverse.
def clear_inverse_of_cache!
@inverse_of = nil
end
# Removes the cached inverse association that was found automatically
# and prevents this object from finding the inverse association
# automatically in the future.
def remove_automatic_inverse_of!
@automatic_inverse_of = nil
options[:automatic_inverse_of] = false
@inverse_of ||= klass.reflect_on_association inverse_name
end
def polymorphic_inverse_of(associated_class)
@ -389,26 +370,21 @@ def polymorphic?
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
private
# Attempts to find the inverse association automatically.
# If it cannot find a suitable inverse association, it returns
# Attempts to find the inverse association name automatically.
# If it cannot find a suitable inverse association name, it returns
# nil.
def find_inverse_of_automatically
if @automatic_inverse_of == false
nil
elsif @automatic_inverse_of.nil?
set_automatic_inverse_of
else
klass.reflect_on_association(@automatic_inverse_of)
def inverse_name
options.fetch(:inverse_of) do
if @automatic_inverse_of == false
nil
else
@automatic_inverse_of ||= automatic_inverse_of
end
end
end
# Sets the +@automatic_inverse_of+ instance variable, and returns
# either nil or the inverse association that it finds.
#
# This method caches the inverse association that is found so that
# future calls to +find_inverse_of_automatically+ have much less
# overhead.
def set_automatic_inverse_of
# returns either nil or the inverse association name that it finds.
def automatic_inverse_of
if can_find_inverse_of_automatically?(self)
inverse_name = active_record.name.downcase.to_sym
@ -421,16 +397,11 @@ def set_automatic_inverse_of
end
if valid_inverse_reflection?(reflection)
@automatic_inverse_of = inverse_name
reflection
else
@automatic_inverse_of = false
nil
return inverse_name
end
else
@automatic_inverse_of = false
nil
end
false
end
# Checks if the inverse reflection that is returned from the
@ -442,22 +413,23 @@ def set_automatic_inverse_of
# from calling +klass+, +reflection+ will already be set to false.
def valid_inverse_reflection?(reflection)
reflection &&
klass.name == reflection.active_record.try(:name) &&
klass.name == reflection.active_record.name &&
klass.primary_key == reflection.active_record_primary_key &&
can_find_inverse_of_automatically?(reflection)
end
# Checks to see if the reflection doesn't have any options that prevent
# us from being able to guess the inverse automatically. First, the
# +automatic_inverse_of+ option cannot be set to false. Second, we must
# have +has_many+, +has_one+, +belongs_to+ associations. Third, we must
# not have options such as +:polymorphic+ or +:foreign_key+ which prevent us
# from correctly guessing the inverse association.
# <tt>inverse_of</tt> option cannot be set to false. Second, we must
# have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
# Third, we must not have options such as <tt>:polymorphic</tt> or
# <tt>:foreign_key</tt> which prevent us from correctly guessing the
# inverse association.
#
# Anything with a scope can additionally ruin our attempt at finding an
# inverse, so we exclude reflections with scopes.
def can_find_inverse_of_automatically?(reflection)
reflection.options[:automatic_inverse_of] != false &&
reflection.options[:inverse_of] != false &&
VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
!INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] } &&
!reflection.scope
@ -494,6 +466,11 @@ class ThroughReflection < AssociationReflection #:nodoc:
delegate :foreign_key, :foreign_type, :association_foreign_key,
:active_record_primary_key, :type, :to => :source_reflection
def initialize(macro, name, scope, options, active_record)
super
@source_reflection_name = options[:source]
end
# Returns the source of the through reflection. It checks both a singularized
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
#
@ -512,7 +489,7 @@ class ThroughReflection < AssociationReflection #:nodoc:
# # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags">
#
def source_reflection
@source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
through_reflection.klass.reflect_on_association(source_reflection_name)
end
# Returns the AssociationReflection object specified in the <tt>:through</tt> option
@ -528,7 +505,7 @@ def source_reflection
# # => <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @active_record=Post, @plural_name="taggings">
#
def through_reflection
@through_reflection ||= active_record.reflect_on_association(options[:through])
active_record.reflect_on_association(options[:through])
end
# Returns an array of reflections which are involved in this association. Each item in the
@ -630,7 +607,32 @@ def association_primary_key(klass = nil)
# # => [:tag, :tags]
#
def source_reflection_names
@source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
(options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }.uniq
end
def source_reflection_name # :nodoc:
return @source_reflection_name if @source_reflection_name
names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
names = names.find_all { |n|
through_reflection.klass.reflect_on_association(n)
}
if names.length > 1
example_options = options.dup
example_options[:source] = source_reflection_names.first
ActiveSupport::Deprecation.warn <<-eowarn
Ambiguous source reflection for through association. Please specify a :source
directive on your declaration like:
class #{active_record.name} < ActiveRecord::Base
#{macro} :#{name}, #{example_options}
end
eowarn
end
@source_reflection_name = names.first
end
def source_options

@ -247,7 +247,7 @@ def size
def empty?
return @records.empty? if loaded?
c = count
c = count(:all)
c.respond_to?(:zero?) ? c.zero? : c.empty?
end

@ -11,7 +11,7 @@ module Batches
# The #find_each method uses #find_in_batches with a batch size of 1000 (or as
# specified by the +:batch_size+ option).
#
# Person.all.find_each do |person|
# Person.find_each do |person|
# person.do_awesome_stuff
# end
#
@ -19,8 +19,26 @@ module Batches
# person.party_all_night!
# end
#
# You can also pass the +:start+ option to specify
# an offset to control the starting point.
# ==== Options
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
# * <tt>:start</tt> - Specifies the starting point for the batch processing.
# This is especially useful if you want multiple workers dealing with
# the same processing queue. You can make worker 1 handle all the records
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
# (by setting the +:start+ option on that worker).
#
# # Let's process for a batch of 2000 records, skiping the first 2000 rows
# Person.find_each(start: 2000, batch_size: 2000) do |person|
# person.party_all_night!
# end
#
# NOTE: It's not possible to set the order. That is automatically set to
# ascending on the primary key ("id ASC") to make the batch ordering
# work. This also means that this method only works with integer-based
# primary keys.
#
# NOTE: You can't set the limit either, that's used to control
# the batch sizes.
def find_each(options = {})
find_in_batches(options) do |records|
records.each { |record| yield record }
@ -28,31 +46,33 @@ def find_each(options = {})
end
# Yields each batch of records that was found by the find +options+ as
# an array. The size of each batch is set by the +:batch_size+
# option; the default is 1000.
#
# You can control the starting point for the batch processing by
# supplying the +:start+ option. This is especially useful if you
# want multiple workers dealing with the same processing queue. You can
# make worker 1 handle all the records between id 0 and 10,000 and
# worker 2 handle from 10,000 and beyond (by setting the +:start+
# option on that worker).
#
# It's not possible to set the order. That is automatically set to
# ascending on the primary key ("id ASC") to make the batch ordering
# work. This also means that this method only works with integer-based
# primary keys. You can't set the limit either, that's used to control
# the batch sizes.
# an array.
#
# Person.where("age > 21").find_in_batches do |group|
# sleep(50) # Make sure it doesn't get too crowded in there!
# group.each { |person| person.party_all_night! }
# end
#
# ==== Options
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
# * <tt>:start</tt> - Specifies the starting point for the batch processing.
# This is especially useful if you want multiple workers dealing with
# the same processing queue. You can make worker 1 handle all the records
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
# (by setting the +:start+ option on that worker).
#
# # Let's process the next 2000 records
# Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group|
# Person.find_in_batches(start: 2000, batch_size: 2000) do |group|
# group.each { |person| person.party_all_night! }
# end
#
# NOTE: It's not possible to set the order. That is automatically set to
# ascending on the primary key ("id ASC") to make the batch ordering
# work. This also means that this method only works with integer-based
# primary keys.
#
# NOTE: You can't set the limit either, that's used to control
# the batch sizes.
def find_in_batches(options = {})
options.assert_valid_keys(:start, :batch_size)

@ -106,8 +106,6 @@ def calculate(operation, column_name, options = {})
else
relation.calculate(operation, column_name, options)
end
rescue ThrowResult
0
end
# Use <tt>pluck</tt> as a shortcut to select one or more attributes without
@ -209,15 +207,18 @@ def perform_calculation(operation, column_name, options = {})
end
if operation == "count"
column_name ||= (select_for_count || :all)
if select_values.present?
column_name ||= select_values.join(", ")
else
column_name ||= :all
end
unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
distinct = true
end
column_name = primary_key if column_name == :all && distinct
distinct = nil if column_name =~ /\s*DISTINCT\s+/i
distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
end
if group_values.any?
@ -378,13 +379,6 @@ def type_cast_using_column(value, column)
column ? column.type_cast(value) : value
end
def select_for_count
if select_values.present?
select = select_values.join(", ")
select if select !~ /[,*]/
end
end
def build_count_subquery(relation, column_name, distinct)
column_alias = Arel.sql('count_column')
subquery_alias = Arel.sql('subquery_for_count')

@ -11,9 +11,11 @@ module FinderMethods
# Person.find([1]) # returns an array for the object with ID = 1
# Person.where("administrator = 1").order("created_on DESC").find(1)
#
# Note that returned records may not be in the same order as the ids you
# provide since database rows are unordered. Give an explicit <tt>order</tt>
# to ensure the results are sorted.
# <tt>ActiveRecord::RecordNotFound</tt> will be raised if one or more ids are not found.
#
# NOTE: The returned records may not be in the same order as the ids you
# provide since database rows are unordered. You'd need to provide an explicit <tt>order</tt>
# option if you want the results are sorted.
#
# ==== Find with lock
#
@ -28,6 +30,34 @@ module FinderMethods
# person.visits += 1
# person.save!
# end
#
# ==== Variations of +find+
#
# Person.where(name: 'Spartacus', rating: 4)
# # returns a chainable list (which can be empty).
#
# Person.find_by(name: 'Spartacus', rating: 4)
# # returns the first item or nil.
#
# Person.where(name: 'Spartacus', rating: 4).first_or_initialize
# # returns the first item or returns a new instance (requires you call .save to persist against the database).
#
# Person.where(name: 'Spartacus', rating: 4).first_or_create
# # returns the first item or creates it and returns it, available since Rails 3.2.1.
#
# ==== Alternatives for +find+
#
# Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none)
# # returns a boolean indicating if any record with the given conditions exist.
#
# Person.where(name: 'Spartacus', rating: 4).select("field1, field2, field3")
# # returns a chainable list of instances with only the mentioned fields.
#
# Person.where(name: 'Spartacus', rating: 4).ids
# # returns an Array of ids, available since Rails 3.2.1.
#
# Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2)
# # returns an Array of the required fields, available since Rails 3.1.
def find(*args)
if block_given?
to_a.find { |*block_args| yield(*block_args) }
@ -79,13 +109,22 @@ def take!
# Person.where(["user_name = :u", { u: user_name }]).first
# Person.order("created_on DESC").offset(5).first
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
#
# ==== Rails 3
#
# Person.first # SELECT "people".* FROM "people" LIMIT 1
#
# NOTE: Rails 3 may not order this query by the primary key and the order
# will depend on the database implementation. In order to ensure that behavior,
# use <tt>User.order(:id).first</tt> instead.
#
# ==== Rails 4
#
# Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1
#
def first(limit = nil)
if limit
if order_values.empty? && primary_key
order(arel_table[primary_key].asc).limit(limit).to_a
else
limit(limit).to_a
end
find_first_with_limit(order_values, limit)
else
find_first
end
@ -160,8 +199,9 @@ def exists?(conditions = :none)
conditions = conditions.id if Base === conditions
return false if !conditions
join_dependency = construct_join_dependency
relation = construct_relation_for_association_find(join_dependency)
relation = construct_relation_for_association_find(construct_join_dependency)
return false if ActiveRecord::NullRelation === relation
relation = relation.except(:select, :order).select("1 AS one").limit(1)
case conditions
@ -171,9 +211,8 @@ def exists?(conditions = :none)
relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
end
connection.select_value(relation, "#{name} Exists", relation.bind_values)
rescue ThrowResult
false
relation = relation.with_default_scope
connection.select_value(relation.arel, "#{name} Exists", relation.bind_values)
end
# This method is called whenever no records are found with either a single
@ -203,10 +242,12 @@ def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc:
def find_with_associations
join_dependency = construct_join_dependency
relation = construct_relation_for_association_find(join_dependency)
rows = connection.select_all(relation, 'SQL', relation.bind_values.dup)
join_dependency.instantiate(rows)
rescue ThrowResult
[]
if ActiveRecord::NullRelation === relation
[]
else
rows = connection.select_all(relation, 'SQL', relation.bind_values.dup)
join_dependency.instantiate(rows)
end
end
def construct_join_dependency(joins = [])
@ -230,21 +271,22 @@ def apply_join_dependency(relation, join_dependency)
if using_limitable_reflections?(join_dependency.reflections)
relation
else
relation.where!(construct_limited_ids_condition(relation)) if relation.limit_value
if relation.limit_value
limited_ids = limited_ids_for(relation)
limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
end
relation.except(:limit, :offset)
end
end
def construct_limited_ids_condition(relation)
def limited_ids_for(relation)
values = @klass.connection.columns_for_distinct(
"#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
relation = relation.except(:select).select(values).distinct!
id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
ids_array = id_rows.map {|row| row[primary_key]}
ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
id_rows.map {|row| row[primary_key]}
end
def find_with_ids(*ids)
@ -312,12 +354,15 @@ def find_first
if loaded?
@records.first
else
@first ||=
if with_default_scope.order_values.empty? && primary_key
order(arel_table[primary_key].asc).limit(1).to_a.first
else
limit(1).to_a.first
end
@first ||= find_first_with_limit(with_default_scope.order_values, 1).first
end
end
def find_first_with_limit(order_values, limit)
if order_values.empty? && primary_key
order(arel_table[primary_key].asc).limit(limit).to_a
else
limit(limit).to_a
end
end

@ -10,9 +10,9 @@ module ActiveRecord
#
# config.active_record.record_timestamps = false
#
# Timestamps are in the local timezone by default but you can use UTC by setting:
# Timestamps are in UTC by default but you can use the local timezone by setting:
#
# config.active_record.default_timezone = :utc
# config.active_record.default_timezone = :local
#
# == Time Zone aware attributes
#

@ -250,7 +250,8 @@ def test_loading_with_no_associations
assert_nil Post.all.merge!(:includes => :author).find(posts(:authorless).id).author
end
def test_nested_loading_with_no_associations
# Regression test for 21c75e5
def test_nested_loading_does_not_raise_exception_when_association_does_not_exist
assert_nothing_raised do
Post.all.merge!(:includes => {:author => :author_addresss}).find(posts(:authorless).id)
end

@ -6,6 +6,7 @@
require 'models/organization'
require 'models/possession'
require 'models/topic'
require 'models/reply'
require 'models/minivan'
require 'models/speedometer'
require 'models/ship_part'
@ -166,6 +167,15 @@ def test_no_limit_no_offset
assert_no_match(/OFFSET/, queries.first)
end
def test_count_on_invalid_columns_raises
e = assert_raises(ActiveRecord::StatementInvalid) {
Account.select("credit_limit, firm_name").count
}
assert_match "accounts", e.message
assert_match "credit_limit, firm_name", e.message
end
def test_should_group_by_summed_field_having_condition
c = Account.group(:firm_id).having('sum(credit_limit) > 50').sum(:credit_limit)
assert_nil c[1]
@ -472,6 +482,11 @@ def test_pluck
assert_equal [1,2,3,4], Topic.order(:id).pluck(:id)
end
def test_pluck_without_column_names
assert_equal [[1, "Firm", 1, nil, "37signals", nil, 1, nil, ""]],
Company.order(:id).limit(1).pluck
end
def test_pluck_type_cast
topic = topics(:first)
relation = Topic.where(:id => topic.id)
@ -533,6 +548,11 @@ def test_plucks_with_ids
assert_equal Company.all.map(&:id).sort, Company.ids.sort
end
def test_pluck_with_includes_limit_and_empty_result
assert_equal [], Topic.includes(:replies).limit(0).pluck(:id)
assert_equal [], Topic.includes(:replies).limit(1).where('0 = 1').pluck(:id)
end
def test_pluck_not_auto_table_name_prefix_if_column_included
Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
ids = Company.includes(:contracts).pluck(:developer_id)

@ -329,7 +329,7 @@ def test_pool_sets_connection_visitor
end
# make sure exceptions are thrown when establish_connection
# is called with a anonymous class
# is called with an anonymous class
def test_anonymous_class_exception
anonymous = Class.new(ActiveRecord::Base)
handler = ActiveRecord::Base.connection_handler

@ -0,0 +1,26 @@
require "cases/helper"
class TestRecord < ActiveRecord::Base
end
class TestDisconnectedAdapter < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
@connection = ActiveRecord::Base.connection
end
def teardown
spec = ActiveRecord::Base.connection_config
ActiveRecord::Base.establish_connection(spec)
@connection = nil
end
test "can't execute statements while disconnected" do
@connection.execute "SELECT count(*) from products"
@connection.disconnect!
assert_raises(ActiveRecord::StatementInvalid) do
@connection.execute "SELECT count(*) from products"
end
end
end

@ -245,6 +245,22 @@ def test_binary_in_fixtures
def test_serialized_fixtures
assert_equal ["Green", "Red", "Orange"], traffic_lights(:uk).state
end
def test_fixtures_are_set_up_with_database_env_variable
ENV.stubs(:[]).with("DATABASE_URL").returns("sqlite3:///:memory:")
ActiveRecord::Base.stubs(:configurations).returns({})
test_case = Class.new(ActiveRecord::TestCase) do
fixtures :accounts
def test_fixtures
assert accounts(:signals37)
end
end
result = test_case.new(:test_fixtures).run
assert result.passed?, "Expected #{result.name} to pass:\n#{result}"
end
end
if Account.connection.respond_to?(:reset_pk_sequence!)

@ -242,7 +242,7 @@ def test_polymorphic_destroy_with_dependencies_and_lock_version
car = Car.create!
assert_difference 'car.wheels.count' do
car.wheels << Wheel.create!
car.wheels << Wheel.create!
end
assert_difference 'car.wheels.count', -1 do
car.destroy

@ -797,26 +797,6 @@ def test_validate_presence_of_parent_works_with_inverse_of
end
end
def test_validate_presence_of_parent_fails_without_inverse_of
Man.accepts_nested_attributes_for(:interests)
Man.reflect_on_association(:interests).options.delete(:inverse_of)
Man.reflect_on_association(:interests).clear_inverse_of_cache!
Interest.reflect_on_association(:man).options.delete(:inverse_of)
Interest.reflect_on_association(:man).clear_inverse_of_cache!
repair_validations(Interest) do
Interest.validates_presence_of(:man)
assert_no_difference ['Man.count', 'Interest.count'] do
man = Man.create(:name => 'John',
:interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}])
assert !man.errors[:"interests.man"].empty?
end
end
ensure
Man.reflect_on_association(:interests).options[:inverse_of] = :man
Interest.reflect_on_association(:man).options[:inverse_of] = :interests
end
def test_can_use_symbols_as_object_identifier
@pirate.attributes = { :parrots_attributes => { :foo => { :name => 'Lovely Day' }, :bar => { :name => 'Blown Away' } } }
assert_nothing_raised(NoMethodError) { @pirate.save! }

@ -278,8 +278,9 @@ def test_null_relation_content_size_methods
def test_null_relation_calculations_methods
assert_no_queries do
assert_equal 0, Developer.none.count
assert_equal nil, Developer.none.calculate(:average, 'salary')
assert_equal 0, Developer.none.count
assert_equal 0, Developer.none.calculate(:count, nil, {})
assert_equal nil, Developer.none.calculate(:average, 'salary')
end
end

@ -85,7 +85,7 @@ class Author < ActiveRecord::Base
has_many :author_favorites
has_many :favorite_authors, -> { order('name') }, :through => :author_favorites
has_many :taggings, :through => :posts
has_many :taggings, :through => :posts, :source => :taggings
has_many :taggings_2, :through => :posts, :source => :tagging
has_many :tags, :through => :posts
has_many :post_categories, :through => :posts, :source => :categories

@ -1,6 +1,6 @@
class Club < ActiveRecord::Base
has_one :membership
has_many :memberships, :automatic_inverse_of => false
has_many :memberships, :inverse_of => false
has_many :members, :through => :memberships
has_many :current_memberships
has_one :sponsor

@ -141,7 +141,7 @@ class Client < Company
belongs_to :firm_with_primary_key_symbols, :class_name => "Firm", :primary_key => :name, :foreign_key => :firm_name
belongs_to :readonly_firm, -> { readonly }, :class_name => "Firm", :foreign_key => "firm_id"
belongs_to :bob_firm, -> { where :name => "Bob" }, :class_name => "Firm", :foreign_key => "client_of"
has_many :accounts, :through => :firm
has_many :accounts, :through => :firm, :source => :accounts
belongs_to :account
class RaisedOnSave < RuntimeError; end

@ -1,5 +1,5 @@
class Interest < ActiveRecord::Base
belongs_to :man, :inverse_of => :interests, :automatic_inverse_of => false
belongs_to :man, :inverse_of => :interests
belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_interests
belongs_to :zine, :inverse_of => :interests
end

@ -1,7 +1,7 @@
class Man < ActiveRecord::Base
has_one :face, :inverse_of => :man
has_one :polymorphic_face, :class_name => 'Face', :as => :polymorphic_man, :inverse_of => :polymorphic_man
has_many :interests, :inverse_of => :man, :automatic_inverse_of => false
has_many :interests, :inverse_of => :man
has_many :polymorphic_interests, :class_name => 'Interest', :as => :polymorphic_man, :inverse_of => :polymorphic_man
# These are "broken" inverse_of associations for the purposes of testing
has_one :dirty_face, :class_name => 'Face', :inverse_of => :dirty_man

@ -9,7 +9,7 @@ class Member < ActiveRecord::Base
has_one :hairy_club, -> { where :clubs => {:name => "Moustache and Eyebrow Fancier Club"} }, :through => :membership, :source => :club
has_one :sponsor, :as => :sponsorable
has_one :sponsor_club, :through => :sponsor
has_one :member_detail, :automatic_inverse_of => false
has_one :member_detail, :inverse_of => false
has_one :organization, :through => :member_detail
belongs_to :member_type

@ -1,5 +1,5 @@
class MemberDetail < ActiveRecord::Base
belongs_to :member, :automatic_inverse_of => false
belongs_to :member, :inverse_of => false
belongs_to :organization
has_one :member_type, :through => :member

@ -1,5 +1,29 @@
* `HashWithIndifferentAccess#select` now returns a `HashWithIndifferentAccess`
instance instead of a `Hash` instance.
Fixes #10723
*Albert Llop*
* Add `DateTime#usec` and `DateTime#nsec` so that `ActiveSupport::TimeWithZone` keeps
sub-second resolution when wrapping a `DateTime` value.
Fixes #10855
*Andrew White*
* Fix `ActiveSupport::Dependencies::Loadable#load_dependency` calling
`#blame_file!` on Exceptions that do not have the Blamable mixin
*Andrew Kreiling*
* Override `Time.at` to support the passing of Time-like values when called with a single argument.
*Andrew White*
* Prevent side effects to hashes inside arrays when
`Hash#with_indifferent_access` is called.
Fixes #10526
*Yves Senn*

@ -13,17 +13,17 @@ module ActiveSupport
# can focus on the rest.
#
# bc = BacktraceCleaner.new
# bc.add_filter { |line| line.gsub(Rails.root, '') }
# bc.add_silencer { |line| line =~ /mongrel|rubygems/ }
# bc.clean(exception.backtrace) # will strip the Rails.root prefix and skip any lines from mongrel or rubygems
# bc.add_filter { |line| line.gsub(Rails.root, '') } # strip the Rails.root prefix
# bc.add_silencer { |line| line =~ /mongrel|rubygems/ } # skip any lines from mongrel or rubygems
# bc.clean(exception.backtrace) # perform the cleanup
#
# To reconfigure an existing BacktraceCleaner (like the default one in Rails)
# and show as much data as possible, you can always call
# <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the
# backtrace to a pristine state. 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.
# of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!<tt>
# These two methods will give you a completely untouched backtrace.
#
# Inspired by the Quiet Backtrace gem by Thoughtbot.
class BacktraceCleaner

@ -228,7 +228,7 @@ def self.instrument
#
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where
# a cache entry is used very frequently and is under heavy load. If a
# cache expires and due to heavy load seven different processes will try
# cache expires and due to heavy load several different processes will try
# to read data natively and then they all will try to write to cache. To
# avoid that case the first process to find an expired cache entry will
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.

@ -94,6 +94,15 @@ def run_callbacks(kind, &block)
def halted_callback_hook(filter)
end
module Conditionals # :nodoc:
class Value
def initialize(&block)
@block = block
end
def call(target, value); @block.call(value); end
end
end
module Filters
Environment = Struct.new(:target, :halted, :value, :run_block)
@ -403,8 +412,8 @@ def invert_lambda(l)
# the same after this point:
#
# Symbols:: Already methods.
# Strings:: class_eval'ed into methods.
# Procs:: define_method'ed into methods.
# Strings:: class_eval'd into methods.
# Procs:: using define_method compiled into methods.
# Objects::
# a method is created that calls the before_foo method
# on the object.
@ -415,6 +424,7 @@ def make_lambda(filter)
when String
l = eval "lambda { |value| #{filter} }"
lambda { |target, value| target.instance_exec(value, &l) }
when Conditionals::Value then filter
when ::Proc
if filter.arity > 1
return lambda { |target, _, &block|
@ -648,7 +658,7 @@ def reset_callbacks(symbol)
#
# * <tt>:terminator</tt> - Determines when a before filter will halt the
# callback chain, preventing following callbacks from being called and
# the event from being triggered. This is a string to be eval'ed. The
# the event from being triggered. This is a string to be eval'd. The
# result of the callback is available in the +result+ variable.
#
# define_callbacks :validate, terminator: 'result == false'

@ -80,6 +80,16 @@ def to_i
seconds_since_unix_epoch.to_i
end
# Returns the fraction of a second as microseconds
def usec
(sec_fraction * 1_000_000).to_i
end
# Returns the fraction of a second as nanoseconds
def nsec
(sec_fraction * 1_000_000_000).to_i
end
private
def offset_in_seconds

@ -164,7 +164,7 @@ def delegate(*methods)
#
# Reason is twofold: On one hand doing less calls is in general better.
# On the other hand it could be that the target has side-effects,
# whereas conceptualy, from the user point of view, the delegator should
# whereas conceptually, from the user point of view, the delegator should
# be doing one call.
if allow_nil
module_eval(<<-EOS, file, line - 3)

@ -65,6 +65,18 @@ def local_time(*args)
def current
::Time.zone ? ::Time.zone.now : ::Time.now
end
# Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
# instances can be used when called with a single argument
def at_with_coercion(*args)
if args.size == 1 && args.first.acts_like?(:time)
at_without_coercion(args.first.to_i)
else
at_without_coercion(*args)
end
end
alias_method :at_without_coercion, :at
alias_method :at, :at_with_coercion
end
# Seconds since midnight: Time.now.seconds_since_midnight

@ -213,7 +213,7 @@ def load_dependency(file)
yield
end
rescue Exception => exception # errors from loading file
exception.blame_file! file
exception.blame_file! file if exception.respond_to? :blame_file!
raise
end

@ -115,7 +115,7 @@ def max_mtime(paths)
end
def compile_glob(hash)
hash.freeze # Freeze so changes aren't accidently pushed
hash.freeze # Freeze so changes aren't accidentally pushed
return if hash.empty?
globs = hash.map do |key, value|

@ -227,6 +227,10 @@ def symbolize_keys; to_hash.symbolize_keys! end
def deep_symbolize_keys; to_hash.deep_symbolize_keys end
def to_options!; self end
def select(*args, &block)
dup.select!(*args, &block)
end
# Convert to a regular hash with string keys.
def to_hash
_new_hash= {}

@ -219,7 +219,12 @@ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
# unknown.
def constantize(camel_cased_word)
names = camel_cased_word.split('::')
names.shift if names.empty? || names.first.empty?
# Trigger a builtin NameError exception including the ill-formed constant in the message.
Object.const_get(camel_cased_word) if names.empty?
# Remove the first blank element in case of '::ClassName' notation.
names.shift if names.size > 1 && names.first.empty?
names.inject(Object) do |constant, name|
if constant == Object
@ -314,9 +319,14 @@ def ordinalize(number)
private
# Mount a regular expression that will match part by part of the constant.
# For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
#
# const_regexp("Foo::Bar::Baz") # => /Foo(::Bar(::Baz)?)?/
# const_regexp("::") # => /::/
def const_regexp(camel_cased_word) #:nodoc:
parts = camel_cased_word.split("::")
return Regexp.escape(camel_cased_word) if parts.blank?
last = parts.pop
parts.reverse.inject(last) do |acc, part|

@ -143,8 +143,8 @@ module ActiveSupport
#
# == Default Queue
#
# Notifications ships with a queue implementation that consumes and publish events
# to log subscribers in a thread. You can use any queue implementation you want.
# Notifications ships with a queue implementation that consumes and publishes events
# to all log subscribers. You can use any queue implementation you want.
#
module Notifications
class << self

@ -292,7 +292,7 @@ def advance(options)
end
end
%w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
%w(year mon month day mday wday yday hour min sec usec nsec to_date).each do |method_name|
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{method_name} # def month
time.#{method_name} # time.month
@ -300,10 +300,6 @@ def #{method_name} # def month
EOV
end
def usec
time.respond_to?(:usec) ? time.usec : 0
end
def to_a
[time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
end

@ -34,8 +34,6 @@ def run_constantize_tests_on
assert_equal Case::Dice, yield("Object::Case::Dice")
assert_equal ConstantizeTestCases, yield("ConstantizeTestCases")
assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases")
assert_equal Object, yield("")
assert_equal Object, yield("::")
assert_raises(NameError) { yield("UnknownClass") }
assert_raises(NameError) { yield("UnknownClass::Ace") }
assert_raises(NameError) { yield("UnknownClass::Ace::Base") }
@ -45,6 +43,8 @@ def run_constantize_tests_on
assert_raises(NameError) { yield("Ace::Base::ConstantizeTestCases") }
assert_raises(NameError) { yield("Ace::Gas::Base") }
assert_raises(NameError) { yield("Ace::Gas::ConstantizeTestCases") }
assert_raises(NameError) { yield("") }
assert_raises(NameError) { yield("::") }
end
def run_safe_constantize_tests_on
@ -58,8 +58,8 @@ def run_safe_constantize_tests_on
assert_equal Case::Dice, yield("Object::Case::Dice")
assert_equal ConstantizeTestCases, yield("ConstantizeTestCases")
assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases")
assert_equal Object, yield("")
assert_equal Object, yield("::")
assert_nil yield("")
assert_nil yield("::")
assert_nil yield("UnknownClass")
assert_nil yield("UnknownClass::Ace")
assert_nil yield("UnknownClass::Ace::Base")

@ -327,6 +327,16 @@ def test_to_i
assert_equal 946684800, DateTime.civil(1999,12,31,19,0,0,Rational(-5,24)).to_i
end
def test_usec
assert_equal 0, DateTime.civil(2000).usec
assert_equal 500000, DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)).usec
end
def test_nsec
assert_equal 0, DateTime.civil(2000).nsec
assert_equal 500000000, DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)).nsec
end
protected
def with_env_tz(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz

@ -480,6 +480,36 @@ def test_indifferent_deleting
assert_equal hash.delete('a'), nil
end
def test_indifferent_select
hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select {|k,v| v == 1}
assert_equal({ 'a' => 1 }, hash)
assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash
end
def test_indifferent_select_bang
indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings)
indifferent_strings.select! {|k,v| v == 1}
assert_equal({ 'a' => 1 }, indifferent_strings)
assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
end
def test_indifferent_reject
hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).reject {|k,v| v != 1}
assert_equal({ 'a' => 1 }, hash)
assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash
end
def test_indifferent_reject_bang
indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings)
indifferent_strings.reject! {|k,v| v != 1}
assert_equal({ 'a' => 1 }, indifferent_strings)
assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
end
def test_indifferent_to_hash
# Should convert to a Hash with String keys.
assert_equal @strings, @mixed.with_indifferent_access.to_hash

@ -741,6 +741,28 @@ def test_compare_with_time_with_zone
assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] ))
end
def test_at_with_datetime
assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(DateTime.civil(2000, 1, 1, 0, 0, 0))
# Only test this if the underlying Time.at raises a TypeError
begin
Time.at_without_coercion(Time.now, 0)
rescue TypeError
assert_raise(TypeError) { assert_equal(Time.utc(2000, 1, 1, 0, 0, 0), Time.at(DateTime.civil(2000, 1, 1, 0, 0, 0), 0)) }
end
end
def test_at_with_time_with_zone
assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC']))
# Only test this if the underlying Time.at raises a TypeError
begin
Time.at_without_coercion(Time.now, 0)
rescue TypeError
assert_raise(TypeError) { assert_equal(Time.utc(2000, 1, 1, 0, 0, 0), Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC']), 0)) }
end
end
def test_eql?
assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) )
assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )

@ -445,6 +445,16 @@ def test_usec_returns_0_when_datetime_is_wrapped
assert_equal 0, twz.usec
end
def test_usec_returns_sec_fraction_when_datetime_is_wrapped
twz = ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)), @time_zone)
assert_equal 500000, twz.usec
end
def test_nsec_returns_sec_fraction_when_datetime_is_wrapped
twz = ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)), @time_zone)
assert_equal 500000000, twz.nsec
end
def test_utc_to_local_conversion_saves_period_in_instance_variable
assert_nil @twz.instance_variable_get('@period')
@twz.time

@ -0,0 +1,5 @@
exception = Exception.new('I am not blamable!')
class << exception
undef_method(:blame_file!)
end
raise exception

@ -76,6 +76,14 @@ def test_dependency_which_raises_exception_isnt_added_to_loaded_set
end
end
def test_dependency_which_raises_doesnt_blindly_call_blame_file!
with_loading do
filename = 'dependencies/raises_exception_without_blame_file'
assert_raises(Exception) { require_dependency filename }
end
end
def test_warnings_should_be_enabled_on_first_load
with_loading 'dependencies' do
old_warnings, ActiveSupport::Dependencies.warnings_on_first_load = ActiveSupport::Dependencies.warnings_on_first_load, true

@ -10,7 +10,7 @@ def test_uniq_load_paths
}
load_paths_count[File.expand_path('../../lib', __FILE__)] -= 1
filtered = load_paths_count.select { |k, v| v > 1 }
assert filtered.empty?, filtered.inspect
load_paths_count.select! { |k, v| v > 1 }
assert load_paths_count.empty?, load_paths_count.inspect
end
end

@ -178,7 +178,7 @@ def test_children_with_non_adjacent_text
private
def assert_equal_rexml(xml)
parsed_xml = XmlMini.parse(xml)
hash = XmlMini.with_backend('REXML') { parsed_xml }
hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
assert_equal(hash, parsed_xml)
end
end

@ -195,7 +195,8 @@ def test_children_with_blank_text_and_attribute
private
def assert_equal_rexml(xml)
parsed_xml = XmlMini.parse(xml)
hash = XmlMini.with_backend('REXML') { parsed_xml }
xml.rewind if xml.respond_to?(:rewind)
hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
assert_equal(hash, parsed_xml)
end
end

@ -186,7 +186,8 @@ def test_children_with_blank_text
private
def assert_equal_rexml(xml)
parsed_xml = XmlMini.parse(xml)
hash = XmlMini.with_backend('REXML') { parsed_xml }
xml.rewind if xml.respond_to?(:rewind)
hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
assert_equal(hash, parsed_xml)
end
end

@ -208,7 +208,8 @@ def test_children_with_blank_text_and_attribute
private
def assert_equal_rexml(xml)
parsed_xml = XmlMini.parse(xml)
hash = XmlMini.with_backend('REXML') { parsed_xml }
xml.rewind if xml.respond_to?(:rewind)
hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
assert_equal(hash, parsed_xml)
end
end

@ -209,7 +209,8 @@ def test_children_with_blank_text_and_attribute
private
def assert_equal_rexml(xml)
parsed_xml = XmlMini.parse(xml)
hash = XmlMini.with_backend('REXML') { parsed_xml }
xml.rewind if xml.respond_to?(:rewind)
hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
assert_equal(hash, parsed_xml)
end
end

@ -30,7 +30,8 @@ def test_parse_from_io
private
def assert_equal_rexml(xml)
parsed_xml = XmlMini.parse(xml)
hash = XmlMini.with_backend('REXML') { parsed_xml }
xml.rewind if xml.respond_to?(:rewind)
hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
assert_equal(hash, parsed_xml)
end
end

@ -1,3 +1,5 @@
* No changes.
* Removed repetitive th tags. Instead of them added one th tag with a colspan attribute.
*Sıtkı Bağdat*
Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/guides/CHANGELOG.md) for previous changes.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 B

After

Width:  |  Height:  |  Size: 35 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 B

After

Width:  |  Height:  |  Size: 36 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 11 KiB

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