diff options
70 files changed, 335 insertions, 171 deletions
@@ -14,7 +14,7 @@ gem 'rack-cache', '~> 1.2' gem 'jquery-rails', '~> 4.0.0.beta2' gem 'coffee-rails', '~> 4.1.0' gem 'turbolinks' -gem 'arel', '~> 6.0.0.beta2' +gem 'arel', github: 'rails/arel' # require: false so bcrypt is loaded only when has_secure_password is used. # This is to avoid ActiveModel (and by extension the entire framework) diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc index b4eb40f54d..a4e660d621 100644 --- a/actionmailer/README.rdoc +++ b/actionmailer/README.rdoc @@ -74,14 +74,17 @@ Or you can just chain the methods together like: == Setting defaults -It is possible to set default values that will be used in every method in your Action Mailer class. -To implement this functionality, you just call the public class method <tt>default</tt> which you get for free from -<tt>ActionMailer::Base</tt>. This method accepts a Hash as the parameter. You can use any of the headers, email messages -have, like <tt>:from</tt> as the key. You can also pass in a string as the key, like "Content-Type", but Action Mailer -does this out of the box for you, so you won't need to worry about that. -Finally, it is also possible to pass in a Proc that will get evaluated when it is needed. - -Note that every value you set with this method will get overwritten if you use the same key in your mailer method. +It is possible to set default values that will be used in every method in your +Action Mailer class. To implement this functionality, you just call the public +class method +default+ which you get for free from <tt>ActionMailer::Base</tt>. +This method accepts a Hash as the parameter. You can use any of the headers, +email messages have, like +:from+ as the key. You can also pass in a string as +the key, like "Content-Type", but Action Mailer does this out of the box for you, +so you won't need to worry about that. Finally, it is also possible to pass in a +Proc that will get evaluated when it is needed. + +Note that every value you set with this method will get overwritten if you use the +same key in your mailer method. Example: @@ -92,10 +95,11 @@ Example: == Receiving emails -To receive emails, you need to implement a public instance method called <tt>receive</tt> that takes an -email object as its single parameter. The Action Mailer framework has a corresponding class method, -which is also called <tt>receive</tt>, that accepts a raw, unprocessed email as a string, which it then turns -into the email object and calls the receive instance method. +To receive emails, you need to implement a public instance method called ++receive+ that takes an email object as its single parameter. The Action Mailer +framework has a corresponding class method, which is also called +receive+, that +accepts a raw, unprocessed email as a string, which it then turns into the email +object and calls the receive instance method. Example: @@ -116,13 +120,14 @@ Example: end end -This Mailman can be the target for Postfix or other MTAs. In Rails, you would use the runner in the -trivial case like this: +This Mailman can be the target for Postfix or other MTAs. In Rails, you would use +the runner in the trivial case like this: rails runner 'Mailman.receive(STDIN.read)' -However, invoking Rails in the runner for each mail to be received is very resource intensive. A single -instance of Rails should be run within a daemon, if it is going to process more than just a limited amount of email. +However, invoking Rails in the runner for each mail to be received is very +resource intensive. A single instance of Rails should be run within a daemon, if +it is going to process more than just a limited amount of email. == Configuration diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb index c9ef3a3dad..1e13b3761f 100644 --- a/actionpack/lib/action_controller/metal/live.rb +++ b/actionpack/lib/action_controller/metal/live.rb @@ -102,7 +102,7 @@ module ActionController end end - message = json.gsub("\n", "\ndata: ") + message = json.gsub(/\n/, "\ndata: ") @stream.write "data: #{message}\n\n" end end diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb index ff72592b94..798c087d64 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb @@ -35,7 +35,7 @@ module ActionDispatch if env['action_dispatch.show_detailed_exceptions'] request = Request.new(env) - traces = traces_from_wrapper(wrapper) + traces = wrapper.traces trace_to_show = 'Application Trace' if traces[trace_to_show].empty? @@ -106,33 +106,5 @@ module ActionDispatch ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes) end end - - # Augment the exception traces by providing ids for all unique stack frame - def traces_from_wrapper(wrapper) - application_trace = wrapper.application_trace - framework_trace = wrapper.framework_trace - full_trace = wrapper.full_trace - - appplication_trace_with_ids = [] - framework_trace_with_ids = [] - full_trace_with_ids = [] - - if full_trace - full_trace.each_with_index do |trace, idx| - id_trace = { - id: idx, - trace: trace - } - appplication_trace_with_ids << id_trace if application_trace.include? trace - framework_trace_with_ids << id_trace if framework_trace.include? trace - full_trace_with_ids << id_trace - end - end - { - "Application Trace" => appplication_trace_with_ids, - "Framework Trace" => framework_trace_with_ids, - "Full Trace" => full_trace_with_ids - } - end end end diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index a6285848b5..e0140b0692 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -57,6 +57,28 @@ module ActionDispatch clean_backtrace(:all) end + def traces + appplication_trace_with_ids = [] + framework_trace_with_ids = [] + full_trace_with_ids = [] + + if full_trace + full_trace.each_with_index do |trace, idx| + trace_with_id = { id: idx, trace: trace } + + appplication_trace_with_ids << trace_with_id if application_trace.include?(trace) + framework_trace_with_ids << trace_with_id if framework_trace.include?(trace) + full_trace_with_ids << trace_with_id + end + end + + { + "Application Trace" => appplication_trace_with_ids, + "Framework Trace" => framework_trace_with_ids, + "Full Trace" => full_trace_with_ids + } + end + def self.status_code_for_exception(class_name) Rack::Utils.status_code(@@rescue_responses[class_name]) end diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb index 385d57a7a5..b2038576a2 100644 --- a/actionview/lib/action_view/helpers/tag_helper.rb +++ b/actionview/lib/action_view/helpers/tag_helper.rb @@ -123,7 +123,7 @@ module ActionView # cdata_section("hello]]>world") # # => <![CDATA[hello]]]]><![CDATA[>world]]> def cdata_section(content) - splitted = content.to_s.gsub(']]>', ']]]]><![CDATA[>') + splitted = content.to_s.gsub(/\]\]\>/, ']]]]><![CDATA[>') "<![CDATA[#{splitted}]]>".html_safe end diff --git a/actionview/lib/action_view/template/handlers/raw.rb b/actionview/lib/action_view/template/handlers/raw.rb index 0c0d1fffcb..397c86014a 100644 --- a/actionview/lib/action_view/template/handlers/raw.rb +++ b/actionview/lib/action_view/template/handlers/raw.rb @@ -2,7 +2,7 @@ module ActionView module Template::Handlers class Raw def call(template) - escaped = template.source.gsub(':', '\:') + escaped = template.source.gsub(/:/, '\:') '%q:' + escaped + ':;' end diff --git a/activejob/lib/active_job/callbacks.rb b/activejob/lib/active_job/callbacks.rb index 29e2a878b4..c4ceb484cc 100644 --- a/activejob/lib/active_job/callbacks.rb +++ b/activejob/lib/active_job/callbacks.rb @@ -22,6 +22,8 @@ module ActiveJob define_callbacks :enqueue end + # These methods will be included into any Active Job object, adding + # callbacks for +perform+ and +enqueue+ methods. module ClassMethods # Defines a callback that will get called right before the # job's perform method is executed. diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb index 68d46e6466..a0e55a0028 100644 --- a/activejob/lib/active_job/core.rb +++ b/activejob/lib/active_job/core.rb @@ -17,6 +17,8 @@ module ActiveJob attr_writer :queue_name end + # These methods will be included into any Active Job object, adding + # helpers for de/serialization and creation of job instances. module ClassMethods # Creates a new job instance from a hash created with +serialize+ def deserialize(job_data) diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb index 53d944021a..430c17e1bf 100644 --- a/activejob/lib/active_job/enqueuing.rb +++ b/activejob/lib/active_job/enqueuing.rb @@ -4,6 +4,7 @@ module ActiveJob module Enqueuing extend ActiveSupport::Concern + # Includes the +perform_later+ method for job initialization. module ClassMethods # Push a job onto the queue. The arguments must be legal JSON types # (string, int, float, nil, true, false, hash or array) or diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb index 7ff857206d..79d232da4a 100644 --- a/activejob/lib/active_job/execution.rb +++ b/activejob/lib/active_job/execution.rb @@ -6,6 +6,7 @@ module ActiveJob extend ActiveSupport::Concern include ActiveSupport::Rescuable + # Includes methods for executing and performing jobs instantly. module ClassMethods # Performs the job immediately. # diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb index 6e703faaa7..21d2fda3ff 100644 --- a/activejob/lib/active_job/logging.rb +++ b/activejob/lib/active_job/logging.rb @@ -75,7 +75,7 @@ module ActiveJob def perform(event) info do job = event.payload[:job] - "Performed #{job.class.name} from #{queue_name(event)} in #{event.duration.round(2).to_s}ms" + "Performed #{job.class.name} from #{queue_name(event)} in #{event.duration.round(2)}ms" end end diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb index 4bd28522ab..85d7c44bb8 100644 --- a/activejob/lib/active_job/queue_adapter.rb +++ b/activejob/lib/active_job/queue_adapter.rb @@ -2,9 +2,12 @@ require 'active_job/queue_adapters/inline_adapter' require 'active_support/core_ext/string/inflections' module ActiveJob + # The <tt>ActionJob::QueueAdapter</tt> module is used to load the + # correct adapter. The default queue adapter is the :inline queue. module QueueAdapter #:nodoc: extend ActiveSupport::Concern + # Includes the setter method for changing the active queue adapter. module ClassMethods mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb index efbcd3cb3a..f22b0502dc 100644 --- a/activejob/lib/active_job/queue_adapters.rb +++ b/activejob/lib/active_job/queue_adapters.rb @@ -13,7 +13,7 @@ module ActiveJob # * {Sneakers}[https://github.com/jondot/sneakers] # * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch] # - # #### Backends Features + # === Backends Features # # | | Async | Queues | Delayed | Priorities | Timeout | Retries | # |-------------------|-------|--------|-----------|------------|---------|---------| diff --git a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb index 4d27c4fff8..69d9e70de3 100644 --- a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -6,10 +6,10 @@ module ActiveJob # # Delayed::Job (or DJ) encapsulates the common pattern of asynchronously # executing longer tasks in the background. Although DJ can have many - # storage backends one of the most used is based on Active Record. + # storage backends, one of the most used is based on Active Record. # Read more about Delayed Job {here}[https://github.com/collectiveidea/delayed_job]. # - # To use Delayed Job set the queue_adapter config to +:delayed_job+. + # To use Delayed Job, set the queue_adapter config to +:delayed_job+. # # Rails.application.config.active_job.queue_adapter = :delayed_job class DelayedJobAdapter diff --git a/activejob/lib/active_job/queue_name.rb b/activejob/lib/active_job/queue_name.rb index 6ee7142ce6..9ae0345120 100644 --- a/activejob/lib/active_job/queue_name.rb +++ b/activejob/lib/active_job/queue_name.rb @@ -2,6 +2,7 @@ module ActiveJob module QueueName extend ActiveSupport::Concern + # Includes the ability to override the default queue name and prefix. module ClassMethods mattr_accessor(:queue_name_prefix) mattr_accessor(:default_queue_name) { "default" } diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb index ade7a94c9d..bb20d93947 100644 --- a/activejob/lib/active_job/test_helper.rb +++ b/activejob/lib/active_job/test_helper.rb @@ -53,7 +53,7 @@ module ActiveJob end end - # Assert that no job have been enqueued. + # Asserts that no jobs have been enqueued. # # def test_jobs # assert_no_enqueued_jobs @@ -137,7 +137,7 @@ module ActiveJob # Asserts that the job passed in the block has been enqueued with the given arguments. # - # def assert_enqueued_job + # def test_assert_enqueued_with # assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low') do # MyJob.perform_later(1,2,3) # end diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb index ff3e95da34..02478dd5b6 100644 --- a/activemodel/lib/active_model/validations/format.rb +++ b/activemodel/lib/active_model/validations/format.rb @@ -54,7 +54,7 @@ module ActiveModel module HelperMethods # Validates whether the value of the specified attribute is of the correct - # form, going by the regular expression provided.You can require that the + # form, going by the regular expression provided. You can require that the # attribute matches the regular expression: # # class Person < ActiveRecord::Base diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index 699a872e42..8d4b74ee49 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -3,7 +3,6 @@ require 'cases/helper' require 'models/person' require 'models/topic' require 'models/person_with_validator' -require 'validators/email_validator' require 'validators/namespace/email_validator' class ValidatesTest < ActiveModel::TestCase diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ad4ee0f489..1961ba93c6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,17 @@ +* Added SchemaDumper support for tables with jsonb columns. + + *Ted O'Meara* + +* Deprecate `sanitize_sql_hash_for_conditions` without replacement. Using a + `Relation` for performing queries and updates is the prefered API. + + *Sean Griffin* + +* Queries now properly type cast values that are part of a join statement, + even when using type decorators such as `serialize`. + + *Melanie Gilman & Sean Griffin* + * MySQL enum type lookups, with values matching another type, no longer result in an endless loop. 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 04a69ba446..bde23fc116 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -161,7 +161,7 @@ module ActiveRecord if scope.klass.primary_key count = scope.destroy_all.length else - scope.to_a.each do |record| + scope.each do |record| record._run_destroy_callbacks end diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index d57da366bd..12bf3ef138 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -81,6 +81,7 @@ module ActiveRecord unless reflection_scope.where_values.empty? scope.includes_values = Array(reflection_scope.values[:includes] || options[:source]) scope.where_values = reflection_scope.values[:where] + scope.bind_values = reflection_scope.bind_values end scope.references! reflection_scope.values[:references] diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index a0d9086875..582dd360f0 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -14,10 +14,7 @@ module ActiveRecord module ConnectionAdapters # :nodoc: extend ActiveSupport::Autoload - autoload_at 'active_record/connection_adapters/column' do - autoload :Column - autoload :NullColumn - end + autoload :Column autoload :ConnectionSpecification autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do @@ -267,7 +264,7 @@ module ActiveRecord # Returns a bind substitution value given a bind +index+ and +column+ # NOTE: The column param is currently being used by the sqlserver-adapter - def substitute_at(column, index) + def substitute_at(column, index = 0) Arel::Nodes::BindParam.new '?' end @@ -386,6 +383,10 @@ module ActiveRecord type_map.lookup(sql_type) end + def column_name_for_operation(operation, node) # :nodoc: + node.to_sql + end + protected def initialize_type_map(m) # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index cff6798eb3..3357ed52e5 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -88,7 +88,7 @@ module ActiveRecord end def clear - cache.values.each do |hash| + cache.each_value do |hash| hash[:stmt].close end cache.clear diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 89a7257d77..cf379ab210 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -156,7 +156,7 @@ module ActiveRecord end end - def substitute_at(column, index) + def substitute_at(column, index = 0) Arel::Nodes::BindParam.new "$#{index + 1}" end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 8e209e1051..9941f74766 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -104,6 +104,7 @@ module ActiveRecord macaddr: { name: "macaddr" }, uuid: { name: "uuid" }, json: { name: "json" }, + jsonb: { name: "jsonb" }, ltree: { name: "ltree" }, citext: { name: "citext" }, point: { name: "point" }, @@ -393,6 +394,16 @@ module ActiveRecord super(oid) end + def column_name_for_operation(operation, node) # :nodoc: + OPERATION_ALIASES.fetch(operation) { operation.downcase } + end + + OPERATION_ALIASES = { # :nodoc: + "maximum" => "max", + "minimum" => "min", + "average" => "avg", + } + protected # Returns the version of the connected PostgreSQL server. diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 4756896ac5..1b5e3bdbac 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -115,7 +115,7 @@ module ActiveRecord end def clear - cache.values.each do |hash| + cache.each_value do |hash| dealloc hash[:stmt] end cache.clear diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 125a119b5f..db6421dacb 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -649,7 +649,7 @@ module ActiveRecord model_class end - reflection_class._reflections.values.each do |association| + reflection_class._reflections.each_value do |association| case association.macro when :belongs_to # Do not replace association name with association foreign key if they are named the same diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 44765bd050..3ec25f9f17 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -196,7 +196,8 @@ db_namespace = namespace :db do fixture_files = if ENV['FIXTURES'] ENV['FIXTURES'].split(',') else - Pathname.glob("#{fixtures_dir}/**/*.yml").map {|f| f.basename.sub_ext('').to_s } + # The use of String#[] here is to support namespaced fixtures + Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] } end ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_files) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 7f51e4134d..03bce4f5b7 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -79,22 +79,27 @@ module ActiveRecord scope.unscope!(where: @klass.inheritance_column) end - um = scope.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key) + relation = scope.where(@klass.primary_key => (id_was || id)) + bvs = binds + relation.bind_values + um = relation + .arel + .compile_update(substitutes, @klass.primary_key) + reorder_bind_params(um.ast, bvs) @klass.connection.update( um, 'SQL', - binds) + bvs, + ) end def substitute_values(values) # :nodoc: - substitutes = values.sort_by { |arel_attr,_| arel_attr.name } - binds = substitutes.map do |arel_attr, value| + binds = values.map do |arel_attr, value| [@klass.columns_hash[arel_attr.name], value] end - substitutes.each_with_index do |tuple, i| - tuple[1] = @klass.connection.substitute_at(binds[i][0], i) + substitutes = values.each_with_index.map do |(arel_attr, _), i| + [arel_attr, @klass.connection.substitute_at(binds[i][0], i)] end [substitutes, binds] @@ -337,7 +342,7 @@ module ActiveRecord stmt.wheres = arel.constraints end - bvs = bind_values + arel.bind_values + bvs = arel.bind_values + bind_values @klass.connection.update stmt, 'SQL', bvs end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index e20cc0e76d..c8ebb41131 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -253,7 +253,8 @@ module ActiveRecord select_value = operation_over_aggregate_column(column, operation, distinct) - column_alias = select_value.alias || select_value.to_sql + column_alias = select_value.alias + column_alias ||= @klass.connection.column_name_for_operation(operation, select_value) relation.select_values = [select_value] query_builder = relation.arel diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 145b7378cf..eacae73ebb 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -82,12 +82,16 @@ module ActiveRecord # Post.find_by "published_at < ?", 2.weeks.ago def find_by(*args) where(*args).take + rescue RangeError + nil end # Like <tt>find_by</tt>, except that if no record is found, raises # an <tt>ActiveRecord::RecordNotFound</tt> error. def find_by!(*args) where(*args).take! + rescue RangeError + raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range value" end # Gives a record (or N records if a parameter is supplied) without any implied @@ -446,10 +450,7 @@ module ActiveRecord MSG end - column = columns_hash[primary_key] - substitute = connection.substitute_at(column, bind_values.length) - relation = where(table[primary_key].eq(substitute)) - relation.bind_values += [[column, id]] + relation = where(primary_key => id) record = relation.take raise_record_not_found_exception!(id, 0, 1) unless record @@ -458,7 +459,7 @@ module ActiveRecord end def find_some(ids) - result = where(table[primary_key].in(ids)).to_a + result = where(primary_key => ids).to_a expected_size = if limit_value && ids.size > limit_value diff --git a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb index 618fa3cdd9..063150958a 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb @@ -6,7 +6,7 @@ module ActiveRecord value = value.select(value.klass.arel_table[value.klass.primary_key]) end - attribute.in(value.arel.ast) + attribute.in(value.arel) end end end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 2c3cfb7631..a686e3263b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -439,7 +439,7 @@ module ActiveRecord self end - def bind(value) + def bind(value) # :nodoc: spawn.bind!(value) end @@ -883,12 +883,15 @@ module ActiveRecord # Reorder bind indexes if joins produced bind values bvs = arel.bind_values + bind_values - arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i| + reorder_bind_params(arel.ast, bvs) + arel + end + + def reorder_bind_params(ast, bvs) + ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i| column = bvs[i].first bp.replace connection.substitute_at(column, i) end - - arel end def symbol_unscoping(scope) @@ -958,8 +961,7 @@ module ActiveRecord when Hash opts = PredicateBuilder.resolve_column_aliases(klass, opts) - bv_len = bind_values.length - tmp_opts, bind_values = create_binds(opts, bv_len) + tmp_opts, bind_values = create_binds(opts) self.bind_values += bind_values attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts) @@ -971,7 +973,7 @@ module ActiveRecord end end - def create_binds(opts, idx) + def create_binds(opts) bindable, non_binds = opts.partition do |column, value| case value when String, Integer, ActiveRecord::StatementCache::Substitute @@ -981,12 +983,23 @@ module ActiveRecord end end + association_binds, non_binds = non_binds.partition do |column, value| + value.is_a?(Hash) && association_for_table(column) + end + new_opts = {} binds = [] - bindable.each_with_index do |(column,value), index| + bindable.each do |(column,value)| binds.push [@klass.columns_hash[column.to_s], value] - new_opts[column] = connection.substitute_at(column, index + idx) + new_opts[column] = connection.substitute_at(column) + end + + association_binds.each do |(column, value)| + association_relation = association_for_table(column).klass.send(:relation) + association_new_opts, association_bind = association_relation.send(:create_binds, value) + new_opts[column] = association_new_opts + binds += association_bind end non_binds.each { |column,value| new_opts[column] = value } @@ -994,6 +1007,12 @@ module ActiveRecord [new_opts, binds] end + def association_for_table(table_name) + table_name = table_name.to_s + @klass._reflect_on_association(table_name) || + @klass._reflect_on_association(table_name.singularize) + end + def build_from opts, name = from_value case opts diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index ff70cbed0f..6a130c145b 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -87,6 +87,9 @@ module ActiveRecord # { address: Address.new("123 abc st.", "chicago") } # # => "address_street='123 abc st.' and address_city='chicago'" def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name) + ActiveSupport::Deprecation.warn(<<-EOWARN) +sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0 + EOWARN attrs = PredicateBuilder.resolve_column_aliases self, attrs attrs = expand_hash_conditions_for_aggregates(attrs) @@ -134,7 +137,7 @@ module ActiveRecord raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size) bound = values.dup c = connection - statement.gsub('?') do + statement.gsub(/\?/) do replace_bind_variable(bound.shift, c) end end diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index 86ba849445..7a9fdd45e8 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -3,8 +3,11 @@ require "cases/helper" require 'active_record/base' require 'active_record/connection_adapters/postgresql_adapter' +require 'support/schema_dumping_helper' module PostgresqlJSONSharedTestCases + include SchemaDumpingHelper + class JsonDataType < ActiveRecord::Base self.table_name = 'json_data_type' @@ -64,6 +67,11 @@ module PostgresqlJSONSharedTestCases JsonDataType.reset_column_information end + def test_schema_dumping + output = dump_table_schema("json_data_type") + assert_match(/t.#{column_type.to_s}\s+"payload",\s+default: {}/, output) + end + def test_cast_value_on_write x = JsonDataType.new payload: {"string" => "foo", :symbol => :bar} assert_equal({"string" => "foo", :symbol => :bar}, x.payload_before_type_cast) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 4e1685f151..3675401555 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -522,6 +522,10 @@ class BasicsTest < ActiveRecord::TestCase assert_equal Topic.find(['1-meowmeow', '2-hello']), Topic.find([1, 2]) end + def test_find_by_slug_with_range + assert_equal Topic.where(id: '1-meowmeow'..'2-hello'), Topic.where(id: 1..2) + end + def test_equality_of_new_records assert_not_equal Topic.new, Topic.new assert_equal false, Topic.new == Topic.new diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index d1ff8516e8..dc73faa5be 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -210,6 +210,16 @@ class FinderTest < ActiveRecord::TestCase assert_nil Topic.find_by_id('9999999999999999999999999999999') end + def test_find_on_relation_with_large_number + assert_nil Topic.where('1=1').find_by(id: 9999999999999999999999999999999) + end + + def test_find_by_bang_on_relation_with_large_number + assert_raises(ActiveRecord::RecordNotFound) do + Topic.where('1=1').find_by!(id: 9999999999999999999999999999999) + end + end + def test_find_an_empty_array assert_equal [], Topic.find([]) end diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index 39c8afdd23..a453203e15 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -7,6 +7,7 @@ require 'models/comment' require 'models/edge' require 'models/topic' require 'models/binary' +require 'models/vertex' module ActiveRecord class WhereTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb index dca85fb5eb..f477cf7d92 100644 --- a/activerecord/test/cases/sanitize_test.rb +++ b/activerecord/test/cases/sanitize_test.rb @@ -13,7 +13,9 @@ class SanitizeTest < ActiveRecord::TestCase quoted_table_name = ActiveRecord::Base.connection.quote_table_name("adorable_animals") expected_value = "#{quoted_table_name}.#{quoted_column_name} = #{quoted_bambi}" - assert_equal expected_value, Binary.send(:sanitize_sql_hash, {adorable_animals: {name: 'Bambi'}}) + assert_deprecated do + assert_equal expected_value, Binary.send(:sanitize_sql_hash, {adorable_animals: {name: 'Bambi'}}) + end end def test_sanitize_sql_array_handles_string_interpolation diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 2241c41f36..6303393c22 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -271,13 +271,6 @@ class SchemaDumperTest < ActiveRecord::TestCase end end - def test_schema_dump_includes_json_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_json_data_type"} =~ output - assert_match %r|t.json "json_data", default: {}|, output - end - end - def test_schema_dump_includes_inet_shorthand_definition output = standard_dump if %r{create_table "postgresql_network_addresses"} =~ output diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb index 3f52e80e11..35b13ea247 100644 --- a/activerecord/test/cases/serialization_test.rb +++ b/activerecord/test/cases/serialization_test.rb @@ -2,6 +2,8 @@ require "cases/helper" require 'models/contact' require 'models/topic' require 'models/book' +require 'models/author' +require 'models/post' class SerializationTest < ActiveRecord::TestCase fixtures :books @@ -92,4 +94,11 @@ class SerializationTest < ActiveRecord::TestCase book = klazz.find(books(:awdr).id) assert_equal 'paperback', book.read_attribute_for_serialization(:format) end + + def test_find_records_by_serialized_attributes_through_join + author = Author.create!(name: "David") + author.serialized_posts.create!(title: "Hello") + + assert_equal 1, Author.joins(:serialized_posts).where(name: "David", serialized_posts: { title: "Hello" }).length + end end diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 3f34d09a04..3da3a1fd59 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -1,5 +1,6 @@ class Author < ActiveRecord::Base has_many :posts + has_many :serialized_posts has_one :post has_many :very_special_comments, :through => :posts has_many :posts_with_comments, -> { includes(:comments) }, :class_name => "Post" diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 67027cbc22..36cf221d45 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -233,3 +233,7 @@ class PostWithCommentWithDefaultScopeReferencesAssociation < ActiveRecord::Base has_many :comment_with_default_scope_references_associations, foreign_key: :post_id has_one :first_comment, class_name: "CommentWithDefaultScopeReferencesAssociation", foreign_key: :post_id end + +class SerializedPost < ActiveRecord::Base + serialize :title +end diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index e9294a11b9..7c3b170c08 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -1,7 +1,9 @@ ActiveRecord::Schema.define do - %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_uuids postgresql_ltrees - postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent postgresql_json_data_type postgresql_citext).each do |table_name| + %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times + postgresql_network_addresses postgresql_uuids postgresql_ltrees postgresql_oids postgresql_xml_data_type defaults + geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent + postgresql_citext).each do |table_name| execute "DROP TABLE IF EXISTS #{quote_table_name table_name}" end @@ -108,15 +110,6 @@ _SQL _SQL end - if 't' == select_value("select 'json'=ANY(select typname from pg_type)") - execute <<_SQL - CREATE TABLE postgresql_json_data_type ( - id SERIAL PRIMARY KEY, - json_data json default '{}'::json - ); -_SQL - end - execute <<_SQL CREATE TABLE postgresql_numbers ( id SERIAL PRIMARY KEY, diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 2b68236256..539a0d2a49 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -585,6 +585,11 @@ ActiveRecord::Schema.define do t.integer :tags_with_nullify_count, default: 0 end + create_table :serialized_posts, force: true do |t| + t.integer :author_id + t.string :title, null: false + end + create_table :price_estimates, force: true do |t| t.string :estimate_of_type t.integer :estimate_of_id diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 4ad602b066..b2b3cf4bd4 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,7 @@ +* `String#remove` and `String#remove!` accept multiple arguments. + + *Pavel Pravosud* + * TimeWithZone#strftime now delegates every directive to Time#strftime except for '%Z', it also now correctly handles escaped '%' characters placed just before time zone related directives. diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 8ad600b171..41b2279013 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -1,6 +1,12 @@ class Hash - # Slice a hash to include only the given keys. This is useful for - # limiting an options hash to valid keys before passing to a method: + # Slice a hash to include only the given keys. Returns a hash containing + # the given keys. + # + # { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b) + # # => {:a=>1, :b=>2} + # + # This is useful for limiting an options hash to valid keys before + # passing to a method: # # def search(criteria = {}) # criteria.assert_valid_keys(:mass, :velocity, :time) diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb index 2b1583d4ac..499b9b26bc 100644 --- a/activesupport/lib/active_support/core_ext/string/filters.rb +++ b/activesupport/lib/active_support/core_ext/string/filters.rb @@ -20,14 +20,18 @@ class String self end - # Returns a new string with all occurrences of the pattern removed. Short-hand for String#gsub(pattern, ''). - def remove(pattern) - gsub pattern, '' + # Returns a new string with all occurrences of the patterns removed. + def remove(*patterns) + dup.remove!(*patterns) end - # Alters the string by removing all occurrences of the pattern. Short-hand for String#gsub!(pattern, ''). - def remove!(pattern) - gsub! pattern, '' + # Alters the string by removing all occurrences of the patterns. + def remove!(*patterns) + patterns.each do |pattern| + gsub! pattern, "" + end + + self end # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>: diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb index c0dba5f7fd..13036d521d 100644 --- a/activesupport/lib/active_support/dependencies/autoload.rb +++ b/activesupport/lib/active_support/dependencies/autoload.rb @@ -67,7 +67,7 @@ module ActiveSupport end def eager_load! - @_autoloads.values.each { |file| require file } + @_autoloads.each_value { |file| require file } end def autoloads diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 584fc1e1c5..baa83223d2 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -1,4 +1,3 @@ -require 'active_support/proxy_object' require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/object/acts_like' diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 637736c5df..74b3a7c2a9 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -73,7 +73,7 @@ module ActiveSupport string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase } end string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" } - string.gsub!('/', '::') + string.gsub!(/\//, '::') string end diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 5a2af7f943..a5d254e115 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -30,7 +30,6 @@ class DurationTest < ActiveSupport::TestCase assert ActiveSupport::Duration === 1.day assert !(ActiveSupport::Duration === 1.day.to_i) assert !(ActiveSupport::Duration === 'foo') - assert !(ActiveSupport::Duration === ActiveSupport::ProxyObject.new) end def test_equals diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 2f4691817f..b97aa56aef 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -260,8 +260,18 @@ class StringInflectionsTest < ActiveSupport::TestCase end def test_remove - assert_equal "Summer", "Fast Summer".remove(/Fast /) - assert_equal "Summer", "Fast Summer".remove!(/Fast /) + original = "This is a good day to die" + assert_equal "This is a good day", original.remove(" to die") + assert_equal "This is a good day", original.remove(" to ", /die/) + assert_equal "This is a good day to die", original + end + + def test_remove! + original = "This is a very good day to die" + assert_equal "This is a good day to die", original.remove!(" very") + assert_equal "This is a good day to die", original + assert_equal "This is a good day", original.remove!(" to ", /die/) + assert_equal "This is a good day", original end def test_constantize @@ -408,7 +418,7 @@ class StringConversionsTest < ActiveSupport::TestCase now = Time.now assert_equal Time.local(now.year, now.month, now.day, 23, 50), "23:50".to_time assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "23:50".to_time(:utc) - assert_equal Time.local(now.year, now.month, now.day, 18, 50), "13:50 -0100".to_time + assert_equal Time.local(now.year, now.month, now.day, 17, 50), "13:50 -0100".to_time assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "22:50 -0100".to_time(:utc) end end diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index d9f49aac55..17894b2752 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -49,13 +49,13 @@ bog down the controller or model. The new GlobalID library makes it easy to pass Active Record objects to jobs by serializing them in a generic form. This means you no longer have to manually pack and unpack your Active Records by passing ids. Just give the job the -straight Active Record object, and it'll serialize it using GlobalID, and -deserialize it at run time. +Active Record object, and it'll serialize it using GlobalID, and deserialize +it at run time. ### Adequate Record Adequate Record is a set of refactorings that make Active Record `find` and -`find_by` methods and some association queries upto 2x faster. +`find_by` methods and some association queries up to 2x faster. It works by caching SQL query patterns while executing the Active Record calls. The cache helps skip parts of the computation involved in the transformation of @@ -88,7 +88,7 @@ The caching is not used in the following scenarios: Post.find [1,2] ``` -- `find_by` with sql fragments: +- `find_by` with SQL fragments: ```ruby Post.find_by "published_at < ?", 2.weeks.ago @@ -196,9 +196,9 @@ end Due to a [change in Rack](https://github.com/rack/rack/commit/28b014484a8ac0bbb388e7eaeeef159598ec64fc), `rails server` now listens on `localhost` instead of `0.0.0.0` by default. This should have minimal impact on the standard development workflow as both http://127.0.0.1:3000 -and http://localhost:3000 would continue to work as before on your own machine. +and http://localhost:3000 will continue to work as before on your own machine. -However, with this change you would no longer be able to access the Rails server +However, with this change you will no longer be able to access the Rails server from a different machine (e.g. your development environment is in a virtual machine and you would like to access it from the host machine), you would need to start the server with `rails server -b 0.0.0.0` to restore the old behavior. @@ -471,7 +471,7 @@ Please refer to the [Changelog][action-pack] for detailed changes. ([Pull Request](https://github.com/rails/rails/pull/14280)) * When the Rails server is set to serve static assets, gzip assets will now be - served if the client supports it and a pre-generated gzip file (.gz) is on disk. + served if the client supports it and a pre-generated gzip file (`.gz`) is on disk. By default the asset pipeline generates `.gz` files for all compressible assets. Serving gzip files minimizes data transfer and speeds up asset requests. Always [use a CDN](http://guides.rubyonrails.org/asset_pipeline.html#cdns) if you are @@ -479,7 +479,7 @@ Please refer to the [Changelog][action-pack] for detailed changes. ([Pull Request](https://github.com/rails/rails/pull/16466)) * The way `assert_select` works has changed; specifically a different library - is used to interpret css selectors, build the transient DOM that the + is used to interpret CSS selectors, build the transient DOM that the selectors are applied against, and to extract the data from that DOM. These changes should only affect edge cases. Examples: * Values in attribute selectors may need to be quoted if they contain @@ -537,7 +537,7 @@ Please refer to the [Changelog][action-mailer] for detailed changes. * Deprecated `*_path` helpers in mailers. Always use `*_url` helpers instead. ([Pull Request](https://github.com/rails/rails/pull/15840)) -* Deprecated `deliver` / `deliver!` in favour of `deliver_now` / `deliver_now!`. +* Deprecated `deliver` / `deliver!` in favor of `deliver_now` / `deliver_now!`. ([Pull Request](https://github.com/rails/rails/pull/16582)) ### Notable changes @@ -596,7 +596,7 @@ Please refer to the [Changelog][active-record] for detailed changes. ([Pull Request](https://github.com/rails/rails/pull/15704)) * Deprecated returning `nil` from `column_for_attribute` when no column - exists. It will return a null object in Rails 5.0 + exists. It will return a null object in Rails 5.0. ([Pull Request](https://github.com/rails/rails/pull/15878)) * Deprecated using `.joins`, `.preload` and `.eager_load` with associations @@ -648,7 +648,7 @@ Please refer to the [Changelog][active-record] for detailed changes. ([Commit](https://github.com/rails/rails/commit/e2f232aba15937a4b9d14bd91e0392c6d55be58d)) * `ActiveRecord::Dirty` now detects in-place changes to mutable values. - Serialized attributes on ActiveRecord models will no longer save when + Serialized attributes on Active Record models will no longer save when unchanged. This also works with other types such as string columns and json columns on PostgreSQL. (Pull Requests [1](https://github.com/rails/rails/pull/15674), diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb index f84f1cb376..f2315bfe22 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -15,5 +15,5 @@ </p> <% end %> <p> - The guides for earlier releases: <a href="http://guides.rubyonrails.org/v4.1.4/">Rails 4.1.4</a>, <a href="http://guides.rubyonrails.org/v4.0.8/">Rails 4.0.8</a>, <a href="http://guides.rubyonrails.org/v3.2.19/">Rails 3.2.19</a> and <a href="http://guides.rubyonrails.org/v2.3.11/">Rails 2.3.11</a>. + The guides for earlier releases: <a href="http://guides.rubyonrails.org/v4.1.6/">Rails 4.1.6</a>, <a href="http://guides.rubyonrails.org/v4.0.10/">Rails 4.0.10</a>, <a href="http://guides.rubyonrails.org/v3.2.19/">Rails 3.2.19</a> and <a href="http://guides.rubyonrails.org/v2.3.11/">Rails 2.3.11</a>. </p> diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 8890ea453e..1ca0d9ed55 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -1213,12 +1213,12 @@ Create the controller and views. * `app/views` ``` - errors/ - not_found.html.erb - unprocessable_entity.html.erb - server_error.html.erb - layouts/ - error.html.erb + errors/ + not_found.html.erb + unprocessable_entity.html.erb + server_error.html.erb + layouts/ + error.html.erb ``` Do not forget to set the correct status code on the controller as shown before. diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index de69ab3211..ca851371a9 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -41,10 +41,12 @@ This section will provide a step-by-step guide to creating a job and enqueuing i ### Create the Job Active Job provides a Rails generator to create jobs. The following will create a -job in `app/jobs`: +job in `app/jobs` (with an attached test case under `test/jobs`): ```bash $ bin/rails generate job guests_cleanup +invoke test_unit +create test/jobs/guests_cleanup_job_test.rb create app/jobs/guests_cleanup_job.rb ``` @@ -52,7 +54,6 @@ You can also create a job that will run on a specific queue: ```bash $ bin/rails generate job guests_cleanup --queue urgent -create app/jobs/guests_cleanup_job.rb ``` As you can see, you can generate jobs just like you use other generators with @@ -78,15 +79,18 @@ end Enqueue a job like so: ```ruby -MyJob.perform_later record # Enqueue a job to be performed as soon the queueing system is free. +# Enqueue a job to be performed as soon the queueing system is free. +MyJob.perform_later record ``` ```ruby -MyJob.set(wait_until: Date.tomorrow.noon).perform_later(record) # Enqueue a job to be performed tomorrow at noon. +# Enqueue a job to be performed tomorrow at noon. +MyJob.set(wait_until: Date.tomorrow.noon).perform_later(record) ``` ```ruby -MyJob.set(wait: 1.week).perform_later(record) # Enqueue a job to be performed 1 week from now. +# Enqueue a job to be performed 1 week from now. +MyJob.set(wait: 1.week).perform_later(record) ``` That's it! @@ -108,9 +112,9 @@ see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails. You can easily change your queueing backend: ```ruby -# be sure to have the adapter gem in your Gemfile and follow the adapter specific -# installation and deployment instructions -Rails.application.config.active_job.queue_adapter = :sidekiq +# be sure to have the adapter gem in your Gemfile and follow +# the adapter specific installation and deployment instructions +config.active_job.queue_adapter = :sidekiq ``` @@ -262,12 +266,13 @@ UserMailer.welcome(@user).deliver_later GlobalID -------- + Active Job supports GlobalID for parameters. This makes it possible to pass live Active Record objects to your job instead of class/id pairs, which you then have to manually deserialize. Before, jobs would look like this: ```ruby -class TrashableCleanupJob +class TrashableCleanupJob < ActiveJob::Base def perform(trashable_class, trashable_id, depth) trashable = trashable_class.constantize.find(trashable_id) trashable.cleanup(depth) @@ -278,7 +283,7 @@ end Now you can simply do: ```ruby -class TrashableCleanupJob +class TrashableCleanupJob < ActiveJob::Base def perform(trashable, depth) trashable.cleanup(depth) end diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index 6f45ca698d..546c0608ee 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -533,6 +533,7 @@ validates :boolean_field_name, presence: true validates :boolean_field_name, inclusion: { in: [true, false] } validates :boolean_field_name, exclusion: { in: [nil] } ``` + By using one of these validations, you will ensure the value will NOT be `nil` which would result in a `NULL` value in most cases. diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index e552c3804d..ae0f19c02a 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -1347,7 +1347,8 @@ config.assets.digest = true Rails 4 no longer sets default config values for Sprockets in `test.rb`, so `test.rb` now requires Sprockets configuration. The old defaults in the test -environment are: `config.assets.compile = true`, `config.assets.compress = false`, `config.assets.debug = false` and `config.assets.digest = false`. +environment are: `config.assets.compile = true`, `config.assets.compress = false`, +`config.assets.debug = false` and `config.assets.digest = false`. The following should also be added to `Gemfile`: diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 58c3f217eb..2957232186 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -153,7 +153,7 @@ pipeline is enabled. It is set to true by default. * `config.assets.manifest` defines the full path to be used for the asset precompiler's manifest file. Defaults to a file named `manifest-<random>.json` in the `config.assets.prefix` directory within the public folder. -* `config.assets.digest` enables the use of MD5 fingerprints in asset names. Set to `true` by default in `production.rb`. +* `config.assets.digest` enables the use of MD5 fingerprints in asset names. Set to `true` by default in `production.rb` and `development.rb`. * `config.assets.debug` disables the concatenation and compression of assets. Set to `true` by default in `development.rb`. diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index 16fa23c129..cb45e38614 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -506,7 +506,7 @@ As the name implies, this only generates option tags. To generate a working sele <%= collection_select(:person, :city_id, City.all, :id, :name) %> ``` -As with other helpers, if you were to use the collection_select helper on a form builder scoped to the @person object, the syntax would be: +As with other helpers, if you were to use the `collection_select` helper on a form builder scoped to the `@person` object, the syntax would be: ```erb <%= f.collection_select(:city_id, City.all, :id, :name) %> diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 1769448531..92f8ef5b08 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -195,8 +195,8 @@ TIP: Compiling CoffeeScript and JavaScript asset compression requires you have a JavaScript runtime available on your system, in the absence of a runtime you will see an `execjs` error during asset compilation. Usually Mac OS X and Windows come with a JavaScript runtime installed. -Rails adds the `therubyracer` gem to the generated `Gemfile` in a -commented line for new apps and you can uncomment if you need it. +Rails adds the `therubyracer` gem to the generated `Gemfile` in a +commented line for new apps and you can uncomment if you need it. `therubyrhino` is the recommended runtime for JRuby users and is added by default to the `Gemfile` in apps generated under JRuby. You can investigate all the supported runtimes at [ExecJS](https://github.com/sstephenson/execjs#readme). @@ -338,8 +338,8 @@ You can create, read, update and destroy items for a resource and these operations are referred to as _CRUD_ operations. Rails provides a `resources` method which can be used to declare a standard REST -resource. Here's what `config/routes.rb` should look like after the -_article resource_ is declared. +resource. You need to add the _article resource_ to the +`config/routes.rb` as follows: ```ruby Rails.application.routes.draw do diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md index 6206b3c715..c0438f6341 100644 --- a/guides/source/ruby_on_rails_guides_guidelines.md +++ b/guides/source/ruby_on_rails_guides_guidelines.md @@ -54,6 +54,7 @@ API Documentation Guidelines The guides and the API should be coherent and consistent where appropriate. In particular, these sections of the [API Documentation Guidelines](api_documentation_guidelines.html) also apply to the guides: * [Wording](api_documentation_guidelines.html#wording) +* [English](api_documentation_guidelines.html#english) * [Example Code](api_documentation_guidelines.html#example-code) * [Filenames](api_documentation_guidelines.html#file-names) * [Fonts](api_documentation_guidelines.html#fonts) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index bc966e87c6..18d9cb72d6 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -88,6 +88,7 @@ module Rails def inherited(base) super Rails.app_class = base + add_lib_to_load_path!(find_root(base.called_from)) end def instance @@ -98,6 +99,10 @@ module Rails new(initial_variable_values, &block).run_load_hooks! end + def find_root(from) + find_root_with_flag "config.ru", from, Dir.pwd + end + # Makes the +new+ method public. # # Note that Rails::Application inherits from Rails::Engine, which @@ -129,8 +134,6 @@ module Rails # are these actually used? @initial_variable_values = initial_variable_values @block = block - - add_lib_to_load_path! end # Returns true if the application is initialized. @@ -313,8 +316,8 @@ module Rails # are changing config.root inside your application definition or having a custom # Rails application, you will need to add lib to $LOAD_PATH on your own in case # you need to load files in lib/ during the application configuration as well. - def add_lib_to_load_path! #:nodoc: - path = File.join config.root, 'lib' + def self.add_lib_to_load_path!(root) #:nodoc: + path = File.join root, 'lib' if File.exist?(path) && !$LOAD_PATH.include?(path) $LOAD_PATH.unshift(path) end @@ -358,7 +361,7 @@ module Rails end def config #:nodoc: - @config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd)) + @config ||= Application::Configuration.new(self.class.find_root(self.class.called_from)) end def config=(configuration) #:nodoc: diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index dc3da1eb41..d985518fd9 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -364,6 +364,10 @@ module Rails super end + def find_root(from) + find_root_with_flag "lib", from + end + def endpoint(endpoint = nil) @endpoint ||= nil @endpoint = endpoint if endpoint @@ -531,7 +535,7 @@ module Rails # Define the configuration object for the engine. def config - @config ||= Engine::Configuration.new(find_root_with_flag("lib")) + @config ||= Engine::Configuration.new(self.class.find_root(self.class.called_from)) end # Load data from db/seeds.rb file. It can be used in to load engines' @@ -658,8 +662,7 @@ module Rails paths["db/migrate"].existent.any? end - def find_root_with_flag(flag, default=nil) #:nodoc: - root_path = self.class.called_from + def self.find_root_with_flag(flag, root_path, default=nil) #:nodoc: while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}") parent = File.dirname(root_path) diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 0eddf644d9..2b6eb3624a 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -59,6 +59,20 @@ module ApplicationTests end end + test "lib dir is on LOAD_PATH during config" do + app_file 'lib/my_logger.rb', <<-RUBY + require "logger" + class MyLogger < ::Logger + end + RUBY + add_to_top_of_config <<-RUBY + require 'my_logger' + config.logger = MyLogger.new STDOUT + RUBY + require "#{app_path}/config/environment" + assert_equal 'MyLogger', Rails.application.config.logger.class.name + end + test "a renders exception on pending migration" do add_to_config <<-RUBY config.active_record.migration_error = :page_load diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 267469b6f5..524c70aad2 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -109,6 +109,16 @@ module ApplicationTests db_fixtures_load database_url_db_name end + test 'db:fixtures:load with namespaced fixture' do + require "#{app_path}/config/environment" + Dir.chdir(app_path) do + `rails generate model admin::book title:string; + bundle exec rake db:migrate db:fixtures:load` + require "#{app_path}/app/models/admin/book" + assert_equal 2, Admin::Book.count + end + end + def db_structure_dump_and_load(expected_database) Dir.chdir(app_path) do `rails generate model book title:string; diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 40469e31d7..bf2992005b 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -231,6 +231,15 @@ module TestHelpers end end + def add_to_top_of_config(str) + environment = File.read("#{app_path}/config/application.rb") + if environment =~ /(Rails::Application\s*)/ + File.open("#{app_path}/config/application.rb", 'w') do |f| + f.puts $` + $1 + "\n#{str}\n" + $' + end + end + end + def add_to_config(str) environment = File.read("#{app_path}/config/application.rb") if environment =~ /(\n\s*end\s*end\s*)\Z/ diff --git a/tools/README.md b/tools/README.md index 13be763dac..25ab798bd5 100644 --- a/tools/README.md +++ b/tools/README.md @@ -3,5 +3,5 @@ This is a collection of utilities used for Rails internal development. They aren't used by Rails apps directly. - * `console` drops you in irb and loads this local Rails repos - * `profile` profiles `Kernel#require` to help reduce startup times + * `console` drops you in irb and loads local Rails repos + * `profile` profiles `Kernel#require` to help reduce startup time |