diff options
105 files changed, 726 insertions, 187 deletions
@@ -78,7 +78,7 @@ We encourage you to contribute to Ruby on Rails! Please check out the ## Code Status -* [](https://travis-ci.org/rails/rails) +[](https://travis-ci.org/rails/rails) ## License diff --git a/actionmailer/lib/action_mailer/delivery_job.rb b/actionmailer/lib/action_mailer/delivery_job.rb index 95231411fb..e864ab7a4d 100644 --- a/actionmailer/lib/action_mailer/delivery_job.rb +++ b/actionmailer/lib/action_mailer/delivery_job.rb @@ -3,10 +3,10 @@ require 'active_job' module ActionMailer # The <tt>ActionMailer::DeliveryJob</tt> class is used when you # want to send emails outside of the request-response cycle. - class DeliveryJob < ActiveJob::Base #:nodoc: + class DeliveryJob < ActiveJob::Base # :nodoc: queue_as :mailers - def perform(mailer, mail_method, delivery_method, *args) #:nodoc# + def perform(mailer, mail_method, delivery_method, *args) #:nodoc: mailer.constantize.public_send(mail_method, *args).send(delivery_method) end end diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 115ad54190..98b573e21e 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -9,11 +9,11 @@ *Chris Sinjakli* -* Fixed usage of optional scopes in URL helpers. +* Fixed usage of optional scopes in url helpers. *Alex Robbin* -* Fixed handling of positional url helper arguments when `format: false`. +* Fixed handling of positional url helper arguments when `format: false`. Fixes #17819. diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 93d1d33f78..8d3ce24612 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -410,7 +410,7 @@ module ActionDispatch @options[:serializer] == :hybrid && value.start_with?(MARSHAL_SIGNATURE) end - def serialize(name, value) + def serialize(value) serializer.dump(value) end @@ -463,9 +463,9 @@ module ActionDispatch def []=(name, options) if options.is_a?(Hash) options.symbolize_keys! - options[:value] = @verifier.generate(serialize(name, options[:value])) + options[:value] = @verifier.generate(serialize(options[:value])) else - options = { :value => @verifier.generate(serialize(name, options)) } + options = { :value => @verifier.generate(serialize(options)) } end raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE @@ -524,7 +524,7 @@ module ActionDispatch options = { :value => options } end - options[:value] = @encryptor.encrypt_and_sign(serialize(name, options[:value])) + options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value])) raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE @parent_jar[name] = options diff --git a/actionview/lib/action_view/helpers/tags/search_field.rb b/actionview/lib/action_view/helpers/tags/search_field.rb index 4597cec6fa..a848aeabfa 100644 --- a/actionview/lib/action_view/helpers/tags/search_field.rb +++ b/actionview/lib/action_view/helpers/tags/search_field.rb @@ -3,18 +3,21 @@ module ActionView module Tags # :nodoc: class SearchField < TextField # :nodoc: def render - super do |options| - if options["autosave"] - if options["autosave"] == true - options["autosave"] = request.host.split(".").reverse.join(".") - end - options["results"] ||= 10 - end + options = @options.stringify_keys - if options["onsearch"] - options["incremental"] = true unless options.has_key?("incremental") + if options["autosave"] + if options["autosave"] == true + options["autosave"] = request.host.split(".").reverse.join(".") end + options["results"] ||= 10 + end + + if options["onsearch"] + options["incremental"] = true unless options.has_key?("incremental") end + + @options = options + super end end end diff --git a/actionview/lib/action_view/helpers/tags/text_field.rb b/actionview/lib/action_view/helpers/tags/text_field.rb index 49fc81ec8c..5c576a20ca 100644 --- a/actionview/lib/action_view/helpers/tags/text_field.rb +++ b/actionview/lib/action_view/helpers/tags/text_field.rb @@ -11,7 +11,6 @@ module ActionView options["size"] = options["maxlength"] unless options.key?("size") options["type"] ||= field_type options["value"] = options.fetch("value") { value_before_type_cast(object) } unless field_type == "file" - yield options if block_given? add_default_name_and_id(options) tag("input", options) end diff --git a/actionview/test/lib/controller/fake_models.rb b/actionview/test/lib/controller/fake_models.rb index a463a08bb6..789b1d198b 100644 --- a/actionview/test/lib/controller/fake_models.rb +++ b/actionview/test/lib/controller/fake_models.rb @@ -111,19 +111,6 @@ class CommentRelevance end end -class Sheep - extend ActiveModel::Naming - include ActiveModel::Conversion - - attr_reader :id - def to_key; id ? [id] : nil end - def save; @id = 1 end - def new_record?; @id.nil? end - def name - @id.nil? ? 'new sheep' : "sheep ##{@id}" - end -end - class TagRelevance extend ActiveModel::Naming include ActiveModel::Conversion @@ -183,3 +170,15 @@ end class Car < Struct.new(:color) end + +class Plane + attr_reader :to_key + + def model_name + OpenStruct.new param_key: 'airplane' + end + + def save + @to_key = [1] + end +end diff --git a/actionview/test/template/record_identifier_test.rb b/actionview/test/template/record_identifier_test.rb index 22038110a5..04898c0b0e 100644 --- a/actionview/test/template/record_identifier_test.rb +++ b/actionview/test/template/record_identifier_test.rb @@ -9,7 +9,6 @@ class RecordIdentifierTest < ActiveSupport::TestCase @record = @klass.new @singular = 'comment' @plural = 'comments' - @uncountable = Sheep end def test_dom_id_with_new_record @@ -47,3 +46,46 @@ class RecordIdentifierTest < ActiveSupport::TestCase assert_equal @singular, ActionView::RecordIdentifier.dom_class(@record) end end + +class RecordIdentifierWithoutActiveModelTest < ActiveSupport::TestCase + include ActionView::RecordIdentifier + + def setup + @record = Plane.new + end + + def test_dom_id_with_new_record + assert_equal "new_airplane", dom_id(@record) + end + + def test_dom_id_with_new_record_and_prefix + assert_equal "custom_prefix_airplane", dom_id(@record, :custom_prefix) + end + + def test_dom_id_with_saved_record + @record.save + assert_equal "airplane_1", dom_id(@record) + end + + def test_dom_id_with_prefix + @record.save + assert_equal "edit_airplane_1", dom_id(@record, :edit) + end + + def test_dom_class + assert_equal 'airplane', dom_class(@record) + end + + def test_dom_class_with_prefix + assert_equal "custom_prefix_airplane", dom_class(@record, :custom_prefix) + end + + def test_dom_id_as_singleton_method + @record.save + assert_equal "airplane_1", ActionView::RecordIdentifier.dom_id(@record) + end + + def test_dom_class_as_singleton_method + assert_equal 'airplane', ActionView::RecordIdentifier.dom_class(@record) + end +end diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb index e2c076eb3f..752be6898e 100644 --- a/activejob/lib/active_job/arguments.rb +++ b/activejob/lib/active_job/arguments.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/hash/indifferent_access' + module ActiveJob # Raised when an exception is raised during job arguments deserialization. # diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index 60af31cca7..337b61c55c 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -170,7 +170,7 @@ module ActiveModel # Handle <tt>*_changed?</tt> for +method_missing+. def attribute_changed?(attr, options = {}) #:nodoc: - result = changed_attributes.include?(attr) + result = changes_include?(attr) result &&= options[:to] == __send__(attr) if options.key?(:to) result &&= options[:from] == changed_attributes[attr] if options.key?(:from) result @@ -188,6 +188,10 @@ module ActiveModel private + def changes_include?(attr_name) + attributes_changed_by_setter.include?(attr_name) + end + # Removes current changes and makes them accessible through +previous_changes+. def changes_applied # :doc: @previously_changed = changes diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 9105ef5dd6..55687cb3c7 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -283,9 +283,9 @@ module ActiveModel # ActiveModel::StrictValidationFailed instead of adding the error. # <tt>:strict</tt> option can also be set to any other exception. # - # person.errors.add(:name, nil, strict: true) + # person.errors.add(:name, :invalid, strict: true) # # => ActiveModel::StrictValidationFailed: name is invalid - # person.errors.add(:name, nil, strict: NameIsInvalid) + # person.errors.add(:name, :invalid, strict: NameIsInvalid) # # => NameIsInvalid: name is invalid # # person.errors.messages # => {} diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index cef66f3c0d..2b932683ea 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -171,9 +171,8 @@ class ValidationsTest < ActiveModel::TestCase # A common mistake -- we meant to call 'validates' Topic.validate :title, presence: true end - message = 'Unknown key: :presence. Valid keys are: :on, :if, :unless. Perhaps you meant to call `validates` instead of `validate`?' - assert_includes error.message, "Unknown key: :presence" - assert_includes error.message, "Perhaps you meant to call `validates` instead of `validate`?" + message = 'Unknown key: :presence. Valid keys are: :on, :if, :unless, :prepend. Perhaps you meant to call `validates` instead of `validate`?' + assert_equal message, error.message end def test_callback_options_to_validate diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 6494c7374e..f282029d22 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,26 @@ +* Add a `:foreign_key` option to `references` and associated migration + methods. The model and migration generators now use this option, rather than + the `add_foreign_key` form. + + *Sean Griffin* + +* Don't raise when writing an attribute with an out-of-range datetime passed + by the user. + + *Grey Baker* + +* Replace deprecated `ActiveRecord::Tasks::DatabaseTasks#load_schema` with + `ActiveRecord::Tasks::DatabaseTasks#load_schema_for`. + + *Yves Senn* + +* Fixes bug with 'ActiveRecord::Type::Numeric' that causes negative values to + be marked as having changed when set to the same negative value. + + Closes #18161. + + *Daniel Fox* + * Introduce `force: :cascade` option for `create_table`. Using this option will recreate tables even if they have dependent objects (like foreign keys). `db/schema.rb` now uses `force: :cascade`. This makes it possible to @@ -13,7 +36,7 @@ *Yves Senn* -* Fix undesirable RangeError by Type::Integer. Add Type::UnsignedInteger. +* Fix undesirable RangeError by `Type::Integer`. Add `Type::UnsignedInteger`. *Ryuta Kamizono* diff --git a/activerecord/RUNNING_UNIT_TESTS.rdoc b/activerecord/RUNNING_UNIT_TESTS.rdoc index 569685bd45..7e3460365b 100644 --- a/activerecord/RUNNING_UNIT_TESTS.rdoc +++ b/activerecord/RUNNING_UNIT_TESTS.rdoc @@ -20,11 +20,11 @@ example: Simply executing <tt>bundle exec rake test</tt> is equivalent to the following: - $ bundle exec rake test_mysql - $ bundle exec rake test_mysql2 - $ bundle exec rake test_postgresql - $ bundle exec rake test_sqlite3 - $ bundle exec rake test_sqlite3_mem + $ bundle exec rake test:mysql + $ bundle exec rake test:mysql2 + $ bundle exec rake test:postgresql + $ bundle exec rake test:sqlite3 + $ bundle exec rake test:sqlite3_mem There should be tests available for each database backend listed in the {Config File}[rdoc-label:label-Config+File]. (the exact set of available tests is diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 81fdd681de..c63b42e2a0 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -73,11 +73,11 @@ module ActiveRecord # Checks whether record is different to the current target, without loading it def different_target?(record) - record.id != owner[reflection.foreign_key] + record.id != owner._read_attribute(reflection.foreign_key) end def replace_keys(record) - owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)] + owner[reflection.foreign_key] = record._read_attribute(reflection.association_primary_key(record.class)) end def remove_keys @@ -85,7 +85,7 @@ module ActiveRecord end def foreign_key_present? - owner[reflection.foreign_key] + owner._read_attribute(reflection.foreign_key) end # NOTE - for now, we're only supporting inverse setting from belongs_to back onto @@ -99,12 +99,13 @@ module ActiveRecord if options[:primary_key] owner.send(reflection.name).try(:id) else - owner[reflection.foreign_key] + owner._read_attribute(reflection.foreign_key) end end def stale_state - owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s + result = owner._read_attribute(reflection.foreign_key) + result && result.to_s end end end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 3f4d3bfc08..7a050ca224 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -13,21 +13,6 @@ module ActiveRecord @through_association = nil end - # Returns the size of the collection by executing a SELECT COUNT(*) query - # if the collection hasn't been loaded, and by calling collection.size if - # it has. If the collection will likely have a size greater than zero, - # and if fetching the collection will be needed afterwards, one less - # SELECT query will be generated by using #length instead. - def size - if has_cached_counter? - owner._read_attribute cached_counter_attribute_name(reflection) - elsif loaded? - target.size - else - super - end - end - def concat(*records) unless owner.new_record? records.flatten.each do |record| diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 033e71f7b9..d5702accaf 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -76,6 +76,10 @@ module ActiveRecord private + def changes_include?(attr_name) + super || attribute_changed_in_place?(attr_name) + end + def calculate_changes_from_defaults @changed_attributes = nil self.class.column_defaults.each do |attr, orig_value| diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index 87274dd4e1..777f7ab4d7 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -12,7 +12,11 @@ module ActiveRecord if value.is_a?(Array) value.map { |v| type_cast_from_user(v) } elsif value.respond_to?(:in_time_zone) - value.in_time_zone || super + begin + value.in_time_zone || super + rescue ArgumentError + nil + end end end diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb index 3288108a6a..08f274fd42 100644 --- a/activerecord/lib/active_record/attributes.rb +++ b/activerecord/lib/active_record/attributes.rb @@ -53,9 +53,9 @@ module ActiveRecord # store_listing.price_in_cents # => 10 # # Users may also define their own custom types, as long as they respond to the methods - # defined on the value type. The `type_cast` method on your type object will be called + # defined on the value type. The +type_cast+ method on your type object will be called # with values both from the database, and from your controllers. See - # `ActiveRecord::Attributes::Type::Value` for the expected API. It is recommended that your + # +ActiveRecord::Attributes::Type::Value+ for the expected API. It is recommended that your # type objects inherit from an existing type, or the base value type. # # class MoneyType < ActiveRecord::Type::Integer diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f978fbd0a4..954d22f1d5 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -141,7 +141,7 @@ module ActiveRecord #:nodoc: # # In addition to the basic accessors, query methods are also automatically available on the Active Record object. # Query methods allow you to test whether an attribute value is present. - # For numeric values, present is defined as non-zero. + # Additionally, when dealing with numeric values, a query method will return false if the value is zero. # # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call # to determine whether the user has a name: diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 0fa00d03a3..3968b90341 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -363,7 +363,7 @@ module ActiveRecord conn.expire end - release owner + release conn, owner @available.add conn end @@ -376,7 +376,7 @@ module ActiveRecord @connections.delete conn @available.delete conn - release conn.owner + release conn, conn.owner @available.add checkout_new_connection if @available.any_waiting? end @@ -424,10 +424,12 @@ module ActiveRecord end end - def release(owner) + def release(conn, owner) thread_id = owner.object_id - @reserved_connections.delete thread_id + if @reserved_connections[thread_id] == conn + @reserved_connections.delete thread_id + end end def new_connection diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 537e21029e..8defc3986f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -68,6 +68,84 @@ module ActiveRecord end end + class ReferenceDefinition # :nodoc: + def initialize( + name, + polymorphic: false, + index: false, + foreign_key: false, + type: :integer, + **options + ) + @name = name + @polymorphic = polymorphic + @index = index + @foreign_key = foreign_key + @type = type + @options = options + + if polymorphic && foreign_key + raise ArgumentError, "Cannot add a foreign key to a polymorphic relation" + end + end + + def add_to(table) + columns.each do |column_options| + table.column(*column_options) + end + + if index + table.index(column_names, index_options) + end + + if foreign_key + table.foreign_key(foreign_table_name, foreign_key_options) + end + end + + protected + + attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options + + private + + def as_options(value, default = {}) + if value.is_a?(Hash) + value + else + default + end + end + + def polymorphic_options + as_options(polymorphic, options) + end + + def index_options + as_options(index) + end + + def foreign_key_options + as_options(foreign_key) + end + + def columns + result = [["#{name}_id", type, options]] + if polymorphic + result.unshift(["#{name}_type", :string, polymorphic_options]) + end + result + end + + def column_names + columns.map(&:first) + end + + def foreign_table_name + name.to_s.pluralize + end + end + # Represents the schema of an SQL table in an abstract way. This class # provides methods for manipulating the schema representation. # @@ -94,11 +172,12 @@ module ActiveRecord # An array of ColumnDefinition objects, representing the column changes # that have been defined. attr_accessor :indexes - attr_reader :name, :temporary, :options, :as + attr_reader :name, :temporary, :options, :as, :foreign_keys def initialize(types, name, temporary, options, as = nil) @columns_hash = {} @indexes = {} + @foreign_keys = {} @native = types @temporary = temporary @options = options @@ -286,6 +365,10 @@ module ActiveRecord indexes[column_name] = options end + def foreign_key(table_name, options = {}) # :nodoc: + foreign_keys[table_name] = options + end + # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and # <tt>:updated_at</tt> to the table. See SchemaStatements#add_timestamps # @@ -297,24 +380,21 @@ module ActiveRecord column(:updated_at, :datetime, options) end - # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided. - # <tt>references</tt> and <tt>belongs_to</tt> are acceptable. The reference column will be an +integer+ - # by default, the <tt>:type</tt> option can be used to specify a different type. + # Adds a reference. Optionally adds a +type+ column, if + # <tt>:polymorphic</tt> option is provided. <tt>references</tt> and + # <tt>belongs_to</tt> are acceptable. The reference column will be an + # +integer+ by default, the <tt>:type</tt> option can be used to specify + # a different type. A foreign key will be created if a +foreign_key+ + # option is passed. # # t.references(:user) # t.references(:user, type: "string") # t.belongs_to(:supplier, polymorphic: true) # # See SchemaStatements#add_reference - def references(*args) - options = args.extract_options! - polymorphic = options.delete(:polymorphic) - index_options = options.delete(:index) - type = options.delete(:type) || :integer + def references(*args, **options) args.each do |col| - column("#{col}_id", type, options) - column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic - index(polymorphic ? %w(type id).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options + ReferenceDefinition.new(col, **options).add_to(self) end end alias :belongs_to :references @@ -520,9 +600,12 @@ module ActiveRecord @base.rename_column(name, column_name, new_column_name) end - # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided. - # <tt>references</tt> and <tt>belongs_to</tt> are acceptable. The reference column will be an +integer+ - # by default, the <tt>:type</tt> option can be used to specify a different type. + # Adds a reference. Optionally adds a +type+ column, if + # <tt>:polymorphic</tt> option is provided. <tt>references</tt> and + # <tt>belongs_to</tt> are acceptable. The reference column will be an + # +integer+ by default, the <tt>:type</tt> option can be used to specify + # a different type. A foreign key will be created if a +foreign_key+ + # option is passed. # # t.references(:user) # t.references(:user, type: "string") @@ -565,6 +648,10 @@ module ActiveRecord end end + def foreign_key(*args) # :nodoc: + @base.add_foreign_key(name, *args) + end + private def native @base.native_database_types 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 b340e8334b..6e42089801 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -204,7 +204,17 @@ module ActiveRecord end result = execute schema_creation.accept td - td.indexes.each_pair { |c, o| add_index(table_name, c, o) } unless supports_indexes_in_create? + + unless supports_indexes_in_create? + td.indexes.each_pair do |column_name, index_options| + add_index(table_name, column_name, index_options) + end + end + + td.foreign_keys.each_pair do |other_table_name, foreign_key_options| + add_foreign_key(table_name, other_table_name, foreign_key_options) + end + result end @@ -627,17 +637,16 @@ module ActiveRecord # # add_belongs_to(:products, :supplier, polymorphic: true) # - # ====== Create a supplier_id, supplier_type columns and appropriate index + # ====== Create supplier_id, supplier_type columns and appropriate index # # add_reference(:products, :supplier, polymorphic: true, index: true) # - def add_reference(table_name, ref_name, options = {}) - polymorphic = options.delete(:polymorphic) - index_options = options.delete(:index) - type = options.delete(:type) || :integer - add_column(table_name, "#{ref_name}_id", type, options) - add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic - add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options + # ====== Create a supplier_id column and appropriate foreign key + # + # add_reference(:products, :supplier, foreign_key: true) + # + def add_reference(table_name, *args) + ReferenceDefinition.new(*args).add_to(update_table_definition(table_name, self)) end alias :add_belongs_to :add_reference diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 6ef47d8a11..02cafc8079 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -445,8 +445,8 @@ module ActiveRecord def initialize_type_map(m) # :nodoc: register_class_with_limit m, 'int2', OID::Integer - m.alias_type 'int4', 'int2' - m.alias_type 'int8', 'int2' + register_class_with_limit m, 'int4', OID::Integer + register_class_with_limit m, 'int8', OID::Integer m.alias_type 'oid', 'int2' m.register_type 'float4', OID::Float.new m.alias_type 'float8', 'float4' diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index c2d5582f02..94d1e07069 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -131,6 +131,7 @@ module ActiveRecord return super if block_given? || primary_key.nil? || default_scopes.any? || + current_scope || columns_hash.include?(inheritance_column) || ids.first.kind_of?(Array) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 8a2a06f2ca..0f9b52f69f 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -81,6 +81,9 @@ module ActiveRecord # # Note that the model will _not_ be destroyed until the parent is saved. # + # Also note that the model will not be destroyed unless you also specify + # its id in the updated hash. + # # === One-to-many # # Consider a member that has a number of posts: diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 4daf2a0e2b..04c2be045d 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -319,7 +319,7 @@ db_namespace = namespace :db do begin should_reconnect = ActiveRecord::Base.connection_pool.active_connection? ActiveRecord::Schema.verbose = false - ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA'] + ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA'] ensure if should_reconnect ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env]) @@ -329,7 +329,7 @@ db_namespace = namespace :db do # desc "Recreate the test database from an existent structure.sql file" task :load_structure => %w(db:test:purge) do - ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :sql, ENV['SCHEMA'] + ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations['test'], :sql, ENV['SCHEMA'] end # desc "Recreate the test database from a fresh schema" diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 6ed610e5f0..69aceb66b1 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -188,27 +188,7 @@ module ActiveRecord class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename) end - def load_schema(format = ActiveRecord::Base.schema_format, file = nil) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - This method will act on a specific connection in the future. - To act on the current connection, use `load_schema_current` instead. - MSG - - load_schema_current(format, file) - end - - def schema_file(format = ActiveSupport::Base.schema_format) - case format - when :ruby - File.join(db_dir, "schema.rb") - when :sql - File.join(db_dir, "structure.sql") - end - end - - # This method is the successor of +load_schema+. We should rename it - # after +load_schema+ went through a deprecation cycle. (Rails > 4.2) - def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc: + def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc: file ||= schema_file(format) case format @@ -224,6 +204,23 @@ module ActiveRecord end end + def load_schema_for(*args) + ActiveSupport::Deprecation.warn(<<-MSG.squish) + This method was renamed to `#load_schema` and will be removed in the future. + Use `#load_schema` instead. + MSG + load_schema(*args) + end + + def schema_file(format = ActiveSupport::Base.schema_format) + case format + when :ruby + File.join(db_dir, "schema.rb") + when :sql + File.join(db_dir, "structure.sql") + end + end + def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env) if File.exist?(file || schema_file(format)) load_schema_current(format, file, environment) @@ -232,7 +229,7 @@ module ActiveRecord def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env) each_current_configuration(environment) { |configuration| - load_schema_for configuration, format, file + load_schema configuration, format, file } ActiveRecord::Base.establish_connection(environment.to_sym) end diff --git a/activerecord/lib/active_record/type/decimal.rb b/activerecord/lib/active_record/type/decimal.rb index d10778eeb6..7b2bee2c42 100644 --- a/activerecord/lib/active_record/type/decimal.rb +++ b/activerecord/lib/active_record/type/decimal.rb @@ -16,7 +16,7 @@ module ActiveRecord def cast_value(value) case value when ::Float - BigDecimal(value, float_precision) + convert_float_to_big_decimal(value) when ::Numeric, ::String BigDecimal(value, precision.to_i) else @@ -28,6 +28,14 @@ module ActiveRecord end end + def convert_float_to_big_decimal(value) + if precision + BigDecimal(value, float_precision) + else + value.to_d + end + end + def float_precision if precision.to_i > ::Float::DIG + 1 ::Float::DIG + 1 diff --git a/activerecord/lib/active_record/type/numeric.rb b/activerecord/lib/active_record/type/numeric.rb index fa43266504..674f996f38 100644 --- a/activerecord/lib/active_record/type/numeric.rb +++ b/activerecord/lib/active_record/type/numeric.rb @@ -29,7 +29,7 @@ module ActiveRecord # 'wibble'.to_i will give zero, we want to make sure # that we aren't marking int zero to string zero as # changed. - value.to_s !~ /\A\d+\.?\d*\z/ + value.to_s !~ /\A-?\d+\.?\d*\z/ end end end diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb index 3191a868ef..3cac03464e 100644 --- a/activerecord/lib/active_record/type/serialized.rb +++ b/activerecord/lib/active_record/type/serialized.rb @@ -29,7 +29,7 @@ module ActiveRecord def changed_in_place?(raw_old_value, value) return false if value.nil? - subtype.changed_in_place?(raw_old_value, coder.dump(value)) + subtype.changed_in_place?(raw_old_value, type_cast_for_database(value)) end def accessor diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb index 9456a4a56c..75679b8692 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -91,8 +91,8 @@ module ActiveRecord # Convenience method for types which do not need separate type casting # behavior for user and database inputs. Called by - # `type_cast_from_database` and `type_cast_from_user` for all values - # except `nil`. + # +type_cast_from_database+ and +type_cast_from_user+ for all values + # except +nil+. def cast_value(value) # :doc: value end diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb index fb0fbb4759..f7bf6987c4 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb @@ -15,8 +15,5 @@ class <%= migration_class_name %> < ActiveRecord::Migration <% attributes_with_index.each do |attribute| -%> add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> <% end -%> -<% attributes.select(&:reference?).reject(&:polymorphic?).each do |attribute| -%> - add_foreign_key :<%= table_name %>, :<%= attribute.name.pluralize %> -<% end -%> end end diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb index 7df9bcad25..ae9c74fd05 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb @@ -4,9 +4,6 @@ class <%= migration_class_name %> < ActiveRecord::Migration <% attributes.each do |attribute| -%> <%- if attribute.reference? -%> add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> - <%- unless attribute.polymorphic? -%> - add_foreign_key :<%= table_name %>, :<%= attribute.name.pluralize %> - <%- end -%> <%- else -%> add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> <%- if attribute.has_index? -%> @@ -29,9 +26,6 @@ class <%= migration_class_name %> < ActiveRecord::Migration <%- if migration_action -%> <%- if attribute.reference? -%> remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> - <%- unless attribute.polymorphic? -%> - remove_foreign_key :<%= table_name %>, :<%= attribute.name.pluralize %> - <%- end -%> <%- else -%> <%- if attribute.has_index? -%> remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> diff --git a/activerecord/test/cases/adapters/postgresql/integer_test.rb b/activerecord/test/cases/adapters/postgresql/integer_test.rb new file mode 100644 index 0000000000..7f8751281e --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/integer_test.rb @@ -0,0 +1,25 @@ +require "cases/helper" +require "active_support/core_ext/numeric/bytes" + +class PostgresqlIntegerTest < ActiveRecord::TestCase + class PgInteger < ActiveRecord::Base + end + + def setup + @connection = ActiveRecord::Base.connection + + @connection.transaction do + @connection.create_table "pg_integers", force: true do |t| + t.integer :quota, limit: 8, default: 2.gigabytes + end + end + end + + teardown do + @connection.execute "drop table if exists pg_integers" + end + + test "schema properly respects bigint ranges" do + assert_equal 2.gigabytes, PgInteger.new.quota + end +end diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb index 87183174f2..54cff192c1 100644 --- a/activerecord/test/cases/adapters/postgresql/money_test.rb +++ b/activerecord/test/cases/adapters/postgresql/money_test.rb @@ -10,7 +10,7 @@ class PostgresqlMoneyTest < ActiveRecord::TestCase setup do @connection = ActiveRecord::Base.connection @connection.execute("set lc_monetary = 'C'") - @connection.create_table('postgresql_moneys') do |t| + @connection.create_table('postgresql_moneys', force: true) do |t| t.column "wealth", "money" t.column "depth", "money", default: "150.55" end diff --git a/activerecord/test/cases/date_time_test.rb b/activerecord/test/cases/date_time_test.rb index c0491bbee5..c2ec92c40d 100644 --- a/activerecord/test/cases/date_time_test.rb +++ b/activerecord/test/cases/date_time_test.rb @@ -3,6 +3,8 @@ require 'models/topic' require 'models/task' class DateTimeTest < ActiveRecord::TestCase + include InTimeZone + def test_saves_both_date_and_time with_env_tz 'America/New_York' do with_timezone_config default: :utc do @@ -29,6 +31,14 @@ class DateTimeTest < ActiveRecord::TestCase assert_nil task.ending end + def test_assign_bad_date_time_with_timezone + in_time_zone "Pacific Time (US & Canada)" do + task = Task.new + task.starting = '2014-07-01T24:59:59GMT' + assert_nil task.starting + end + end + def test_assign_empty_date topic = Topic.new topic.last_read = '' diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index eb9b1a2d74..1eaff5e293 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -698,6 +698,21 @@ class DirtyTest < ActiveRecord::TestCase assert binary.changed? end + test "attribute_changed? doesn't compute in-place changes for unrelated attributes" do + test_type_class = Class.new(ActiveRecord::Type::Value) do + define_method(:changed_in_place?) do |*| + raise + end + end + klass = Class.new(ActiveRecord::Base) do + self.table_name = 'people' + attribute :foo, test_type_class.new + end + + model = klass.new(first_name: "Jim") + assert model.first_name_changed? + end + private def with_partial_writes(klass, on = true) old = klass.partial_writes? diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 5c98be342f..02dc5d3ad3 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -15,9 +15,11 @@ require 'models/customer' require 'models/toy' require 'models/matey' require 'models/dog' +require 'models/car' +require 'models/tyre' class FinderTest < ActiveRecord::TestCase - fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers, :categories, :categorizations + fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers, :categories, :categorizations, :cars def test_find_by_id_with_hash assert_raises(ActiveRecord::StatementInvalid) do @@ -1101,6 +1103,26 @@ class FinderTest < ActiveRecord::TestCase end end + test "find on a scope does not perform statement caching" do + honda = cars(:honda) + zyke = cars(:zyke) + tyre = honda.tyres.create! + tyre2 = zyke.tyres.create! + + assert_equal tyre, honda.tyres.custom_find(tyre.id) + assert_equal tyre2, zyke.tyres.custom_find(tyre2.id) + end + + test "find_by on a scope does not perform statement caching" do + honda = cars(:honda) + zyke = cars(:zyke) + tyre = honda.tyres.create! + tyre2 = zyke.tyres.create! + + assert_equal tyre, honda.tyres.custom_find_by(id: tyre.id) + assert_equal tyre2, zyke.tyres.custom_find_by(id: tyre2.id) + end + protected def bind(statement, *vars) if vars.first.is_a?(Hash) diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb new file mode 100644 index 0000000000..bba8897a0d --- /dev/null +++ b/activerecord/test/cases/migration/references_foreign_key_test.rb @@ -0,0 +1,101 @@ +require 'cases/helper' + +if ActiveRecord::Base.connection.supports_foreign_keys? +module ActiveRecord + class Migration + class ReferencesForeignKeyTest < ActiveRecord::TestCase + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table(:testing_parents, force: true) + end + + teardown do + @connection.execute("drop table if exists testings") + @connection.execute("drop table if exists testing_parents") + end + + test "foreign keys can be created with the table" do + @connection.create_table :testings do |t| + t.references :testing_parent, foreign_key: true + end + + fk = @connection.foreign_keys("testings").first + assert_equal "testings", fk.from_table + assert_equal "testing_parents", fk.to_table + end + + test "no foreign key is created by default" do + @connection.create_table :testings do |t| + t.references :testing_parent + end + + assert_equal [], @connection.foreign_keys("testings") + end + + test "options hash can be passed" do + @connection.change_table :testing_parents do |t| + t.integer :other_id + t.index :other_id, unique: true + end + @connection.create_table :testings do |t| + t.references :testing_parent, foreign_key: { primary_key: :other_id } + end + + fk = @connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" } + assert_equal "other_id", fk.primary_key + end + + test "foreign keys cannot be added to polymorphic relations when creating the table" do + @connection.create_table :testings do |t| + assert_raises(ArgumentError) do + t.references :testing_parent, polymorphic: true, foreign_key: true + end + end + end + + test "foreign keys can be created while changing the table" do + @connection.create_table :testings + @connection.change_table :testings do |t| + t.references :testing_parent, foreign_key: true + end + + fk = @connection.foreign_keys("testings").first + assert_equal "testings", fk.from_table + assert_equal "testing_parents", fk.to_table + end + + test "foreign keys are not added by default when changing the table" do + @connection.create_table :testings + @connection.change_table :testings do |t| + t.references :testing_parent + end + + assert_equal [], @connection.foreign_keys("testings") + end + + test "foreign keys accept options when changing the table" do + @connection.change_table :testing_parents do |t| + t.integer :other_id + t.index :other_id, unique: true + end + @connection.create_table :testings + @connection.change_table :testings do |t| + t.references :testing_parent, foreign_key: { primary_key: :other_id } + end + + fk = @connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" } + assert_equal "other_id", fk.primary_key + end + + test "foreign keys cannot be added to polymorphic relations when changing the table" do + @connection.create_table :testings + @connection.change_table :testings do |t| + assert_raises(ArgumentError) do + t.references :testing_parent, polymorphic: true, foreign_key: true + end + end + end + end + end +end +end diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index 98888150a8..287a3f33ea 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -35,6 +35,22 @@ class PooledConnectionsTest < ActiveRecord::TestCase end end + def checkout_checkin_connections_loop(pool_size, loops) + ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5})) + @connection_count = 0 + @timed_out = 0 + loops.times do + begin + conn = ActiveRecord::Base.connection_pool.checkout + ActiveRecord::Base.connection_pool.checkin conn + @connection_count += 1 + ActiveRecord::Base.connection.tables + rescue ActiveRecord::ConnectionTimeoutError + @timed_out += 1 + end + end + end + def test_pooled_connection_checkin_one checkout_checkin_connections 1, 2 assert_equal 2, @connection_count @@ -42,6 +58,20 @@ class PooledConnectionsTest < ActiveRecord::TestCase assert_equal 1, ActiveRecord::Base.connection_pool.connections.size end + def test_pooled_connection_checkin_two + checkout_checkin_connections_loop 2, 3 + assert_equal 3, @connection_count + assert_equal 0, @timed_out + assert_equal 2, ActiveRecord::Base.connection_pool.connections.size + end + + def test_pooled_connection_remove + ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.5})) + old_connection = ActiveRecord::Base.connection + extra_connection = ActiveRecord::Base.connection_pool.checkout + ActiveRecord::Base.connection_pool.remove(extra_connection) + assert_equal ActiveRecord::Base.connection, old_connection + end private diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index 56a0e92e1d..c8441201ca 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -256,4 +256,12 @@ class SerializedAttributeTest < ActiveRecord::TestCase assert_equal("second", t.content) assert_equal("second", t.reload.content) end + + def test_nil_is_not_changed_when_serialized_with_a_class + Topic.serialize(:content, Array) + + topic = Topic.new(content: nil) + + assert_not topic.content_changed? + end end diff --git a/activerecord/test/cases/type/decimal_test.rb b/activerecord/test/cases/type/decimal_test.rb index da30de373e..34ed1d7b19 100644 --- a/activerecord/test/cases/type/decimal_test.rb +++ b/activerecord/test/cases/type/decimal_test.rb @@ -15,6 +15,11 @@ module ActiveRecord assert_equal BigDecimal.new("123.0"), type.type_cast_from_user(123.0) end + def test_type_cast_from_float_with_unspecified_precision + type = Decimal.new + assert_equal 22.68.to_d, type.type_cast_from_user(22.68) + end + def test_type_cast_decimal_from_rational_with_precision type = Decimal.new(precision: 2) assert_equal BigDecimal("0.33"), type.type_cast_from_user(Rational(1, 3)) @@ -33,6 +38,14 @@ module ActiveRecord type = Decimal.new assert_equal BigDecimal("1"), type.type_cast_from_user(value) end + + def test_changed? + type = Decimal.new + + assert type.changed?(5.0, 5.0, '5.0wibble') + assert_not type.changed?(5.0, 5.0, '5.0') + assert_not type.changed?(-5.0, -5.0, '-5.0') + end end end end diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb index 5942f77e18..af4d0b4642 100644 --- a/activerecord/test/cases/type/integer_test.rb +++ b/activerecord/test/cases/type/integer_test.rb @@ -47,6 +47,8 @@ module ActiveRecord assert type.changed?(5, 5, '5wibble') assert_not type.changed?(5, 5, '5') assert_not type.changed?(5, 5, '5.0') + assert_not type.changed?(-5, -5, '-5') + assert_not type.changed?(-5, -5, '-5.0') assert_not type.changed?(nil, nil, nil) end diff --git a/activerecord/test/models/tyre.rb b/activerecord/test/models/tyre.rb index bc3444aa7d..e50a21ca68 100644 --- a/activerecord/test/models/tyre.rb +++ b/activerecord/test/models/tyre.rb @@ -1,3 +1,11 @@ class Tyre < ActiveRecord::Base belongs_to :car + + def self.custom_find(id) + find(id) + end + + def self.custom_find_by(*args) + find_by(*args) + end end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 3f340958ea..f0ccdb1f21 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,4 +1,4 @@ -* Added support for error dispatcher classes in ActiveSupport::Rescuable. Now it acts closer to Ruby's rescue. +* Added support for error dispatcher classes in `ActiveSupport::Rescuable`. Now it acts closer to Ruby's rescue. class BaseController < ApplicationController module ErrorDispatcher diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb index 688f177578..c1968af64a 100644 --- a/guides/rails_guides/markdown/renderer.rb +++ b/guides/rails_guides/markdown/renderer.rb @@ -23,8 +23,9 @@ HTML end def paragraph(text) - if text =~ /^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)/ + if text =~ /^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:]/ convert_notes(text) + elsif text.include?('DO NOT READ THIS FILE IN GITHUB') elsif text =~ /^\[<sup>(\d+)\]:<\/sup> (.+)$/ linkback = %(<a href="#footnote-#{$1}-ref"><sup>#{$1}</sup></a>) %(<p class="footnote" id="footnote-#{$1}">#{linkback} #{$2}</p>) diff --git a/guides/source/2_2_release_notes.md b/guides/source/2_2_release_notes.md index b11aaa15a8..019da08687 100644 --- a/guides/source/2_2_release_notes.md +++ b/guides/source/2_2_release_notes.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails 2.2 Release Notes =============================== diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md index 20566c9155..4ac1529e76 100644 --- a/guides/source/2_3_release_notes.md +++ b/guides/source/2_3_release_notes.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails 2.3 Release Notes =============================== diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md index e985f1ab4b..3d7966e50b 100644 --- a/guides/source/3_0_release_notes.md +++ b/guides/source/3_0_release_notes.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails 3.0 Release Notes =============================== diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md index b7ed285b96..8728750966 100644 --- a/guides/source/3_1_release_notes.md +++ b/guides/source/3_1_release_notes.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails 3.1 Release Notes =============================== diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md index c5db0262e9..0b28aac9ce 100644 --- a/guides/source/3_2_release_notes.md +++ b/guides/source/3_2_release_notes.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails 3.2 Release Notes =============================== diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md index 84a65df2bc..5f52c33746 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails 4.0 Release Notes =============================== diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md index c7877a9cb5..dbc151c0ca 100644 --- a/guides/source/4_1_release_notes.md +++ b/guides/source/4_1_release_notes.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails 4.1 Release Notes =============================== diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index f5575ac523..e8ddfcc9e2 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails 4.2 Release Notes =============================== diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 57546da389..826d25d173 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Action Controller Overview ========================== diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index ba7c16aa1d..9a5204848b 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Action Mailer Basics ==================== diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index f4d5bb8272..a6bde4f517 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Action View Overview ==================== diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index 3657d7cad7..63b4409c6c 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Job Basics ================= diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md index a520b91a4d..8dee1cc5ec 100644 --- a/guides/source/active_model_basics.md +++ b/guides/source/active_model_basics.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Model Basics =================== diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md index ef86531eef..9d2ba328ea 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Record Basics ==================== diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index 9c7e60cbb0..9d3a8c3af6 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Record Callbacks ======================= diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md index e76a57e164..8ae282bad2 100644 --- a/guides/source/active_record_migrations.md +++ b/guides/source/active_record_migrations.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Record Migrations ======================== diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md index 446be1c6d1..fa0f31cbbd 100644 --- a/guides/source/active_record_postgresql.md +++ b/guides/source/active_record_postgresql.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Record and PostgreSQL ============================ diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 2e7bb74d0b..9c91d6d40b 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Record Query Interface ============================= @@ -1382,16 +1384,12 @@ WHERE people.name = 'John' LIMIT 1 ``` -NOTE: Remember that, if `find_by` return more than one registry, it will take just the first -and ignore the others. Note the `LIMIT 1` statement above. +NOTE: Remember that, if `find_by` return more than one registry, it will take just the first and ignore the others. Note the `LIMIT 1` statement above. Find or Build a New Object -------------------------- -NOTE: Some dynamic finders were deprecated in Rails 4.0 and -removed in Rails 4.1. The best practice is to use Active Record scopes -instead. You can find the deprecation gem at -https://github.com/rails/activerecord-deprecated_finders +NOTE: Some dynamic finders were deprecated in Rails 4.0 and removed in Rails 4.1. The best practice is to use Active Record scopes instead. You can find the deprecation gem at https://github.com/rails/activerecord-deprecated_finders It's common that you need to find a record or create it if it doesn't exist. You can do that with the `find_or_create_by` and `find_or_create_by!` methods. diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index 7628b8278d..8c832bafff 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Record Validations ========================= diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index faad34d021..c857f30541 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Support Core Extensions ============================== diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index 9dfacce560..0aa74e387d 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Support Instrumentation ============================== diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index a2ebf55335..d481700709 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + API Documentation Guidelines ============================ diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 156daf1bac..6f37d9690e 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + The Asset Pipeline ================== diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index 5c05f0c4b7..2fa76cfe53 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Record Associations ========================== diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md index c39210383b..d14b8d9011 100644 --- a/guides/source/autoloading_and_reloading_constants.md +++ b/guides/source/autoloading_and_reloading_constants.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Autoloading and Reloading Constants =================================== diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index cbcd053950..cf6b5d08e6 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Caching with Rails: An overview =============================== diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 713c91d167..d44782ecf3 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + The Rails Command Line ====================== diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 0c730acb60..3a2b4abcd5 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Configuring Rails Applications ============================== @@ -197,7 +199,7 @@ The full set of methods that can be used in this block are as follows: Every Rails application comes with a standard set of middleware which it uses in this order in the development environment: * `ActionDispatch::SSL` forces every request to be under HTTPS protocol. Will be available if `config.force_ssl` is set to `true`. Options passed to this can be configured by using `config.ssl_options`. -* `ActionDispatch::Static` is used to serve static assets. Disabled if `config.serve_static_assets` is `false`. +* `ActionDispatch::Static` is used to serve static assets. Disabled if `config.serve_static_files` is `false`. * `Rack::Lock` wraps the app in mutex so it can only be called by a single thread at a time. Only enabled when `config.cache_classes` is `false`. * `ActiveSupport::Cache::Strategy::LocalCache` serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. * `Rack::Runtime` sets an `X-Runtime` header, containing the time (in seconds) taken to execute the request. diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index 17afd07820..d9dd4d8373 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Contributing to Ruby on Rails ============================= @@ -287,7 +289,12 @@ $ ruby -w -Itest test/mail_layout_test.rb -n test_explicit_class_layout The `-n` option allows you to run a single method instead of the whole file. -##### Testing Active Record +#### Testing Active Record + +First, create the databases you'll need. For MySQL and PostgreSQL, +running the SQL statements `create database activerecord_unittest` and +`create database activerecord_unittest2` is sufficient. This is not +necessary for SQLite3. This is how you run the Active Record test suite only for SQLite3: diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index 1a647f8375..4886a0245a 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Debugging Rails Applications ============================ diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index 3d9ec578ae..9eacc3a2fe 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Development Dependencies Install ================================ diff --git a/guides/source/engines.md b/guides/source/engines.md index a1f2da18ed..731178787f 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Getting Started with Engines ============================ diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index f3f7415b3c..a8dcd3ee4f 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Form Helpers ============ diff --git a/guides/source/generators.md b/guides/source/generators.md index f5d2c67cb4..be513dc0ef 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Creating and Customizing Rails Generators & Templates ===================================================== diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index e68e07a519..1516f965ca 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Getting Started with Rails ========================== diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 75b5275245..bd6babff41 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Rails Internationalization (I18n) API ===================================== diff --git a/guides/source/initialization.md b/guides/source/initialization.md index 53bf3039fa..0acf094f71 100644 --- a/guides/source/initialization.md +++ b/guides/source/initialization.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + The Rails Initialization Process ================================ diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index 28fa61a964..6bf17b4a98 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Layouts and Rendering in Rails ============================== @@ -425,6 +427,9 @@ Rails understands both numeric status codes and the corresponding symbols shown | | 510 | :not_extended | | | 511 | :network_authentication_required | +NOTE: If you try to render content along with a non-content status code +(100-199, 204, 205 or 304), it will be dropped from the response. + #### Finding Layouts To find the current layout, Rails first looks for a file in `app/views/layouts` with the same base name as the controller. For example, rendering actions from the `PhotosController` class will use `app/views/layouts/photos.html.erb` (or `app/views/layouts/photos.builder`). If there is no such controller-specific layout, Rails will use `app/views/layouts/application.html.erb` or `app/views/layouts/application.builder`. If there is no `.erb` layout, Rails will use a `.builder` layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions. diff --git a/guides/source/maintenance_policy.md b/guides/source/maintenance_policy.md index 050a64ddf3..45cdc549f7 100644 --- a/guides/source/maintenance_policy.md +++ b/guides/source/maintenance_policy.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Maintenance Policy for Ruby on Rails ==================================== diff --git a/guides/source/nested_model_forms.md b/guides/source/nested_model_forms.md index f0ee34cfb1..44236ad239 100644 --- a/guides/source/nested_model_forms.md +++ b/guides/source/nested_model_forms.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Rails Nested Model Forms ======================== diff --git a/guides/source/plugins.md b/guides/source/plugins.md index 7b7eb80081..bd884441ac 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + The Basics of Creating Rails Plugins ==================================== @@ -433,7 +435,7 @@ Once your README is solid, go through and add rdoc comments to all of the method Once your comments are good to go, navigate to your plugin directory and run: ```bash -$ bin/rake rdoc +$ bundle exec rake rdoc ``` ### References diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md index 6512b14e60..0db777b9bb 100644 --- a/guides/source/rails_application_templates.md +++ b/guides/source/rails_application_templates.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Rails Application Templates =========================== diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index 042ebde9bb..bfe4ced87b 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Rails on Rack ============= diff --git a/guides/source/routing.md b/guides/source/routing.md index b1a287f53a..893cedeefc 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Rails Routing from the Outside In ================================= diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md index c0438f6341..0ecf8a80df 100644 --- a/guides/source/ruby_on_rails_guides_guidelines.md +++ b/guides/source/ruby_on_rails_guides_guidelines.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails Guides Guidelines =============================== diff --git a/guides/source/security.md b/guides/source/security.md index b3869b1ba5..4a80edbdad 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Ruby on Rails Security Guide ============================ @@ -237,7 +239,7 @@ Or the attacker places the code into the onmouseover event handler of an image: <img src="http://www.harmless.com/img" width="400" height="400" onmouseover="..." /> ``` -There are many other possibilities, like using a `<script>` tag to make a cross-site request to a URL with a JSONP or JavaScript response. The response is executable code that the attacker can find a way to run, possibly extracting sensitive data. To protect against this data leakage, we disallow cross-site `<script>` tags. Only Ajax requests may have JavaScript responses since XmlHttpRequest is subject to the browser Same-Origin policy - meaning only your site can initiate the request. +There are many other possibilities, like using a `<script>` tag to make a cross-site request to a URL with a JSONP or JavaScript response. The response is executable code that the attacker can find a way to run, possibly extracting sensitive data. To protect against this data leakage, we disallow cross-site `<script>` tags. Only Ajax requests may have JavaScript responses since `XMLHttpRequest` is subject to the browser Same-Origin policy - meaning only your site can initiate the request. To protect against all other forged requests, we introduce a _required security token_ that our site knows but other sites don't know. We include the security token in requests and verify it on the server. This is a one-liner in your application controller, and is the default for newly created rails applications: @@ -247,6 +249,8 @@ protect_from_forgery with: :exception This will automatically include a security token in all forms and Ajax requests generated by Rails. If the security token doesn't match what was expected, an exception will be thrown. +NOTE: By default, Rails includes jQuery and an [unobtrusive scripting adapter for jQuery](https://github.com/rails/jquery-ujs), which adds a header called `X-CSRF-Token` on every non-GET Ajax call made by jQuery with the security token. Without this header, non-GET Ajax requests won't be accepted by Rails. When using another library to make Ajax calls, it is necessary to add the security token as a default header for Ajax calls in your library. To get the token, have a look at `<meta name='csrf-token' content='THE-TOKEN'>` tag printed by `<%= csrf_meta_tags %>` in your application view. + It is common to use persistent cookies to store user information, with `cookies.permanent` for example. In this case, the cookies will not be cleared and the out of the box CSRF protection will not be effective. If you are using a different cookie store than the session for this information, you must handle what to do with it yourself: ```ruby diff --git a/guides/source/testing.md b/guides/source/testing.md index c8a87c57e8..c762379cca 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + A Guide to Testing Rails Applications ===================================== @@ -43,7 +45,7 @@ controllers/ helpers/ mailers/ test_helper.rb fixtures/ integration/ models/ ``` -The `models` directory is meant to hold tests for your models, the `controllers` directory is meant to hold tests for your controllers and the `integration` directory is meant to hold tests that involve any number of controllers interacting. There is also a directory for testing your mailers, and helper methods which you are used within the view layer. +The `models` directory is meant to hold tests for your models, the `controllers` directory is meant to hold tests for your controllers and the `integration` directory is meant to hold tests that involve any number of controllers interacting. There is also a directory for testing your mailers and one for testing view helpers. Fixtures are a way of organizing test data; they reside in the `fixtures` directory. @@ -126,7 +128,7 @@ TIP: In order to remove existing data from the database, Rails tries to disable #### Fixtures are Active Record objects -Fixtures are instances of Active Record. As mentioned in point #3 above, you can access the object directly because it is automatically available as a method who's scope is local of the test case. For example: +Fixtures are instances of Active Record. As mentioned in point #3 above, you can access the object directly because it is automatically available as a method whose scope is local of the test case. For example: ```ruby # this will return the User object for the fixture named david @@ -144,11 +146,9 @@ Unit Testing your Models In Rails, unit tests are what you write to test your models. -For this guide we will be using Rails _scaffolding_. It will create the model, a migration, controller and views for the new resource in a single operation. It will also create a full test suite following Rails best practices. We will be using examples from this generated code and will be supplementing it with additional examples where necessary. - -NOTE: For more information on Rails _scaffolding_, refer to [Getting Started with Rails](getting_started.html) +For this guide we will be using the application we built in the [Getting Started with Rails](getting_started.html) guide. -When you use `rails generate scaffold`, for a resource among other things it creates a test stub in the `test/models` directory: +If you remember when you used the `rails generate scaffold` command from earlier. We created our first resource among other things it created a test stub in the `test/models` directory: ```bash $ bin/rails generate scaffold article title:string body:text @@ -177,18 +177,18 @@ A line by line examination of this file will help get you oriented to Rails test require 'test_helper' ``` -As you know by now, `test_helper.rb` specifies the default configuration to run our tests. This is included with all the tests, so any methods added to this file are available to all your tests. +By requiring this file, `test_helper.rb` the default configuration to run our tests is loaded. We will include this with all the tests we write, so any methods added to this file are available to all your tests. ```ruby class ArticleTest < ActiveSupport::TestCase ``` -The `ArticleTest` class defines a _test case_ because it inherits from `ActiveSupport::TestCase`. `ArticleTest` thus has all the methods available from `ActiveSupport::TestCase`. You'll see those methods a little later in this guide. +The `ArticleTest` class defines a _test case_ because it inherits from `ActiveSupport::TestCase`. `ArticleTest` thus has all the methods available from `ActiveSupport::TestCase`. Later in this guide, you'll see some of the methods it gives you. Any method defined within a class inherited from `Minitest::Test` -(which is the superclass of `ActiveSupport::TestCase`) that begins with `test_` (case sensitive) is simply called a test. So, `test_password` and `test_valid_password` are legal test names and are run automatically when the test case is run. +(which is the superclass of `ActiveSupport::TestCase`) that begins with `test_` (case sensitive) is simply called a test. So, methods defined as `test_password` and `test_valid_password` are legal test names and are run automatically when the test case is run. -Rails adds a `test` method that takes a test name and a block. It generates a normal `Minitest::Unit` test with method names prefixed with `test_`. So, +Rails also adds a `test` method that takes a test name and a block. It generates a normal `Minitest::Unit` test with method names prefixed with `test_`. So you don't have to worry about naming the methods, and you can write something like: ```ruby test "the truth" do @@ -196,7 +196,7 @@ test "the truth" do end ``` -acts as if you had written +Which is approximately the same as writing this: ```ruby def test_the_truth @@ -204,26 +204,37 @@ def test_the_truth end ``` -only the `test` macro allows a more readable test name. You can still use regular method definitions though. +However only the `test` macro allows a more readable test name. You can still use regular method definitions though. -NOTE: The method name is generated by replacing spaces with underscores. The result does not need to be a valid Ruby identifier though, the name may contain punctuation characters etc. That's because in Ruby technically any string may be a method name. Odd ones need `define_method` and `send` calls, but formally there's no restriction. +NOTE: The method name is generated by replacing spaces with underscores. The result does not need to be a valid Ruby identifier though, the name may contain punctuation characters etc. That's because in Ruby technically any string may be a method name. This may require use of `define_method` and `send` calls to function properly, but formally there's little restriction on the name. + +Next, let's look at our first assertion: ```ruby assert true ``` -This line of code is called an _assertion_. An assertion is a line of code that evaluates an object (or expression) for expected results. For example, an assertion can check: +An assertion is a line of code that evaluates an object (or expression) for expected results. For example, an assertion can check: * does this value = that value? * is this object nil? * does this line of code throw an exception? * is the user's password greater than 5 characters? -Every test contains one or more assertions. Only when all the assertions are successful will the test pass. +Every test must contain at least one assertion, with no restriction as to how many assertions are allowed. Only when all the assertions are successful will the test pass. ### Maintaining the test database schema -In order to run your tests, your test database will need to have the current structure. The test helper checks whether your test database has any pending migrations. If so, it will try to load your `db/schema.rb` or `db/structure.sql` into the test database. If migrations are still pending, an error will be raised. +In order to run your tests, your test database will need to have the current +structure. The test helper checks whether your test database has any pending +migrations. If so, it will try to load your `db/schema.rb` or `db/structure.sql` +into the test database. If migrations are still pending, an error will be +raised. Usually this indicates that your schema is not fully migrated. Running +the migrations against the development database (`bin/rake db:migrate`) will +bring the schema up to date. + +NOTE: If existing migrations required modifications, the test database needs to +be rebuilt. This can be done by executing `bin/rake db:test:prepare`. ### Running Tests @@ -249,10 +260,12 @@ Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s. 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips ``` -This will run all test methods from the test case. Note that `test_helper.rb` is in the `test` directory, hence this directory needs to be added to the load path using the `-I` switch. +This will run all test methods from the test case. The `.` (dot) above indicates a passing test. When a test fails you see an `F`; when a test throws an error you see an `E` in its place. The last line of the output is the summary. +#### Your first failing test + To see how a test failure is reported, you can add a failing test to the `article_test.rb` test case. ```ruby @@ -315,7 +328,7 @@ Finished tests in 0.047721s, 20.9551 tests/s, 20.9551 assertions/s. Now, if you noticed, we first wrote a test which fails for a desired functionality, then we wrote some code which adds the functionality and finally we ensured that our test passes. This approach to software development is referred to as _Test-Driven Development_ (TDD). -TIP: Many Rails developers practice _Test-Driven Development_ (TDD). This is an excellent way to build up a test suite that exercises every part of your application. TDD is beyond the scope of this guide, but one place to start is with [15 TDD steps to create a Rails application](http://andrzejonsoftware.blogspot.com/2007/05/15-tdd-steps-to-create-rails.html). +#### What an error looks like To see how an error gets reported, here's a test containing an error: @@ -358,10 +371,6 @@ behavior: $ BACKTRACE=1 bin/rake test test/models/article_test.rb ``` -### What to Include in Your Unit Tests - -Ideally, you would like to include a test for everything which could possibly break. It's a good practice to have at least one test for each of your validations and at least one test for every method in your model. - ### Available Assertions By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned. diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 51c144993c..a98e17a651 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + A Guide for Upgrading Ruby on Rails =================================== @@ -1110,7 +1112,7 @@ You can help test performance with these additions to your test environment: ```ruby # Configure static asset server for tests with Cache-Control for performance -config.serve_static_assets = true +config.serve_static_files = true config.static_cache_control = 'public, max-age=3600' ``` diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 7c3fd9f69d..5131f809d7 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Working with JavaScript in Rails ================================ diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index f8bd6096f2..ad8b52a39f 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -368,7 +368,21 @@ module Rails @config = configuration end - def secrets #:nodoc: + # Returns secrets added to config/secrets.yml. + # + # Example: + # + # development: + # secret_key_base: 836fa3665997a860728bcb9e9a1e704d427cfc920e79d847d79c8a9a907b9e965defa4154b2b86bdec6930adbe33f21364523a6f6ce363865724549fdfc08553 + # test: + # secret_key_base: 5a37811464e7d378488b0f073e2193b093682e4e21f5d6f3ae0a4e1781e61a351fdc878a843424e81c73fb484a40d23f92c8dafac4870e74ede6e5e174423010 + # production: + # secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> + # namespace: my_app_production + # + # +Rails.application.secrets.namespace+ returns +my_app_production+ in the + # production environment. + def secrets @secrets ||= begin secrets = ActiveSupport::OrderedOptions.new yaml = config.paths["config/secrets"].first diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb index f16bd8e082..77a3dbf9a2 100644 --- a/railties/lib/rails/generators/generated_attribute.rb +++ b/railties/lib/rails/generators/generated_attribute.rb @@ -159,6 +159,10 @@ module Rails options.delete(:required) options[:null] = false end + + if reference? && !polymorphic? + options[:foreign_key] = true + end end end end diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index f8a8ae90d9..1ff1f970b5 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -338,7 +338,7 @@ module Rails # # This class should be called before the AppGenerator is required and started # since it configures and mutates ARGV correctly. - class ARGVScrubber # :nodoc + class ARGVScrubber # :nodoc: def initialize(argv = ARGV) @argv = argv end diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index c5bd98a30e..ecaec618dc 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -22,7 +22,7 @@ source 'https://rubygems.org' # gem 'capistrano-rails', group: :development group :development, :test do -<% unless defined?(JRUBY_VERSION) -%> +<% if RUBY_ENGINE == 'ruby' -%> <%- if RUBY_VERSION < '2.0.0' -%> # Call 'debugger' anywhere in the code to stop execution and get a debugger console gem 'debugger' diff --git a/railties/lib/rails/generators/rails/plugin/templates/Gemfile b/railties/lib/rails/generators/rails/plugin/templates/Gemfile index 35ad9fbf9e..ab8b8925eb 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/Gemfile +++ b/railties/lib/rails/generators/rails/plugin/templates/Gemfile @@ -37,7 +37,7 @@ end <% end -%> <% end -%> -<% unless defined?(JRUBY_VERSION) -%> +<% if RUBY_ENGINE == 'ruby' -%> # To use a debugger <%- if RUBY_VERSION < '2.0.0' -%> # gem 'debugger', group: [:development, :test] diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 5f9f7ad50a..3bda924570 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -340,7 +340,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_inclusion_of_a_debugger run_generator - if defined?(JRUBY_VERSION) + if defined?(JRUBY_VERSION) || RUBY_ENGINE == "rbx" assert_file "Gemfile" do |content| assert_no_match(/byebug/, content) assert_no_match(/debugger/, content) diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb index c2c8e2abad..413d457d54 100644 --- a/railties/test/generators/migration_generator_test.rb +++ b/railties/test/generators/migration_generator_test.rb @@ -91,8 +91,9 @@ class MigrationGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/#{migration}.rb" do |content| assert_method :change, content do |change| - assert_match(/remove_foreign_key :books, :authors/, change) - assert_no_match(/remove_foreign_key :books, :distributors/, change) + assert_match(/remove_reference :books, :author,.*\sforeign_key: true/, change) + assert_match(/remove_reference :books, :distributor/, change) # sanity check + assert_no_match(/remove_reference :books, :distributor,.*\sforeign_key: true/, change) end end end @@ -189,8 +190,9 @@ class MigrationGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/#{migration}.rb" do |content| assert_method :change, content do |change| - assert_match(/add_foreign_key :books, :authors/, change) - assert_no_match(/add_foreign_key :books, :distributors/, change) + assert_match(/add_reference :books, :author,.*\sforeign_key: true/, change) + assert_match(/add_reference :books, :distributor/, change) # sanity check + assert_no_match(/add_reference :books, :distributor,.*\sforeign_key: true/, change) end end end diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index ac7a0acf6b..9dc438fe3c 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -407,13 +407,23 @@ class ModelGeneratorTest < Rails::Generators::TestCase end end + def test_foreign_key_is_not_added_for_non_references + run_generator ["account", "supplier:string"] + + assert_migration "db/migrate/create_accounts.rb" do |m| + assert_method :change, m do |up| + assert_no_match(/foreign_key/, up) + end + end + end + def test_foreign_key_is_added_for_references run_generator ["account", "supplier:belongs_to", "user:references"] assert_migration "db/migrate/create_accounts.rb" do |m| assert_method :change, m do |up| - assert_match(/add_foreign_key :accounts, :suppliers/, up) - assert_match(/add_foreign_key :accounts, :users/, up) + assert_match(/t\.belongs_to :supplier,.*\sforeign_key: true/, up) + assert_match(/t\.references :user,.*\sforeign_key: true/, up) end end end @@ -423,7 +433,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/create_accounts.rb" do |m| assert_method :change, m do |up| - assert_no_match(/add_foreign_key :accounts, :suppliers/, up) + assert_no_match(/foreign_key/, up) end end end diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index dbc87be614..95a554adef 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -71,7 +71,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase def test_inclusion_of_a_debugger run_generator [destination_root, '--full'] - if defined?(JRUBY_VERSION) + if defined?(JRUBY_VERSION) || RUBY_ENGINE == "rbx" assert_file "Gemfile" do |content| assert_no_match(/byebug/, content) assert_no_match(/debugger/, content) |