diff options
25 files changed, 290 insertions, 154 deletions
@@ -47,6 +47,7 @@ gem "dalli" gem "listen", ">= 3.0.5", "< 3.2", require: false gem "libxml-ruby", platforms: :ruby gem "connection_pool", require: false +gem "i18n", "~> 1.0.1" # for railties app_generator_test gem "bootsnap", ">= 1.1.0", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 259221979e..87d017a8a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -534,6 +534,7 @@ DEPENDENCIES ffi (<= 1.9.21) google-cloud-storage (~> 1.11) hiredis + i18n (~> 1.0.1) image_processing (~> 1.2) json (>= 2.0.0) kindlerb (~> 1.2.0) diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index edfef39771..ea637c8150 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -17,7 +17,7 @@ module ActionController #:nodoc: # access. When a request reaches your application, \Rails verifies the received # token with the token in the session. All requests are checked except GET requests # as these should be idempotent. Keep in mind that all session-oriented requests - # should be CSRF protected, including JavaScript and HTML requests. + # are CSRF protected by default, including JavaScript and HTML requests. # # Since HTML and JavaScript requests are typically made from the browser, we # need to ensure to verify request authenticity for the web browser. We can @@ -30,16 +30,23 @@ module ActionController #:nodoc: # URL on your site. When your JavaScript response loads on their site, it executes. # With carefully crafted JavaScript on their end, sensitive data in your JavaScript # response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or - # Ajax) requests are allowed to make GET requests for JavaScript responses. + # Ajax) requests are allowed to make requests for JavaScript responses. # - # It's important to remember that XML or JSON requests are also affected and if - # you're building an API you should change forgery protection method in + # It's important to remember that XML or JSON requests are also checked by default. If + # you're building an API or an SPA you could change forgery protection method in # <tt>ApplicationController</tt> (by default: <tt>:exception</tt>): # # class ApplicationController < ActionController::Base # protect_from_forgery unless: -> { request.format.json? } # end # + # It is generally safe to exclude XHR requests from CSRF protection + # (like the code snippet above does), because XHR requests can only be made from + # the same origin. Note however that any cross-origin third party domain + # allowed via {CORS}[https://en.wikipedia.org/wiki/Cross-origin_resource_sharing] + # will also be able to create XHR requests. Be sure to check your + # CORS whitelist before disabling forgery protection for XHR. + # # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method. # By default <tt>protect_from_forgery</tt> protects your session with # <tt>:null_session</tt> method, which provides an empty session diff --git a/activejob/lib/active_job/queue_adapters/test_adapter.rb b/activejob/lib/active_job/queue_adapters/test_adapter.rb index 885f9ff01c..3aa25425eb 100644 --- a/activejob/lib/active_job/queue_adapters/test_adapter.rb +++ b/activejob/lib/active_job/queue_adapters/test_adapter.rb @@ -29,14 +29,14 @@ module ActiveJob return if filtered?(job) job_data = job_to_hash(job) - enqueue_or_perform(perform_enqueued_jobs, job, job_data) + perform_or_enqueue(perform_enqueued_jobs, job, job_data) end def enqueue_at(job, timestamp) #:nodoc: return if filtered?(job) job_data = job_to_hash(job, at: timestamp) - enqueue_or_perform(perform_enqueued_at_jobs, job, job_data) + perform_or_enqueue(perform_enqueued_at_jobs, job, job_data) end private @@ -44,7 +44,7 @@ module ActiveJob { job: job.class, args: job.serialize.fetch("arguments"), queue: job.queue_name }.merge!(extras) end - def enqueue_or_perform(perform, job, job_data) + def perform_or_enqueue(perform, job, job_data) if perform performed_jobs << job_data Base.execute job.serialize diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb index 25e1541d66..f77fb98c32 100644 --- a/activemodel/lib/active_model/serializers/json.rb +++ b/activemodel/lib/active_model/serializers/json.rb @@ -26,13 +26,13 @@ module ActiveModel # user = User.find(1) # user.as_json # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16, - # # "created_at" => "2006/08/01", "awesome" => true} + # # "created_at" => "2006-08-01T17:27:133.000Z", "awesome" => true} # # ActiveRecord::Base.include_root_in_json = true # # user.as_json # # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16, - # # "created_at" => "2006/08/01", "awesome" => true } } + # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } } # # This behavior can also be achieved by setting the <tt>:root</tt> option # to +true+ as in: @@ -40,7 +40,7 @@ module ActiveModel # user = User.find(1) # user.as_json(root: true) # # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16, - # # "created_at" => "2006/08/01", "awesome" => true } } + # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } } # # Without any +options+, the returned Hash will include all the model's # attributes. @@ -48,7 +48,7 @@ module ActiveModel # user = User.find(1) # user.as_json # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16, - # # "created_at" => "2006/08/01", "awesome" => true} + # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true} # # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit # the attributes included, and work similar to the +attributes+ method. @@ -63,14 +63,14 @@ module ActiveModel # # user.as_json(methods: :permalink) # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16, - # # "created_at" => "2006/08/01", "awesome" => true, + # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true, # # "permalink" => "1-konata-izumi" } # # To include associations use <tt>:include</tt>: # # user.as_json(include: :posts) # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16, - # # "created_at" => "2006/08/01", "awesome" => true, + # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true, # # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" }, # # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] } # @@ -81,7 +81,7 @@ module ActiveModel # only: :body } }, # only: :title } }) # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16, - # # "created_at" => "2006/08/01", "awesome" => true, + # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true, # # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ], # # "title" => "Welcome to the weblog" }, # # { "comments" => [ { "body" => "Don't think too hard" } ], @@ -93,11 +93,12 @@ module ActiveModel include_root_in_json end + hash = serializable_hash(options).as_json if root root = model_name.element if root == true - { root => serializable_hash(options) } + { root => hash } else - serializable_hash(options) + hash end end diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb index aae98c9fe4..625e0a427a 100644 --- a/activemodel/test/cases/serializers/json_serialization_test.rb +++ b/activemodel/test/cases/serializers/json_serialization_test.rb @@ -129,6 +129,10 @@ class JsonSerializationTest < ActiveModel::TestCase assert_equal :name, options[:except] end + test "as_json should serialize timestamps" do + assert_equal "2006-08-01T00:00:00.000Z", @contact.as_json["created_at"] + end + test "as_json should return a hash if include_root_in_json is true" do begin original_include_root_in_json = Contact.include_root_in_json @@ -138,7 +142,7 @@ class JsonSerializationTest < ActiveModel::TestCase assert_kind_of Hash, json assert_kind_of Hash, json["contact"] %w(name age created_at awesome preferences).each do |field| - assert_equal @contact.send(field), json["contact"][field] + assert_equal @contact.send(field).as_json, json["contact"][field] end ensure Contact.include_root_in_json = original_include_root_in_json 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 d2b7817b45..294a3dc32c 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -73,7 +73,7 @@ module ActiveRecord # `skip_time_zone_conversion_for_attributes` would not be picked up. subclass.class_eval do matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) } - decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type| + decorate_matching_attribute_types(matcher, "_time_zone_conversion") do |type| TimeZoneConverter.new(type) end end diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 7f096bb532..1030b2368b 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -165,7 +165,7 @@ module ActiveRecord def inherited(subclass) subclass.class_eval do is_lock_column = ->(name, _) { lock_optimistically && name == locking_column } - decorate_matching_attribute_types(is_lock_column, :_optimistic_locking) do |type| + decorate_matching_attribute_types(is_lock_column, "_optimistic_locking") do |type| LockingType.new(type) end end diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md index 0e9c2b5619..f6f195770c 100644 --- a/activestorage/CHANGELOG.md +++ b/activestorage/CHANGELOG.md @@ -1,3 +1,10 @@ +* Active Storage error classes like `ActiveStorage::IntegrityError` and + `ActiveStorage::UnrepresentableError` now inherit from `ActiveStorage::Error` + instead of `StandardError`. This permits rescuing `ActiveStorage::Error` to + handle all Active Storage errors. + + *Andrei Makarov*, *George Claghorn* + * Uploaded files assigned to a record are persisted to storage when the record is saved instead of immediately. diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb index 056fd2be99..ea57fa5f78 100644 --- a/activestorage/app/models/active_storage/variant.rb +++ b/activestorage/app/models/active_storage/variant.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "ostruct" + # Image blobs can have variants that are the result of a set of transformations applied to the original. # These variants are used to create thumbnails, fixed-size avatars, or any other derivative image from the # original. @@ -51,7 +53,7 @@ # * {ImageProcessing::Vips}[https://github.com/janko-m/image_processing/blob/master/doc/vips.md#methods] # * {ruby-vips reference}[http://www.rubydoc.info/gems/ruby-vips/Vips/Image] class ActiveStorage::Variant - WEB_IMAGE_CONTENT_TYPES = %w( image/png image/jpeg image/jpg image/gif ) + WEB_IMAGE_CONTENT_TYPES = %w[ image/png image/jpeg image/jpg image/gif ] attr_reader :blob, :variation delegate :service, to: :blob @@ -95,37 +97,35 @@ class ActiveStorage::Variant def process blob.open do |image| - transform image do |output| - upload output - end + transform(image) { |output| upload(output) } end end - - def filename - if WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type) - blob.filename - else - ActiveStorage::Filename.new("#{blob.filename.base}.png") - end + def transform(image, &block) + variation.transform(image, format: format, &block) end - def content_type - blob.content_type.presence_in(WEB_IMAGE_CONTENT_TYPES) || "image/png" + def upload(file) + service.upload(key, file) end - def transform(image) - format = "png" unless WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type) - result = variation.transform(image, format: format) - begin - yield result - ensure - result.close! - end + def specification + @specification ||= + if WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type) + Specification.new \ + filename: blob.filename, + content_type: blob.content_type, + format: nil + else + Specification.new \ + filename: ActiveStorage::Filename.new("#{blob.filename.base}.png"), + content_type: "image/png", + format: "png" + end end - def upload(file) - service.upload(key, file) - end + delegate :filename, :content_type, :format, to: :specification + + class Specification < OpenStruct; end end diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb index ae1376a6cf..3adc2407e5 100644 --- a/activestorage/app/models/active_storage/variation.rb +++ b/activestorage/app/models/active_storage/variation.rb @@ -47,13 +47,9 @@ class ActiveStorage::Variation # saves the transformed image into a temporary file. If +format+ is specified # it will be the format of the result image, otherwise the result image # retains the source format. - def transform(file, format: nil) + def transform(file, format: nil, &block) ActiveSupport::Notifications.instrument("transform.active_storage") do - if processor - image_processing_transform(file, format) - else - mini_magick_transform(file, format) - end + transformer.transform(file, format: format, &block) end end @@ -63,63 +59,22 @@ class ActiveStorage::Variation end private - # Applies image transformations using the ImageProcessing gem. - def image_processing_transform(file, format) - operations = transformations.each_with_object([]) do |(name, argument), list| - if name.to_s == "combine_options" - ActiveSupport::Deprecation.warn("The ImageProcessing ActiveStorage variant backend doesn't need :combine_options, as it already generates a single MiniMagick command. In Rails 6.1 :combine_options will not be supported anymore.") - list.concat argument.keep_if { |key, value| value.present? }.to_a - elsif argument.present? - list << [name, argument] + def transformer + if ActiveStorage.variant_processor + begin + require "image_processing" + rescue LoadError + ActiveSupport::Deprecation.warn <<~WARNING + Generating image variants will require the image_processing gem in Rails 6.1. + Please add `gem 'image_processing', '~> 1.2'` to your Gemfile. + WARNING + + ActiveStorage::Transformers::MiniMagickTransformer.new(transformations) + else + ActiveStorage::Transformers::ImageProcessingTransformer.new(transformations) end - end - - processor - .source(file) - .loader(page: 0) - .convert(format) - .apply(operations) - .call - end - - # Applies image transformations using the MiniMagick gem. - def mini_magick_transform(file, format) - image = MiniMagick::Image.new(file.path, file) - - transformations.each do |name, argument_or_subtransformations| - image.mogrify do |command| - if name.to_s == "combine_options" - argument_or_subtransformations.each do |subtransformation_name, subtransformation_argument| - pass_transform_argument(command, subtransformation_name, subtransformation_argument) - end - else - pass_transform_argument(command, name, argument_or_subtransformations) - end - end - end - - image.format(format) if format - - image.tempfile.tap(&:open) - end - - # Returns the ImageProcessing processor class specified by `ActiveStorage.variant_processor`. - def processor - begin - require "image_processing" - rescue LoadError - ActiveSupport::Deprecation.warn("Using mini_magick gem directly is deprecated and will be removed in Rails 6.1. Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.") - return nil - end - - ImageProcessing.const_get(ActiveStorage.variant_processor.to_s.camelize) if ActiveStorage.variant_processor - end - - def pass_transform_argument(command, method, argument) - if argument == true - command.public_send(method) - elsif argument.present? - command.public_send(method, argument) + else + ActiveStorage::Transformers::MiniMagickTransformer.new(transformations) end end end diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb index 928db32691..d3e3a2f49b 100644 --- a/activestorage/lib/active_storage.rb +++ b/activestorage/lib/active_storage.rb @@ -50,4 +50,12 @@ module ActiveStorage mattr_accessor :variable_content_types, default: [] mattr_accessor :content_types_to_serve_as_binary, default: [] mattr_accessor :service_urls_expire_in, default: 5.minutes + + module Transformers + extend ActiveSupport::Autoload + + autoload :Transformer + autoload :ImageProcessingTransformer + autoload :MiniMagickTransformer + end end diff --git a/activestorage/lib/active_storage/errors.rb b/activestorage/lib/active_storage/errors.rb index bedcd080c4..f4bf66a615 100644 --- a/activestorage/lib/active_storage/errors.rb +++ b/activestorage/lib/active_storage/errors.rb @@ -1,11 +1,22 @@ # frozen_string_literal: true module ActiveStorage - class InvariableError < StandardError; end - class UnpreviewableError < StandardError; end - class UnrepresentableError < StandardError; end + # Generic base class for all Active Storage exceptions. + class Error < StandardError; end + + # Raised when ActiveStorage::Blob#variant is called on a blob that isn't variable. + # Use ActiveStorage::Blob#variable? to determine whether a blob is variable. + class InvariableError < Error; end + + # Raised when ActiveStorage::Blob#preview is called on a blob that isn't previewable. + # Use ActiveStorage::Blob#previewable? to determine whether a blob is previewable. + class UnpreviewableError < Error; end + + # Raised when ActiveStorage::Blob#representation is called on a blob that isn't representable. + # Use ActiveStorage::Blob#representable? to determine whether a blob is representable. + class UnrepresentableError < Error; end # Raised when uploaded or downloaded data does not match a precomputed checksum. # Indicates that a network error or a software bug caused data corruption. - class IntegrityError < StandardError; end + class IntegrityError < Error; end end diff --git a/activestorage/lib/active_storage/transformers/image_processing_transformer.rb b/activestorage/lib/active_storage/transformers/image_processing_transformer.rb new file mode 100644 index 0000000000..7f8685b72d --- /dev/null +++ b/activestorage/lib/active_storage/transformers/image_processing_transformer.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "image_processing" + +module ActiveStorage + module Transformers + class ImageProcessingTransformer < Transformer + private + def process(file, format:) + processor. + source(file). + loader(page: 0). + convert(format). + apply(operations). + call + end + + def processor + ImageProcessing.const_get(ActiveStorage.variant_processor.to_s.camelize) + end + + def operations + transformations.each_with_object([]) do |(name, argument), list| + if name.to_s == "combine_options" + ActiveSupport::Deprecation.warn <<~WARNING + Active Storage's ImageProcessing transformer doesn't support :combine_options, + as it always generates a single ImageMagick command. Passing :combine_options will + not be supported in Rails 6.1. + WARNING + + list.concat argument.keep_if { |key, value| value.present? }.to_a + elsif argument.present? + list << [ name, argument ] + end + end + end + end + end +end diff --git a/activestorage/lib/active_storage/transformers/mini_magick_transformer.rb b/activestorage/lib/active_storage/transformers/mini_magick_transformer.rb new file mode 100644 index 0000000000..e8e99cea9e --- /dev/null +++ b/activestorage/lib/active_storage/transformers/mini_magick_transformer.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "mini_magick" + +module ActiveStorage + module Transformers + class MiniMagickTransformer < Transformer + private + def process(file, format:) + image = MiniMagick::Image.new(file.path, file) + + transformations.each do |name, argument_or_subtransformations| + image.mogrify do |command| + if name.to_s == "combine_options" + argument_or_subtransformations.each do |subtransformation_name, subtransformation_argument| + pass_transform_argument(command, subtransformation_name, subtransformation_argument) + end + else + pass_transform_argument(command, name, argument_or_subtransformations) + end + end + end + + image.format(format) if format + + image.tempfile.tap(&:open) + end + + def pass_transform_argument(command, method, argument) + if argument == true + command.public_send(method) + elsif argument.present? + command.public_send(method, argument) + end + end + end + end +end diff --git a/activestorage/lib/active_storage/transformers/transformer.rb b/activestorage/lib/active_storage/transformers/transformer.rb new file mode 100644 index 0000000000..2e21201004 --- /dev/null +++ b/activestorage/lib/active_storage/transformers/transformer.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module ActiveStorage + module Transformers + # A Transformer applies a set of transformations to an image. + # + # The following concrete subclasses are included in Active Storage: + # + # * ActiveStorage::Transformers::ImageProcessingTransformer: + # backed by ImageProcessing, a common interface for MiniMagick and ruby-vips + # + # * ActiveStorage::Transformers::MiniMagickTransformer: + # backed by MiniMagick, a wrapper around the ImageMagick CLI + class Transformer + attr_reader :transformations + + def initialize(transformations) + @transformations = transformations + end + + # Applies the transformations to the source image in +file+, producing a target image in the + # specified +format+. Yields an open Tempfile containing the target image. Closes and unlinks + # the output tempfile after yielding to the given block. Returns the result of the block. + def transform(file, format:) + output = process(file, format: format) + + begin + yield output + ensure + output.close! + end + end + + private + # Returns an open Tempfile containing a transformed image in the given +format+. + # All subclasses implement this method. + def process(file, format:) #:doc: + raise NotImplementedError + end + end + end +end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 5423d59984..25e2ee04f9 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -96,7 +96,7 @@ *Godfrey Chan* -* Fix bug where `URI.unscape` would fail with mixed Unicode/escaped character input: +* Fix bug where `URI.unescape` would fail with mixed Unicode/escaped character input: URI.unescape("\xe3\x83\x90") # => "バ" URI.unescape("%E3%83%90") # => "バ" diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index 057bcf2c1b..7a414f21fe 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -377,30 +377,38 @@ command inside of the `activestorage` directory to install the dependencies: yarn install ``` -Extracting previews, tested in ActiveStorage's test suite requires third-party +Extracting previews, tested in Active Storage's test suite requires third-party applications, FFmpeg for video and muPDF for PDFs, and on macOS also XQuartz -and Poppler. Without these applications installed, ActiveStorage tests will +and Poppler. Without these applications installed, Active Storage tests will raise errors. On macOS you can run: ```bash -brew install ffmpeg -brew cask install xquartz -brew install mupdf-tools -brew install poppler +$ brew install ffmpeg +$ brew cask install xquartz +$ brew install mupdf-tools +$ brew install poppler ``` On Ubuntu, you can run: ```bash -sudo apt-get update && install ffmpeg -sudo apt-get update && install mupdf mupdf-tools +$ sudo apt-get update +$ sudo apt-get install ffmpeg +$ sudo apt-get install mupdf mupdf-tools ``` On Fedora or CentOS, just run: ```bash -sudo yum install ffmpeg -sudo yum install mupdf +$ sudo yum install ffmpeg +$ sudo yum install mupdf +``` + +FreeBSD users can just run: + +```bash +# pkg install ffmpeg +# pkg install mupdf ``` diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 985e9ab263..a6dc60342c 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -458,6 +458,12 @@ module Rails end end + def generate_bundler_binstub + if bundle_install? + bundle_command("binstubs bundler") + end + end + def generate_spring_binstubs if bundle_install? && spring_install? bundle_command("exec spring binstub --all") diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 83c5c9f297..a6d160f1eb 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -469,7 +469,8 @@ module Rails end public_task :apply_rails_template, :run_bundle - public_task :run_webpack, :generate_spring_binstubs + public_task :generate_bundler_binstub, :generate_spring_binstubs + public_task :run_webpack def run_after_bundle_callbacks @after_bundle_callbacks.each(&:call) diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt b/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt deleted file mode 100644 index a84f0afe47..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt +++ /dev/null @@ -1,2 +0,0 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -load Gem.bin_path('bundler', 'bundle') diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index d646694477..888336af92 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -69,7 +69,7 @@ Rails.application.configure do # Use a real queuing backend for Active Job (and separate queues per environment). # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "<%= app_name %>_#{Rails.env}" + # config.active_job.queue_name_prefix = "<%= app_name %>_production" <%- unless options.skip_action_mailer? -%> config.action_mailer.perform_caching = false diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index 455dc60efd..5c34b205c9 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -525,9 +525,18 @@ module ApplicationTests def test_run_in_parallel_with_processes file_name = create_parallel_processes_test_file + app_file "db/schema.rb", <<-RUBY + ActiveRecord::Schema.define(version: 1) do + create_table :users do |t| + t.string :name + end + end + RUBY + output = run_test_command(file_name) assert_match %r{Finished in.*\n2 runs, 2 assertions}, output + assert_no_match "create_table(:users)", output end def test_run_in_parallel_with_threads @@ -539,9 +548,18 @@ module ApplicationTests file_name = create_parallel_threads_test_file + app_file "db/schema.rb", <<-RUBY + ActiveRecord::Schema.define(version: 1) do + create_table :users do |t| + t.string :name + end + end + RUBY + output = run_test_command(file_name) assert_match %r{Finished in.*\n2 runs, 2 assertions}, output + assert_no_match "create_table(:users)", output end def test_raise_error_when_specified_file_does_not_exist diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb index 9c523ad372..c2540f4091 100644 --- a/railties/test/generators/api_app_generator_test.rb +++ b/railties/test/generators/api_app_generator_test.rb @@ -118,7 +118,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase app/views/layouts app/views/layouts/mailer.html.erb app/views/layouts/mailer.text.erb - bin/bundle bin/rails bin/rake bin/setup diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index b0f958091c..f33a7bd99d 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -37,7 +37,6 @@ DEFAULT_APP_FILES = %w( app/views/layouts/application.html.erb app/views/layouts/mailer.html.erb app/views/layouts/mailer.text.erb - bin/bundle bin/rails bin/rake bin/setup @@ -763,17 +762,23 @@ class AppGeneratorTest < Rails::Generators::TestCase end def test_generation_runs_bundle_install - assert_generates_with_bundler + generator([destination_root], {}) + + assert_bundler_command_called("install") end def test_dev_option - assert_generates_with_bundler dev: true + generator([destination_root], dev: true) + + assert_bundler_command_called("install") rails_path = File.expand_path("../../..", Rails.root) assert_file "Gemfile", /^gem\s+["']rails["'],\s+path:\s+["']#{Regexp.escape(rails_path)}["']$/ end def test_edge_option - assert_generates_with_bundler edge: true + generator([destination_root], edge: true) + + assert_bundler_command_called("install") assert_file "Gemfile", %r{^gem\s+["']rails["'],\s+github:\s+["']#{Regexp.escape("rails/rails")}["']$} end @@ -782,23 +787,14 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_gem "spring" end + def test_bundler_binstub + assert_bundler_command_called("binstubs bundler") + end + def test_spring_binstubs jruby_skip "spring doesn't run on JRuby" - command_check = -> command do - @binstub_called ||= 0 - - case command - when "install" - # Called when running bundle, we just want to stub it so nothing to do here. - when "exec spring binstub --all" - @binstub_called += 1 - assert_equal 1, @binstub_called, "exec spring binstub --all expected to be called once, but was called #{@install_called} times." - end - end - generator.stub :bundle_command, command_check do - quietly { generator.invoke_all } - end + assert_bundler_command_called("exec spring binstub --all") end def test_spring_no_fork @@ -976,7 +972,7 @@ class AppGeneratorTest < Rails::Generators::TestCase template end - sequence = ["git init", "install", "exec spring binstub --all", "echo ran after_bundle"] + sequence = ["git init", "install", "binstubs bundler", "exec spring binstub --all", "echo ran after_bundle"] @sequence_step ||= 0 ensure_bundler_first = -> command, options = nil do assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" @@ -993,7 +989,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - assert_equal 4, @sequence_step + assert_equal 5, @sequence_step end def test_gitignore @@ -1063,18 +1059,14 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def assert_generates_with_bundler(options = {}) - generator([destination_root], options) - + def assert_bundler_command_called(target_command) command_check = -> command do - @install_called ||= 0 + @command_called ||= 0 case command - when "install" - @install_called += 1 - assert_equal 1, @install_called, "install expected to be called once, but was called #{@install_called} times" - when "exec spring binstub --all" - # Called when running tests with spring, let through unscathed. + when target_command + @command_called += 1 + assert_equal 1, @command_called, "#{command} expected to be called once, but was called #{@command_called} times." end end |