diff options
72 files changed, 630 insertions, 319 deletions
diff --git a/.travis.yml b/.travis.yml index 8317b58c52..74f8d4795f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: xenial language: ruby sudo: false @@ -12,24 +13,20 @@ cache: services: - memcached - redis-server + - mysql addons: postgresql: 10 chrome: stable apt: sources: - - sourceline: "ppa:mc3man/trusty-media" + - sourceline: "ppa:jonathonf/ffmpeg-3" - sourceline: "ppa:ubuntuhandbook1/apps" - - mysql-5.7-trusty packages: - ffmpeg - mupdf - mupdf-tools - poppler-utils - - mysql-server - - mysql-client - - postgresql-10 - - postgresql-client-10 bundler_args: --jobs 3 --retry 3 before_install: @@ -43,6 +40,7 @@ before_install: - "[[ $GEM != 'actionview:ujs' ]] || (cd actionview && npm install)" - "[[ $GEM != 'railties' ]] || (curl -o- -L https://yarnpkg.com/install.sh | bash)" - "[[ $GEM != 'railties' ]] || export PATH=$HOME/.yarn/bin:$PATH" + - "[[ $GEM != 'activerecord:postgresql' ]] || sudo mount -o remount,size=50% /var/ramfs" before_script: # Set Sauce Labs username and access key. Obfuscated, purposefully not encrypted. @@ -56,10 +54,13 @@ env: global: - "JRUBY_OPTS='--dev -J-Xmx1024M'" matrix: + - "GEM=railties" - "GEM=actionpack,actioncable" - "GEM=actionmailer,activemodel,activesupport,actionview,activejob,activestorage,actionmailbox,actiontext" - "GEM=activesupport PRESERVE_TIMEZONES=1" - "GEM=activerecord:sqlite3" + - "GEM=activerecord:postgresql" + - "GEM=activerecord:mysql2" - "GEM=guides" - "GEM=actioncable:integration" @@ -71,126 +72,72 @@ rvm: matrix: include: - rvm: 2.5.3 - env: "GEM=railties" - sudo: required - before_install: - - "rm ${BUNDLE_GEMFILE}.lock" - - "travis_retry gem update --system" - - "travis_retry gem install bundler" - - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf" - - "sudo service postgresql restart 10" - - rvm: 2.6.0 - env: "GEM=railties" - sudo: required - before_install: - - "rm ${BUNDLE_GEMFILE}.lock" - - "travis_retry gem update --system" - - "travis_retry gem install bundler" - - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf" - - "sudo service postgresql restart 10" - - rvm: ruby-head - env: "GEM=railties" - sudo: required - before_install: - - "rm ${BUNDLE_GEMFILE}.lock" - - "travis_retry gem update --system" - - "travis_retry gem install bundler" - - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf" - - "sudo service postgresql restart 10" - - rvm: 2.5.3 env: "GEM=actionview:ujs" - rvm: 2.5.3 sudo: required env: "GEM=activejob:integration" + addons: + postgresql: 10 + apt: + packages: + - rabbitmq-server services: - memcached - redis-server - - rabbitmq + - rabbitmq-server before_install: - - sudo sed -i -e '/local.*peer/s/postgres/all/' -e 's/peer\|md5/trust/g' /etc/postgresql/*/main/pg_hba.conf - - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf" - - "sudo service postgresql restart 10" - "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/beanstalkd/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)" - "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd" - rvm: 2.6.0 sudo: required env: "GEM=activejob:integration" + addons: + postgresql: 10 + apt: + packages: + - rabbitmq-server services: - memcached - redis-server - - rabbitmq + - rabbitmq-server before_install: - - sudo sed -i -e '/local.*peer/s/postgres/all/' -e 's/peer\|md5/trust/g' /etc/postgresql/*/main/pg_hba.conf - - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf" - - "sudo service postgresql restart 10" - "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/beanstalkd/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)" - "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd" - rvm: ruby-head sudo: required env: "GEM=activejob:integration" + addons: + postgresql: 10 + apt: + packages: + - rabbitmq-server services: - memcached - redis-server - - rabbitmq + - rabbitmq-server before_install: - - sudo sed -i -e '/local.*peer/s/postgres/all/' -e 's/peer\|md5/trust/g' /etc/postgresql/*/main/pg_hba.conf - - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf" - - "sudo service postgresql restart 10" + - "rm ${BUNDLE_GEMFILE}.lock" + - "travis_retry gem update --system" + - "travis_retry gem install bundler" - "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/beanstalkd/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)" - "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd" - rvm: 2.5.3 - env: "GEM=activerecord:mysql2" - sudo: required - before_install: - - "sudo mysql -e \"use mysql; update user set authentication_string='' where User='root'; update user set plugin='mysql_native_password';FLUSH PRIVILEGES;\"" - - "sudo mysql_upgrade" - - "sudo service mysql restart" - - rvm: 2.6.0 - env: "GEM=activerecord:mysql2" - sudo: required - before_install: - - "sudo mysql -e \"use mysql; update user set authentication_string='' where User='root'; update user set plugin='mysql_native_password';FLUSH PRIVILEGES;\"" - - "sudo mysql_upgrade" - - "sudo service mysql restart" - - rvm: ruby-head - env: "GEM=activerecord:mysql2" - sudo: required + env: + - "GEM=activerecord:mysql2 MYSQL=mariadb" before_install: - - "sudo mysql -e \"use mysql; update user set authentication_string='' where User='root'; update user set plugin='mysql_native_password';FLUSH PRIVILEGES;\"" - "sudo mysql_upgrade" - "sudo service mysql restart" - - rvm: 2.5.3 - env: - - "GEM=activerecord:mysql2 MYSQL=mariadb" addons: mariadb: 10.3 - rvm: 2.5.3 env: - "GEM=activerecord:sqlite3_mem" - - rvm: 2.5.3 - env: "GEM=activerecord:postgresql" - sudo: required - before_install: - - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf" - - "sudo service postgresql restart 10" - - rvm: 2.6.0 - env: "GEM=activerecord:postgresql" - sudo: required - before_install: - - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf" - - "sudo service postgresql restart 10" - - rvm: ruby-head - env: "GEM=activerecord:postgresql" - sudo: required - before_install: - - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf" - - "sudo service postgresql restart 10" - rvm: jruby-head - jdk: oraclejdk8 + jdk: oraclejdk11 env: - "GEM=actionpack" - rvm: jruby-head - jdk: oraclejdk8 + jdk: oraclejdk11 env: - "GEM=actionmailer,activemodel,activejob" allow_failures: @@ -52,7 +52,7 @@ group :job do gem "sidekiq", require: false gem "sucker_punch", require: false gem "delayed_job", require: false - gem "queue_classic", github: "rafaelfranca/queue_classic", branch: "update-pg", require: false, platforms: :ruby + gem "queue_classic", github: "QueueClassic/queue_classic", require: false, platforms: :ruby gem "sneakers", require: false gem "que", require: false gem "backburner", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 70a29f501d..1f02b9c4c7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,4 +1,11 @@ GIT + remote: https://github.com/QueueClassic/queue_classic.git + revision: 4cdc9b8e804badf7dea7078dd81092972d292c14 + specs: + queue_classic (3.2.0.RC1) + pg (>= 0.17, < 2.0) + +GIT remote: https://github.com/matthewd/websocket-client-simple.git revision: e161305f1a466b9398d86df3b1731b03362da91b branch: close-race @@ -7,14 +14,6 @@ GIT event_emitter websocket -GIT - remote: https://github.com/rafaelfranca/queue_classic.git - revision: dee64b361355d56700ad7aa3b151bf653a617526 - branch: update-pg - specs: - queue_classic (3.2.0.RC1) - pg (>= 0.17, < 2.0) - PATH remote: . specs: diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md index 4109dd6138..f0a01dce47 100644 --- a/actioncable/CHANGELOG.md +++ b/actioncable/CHANGELOG.md @@ -1,5 +1,30 @@ +* Add `Channel::Base#broadcast_to`. + + You can now call `broadcast_to` within a channel action, which equals to + the `self.class.broadcast_to`. + + *Vladimir Dementyev* + +* Make `Channel::Base.broadcasting_for` a public API. + + You can use `.broadcasting_for` to generate a unique stream identifier within + a channel for the specified target (e.g. Active Record model): + + ```ruby + ChatChannel.broadcasting_for(model) # => "chat:<model.to_gid_param>" + ``` + + *Vladimir Dementyev* + + ## Rails 6.0.0.beta1 (January 18, 2019) ## +* [Rename npm package](https://github.com/rails/rails/pull/34905) from + [`actioncable`](https://www.npmjs.com/package/actioncable) to + [`@rails/actioncable`](https://www.npmjs.com/package/@rails/actioncable). + + *Javan Makhmali* + * Merge [`action-cable-testing`](https://github.com/palkan/action-cable-testing) to Rails. *Vladimir Dementyev* diff --git a/actioncable/lib/action_cable/channel/broadcasting.rb b/actioncable/lib/action_cable/channel/broadcasting.rb index 9a96720f4a..9f702e425e 100644 --- a/actioncable/lib/action_cable/channel/broadcasting.rb +++ b/actioncable/lib/action_cable/channel/broadcasting.rb @@ -7,22 +7,32 @@ module ActionCable module Broadcasting extend ActiveSupport::Concern - delegate :broadcasting_for, to: :class + delegate :broadcasting_for, :broadcast_to, to: :class module ClassMethods # Broadcast a hash to a unique broadcasting for this <tt>model</tt> in this channel. def broadcast_to(model, message) - ActionCable.server.broadcast(broadcasting_for([ channel_name, model ]), message) + ActionCable.server.broadcast(broadcasting_for(model), message) end - def broadcasting_for(model) #:nodoc: + # Returns a unique broadcasting identifier for this <tt>model</tt> in this channel: + # + # CommentsChannel.broadcasting_for("all") # => "comments:all" + # + # You can pass any object as a target (e.g. Active Record model), and it + # would be serialized into a string under the hood. + def broadcasting_for(model) + serialize_broadcasting([ channel_name, model ]) + end + + def serialize_broadcasting(object) #:nodoc: case - when model.is_a?(Array) - model.map { |m| broadcasting_for(m) }.join(":") - when model.respond_to?(:to_gid_param) - model.to_gid_param + when object.is_a?(Array) + object.map { |m| serialize_broadcasting(m) }.join(":") + when object.respond_to?(:to_gid_param) + object.to_gid_param else - model.to_param + object.to_param end end end diff --git a/actioncable/lib/action_cable/channel/streams.rb b/actioncable/lib/action_cable/channel/streams.rb index 81c2c38064..7e1ed3c850 100644 --- a/actioncable/lib/action_cable/channel/streams.rb +++ b/actioncable/lib/action_cable/channel/streams.rb @@ -99,7 +99,7 @@ module ActionCable # Pass <tt>coder: ActiveSupport::JSON</tt> to decode messages as JSON before passing to the callback. # Defaults to <tt>coder: nil</tt> which does no decoding, passes raw messages. def stream_for(model, callback = nil, coder: nil, &block) - stream_from(broadcasting_for([ channel_name, model ]), callback || block, coder: coder) + stream_from(broadcasting_for(model), callback || block, coder: coder) end # Unsubscribes all streams associated with this channel from the pubsub queue. diff --git a/actioncable/lib/action_cable/channel/test_case.rb b/actioncable/lib/action_cable/channel/test_case.rb index c4cf0ac0e7..b05d51a61a 100644 --- a/actioncable/lib/action_cable/channel/test_case.rb +++ b/actioncable/lib/action_cable/channel/test_case.rb @@ -143,7 +143,7 @@ module ActionCable # You need to set up your connection manually to provide values for the identifiers. # To do this just use: # - # stub_connection(user: users[:john]) + # stub_connection(user: users(:john)) # # == Testing broadcasting # @@ -157,9 +157,9 @@ module ActionCable # end # # def test_speak - # subscribe room_id: rooms[:chat].id + # subscribe room_id: rooms(:chat).id # - # assert_broadcasts_on(rooms[:chat], text: "Hello, Rails!") do + # assert_broadcasts_on(rooms(:chat), text: "Hello, Rails!") do # perform :speak, message: "Hello, Rails!" # end # end @@ -300,9 +300,7 @@ module ActionCable def broadcasting_for(stream_or_object) return stream_or_object if stream_or_object.is_a?(String) - self.class.channel_class.broadcasting_for( - [self.class.channel_class.channel_name, stream_or_object] - ) + self.class.channel_class.broadcasting_for(stream_or_object) end end diff --git a/actioncable/lib/action_cable/connection/test_case.rb b/actioncable/lib/action_cable/connection/test_case.rb index 26a183d1ec..8d25a55c8a 100644 --- a/actioncable/lib/action_cable/connection/test_case.rb +++ b/actioncable/lib/action_cable/connection/test_case.rb @@ -42,8 +42,6 @@ module ActionCable class TestRequest < ActionDispatch::TestRequest attr_accessor :session, :cookie_jar - - attr_writer :cookie_jar end module TestConnection diff --git a/actioncable/test/channel/broadcasting_test.rb b/actioncable/test/channel/broadcasting_test.rb index 2cbfabc1d0..fb501a1bc2 100644 --- a/actioncable/test/channel/broadcasting_test.rb +++ b/actioncable/test/channel/broadcasting_test.rb @@ -26,14 +26,23 @@ class ActionCable::Channel::BroadcastingTest < ActionCable::TestCase end test "broadcasting_for with an object" do - assert_equal "Room#1-Campfire", ChatChannel.broadcasting_for(Room.new(1)) + assert_equal( + "action_cable:channel:broadcasting_test:chat:Room#1-Campfire", + ChatChannel.broadcasting_for(Room.new(1)) + ) end test "broadcasting_for with an array" do - assert_equal "Room#1-Campfire:Room#2-Campfire", ChatChannel.broadcasting_for([ Room.new(1), Room.new(2) ]) + assert_equal( + "action_cable:channel:broadcasting_test:chat:Room#1-Campfire:Room#2-Campfire", + ChatChannel.broadcasting_for([ Room.new(1), Room.new(2) ]) + ) end test "broadcasting_for with a string" do - assert_equal "hello", ChatChannel.broadcasting_for("hello") + assert_equal( + "action_cable:channel:broadcasting_test:chat:hello", + ChatChannel.broadcasting_for("hello") + ) end end diff --git a/actioncable/test/channel/test_case_test.rb b/actioncable/test/channel/test_case_test.rb index 9c360d5dc3..a166c41e11 100644 --- a/actioncable/test/channel/test_case_test.rb +++ b/actioncable/test/channel/test_case_test.rb @@ -180,7 +180,7 @@ class BroadcastsTestChannel < ActionCable::Channel::Base def broadcast_to_user(data) user = User.new user_id - self.class.broadcast_to user, text: data["message"] + broadcast_to user, text: data["message"] end end diff --git a/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailbox_tables.rb b/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailbox_tables.rb index 89ab66c1a9..2bf4335808 100644 --- a/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailbox_tables.rb +++ b/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailbox_tables.rb @@ -5,11 +5,7 @@ class CreateActionMailboxTables < ActiveRecord::Migration[6.0] t.string :message_id, null: false t.string :message_checksum, null: false - if supports_datetime_with_precision? - t.timestamps precision: 6 - else - t.timestamps - end + t.timestamps t.index [ :message_id, :message_checksum ], name: "index_action_mailbox_inbound_emails_uniqueness", unique: true end diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index fb2b2bd3b0..1fb3e9db00 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -31,22 +31,34 @@ module ActionDispatch "ActionController::MissingExactTemplate" => "missing_exact_template", ) + cattr_accessor :wrapper_exceptions, default: [ + "ActionView::Template::Error" + ] + attr_reader :backtrace_cleaner, :exception, :wrapped_causes, :line_number, :file def initialize(backtrace_cleaner, exception) @backtrace_cleaner = backtrace_cleaner - @exception = original_exception(exception) + @exception = exception @wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner) expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError) end + def unwrapped_exception + if wrapper_exceptions.include?(exception.class.to_s) + exception.cause + else + exception + end + end + def rescue_template @@rescue_templates[@exception.class.name] end def status_code - self.class.status_code_for_exception(@exception.class.name) + self.class.status_code_for_exception(unwrapped_exception.class.name) end def application_trace @@ -122,14 +134,6 @@ module ActionDispatch Array(@exception.backtrace) end - def original_exception(exception) - if @@rescue_responses.has_key?(exception.cause.class.name) - exception.cause - else - exception - end - end - def causes_for(exception) return enum_for(__method__, exception) unless block_given? diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 3c88afd4d3..767143a368 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -45,7 +45,7 @@ module ActionDispatch backtrace_cleaner = request.get_header "action_dispatch.backtrace_cleaner" wrapper = ExceptionWrapper.new(backtrace_cleaner, exception) status = wrapper.status_code - request.set_header "action_dispatch.exception", wrapper.exception + request.set_header "action_dispatch.exception", wrapper.unwrapped_exception request.set_header "action_dispatch.original_path", request.path_info request.path_info = "/#{status}" response = @exceptions_app.call(request.env) diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb index 2fa78dd385..1fbc107e28 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb @@ -2,6 +2,6 @@ <h1>Blocked host: <%= @host %></h1> </header> <div id="container"> - <h2>To allow requests to <%= @host %>, add the following configuration:</h2> - <pre>Rails.application.config.hosts << "<%= @host %>"</pre> + <h2>To allow requests to <%= @host %>, add the following to your environment configuration:</h2> + <pre>config.hosts << "<%= @host %>"</pre> </div> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb index 4e2d1d0b08..a94dd982a7 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb @@ -1,5 +1,5 @@ Blocked host: <%= @host %> -To allow requests to <%= @host %>, add the following configuration: +To allow requests to <%= @host %>, add the following to your environment configuration: - Rails.application.config.hosts << "<%= @host %>" + config.hosts << "<%= @host %>" diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 998498e1b2..7f1c41787a 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -68,10 +68,18 @@ class RedirectController < ActionController::Base redirect_back(fallback_location: "/things/stuff", status: 307) end + def redirect_back_with_status_and_fallback_location_to_another_host + redirect_back(fallback_location: "http://www.rubyonrails.org/", status: 307) + end + def safe_redirect_back_with_status redirect_back(fallback_location: "/things/stuff", status: 307, allow_other_host: false) end + def safe_redirect_back_with_status_and_fallback_location_to_another_host + redirect_back(fallback_location: "http://www.rubyonrails.org/", status: 307, allow_other_host: false) + end + def host_redirect redirect_to action: "other_host", only_path: false, host: "other.test.host" end @@ -280,6 +288,13 @@ class RedirectTest < ActionController::TestCase assert_equal "http://test.host/things/stuff", redirect_to_url end + def test_redirect_back_with_no_referer_redirects_to_another_host + get :redirect_back_with_status_and_fallback_location_to_another_host + + assert_response 307 + assert_equal "http://www.rubyonrails.org/", redirect_to_url + end + def test_safe_redirect_back_from_other_host @request.env["HTTP_REFERER"] = "http://another.host/coming/from" get :safe_redirect_back_with_status @@ -297,6 +312,20 @@ class RedirectTest < ActionController::TestCase assert_equal referer, redirect_to_url end + def test_safe_redirect_back_with_no_referer + get :safe_redirect_back_with_status + + assert_response 307 + assert_equal "http://test.host/things/stuff", redirect_to_url + end + + def test_safe_redirect_back_with_no_referer_redirects_to_another_host + get :safe_redirect_back_with_status_and_fallback_location_to_another_host + + assert_response 307 + assert_equal "http://www.rubyonrails.org/", redirect_to_url + end + def test_redirect_to_record with_routing do |set| set.draw do diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index aadc6be077..6914fb66f9 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -39,52 +39,56 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest def call(env) env["action_dispatch.show_detailed_exceptions"] = @detailed req = ActionDispatch::Request.new(env) + template = ActionView::Template.new(File.read(__FILE__), __FILE__, ActionView::Template::Handlers::Raw.new, {}) + case req.path - when %r{/pass} + when "/pass" [404, { "X-Cascade" => "pass" }, self] - when %r{/not_found} + when "/not_found" raise AbstractController::ActionNotFound - when %r{/runtime_error} + when "/runtime_error" raise RuntimeError - when %r{/method_not_allowed} + when "/method_not_allowed" raise ActionController::MethodNotAllowed - when %r{/intercepted_error} + when "/intercepted_error" raise InterceptedErrorInstance - when %r{/unknown_http_method} + when "/unknown_http_method" raise ActionController::UnknownHttpMethod - when %r{/not_implemented} + when "/not_implemented" raise ActionController::NotImplemented - when %r{/unprocessable_entity} + when "/unprocessable_entity" raise ActionController::InvalidAuthenticityToken - when %r{/not_found_original_exception} + when "/not_found_original_exception" begin raise AbstractController::ActionNotFound.new rescue - raise ActionView::Template::Error.new("template") + raise ActionView::Template::Error.new(template) + end + when "/cause_mapped_to_rescue_responses" + begin + raise ActionController::ParameterMissing, :missing_param_key + rescue + raise NameError.new("uninitialized constant Userr") end - when %r{/missing_template} + when "/missing_template" raise ActionView::MissingTemplate.new(%w(foo), "foo/index", %w(foo), false, "mailer") - when %r{/bad_request} + when "/bad_request" raise ActionController::BadRequest - when %r{/missing_keys} + when "/missing_keys" raise ActionController::UrlGenerationError, "No route matches" - when %r{/parameter_missing} + when "/parameter_missing" raise ActionController::ParameterMissing, :missing_param_key - when %r{/original_syntax_error} + when "/original_syntax_error" eval "broke_syntax =" # `eval` need for raise native SyntaxError at runtime - when %r{/syntax_error_into_view} + when "/syntax_error_into_view" begin eval "broke_syntax =" rescue Exception - template = ActionView::Template.new(File.read(__FILE__), - __FILE__, - ActionView::Template::Handlers::Raw.new, - {}) raise ActionView::Template::Error.new(template) end - when %r{/framework_raises} + when "/framework_raises" method_that_raises - when %r{/nested_exceptions} + when "/nested_exceptions" raise_nested_exceptions else raise "puke!" @@ -313,12 +317,22 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_match(""foo"=>"[FILTERED]"", body) end - test "show registered original exception for wrapped exceptions" do + test "show registered original exception if the last exception is TemplateError" do @app = DevelopmentApp get "/not_found_original_exception", headers: { "action_dispatch.show_exceptions" => true } assert_response 404 - assert_match(/AbstractController::ActionNotFound/, body) + assert_match %r{AbstractController::ActionNotFound}, body + assert_match %r{Showing <i>.*test/dispatch/debug_exceptions_test.rb</i>}, body + end + + test "show the last exception and cause even when the cause is mapped to resque_responses" do + @app = DevelopmentApp + + get "/cause_mapped_to_rescue_responses", headers: { "action_dispatch.show_exceptions" => true } + assert_response 500 + assert_match %r{ActionController::ParameterMissing}, body + assert_match %r{NameError}, body end test "named urls missing keys raise 500 level error" do @@ -480,6 +494,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_select "#Application-Trace-0" do assert_select "code", /syntax error, unexpected/ end + assert_match %r{Showing <i>.*test/dispatch/debug_exceptions_test.rb</i>}, body end test "debug exceptions app shows user code that caused the error in source view" do diff --git a/actiontext/app/helpers/action_text/tag_helper.rb b/actiontext/app/helpers/action_text/tag_helper.rb index 837b2264b1..8434f2c611 100644 --- a/actiontext/app/helpers/action_text/tag_helper.rb +++ b/actiontext/app/helpers/action_text/tag_helper.rb @@ -4,7 +4,7 @@ module ActionText module TagHelper cattr_accessor(:id, instance_accessor: false) { 0 } - # Returns a `trix-editor` tag that instantiates the Trix JavaScript editor as well as a hidden field + # Returns a +trix-editor+ tag that instantiates the Trix JavaScript editor as well as a hidden field # that Trix will write to on changes, so the content will be sent on form submissions. # # ==== Options @@ -50,7 +50,7 @@ module ActionView::Helpers end module FormHelper - # Returns a `trix-editor` tag that instantiates the Trix JavaScript editor as well as a hidden field + # Returns a +trix-editor+ tag that instantiates the Trix JavaScript editor as well as a hidden field # that Trix will write to on changes, so the content will be sent on form submissions. # # ==== Options diff --git a/actiontext/app/models/action_text/rich_text.rb b/actiontext/app/models/action_text/rich_text.rb index 705dd30983..19fa3e030e 100644 --- a/actiontext/app/models/action_text/rich_text.rb +++ b/actiontext/app/models/action_text/rich_text.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true module ActionText - # The RichText record holds the content produced by the Trix editor in a serialized `body` attribute. + # The RichText record holds the content produced by the Trix editor in a serialized +body+ attribute. # It also holds all the references to the embedded files, which are stored using Active Storage. # This record is then associated with the Active Record model the application desires to have - # rich text content using the `has_rich_text` class method. + # rich text content using the +has_rich_text+ class method. class RichText < ActiveRecord::Base self.table_name = "action_text_rich_texts" @@ -15,7 +15,7 @@ module ActionText has_many_attached :embeds before_save do - self.embeds = body.attachments.map(&:attachable) if body.present? + self.embeds = body.attachables.grep(ActiveStorage::Blob) if body.present? end def to_plain_text diff --git a/actiontext/app/views/active_storage/blobs/_blob.html.erb b/actiontext/app/views/active_storage/blobs/_blob.html.erb index 049f57e804..49ba357dd1 100644 --- a/actiontext/app/views/active_storage/blobs/_blob.html.erb +++ b/actiontext/app/views/active_storage/blobs/_blob.html.erb @@ -1,6 +1,6 @@ <figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>"> <% if blob.representable? %> - <%= image_tag blob.representation(resize_to_fit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %> + <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %> <% end %> <figcaption class="attachment__caption"> diff --git a/actiontext/test/dummy/db/migrate/2018052816_create_action_text_tables.rb b/actiontext/test/dummy/db/migrate/2018052816_create_action_text_tables.rb index 74c7a0ecb9..6e7177620f 100644 --- a/actiontext/test/dummy/db/migrate/2018052816_create_action_text_tables.rb +++ b/actiontext/test/dummy/db/migrate/2018052816_create_action_text_tables.rb @@ -5,8 +5,7 @@ class CreateActionTextTables < ActiveRecord::Migration[6.0] t.text :body, limit: 16777215 t.references :record, null: false, polymorphic: true, index: false - t.datetime :created_at, null: false - t.datetime :updated_at, null: false + t.timestamps t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true end diff --git a/actiontext/test/dummy/db/schema.rb b/actiontext/test/dummy/db/schema.rb index 7f8f4dff4e..71080a1c69 100644 --- a/actiontext/test/dummy/db/schema.rb +++ b/actiontext/test/dummy/db/schema.rb @@ -17,8 +17,8 @@ ActiveRecord::Schema.define(version: 2018_10_03_185713) do t.text "body", limit: 16777215 t.string "record_type", null: false t.integer "record_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true end @@ -45,14 +45,14 @@ ActiveRecord::Schema.define(version: 2018_10_03_185713) do create_table "messages", force: :cascade do |t| t.string "subject" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false end create_table "people", force: :cascade do |t| t.string "name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false end end diff --git a/actiontext/test/unit/model_test.rb b/actiontext/test/unit/model_test.rb index 122a20700b..d56363adc0 100644 --- a/actiontext/test/unit/model_test.rb +++ b/actiontext/test/unit/model_test.rb @@ -35,6 +35,15 @@ class ActionText::ModelTest < ActiveSupport::TestCase assert_equal "racecar.jpg", message.content.embeds.first.filename.to_s end + test "embed extraction only extracts file attachments" do + remote_image_html = '<action-text-attachment content-type="image" url="http://example.com/cat.jpg"></action-text-attachment>' + blob = create_file_blob(filename: "racecar.jpg", content_type: "image/jpg") + content = ActionText::Content.new(remote_image_html).append_attachables(blob) + message = Message.create!(subject: "Greetings", content: content) + assert_equal [ActionText::Attachables::RemoteImage, ActiveStorage::Blob], message.content.body.attachables.map(&:class) + assert_equal [ActiveStorage::Attachment], message.content.embeds.map(&:class) + end + test "saving content" do message = Message.create!(subject: "Greetings", content: "<h1>Hello world</h1>") assert_equal "Hello world", message.content.to_plain_text diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 16361fd2eb..e2cd9f7558 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,5 +1,11 @@ ## Rails 6.0.0.beta1 (January 18, 2019) ## +* [Rename npm package](https://github.com/rails/rails/pull/34905) from + [`rails-ujs`](https://www.npmjs.com/package/rails-ujs) to + [`@rails/ujs`](https://www.npmjs.com/package/@rails/ujs). + + *Javan Makhmali* + * Remove deprecated `image_alt` helper. *Rafael Mendonça França* diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index c186cb8422..59d70a1dc4 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -329,14 +329,14 @@ module ActionView # image_tag("pic.jpg", srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], sizes: "100vw") # # => <img src="/assets/pic.jpg" srcset="/assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw"> # - # Active Storage (images that are uploaded by the users of your app): + # Active Storage blobs (images that are uploaded by the users of your app): # # image_tag(user.avatar) # # => <img src="/rails/active_storage/blobs/.../tiger.jpg" /> - # image_tag(user.avatar.variant(resize_to_fit: [100, 100])) - # # => <img src="/rails/active_storage/variants/.../tiger.jpg" /> - # image_tag(user.avatar.variant(resize_to_fit: [100, 100]), size: '100') - # # => <img width="100" height="100" src="/rails/active_storage/variants/.../tiger.jpg" /> + # image_tag(user.avatar.variant(resize_to_limit: [100, 100])) + # # => <img src="/rails/active_storage/representations/.../tiger.jpg" /> + # image_tag(user.avatar.variant(resize_to_limit: [100, 100]), size: '100') + # # => <img width="100" height="100" src="/rails/active_storage/representations/.../tiger.jpg" /> def image_tag(source, options = {}) options = options.symbolize_keys check_for_image_tag_errors(options) diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index cb850d75ee..f175c30aa1 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -363,7 +363,7 @@ module ActionView @options = options @block = block - @locals = options[:locals] ? options[:locals].symbolize_keys : {} + @locals = options[:locals] || {} @details = extract_details(options) prepend_formats(options[:formats]) diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb index bb9db21e32..f414620923 100644 --- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb @@ -43,14 +43,14 @@ module ActionView # For streaming, instead of rendering a given a template, we return a Body # object that responds to each. This object is initialized with a block # that knows how to render the template. - def render_template(template, layout_name = nil, locals = {}) #:nodoc: + def render_template(view, template, layout_name = nil, locals = {}) #:nodoc: return [super] unless layout_name && template.supports_streaming? locals ||= {} layout = layout_name && find_layout(layout_name, locals.keys, [formats.first]) Body.new do |buffer| - delayed_render(buffer, template, layout, @view, locals) + delayed_render(buffer, template, layout, view, locals) end end diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb index ce8908924a..fb5539e0db 100644 --- a/actionview/lib/action_view/renderer/template_renderer.rb +++ b/actionview/lib/action_view/renderer/template_renderer.rb @@ -5,7 +5,6 @@ require "active_support/core_ext/object/try" module ActionView class TemplateRenderer < AbstractRenderer #:nodoc: def render(context, options) - @view = context @details = extract_details(options) template = determine_template(options) @@ -13,7 +12,7 @@ module ActionView @lookup_context.rendered_format ||= (template.formats.first || formats.first) - render_template(template, options[:layout], options[:locals]) + render_template(context, template, options[:layout], options[:locals]) end private @@ -46,22 +45,21 @@ module ActionView # Renders the given template. A string representing the layout can be # supplied as well. - def render_template(template, layout_name = nil, locals = nil) - view, locals = @view, locals || {} + def render_template(view, template, layout_name = nil, locals = nil) + locals ||= {} - render_with_layout(layout_name, locals) do |layout| + render_with_layout(view, layout_name, locals) do |layout| instrument(:template, identifier: template.identifier, layout: layout.try(:virtual_path)) do template.render(view, locals) { |*name| view._layout_for(*name) } end end end - def render_with_layout(path, locals) + def render_with_layout(view, path, locals) layout = path && find_layout(path, locals.keys, [formats.first]) content = yield(layout) if layout - view = @view view.view_flow.set(:layout, content) layout.render(view, locals) { |*name| view._layout_for(*name) } else diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb index 204903c60c..727d3fbc1a 100644 --- a/actionview/test/actionpack/controller/render_test.rb +++ b/actionview/test/actionpack/controller/render_test.rb @@ -485,8 +485,8 @@ class TestController < ActionController::Base render partial: "customer", locals: { customer: Customer.new("david") } end - def partial_with_string_locals - render partial: "customer", locals: { "customer" => Customer.new("david") } + def partial_with_hashlike_locals + render partial: "customer", locals: ActionController::Parameters.new(customer: Customer.new("david")) end def partial_with_form_builder @@ -691,7 +691,7 @@ class RenderTest < ActionController::TestCase get :partial_with_locals, to: "test#partial_with_locals" get :partial_with_nested_object, to: "test#partial_with_nested_object" get :partial_with_nested_object_shorthand, to: "test#partial_with_nested_object_shorthand" - get :partial_with_string_locals, to: "test#partial_with_string_locals" + get :partial_with_hashlike_locals, to: "test#partial_with_hashlike_locals" get :partials_list, to: "test#partials_list" get :render_action_hello_world, to: "test#render_action_hello_world" get :render_action_hello_world_as_string, to: "test#render_action_hello_world_as_string" @@ -1292,8 +1292,8 @@ class RenderTest < ActionController::TestCase assert_equal "Hello: david", @response.body end - def test_partial_with_string_locals - get :partial_with_string_locals + def test_partial_with_hashlike_locals + get :partial_with_hashlike_locals assert_equal "Hello: david", @response.body end diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index 9cb8b543b0..51e224d5cd 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "bigdecimal/util" + module ActiveModel module Validations class NumericalityValidator < EachValidator # :nodoc: diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 2a0cd81be5..167ade30e3 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Make `t.timestamps` with precision by default. + + *Ryuta Kamizono* + + ## Rails 6.0.0.beta1 (January 18, 2019) ## * Remove deprecated `#set_state` from the transaction object. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 99934a0e31..c8d5f679a8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -1006,7 +1006,16 @@ module ActiveRecord # for (not necessarily the current class). def retrieve_connection(spec_name) #:nodoc: pool = retrieve_connection_pool(spec_name) - raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found." unless pool + + unless pool + # multiple database application + if ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler + raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role." + else + raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found." + end + end + pool.connection end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index db489143af..28d37881b6 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -263,6 +263,7 @@ module ActiveRecord deprecate :indexes= def initialize( + conn, name, temporary: false, if_not_exists: false, @@ -271,6 +272,7 @@ module ActiveRecord comment: nil, ** ) + @conn = conn @columns_hash = {} @indexes = [] @foreign_keys = [] @@ -410,6 +412,10 @@ module ActiveRecord def timestamps(**options) options[:null] = false if options[:null].nil? + if !options.key?(:precision) && @conn.supports_datetime_with_precision? + options[:precision] = 6 + end + column(:created_at, :datetime, options) column(:updated_at, :datetime, options) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 208c8c9c64..d88e75d692 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -130,11 +130,11 @@ module ActiveRecord # column_exists?(:suppliers, :name, :string, null: false) # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2) # - def column_exists?(table_name, column_name, type = nil, options = {}) + def column_exists?(table_name, column_name, type = nil, **options) column_name = column_name.to_s checks = [] checks << lambda { |c| c.name == column_name } - checks << lambda { |c| c.type == type } if type + checks << lambda { |c| c.type == type.to_sym rescue nil } if type column_options_keys.each do |attr| checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr) end @@ -1129,6 +1129,10 @@ module ActiveRecord def add_timestamps(table_name, options = {}) options[:null] = false if options[:null].nil? + if !options.key?(:precision) && supports_datetime_with_precision? + options[:precision] = 6 + end + add_column table_name, :created_at, :datetime, options add_column table_name, :updated_at, :datetime, options end @@ -1290,7 +1294,7 @@ module ActiveRecord end def create_table_definition(*args) - TableDefinition.new(*args) + TableDefinition.new(self, *args) end def create_alter_table(name) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 5479ddab71..415a0576c4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -709,6 +709,12 @@ module ActiveRecord end def add_timestamps_for_alter(table_name, options = {}) + options[:null] = false if options[:null].nil? + + if !options.key?(:precision) && supports_datetime_with_precision? + options[:precision] = 6 + end + [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)] end diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb index 47b5c4b9ec..e9484a08de 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb @@ -127,7 +127,7 @@ module ActiveRecord end def create_table_definition(*args) - MySQL::TableDefinition.new(*args) + MySQL::TableDefinition.new(self, *args) end def new_column_from_field(table_name, field) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 7cf371be68..946436f7f9 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -637,7 +637,7 @@ module ActiveRecord end def create_table_definition(*args) - PostgreSQL::TableDefinition.new(*args) + PostgreSQL::TableDefinition.new(self, *args) end def create_alter_table(name) @@ -716,6 +716,12 @@ module ActiveRecord end def add_timestamps_for_alter(table_name, options = {}) + options[:null] = false if options[:null].nil? + + if !options.key?(:precision) && supports_datetime_with_precision? + options[:precision] = 6 + end + [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)] end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb index 8650c07bab..2394982a7d 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb @@ -62,7 +62,7 @@ module ActiveRecord end def create_table_definition(*args) - SQLite3::TableDefinition.new(*args) + SQLite3::TableDefinition.new(self, *args) end def new_column_from_field(table_name, field) diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index 4a941055d1..558cdeccf2 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -158,10 +158,6 @@ module ActiveRecord end def with_handler(handler_key, &blk) # :nodoc: - unless ActiveRecord::Base.connection_handlers.keys.include?(handler_key) - raise ArgumentError, "The #{handler_key} role does not exist. Add it by establishing a connection with `connects_to` or use an existing role (#{ActiveRecord::Base.connection_handlers.keys.join(", ")})." - end - handler = lookup_connection_handler(handler_key) swap_connection_handler(handler, &blk) end diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index 608182e363..94906a2943 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -16,13 +16,55 @@ module ActiveRecord V6_0 = Current class V5_2 < V6_0 + module TableDefinition + def timestamps(**options) + options[:precision] ||= nil + super + end + end + module CommandRecorder def invert_transaction(args, &block) [:transaction, args, block] end end + def create_table(table_name, **options) + if block_given? + super { |t| yield compatible_table_definition(t) } + else + super + end + end + + def change_table(table_name, **options) + if block_given? + super { |t| yield compatible_table_definition(t) } + else + super + end + end + + def create_join_table(table_1, table_2, **options) + if block_given? + super { |t| yield compatible_table_definition(t) } + else + super + end + end + + def add_timestamps(table_name, **options) + options[:precision] ||= nil + super + end + private + def compatible_table_definition(t) + class << t + prepend TableDefinition + end + t + end def command_recorder recorder = super @@ -87,35 +129,12 @@ module ActiveRecord options[:id] = :integer end - if block_given? - super do |t| - yield compatible_table_definition(t) - end - else - super - end - end - - def change_table(table_name, options = {}) - if block_given? - super do |t| - yield compatible_table_definition(t) - end - else - super - end + super end def create_join_table(table_1, table_2, column_options: {}, **options) column_options.reverse_merge!(type: :integer) - - if block_given? - super do |t| - yield compatible_table_definition(t) - end - else - super - end + super end def add_column(table_name, column_name, type, options = {}) @@ -136,7 +155,7 @@ module ActiveRecord class << t prepend TableDefinition end - t + super end end @@ -154,33 +173,13 @@ module ActiveRecord end end - def create_table(table_name, options = {}) - if block_given? - super do |t| - yield compatible_table_definition(t) - end - else - super - end - end - - def change_table(table_name, options = {}) - if block_given? - super do |t| - yield compatible_table_definition(t) - end - else - super - end - end - - def add_reference(*, **options) + def add_reference(table_name, ref_name, **options) options[:index] ||= false super end alias :add_belongs_to :add_reference - def add_timestamps(_, **options) + def add_timestamps(table_name, **options) options[:null] = true if options[:null].nil? super end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index c2b60610ce..2213fbefb4 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -436,7 +436,7 @@ module ActiveRecord end alias update_attributes update - deprecate :update_attributes + deprecate update_attributes: "please, use update instead" # Updates its receiver just like #update but calls #save! instead # of +save+, so an exception is raised if the record is invalid and saving will fail. @@ -450,7 +450,7 @@ module ActiveRecord end alias update_attributes! update! - deprecate :update_attributes! + deprecate update_attributes!: "please, use update! instead" # Equivalent to <code>update_columns(name => value)</code>. def update_column(name, value) diff --git a/activerecord/lib/active_record/relation/query_attribute.rb b/activerecord/lib/active_record/relation/query_attribute.rb index 5e0b4ac160..1dd6462d8d 100644 --- a/activerecord/lib/active_record/relation/query_attribute.rb +++ b/activerecord/lib/active_record/relation/query_attribute.rb @@ -32,7 +32,7 @@ module ActiveRecord if defined?(@_unboundable) @_unboundable else - value_for_database + value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute) @_unboundable = nil end rescue ::RangeError diff --git a/activerecord/lib/arel/nodes/and.rb b/activerecord/lib/arel/nodes/and.rb index c530a77bfb..bf516db35f 100644 --- a/activerecord/lib/arel/nodes/and.rb +++ b/activerecord/lib/arel/nodes/and.rb @@ -2,7 +2,7 @@ module Arel # :nodoc: all module Nodes - class And < Arel::Nodes::Node + class And < Arel::Nodes::NodeExpression attr_reader :children def initialize(children) diff --git a/activerecord/lib/arel/nodes/case.rb b/activerecord/lib/arel/nodes/case.rb index b8f83128c8..1c4b727bf6 100644 --- a/activerecord/lib/arel/nodes/case.rb +++ b/activerecord/lib/arel/nodes/case.rb @@ -2,9 +2,7 @@ module Arel # :nodoc: all module Nodes - class Case < Arel::Nodes::Node - include Arel::AliasPredication - + class Case < Arel::Nodes::NodeExpression attr_accessor :case, :conditions, :default def initialize(expression = nil, default = nil) diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index 2d71ee2f15..88c2ac5d0a 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -139,8 +139,8 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase with_real_execute do ActiveRecord::Base.connection.create_table :delete_me ActiveRecord::Base.connection.add_timestamps :delete_me, null: true - assert column_present?("delete_me", "updated_at", "datetime") - assert column_present?("delete_me", "created_at", "datetime") + assert column_exists?("delete_me", "updated_at", "datetime") + assert column_exists?("delete_me", "created_at", "datetime") ensure ActiveRecord::Base.connection.drop_table :delete_me rescue nil end @@ -152,8 +152,8 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase t.timestamps null: true end ActiveRecord::Base.connection.remove_timestamps :delete_me, null: true - assert_not column_present?("delete_me", "updated_at", "datetime") - assert_not column_present?("delete_me", "created_at", "datetime") + assert_not column_exists?("delete_me", "updated_at", "datetime") + assert_not column_exists?("delete_me", "created_at", "datetime") ensure ActiveRecord::Base.connection.drop_table :delete_me rescue nil end @@ -194,9 +194,4 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase def method_missing(method_symbol, *arguments) ActiveRecord::Base.connection.send(method_symbol, *arguments) end - - def column_present?(table_name, column_name, type) - results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'") - results.first && results.first["Type"] == type - end end diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index f05dcac7dd..9d88b14dab 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -116,8 +116,8 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase end end - assert_not @connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null - assert_not @connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + assert @connection.column_exists?(:has_timestamps, :created_at, null: false) + assert @connection.column_exists?(:has_timestamps, :updated_at, null: false) end def test_timestamps_without_null_set_null_to_false_on_change_table @@ -129,8 +129,23 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase end end - assert_not @connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null - assert_not @connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + assert @connection.column_exists?(:has_timestamps, :created_at, null: false) + assert @connection.column_exists?(:has_timestamps, :updated_at, null: false) + end + + if ActiveRecord::Base.connection.supports_bulk_alter? + def test_timestamps_without_null_set_null_to_false_on_change_table_with_bulk + ActiveRecord::Schema.define do + create_table :has_timestamps + + change_table :has_timestamps, bulk: true do |t| + t.timestamps default: Time.now + end + end + + assert @connection.column_exists?(:has_timestamps, :created_at, null: false) + assert @connection.column_exists?(:has_timestamps, :updated_at, null: false) + end end def test_timestamps_without_null_set_null_to_false_on_add_timestamps @@ -139,7 +154,58 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase add_timestamps :has_timestamps, default: Time.now end - assert_not @connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null - assert_not @connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + assert @connection.column_exists?(:has_timestamps, :created_at, null: false) + assert @connection.column_exists?(:has_timestamps, :updated_at, null: false) + end + + if subsecond_precision_supported? + def test_timestamps_sets_presicion_on_create_table + ActiveRecord::Schema.define do + create_table :has_timestamps do |t| + t.timestamps + end + end + + assert @connection.column_exists?(:has_timestamps, :created_at, precision: 6, null: false) + assert @connection.column_exists?(:has_timestamps, :updated_at, precision: 6, null: false) + end + + def test_timestamps_sets_presicion_on_change_table + ActiveRecord::Schema.define do + create_table :has_timestamps + + change_table :has_timestamps do |t| + t.timestamps default: Time.now + end + end + + assert @connection.column_exists?(:has_timestamps, :created_at, precision: 6, null: false) + assert @connection.column_exists?(:has_timestamps, :updated_at, precision: 6, null: false) + end + + if ActiveRecord::Base.connection.supports_bulk_alter? + def test_timestamps_sets_presicion_on_change_table_with_bulk + ActiveRecord::Schema.define do + create_table :has_timestamps + + change_table :has_timestamps, bulk: true do |t| + t.timestamps default: Time.now + end + end + + assert @connection.column_exists?(:has_timestamps, :created_at, precision: 6, null: false) + assert @connection.column_exists?(:has_timestamps, :updated_at, precision: 6, null: false) + end + end + + def test_timestamps_sets_presicion_on_add_timestamps + ActiveRecord::Schema.define do + create_table :has_timestamps + add_timestamps :has_timestamps, default: Time.now + end + + assert @connection.column_exists?(:has_timestamps, :created_at, precision: 6, null: false) + assert @connection.column_exists?(:has_timestamps, :updated_at, precision: 6, null: false) + end end end diff --git a/activerecord/test/cases/arel/nodes/and_test.rb b/activerecord/test/cases/arel/nodes/and_test.rb index eff54abd91..d123ca9fd0 100644 --- a/activerecord/test/cases/arel/nodes/and_test.rb +++ b/activerecord/test/cases/arel/nodes/and_test.rb @@ -16,6 +16,15 @@ module Arel assert_equal 2, array.uniq.size end end + + describe "functions as node expression" do + it "allows aliasing" do + aliased = And.new(["foo", "bar"]).as("baz") + + assert_kind_of As, aliased + assert_kind_of SqlLiteral, aliased.right + end + end end end end diff --git a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb index 865aacc1b5..8988755d24 100644 --- a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb @@ -336,13 +336,13 @@ module ActiveRecord end def test_calling_connected_to_on_a_non_existent_handler_raises - error = assert_raises ArgumentError do + error = assert_raises ActiveRecord::ConnectionNotEstablished do ActiveRecord::Base.connected_to(role: :reading) do - yield + Person.first end end - assert_equal "The reading role does not exist. Add it by establishing a connection with `connects_to` or use an existing role (writing).", error.message + assert_equal "No connection pool with 'primary' found for the 'reading' role.", error.message end end end diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index 017ee7951e..5753bd7117 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -86,8 +86,8 @@ module ActiveRecord ActiveRecord::Migrator.new(:up, [migration]).migrate - assert connection.columns(:more_testings).find { |c| c.name == "created_at" }.null - assert connection.columns(:more_testings).find { |c| c.name == "updated_at" }.null + assert connection.column_exists?(:more_testings, :created_at, null: true) + assert connection.column_exists?(:more_testings, :updated_at, null: true) ensure connection.drop_table :more_testings rescue nil end @@ -103,8 +103,25 @@ module ActiveRecord ActiveRecord::Migrator.new(:up, [migration]).migrate - assert connection.columns(:testings).find { |c| c.name == "created_at" }.null - assert connection.columns(:testings).find { |c| c.name == "updated_at" }.null + assert connection.column_exists?(:testings, :created_at, null: true) + assert connection.column_exists?(:testings, :updated_at, null: true) + end + + if ActiveRecord::Base.connection.supports_bulk_alter? + def test_timestamps_have_null_constraints_if_not_present_in_migration_of_change_table_with_bulk + migration = Class.new(ActiveRecord::Migration[4.2]) { + def migrate(x) + change_table :testings, bulk: true do |t| + t.timestamps + end + end + }.new + + ActiveRecord::Migrator.new(:up, [migration]).migrate + + assert connection.column_exists?(:testings, :created_at, null: true) + assert connection.column_exists?(:testings, :updated_at, null: true) + end end def test_timestamps_have_null_constraints_if_not_present_in_migration_for_adding_timestamps_to_existing_table @@ -116,8 +133,70 @@ module ActiveRecord ActiveRecord::Migrator.new(:up, [migration]).migrate - assert connection.columns(:testings).find { |c| c.name == "created_at" }.null - assert connection.columns(:testings).find { |c| c.name == "updated_at" }.null + assert connection.column_exists?(:testings, :created_at, null: true) + assert connection.column_exists?(:testings, :updated_at, null: true) + end + + def test_timestamps_doesnt_set_precision_on_create_table + migration = Class.new(ActiveRecord::Migration[5.2]) { + def migrate(x) + create_table :more_testings do |t| + t.timestamps + end + end + }.new + + ActiveRecord::Migrator.new(:up, [migration]).migrate + + assert connection.column_exists?(:more_testings, :created_at, null: false, **precision_implicit_default) + assert connection.column_exists?(:more_testings, :updated_at, null: false, **precision_implicit_default) + ensure + connection.drop_table :more_testings rescue nil + end + + def test_timestamps_doesnt_set_precision_on_change_table + migration = Class.new(ActiveRecord::Migration[5.2]) { + def migrate(x) + change_table :testings do |t| + t.timestamps default: Time.now + end + end + }.new + + ActiveRecord::Migrator.new(:up, [migration]).migrate + + assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default) + assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default) + end + + if ActiveRecord::Base.connection.supports_bulk_alter? + def test_timestamps_doesnt_set_precision_on_change_table_with_bulk + migration = Class.new(ActiveRecord::Migration[5.2]) { + def migrate(x) + change_table :testings, bulk: true do |t| + t.timestamps + end + end + }.new + + ActiveRecord::Migrator.new(:up, [migration]).migrate + + assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default) + assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default) + end + end + + def test_timestamps_doesnt_set_precision_on_add_timestamps + migration = Class.new(ActiveRecord::Migration[5.2]) { + def migrate(x) + add_timestamps :testings, default: Time.now + end + }.new + + ActiveRecord::Migrator.new(:up, [migration]).migrate + + assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default) + assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default) end def test_legacy_migrations_raises_exception_when_inherited @@ -159,6 +238,15 @@ module ActiveRecord ActiveRecord::Base.clear_cache! end end + + private + def precision_implicit_default + if current_adapter?(:Mysql2Adapter) + { presicion: 0 } + else + { presicion: nil } + end + end end end end diff --git a/activerecord/test/cases/statement_cache_test.rb b/activerecord/test/cases/statement_cache_test.rb index e3c12f68fd..6a6d73dc38 100644 --- a/activerecord/test/cases/statement_cache_test.rb +++ b/activerecord/test/cases/statement_cache_test.rb @@ -4,6 +4,7 @@ require "cases/helper" require "models/book" require "models/liquid" require "models/molecule" +require "models/numeric_data" require "models/electron" module ActiveRecord @@ -74,6 +75,11 @@ module ActiveRecord assert_equal "salty", liquids[0].name end + def test_statement_cache_with_strictly_cast_attribute + row = NumericData.create(temperature: 1.5) + assert_equal row, NumericData.find_by(temperature: 1.5) + end + def test_statement_cache_values_differ cache = ActiveRecord::StatementCache.create(Book.connection) do |params| Book.where(name: "my book") diff --git a/activerecord/test/cases/unconnected_test.rb b/activerecord/test/cases/unconnected_test.rb index 9eefc32745..f0a0e7f805 100644 --- a/activerecord/test/cases/unconnected_test.rb +++ b/activerecord/test/cases/unconnected_test.rb @@ -29,6 +29,14 @@ class TestUnconnectedAdapter < ActiveRecord::TestCase end end + def test_error_message_when_connection_not_established + error = assert_raise(ActiveRecord::ConnectionNotEstablished) do + TestRecord.find(1) + end + + assert_equal "No connection pool with 'primary' found.", error.message + end + def test_underlying_adapter_no_longer_active assert_not @underlying.active?, "Removed adapter should no longer be active" end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 7034c773d2..86d5a67a13 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -682,11 +682,7 @@ ActiveRecord::Schema.define do create_table :pets, primary_key: :pet_id, force: true do |t| t.string :name t.integer :owner_id, :integer - if subsecond_precision_supported? - t.timestamps null: false, precision: 6 - else - t.timestamps null: false - end + t.timestamps end create_table :pets_treasures, force: true do |t| @@ -904,11 +900,7 @@ ActiveRecord::Schema.define do t.string :parent_title t.string :type t.string :group - if subsecond_precision_supported? - t.timestamps null: true, precision: 6 - else - t.timestamps null: true - end + t.timestamps null: true end create_table :toys, primary_key: :toy_id, force: true do |t| diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md index 659bacebde..0c7e0426ae 100644 --- a/activestorage/CHANGELOG.md +++ b/activestorage/CHANGELOG.md @@ -1,5 +1,11 @@ ## Rails 6.0.0.beta1 (January 18, 2019) ## +* [Rename npm package](https://github.com/rails/rails/pull/34905) from + [`activestorage`](https://www.npmjs.com/package/activestorage) to + [`@rails/activestorage`](https://www.npmjs.com/package/@rails/activestorage). + + *Javan Makhmali* + * Replace `config.active_storage.queue` with two options that indicate which queues analysis and purge jobs should use, respectively: diff --git a/activestorage/README.md b/activestorage/README.md index f658b8d542..2886169ca7 100644 --- a/activestorage/README.md +++ b/activestorage/README.md @@ -101,7 +101,7 @@ Variation of image attachment: ```erb <%# Hitting the variant URL will lazy transform the original blob and then redirect to its new service location %> -<%= image_tag user.avatar.variant(resize_to_fit: [100, 100]) %> +<%= image_tag user.avatar.variant(resize_to_limit: [100, 100]) %> ``` ## Direct uploads diff --git a/activestorage/app/models/active_storage/blob/representable.rb b/activestorage/app/models/active_storage/blob/representable.rb index 03d5511481..32e8fcefdf 100644 --- a/activestorage/app/models/active_storage/blob/representable.rb +++ b/activestorage/app/models/active_storage/blob/representable.rb @@ -10,7 +10,7 @@ module ActiveStorage::Blob::Representable # Returns an ActiveStorage::Variant instance with the set of +transformations+ provided. This is only relevant for image # files, and it allows any image to be transformed for size, colors, and the like. Example: # - # avatar.variant(resize_to_fit: [100, 100]).processed.service_url + # avatar.variant(resize_to_limit: [100, 100]).processed.service_url # # This will create and process a variant of the avatar blob that's constrained to a height and width of 100px. # Then it'll upload said variant to the service according to a derivative key of the blob and the transformations. @@ -18,7 +18,7 @@ module ActiveStorage::Blob::Representable # Frequently, though, you don't actually want to transform the variant right away. But rather simply refer to a # specific variant that can be created by a controller on-demand. Like so: # - # <%= image_tag Current.user.avatar.variant(resize_to_fit: [100, 100]) %> + # <%= image_tag Current.user.avatar.variant(resize_to_limit: [100, 100]) %> # # This will create a URL for that specific blob with that specific variant, which the ActiveStorage::RepresentationsController # can then produce on-demand. @@ -43,13 +43,13 @@ module ActiveStorage::Blob::Representable # from a non-image blob. Active Storage comes with built-in previewers for videos and PDF documents. The video previewer # extracts the first frame from a video and the PDF previewer extracts the first page from a PDF document. # - # blob.preview(resize_to_fit: [100, 100]).processed.service_url + # blob.preview(resize_to_limit: [100, 100]).processed.service_url # # Avoid processing previews synchronously in views. Instead, link to a controller action that processes them on demand. # Active Storage provides one, but you may want to create your own (for example, if you need authentication). Here’s # how to use the built-in version: # - # <%= image_tag video.preview(resize_to_fit: [100, 100]) %> + # <%= image_tag video.preview(resize_to_limit: [100, 100]) %> # # This method raises ActiveStorage::UnpreviewableError if no previewer accepts the receiving blob. To determine # whether a blob is accepted by any previewer, call ActiveStorage::Blob#previewable?. @@ -69,7 +69,7 @@ module ActiveStorage::Blob::Representable # Returns an ActiveStorage::Preview for a previewable blob or an ActiveStorage::Variant for a variable image blob. # - # blob.representation(resize_to_fit: [100, 100]).processed.service_url + # blob.representation(resize_to_limit: [100, 100]).processed.service_url # # Raises ActiveStorage::UnrepresentableError if the receiving blob is neither variable nor previewable. Call # ActiveStorage::Blob#representable? to determine whether a blob is representable. diff --git a/activestorage/app/models/active_storage/preview.rb b/activestorage/app/models/active_storage/preview.rb index dd50494799..bb9d960443 100644 --- a/activestorage/app/models/active_storage/preview.rb +++ b/activestorage/app/models/active_storage/preview.rb @@ -38,7 +38,7 @@ class ActiveStorage::Preview # Processes the preview if it has not been processed yet. Returns the receiving Preview instance for convenience: # - # blob.preview(resize_to_fit: [100, 100]).processed.service_url + # blob.preview(resize_to_limit: [100, 100]).processed.service_url # # Processing a preview generates an image from its blob and attaches the preview image to the blob. Because the preview # image is stored with the blob, it is only generated once. diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb index ea57fa5f78..bc0058967a 100644 --- a/activestorage/app/models/active_storage/variant.rb +++ b/activestorage/app/models/active_storage/variant.rb @@ -27,7 +27,7 @@ require "ostruct" # To refer to such a delayed on-demand variant, simply link to the variant through the resolved route provided # by Active Storage like so: # -# <%= image_tag Current.user.avatar.variant(resize_to_fit: [100, 100]) %> +# <%= image_tag Current.user.avatar.variant(resize_to_limit: [100, 100]) %> # # This will create a URL for that specific blob with that specific variant, which the ActiveStorage::RepresentationsController # can then produce on-demand. @@ -36,15 +36,15 @@ require "ostruct" # has already been processed and uploaded to the service, and, if so, just return that. Otherwise it will perform # the transformations, upload the variant to the service, and return itself again. Example: # -# avatar.variant(resize_to_fit: [100, 100]).processed.service_url +# avatar.variant(resize_to_limit: [100, 100]).processed.service_url # # This will create and process a variant of the avatar blob that's constrained to a height and width of 100. # Then it'll upload said variant to the service according to a derivative key of the blob and the transformations. # # You can combine any number of ImageMagick/libvips operations into a variant, as well as any macros provided by the -# ImageProcessing gem (such as +resize_to_fit+): +# ImageProcessing gem (such as +resize_to_limit+): # -# avatar.variant(resize_to_fit: [800, 800], monochrome: true, rotate: "-90") +# avatar.variant(resize_to_limit: [800, 800], monochrome: true, rotate: "-90") # # Visit the following links for a list of available ImageProcessing commands and ImageMagick/libvips operations: # diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb index 3adc2407e5..67568772da 100644 --- a/activestorage/app/models/active_storage/variation.rb +++ b/activestorage/app/models/active_storage/variation.rb @@ -6,7 +6,7 @@ # In case you do need to use this directly, it's instantiated using a hash of transformations where # the key is the command and the value is the arguments. Example: # -# ActiveStorage::Variation.new(resize_to_fit: [100, 100], monochrome: true, trim: true, rotate: "-90") +# ActiveStorage::Variation.new(resize_to_limit: [100, 100], monochrome: true, trim: true, rotate: "-90") # # The options map directly to {ImageProcessing}[https://github.com/janko-m/image_processing] commands. class ActiveStorage::Variation diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md index 474a93c83e..e3bb41ae32 100644 --- a/guides/source/active_storage_overview.md +++ b/guides/source/active_storage_overview.md @@ -434,7 +434,7 @@ original blob into the specified format and redirect to its new service location. ```erb -<%= image_tag user.avatar.variant(resize_to_fit: [100, 100]) %> +<%= image_tag user.avatar.variant(resize_to_limit: [100, 100]) %> ``` To switch to the Vips processor, you would add the following to diff --git a/guides/source/configuring.md b/guides/source/configuring.md index a727dcd010..2911b1f7c3 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -1239,6 +1239,8 @@ Using Initializer Files After loading the framework and any gems in your application, Rails turns to loading initializers. An initializer is any Ruby file stored under `config/initializers` in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and gems are loaded, such as options to configure settings for these parts. +NOTE: There is no guarantee that your initializers will run after all the gem initializers, so any initialization code that depends on a given gem having been initialized should go into a `config.after_initialize` block. + NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the initializers folder on down. TIP: While Rails supports numbering of initializer file names for load ordering purposes, a better technique is to place any code that need to load in a specific order within the same file. This reduces file name churn, makes dependencies more explicit, and can help surface new concepts within your application. diff --git a/guides/source/testing.md b/guides/source/testing.md index f9661c52a0..9667521f3b 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -1806,16 +1806,16 @@ require "test_helper" class WebNotificationsChannelTest < ActionCable::Channel::TestCase test "subscribes and stream for user" do - stub_connection current_user: users[:john] + stub_connection current_user: users(:john) subscribe - assert_has_stream_for users[:john] + assert_has_stream_for users(:john) end end ``` -See the API documentation for [`AcionCable::Channel::TestCase`](http://api.rubyonrails.org/classes/ActionCable/Channel/TestCase.html) for more information. +See the API documentation for [`ActionCable::Channel::TestCase`](http://api.rubyonrails.org/classes/ActionCable/Channel/TestCase.html) for more information. ### Custom Assertions And Testing Broadcasts Inside Other Components @@ -1837,6 +1837,33 @@ class ProductTest < ActionCable::TestCase end ``` +If you want to test the broadcasting made with `Channel.broadcast_to`, you shoud use +`Channel.broadcasting_for` to generate an underlying stream name: + +```ruby +# app/jobs/chat_relay_job.rb +class ChatRelayJob < ApplicationJob + def perform_later(room, message) + ChatChannel.broadcast_to room, text: message + end +end + +# test/jobs/chat_relay_job_test.rb +require 'test_helper' + +class ChatRelayJobTest < ActiveJob::TestCase + include ActionCable::TestHelper + + test "broadcast message to room" do + room = rooms(:all) + + assert_broadcast_on(ChatChannel.broadcasting_for(room), text: "Hi!") do + ChatRelayJob.perform_now(room, "Hi!") + end + end +end +``` + Additional Testing Resources ---------------------------- diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 5b249de47e..e55217c5c4 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,8 @@ +* Fix deeply nested namespace command printing. + + *Gannon McGibbon* + + ## Rails 6.0.0.beta1 (January 18, 2019) ## * Remove deprecated `after_bundle` helper inside plugins templates. diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index d5a66b6ec1..b7838f7e32 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -30,7 +30,7 @@ module Rails @filter_parameters = [] @filter_redirect = [] @helpers_paths = [] - @hosts = Array(([IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0"), "localhost"] if Rails.env.development?)) + @hosts = Array(([IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0"), ".localhost"] if Rails.env.development?)) @public_file_server = ActiveSupport::OrderedOptions.new @public_file_server.enabled = true @public_file_server.index_name = "index" diff --git a/railties/lib/rails/command/actions.rb b/railties/lib/rails/command/actions.rb index cbb743346b..50651ad61a 100644 --- a/railties/lib/rails/command/actions.rb +++ b/railties/lib/rails/command/actions.rb @@ -11,10 +11,20 @@ module Rails end def require_application_and_environment! + require_application! + require_environment! + end + + def require_application! require ENGINE_PATH if defined?(ENGINE_PATH) if defined?(APP_PATH) require APP_PATH + end + end + + def require_environment! + if defined?(APP_PATH) Rails.application.require_environment! end end diff --git a/railties/lib/rails/command/base.rb b/railties/lib/rails/command/base.rb index 766872de8a..a22b198c66 100644 --- a/railties/lib/rails/command/base.rb +++ b/railties/lib/rails/command/base.rb @@ -115,7 +115,7 @@ module Rails # For a Rails::Command::TestCommand placed in <tt>rails/command/test_command.rb</tt> # would return <tt>rails/test</tt>. def default_command_root - path = File.expand_path(File.join("../commands", command_root_namespace), __dir__) + path = File.expand_path(relative_command_path, __dir__) path if File.exist?(path) end @@ -135,12 +135,20 @@ module Rails end def command_root_namespace - (namespace.split(":") - %w( rails )).first + (namespace.split(":") - %w(rails)).join(":") + end + + def relative_command_path + File.join("../commands", *command_root_namespace.split(":")) end def namespaced_commands commands.keys.map do |key| - key == command_root_namespace ? key : "#{command_root_namespace}:#{key}" + if command_root_namespace.match?(/(\A|\:)#{key}\z/) + command_root_namespace + else + "#{command_root_namespace}:#{key}" + end end end end diff --git a/railties/lib/rails/commands/credentials/USAGE b/railties/lib/rails/commands/credentials/USAGE index d235592f46..f7268a64d1 100644 --- a/railties/lib/rails/commands/credentials/USAGE +++ b/railties/lib/rails/commands/credentials/USAGE @@ -54,5 +54,5 @@ doesn't exist. The encryption key can also be put in `ENV["RAILS_MASTER_KEY"]`, which takes precedence over the file encryption key. -In addition to that, the default credentials lookup paths can be overriden through +In addition to that, the default credentials lookup paths can be overridden through `config.credentials.content_path` and `config.credentials.key_path`. diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb index 852cd401d7..54ccd97506 100644 --- a/railties/lib/rails/commands/credentials/credentials_command.rb +++ b/railties/lib/rails/commands/credentials/credentials_command.rb @@ -20,7 +20,7 @@ module Rails end def edit - require_application_and_environment! + require_application! ensure_editor_available(command: "bin/rails credentials:edit") || (return) @@ -37,7 +37,7 @@ module Rails end def show - require_application_and_environment! + require_application! say credentials.read.presence || missing_credentials_message end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index f768c30db0..d6c329b581 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -473,9 +473,10 @@ module Rails # files inside eager_load paths. def eager_load! config.eager_load_paths.each do |load_path| - matcher = /\A#{Regexp.escape(load_path.to_s)}\/(.*)\.rb\Z/ + # Starts after load_path plus a slash, ends before ".rb". + relname_range = (load_path.to_s.length + 1)...-3 Dir.glob("#{load_path}/**/*.rb").sort.each do |file| - require_dependency file.sub(matcher, '\1') + require_dependency file[relname_range] end end end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 3e979ea20d..9da3956dda 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -2289,6 +2289,11 @@ module ApplicationTests MESSAGE end + test "the host whitelist includes .localhost in development" do + app "development" + assert_includes Rails.application.config.hosts, ".localhost" + end + private def force_lazy_load_hooks yield # Tasty clarifying sugar, homie! We only need to reference a constant to load it. diff --git a/railties/test/command/base_test.rb b/railties/test/command/base_test.rb index a49ae8aae7..9132c8b4af 100644 --- a/railties/test/command/base_test.rb +++ b/railties/test/command/base_test.rb @@ -4,10 +4,12 @@ require "abstract_unit" require "rails/command" require "rails/commands/generate/generate_command" require "rails/commands/secrets/secrets_command" +require "rails/commands/db/system/change/change_command" class Rails::Command::BaseTest < ActiveSupport::TestCase test "printing commands" do assert_equal %w(generate), Rails::Command::GenerateCommand.printing_commands assert_equal %w(secrets:setup secrets:edit secrets:show), Rails::Command::SecretsCommand.printing_commands + assert_equal %w(db:system:change), Rails::Command::Db::System::ChangeCommand.printing_commands end end diff --git a/railties/test/commands/credentials_test.rb b/railties/test/commands/credentials_test.rb index 7842b0db61..26ffe3070c 100644 --- a/railties/test/commands/credentials_test.rb +++ b/railties/test/commands/credentials_test.rb @@ -63,6 +63,14 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase end end + test "edit command does not raise when an initializer tries to acces non-existent credentials" do + app_file "config/initializers/raise_when_loaded.rb", <<-RUBY + Rails.application.credentials.missing_key! + RUBY + + assert_match(/access_key_id: 123/, run_edit_command(environment: "qa")) + end + test "show credentials" do assert_match(/access_key_id: 123/, run_show_command) end diff --git a/tasks/release.rb b/tasks/release.rb index 6784330fd6..2fdcea9d12 100644 --- a/tasks/release.rb +++ b/tasks/release.rb @@ -218,7 +218,7 @@ namespace :all do <p> <% if @user.avatar.attached? -%> - <%= image_tag @user.avatar.representation(resize_to_fit: [500, 500]) %> + <%= image_tag @user.avatar.representation(resize_to_limit: [500, 500]) %> <% end -%> </p> CODE |