aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb6
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb6
-rw-r--r--actionview/test/template/render_test.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/reflection.rb30
-rw-r--r--activerecord/lib/active_record/sanitization.rb75
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb13
-rw-r--r--activerecord/test/models/company.rb3
-rw-r--r--activerecord/test/models/developer.rb3
-rw-r--r--activerecord/test/models/project.rb2
-rw-r--r--activerecord/test/schema/schema.rb2
-rw-r--r--railties/test/application/routing_test.rb12
14 files changed, 129 insertions, 41 deletions
diff --git a/README.md b/README.md
index a17b6245f5..f823a49f7d 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ Ruby code (ERB files). Views are typically rendered to generate a controller res
or to generate the body of an email. In Rails, View generation is handled by Action View.
You can read more about Action View in its [README](actionview/README.rdoc).
-Active Record, Action Pack, and Action View can each be used independently outside Rails.
+Active Record, Active Model, Action Pack, and Action View can each be used independently outside Rails.
In addition to them, Rails also comes with Action Mailer ([README](actionmailer/README.rdoc)), a library
to generate and send emails; Active Job ([README](activejob/README.md)), a
framework for declaring jobs and making them run on a variety of queueing
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index 91f6f66fe3..68083ed747 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -18,7 +18,7 @@ class MimeTypeTest < ActiveSupport::TestCase
assert_equal Mime::Type[:MOBILE], Mime::EXTENSION_LOOKUP['mobile']
Mime::Type.unregister(:mobile)
- assert !Mime.const_defined?(:MOBILE), "Mime::MOBILE should not be defined"
+ assert !Mime::Type.registered?(:MOBILE), "Mime::MOBILE should not be defined"
assert !Mime::LOOKUP.has_key?('text/x-mobile'), "Mime::LOOKUP should not have key ['text/x-mobile]"
assert !Mime::EXTENSION_LOOKUP.has_key?('mobile'), "Mime::EXTENSION_LOOKUP should not have key ['mobile]"
ensure
@@ -183,6 +183,10 @@ class MimeTypeTest < ActiveSupport::TestCase
end
end
+ test "deprecated const_defined?" do
+ assert_deprecated { Mime.const_defined?(:ALL) }
+ end
+
test "verifiable mime types" do
all_types = Mime::SET.symbols
all_types.uniq!
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 610f5c0b4a..2a367b85af 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -1038,7 +1038,7 @@ module ActionView
# date_field("user", "born_on")
# # => <input id="user_born_on" name="user[born_on]" type="date" />
#
- # The default value is generated by trying to call "to_date"
+ # The default value is generated by trying to call +strftime+ with "%Y-%m-%d"
# on the object's value, which makes it behave as expected for instances
# of DateTime and ActiveSupport::TimeWithZone. You can still override that
# by passing the "value" option explicitly, e.g.
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index f6dd179bb7..430051379d 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -548,7 +548,7 @@ module ActionView
end
# Returns a string of option tags for pretty much any time zone in the
- # world. Supply a ActiveSupport::TimeZone name as +selected+ to have it
+ # world. Supply an ActiveSupport::TimeZone name as +selected+ to have it
# marked as the selected option tag. You can also supply an array of
# ActiveSupport::TimeZone objects as +priority_zones+, so that they will
# be listed above the rest of the (long) list. (You can use
@@ -556,7 +556,7 @@ module ActionView
# of the US time zones, or a Regexp to select the zones of your choice)
#
# The +selected+ parameter must be either +nil+, or a string that names
- # a ActiveSupport::TimeZone.
+ # an ActiveSupport::TimeZone.
#
# By default, +model+ is the ActiveSupport::TimeZone constant (which can
# be obtained in Active Record as a value object). The only requirement
@@ -655,7 +655,7 @@ module ActionView
#
# params.require(:user).permit(...)
#
- # will raise a error since no +{user: ...}+ will be present.
+ # will raise an error since no +{user: ...}+ will be present.
#
# To prevent this the helper generates an auxiliary hidden field before
# every collection of radio buttons. The hidden field has the same name as collection radio button and blank value.
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 9c2c9507b7..00fc28a522 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -627,6 +627,16 @@ class CachedCollectionViewRenderTest < CachedViewRenderTest
@view.render(partial: "test/customer", collection: [customer], cache: ->(item) { [item, 'key'] })
end
+ test "with caching with custom key and rendering with different key" do
+ customer = Customer.new("david")
+ key = cache_key([customer, 'key'], "test/_customer")
+
+ ActionView::PartialRenderer.collection_cache.write(key, 'Hello')
+
+ assert_equal "Hello: david",
+ @view.render(partial: "test/customer", collection: [customer], cache: ->(item) { [item, 'another_key'] })
+ end
+
test "automatic caching with inferred cache name" do
customer = CachedCustomer.new("david")
key = cache_key(customer, "test/_cached_customer")
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 285b4065b1..7e53f8958a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -507,7 +507,7 @@ module ActiveRecord
raise NotImplementedError, "change_column_default is not implemented"
end
- # Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
+ # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
# indicates whether the value can be +NULL+. For example
#
# change_column_null(:users, :nickname, false)
@@ -519,7 +519,7 @@ module ActiveRecord
# allows them to be +NULL+ (drops the constraint).
#
# The method accepts an optional fourth argument to replace existing
- # +NULL+s with some other value. Use that one when enabling the
+ # <tt>NULL</tt>s with some other value. Use that one when enabling the
# constraint if needed, since otherwise those rows would not be valid.
#
# Please note the fourth argument does not set a column's default.
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 4e0fc407bc..65ec356735 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -175,6 +175,20 @@ module ActiveRecord
end
end
+ def inverse_of
+ return unless inverse_name
+
+ @inverse_of ||= klass._reflect_on_association inverse_name
+ end
+
+ def check_validity_of_inverse!
+ unless polymorphic?
+ if has_inverse? && inverse_of.nil?
+ raise InverseOfAssociationNotFoundError.new(self)
+ end
+ end
+ end
+
# This shit is nasty. We need to avoid the following situation:
#
# * An associated record is deleted via record.destroy
@@ -377,14 +391,6 @@ module ActiveRecord
check_validity_of_inverse!
end
- def check_validity_of_inverse!
- unless polymorphic?
- if has_inverse? && inverse_of.nil?
- raise InverseOfAssociationNotFoundError.new(self)
- end
- end
- end
-
def check_preloadable!
return unless scope
@@ -436,12 +442,6 @@ module ActiveRecord
inverse_name
end
- def inverse_of
- return unless inverse_name
-
- @inverse_of ||= klass._reflect_on_association inverse_name
- end
-
def polymorphic_inverse_of(associated_class)
if has_inverse?
if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
@@ -924,6 +924,8 @@ module ActiveRecord
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
end
+ def inverse_name; delegate_reflection.send(:inverse_name); end
+
private
def derive_class_name
# get the class_name of the belongs_to association of the through reflection
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 32f4229977..7f6664ea50 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -4,7 +4,7 @@ module ActiveRecord
module ClassMethods
# Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
- def sanitize(object) #:nodoc:
+ def sanitize(object) # :nodoc:
connection.quote(object)
end
alias_method :quote_value, :sanitize
@@ -13,8 +13,15 @@ module ActiveRecord
# Accepts an array or string of SQL conditions and sanitizes
# them into a valid SQL fragment for a WHERE clause.
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
- # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
+ #
+ # sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
+ # # => "name='foo''bar' and group_id=4"
+ #
+ # sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
+ # # => "name='foo''bar' and group_id='4'"
+ #
+ # sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
+ # # => "name='foo''bar' and group_id='4'"
def sanitize_sql_for_conditions(condition)
return nil if condition.blank?
@@ -28,7 +35,15 @@ module ActiveRecord
# Accepts an array, hash, or string of SQL conditions and sanitizes
# them into a valid SQL fragment for a SET clause.
- # { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
+ #
+ # sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
+ # # => "name=NULL and group_id=4"
+ #
+ # Post.send(:sanitize_sql_for_assignment, { name: nil, group_id: 4 })
+ # # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
+ #
+ # sanitize_sql_for_assignment("name=NULL and group_id='4'")
+ # # => "name=NULL and group_id='4'"
def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
case assignments
when Array; sanitize_sql_array(assignments)
@@ -40,14 +55,18 @@ module ActiveRecord
# Accepts a hash of SQL conditions and replaces those attributes
# that correspond to a +composed_of+ relationship with their expanded
# aggregate attribute values.
+ #
# Given:
- # class Person < ActiveRecord::Base
- # composed_of :address, class_name: "Address",
- # mapping: [%w(address_street street), %w(address_city city)]
- # end
+ #
+ # class Person < ActiveRecord::Base
+ # composed_of :address, class_name: "Address",
+ # mapping: [%w(address_street street), %w(address_city city)]
+ # end
+ #
# Then:
- # { address: Address.new("813 abc st.", "chicago") }
- # # => { address_street: "813 abc st.", address_city: "chicago" }
+ #
+ # { address: Address.new("813 abc st.", "chicago") }
+ # # => { address_street: "813 abc st.", address_city: "chicago" }
def expand_hash_conditions_for_aggregates(attrs)
expanded_attrs = {}
attrs.each do |attr, value|
@@ -68,8 +87,9 @@ module ActiveRecord
end
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
- # { status: nil, group_id: 1 }
- # # => "status = NULL , group_id = 1"
+ #
+ # sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
+ # # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
def sanitize_sql_hash_for_assignment(attrs, table)
c = connection
attrs.map do |attr, value|
@@ -79,7 +99,19 @@ module ActiveRecord
end
# Sanitizes a +string+ so that it is safe to use within an SQL
- # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%"
+ # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
+ #
+ # sanitize_sql_like("100%")
+ # # => "100\\%"
+ #
+ # sanitize_sql_like("snake_cased_string")
+ # # => "snake\\_cased\\_string"
+ #
+ # sanitize_sql_like("100%", "!")
+ # # => "100!%"
+ #
+ # sanitize_sql_like("snake_cased_string", "!")
+ # # => "snake!_cased!_string"
def sanitize_sql_like(string, escape_character = "\\")
pattern = Regexp.union(escape_character, "%", "_")
string.gsub(pattern) { |x| [escape_character, x].join }
@@ -87,7 +119,12 @@ module ActiveRecord
# Accepts an array of conditions. The array has each value
# sanitized and interpolated into the SQL statement.
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
+ #
+ # sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
+ # # => "name='foo''bar' and group_id=4"
+ #
+ # sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
+ # # => "name='foo''bar' and group_id='4'"
def sanitize_sql_array(ary)
statement, *values = ary
if values.first.is_a?(Hash) && statement =~ /:\w+/
@@ -101,7 +138,7 @@ module ActiveRecord
end
end
- def replace_bind_variables(statement, values) #:nodoc:
+ def replace_bind_variables(statement, values) # :nodoc:
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
bound = values.dup
c = connection
@@ -110,7 +147,7 @@ module ActiveRecord
end
end
- def replace_bind_variable(value, c = connection) #:nodoc:
+ def replace_bind_variable(value, c = connection) # :nodoc:
if ActiveRecord::Relation === value
value.to_sql
else
@@ -118,7 +155,7 @@ module ActiveRecord
end
end
- def replace_named_bind_variables(statement, bind_vars) #:nodoc:
+ def replace_named_bind_variables(statement, bind_vars) # :nodoc:
statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
if $1 == ':' # skip postgresql casts
match # return the whole match
@@ -130,7 +167,7 @@ module ActiveRecord
end
end
- def quote_bound_value(value, c = connection) #:nodoc:
+ def quote_bound_value(value, c = connection) # :nodoc:
if value.respond_to?(:map) && !value.acts_like?(:string)
if value.respond_to?(:empty?) && value.empty?
c.quote(nil)
@@ -142,7 +179,7 @@ module ActiveRecord
end
end
- def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
+ def raise_if_bind_arity_mismatch(statement, expected, provided) # :nodoc:
unless expected == provided
raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
end
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index 423b8238b1..ece4dab539 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -13,6 +13,9 @@ require 'models/mixed_case_monkey'
require 'models/admin'
require 'models/admin/account'
require 'models/admin/user'
+require 'models/developer'
+require 'models/company'
+require 'models/project'
class AutomaticInverseFindingTests < ActiveRecord::TestCase
fixtures :ratings, :comments, :cars
@@ -198,6 +201,16 @@ class InverseAssociationTests < ActiveRecord::TestCase
belongs_to_ref = Sponsor.reflect_on_association(:sponsor_club)
assert_nil belongs_to_ref.inverse_of
end
+
+ def test_this_inverse_stuff
+ firm = Firm.create!(name: 'Adequate Holdings')
+ Project.create!(name: 'Project 1', firm: firm)
+ Developer.create!(name: 'Gorbypuff', firm: firm)
+
+ new_project = Project.last
+ assert Project.reflect_on_association(:lead_developer).inverse_of.present?, "Expected inverse of to be present"
+ assert new_project.lead_developer.present?, "Expected lead developer to be present on the project"
+ end
end
class InverseHasOneTests < ActiveRecord::TestCase
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 67936e8e5d..a96b8ef0f2 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -86,6 +86,9 @@ class Firm < Company
has_many :association_with_references, -> { references(:foo) }, :class_name => 'Client'
+ has_one :lead_developer, class_name: "Developer"
+ has_many :projects
+
def log
@log ||= []
end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 7565e8f967..7c5941b1af 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -54,6 +54,9 @@ class Developer < ActiveRecord::Base
has_many :ratings, through: :comments
has_one :ship, dependent: :nullify
+ belongs_to :firm
+ has_many :contracted_projects, class_name: "Project"
+
scope :jamises, -> { where(:name => 'Jamis') }
validates_inclusion_of :salary, :in => 50000..200000
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index 7f42a4b1f8..5328330653 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -11,6 +11,8 @@ class Project < ActiveRecord::Base
:before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"},
:after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"}
has_and_belongs_to_many :well_payed_salary_groups, -> { group("developers.salary").having("SUM(salary) > 10000").select("SUM(salary) as salary") }, :class_name => "Developer"
+ belongs_to :firm
+ has_one :lead_developer, through: :firm, inverse_of: :contracted_projects
attr_accessor :developers_log
after_initialize :set_developers_log
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 4b7272f10a..d334a2740e 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -252,6 +252,7 @@ ActiveRecord::Schema.define do
t.string :name
t.string :first_name
t.integer :salary, default: 70000
+ t.integer :firm_id
if subsecond_precision_supported?
t.datetime :created_at, precision: 6
t.datetime :updated_at, precision: 6
@@ -658,6 +659,7 @@ ActiveRecord::Schema.define do
create_table :projects, force: true do |t|
t.string :name
t.string :type
+ t.integer :firm_id
end
create_table :randomly_named_table1, force: true do |t|
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index cbada6be97..0777714d35 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -21,6 +21,12 @@ module ApplicationTests
assert_equal 200, last_response.status
end
+ test "rails/info in development" do
+ app("development")
+ get "/rails/info"
+ assert_equal 302, last_response.status
+ end
+
test "rails/info/routes in development" do
app("development")
get "/rails/info/routes"
@@ -63,6 +69,12 @@ module ApplicationTests
assert_equal 404, last_response.status
end
+ test "rails/info in production" do
+ app("production")
+ get "/rails/info"
+ assert_equal 404, last_response.status
+ end
+
test "rails/info/routes in production" do
app("production")
get "/rails/info/routes"