mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge branch 'master' into unlock-minitest
This commit is contained in:
commit
e1758b5e8c
421 changed files with 3517 additions and 2199 deletions
42
.rubocop.yml
42
.rubocop.yml
|
@ -18,27 +18,27 @@ Style/BracesAroundHashParameters:
|
|||
Enabled: true
|
||||
|
||||
# Align `when` with `case`.
|
||||
Style/CaseIndentation:
|
||||
Layout/CaseIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Align comments with method definitions.
|
||||
Style/CommentIndentation:
|
||||
Layout/CommentIndentation:
|
||||
Enabled: true
|
||||
|
||||
# No extra empty lines.
|
||||
Style/EmptyLines:
|
||||
Layout/EmptyLines:
|
||||
Enabled: true
|
||||
|
||||
# In a regular class definition, no empty lines around the body.
|
||||
Style/EmptyLinesAroundClassBody:
|
||||
Layout/EmptyLinesAroundClassBody:
|
||||
Enabled: true
|
||||
|
||||
# In a regular method definition, no empty lines around the body.
|
||||
Style/EmptyLinesAroundMethodBody:
|
||||
Layout/EmptyLinesAroundMethodBody:
|
||||
Enabled: true
|
||||
|
||||
# In a regular module definition, no empty lines around the body.
|
||||
Style/EmptyLinesAroundModuleBody:
|
||||
Layout/EmptyLinesAroundModuleBody:
|
||||
Enabled: true
|
||||
|
||||
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
|
||||
|
@ -47,30 +47,30 @@ Style/HashSyntax:
|
|||
|
||||
# Method definitions after `private` or `protected` isolated calls need one
|
||||
# extra level of indentation.
|
||||
Style/IndentationConsistency:
|
||||
Layout/IndentationConsistency:
|
||||
Enabled: true
|
||||
EnforcedStyle: rails
|
||||
|
||||
# Two spaces, no tabs (for indentation).
|
||||
Style/IndentationWidth:
|
||||
Layout/IndentationWidth:
|
||||
Enabled: true
|
||||
|
||||
Style/SpaceAfterColon:
|
||||
Layout/SpaceAfterColon:
|
||||
Enabled: true
|
||||
|
||||
Style/SpaceAfterComma:
|
||||
Layout/SpaceAfterComma:
|
||||
Enabled: true
|
||||
|
||||
Style/SpaceAroundEqualsInParameterDefault:
|
||||
Layout/SpaceAroundEqualsInParameterDefault:
|
||||
Enabled: true
|
||||
|
||||
Style/SpaceAroundKeyword:
|
||||
Layout/SpaceAroundKeyword:
|
||||
Enabled: true
|
||||
|
||||
Style/SpaceAroundOperators:
|
||||
Layout/SpaceAroundOperators:
|
||||
Enabled: true
|
||||
|
||||
Style/SpaceBeforeFirstArg:
|
||||
Layout/SpaceBeforeFirstArg:
|
||||
Enabled: true
|
||||
|
||||
# Defining a method with parameters needs parentheses.
|
||||
|
@ -78,18 +78,18 @@ Style/MethodDefParentheses:
|
|||
Enabled: true
|
||||
|
||||
# Use `foo {}` not `foo{}`.
|
||||
Style/SpaceBeforeBlockBraces:
|
||||
Layout/SpaceBeforeBlockBraces:
|
||||
Enabled: true
|
||||
|
||||
# Use `foo { bar }` not `foo {bar}`.
|
||||
Style/SpaceInsideBlockBraces:
|
||||
Layout/SpaceInsideBlockBraces:
|
||||
Enabled: true
|
||||
|
||||
# Use `{ a: 1 }` not `{a:1}`.
|
||||
Style/SpaceInsideHashLiteralBraces:
|
||||
Layout/SpaceInsideHashLiteralBraces:
|
||||
Enabled: true
|
||||
|
||||
Style/SpaceInsideParens:
|
||||
Layout/SpaceInsideParens:
|
||||
Enabled: true
|
||||
|
||||
# Check quotes usage according to lint rule below.
|
||||
|
@ -98,15 +98,15 @@ Style/StringLiterals:
|
|||
EnforcedStyle: double_quotes
|
||||
|
||||
# Detect hard tabs, no hard tabs.
|
||||
Style/Tab:
|
||||
Layout/Tab:
|
||||
Enabled: true
|
||||
|
||||
# Blank lines should not have any spaces.
|
||||
Style/TrailingBlankLines:
|
||||
Layout/TrailingBlankLines:
|
||||
Enabled: true
|
||||
|
||||
# No trailing whitespace.
|
||||
Style/TrailingWhitespace:
|
||||
Layout/TrailingWhitespace:
|
||||
Enabled: true
|
||||
|
||||
# Use quotes for string literals when they are enough.
|
||||
|
|
|
@ -98,17 +98,17 @@ matrix:
|
|||
- "GEM=ar:postgresql POSTGRES=9.2"
|
||||
addons:
|
||||
postgresql: "9.2"
|
||||
- rvm: jruby-9.1.8.0
|
||||
- rvm: jruby-9.1.10.0
|
||||
jdk: oraclejdk8
|
||||
env:
|
||||
- "GEM=ap"
|
||||
- rvm: jruby-9.1.8.0
|
||||
- rvm: jruby-9.1.10.0
|
||||
jdk: oraclejdk8
|
||||
env:
|
||||
- "GEM=am,amo,aj"
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
- rvm: jruby-9.1.8.0
|
||||
- rvm: jruby-9.1.10.0
|
||||
- env: "GEM=ac:integration"
|
||||
fast_finish: true
|
||||
|
||||
|
|
12
Gemfile
12
Gemfile
|
@ -16,7 +16,7 @@ gem "rake", ">= 11.1"
|
|||
# be loaded after loading the test library.
|
||||
gem "mocha", "~> 0.14", require: false
|
||||
|
||||
gem "capybara", "~> 2.13.0"
|
||||
gem "capybara", "~> 2.13"
|
||||
|
||||
gem "rack-cache", "~> 1.2"
|
||||
gem "jquery-rails"
|
||||
|
@ -33,8 +33,8 @@ gem "bcrypt", "~> 3.1.11", require: false
|
|||
# sprockets.
|
||||
gem "uglifier", ">= 1.3.0", require: false
|
||||
|
||||
# FIXME: Remove this fork after https://github.com/nex3/rb-inotify/pull/49 is fixed.
|
||||
gem "rb-inotify", github: "matthewd/rb-inotify", branch: "close-handling", require: false
|
||||
# FIXME: Pending rb-inotify 0.9.9 release
|
||||
gem "rb-inotify", github: "guard/rb-inotify", branch: "master", require: false
|
||||
|
||||
# Explicitly avoid 1.x that doesn't support Ruby 2.4+
|
||||
gem "json", ">= 2.0.0"
|
||||
|
@ -90,7 +90,7 @@ group :cable do
|
|||
end
|
||||
|
||||
# Add your own local bundler stuff.
|
||||
local_gemfile = File.dirname(__FILE__) + "/.Gemfile"
|
||||
local_gemfile = File.expand_path(".Gemfile", __dir__)
|
||||
instance_eval File.read local_gemfile if File.exist? local_gemfile
|
||||
|
||||
group :test do
|
||||
|
@ -153,3 +153,7 @@ end
|
|||
gem "ibm_db" if ENV["IBM_DB"]
|
||||
gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
||||
gem "wdm", ">= 0.1.0", platforms: [:mingw, :mswin, :x64_mingw, :mswin64]
|
||||
|
||||
platforms :ruby_25 do
|
||||
gem "mathn"
|
||||
end
|
||||
|
|
61
Gemfile.lock
61
Gemfile.lock
|
@ -7,12 +7,12 @@ GIT
|
|||
pg (>= 0.17, < 0.20)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/matthewd/rb-inotify.git
|
||||
revision: 90553518d1fb79aedc98a3036c59bd2b6731ac40
|
||||
branch: close-handling
|
||||
remote: https://github.com/guard/rb-inotify.git
|
||||
revision: 7e3c714a09ae2b38d2620835e794150d8857cd49
|
||||
branch: master
|
||||
specs:
|
||||
rb-inotify (0.9.7)
|
||||
ffi (>= 0.5.0)
|
||||
rb-inotify (0.9.9)
|
||||
ffi (~> 1.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/matthewd/websocket-client-simple.git
|
||||
|
@ -25,7 +25,7 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: https://github.com/rails/arel.git
|
||||
revision: 437aa3a4bb8ad4f3f4eba299dbb1112852f9c7ac
|
||||
revision: 5db56a513286814991c27000af2c0243cc19d1e2
|
||||
specs:
|
||||
arel (8.0.0)
|
||||
|
||||
|
@ -91,7 +91,7 @@ PATH
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.5.0)
|
||||
addressable (2.5.1)
|
||||
public_suffix (~> 2.0, >= 2.0.2)
|
||||
amq-protocol (2.1.0)
|
||||
ast (2.3.0)
|
||||
|
@ -125,7 +125,7 @@ GEM
|
|||
bunny (2.6.2)
|
||||
amq-protocol (>= 2.0.1)
|
||||
byebug (9.0.6)
|
||||
capybara (2.13.0)
|
||||
capybara (2.14.0)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
|
@ -148,10 +148,10 @@ GEM
|
|||
daemons (1.2.4)
|
||||
dalli (2.7.6)
|
||||
dante (0.2.0)
|
||||
delayed_job (4.1.2)
|
||||
activesupport (>= 3.0, < 5.1)
|
||||
delayed_job_active_record (4.1.1)
|
||||
activerecord (>= 3.0, < 5.1)
|
||||
delayed_job (4.1.3)
|
||||
activesupport (>= 3.0, < 5.2)
|
||||
delayed_job_active_record (4.1.2)
|
||||
activerecord (>= 3.0, < 5.2)
|
||||
delayed_job (>= 3.0, < 5)
|
||||
em-hiredis (0.3.1)
|
||||
eventmachine (~> 1.0)
|
||||
|
@ -207,8 +207,9 @@ GEM
|
|||
ruby_dep (~> 1.2)
|
||||
loofah (2.0.3)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.6.4)
|
||||
mail (2.6.5)
|
||||
mime-types (>= 1.16, < 4)
|
||||
mathn (0.1.0)
|
||||
metaclass (0.0.4)
|
||||
method_source (0.8.2)
|
||||
mime-types (3.1)
|
||||
|
@ -227,16 +228,17 @@ GEM
|
|||
multi_json (1.12.1)
|
||||
multipart-post (2.0.0)
|
||||
mustache (1.0.3)
|
||||
mysql2 (0.4.5)
|
||||
mysql2 (0.4.5-x64-mingw32)
|
||||
mysql2 (0.4.5-x86-mingw32)
|
||||
mysql2 (0.4.6)
|
||||
mysql2 (0.4.6-x64-mingw32)
|
||||
mysql2 (0.4.6-x86-mingw32)
|
||||
nio4r (2.0.0)
|
||||
nokogiri (1.7.0.1)
|
||||
nokogiri (1.7.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
nokogiri (1.7.0.1-x64-mingw32)
|
||||
nokogiri (1.7.1-x64-mingw32)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
nokogiri (1.7.0.1-x86-mingw32)
|
||||
nokogiri (1.7.1-x86-mingw32)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
parallel (1.11.2)
|
||||
parser (2.4.0.0)
|
||||
ast (~> 2.2)
|
||||
path_expander (1.0.1)
|
||||
|
@ -255,10 +257,10 @@ GEM
|
|||
simple_uuid
|
||||
que (0.12.0)
|
||||
racc (1.4.14)
|
||||
rack (2.0.1)
|
||||
rack (2.0.3)
|
||||
rack-cache (1.6.1)
|
||||
rack (>= 0.4)
|
||||
rack-protection (1.5.3)
|
||||
rack-protection (2.0.0)
|
||||
rack
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
|
@ -267,12 +269,13 @@ GEM
|
|||
nokogiri (~> 1.6)
|
||||
rails-html-sanitizer (1.0.3)
|
||||
loofah (~> 2.0)
|
||||
rainbow (2.2.1)
|
||||
rainbow (2.2.2)
|
||||
rake
|
||||
rake (12.0.0)
|
||||
rb-fsevent (0.9.8)
|
||||
rdoc (5.1.0)
|
||||
redcarpet (3.2.3)
|
||||
redis (3.3.2)
|
||||
redis (3.3.3)
|
||||
redis-namespace (1.5.2)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
resque (1.27.0)
|
||||
|
@ -286,7 +289,8 @@ GEM
|
|||
redis (~> 3.3)
|
||||
resque (~> 1.26)
|
||||
rufus-scheduler (~> 3.2)
|
||||
rubocop (0.48.1)
|
||||
rubocop (0.49.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.3.3.1, < 3.0)
|
||||
powerpack (~> 0.1)
|
||||
rainbow (>= 1.99.1, < 3.0)
|
||||
|
@ -313,11 +317,11 @@ GEM
|
|||
sequel (4.42.1)
|
||||
serverengine (1.5.11)
|
||||
sigdump (~> 0.2.2)
|
||||
sidekiq (4.2.9)
|
||||
sidekiq (5.0.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (~> 3.2, >= 3.2.1)
|
||||
redis (~> 3.3, >= 3.3.3)
|
||||
sigdump (0.2.4)
|
||||
simple_uuid (0.4.0)
|
||||
sinatra (1.0)
|
||||
|
@ -389,7 +393,7 @@ DEPENDENCIES
|
|||
blade
|
||||
blade-sauce_labs_plugin
|
||||
byebug
|
||||
capybara (~> 2.13.0)
|
||||
capybara (~> 2.13)
|
||||
coffee-rails
|
||||
dalli (>= 2.2.1)
|
||||
delayed_job
|
||||
|
@ -402,6 +406,7 @@ DEPENDENCIES
|
|||
kindlerb (~> 1.2.0)
|
||||
libxml-ruby
|
||||
listen (>= 3.0.5, < 3.2)
|
||||
mathn
|
||||
minitest-bisect
|
||||
mocha (~> 0.14)
|
||||
mysql2 (>= 0.4.4)
|
||||
|
@ -439,4 +444,4 @@ DEPENDENCIES
|
|||
websocket-client-simple!
|
||||
|
||||
BUNDLED WITH
|
||||
1.14.6
|
||||
1.15.0
|
||||
|
|
2
Rakefile
2
Rakefile
|
@ -1,6 +1,6 @@
|
|||
require "net/http"
|
||||
|
||||
$:.unshift File.expand_path("..", __FILE__)
|
||||
$:.unshift __dir__
|
||||
require "tasks/release"
|
||||
require "railties/lib/rails/api/task"
|
||||
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
Previously any socket errors were ignored and this made it hard to diagnose socket issues (e.g. as discussed in #28362).
|
||||
|
||||
*Edward Poot*
|
||||
|
||||
|
||||
|
||||
Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/actioncable/CHANGELOG.md) for previous changes.
|
||||
|
|
|
@ -409,7 +409,7 @@ application. The recommended basic setup is as follows:
|
|||
|
||||
```ruby
|
||||
# cable/config.ru
|
||||
require ::File.expand_path('../../config/environment', __FILE__)
|
||||
require ::File.expand_path('../config/environment', __dir__)
|
||||
Rails.application.eager_load!
|
||||
|
||||
run ActionCable.server
|
||||
|
|
|
@ -3,15 +3,13 @@ require "pathname"
|
|||
require "open3"
|
||||
require "action_cable"
|
||||
|
||||
dir = File.dirname(__FILE__)
|
||||
|
||||
task default: :test
|
||||
|
||||
task package: %w( assets:compile assets:verify )
|
||||
|
||||
Rake::TestTask.new do |t|
|
||||
t.libs << "test"
|
||||
t.test_files = Dir.glob("#{dir}/test/**/*_test.rb")
|
||||
t.test_files = Dir.glob("#{__dir__}/test/**/*_test.rb")
|
||||
t.warning = true
|
||||
t.verbose = true
|
||||
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
|
||||
|
@ -46,7 +44,7 @@ namespace :assets do
|
|||
desc "Verify compiled Action Cable assets"
|
||||
task :verify do
|
||||
file = "lib/assets/compiled/action_cable.js"
|
||||
pathname = Pathname.new("#{dir}/#{file}")
|
||||
pathname = Pathname.new("#{__dir__}/#{file}")
|
||||
|
||||
print "[verify] #{file} exists "
|
||||
if pathname.exist?
|
||||
|
@ -64,8 +62,8 @@ namespace :assets do
|
|||
fail
|
||||
end
|
||||
|
||||
print "[verify] #{dir} can be required as a module "
|
||||
stdout, stderr, status = Open3.capture3("node", "--print", "window = {}; require('#{dir}');")
|
||||
print "[verify] #{__dir__} can be required as a module "
|
||||
_, stderr, status = Open3.capture3("node", "--print", "window = {}; require('#{__dir__}');")
|
||||
if status.success?
|
||||
puts "[OK]"
|
||||
else
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
|
||||
version = File.read(File.expand_path("../RAILS_VERSION", __dir__)).strip
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
|
|
|
@ -4,8 +4,7 @@ module ActionCable
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :periodic_timers, instance_reader: false
|
||||
self.periodic_timers = []
|
||||
class_attribute :periodic_timers, instance_reader: false, default: []
|
||||
|
||||
after_subscribe :start_periodic_timers
|
||||
after_unsubscribe :stop_periodic_timers
|
||||
|
|
|
@ -6,8 +6,7 @@ module ActionCable
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :identifiers
|
||||
self.identifiers = Set.new
|
||||
class_attribute :identifiers, default: Set.new
|
||||
end
|
||||
|
||||
class_methods do
|
||||
|
|
|
@ -45,7 +45,7 @@ module ActionCable
|
|||
end
|
||||
|
||||
# Returns all the identifiers that were applied to this connection.
|
||||
def identifiers
|
||||
redefine_method :identifiers do
|
||||
server.connection_identifiers
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Rails
|
||||
module Generators
|
||||
class ChannelGenerator < NamedBase
|
||||
source_root File.expand_path("../templates", __FILE__)
|
||||
source_root File.expand_path("templates", __dir__)
|
||||
|
||||
argument :actions, type: :array, default: [], banner: "method method"
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ rescue LoadError
|
|||
end
|
||||
|
||||
# Require all the stubs and models
|
||||
Dir[File.dirname(__FILE__) + "/stubs/*.rb"].each { |file| require file }
|
||||
Dir[File.expand_path("stubs/*.rb", __dir__)].each { |file| require file }
|
||||
|
||||
class ActionCable::TestCase < ActiveSupport::TestCase
|
||||
def wait_for_async
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
|
||||
version = File.read(File.expand_path("../RAILS_VERSION", __dir__)).strip
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
|
|
|
@ -25,6 +25,7 @@ require "abstract_controller"
|
|||
require "action_mailer/version"
|
||||
|
||||
# Common Active Support usage in Action Mailer
|
||||
require "active_support"
|
||||
require "active_support/rails"
|
||||
require "active_support/core_ext/class"
|
||||
require "active_support/core_ext/module/attr_internal"
|
||||
|
|
|
@ -459,8 +459,7 @@ module ActionMailer
|
|||
|
||||
helper ActionMailer::MailHelper
|
||||
|
||||
class_attribute :default_params
|
||||
self.default_params = {
|
||||
class_attribute :default_params, default: {
|
||||
mime_version: "1.0",
|
||||
charset: "UTF-8",
|
||||
content_type: "text/plain",
|
||||
|
|
|
@ -7,8 +7,6 @@ module ActionMailer
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :delivery_methods, :delivery_method
|
||||
|
||||
# Do not make this inheritable, because we always want it to propagate
|
||||
cattr_accessor :raise_delivery_errors
|
||||
self.raise_delivery_errors = true
|
||||
|
@ -19,8 +17,8 @@ module ActionMailer
|
|||
cattr_accessor :deliver_later_queue_name
|
||||
self.deliver_later_queue_name = :mailers
|
||||
|
||||
self.delivery_methods = {}.freeze
|
||||
self.delivery_method = :smtp
|
||||
class_attribute :delivery_methods, default: {}.freeze
|
||||
class_attribute :delivery_method, default: :smtp
|
||||
|
||||
add_delivery_method :smtp, Mail::SMTP,
|
||||
address: "localhost",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Rails
|
||||
module Generators
|
||||
class MailerGenerator < NamedBase
|
||||
source_root File.expand_path("../templates", __FILE__)
|
||||
source_root File.expand_path("templates", __dir__)
|
||||
|
||||
argument :actions, type: :array, default: [], banner: "method method"
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ end
|
|||
|
||||
module Rails
|
||||
def self.root
|
||||
File.expand_path("../", File.dirname(__FILE__))
|
||||
File.expand_path("..", __dir__)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -28,7 +28,7 @@ ActiveSupport::Deprecation.debug = true
|
|||
# Disable available locale checks to avoid warnings running the test suite.
|
||||
I18n.enforce_available_locales = false
|
||||
|
||||
FIXTURE_LOAD_PATH = File.expand_path("fixtures", File.dirname(__FILE__))
|
||||
FIXTURE_LOAD_PATH = File.expand_path("fixtures", __dir__)
|
||||
ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
|
||||
|
||||
class ActiveSupport::TestCase
|
||||
|
|
|
@ -5,7 +5,7 @@ require "mailers/caching_mailer"
|
|||
|
||||
CACHE_DIR = "test_cache"
|
||||
# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
|
||||
FILE_STORE_PATH = File.join(File.dirname(__FILE__), "/../temp/", CACHE_DIR)
|
||||
FILE_STORE_PATH = File.join(__dir__, "/../temp/", CACHE_DIR)
|
||||
|
||||
class FragmentCachingMailer < ActionMailer::Base
|
||||
abstract!
|
||||
|
@ -21,10 +21,6 @@ class BaseCachingTest < ActiveSupport::TestCase
|
|||
@mailer.perform_caching = true
|
||||
@mailer.cache_store = @store
|
||||
end
|
||||
|
||||
def test_fragment_cache_key
|
||||
assert_equal "views/what a key", @mailer.fragment_cache_key("what a key")
|
||||
end
|
||||
end
|
||||
|
||||
class FragmentCachingTest < BaseCachingTest
|
||||
|
@ -126,7 +122,7 @@ class FunctionalFragmentCachingTest < BaseCachingTest
|
|||
|
||||
assert_match expected_body, email.body.encoded
|
||||
assert_match expected_body,
|
||||
@store.read("views/caching/#{template_digest("caching_mailer/fragment_cache")}")
|
||||
@store.read("views/caching_mailer/fragment_cache:#{template_digest("caching_mailer/fragment_cache")}/caching")
|
||||
end
|
||||
|
||||
def test_fragment_caching_in_partials
|
||||
|
@ -135,7 +131,7 @@ class FunctionalFragmentCachingTest < BaseCachingTest
|
|||
assert_match(expected_body, email.body.encoded)
|
||||
|
||||
assert_match(expected_body,
|
||||
@store.read("views/caching/#{template_digest("caching_mailer/_partial")}"))
|
||||
@store.read("views/caching_mailer/_partial:#{template_digest("caching_mailer/_partial")}/caching"))
|
||||
end
|
||||
|
||||
def test_skip_fragment_cache_digesting
|
||||
|
@ -185,7 +181,7 @@ class FunctionalFragmentCachingTest < BaseCachingTest
|
|||
end
|
||||
|
||||
assert_equal "caching_mailer", payload[:mailer]
|
||||
assert_equal "views/caching/#{template_digest("caching_mailer/fragment_cache")}", payload[:key]
|
||||
assert_equal [ :views, "caching_mailer/fragment_cache:#{template_digest("caching_mailer/fragment_cache")}", :caching ], payload[:key]
|
||||
ensure
|
||||
@mailer.enable_fragment_cache_logging = true
|
||||
end
|
||||
|
|
|
@ -26,7 +26,7 @@ class AMLogSubscriberTest < ActionMailer::TestCase
|
|||
wait
|
||||
|
||||
assert_equal(1, @logger.logged(:info).size)
|
||||
assert_match(/Sent mail to system@test.lindsaar.net/, @logger.logged(:info).first)
|
||||
assert_match(/Sent mail to system@test\.lindsaar\.net/, @logger.logged(:info).first)
|
||||
|
||||
assert_equal(2, @logger.logged(:debug).size)
|
||||
assert_match(/BaseMailer#welcome: processed outbound mail in [\d.]+ms/, @logger.logged(:debug).first)
|
||||
|
@ -36,7 +36,7 @@ class AMLogSubscriberTest < ActionMailer::TestCase
|
|||
end
|
||||
|
||||
def test_receive_is_notified
|
||||
fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email")
|
||||
fixture = File.read(File.expand_path("fixtures/raw_email", __dir__))
|
||||
TestMailer.receive(fixture)
|
||||
wait
|
||||
assert_equal(1, @logger.logged(:info).size)
|
||||
|
|
|
@ -1,4 +1,27 @@
|
|||
* Add `action_controller_api` and `action_controller_base` load hooks to be called in `ActiveSupport.on_load`
|
||||
* AEAD encrypted cookies and sessions with GCM
|
||||
|
||||
Encrypted cookies now use AES-GCM which couples authentication and
|
||||
encryption in one faster step and produces shorter ciphertexts. Cookies
|
||||
encrypted using AES in CBC HMAC mode will be seamlessly upgraded when
|
||||
this new mode is enabled via the
|
||||
`action_dispatch.use_authenticated_cookie_encryption` configuration value.
|
||||
|
||||
*Michael J Coyne*
|
||||
|
||||
* Change the cache key format for fragments to make it easier to debug key churn. The new format is:
|
||||
|
||||
views/template/action.html.erb:7a1156131a6928cb0026877f8b749ac9/projects/123
|
||||
^template path ^template tree digest ^class ^id
|
||||
|
||||
*DHH*
|
||||
|
||||
* Add support for recyclable cache keys with fragment caching. This uses the new versioned entries in the
|
||||
`ActiveSupport::Cache` stores and relies on the fact that Active Record has split `#cache_key` and `#cache_version`
|
||||
to support it.
|
||||
|
||||
*DHH*
|
||||
|
||||
* Add `action_controller_api` and `action_controller_base` load hooks to be called in `ActiveSupport.on_load`
|
||||
|
||||
`ActionController::Base` and `ActionController::API` have differing implementations. This means that
|
||||
the one umbrella hook `action_controller` is not able to address certain situations where a method
|
||||
|
@ -10,4 +33,5 @@
|
|||
|
||||
*Julian Nadeau*
|
||||
|
||||
|
||||
Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/actionpack/CHANGELOG.md) for previous changes.
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace :test do
|
|||
end
|
||||
|
||||
task :lines do
|
||||
load File.expand_path("..", File.dirname(__FILE__)) + "/tools/line_statistics"
|
||||
load File.expand_path("..", __dir__) + "/tools/line_statistics"
|
||||
files = FileList["lib/**/*.rb"]
|
||||
CodeTools::LineStatistics.new(files).print_loc
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
|
||||
version = File.read(File.expand_path("../RAILS_VERSION", __dir__)).strip
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
|
|
|
@ -14,8 +14,16 @@ module AbstractController
|
|||
# expected to provide their own +render+ method, since rendering means
|
||||
# different things depending on the context.
|
||||
class Base
|
||||
##
|
||||
# Returns the body of the HTTP response sent by the controller.
|
||||
attr_internal :response_body
|
||||
|
||||
##
|
||||
# Returns the name of the action this controller is processing.
|
||||
attr_internal :action_name
|
||||
|
||||
##
|
||||
# Returns the formats that can be processed by the controller.
|
||||
attr_internal :formats
|
||||
|
||||
include ActiveSupport::Configurable
|
||||
|
|
|
@ -37,8 +37,7 @@ module AbstractController
|
|||
config_accessor :enable_fragment_cache_logging
|
||||
self.enable_fragment_cache_logging = false
|
||||
|
||||
class_attribute :_view_cache_dependencies
|
||||
self._view_cache_dependencies = []
|
||||
class_attribute :_view_cache_dependencies, default: []
|
||||
helper_method :view_cache_dependencies if respond_to?(:helper_method)
|
||||
end
|
||||
|
||||
|
|
|
@ -25,7 +25,10 @@ module AbstractController
|
|||
|
||||
self.fragment_cache_keys = []
|
||||
|
||||
helper_method :fragment_cache_key if respond_to?(:helper_method)
|
||||
if respond_to?(:helper_method)
|
||||
helper_method :fragment_cache_key
|
||||
helper_method :combined_fragment_cache_key
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
@ -62,17 +65,36 @@ module AbstractController
|
|||
# with the specified +key+ value. The key is expanded using
|
||||
# ActiveSupport::Cache.expand_cache_key.
|
||||
def fragment_cache_key(key)
|
||||
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
||||
Calling fragment_cache_key directly is deprecated and will be removed in Rails 6.0.
|
||||
All fragment accessors now use the combined_fragment_cache_key method that retains the key as an array,
|
||||
such that the caching stores can interrogate the parts for cache versions used in
|
||||
recyclable cache keys.
|
||||
MSG
|
||||
|
||||
head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
|
||||
tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
|
||||
ActiveSupport::Cache.expand_cache_key([*head, *tail], :views)
|
||||
end
|
||||
|
||||
# Given a key (as described in +expire_fragment+), returns
|
||||
# a key array suitable for use in reading, writing, or expiring a
|
||||
# cached fragment. All keys begin with <tt>:views</tt>,
|
||||
# followed by ENV["RAILS_CACHE_ID"] or ENV["RAILS_APP_VERSION"] if set,
|
||||
# followed by any controller-wide key prefix values, ending
|
||||
# with the specified +key+ value.
|
||||
def combined_fragment_cache_key(key)
|
||||
head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
|
||||
tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
|
||||
[ :views, (ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]), *head, *tail ].compact
|
||||
end
|
||||
|
||||
# Writes +content+ to the location signified by
|
||||
# +key+ (see +expire_fragment+ for acceptable formats).
|
||||
def write_fragment(key, content, options = nil)
|
||||
return content unless cache_configured?
|
||||
|
||||
key = fragment_cache_key(key)
|
||||
key = combined_fragment_cache_key(key)
|
||||
instrument_fragment_cache :write_fragment, key do
|
||||
content = content.to_str
|
||||
cache_store.write(key, content, options)
|
||||
|
@ -85,7 +107,7 @@ module AbstractController
|
|||
def read_fragment(key, options = nil)
|
||||
return unless cache_configured?
|
||||
|
||||
key = fragment_cache_key(key)
|
||||
key = combined_fragment_cache_key(key)
|
||||
instrument_fragment_cache :read_fragment, key do
|
||||
result = cache_store.read(key, options)
|
||||
result.respond_to?(:html_safe) ? result.html_safe : result
|
||||
|
@ -96,7 +118,7 @@ module AbstractController
|
|||
# +key+ exists (see +expire_fragment+ for acceptable formats).
|
||||
def fragment_exist?(key, options = nil)
|
||||
return unless cache_configured?
|
||||
key = fragment_cache_key(key)
|
||||
key = combined_fragment_cache_key(key)
|
||||
|
||||
instrument_fragment_cache :exist_fragment?, key do
|
||||
cache_store.exist?(key, options)
|
||||
|
@ -123,7 +145,7 @@ module AbstractController
|
|||
# method (or <tt>delete_matched</tt>, for Regexp keys).
|
||||
def expire_fragment(key, options = nil)
|
||||
return unless cache_configured?
|
||||
key = fragment_cache_key(key) unless key.is_a?(Regexp)
|
||||
key = combined_fragment_cache_key(key) unless key.is_a?(Regexp)
|
||||
|
||||
instrument_fragment_cache :expire_fragment, key do
|
||||
if key.is_a?(Regexp)
|
||||
|
@ -135,8 +157,7 @@ module AbstractController
|
|||
end
|
||||
|
||||
def instrument_fragment_cache(name, key) # :nodoc:
|
||||
payload = instrument_payload(key)
|
||||
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", payload) { yield }
|
||||
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key)) { yield }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,24 @@
|
|||
module AbstractController
|
||||
# = Abstract Controller Callbacks
|
||||
#
|
||||
# Abstract Controller provides hooks during the life cycle of a controller action.
|
||||
# Callbacks allow you to trigger logic during this cycle. Available callbacks are:
|
||||
#
|
||||
# * <tt>after_action</tt>
|
||||
# * <tt>append_after_action</tt>
|
||||
# * <tt>append_around_action</tt>
|
||||
# * <tt>append_before_action</tt>
|
||||
# * <tt>around_action</tt>
|
||||
# * <tt>before_action</tt>
|
||||
# * <tt>prepend_after_action</tt>
|
||||
# * <tt>prepend_around_action</tt>
|
||||
# * <tt>prepend_before_action</tt>
|
||||
# * <tt>skip_after_action</tt>
|
||||
# * <tt>skip_around_action</tt>
|
||||
# * <tt>skip_before_action</tt>
|
||||
#
|
||||
# NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
|
||||
#
|
||||
module Callbacks
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
|
|
|
@ -5,11 +5,8 @@ module AbstractController
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :_helpers
|
||||
self._helpers = Module.new
|
||||
|
||||
class_attribute :_helper_methods
|
||||
self._helper_methods = Array.new
|
||||
class_attribute :_helpers, default: Module.new
|
||||
class_attribute :_helper_methods, default: Array.new
|
||||
end
|
||||
|
||||
class MissingHelperError < LoadError
|
||||
|
|
|
@ -60,9 +60,9 @@ module ActionController
|
|||
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
||||
def #{method}(event)
|
||||
return unless logger.info? && ActionController::Base.enable_fragment_cache_logging
|
||||
key_or_path = event.payload[:key] || event.payload[:path]
|
||||
key = ActiveSupport::Cache.expand_cache_key(event.payload[:key] || event.payload[:path])
|
||||
human_name = #{method.to_s.humanize.inspect}
|
||||
info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
|
||||
info("\#{human_name} \#{key} (\#{event.duration.round(1)}ms)")
|
||||
end
|
||||
METHOD
|
||||
end
|
||||
|
|
|
@ -208,8 +208,7 @@ module ActionController
|
|||
@_request.reset_session
|
||||
end
|
||||
|
||||
class_attribute :middleware_stack
|
||||
self.middleware_stack = ActionController::MiddlewareStack.new
|
||||
class_attribute :middleware_stack, default: ActionController::MiddlewareStack.new
|
||||
|
||||
def self.inherited(base) # :nodoc:
|
||||
base.middleware_stack = middleware_stack.dup
|
||||
|
|
|
@ -7,8 +7,7 @@ module ActionController
|
|||
include Head
|
||||
|
||||
included do
|
||||
class_attribute :etaggers
|
||||
self.etaggers = []
|
||||
class_attribute :etaggers, default: []
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
|
|
@ -22,8 +22,7 @@ module ActionController
|
|||
include ActionController::ConditionalGet
|
||||
|
||||
included do
|
||||
class_attribute :etag_with_template_digest
|
||||
self.etag_with_template_digest = true
|
||||
class_attribute :etag_with_template_digest, default: true
|
||||
|
||||
ActiveSupport.on_load :action_view, yield: true do
|
||||
etag do |options|
|
||||
|
|
|
@ -3,8 +3,7 @@ module ActionController #:nodoc:
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :_flash_types, instance_accessor: false
|
||||
self._flash_types = []
|
||||
class_attribute :_flash_types, instance_accessor: false, default: []
|
||||
|
||||
delegate :flash, to: :request
|
||||
add_flash_types(:alert, :notice)
|
||||
|
|
|
@ -53,9 +53,8 @@ module ActionController
|
|||
include AbstractController::Helpers
|
||||
|
||||
included do
|
||||
class_attribute :helpers_path, :include_all_helpers
|
||||
self.helpers_path ||= []
|
||||
self.include_all_helpers = true
|
||||
class_attribute :helpers_path, default: []
|
||||
class_attribute :include_all_helpers, default: true
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
|
|
@ -159,8 +159,7 @@ module ActionController
|
|||
end
|
||||
|
||||
included do
|
||||
class_attribute :_wrapper_options
|
||||
self._wrapper_options = Options.from_hash(format: [])
|
||||
class_attribute :_wrapper_options, default: Options.from_hash(format: [])
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
|
|
@ -26,8 +26,7 @@ module ActionController
|
|||
RENDERERS = Set.new
|
||||
|
||||
included do
|
||||
class_attribute :_renderers
|
||||
self._renderers = Set.new.freeze
|
||||
class_attribute :_renderers, default: Set.new.freeze
|
||||
end
|
||||
|
||||
# Used in <tt>ActionController::Base</tt>
|
||||
|
|
|
@ -666,8 +666,8 @@ module ActionController
|
|||
# to key. If the key is not found, returns the default value. If the
|
||||
# optional code block is given and the key is not found, pass in the key
|
||||
# and return the result of block.
|
||||
def delete(key)
|
||||
convert_value_to_parameters(@parameters.delete(key))
|
||||
def delete(key, &block)
|
||||
convert_value_to_parameters(@parameters.delete(key, &block))
|
||||
end
|
||||
|
||||
# Returns a new instance of <tt>ActionController::Parameters</tt> with only
|
||||
|
|
|
@ -27,14 +27,18 @@ module ActionDispatch
|
|||
@tempfile = hash[:tempfile]
|
||||
raise(ArgumentError, ":tempfile is required") unless @tempfile
|
||||
|
||||
@original_filename = hash[:filename]
|
||||
if @original_filename
|
||||
if hash[:filename]
|
||||
@original_filename = hash[:filename].dup
|
||||
|
||||
begin
|
||||
@original_filename.encode!(Encoding::UTF_8)
|
||||
rescue EncodingError
|
||||
@original_filename.force_encoding(Encoding::UTF_8)
|
||||
end
|
||||
else
|
||||
@original_filename = nil
|
||||
end
|
||||
|
||||
@content_type = hash[:type]
|
||||
@headers = hash[:head]
|
||||
end
|
||||
|
|
|
@ -18,14 +18,6 @@ module ActionDispatch
|
|||
@tt = transition_table
|
||||
end
|
||||
|
||||
def simulate(string)
|
||||
ms = memos(string) { return }
|
||||
MatchData.new(ms)
|
||||
end
|
||||
|
||||
alias :=~ :simulate
|
||||
alias :match :simulate
|
||||
|
||||
def memos(string)
|
||||
input = StringScanner.new(string)
|
||||
state = [0]
|
||||
|
|
|
@ -82,7 +82,7 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
def visualizer(paths, title = "FSM")
|
||||
viz_dir = File.join File.dirname(__FILE__), "..", "visualizer"
|
||||
viz_dir = File.join __dir__, "..", "visualizer"
|
||||
fsm_js = File.read File.join(viz_dir, "fsm.js")
|
||||
fsm_css = File.read File.join(viz_dir, "fsm.css")
|
||||
erb = File.read File.join(viz_dir, "index.html.erb")
|
||||
|
|
|
@ -13,11 +13,13 @@ module ActionDispatch
|
|||
# normalize_path("") # => "/"
|
||||
# normalize_path("/%ab") # => "/%AB"
|
||||
def self.normalize_path(path)
|
||||
encoding = path.encoding
|
||||
path = "/#{path}"
|
||||
path.squeeze!("/".freeze)
|
||||
path.sub!(%r{/+\Z}, "".freeze)
|
||||
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
||||
path = "/" if path == "".freeze
|
||||
path.force_encoding(encoding)
|
||||
path
|
||||
end
|
||||
|
||||
|
@ -59,11 +61,11 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
private
|
||||
def escape(component, pattern) # :doc:
|
||||
def escape(component, pattern)
|
||||
component.gsub(pattern) { |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
|
||||
end
|
||||
|
||||
def percent_encode(unsafe) # :doc:
|
||||
def percent_encode(unsafe)
|
||||
safe = EMPTY.dup
|
||||
unsafe.each_byte { |b| safe << DEC2HEX[b] }
|
||||
safe
|
||||
|
@ -84,6 +86,10 @@ module ActionDispatch
|
|||
ENCODER.escape_fragment(fragment.to_s)
|
||||
end
|
||||
|
||||
# Replaces any escaped sequences with their unescaped representations.
|
||||
#
|
||||
# uri = "/topics?title=Ruby%20on%20Rails"
|
||||
# unescape_uri(uri) #=> "/topics?title=Ruby on Rails"
|
||||
def self.unescape_uri(uri)
|
||||
ENCODER.unescape_uri(uri)
|
||||
end
|
||||
|
|
|
@ -43,6 +43,10 @@ module ActionDispatch
|
|||
get_header Cookies::ENCRYPTED_SIGNED_COOKIE_SALT
|
||||
end
|
||||
|
||||
def authenticated_encrypted_cookie_salt
|
||||
get_header Cookies::AUTHENTICATED_ENCRYPTED_COOKIE_SALT
|
||||
end
|
||||
|
||||
def secret_token
|
||||
get_header Cookies::SECRET_TOKEN
|
||||
end
|
||||
|
@ -149,6 +153,7 @@ module ActionDispatch
|
|||
SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt".freeze
|
||||
ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt".freeze
|
||||
ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze
|
||||
AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "action_dispatch.authenticated_encrypted_cookie_salt".freeze
|
||||
SECRET_TOKEN = "action_dispatch.secret_token".freeze
|
||||
SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze
|
||||
COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze
|
||||
|
@ -207,6 +212,9 @@ module ActionDispatch
|
|||
# If +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
|
||||
# legacy cookies signed with the old key generator will be transparently upgraded.
|
||||
#
|
||||
# If +config.action_dispatch.encrypted_cookie_salt+ and +config.action_dispatch.encrypted_signed_cookie_salt+
|
||||
# are both set, legacy cookies encrypted with HMAC AES-256-CBC will be transparently upgraded.
|
||||
#
|
||||
# This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
|
||||
#
|
||||
# Example:
|
||||
|
@ -219,6 +227,8 @@ module ActionDispatch
|
|||
@encrypted ||=
|
||||
if upgrade_legacy_signed_cookies?
|
||||
UpgradeLegacyEncryptedCookieJar.new(self)
|
||||
elsif upgrade_legacy_hmac_aes_cbc_cookies?
|
||||
UpgradeLegacyHmacAesCbcCookieJar.new(self)
|
||||
else
|
||||
EncryptedCookieJar.new(self)
|
||||
end
|
||||
|
@ -240,6 +250,13 @@ module ActionDispatch
|
|||
def upgrade_legacy_signed_cookies?
|
||||
request.secret_token.present? && request.secret_key_base.present?
|
||||
end
|
||||
|
||||
def upgrade_legacy_hmac_aes_cbc_cookies?
|
||||
request.secret_key_base.present? &&
|
||||
request.authenticated_encrypted_cookie_salt.present? &&
|
||||
request.encrypted_signed_cookie_salt.present? &&
|
||||
request.encrypted_cookie_salt.present?
|
||||
end
|
||||
end
|
||||
|
||||
# Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream
|
||||
|
@ -576,9 +593,11 @@ module ActionDispatch
|
|||
"Read the upgrade documentation to learn more about this new config option."
|
||||
end
|
||||
|
||||
secret = key_generator.generate_key(request.encrypted_cookie_salt || "")[0, ActiveSupport::MessageEncryptor.key_len]
|
||||
sign_secret = key_generator.generate_key(request.encrypted_signed_cookie_salt || "")
|
||||
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
||||
cipher = "aes-256-gcm"
|
||||
key_len = ActiveSupport::MessageEncryptor.key_len(cipher)
|
||||
secret = key_generator.generate_key(request.authenticated_encrypted_cookie_salt || "")[0, key_len]
|
||||
|
||||
@encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -603,6 +622,32 @@ module ActionDispatch
|
|||
include VerifyAndUpgradeLegacySignedMessage
|
||||
end
|
||||
|
||||
# UpgradeLegacyHmacAesCbcCookieJar is used by ActionDispatch::Session::CookieStore
|
||||
# to upgrade cookies encrypted with AES-256-CBC with HMAC to AES-256-GCM
|
||||
class UpgradeLegacyHmacAesCbcCookieJar < EncryptedCookieJar
|
||||
def initialize(parent_jar)
|
||||
super
|
||||
|
||||
secret = key_generator.generate_key(request.encrypted_cookie_salt || "")[0, ActiveSupport::MessageEncryptor.key_len]
|
||||
sign_secret = key_generator.generate_key(request.encrypted_signed_cookie_salt || "")
|
||||
|
||||
@legacy_encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
||||
end
|
||||
|
||||
def decrypt_and_verify_legacy_encrypted_message(name, signed_message)
|
||||
deserialize(name, @legacy_encryptor.decrypt_and_verify(signed_message)).tap do |value|
|
||||
self[name] = { value: value }
|
||||
end
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
def parse(name, signed_message)
|
||||
super || decrypt_and_verify_legacy_encrypted_message(name, signed_message)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ module ActionDispatch
|
|||
# This middleware is responsible for logging exceptions and
|
||||
# showing a debugging page in case the request is local.
|
||||
class DebugExceptions
|
||||
RESCUES_TEMPLATE_PATH = File.expand_path("../templates", __FILE__)
|
||||
RESCUES_TEMPLATE_PATH = File.expand_path("templates", __dir__)
|
||||
|
||||
class DebugView < ActionView::Base
|
||||
def debug_params(params)
|
||||
|
|
|
@ -16,6 +16,7 @@ module ActionDispatch
|
|||
config.action_dispatch.signed_cookie_salt = "signed cookie"
|
||||
config.action_dispatch.encrypted_cookie_salt = "encrypted cookie"
|
||||
config.action_dispatch.encrypted_signed_cookie_salt = "signed encrypted cookie"
|
||||
config.action_dispatch.use_authenticated_cookie_encryption = false
|
||||
config.action_dispatch.perform_deep_munge = true
|
||||
|
||||
config.action_dispatch.default_headers = {
|
||||
|
@ -36,6 +37,8 @@ module ActionDispatch
|
|||
ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses)
|
||||
ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates)
|
||||
|
||||
config.action_dispatch.authenticated_encrypted_cookie_salt = "authenticated encrypted cookie" if config.action_dispatch.use_authenticated_cookie_encryption
|
||||
|
||||
config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil?
|
||||
ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
|
||||
|
||||
|
|
|
@ -101,11 +101,13 @@ module ActionDispatch
|
|||
|
||||
# Returns keys of the session as Array.
|
||||
def keys
|
||||
load_for_read!
|
||||
@delegate.keys
|
||||
end
|
||||
|
||||
# Returns values of the session as Array.
|
||||
def values
|
||||
load_for_read!
|
||||
@delegate.values
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,12 @@ module ActionDispatch
|
|||
module SystemTesting
|
||||
module TestHelpers
|
||||
module SetupAndTeardown # :nodoc:
|
||||
DEFAULT_HOST = "127.0.0.1"
|
||||
DEFAULT_HOST = "http://127.0.0.1"
|
||||
|
||||
def host!(host)
|
||||
super
|
||||
Capybara.app_host = host
|
||||
end
|
||||
|
||||
def before_setup
|
||||
host! DEFAULT_HOST
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
$:.unshift(File.dirname(__FILE__) + "/lib")
|
||||
$:.unshift(File.dirname(__FILE__) + "/fixtures/helpers")
|
||||
$:.unshift(File.dirname(__FILE__) + "/fixtures/alternate_helpers")
|
||||
$:.unshift File.expand_path("lib", __dir__)
|
||||
$:.unshift File.expand_path("fixtures/helpers", __dir__)
|
||||
$:.unshift File.expand_path("fixtures/alternate_helpers", __dir__)
|
||||
|
||||
require "active_support/core_ext/kernel/reporting"
|
||||
|
||||
|
@ -56,7 +56,7 @@ ActiveSupport::Deprecation.debug = true
|
|||
# Disable available locale checks to avoid warnings running the test suite.
|
||||
I18n.enforce_available_locales = false
|
||||
|
||||
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), "fixtures")
|
||||
FIXTURE_LOAD_PATH = File.join(__dir__, "fixtures")
|
||||
|
||||
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
|
||||
|
||||
|
@ -156,7 +156,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def with_autoload_path(path)
|
||||
path = File.join(File.dirname(__FILE__), "fixtures", path)
|
||||
path = File.join(__dir__, "fixtures", path)
|
||||
if ActiveSupport::Dependencies.autoload_paths.include?(path)
|
||||
yield
|
||||
else
|
||||
|
|
|
@ -83,7 +83,7 @@ class ActionPackAssertionsController < ActionController::Base
|
|||
end
|
||||
|
||||
def render_file_absolute_path
|
||||
render file: File.expand_path("../../../README.rdoc", __FILE__)
|
||||
render file: File.expand_path("../../README.rdoc", __dir__)
|
||||
end
|
||||
|
||||
def render_file_relative_path
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require "abstract_unit"
|
||||
|
||||
module TestApiFileUtils
|
||||
def file_path() File.expand_path(__FILE__) end
|
||||
def file_path() __FILE__ end
|
||||
def file_data() @data ||= File.open(file_path, "rb") { |f| f.read } end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ require "lib/controller/fake_models"
|
|||
|
||||
CACHE_DIR = "test_cache"
|
||||
# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
|
||||
FILE_STORE_PATH = File.join(File.dirname(__FILE__), "/../temp/", CACHE_DIR)
|
||||
FILE_STORE_PATH = File.join(__dir__, "../temp/", CACHE_DIR)
|
||||
|
||||
class FragmentCachingMetalTestController < ActionController::Metal
|
||||
abstract!
|
||||
|
@ -26,10 +26,6 @@ class FragmentCachingMetalTest < ActionController::TestCase
|
|||
@controller.request = @request
|
||||
@controller.response = @response
|
||||
end
|
||||
|
||||
def test_fragment_cache_key
|
||||
assert_equal "views/what a key", @controller.fragment_cache_key("what a key")
|
||||
end
|
||||
end
|
||||
|
||||
class CachingController < ActionController::Base
|
||||
|
@ -43,6 +39,8 @@ class FragmentCachingTestController < CachingController
|
|||
end
|
||||
|
||||
class FragmentCachingTest < ActionController::TestCase
|
||||
ModelWithKeyAndVersion = Struct.new(:cache_key, :cache_version)
|
||||
|
||||
def setup
|
||||
super
|
||||
@store = ActiveSupport::Cache::MemoryStore.new
|
||||
|
@ -53,12 +51,25 @@ class FragmentCachingTest < ActionController::TestCase
|
|||
@controller.params = @params
|
||||
@controller.request = @request
|
||||
@controller.response = @response
|
||||
|
||||
@m1v1 = ModelWithKeyAndVersion.new("model/1", "1")
|
||||
@m1v2 = ModelWithKeyAndVersion.new("model/1", "2")
|
||||
@m2v1 = ModelWithKeyAndVersion.new("model/2", "1")
|
||||
@m2v2 = ModelWithKeyAndVersion.new("model/2", "2")
|
||||
end
|
||||
|
||||
def test_fragment_cache_key
|
||||
assert_equal "views/what a key", @controller.fragment_cache_key("what a key")
|
||||
assert_equal "views/test.host/fragment_caching_test/some_action",
|
||||
@controller.fragment_cache_key(controller: "fragment_caching_test", action: "some_action")
|
||||
assert_deprecated do
|
||||
assert_equal "views/what a key", @controller.fragment_cache_key("what a key")
|
||||
assert_equal "views/test.host/fragment_caching_test/some_action",
|
||||
@controller.fragment_cache_key(controller: "fragment_caching_test", action: "some_action")
|
||||
end
|
||||
end
|
||||
|
||||
def test_combined_fragment_cache_key
|
||||
assert_equal [ :views, "what a key" ], @controller.combined_fragment_cache_key("what a key")
|
||||
assert_equal [ :views, "test.host/fragment_caching_test/some_action" ],
|
||||
@controller.combined_fragment_cache_key(controller: "fragment_caching_test", action: "some_action")
|
||||
end
|
||||
|
||||
def test_read_fragment_with_caching_enabled
|
||||
|
@ -72,6 +83,12 @@ class FragmentCachingTest < ActionController::TestCase
|
|||
assert_nil @controller.read_fragment("name")
|
||||
end
|
||||
|
||||
def test_read_fragment_with_versioned_model
|
||||
@controller.write_fragment([ "stuff", @m1v1 ], "hello")
|
||||
assert_equal "hello", @controller.read_fragment([ "stuff", @m1v1 ])
|
||||
assert_nil @controller.read_fragment([ "stuff", @m1v2 ])
|
||||
end
|
||||
|
||||
def test_fragment_exist_with_caching_enabled
|
||||
@store.write("views/name", "value")
|
||||
assert @controller.fragment_exist?("name")
|
||||
|
@ -198,7 +215,7 @@ CACHED
|
|||
assert_equal expected_body, @response.body
|
||||
|
||||
assert_equal "This bit's fragment cached",
|
||||
@store.read("views/test.host/functional_caching/fragment_cached/#{template_digest("functional_caching/fragment_cached")}")
|
||||
@store.read("views/functional_caching/fragment_cached:#{template_digest("functional_caching/fragment_cached")}/fragment")
|
||||
end
|
||||
|
||||
def test_fragment_caching_in_partials
|
||||
|
@ -207,7 +224,7 @@ CACHED
|
|||
assert_match(/Old fragment caching in a partial/, @response.body)
|
||||
|
||||
assert_match("Old fragment caching in a partial",
|
||||
@store.read("views/test.host/functional_caching/html_fragment_cached_with_partial/#{template_digest("functional_caching/_partial")}"))
|
||||
@store.read("views/functional_caching/_partial:#{template_digest("functional_caching/_partial")}/test.host/functional_caching/html_fragment_cached_with_partial"))
|
||||
end
|
||||
|
||||
def test_skipping_fragment_cache_digesting
|
||||
|
@ -237,7 +254,7 @@ CACHED
|
|||
assert_match(/Some inline content/, @response.body)
|
||||
assert_match(/Some cached content/, @response.body)
|
||||
assert_match("Some cached content",
|
||||
@store.read("views/test.host/functional_caching/inline_fragment_cached/#{template_digest("functional_caching/inline_fragment_cached")}"))
|
||||
@store.read("views/functional_caching/inline_fragment_cached:#{template_digest("functional_caching/inline_fragment_cached")}/test.host/functional_caching/inline_fragment_cached"))
|
||||
end
|
||||
|
||||
def test_fragment_cache_instrumentation
|
||||
|
@ -264,7 +281,7 @@ CACHED
|
|||
assert_equal expected_body, @response.body
|
||||
|
||||
assert_equal "<p>ERB</p>",
|
||||
@store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached")}")
|
||||
@store.read("views/functional_caching/formatted_fragment_cached:#{template_digest("functional_caching/formatted_fragment_cached")}/fragment")
|
||||
end
|
||||
|
||||
def test_xml_formatted_fragment_caching
|
||||
|
@ -275,7 +292,7 @@ CACHED
|
|||
assert_equal expected_body, @response.body
|
||||
|
||||
assert_equal " <p>Builder</p>\n",
|
||||
@store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached")}")
|
||||
@store.read("views/functional_caching/formatted_fragment_cached:#{template_digest("functional_caching/formatted_fragment_cached")}/fragment")
|
||||
end
|
||||
|
||||
def test_fragment_caching_with_variant
|
||||
|
@ -286,7 +303,7 @@ CACHED
|
|||
assert_equal expected_body, @response.body
|
||||
|
||||
assert_equal "<p>PHONE</p>",
|
||||
@store.read("views/test.host/functional_caching/formatted_fragment_cached_with_variant/#{template_digest("functional_caching/formatted_fragment_cached_with_variant")}")
|
||||
@store.read("views/functional_caching/formatted_fragment_cached_with_variant:#{template_digest("functional_caching/formatted_fragment_cached_with_variant")}/fragment")
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -412,7 +429,7 @@ class CollectionCacheTest < ActionController::TestCase
|
|||
def test_collection_fetches_cached_views
|
||||
get :index
|
||||
assert_equal 1, @controller.partial_rendered_times
|
||||
assert_customer_cached "david/1", "david, 1"
|
||||
assert_match "david, 1", ActionView::PartialRenderer.collection_cache.read("views/customers/_customer:7c228ab609f0baf0b1f2367469210937/david/1")
|
||||
|
||||
get :index
|
||||
assert_equal 1, @controller.partial_rendered_times
|
||||
|
@ -444,14 +461,8 @@ class CollectionCacheTest < ActionController::TestCase
|
|||
|
||||
def test_caching_with_callable_cache_key
|
||||
get :index_with_callable_cache_key
|
||||
assert_customer_cached "cached_david", "david, 1"
|
||||
assert_match "david, 1", ActionView::PartialRenderer.collection_cache.read("views/customers/_customer:7c228ab609f0baf0b1f2367469210937/cached_david")
|
||||
end
|
||||
|
||||
private
|
||||
def assert_customer_cached(key, content)
|
||||
assert_match content,
|
||||
ActionView::PartialRenderer.collection_cache.read("views/#{key}/7c228ab609f0baf0b1f2367469210937")
|
||||
end
|
||||
end
|
||||
|
||||
class FragmentCacheKeyTestController < CachingController
|
||||
|
@ -470,11 +481,21 @@ class FragmentCacheKeyTest < ActionController::TestCase
|
|||
@controller.cache_store = @store
|
||||
end
|
||||
|
||||
def test_fragment_cache_key
|
||||
def test_combined_fragment_cache_key
|
||||
@controller.account_id = "123"
|
||||
assert_equal "views/v1/123/what a key", @controller.fragment_cache_key("what a key")
|
||||
assert_equal [ :views, "v1", "123", "what a key" ], @controller.combined_fragment_cache_key("what a key")
|
||||
|
||||
@controller.account_id = nil
|
||||
assert_equal "views/v1//what a key", @controller.fragment_cache_key("what a key")
|
||||
assert_equal [ :views, "v1", "what a key" ], @controller.combined_fragment_cache_key("what a key")
|
||||
end
|
||||
|
||||
def test_combined_fragment_cache_key_with_envs
|
||||
ENV["RAILS_APP_VERSION"] = "55"
|
||||
assert_equal [ :views, "55", "v1", "what a key" ], @controller.combined_fragment_cache_key("what a key")
|
||||
|
||||
ENV["RAILS_CACHE_ID"] = "66"
|
||||
assert_equal [ :views, "66", "v1", "what a key" ], @controller.combined_fragment_cache_key("what a key")
|
||||
ensure
|
||||
ENV["RAILS_CACHE_ID"] = ENV["RAILS_APP_VERSION"] = nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
require "abstract_unit"
|
||||
|
||||
ActionController::Base.helpers_path = File.expand_path("../../fixtures/helpers", __FILE__)
|
||||
ActionController::Base.helpers_path = File.expand_path("../fixtures/helpers", __dir__)
|
||||
|
||||
module Fun
|
||||
class GamesController < ActionController::Base
|
||||
|
@ -48,7 +48,7 @@ end
|
|||
|
||||
class HelpersPathsController < ActionController::Base
|
||||
paths = ["helpers2_pack", "helpers1_pack"].map do |path|
|
||||
File.join(File.expand_path("../../fixtures", __FILE__), path)
|
||||
File.join(File.expand_path("../fixtures", __dir__), path)
|
||||
end
|
||||
$:.unshift(*paths)
|
||||
|
||||
|
@ -61,7 +61,7 @@ class HelpersPathsController < ActionController::Base
|
|||
end
|
||||
|
||||
class HelpersTypoController < ActionController::Base
|
||||
path = File.expand_path("../../fixtures/helpers_typo", __FILE__)
|
||||
path = File.expand_path("../fixtures/helpers_typo", __dir__)
|
||||
$:.unshift(path)
|
||||
self.helpers_path = path
|
||||
end
|
||||
|
@ -178,7 +178,7 @@ class HelperTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def test_all_helpers_with_alternate_helper_dir
|
||||
@controller_class.helpers_path = File.expand_path("../../fixtures/alternate_helpers", __FILE__)
|
||||
@controller_class.helpers_path = File.expand_path("../fixtures/alternate_helpers", __dir__)
|
||||
|
||||
# Reload helpers
|
||||
@controller_class._helpers = Module.new
|
||||
|
|
|
@ -1091,7 +1091,7 @@ class IntegrationFileUploadTest < ActionDispatch::IntegrationTest
|
|||
end
|
||||
|
||||
def self.fixture_path
|
||||
File.dirname(__FILE__) + "/../fixtures/multipart"
|
||||
File.expand_path("../fixtures/multipart", __dir__)
|
||||
end
|
||||
|
||||
routes.draw do
|
||||
|
|
|
@ -152,7 +152,7 @@ module ActionController
|
|||
end
|
||||
|
||||
def write_sleep_autoload
|
||||
path = File.join(File.dirname(__FILE__), "../fixtures")
|
||||
path = File.expand_path("../fixtures", __dir__)
|
||||
ActiveSupport::Dependencies.autoload_paths << path
|
||||
|
||||
response.headers["Content-Type"] = "text/event-stream"
|
||||
|
|
|
@ -29,7 +29,7 @@ class StarStarMimeControllerTest < ActionController::TestCase
|
|||
end
|
||||
|
||||
class AbstractPostController < ActionController::Base
|
||||
self.view_paths = File.dirname(__FILE__) + "/../../fixtures/post_test/"
|
||||
self.view_paths = File.expand_path("../../fixtures/post_test", __dir__)
|
||||
end
|
||||
|
||||
# For testing layouts which are set automatically
|
||||
|
|
|
@ -2,15 +2,15 @@ require "abstract_unit"
|
|||
|
||||
module RenderFile
|
||||
class BasicController < ActionController::Base
|
||||
self.view_paths = File.dirname(__FILE__)
|
||||
self.view_paths = __dir__
|
||||
|
||||
def index
|
||||
render file: File.join(File.dirname(__FILE__), *%w[.. .. fixtures test hello_world])
|
||||
render file: File.expand_path("../../fixtures/test/hello_world", __dir__)
|
||||
end
|
||||
|
||||
def with_instance_variables
|
||||
@secret = "in the sauce"
|
||||
render file: File.join(File.dirname(__FILE__), "../../fixtures/test/render_file_with_ivar")
|
||||
render file: File.expand_path("../../fixtures/test/render_file_with_ivar", __dir__)
|
||||
end
|
||||
|
||||
def relative_path
|
||||
|
@ -25,11 +25,11 @@ module RenderFile
|
|||
|
||||
def pathname
|
||||
@secret = "in the sauce"
|
||||
render file: Pathname.new(File.dirname(__FILE__)).join(*%w[.. .. fixtures test dot.directory render_file_with_ivar])
|
||||
render file: Pathname.new(__dir__).join(*%w[.. .. fixtures test dot.directory render_file_with_ivar])
|
||||
end
|
||||
|
||||
def with_locals
|
||||
path = File.join(File.dirname(__FILE__), "../../fixtures/test/render_file_with_locals")
|
||||
path = File.expand_path("../../fixtures/test/render_file_with_locals", __dir__)
|
||||
render file: path, locals: { secret: "in the sauce" }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module RenderImplicitAction
|
|||
"render_implicit_action/simple/hello_world.html.erb" => "Hello world!",
|
||||
"render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!",
|
||||
"render_implicit_action/simple/not_implemented.html.erb" => "Not Implemented"
|
||||
), ActionView::FileSystemResolver.new(File.expand_path("../../../controller", __FILE__))]
|
||||
), ActionView::FileSystemResolver.new(File.expand_path("../../controller", __dir__))]
|
||||
|
||||
def hello_world() end
|
||||
end
|
||||
|
|
|
@ -25,6 +25,27 @@ class ParametersMutatorsTest < ActiveSupport::TestCase
|
|||
assert_not @params.delete(:person).permitted?
|
||||
end
|
||||
|
||||
test "delete returns the value when the key is present" do
|
||||
assert_equal "32", @params[:person].delete(:age)
|
||||
end
|
||||
|
||||
test "delete removes the entry when the key present" do
|
||||
@params[:person].delete(:age)
|
||||
assert_not @params[:person].key?(:age)
|
||||
end
|
||||
|
||||
test "delete returns nil when the key is not present" do
|
||||
assert_equal nil, @params[:person].delete(:first_name)
|
||||
end
|
||||
|
||||
test "delete returns the value of the given block when the key is not present" do
|
||||
assert_equal "David", @params[:person].delete(:first_name) { "David" }
|
||||
end
|
||||
|
||||
test "delete yields the key to the given block when the key is not present" do
|
||||
assert_equal "first_name: David", @params[:person].delete(:first_name) { |k| "#{k}: David" }
|
||||
end
|
||||
|
||||
test "delete_if retains permitted status" do
|
||||
@params.permit!
|
||||
assert @params.delete_if { |k| k == "person" }.permitted?
|
||||
|
|
|
@ -257,7 +257,7 @@ end
|
|||
module TemplateModificationHelper
|
||||
private
|
||||
def modify_template(name)
|
||||
path = File.expand_path("../../fixtures/#{name}.erb", __FILE__)
|
||||
path = File.expand_path("../fixtures/#{name}.erb", __dir__)
|
||||
original = File.read(path)
|
||||
File.write(path, "#{original} Modified!")
|
||||
ActionView::LookupContext::DetailsKey.clear
|
||||
|
@ -287,9 +287,9 @@ class ExpiresInRenderTest < ActionController::TestCase
|
|||
|
||||
def test_dynamic_render_with_file
|
||||
# This is extremely bad, but should be possible to do.
|
||||
assert File.exist?(File.join(File.dirname(__FILE__), "../../test/abstract_unit.rb"))
|
||||
assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__))
|
||||
response = get :dynamic_render_with_file, params: { id: '../\\../test/abstract_unit.rb' }
|
||||
assert_equal File.read(File.join(File.dirname(__FILE__), "../../test/abstract_unit.rb")),
|
||||
assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)),
|
||||
response.body
|
||||
end
|
||||
|
||||
|
@ -306,16 +306,16 @@ class ExpiresInRenderTest < ActionController::TestCase
|
|||
end
|
||||
|
||||
def test_dynamic_render
|
||||
assert File.exist?(File.join(File.dirname(__FILE__), "../../test/abstract_unit.rb"))
|
||||
assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__))
|
||||
assert_raises ActionView::MissingTemplate do
|
||||
get :dynamic_render, params: { id: '../\\../test/abstract_unit.rb' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_permitted_dynamic_render_file_hash
|
||||
assert File.exist?(File.join(File.dirname(__FILE__), "../../test/abstract_unit.rb"))
|
||||
assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__))
|
||||
response = get :dynamic_render_permit, params: { id: { file: '../\\../test/abstract_unit.rb' } }
|
||||
assert_equal File.read(File.join(File.dirname(__FILE__), "../../test/abstract_unit.rb")),
|
||||
assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)),
|
||||
response.body
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ require "abstract_unit"
|
|||
|
||||
module TestFileUtils
|
||||
def file_name() File.basename(__FILE__) end
|
||||
def file_path() File.expand_path(__FILE__) end
|
||||
def file_path() __FILE__ end
|
||||
def file_data() @data ||= File.open(file_path, "rb") { |f| f.read } end
|
||||
end
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ XML
|
|||
end
|
||||
|
||||
def test_send_file
|
||||
send_file(File.expand_path(__FILE__))
|
||||
send_file(__FILE__)
|
||||
end
|
||||
|
||||
def redirect_to_same_controller
|
||||
|
@ -780,7 +780,7 @@ XML
|
|||
end
|
||||
end
|
||||
|
||||
FILES_DIR = File.dirname(__FILE__) + "/../fixtures/multipart"
|
||||
FILES_DIR = File.expand_path("../fixtures/multipart", __dir__)
|
||||
|
||||
READ_BINARY = "rb:binary"
|
||||
READ_PLAIN = "r:binary"
|
||||
|
@ -855,7 +855,7 @@ XML
|
|||
end
|
||||
|
||||
def test_fixture_file_upload_ignores_fixture_path_given_full_path
|
||||
TestCaseTest.stub :fixture_path, File.dirname(__FILE__) do
|
||||
TestCaseTest.stub :fixture_path, __dir__ do
|
||||
uploaded_file = fixture_file_upload("#{FILES_DIR}/ruby_on_rails.jpg", "image/jpg")
|
||||
assert_equal File.open("#{FILES_DIR}/ruby_on_rails.jpg", READ_PLAIN).read, uploaded_file.read
|
||||
end
|
||||
|
|
|
@ -288,8 +288,7 @@ class CookiesTest < ActionController::TestCase
|
|||
@request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new(SALT, iterations: 2)
|
||||
|
||||
@request.env["action_dispatch.signed_cookie_salt"] =
|
||||
@request.env["action_dispatch.encrypted_cookie_salt"] =
|
||||
@request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT
|
||||
@request.env["action_dispatch.authenticated_encrypted_cookie_salt"] = SALT
|
||||
|
||||
@request.host = "www.nextangle.com"
|
||||
end
|
||||
|
@ -531,9 +530,7 @@ class CookiesTest < ActionController::TestCase
|
|||
get :set_encrypted_cookie
|
||||
cookies = @controller.send :cookies
|
||||
assert_not_equal "bar", cookies[:foo]
|
||||
assert_raise TypeError do
|
||||
cookies.signed[:foo]
|
||||
end
|
||||
assert_nil cookies.signed[:foo]
|
||||
assert_equal "bar", cookies.encrypted[:foo]
|
||||
end
|
||||
|
||||
|
@ -542,9 +539,7 @@ class CookiesTest < ActionController::TestCase
|
|||
get :set_encrypted_cookie
|
||||
cookies = @controller.send :cookies
|
||||
assert_not_equal "bar", cookies[:foo]
|
||||
assert_raises TypeError do
|
||||
cookies.signed[:foo]
|
||||
end
|
||||
assert_nil cookies.signed[:foo]
|
||||
assert_equal "bar", cookies.encrypted[:foo]
|
||||
end
|
||||
|
||||
|
@ -553,9 +548,7 @@ class CookiesTest < ActionController::TestCase
|
|||
get :set_encrypted_cookie
|
||||
cookies = @controller.send :cookies
|
||||
assert_not_equal "bar", cookies[:foo]
|
||||
assert_raises ::JSON::ParserError do
|
||||
cookies.signed[:foo]
|
||||
end
|
||||
assert_nil cookies.signed[:foo]
|
||||
assert_equal "bar", cookies.encrypted[:foo]
|
||||
end
|
||||
|
||||
|
@ -564,9 +557,7 @@ class CookiesTest < ActionController::TestCase
|
|||
get :set_wrapped_encrypted_cookie
|
||||
cookies = @controller.send :cookies
|
||||
assert_not_equal "wrapped: bar", cookies[:foo]
|
||||
assert_raises ::JSON::ParserError do
|
||||
cookies.signed[:foo]
|
||||
end
|
||||
assert_nil cookies.signed[:foo]
|
||||
assert_equal "wrapped: bar", cookies.encrypted[:foo]
|
||||
end
|
||||
|
||||
|
@ -577,38 +568,16 @@ class CookiesTest < ActionController::TestCase
|
|||
assert_equal "bar was dumped and loaded", cookies.encrypted[:foo]
|
||||
end
|
||||
|
||||
def test_encrypted_cookie_using_custom_digest
|
||||
@request.env["action_dispatch.cookies_digest"] = "SHA256"
|
||||
get :set_encrypted_cookie
|
||||
cookies = @controller.send :cookies
|
||||
assert_not_equal "bar", cookies[:foo]
|
||||
assert_equal "bar", cookies.encrypted[:foo]
|
||||
|
||||
sign_secret = @request.env["action_dispatch.key_generator"].generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
|
||||
|
||||
sha1_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer, digest: "SHA1")
|
||||
sha256_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer, digest: "SHA256")
|
||||
|
||||
assert_raises(ActiveSupport::MessageVerifier::InvalidSignature) do
|
||||
sha1_verifier.verify(cookies[:foo])
|
||||
end
|
||||
|
||||
assert_nothing_raised do
|
||||
sha256_verifier.verify(cookies[:foo])
|
||||
end
|
||||
end
|
||||
|
||||
def test_encrypted_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json
|
||||
@request.env["action_dispatch.cookies_serializer"] = :hybrid
|
||||
|
||||
key_generator = @request.env["action_dispatch.key_generator"]
|
||||
encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
|
||||
encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
|
||||
secret = key_generator.generate_key(encrypted_cookie_salt)
|
||||
sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
|
||||
cipher = "aes-256-gcm"
|
||||
salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
|
||||
secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)]
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: Marshal)
|
||||
|
||||
marshal_value = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: Marshal).encrypt_and_sign("bar")
|
||||
@request.headers["Cookie"] = "foo=#{marshal_value}"
|
||||
marshal_value = encryptor.encrypt_and_sign("bar")
|
||||
@request.headers["Cookie"] = "foo=#{::Rack::Utils.escape marshal_value}"
|
||||
|
||||
get :get_encrypted_cookie
|
||||
|
||||
|
@ -616,20 +585,21 @@ class CookiesTest < ActionController::TestCase
|
|||
assert_not_equal "bar", cookies[:foo]
|
||||
assert_equal "bar", cookies.encrypted[:foo]
|
||||
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON)
|
||||
assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
|
||||
json_encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON)
|
||||
assert_not_nil @response.cookies["foo"]
|
||||
assert_equal "bar", json_encryptor.decrypt_and_verify(@response.cookies["foo"])
|
||||
end
|
||||
|
||||
def test_encrypted_cookie_using_hybrid_serializer_can_read_from_json_dumped_value
|
||||
@request.env["action_dispatch.cookies_serializer"] = :hybrid
|
||||
|
||||
key_generator = @request.env["action_dispatch.key_generator"]
|
||||
encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
|
||||
encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
|
||||
secret = key_generator.generate_key(encrypted_cookie_salt)
|
||||
sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
|
||||
json_value = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON).encrypt_and_sign("bar")
|
||||
@request.headers["Cookie"] = "foo=#{json_value}"
|
||||
cipher = "aes-256-gcm"
|
||||
salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
|
||||
secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)]
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON)
|
||||
|
||||
json_value = encryptor.encrypt_and_sign("bar")
|
||||
@request.headers["Cookie"] = "foo=#{::Rack::Utils.escape json_value}"
|
||||
|
||||
get :get_encrypted_cookie
|
||||
|
||||
|
@ -640,19 +610,6 @@ class CookiesTest < ActionController::TestCase
|
|||
assert_nil @response.cookies["foo"]
|
||||
end
|
||||
|
||||
def test_compat_encrypted_cookie_using_64_byte_key
|
||||
# Cookie generated with 64 bytes secret
|
||||
message = ["566d4e75536d686e633246564e6b493062557079626c566d51574d30515430394c53315665564a694e4563786555744f57537454576b396a5a31566a626e52525054303d2d2d34663234333330623130623261306163363562316266323335396164666364613564643134623131"].pack("H*")
|
||||
@request.headers["Cookie"] = "foo=#{message}"
|
||||
|
||||
get :get_encrypted_cookie
|
||||
|
||||
cookies = @controller.send :cookies
|
||||
assert_not_equal "bar", cookies[:foo]
|
||||
assert_equal "bar", cookies.encrypted[:foo]
|
||||
assert_nil @response.cookies["foo"]
|
||||
end
|
||||
|
||||
def test_accessing_nonexistent_encrypted_cookie_should_not_raise_invalid_message
|
||||
get :set_encrypted_cookie
|
||||
assert_nil @controller.send(:cookies).encrypted[:non_existent_attribute]
|
||||
|
@ -813,10 +770,10 @@ class CookiesTest < ActionController::TestCase
|
|||
|
||||
assert_equal "bar", @controller.send(:cookies).encrypted[:foo]
|
||||
|
||||
key_generator = @request.env["action_dispatch.key_generator"]
|
||||
secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"])
|
||||
sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret)
|
||||
cipher = "aes-256-gcm"
|
||||
salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
|
||||
secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)]
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: Marshal)
|
||||
assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
|
||||
end
|
||||
|
||||
|
@ -842,8 +799,6 @@ class CookiesTest < ActionController::TestCase
|
|||
@request.env["action_dispatch.cookies_serializer"] = :json
|
||||
@request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
|
||||
@request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
|
||||
@request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee"
|
||||
@request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9"
|
||||
|
||||
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate("bar")
|
||||
|
||||
|
@ -852,10 +807,10 @@ class CookiesTest < ActionController::TestCase
|
|||
|
||||
assert_equal "bar", @controller.send(:cookies).encrypted[:foo]
|
||||
|
||||
key_generator = @request.env["action_dispatch.key_generator"]
|
||||
secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"])
|
||||
sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON)
|
||||
cipher = "aes-256-gcm"
|
||||
salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
|
||||
secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)]
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON)
|
||||
assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
|
||||
end
|
||||
|
||||
|
@ -881,8 +836,6 @@ class CookiesTest < ActionController::TestCase
|
|||
@request.env["action_dispatch.cookies_serializer"] = :hybrid
|
||||
@request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
|
||||
@request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
|
||||
@request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee"
|
||||
@request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9"
|
||||
|
||||
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate("bar")
|
||||
|
||||
|
@ -891,10 +844,10 @@ class CookiesTest < ActionController::TestCase
|
|||
|
||||
assert_equal "bar", @controller.send(:cookies).encrypted[:foo]
|
||||
|
||||
key_generator = @request.env["action_dispatch.key_generator"]
|
||||
secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"])
|
||||
sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON)
|
||||
cipher = "aes-256-gcm"
|
||||
salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
|
||||
secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)]
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON)
|
||||
assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
|
||||
end
|
||||
|
||||
|
@ -920,8 +873,6 @@ class CookiesTest < ActionController::TestCase
|
|||
@request.env["action_dispatch.cookies_serializer"] = :hybrid
|
||||
@request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33"
|
||||
@request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
|
||||
@request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee"
|
||||
@request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9"
|
||||
|
||||
legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate("bar")
|
||||
|
||||
|
@ -930,10 +881,10 @@ class CookiesTest < ActionController::TestCase
|
|||
|
||||
assert_equal "bar", @controller.send(:cookies).encrypted[:foo]
|
||||
|
||||
key_generator = @request.env["action_dispatch.key_generator"]
|
||||
secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"])
|
||||
sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"])
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON)
|
||||
cipher = "aes-256-gcm"
|
||||
salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
|
||||
secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)]
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON)
|
||||
assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
|
||||
end
|
||||
|
||||
|
@ -959,6 +910,89 @@ class CookiesTest < ActionController::TestCase
|
|||
assert_nil @response.cookies["foo"]
|
||||
end
|
||||
|
||||
def test_legacy_hmac_aes_cbc_encrypted_marshal_cookie_is_upgraded_to_authenticated_encrypted_cookie
|
||||
@request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
|
||||
|
||||
@request.env["action_dispatch.encrypted_cookie_salt"] =
|
||||
@request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT
|
||||
|
||||
key_generator = @request.env["action_dispatch.key_generator"]
|
||||
encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
|
||||
encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
|
||||
secret = key_generator.generate_key(encrypted_cookie_salt)
|
||||
sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
|
||||
marshal_value = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: Marshal).encrypt_and_sign("bar")
|
||||
|
||||
@request.headers["Cookie"] = "foo=#{marshal_value}"
|
||||
|
||||
get :get_encrypted_cookie
|
||||
|
||||
cookies = @controller.send :cookies
|
||||
assert_not_equal "bar", cookies[:foo]
|
||||
assert_equal "bar", cookies.encrypted[:foo]
|
||||
|
||||
aead_cipher = "aes-256-gcm"
|
||||
aead_salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
|
||||
aead_secret = key_generator.generate_key(aead_salt)[0, ActiveSupport::MessageEncryptor.key_len(aead_cipher)]
|
||||
aead_encryptor = ActiveSupport::MessageEncryptor.new(aead_secret, cipher: aead_cipher, serializer: Marshal)
|
||||
|
||||
assert_equal "bar", aead_encryptor.decrypt_and_verify(@response.cookies["foo"])
|
||||
end
|
||||
|
||||
def test_legacy_hmac_aes_cbc_encrypted_json_cookie_is_upgraded_to_authenticated_encrypted_cookie
|
||||
@request.env["action_dispatch.cookies_serializer"] = :json
|
||||
@request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
|
||||
|
||||
@request.env["action_dispatch.encrypted_cookie_salt"] =
|
||||
@request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT
|
||||
|
||||
key_generator = @request.env["action_dispatch.key_generator"]
|
||||
encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
|
||||
encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
|
||||
secret = key_generator.generate_key(encrypted_cookie_salt)
|
||||
sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
|
||||
marshal_value = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON).encrypt_and_sign("bar")
|
||||
|
||||
@request.headers["Cookie"] = "foo=#{marshal_value}"
|
||||
|
||||
get :get_encrypted_cookie
|
||||
|
||||
cookies = @controller.send :cookies
|
||||
assert_not_equal "bar", cookies[:foo]
|
||||
assert_equal "bar", cookies.encrypted[:foo]
|
||||
|
||||
aead_cipher = "aes-256-gcm"
|
||||
aead_salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
|
||||
aead_secret = key_generator.generate_key(aead_salt)[0, ActiveSupport::MessageEncryptor.key_len(aead_cipher)]
|
||||
aead_encryptor = ActiveSupport::MessageEncryptor.new(aead_secret, cipher: aead_cipher, serializer: JSON)
|
||||
|
||||
assert_equal "bar", aead_encryptor.decrypt_and_verify(@response.cookies["foo"])
|
||||
end
|
||||
|
||||
def test_legacy_hmac_aes_cbc_encrypted_cookie_using_64_byte_key_is_upgraded_to_authenticated_encrypted_cookie
|
||||
@request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff"
|
||||
|
||||
@request.env["action_dispatch.encrypted_cookie_salt"] =
|
||||
@request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT
|
||||
|
||||
# Cookie generated with 64 bytes secret
|
||||
message = ["566d4e75536d686e633246564e6b493062557079626c566d51574d30515430394c53315665564a694e4563786555744f57537454576b396a5a31566a626e52525054303d2d2d34663234333330623130623261306163363562316266323335396164666364613564643134623131"].pack("H*")
|
||||
@request.headers["Cookie"] = "foo=#{message}"
|
||||
|
||||
get :get_encrypted_cookie
|
||||
|
||||
cookies = @controller.send :cookies
|
||||
assert_not_equal "bar", cookies[:foo]
|
||||
assert_equal "bar", cookies.encrypted[:foo]
|
||||
cipher = "aes-256-gcm"
|
||||
|
||||
salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"]
|
||||
secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)]
|
||||
encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: Marshal)
|
||||
|
||||
assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
|
||||
end
|
||||
|
||||
def test_cookie_with_all_domain_option
|
||||
get :set_cookie_with_domain
|
||||
assert_response :success
|
||||
|
|
|
@ -21,7 +21,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
|
|||
end
|
||||
end
|
||||
|
||||
FIXTURE_PATH = File.dirname(__FILE__) + "/../../fixtures/multipart"
|
||||
FIXTURE_PATH = File.expand_path("../../fixtures/multipart", __dir__)
|
||||
|
||||
def teardown
|
||||
TestController.last_request_parameters = nil
|
||||
|
|
|
@ -54,6 +54,11 @@ module ActionDispatch
|
|||
assert_equal %w[rails adequate], s.keys
|
||||
end
|
||||
|
||||
def test_keys_with_deferred_loading
|
||||
s = Session.create(store_with_data, req, {})
|
||||
assert_equal %w[sample_key], s.keys
|
||||
end
|
||||
|
||||
def test_values
|
||||
s = Session.create(store, req, {})
|
||||
s["rails"] = "ftw"
|
||||
|
@ -61,6 +66,11 @@ module ActionDispatch
|
|||
assert_equal %w[ftw awesome], s.values
|
||||
end
|
||||
|
||||
def test_values_with_deferred_loading
|
||||
s = Session.create(store_with_data, req, {})
|
||||
assert_equal %w[sample_value], s.values
|
||||
end
|
||||
|
||||
def test_clear
|
||||
s = Session.create(store, req, {})
|
||||
s["rails"] = "ftw"
|
||||
|
@ -113,6 +123,14 @@ module ActionDispatch
|
|||
def delete_session(env, id, options); 123; end
|
||||
}.new
|
||||
end
|
||||
|
||||
def store_with_data
|
||||
Class.new {
|
||||
def load_session(env); [1, { "sample_key" => "sample_value" }]; end
|
||||
def session_exists?(env); true; end
|
||||
def delete_session(env, id, options); 123; end
|
||||
}.new
|
||||
end
|
||||
end
|
||||
|
||||
class SessionIntegrationTest < ActionDispatch::IntegrationTest
|
||||
|
|
|
@ -107,7 +107,7 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
|
|||
query = [
|
||||
"customers[boston][first][name]=David",
|
||||
"something_else=blah",
|
||||
"logo=#{File.expand_path(__FILE__)}"
|
||||
"logo=#{__FILE__}"
|
||||
].join("&")
|
||||
expected = {
|
||||
"customers" => {
|
||||
|
@ -118,7 +118,7 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
|
|||
}
|
||||
},
|
||||
"something_else" => "blah",
|
||||
"logo" => File.expand_path(__FILE__),
|
||||
"logo" => __FILE__,
|
||||
}
|
||||
assert_parses expected, query
|
||||
end
|
||||
|
|
|
@ -110,8 +110,8 @@ class RequestIP < BaseRequestTest
|
|||
request.remote_ip
|
||||
}
|
||||
assert_match(/IP spoofing attack/, e.message)
|
||||
assert_match(/HTTP_X_FORWARDED_FOR="1.1.1.1"/, e.message)
|
||||
assert_match(/HTTP_CLIENT_IP="2.2.2.2"/, e.message)
|
||||
assert_match(/HTTP_X_FORWARDED_FOR="1\.1\.1\.1"/, e.message)
|
||||
assert_match(/HTTP_CLIENT_IP="2\.2\.2\.2"/, e.message)
|
||||
end
|
||||
|
||||
test "remote ip with spoof detection disabled" do
|
||||
|
|
|
@ -4419,7 +4419,7 @@ class TestInvalidUrls < ActionDispatch::IntegrationTest
|
|||
end
|
||||
end
|
||||
|
||||
test "invalid UTF-8 encoding returns a 400 Bad Request" do
|
||||
test "invalid UTF-8 encoding is treated as ASCII 8BIT encode" do
|
||||
with_routing do |set|
|
||||
set.draw do
|
||||
get "/bar/:id", to: redirect("/foo/show/%{id}")
|
||||
|
@ -4435,19 +4435,19 @@ class TestInvalidUrls < ActionDispatch::IntegrationTest
|
|||
end
|
||||
|
||||
get "/%E2%EF%BF%BD%A6"
|
||||
assert_response :bad_request
|
||||
assert_response :not_found
|
||||
|
||||
get "/foo/%E2%EF%BF%BD%A6"
|
||||
assert_response :bad_request
|
||||
assert_response :not_found
|
||||
|
||||
get "/foo/show/%E2%EF%BF%BD%A6"
|
||||
assert_response :bad_request
|
||||
assert_response :ok
|
||||
|
||||
get "/bar/%E2%EF%BF%BD%A6"
|
||||
assert_response :bad_request
|
||||
assert_response :redirect
|
||||
|
||||
get "/foobar/%E2%EF%BF%BD%A6"
|
||||
assert_response :bad_request
|
||||
assert_response :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,3 +19,15 @@ class SetDriverToSeleniumTest < DrivenBySeleniumWithChrome
|
|||
assert_equal :selenium, Capybara.current_driver
|
||||
end
|
||||
end
|
||||
|
||||
class SetHostTest < DrivenByRackTest
|
||||
test "sets default host" do
|
||||
assert_equal "http://127.0.0.1", Capybara.app_host
|
||||
end
|
||||
|
||||
test "overrides host" do
|
||||
host! "http://example.com"
|
||||
|
||||
assert_equal "http://example.com", Capybara.app_host
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,12 @@ module ActionDispatch
|
|||
assert_equal "foo", uf.original_filename
|
||||
end
|
||||
|
||||
def test_filename_is_different_object
|
||||
file_str = "foo"
|
||||
uf = Http::UploadedFile.new(filename: file_str, tempfile: Object.new)
|
||||
assert_not_equal file_str.object_id , uf.original_filename.object_id
|
||||
end
|
||||
|
||||
def test_filename_should_be_in_utf_8
|
||||
uf = Http::UploadedFile.new(filename: "foo", tempfile: Object.new)
|
||||
assert_equal "UTF-8", uf.original_filename.encoding.to_s
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<body>
|
||||
<%= cache do %><p>ERB</p><% end %>
|
||||
<%= cache("fragment") do %><p>ERB</p><% end %>
|
||||
</body>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
xml.body do
|
||||
cache do
|
||||
cache("fragment") do
|
||||
xml.p "Builder"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<body>
|
||||
<%= cache do %><p>PHONE</p><% end %>
|
||||
<%= cache("fragment") do %><p>PHONE</p><% end %>
|
||||
</body>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Hello
|
||||
<%= cache do %>This bit's fragment cached<% end %>
|
||||
<%= cache "fragment" do %>This bit's fragment cached<% end %>
|
||||
<%= 'Ciao' %>
|
||||
|
|
|
@ -35,25 +35,25 @@ module ActionDispatch
|
|||
|
||||
def test_simulate_gt
|
||||
sim = simulator_for ["/foo", "/bar"]
|
||||
assert_match sim, "/foo"
|
||||
assert_match_route sim, "/foo"
|
||||
end
|
||||
|
||||
def test_simulate_gt_regexp
|
||||
sim = simulator_for [":foo"]
|
||||
assert_match sim, "foo"
|
||||
assert_match_route sim, "foo"
|
||||
end
|
||||
|
||||
def test_simulate_gt_regexp_mix
|
||||
sim = simulator_for ["/get", "/:method/foo"]
|
||||
assert_match sim, "/get"
|
||||
assert_match sim, "/get/foo"
|
||||
assert_match_route sim, "/get"
|
||||
assert_match_route sim, "/get/foo"
|
||||
end
|
||||
|
||||
def test_simulate_optional
|
||||
sim = simulator_for ["/foo(/bar)"]
|
||||
assert_match sim, "/foo"
|
||||
assert_match sim, "/foo/bar"
|
||||
assert_no_match sim, "/foo/"
|
||||
assert_match_route sim, "/foo"
|
||||
assert_match_route sim, "/foo/bar"
|
||||
assert_no_match_route sim, "/foo/"
|
||||
end
|
||||
|
||||
def test_match_data
|
||||
|
@ -65,11 +65,11 @@ module ActionDispatch
|
|||
|
||||
sim = GTG::Simulator.new tt
|
||||
|
||||
match = sim.match "/get"
|
||||
assert_equal [paths.first], match.memos
|
||||
memos = sim.memos "/get"
|
||||
assert_equal [paths.first], memos
|
||||
|
||||
match = sim.match "/get/foo"
|
||||
assert_equal [paths.last], match.memos
|
||||
memos = sim.memos "/get/foo"
|
||||
assert_equal [paths.last], memos
|
||||
end
|
||||
|
||||
def test_match_data_ambiguous
|
||||
|
@ -86,8 +86,8 @@ module ActionDispatch
|
|||
builder = GTG::Builder.new ast
|
||||
sim = GTG::Simulator.new builder.transition_table
|
||||
|
||||
match = sim.match "/articles/new"
|
||||
assert_equal [paths[1], paths[3]], match.memos
|
||||
memos = sim.memos "/articles/new"
|
||||
assert_equal [paths[1], paths[3]], memos
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -109,6 +109,14 @@ module ActionDispatch
|
|||
def simulator_for(paths)
|
||||
GTG::Simulator.new tt(paths)
|
||||
end
|
||||
|
||||
def assert_match_route(simulator, path)
|
||||
assert simulator.memos(path), "Simulator should match #{path}."
|
||||
end
|
||||
|
||||
def assert_no_match_route(simulator, path)
|
||||
assert_not simulator.memos(path) { nil }, "Simulator should not match #{path}."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,6 +31,11 @@ module ActionDispatch
|
|||
def test_normalize_path_uppercase
|
||||
assert_equal "/foo%AAbar%AAbaz", Utils.normalize_path("/foo%aabar%aabaz")
|
||||
end
|
||||
|
||||
def test_normalize_path_maintains_string_encoding
|
||||
path = "/foo%AAbar%AAbaz".b
|
||||
assert_equal Encoding::ASCII_8BIT, Utils.normalize_path(path).encoding
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,6 +26,10 @@ Customer = Struct.new(:name, :id) do
|
|||
def persisted?
|
||||
id.present?
|
||||
end
|
||||
|
||||
def cache_key
|
||||
"#{name}/#{id}"
|
||||
end
|
||||
end
|
||||
|
||||
Post = Struct.new(:title, :author_name, :body, :secret, :persisted, :written_on, :cost) do
|
||||
|
|
|
@ -1 +1,11 @@
|
|||
* Add `:json` type to `auto_discovery_link_tag` to support [JSON Feeds](https://jsonfeed.org/version/1)
|
||||
|
||||
*Mike Gunderloy*
|
||||
|
||||
* Update `distance_of_time_in_words` helper to display better error messages
|
||||
for bad input.
|
||||
|
||||
*Jay Hayes*
|
||||
|
||||
|
||||
Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/actionview/CHANGELOG.md) for previous changes.
|
||||
|
|
|
@ -2,8 +2,6 @@ require "rake/testtask"
|
|||
require "fileutils"
|
||||
require "open3"
|
||||
|
||||
dir = File.dirname(__FILE__)
|
||||
|
||||
desc "Default Task"
|
||||
task default: :test
|
||||
|
||||
|
@ -95,7 +93,7 @@ namespace :assets do
|
|||
desc "Verify compiled Action View assets"
|
||||
task :verify do
|
||||
file = "lib/assets/compiled/rails-ujs.js"
|
||||
pathname = Pathname.new("#{dir}/#{file}")
|
||||
pathname = Pathname.new("#{__dir__}/#{file}")
|
||||
|
||||
print "[verify] #{file} exists "
|
||||
if pathname.exist?
|
||||
|
@ -113,13 +111,13 @@ namespace :assets do
|
|||
fail
|
||||
end
|
||||
|
||||
print "[verify] #{dir} can be required as a module "
|
||||
print "[verify] #{__dir__} can be required as a module "
|
||||
js = <<-JS
|
||||
window = { Event: class {} }
|
||||
class Element {}
|
||||
require('#{dir}')
|
||||
require('#{__dir__}')
|
||||
JS
|
||||
stdout, stderr, status = Open3.capture3("node", "--print", js)
|
||||
_, stderr, status = Open3.capture3("node", "--print", js)
|
||||
if status.success?
|
||||
puts "[OK]"
|
||||
else
|
||||
|
@ -130,7 +128,7 @@ namespace :assets do
|
|||
end
|
||||
|
||||
task :lines do
|
||||
load File.expand_path("..", File.dirname(__FILE__)) + "/tools/line_statistics"
|
||||
load File.join(File.expand_path("..", __dir__), "/tools/line_statistics")
|
||||
files = FileList["lib/**/*.rb"]
|
||||
CodeTools::LineStatistics.new(files).print_loc
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
|
||||
version = File.read(File.expand_path("../RAILS_VERSION", __dir__)).strip
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
|
|
|
@ -36,10 +36,20 @@ Require `rails-ujs` into your application.js manifest.
|
|||
//= require rails-ujs
|
||||
```
|
||||
|
||||
Usage with yarn
|
||||
------------
|
||||
|
||||
When using with Webpacker gem or your preferred JavaScript bundler. Just add the following to your main JS file and compile.
|
||||
|
||||
```javascript
|
||||
import Rails from 'rails-ujs';
|
||||
Rails.start()
|
||||
```
|
||||
|
||||
How to run tests
|
||||
------------
|
||||
|
||||
Run `bundle exec rake ujs:server` first, and then run the web tests by visiting [[http://localhost:4567]] in your browser.
|
||||
Run `bundle exec rake ujs:server` first, and then run the web tests by visiting http://localhost:4567 in your browser.
|
||||
|
||||
## License
|
||||
rails-ujs is released under the [MIT License](MIT-LICENSE).
|
||||
|
|
|
@ -14,7 +14,7 @@ AcceptHeaders =
|
|||
Rails.ajax = (options) ->
|
||||
options = prepareOptions(options)
|
||||
xhr = createXHR options, ->
|
||||
response = processResponse(xhr.response, xhr.getResponseHeader('Content-Type'))
|
||||
response = processResponse(xhr.response ? xhr.responseText, xhr.getResponseHeader('Content-Type'))
|
||||
if xhr.status // 100 == 2
|
||||
options.success?(response, xhr.statusText, xhr)
|
||||
else
|
||||
|
|
|
@ -92,5 +92,5 @@ end
|
|||
require "active_support/core_ext/string/output_safety"
|
||||
|
||||
ActiveSupport.on_load(:i18n) do
|
||||
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
|
||||
I18n.load_path << File.expand_path("action_view/locale/en.yml", __dir__)
|
||||
end
|
||||
|
|
|
@ -122,9 +122,9 @@ module ActionView
|
|||
end
|
||||
|
||||
# Returns a link tag that browsers and feed readers can use to auto-detect
|
||||
# an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
|
||||
# <tt>:atom</tt>. Control the link options in url_for format using the
|
||||
# +url_options+. You can modify the LINK tag itself in +tag_options+.
|
||||
# an RSS, Atom, or JSON feed. The +type+ can be <tt>:rss</tt> (default),
|
||||
# <tt>:atom</tt>, or <tt>:json</tt>. Control the link options in url_for format
|
||||
# using the +url_options+. You can modify the LINK tag itself in +tag_options+.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
|
@ -138,6 +138,8 @@ module ActionView
|
|||
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
|
||||
# auto_discovery_link_tag(:atom)
|
||||
# # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
|
||||
# auto_discovery_link_tag(:json)
|
||||
# # => <link rel="alternate" type="application/json" title="JSON" href="http://www.currenthost.com/controller/action" />
|
||||
# auto_discovery_link_tag(:rss, {action: "feed"})
|
||||
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
|
||||
# auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
|
||||
|
@ -147,8 +149,8 @@ module ActionView
|
|||
# auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
|
||||
# # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed.rss" />
|
||||
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
|
||||
if !(type == :rss || type == :atom) && tag_options[:type].blank?
|
||||
raise ArgumentError.new("You should pass :type tag_option key explicitly, because you have passed #{type} type other than :rss or :atom.")
|
||||
if !(type == :rss || type == :atom || type == :json) && tag_options[:type].blank?
|
||||
raise ArgumentError.new("You should pass :type tag_option key explicitly, because you have passed #{type} type other than :rss, :atom, or :json.")
|
||||
end
|
||||
|
||||
tag(
|
||||
|
|
|
@ -8,10 +8,9 @@ module ActionView
|
|||
# fragments, and so on. This method takes a block that contains
|
||||
# the content you wish to cache.
|
||||
#
|
||||
# The best way to use this is by doing key-based cache expiration
|
||||
# on top of a cache store like Memcached that'll automatically
|
||||
# kick out old entries. For more on key-based expiration, see:
|
||||
# http://signalvnoise.com/posts/3113-how-key-based-cache-expiration-works
|
||||
# The best way to use this is by doing recyclable key-based cache expiration
|
||||
# on top of a cache store like Memcached or Redis that'll automatically
|
||||
# kick out old entries.
|
||||
#
|
||||
# When using this method, you list the cache dependency as the name of the cache, like so:
|
||||
#
|
||||
|
@ -23,10 +22,14 @@ module ActionView
|
|||
# This approach will assume that when a new topic is added, you'll touch
|
||||
# the project. The cache key generated from this call will be something like:
|
||||
#
|
||||
# views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9
|
||||
# ^class ^id ^updated_at ^template tree digest
|
||||
# views/template/action.html.erb:7a1156131a6928cb0026877f8b749ac9/projects/123
|
||||
# ^template path ^template tree digest ^class ^id
|
||||
#
|
||||
# The cache is thus automatically bumped whenever the project updated_at is touched.
|
||||
# This cache key is stable, but it's combined with a cache version derived from the project
|
||||
# record. When the project updated_at is touched, the #cache_version changes, even
|
||||
# if the key stays stable. This means that unlike a traditional key-based cache expiration
|
||||
# approach, you won't be generating cache trash, unused keys, simply because the dependent
|
||||
# record is updated.
|
||||
#
|
||||
# If your template cache depends on multiple sources (try to avoid this to keep things simple),
|
||||
# you can name all these dependencies as part of an array:
|
||||
|
@ -217,10 +220,15 @@ module ActionView
|
|||
|
||||
def fragment_name_with_digest(name, virtual_path)
|
||||
virtual_path ||= @virtual_path
|
||||
|
||||
if virtual_path
|
||||
name = controller.url_for(name).split("://").last if name.is_a?(Hash)
|
||||
digest = Digestor.digest name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
|
||||
[ name, digest ]
|
||||
|
||||
if digest = Digestor.digest(name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies).presence
|
||||
[ "#{virtual_path}:#{digest}", name ]
|
||||
else
|
||||
[ virtual_path, name ]
|
||||
end
|
||||
else
|
||||
name
|
||||
end
|
||||
|
|
|
@ -95,8 +95,8 @@ module ActionView
|
|||
scope: :'datetime.distance_in_words'
|
||||
}.merge!(options)
|
||||
|
||||
from_time = from_time.to_time if from_time.respond_to?(:to_time)
|
||||
to_time = to_time.to_time if to_time.respond_to?(:to_time)
|
||||
from_time = normalize_distance_of_time_argument_to_time(from_time)
|
||||
to_time = normalize_distance_of_time_argument_to_time(to_time)
|
||||
from_time, to_time = to_time, from_time if from_time > to_time
|
||||
distance_in_minutes = ((to_time - from_time) / 60.0).round
|
||||
distance_in_seconds = (to_time - from_time).round
|
||||
|
@ -130,22 +130,18 @@ module ActionView
|
|||
# 60 days up to 365 days
|
||||
when 86400...525600 then locale.t :x_months, count: (distance_in_minutes.to_f / 43200.0).round
|
||||
else
|
||||
if from_time.acts_like?(:time) && to_time.acts_like?(:time)
|
||||
fyear = from_time.year
|
||||
fyear += 1 if from_time.month >= 3
|
||||
tyear = to_time.year
|
||||
tyear -= 1 if to_time.month < 3
|
||||
leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count { |x| Date.leap?(x) }
|
||||
minute_offset_for_leap_year = leap_years * 1440
|
||||
# Discount the leap year days when calculating year distance.
|
||||
# e.g. if there are 20 leap year days between 2 dates having the same day
|
||||
# and month then the based on 365 days calculation
|
||||
# the distance in years will come out to over 80 years when in written
|
||||
# English it would read better as about 80 years.
|
||||
minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
|
||||
else
|
||||
minutes_with_offset = distance_in_minutes
|
||||
end
|
||||
from_year = from_time.year
|
||||
from_year += 1 if from_time.month >= 3
|
||||
to_year = to_time.year
|
||||
to_year -= 1 if to_time.month < 3
|
||||
leap_years = (from_year > to_year) ? 0 : (from_year..to_year).count { |x| Date.leap?(x) }
|
||||
minute_offset_for_leap_year = leap_years * 1440
|
||||
# Discount the leap year days when calculating year distance.
|
||||
# e.g. if there are 20 leap year days between 2 dates having the same day
|
||||
# and month then the based on 365 days calculation
|
||||
# the distance in years will come out to over 80 years when in written
|
||||
# English it would read better as about 80 years.
|
||||
minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
|
||||
remainder = (minutes_with_offset % MINUTES_IN_YEAR)
|
||||
distance_in_years = (minutes_with_offset.div MINUTES_IN_YEAR)
|
||||
if remainder < MINUTES_IN_QUARTER_YEAR
|
||||
|
@ -687,6 +683,18 @@ module ActionView
|
|||
|
||||
content_tag("time".freeze, content, options.reverse_merge(datetime: datetime), &block)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def normalize_distance_of_time_argument_to_time(value)
|
||||
if value.is_a?(Numeric)
|
||||
Time.at(value)
|
||||
elsif value.respond_to?(:to_time)
|
||||
value.to_time
|
||||
else
|
||||
raise ArgumentError, "#{value.inspect} can't be converted to a Time value"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DateTimeSelector #:nodoc:
|
||||
|
|
|
@ -1606,14 +1606,15 @@ module ActionView
|
|||
include ModelNaming
|
||||
|
||||
# The methods which wrap a form helper call.
|
||||
class_attribute :field_helpers
|
||||
self.field_helpers = [:fields_for, :fields, :label, :text_field, :password_field,
|
||||
:hidden_field, :file_field, :text_area, :check_box,
|
||||
:radio_button, :color_field, :search_field,
|
||||
:telephone_field, :phone_field, :date_field,
|
||||
:time_field, :datetime_field, :datetime_local_field,
|
||||
:month_field, :week_field, :url_field, :email_field,
|
||||
:number_field, :range_field]
|
||||
class_attribute :field_helpers, default: [
|
||||
:fields_for, :fields, :label, :text_field, :password_field,
|
||||
:hidden_field, :file_field, :text_area, :check_box,
|
||||
:radio_button, :color_field, :search_field,
|
||||
:telephone_field, :phone_field, :date_field,
|
||||
:time_field, :datetime_field, :datetime_local_field,
|
||||
:month_field, :week_field, :url_field, :email_field,
|
||||
:number_field, :range_field
|
||||
]
|
||||
|
||||
attr_accessor :object_name, :object, :options
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ module ActionView
|
|||
end
|
||||
|
||||
value = options.fetch(:selected) { value(object) }
|
||||
select = content_tag("select", add_options(option_tags, options, value), html_options)
|
||||
select = content_tag("select", add_options(option_tags, options, value), html_options.except!("skip_default_ids", "allow_method_names_outside_object"))
|
||||
|
||||
if html_options["multiple"] && options.fetch(:include_hidden, true)
|
||||
tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "") + select
|
||||
|
|
|
@ -33,7 +33,7 @@ module ActionView
|
|||
# [nil, []]
|
||||
# { nil => [] }
|
||||
def grouped_choices?
|
||||
!@choices.empty? && @choices.first.respond_to?(:last) && Array === @choices.first.last
|
||||
!@choices.blank? && @choices.first.respond_to?(:last) && Array === @choices.first.last
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -204,9 +204,9 @@ module ActionView
|
|||
include ActionView::Rendering
|
||||
|
||||
included do
|
||||
class_attribute :_layout, :_layout_conditions, instance_accessor: false
|
||||
self._layout = nil
|
||||
self._layout_conditions = {}
|
||||
class_attribute :_layout, instance_accessor: false
|
||||
class_attribute :_layout_conditions, instance_accessor: false, default: {}
|
||||
|
||||
_write_layout_method
|
||||
end
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ module ActionView
|
|||
end
|
||||
|
||||
def expanded_cache_key(key)
|
||||
key = @view.fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
|
||||
key = @view.combined_fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
|
||||
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
|
||||
end
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
module ActionView
|
||||
module Template::Handlers
|
||||
class Builder
|
||||
# Default format used by Builder.
|
||||
class_attribute :default_format
|
||||
self.default_format = :xml
|
||||
class_attribute :default_format, default: :xml
|
||||
|
||||
def call(template)
|
||||
require_engine
|
||||
|
@ -14,7 +12,6 @@ module ActionView
|
|||
end
|
||||
|
||||
private
|
||||
|
||||
def require_engine # :doc:
|
||||
@required ||= begin
|
||||
require "builder"
|
||||
|
|
|
@ -9,16 +9,13 @@ module ActionView
|
|||
|
||||
# Specify trim mode for the ERB compiler. Defaults to '-'.
|
||||
# See ERB documentation for suitable values.
|
||||
class_attribute :erb_trim_mode
|
||||
self.erb_trim_mode = "-"
|
||||
class_attribute :erb_trim_mode, default: "-"
|
||||
|
||||
# Default implementation used.
|
||||
class_attribute :erb_implementation
|
||||
self.erb_implementation = Erubi
|
||||
class_attribute :erb_implementation, default: Erubi
|
||||
|
||||
# Do not escape templates of these mime types.
|
||||
class_attribute :escape_whitelist
|
||||
self.escape_whitelist = ["text/plain"]
|
||||
class_attribute :escape_whitelist, default: ["text/plain"]
|
||||
|
||||
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
|
||||
|
||||
|
|
|
@ -3,9 +3,7 @@ module ActionView
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :_view_paths
|
||||
self._view_paths = ActionView::PathSet.new
|
||||
_view_paths.freeze
|
||||
class_attribute :_view_paths, default: ActionView::PathSet.new.freeze
|
||||
end
|
||||
|
||||
delegate :template_exists?, :any_templates?, :view_paths, :formats, :formats=,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
$:.unshift(File.dirname(__FILE__) + "/lib")
|
||||
$:.unshift(File.dirname(__FILE__) + "/fixtures/helpers")
|
||||
$:.unshift(File.dirname(__FILE__) + "/fixtures/alternate_helpers")
|
||||
$:.unshift File.expand_path("lib", __dir__)
|
||||
$:.unshift File.expand_path("fixtures/helpers", __dir__)
|
||||
$:.unshift File.expand_path("fixtures/alternate_helpers", __dir__)
|
||||
|
||||
ENV["TMPDIR"] = File.join(File.dirname(__FILE__), "tmp")
|
||||
ENV["TMPDIR"] = File.expand_path("tmp", __dir__)
|
||||
|
||||
require "active_support/core_ext/kernel/reporting"
|
||||
|
||||
|
@ -47,7 +47,7 @@ I18n.backend.store_translations "da", {}
|
|||
I18n.backend.store_translations "pt-BR", {}
|
||||
ORIGINAL_LOCALES = I18n.available_locales.map(&:to_s).sort
|
||||
|
||||
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), "fixtures")
|
||||
FIXTURE_LOAD_PATH = File.expand_path("fixtures", __dir__)
|
||||
|
||||
module RenderERBUtils
|
||||
def view
|
||||
|
@ -133,7 +133,7 @@ class BasicController
|
|||
def config
|
||||
@config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config).tap do |config|
|
||||
# VIEW TODO: View tests should not require a controller
|
||||
public_dir = File.expand_path("../fixtures/public", __FILE__)
|
||||
public_dir = File.expand_path("fixtures/public", __dir__)
|
||||
config.assets_dir = public_dir
|
||||
config.javascripts_dir = "#{public_dir}/javascripts"
|
||||
config.stylesheets_dir = "#{public_dir}/stylesheets"
|
||||
|
@ -196,7 +196,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def with_autoload_path(path)
|
||||
path = File.join(File.dirname(__FILE__), "fixtures", path)
|
||||
path = File.join(File.expand_path("fixtures", __dir__), path)
|
||||
if ActiveSupport::Dependencies.autoload_paths.include?(path)
|
||||
yield
|
||||
else
|
||||
|
|
|
@ -42,7 +42,7 @@ module AbstractController
|
|||
super
|
||||
end
|
||||
|
||||
append_view_path File.expand_path(File.join(File.dirname(__FILE__), "views"))
|
||||
append_view_path File.expand_path("views", __dir__)
|
||||
end
|
||||
|
||||
class Me2 < RenderingController
|
||||
|
@ -152,7 +152,7 @@ module AbstractController
|
|||
class OverridingLocalPrefixes < AbstractController::Base
|
||||
include AbstractController::Rendering
|
||||
include ActionView::Rendering
|
||||
append_view_path File.expand_path(File.join(File.dirname(__FILE__), "views"))
|
||||
append_view_path File.expand_path("views", __dir__)
|
||||
|
||||
def index
|
||||
render
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
require "abstract_unit"
|
||||
|
||||
ActionController::Base.helpers_path = File.expand_path("../../../fixtures/helpers", __FILE__)
|
||||
ActionController::Base.helpers_path = File.expand_path("../../fixtures/helpers", __dir__)
|
||||
|
||||
module AbstractController
|
||||
module Testing
|
||||
|
@ -51,7 +51,7 @@ module AbstractController
|
|||
class AbstractInvalidHelpers < AbstractHelpers
|
||||
include ActionController::Helpers
|
||||
|
||||
path = File.expand_path("../../../fixtures/helpers_missing", __FILE__)
|
||||
path = File.expand_path("../../fixtures/helpers_missing", __dir__)
|
||||
$:.unshift(path)
|
||||
self.helpers_path = path
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ require "abstract_unit"
|
|||
require "active_support/logger"
|
||||
|
||||
class CaptureController < ActionController::Base
|
||||
self.view_paths = [ File.dirname(__FILE__) + "/../../fixtures/actionpack" ]
|
||||
self.view_paths = [ File.expand_path("../../fixtures/actionpack", __dir__) ]
|
||||
|
||||
def self.controller_name; "test"; end
|
||||
def self.controller_path; "test"; end
|
||||
|
|
|
@ -5,7 +5,7 @@ require "active_support/core_ext/array/extract_options"
|
|||
# method has access to the view_paths array when looking for a layout to automatically assign.
|
||||
old_load_paths = ActionController::Base.view_paths
|
||||
|
||||
ActionController::Base.view_paths = [ File.dirname(__FILE__) + "/../../fixtures/actionpack/layout_tests/" ]
|
||||
ActionController::Base.view_paths = [ File.expand_path("../../fixtures/actionpack/layout_tests", __dir__) ]
|
||||
|
||||
class LayoutTest < ActionController::Base
|
||||
def self.controller_path; "views" end
|
||||
|
@ -96,7 +96,7 @@ class StreamingLayoutController < LayoutTest
|
|||
end
|
||||
|
||||
class AbsolutePathLayoutController < LayoutTest
|
||||
layout File.expand_path(File.expand_path(__FILE__) + "/../../../fixtures/actionpack/layout_tests/layouts/layout_test")
|
||||
layout File.expand_path("../../fixtures/actionpack/layout_tests/layouts/layout_test", __dir__)
|
||||
end
|
||||
|
||||
class HasOwnLayoutController < LayoutTest
|
||||
|
@ -117,7 +117,7 @@ end
|
|||
|
||||
class PrependsViewPathController < LayoutTest
|
||||
def hello
|
||||
prepend_view_path File.dirname(__FILE__) + "/../../fixtures/actionpack/layout_tests/alt/"
|
||||
prepend_view_path File.expand_path("../../fixtures/actionpack/layout_tests/alt", __dir__)
|
||||
render layout: "alt"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,7 +56,7 @@ class TestController < ApplicationController
|
|||
end
|
||||
|
||||
def hello_world_file
|
||||
render file: File.expand_path("../../../fixtures/actionpack/hello", __FILE__), formats: [:html]
|
||||
render file: File.expand_path("../../fixtures/actionpack/hello", __dir__), formats: [:html]
|
||||
end
|
||||
|
||||
# :ported:
|
||||
|
@ -125,7 +125,7 @@ class TestController < ApplicationController
|
|||
# :ported:
|
||||
def render_file_with_instance_variables
|
||||
@secret = "in the sauce"
|
||||
path = File.join(File.dirname(__FILE__), "../../fixtures/test/render_file_with_ivar")
|
||||
path = File.expand_path("../../fixtures/test/render_file_with_ivar", __dir__)
|
||||
render file: path
|
||||
end
|
||||
|
||||
|
@ -142,21 +142,21 @@ class TestController < ApplicationController
|
|||
|
||||
def render_file_using_pathname
|
||||
@secret = "in the sauce"
|
||||
render file: Pathname.new(File.dirname(__FILE__)).join("..", "..", "fixtures", "test", "dot.directory", "render_file_with_ivar")
|
||||
render file: Pathname.new(__dir__).join("..", "..", "fixtures", "test", "dot.directory", "render_file_with_ivar")
|
||||
end
|
||||
|
||||
def render_file_from_template
|
||||
@secret = "in the sauce"
|
||||
@path = File.expand_path(File.join(File.dirname(__FILE__), "../../fixtures/test/render_file_with_ivar"))
|
||||
@path = File.expand_path("../../fixtures/test/render_file_with_ivar", __dir__)
|
||||
end
|
||||
|
||||
def render_file_with_locals
|
||||
path = File.join(File.dirname(__FILE__), "../../fixtures/test/render_file_with_locals")
|
||||
path = File.expand_path("../../fixtures/test/render_file_with_locals", __dir__)
|
||||
render file: path, locals: { secret: "in the sauce" }
|
||||
end
|
||||
|
||||
def render_file_as_string_with_locals
|
||||
path = File.expand_path(File.join(File.dirname(__FILE__), "../../fixtures/test/render_file_with_locals"))
|
||||
path = File.expand_path("../../fixtures/test/render_file_with_locals", __dir__)
|
||||
render file: path, locals: { secret: "in the sauce" }
|
||||
end
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue