diff --git a/.bundle/config b/.bundle/config
new file mode 100644
index 0000000..2369228
--- /dev/null
+++ b/.bundle/config
@@ -0,0 +1,2 @@
+---
+BUNDLE_PATH: "vendor/bundle"
diff --git a/Gemfile.lock b/Gemfile.lock
index 78ab10e..c179281 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,23 +1,25 @@
GEM
remote: https://rubygems.org/
specs:
- CFPropertyList (3.0.2)
- activesupport (5.2.4.4)
+ CFPropertyList (3.0.5)
+ rexml
+ activesupport (6.1.4.6)
concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 0.7, < 2)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
+ i18n (>= 1.6, < 2)
+ minitest (>= 5.1)
+ tzinfo (~> 2.0)
+ zeitwerk (~> 2.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
- claide (1.0.3)
- cocoapods (1.10.0)
- addressable (~> 2.6)
+ claide (1.1.0)
+ cocoapods (1.11.2)
+ addressable (~> 2.8)
claide (>= 1.0.2, < 2.0)
- cocoapods-core (= 1.10.0)
+ cocoapods-core (= 1.11.2)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
@@ -28,60 +30,62 @@ GEM
escape (~> 0.0.4)
fourflusher (>= 2.3.0, < 3.0)
gh_inspector (~> 1.0)
- molinillo (~> 0.6.6)
+ molinillo (~> 0.8.0)
nap (~> 1.0)
- ruby-macho (~> 1.4)
- xcodeproj (>= 1.19.0, < 2.0)
- cocoapods-core (1.10.0)
- activesupport (> 5.0, < 6)
- addressable (~> 2.6)
+ ruby-macho (>= 1.0, < 3.0)
+ xcodeproj (>= 1.21.0, < 2.0)
+ cocoapods-core (1.11.2)
+ activesupport (>= 5.0, < 7)
+ addressable (~> 2.8)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
netrc (~> 0.11)
- public_suffix
+ public_suffix (~> 4.0)
typhoeus (~> 1.0)
- cocoapods-deintegrate (1.0.4)
- cocoapods-downloader (1.4.0)
+ cocoapods-deintegrate (1.0.5)
+ cocoapods-downloader (1.5.1)
cocoapods-plugins (1.0.0)
nap
- cocoapods-search (1.0.0)
- cocoapods-trunk (1.5.0)
+ cocoapods-search (1.0.1)
+ cocoapods-trunk (1.6.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored2 (3.1.2)
- concurrent-ruby (1.1.7)
+ concurrent-ruby (1.1.9)
escape (0.0.4)
- ethon (0.12.0)
- ffi (>= 1.3.0)
- ffi (1.13.1)
+ ethon (0.15.0)
+ ffi (>= 1.15.0)
+ ffi (1.15.5)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
httpclient (2.8.3)
- i18n (1.8.5)
+ i18n (1.10.0)
concurrent-ruby (~> 1.0)
- json (2.3.1)
- minitest (5.14.2)
- molinillo (0.6.6)
+ json (2.6.1)
+ minitest (5.15.0)
+ molinillo (0.8.0)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
public_suffix (4.0.6)
- ruby-macho (1.4.0)
- thread_safe (0.3.6)
+ rexml (3.2.5)
+ ruby-macho (2.5.1)
typhoeus (1.4.0)
ethon (>= 0.9.0)
- tzinfo (1.2.8)
- thread_safe (~> 0.1)
- xcodeproj (1.19.0)
+ tzinfo (2.0.4)
+ concurrent-ruby (~> 1.0)
+ xcodeproj (1.21.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
+ rexml (~> 3.2.4)
+ zeitwerk (2.5.4)
PLATFORMS
ruby
@@ -90,4 +94,4 @@ DEPENDENCIES
cocoapods
BUNDLED WITH
- 2.1.4
+ 2.3.6
diff --git a/Zephyr.podspec b/Zephyr.podspec
index 9fc6087..9ebd3bd 100755
--- a/Zephyr.podspec
+++ b/Zephyr.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
# Version
- s.version = "3.6.2"
+ s.version = "3.6.3"
s.swift_version = "5.3"
# Meta
diff --git a/vendor/bundle/ruby/2.6.0/bin/fuzzy_match b/vendor/bundle/ruby/2.6.0/bin/fuzzy_match
new file mode 100755
index 0000000..c340ac1
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/bin/fuzzy_match
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by RubyGems.
+#
+# The application 'fuzzy_match' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+
+version = ">= 0.a"
+
+str = ARGV.first
+if str
+ str = str.b[/\A_(.*)_\z/, 1]
+ if str and Gem::Version.correct?(str)
+ version = str
+ ARGV.shift
+ end
+end
+
+if Gem.respond_to?(:activate_bin_path)
+load Gem.activate_bin_path('fuzzy_match', 'fuzzy_match', version)
+else
+gem "fuzzy_match", version
+load Gem.bin_path("fuzzy_match", "fuzzy_match", version)
+end
diff --git a/vendor/bundle/ruby/2.6.0/bin/httpclient b/vendor/bundle/ruby/2.6.0/bin/httpclient
new file mode 100755
index 0000000..a37f4dd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/bin/httpclient
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by RubyGems.
+#
+# The application 'httpclient' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+
+version = ">= 0.a"
+
+str = ARGV.first
+if str
+ str = str.b[/\A_(.*)_\z/, 1]
+ if str and Gem::Version.correct?(str)
+ version = str
+ ARGV.shift
+ end
+end
+
+if Gem.respond_to?(:activate_bin_path)
+load Gem.activate_bin_path('httpclient', 'httpclient', version)
+else
+gem "httpclient", version
+load Gem.bin_path("httpclient", "httpclient", version)
+end
diff --git a/vendor/bundle/ruby/2.6.0/cache/CFPropertyList-3.0.2.gem b/vendor/bundle/ruby/2.6.0/cache/CFPropertyList-3.0.2.gem
new file mode 100644
index 0000000..01cb910
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/CFPropertyList-3.0.2.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/activesupport-5.2.4.4.gem b/vendor/bundle/ruby/2.6.0/cache/activesupport-5.2.4.4.gem
new file mode 100644
index 0000000..3df1726
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/activesupport-5.2.4.4.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/addressable-2.8.0.gem b/vendor/bundle/ruby/2.6.0/cache/addressable-2.8.0.gem
new file mode 100644
index 0000000..1e41e1c
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/addressable-2.8.0.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/algoliasearch-1.27.5.gem b/vendor/bundle/ruby/2.6.0/cache/algoliasearch-1.27.5.gem
new file mode 100644
index 0000000..4e068e8
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/algoliasearch-1.27.5.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/atomos-0.1.3.gem b/vendor/bundle/ruby/2.6.0/cache/atomos-0.1.3.gem
new file mode 100644
index 0000000..c12e2f0
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/atomos-0.1.3.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/claide-1.0.3.gem b/vendor/bundle/ruby/2.6.0/cache/claide-1.0.3.gem
new file mode 100644
index 0000000..16aeff7
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/claide-1.0.3.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/concurrent-ruby-1.1.7.gem b/vendor/bundle/ruby/2.6.0/cache/concurrent-ruby-1.1.7.gem
new file mode 100644
index 0000000..ae9b370
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/concurrent-ruby-1.1.7.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/ffi-1.13.1.gem b/vendor/bundle/ruby/2.6.0/cache/ffi-1.13.1.gem
new file mode 100644
index 0000000..e59187e
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/ffi-1.13.1.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/fuzzy_match-2.0.4.gem b/vendor/bundle/ruby/2.6.0/cache/fuzzy_match-2.0.4.gem
new file mode 100644
index 0000000..bb9e714
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/fuzzy_match-2.0.4.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/httpclient-2.8.3.gem b/vendor/bundle/ruby/2.6.0/cache/httpclient-2.8.3.gem
new file mode 100644
index 0000000..9c19ad4
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/httpclient-2.8.3.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/i18n-1.8.5.gem b/vendor/bundle/ruby/2.6.0/cache/i18n-1.8.5.gem
new file mode 100644
index 0000000..f5c89ea
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/i18n-1.8.5.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/json-2.3.1.gem b/vendor/bundle/ruby/2.6.0/cache/json-2.3.1.gem
new file mode 100644
index 0000000..1f89633
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/json-2.3.1.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/json-2.6.1.gem b/vendor/bundle/ruby/2.6.0/cache/json-2.6.1.gem
new file mode 100644
index 0000000..0e3430c
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/json-2.6.1.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/minitest-5.14.2.gem b/vendor/bundle/ruby/2.6.0/cache/minitest-5.14.2.gem
new file mode 100644
index 0000000..66232f7
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/minitest-5.14.2.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/nap-1.1.0.gem b/vendor/bundle/ruby/2.6.0/cache/nap-1.1.0.gem
new file mode 100644
index 0000000..1f6dd74
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/nap-1.1.0.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/netrc-0.11.0.gem b/vendor/bundle/ruby/2.6.0/cache/netrc-0.11.0.gem
new file mode 100644
index 0000000..78226f3
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/netrc-0.11.0.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/public_suffix-4.0.6.gem b/vendor/bundle/ruby/2.6.0/cache/public_suffix-4.0.6.gem
new file mode 100644
index 0000000..6f183f4
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/public_suffix-4.0.6.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/thread_safe-0.3.6.gem b/vendor/bundle/ruby/2.6.0/cache/thread_safe-0.3.6.gem
new file mode 100644
index 0000000..7ee950f
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/thread_safe-0.3.6.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/cache/tzinfo-1.2.8.gem b/vendor/bundle/ruby/2.6.0/cache/tzinfo-1.2.8.gem
new file mode 100644
index 0000000..f24bf8e
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/cache/tzinfo-1.2.8.gem differ
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/ffi-1.13.1/gem_make 2.out b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/ffi-1.13.1/gem_make 2.out
new file mode 100644
index 0000000..73913df
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/ffi-1.13.1/gem_make 2.out
@@ -0,0 +1,81 @@
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c
+/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby -I /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0 -r ./siteconf20211106-25171-18c367c.rb extconf.rb
+checking for ffi_prep_closure_loc() in -lffi... no
+checking for ffi_prep_closure_loc() in -llibffi... no
+checking for ffi_prep_closure_loc() in -llibffi-8... no
+creating extconf.h
+creating Makefile
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c
+make "DESTDIR=" clean
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c
+make "DESTDIR="
+Configuring libffi
+/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/configure: line 18632: readelf: command not found
+clang: error: unsupported option '-print-multi-os-directory'
+clang: error: no input files
+cd "/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi-universal-darwin21" && /Applications/Xcode.app/Contents/Developer/usr/bin/make
+/Applications/Xcode.app/Contents/Developer/usr/bin/make all-recursive
+Making all in include
+make[3]: Nothing to be done for `all'.
+Making all in testsuite
+make[3]: Nothing to be done for `all'.
+Making all in man
+make[3]: Nothing to be done for `all'.
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/prep_cif.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/prep_cif.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/prep_cif.c -fno-common -DPIC -o src/.libs/prep_cif.o
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/types.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/types.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/types.c -fno-common -DPIC -o src/.libs/types.o
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/raw_api.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/raw_api.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/raw_api.c -fno-common -DPIC -o src/.libs/raw_api.o
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/java_raw_api.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/java_raw_api.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/java_raw_api.c -fno-common -DPIC -o src/.libs/java_raw_api.o
+/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/java_raw_api.c:328:46: warning: 'ffi_java_raw_size' is deprecated [-Wdeprecated-declarations]
+ ffi_java_raw *raw = (ffi_java_raw*)alloca (ffi_java_raw_size (cif));
+ ^
+include/ffi.h:299:56: note: 'ffi_java_raw_size' has been explicitly marked deprecated here
+size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated));
+ ^
+/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/java_raw_api.c:331:3: warning: 'ffi_java_ptrarray_to_raw' is deprecated [-Wdeprecated-declarations]
+ ffi_java_ptrarray_to_raw (cif, avalue, raw);
+ ^
+include/ffi.h:295:93: note: 'ffi_java_ptrarray_to_raw' has been explicitly marked deprecated here
+void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw) __attribute__((deprecated));
+ ^
+2 warnings generated.
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/closures.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/closures.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/closures.c -fno-common -DPIC -o src/.libs/closures.o
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/arm/ffi.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/arm/ffi.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/arm/ffi.c -fno-common -DPIC -o src/arm/.libs/ffi.o
+/bin/sh ./libtool --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -c -o src/arm/sysv.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/arm/sysv.S
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/arm/sysv.S -fno-common -DPIC -o src/arm/.libs/sysv.o
+/bin/sh ./libtool --tag=CC --mode=link xcrun clang -Wall -fexceptions -L/usr/local/opt/libffi/lib -o libffi_convenience.la src/prep_cif.lo src/types.lo src/raw_api.lo src/java_raw_api.lo src/closures.lo src/arm/ffi.lo src/arm/sysv.lo
+libtool: link: ar cru .libs/libffi_convenience.a src/.libs/prep_cif.o src/.libs/types.o src/.libs/raw_api.o src/.libs/java_raw_api.o src/.libs/closures.o src/arm/.libs/ffi.o src/arm/.libs/sysv.o
+libtool: link: ranlib .libs/libffi_convenience.a
+libtool: link: ( cd ".libs" && rm -f "libffi_convenience.la" && ln -s "../libffi_convenience.la" "libffi_convenience.la" )
+/bin/sh ./libtool --tag=CC --mode=link xcrun clang -Wall -fexceptions -no-undefined -version-info `grep -v '^#' /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/libtool-version` '-L/usr/local/opt/libffi/lib' -L/usr/local/opt/libffi/lib -o libffi.la -rpath /usr/local/lib src/prep_cif.lo src/types.lo src/raw_api.lo src/java_raw_api.lo src/closures.lo src/arm/ffi.lo src/arm/sysv.lo
+libtool: link: xcrun clang -dynamiclib -o .libs/libffi.8.dylib src/.libs/prep_cif.o src/.libs/types.o src/.libs/raw_api.o src/.libs/java_raw_api.o src/.libs/closures.o src/arm/.libs/ffi.o src/arm/.libs/sysv.o -L/usr/local/opt/libffi/lib -install_name /usr/local/lib/libffi.8.dylib -compatibility_version 10 -current_version 10.0 -Wl,-single_module
+ld: warning: directory not found for option '-L/usr/local/opt/libffi/lib'
+Undefined symbols for architecture arm64:
+ "_ffi_call", referenced from:
+ _ffi_raw_call in raw_api.o
+ _ffi_java_raw_call in java_raw_api.o
+ "_ffi_closure_trampoline_table_page", referenced from:
+ _ffi_trampoline_table_alloc in closures.o
+ "_ffi_prep_cif_machdep", referenced from:
+ _ffi_prep_cif_core in prep_cif.o
+ "_ffi_prep_cif_machdep_var", referenced from:
+ _ffi_prep_cif_core in prep_cif.o
+ "_ffi_prep_closure_loc", referenced from:
+ _ffi_prep_closure in prep_cif.o
+ _ffi_prep_raw_closure_loc in raw_api.o
+ _ffi_prep_java_raw_closure_loc in java_raw_api.o
+ld: symbol(s) not found for architecture arm64
+clang: error: linker command failed with exit code 1 (use -v to see invocation)
+make[3]: *** [libffi.la] Error 1
+make[2]: *** [all-recursive] Error 1
+make[1]: *** [all] Error 2
+make: *** ["/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi-universal-darwin21"/.libs/libffi_convenience.a] Error 2
+
+make failed, exit code 2
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/ffi-1.13.1/gem_make.out b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/ffi-1.13.1/gem_make.out
new file mode 100644
index 0000000..2fbb4e3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/ffi-1.13.1/gem_make.out
@@ -0,0 +1,81 @@
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c
+/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby -I /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0 -r ./siteconf20211106-32129-13eyyi4.rb extconf.rb
+checking for ffi_prep_closure_loc() in -lffi... no
+checking for ffi_prep_closure_loc() in -llibffi... no
+checking for ffi_prep_closure_loc() in -llibffi-8... no
+creating extconf.h
+creating Makefile
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c
+make "DESTDIR=" clean
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c
+make "DESTDIR="
+Configuring libffi
+/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/configure: line 18632: readelf: command not found
+clang: error: unsupported option '-print-multi-os-directory'
+clang: error: no input files
+cd "/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi-universal-darwin21" && /Applications/Xcode.app/Contents/Developer/usr/bin/make
+/Applications/Xcode.app/Contents/Developer/usr/bin/make all-recursive
+Making all in include
+make[3]: Nothing to be done for `all'.
+Making all in testsuite
+make[3]: Nothing to be done for `all'.
+Making all in man
+make[3]: Nothing to be done for `all'.
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/prep_cif.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/prep_cif.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/prep_cif.c -fno-common -DPIC -o src/.libs/prep_cif.o
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/types.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/types.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/types.c -fno-common -DPIC -o src/.libs/types.o
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/raw_api.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/raw_api.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/raw_api.c -fno-common -DPIC -o src/.libs/raw_api.o
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/java_raw_api.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/java_raw_api.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/java_raw_api.c -fno-common -DPIC -o src/.libs/java_raw_api.o
+/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/java_raw_api.c:328:46: warning: 'ffi_java_raw_size' is deprecated [-Wdeprecated-declarations]
+ ffi_java_raw *raw = (ffi_java_raw*)alloca (ffi_java_raw_size (cif));
+ ^
+include/ffi.h:299:56: note: 'ffi_java_raw_size' has been explicitly marked deprecated here
+size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated));
+ ^
+/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/java_raw_api.c:331:3: warning: 'ffi_java_ptrarray_to_raw' is deprecated [-Wdeprecated-declarations]
+ ffi_java_ptrarray_to_raw (cif, avalue, raw);
+ ^
+include/ffi.h:295:93: note: 'ffi_java_ptrarray_to_raw' has been explicitly marked deprecated here
+void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw) __attribute__((deprecated));
+ ^
+2 warnings generated.
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/closures.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/closures.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/closures.c -fno-common -DPIC -o src/.libs/closures.o
+/bin/sh ./libtool --tag=CC --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c -o src/arm/ffi.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/arm/ffi.c
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -Wall -fexceptions -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/arm/ffi.c -fno-common -DPIC -o src/arm/.libs/ffi.o
+/bin/sh ./libtool --mode=compile xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -c -o src/arm/sysv.lo /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/arm/sysv.S
+libtool: compile: xcrun clang -DHAVE_CONFIG_H -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -DRUBY_EXTCONF_H=\"extconf.h\" -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -I. -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/include -Iinclude -I/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src -c /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/src/arm/sysv.S -fno-common -DPIC -o src/arm/.libs/sysv.o
+/bin/sh ./libtool --tag=CC --mode=link xcrun clang -Wall -fexceptions -L/usr/local/opt/libffi/lib -o libffi_convenience.la src/prep_cif.lo src/types.lo src/raw_api.lo src/java_raw_api.lo src/closures.lo src/arm/ffi.lo src/arm/sysv.lo
+libtool: link: ar cru .libs/libffi_convenience.a src/.libs/prep_cif.o src/.libs/types.o src/.libs/raw_api.o src/.libs/java_raw_api.o src/.libs/closures.o src/arm/.libs/ffi.o src/arm/.libs/sysv.o
+libtool: link: ranlib .libs/libffi_convenience.a
+libtool: link: ( cd ".libs" && rm -f "libffi_convenience.la" && ln -s "../libffi_convenience.la" "libffi_convenience.la" )
+/bin/sh ./libtool --tag=CC --mode=link xcrun clang -Wall -fexceptions -no-undefined -version-info `grep -v '^#' /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi/libtool-version` '-L/usr/local/opt/libffi/lib' -L/usr/local/opt/libffi/lib -o libffi.la -rpath /usr/local/lib src/prep_cif.lo src/types.lo src/raw_api.lo src/java_raw_api.lo src/closures.lo src/arm/ffi.lo src/arm/sysv.lo
+libtool: link: xcrun clang -dynamiclib -o .libs/libffi.8.dylib src/.libs/prep_cif.o src/.libs/types.o src/.libs/raw_api.o src/.libs/java_raw_api.o src/.libs/closures.o src/arm/.libs/ffi.o src/arm/.libs/sysv.o -L/usr/local/opt/libffi/lib -install_name /usr/local/lib/libffi.8.dylib -compatibility_version 10 -current_version 10.0 -Wl,-single_module
+ld: warning: directory not found for option '-L/usr/local/opt/libffi/lib'
+Undefined symbols for architecture arm64:
+ "_ffi_call", referenced from:
+ _ffi_raw_call in raw_api.o
+ _ffi_java_raw_call in java_raw_api.o
+ "_ffi_closure_trampoline_table_page", referenced from:
+ _ffi_trampoline_table_alloc in closures.o
+ "_ffi_prep_cif_machdep", referenced from:
+ _ffi_prep_cif_core in prep_cif.o
+ "_ffi_prep_cif_machdep_var", referenced from:
+ _ffi_prep_cif_core in prep_cif.o
+ "_ffi_prep_closure_loc", referenced from:
+ _ffi_prep_closure in prep_cif.o
+ _ffi_prep_raw_closure_loc in raw_api.o
+ _ffi_prep_java_raw_closure_loc in java_raw_api.o
+ld: symbol(s) not found for architecture arm64
+clang: error: linker command failed with exit code 1 (use -v to see invocation)
+make[3]: *** [libffi.la] Error 1
+make[2]: *** [all-recursive] Error 1
+make[1]: *** [all] Error 2
+make: *** ["/Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/ext/ffi_c/libffi-universal-darwin21"/.libs/libffi_convenience.a] Error 2
+
+make failed, exit code 2
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/ffi-1.13.1/mkmf.log b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/ffi-1.13.1/mkmf.log
new file mode 100644
index 0000000..1ede79c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/ffi-1.13.1/mkmf.log
@@ -0,0 +1,212 @@
+"pkg-config --exists libffi"
+| pkg-config --libs libffi
+=> "-lffi\n"
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lruby.2.6 "
+ld: warning: directory not found for option '-L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib'
+checked program was:
+/* begin */
+1: #include "ruby.h"
+2:
+3: int main(int argc, char **argv)
+4: {
+5: return 0;
+6: }
+/* end */
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lruby.2.6 -lffi "
+ld: warning: directory not found for option '-L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib'
+checked program was:
+/* begin */
+1: #include "ruby.h"
+2:
+3: int main(int argc, char **argv)
+4: {
+5: return 0;
+6: }
+/* end */
+
+| pkg-config --cflags-only-I libffi
+=> "-I/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/ffi\n"
+| pkg-config --cflags-only-other libffi
+=> "\n"
+| pkg-config --libs-only-l libffi
+=> "-lffi\n"
+package configuration for libffi
+cflags:
+ldflags:
+libs: -lffi
+
+have_library: checking for ffi_prep_closure_loc() in -lffi... -------------------- no
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -I/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/ffi -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lffi -lruby.2.6 -lffi -lffi "
+conftest.c:3:10: fatal error: 'ffi.h' file not found
+#include
+ ^~~~~~~
+1 error generated.
+checked program was:
+/* begin */
+ 1: #include "ruby.h"
+ 2:
+ 3: #include
+ 4:
+ 5: /*top*/
+ 6: extern int t(void);
+ 7: int main(int argc, char **argv)
+ 8: {
+ 9: if (argc > 1000000) {
+10: int (* volatile tp)(void)=(int (*)(void))&t;
+11: printf("%d", (*tp)());
+12: }
+13:
+14: return 0;
+15: }
+16: int t(void) { void ((*volatile p)()); p = (void ((*)()))ffi_prep_closure_loc; return !p; }
+/* end */
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -I/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/ffi -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lffi -lruby.2.6 -lffi -lffi "
+conftest.c:3:10: fatal error: 'ffi.h' file not found
+#include
+ ^~~~~~~
+1 error generated.
+checked program was:
+/* begin */
+ 1: #include "ruby.h"
+ 2:
+ 3: #include
+ 4:
+ 5: /*top*/
+ 6: extern int t(void);
+ 7: int main(int argc, char **argv)
+ 8: {
+ 9: if (argc > 1000000) {
+10: int (* volatile tp)(void)=(int (*)(void))&t;
+11: printf("%d", (*tp)());
+12: }
+13:
+14: return 0;
+15: }
+16: extern void ffi_prep_closure_loc();
+17: int t(void) { ffi_prep_closure_loc(); return 0; }
+/* end */
+
+--------------------
+
+have_library: checking for ffi_prep_closure_loc() in -llibffi... -------------------- no
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -I/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/ffi -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lffi -lruby.2.6 -llibffi -lffi "
+conftest.c:3:10: fatal error: 'ffi.h' file not found
+#include
+ ^~~~~~~
+1 error generated.
+checked program was:
+/* begin */
+ 1: #include "ruby.h"
+ 2:
+ 3: #include
+ 4:
+ 5: /*top*/
+ 6: extern int t(void);
+ 7: int main(int argc, char **argv)
+ 8: {
+ 9: if (argc > 1000000) {
+10: int (* volatile tp)(void)=(int (*)(void))&t;
+11: printf("%d", (*tp)());
+12: }
+13:
+14: return 0;
+15: }
+16: int t(void) { void ((*volatile p)()); p = (void ((*)()))ffi_prep_closure_loc; return !p; }
+/* end */
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -I/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/ffi -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lffi -lruby.2.6 -llibffi -lffi "
+conftest.c:3:10: fatal error: 'ffi.h' file not found
+#include
+ ^~~~~~~
+1 error generated.
+checked program was:
+/* begin */
+ 1: #include "ruby.h"
+ 2:
+ 3: #include
+ 4:
+ 5: /*top*/
+ 6: extern int t(void);
+ 7: int main(int argc, char **argv)
+ 8: {
+ 9: if (argc > 1000000) {
+10: int (* volatile tp)(void)=(int (*)(void))&t;
+11: printf("%d", (*tp)());
+12: }
+13:
+14: return 0;
+15: }
+16: extern void ffi_prep_closure_loc();
+17: int t(void) { ffi_prep_closure_loc(); return 0; }
+/* end */
+
+--------------------
+
+have_library: checking for ffi_prep_closure_loc() in -llibffi-8... -------------------- no
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -I/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/ffi -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lffi -lruby.2.6 -llibffi-8 -lffi "
+conftest.c:3:10: fatal error: 'ffi.h' file not found
+#include
+ ^~~~~~~
+1 error generated.
+checked program was:
+/* begin */
+ 1: #include "ruby.h"
+ 2:
+ 3: #include
+ 4:
+ 5: /*top*/
+ 6: extern int t(void);
+ 7: int main(int argc, char **argv)
+ 8: {
+ 9: if (argc > 1000000) {
+10: int (* volatile tp)(void)=(int (*)(void))&t;
+11: printf("%d", (*tp)());
+12: }
+13:
+14: return 0;
+15: }
+16: int t(void) { void ((*volatile p)()); p = (void ((*)()))ffi_prep_closure_loc; return !p; }
+/* end */
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -I/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/usr/include/ffi -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lffi -lruby.2.6 -llibffi-8 -lffi "
+conftest.c:3:10: fatal error: 'ffi.h' file not found
+#include
+ ^~~~~~~
+1 error generated.
+checked program was:
+/* begin */
+ 1: #include "ruby.h"
+ 2:
+ 3: #include
+ 4:
+ 5: /*top*/
+ 6: extern int t(void);
+ 7: int main(int argc, char **argv)
+ 8: {
+ 9: if (argc > 1000000) {
+10: int (* volatile tp)(void)=(int (*)(void))&t;
+11: printf("%d", (*tp)());
+12: }
+13:
+14: return 0;
+15: }
+16: extern void ffi_prep_closure_loc();
+17: int t(void) { ffi_prep_closure_loc(); return 0; }
+/* end */
+
+--------------------
+
+extconf.h is:
+/* begin */
+1: #ifndef EXTCONF_H
+2: #define EXTCONF_H
+3: #define HAVE_FFI_PREP_CIF_VAR 1
+4: #define USE_INTERNAL_LIBFFI 1
+5: #endif
+/* end */
+
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/gem.build_complete b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/gem.build_complete
new file mode 100644
index 0000000..e69de29
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/gem_make 2.out b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/gem_make 2.out
new file mode 100644
index 0000000..6b7d344
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/gem_make 2.out
@@ -0,0 +1,21 @@
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.3.1/ext/json/ext/generator
+/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby -I /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0 -r ./siteconf20211106-75622-17rtlo1.rb extconf.rb
+creating Makefile
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.3.1/ext/json/ext/generator
+make "DESTDIR=" clean
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.3.1/ext/json/ext/generator
+make "DESTDIR="
+compiling generator.c
+In file included from generator.c:1:
+In file included from ./../fbuffer/fbuffer.h:5:
+In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby.h:33:
+/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: fatal error: 'ruby/config.h' file not found
+#include "ruby/config.h"
+ ^~~~~~~~~~~~~~~
+/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: note: did not find header 'config.h' in framework 'ruby' (loaded from '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks')
+1 error generated.
+make: *** [generator.o] Error 1
+
+make failed, exit code 2
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/gem_make.out b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/gem_make.out
new file mode 100644
index 0000000..4289192
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/gem_make.out
@@ -0,0 +1,13 @@
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.3.1/ext/json
+/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby -I /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0 -r ./siteconf20211106-25171-koumu6.rb extconf.rb
+creating Makefile
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.3.1/ext/json
+make "DESTDIR=" clean
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.3.1/ext/json
+make "DESTDIR="
+make: Nothing to be done for `all'.
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.3.1/ext/json
+make "DESTDIR=" install
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/json/ext/generator.bundle b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/json/ext/generator.bundle
new file mode 100755
index 0000000..09c8b98
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/json/ext/generator.bundle differ
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/json/ext/parser.bundle b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/json/ext/parser.bundle
new file mode 100755
index 0000000..2cda116
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/json/ext/parser.bundle differ
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/mkmf.log b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/mkmf.log
new file mode 100644
index 0000000..537ffb2
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.3.1/mkmf.log
@@ -0,0 +1,64 @@
+have_func: checking for rb_enc_raise() in ruby.h... -------------------- yes
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lruby.2.6 "
+ld: warning: directory not found for option '-L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib'
+checked program was:
+/* begin */
+1: #include "ruby.h"
+2:
+3: int main(int argc, char **argv)
+4: {
+5: return 0;
+6: }
+/* end */
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lruby.2.6 "
+conftest.c:16:57: error: use of undeclared identifier 'rb_enc_raise'
+int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_enc_raise; return !p; }
+ ^
+1 error generated.
+checked program was:
+/* begin */
+ 1: #include "ruby.h"
+ 2:
+ 3: #include
+ 4:
+ 5: /*top*/
+ 6: extern int t(void);
+ 7: int main(int argc, char **argv)
+ 8: {
+ 9: if (argc > 1000000) {
+10: int (* volatile tp)(void)=(int (*)(void))&t;
+11: printf("%d", (*tp)());
+12: }
+13:
+14: return 0;
+15: }
+16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_enc_raise; return !p; }
+/* end */
+
+"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin21 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DUSE_FFI_CLOSURE_ALLOC conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib -lruby.2.6 "
+ld: warning: directory not found for option '-L/System/Volumes/Data/SWE/macOS/BuildRoots/b8ff8433dc/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.Internal.sdk/usr/local/lib'
+checked program was:
+/* begin */
+ 1: #include "ruby.h"
+ 2:
+ 3: #include
+ 4:
+ 5: /*top*/
+ 6: extern int t(void);
+ 7: int main(int argc, char **argv)
+ 8: {
+ 9: if (argc > 1000000) {
+10: int (* volatile tp)(void)=(int (*)(void))&t;
+11: printf("%d", (*tp)());
+12: }
+13:
+14: return 0;
+15: }
+16: extern void rb_enc_raise();
+17: int t(void) { rb_enc_raise(); return 0; }
+/* end */
+
+--------------------
+
diff --git a/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.6.1/gem_make.out b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.6.1/gem_make.out
new file mode 100644
index 0000000..d577dc8
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/extensions/universal-darwin-21/2.6.0/json-2.6.1/gem_make.out
@@ -0,0 +1,21 @@
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.6.1/ext/json/ext/generator
+/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby -I /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0 -r ./siteconf20211106-75777-md22l0.rb extconf.rb
+creating Makefile
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.6.1/ext/json/ext/generator
+make "DESTDIR=" clean
+
+current directory: /Users/arthur/Documents/zephyr/vendor/bundle/ruby/2.6.0/gems/json-2.6.1/ext/json/ext/generator
+make "DESTDIR="
+compiling generator.c
+In file included from generator.c:1:
+In file included from ./../fbuffer/fbuffer.h:5:
+In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby.h:33:
+/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: fatal error: 'ruby/config.h' file not found
+#include "ruby/config.h"
+ ^~~~~~~~~~~~~~~
+/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: note: did not find header 'config.h' in framework 'ruby' (loaded from '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks')
+1 error generated.
+make: *** [generator.o] Error 1
+
+make failed, exit code 2
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/LICENSE b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/LICENSE
new file mode 100644
index 0000000..ba6ffb2
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010 Christian Kruse,
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/README.md b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/README.md
new file mode 100644
index 0000000..49f0c07
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/README.md
@@ -0,0 +1,79 @@
+CFPropertyList implementation
+class to read, manipulate and write both XML and binary property list
+files (plist(5)) as defined by Apple. Have a look at CFPropertyList::List
+for more documentation.
+
+# Caution!
+
+In version 3.0.0 we dropped Ruby 1.8 compatibility. If you are using
+Ruby 1.8 consider to update Ruby; if you can't upgrade, don't upgrade
+CFPropertyList.
+
+# Installation
+
+You could either use ruby gems and install it via
+
+```bash
+gem install CFPropertyList
+```
+
+or you could clone this repository and place it somewhere in your load path.
+
+Example:
+```ruby
+require 'cfpropertylist'
+```
+
+If you're using Rails, you can add it into your Gemfile
+
+```ruby
+gem 'CFPropertyList'
+```
+
+# Usage
+
+ ## create a arbitrary data structure of basic data types
+
+```ruby
+data = {
+ 'name' => 'John Doe',
+ 'missing' => true,
+ 'last_seen' => Time.now,
+ 'friends' => ['Jane Doe','Julian Doe'],
+ 'likes' => {
+ 'me' => false
+ }
+}
+```
+
+## create CFPropertyList::List object
+
+```ruby
+plist = CFPropertyList::List.new
+```
+
+## call CFPropertyList.guess() to create corresponding CFType values
+
+```ruby
+plist.value = CFPropertyList.guess(data)
+```
+
+## write plist to file
+```ruby
+plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
+```
+
+## … later, read it again
+```ruby
+plist = CFPropertyList::List.new(:file => "example.plist")
+data = CFPropertyList.native_types(plist.value)
+```
+
+# Author and license
+
+**Author:** Christian Kruse (mailto:cjk@wwwtech.de)
+
+**Copyright:** Copyright (c) 2010
+
+**License:** MIT License
+
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/README.rdoc b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/README.rdoc
new file mode 100644
index 0000000..a71005c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/README.rdoc
@@ -0,0 +1,43 @@
+CFPropertyList implementation
+class to read, manipulate and write both XML and binary property list
+files (plist(5)) as defined by Apple. Have a look at CFPropertyList::List
+for more documentation.
+
+== Installation
+
+You could either use ruby gems and install it via
+
+ gem install CFPropertyList
+
+or you could clone this repository and place it somewhere in your load path.
+
+== Example
+ require 'cfpropertylist'
+
+ # create a arbitrary data structure of basic data types
+ data = {
+ 'name' => 'John Doe',
+ 'missing' => true,
+ 'last_seen' => Time.now,
+ 'friends' => ['Jane Doe','Julian Doe'],
+ 'likes' => {
+ 'me' => false
+ }
+ }
+
+ # create CFPropertyList::List object
+ plist = CFPropertyList::List.new
+
+ # call CFPropertyList.guess() to create corresponding CFType values
+ plist.value = CFPropertyList.guess(data)
+
+ # write plist to file
+ plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
+
+ # … later, read it again
+ plist = CFPropertyList::List.new(:file => "example.plist")
+ data = CFPropertyList.native_types(plist.value)
+
+Author:: Christian Kruse (mailto:cjk@wwwtech.de)
+Copyright:: Copyright (c) 2010
+License:: MIT License
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/THANKS b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/THANKS
new file mode 100644
index 0000000..c981a51
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/THANKS
@@ -0,0 +1,7 @@
+Special thanks to:
+
+Steve Madsen for providing a lot of performance patches and bugfixes!
+Have a look at his Github account:
+
+
+
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist.rb b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist.rb
new file mode 100644
index 0000000..f83fef7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist.rb
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+
+require 'cfpropertylist/rbCFPropertyList'
+
+
+# eof
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbBinaryCFPropertyList.rb b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbBinaryCFPropertyList.rb
new file mode 100644
index 0000000..da84b63
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbBinaryCFPropertyList.rb
@@ -0,0 +1,594 @@
+# -*- coding: utf-8 -*-
+
+require 'stringio'
+
+module CFPropertyList
+ # Binary PList parser class
+ class Binary
+ # Read a binary plist file
+ def load(opts)
+ @unique_table = {}
+ @count_objects = 0
+ @object_refs = 0
+
+ @written_object_count = 0
+ @object_table = []
+ @object_ref_size = 0
+
+ @offsets = []
+
+ fd = nil
+ if(opts.has_key?(:file))
+ fd = File.open(opts[:file],"rb")
+ file = opts[:file]
+ else
+ fd = StringIO.new(opts[:data],"rb")
+ file = ""
+ end
+
+ # first, we read the trailer: 32 byte from the end
+ fd.seek(-32,IO::SEEK_END)
+ buff = fd.read(32)
+
+ offset_size, object_ref_size, number_of_objects, top_object, table_offset = buff.unpack "x6CCx4Nx4Nx4N"
+
+ # after that, get the offset table
+ fd.seek(table_offset, IO::SEEK_SET)
+ coded_offset_table = fd.read(number_of_objects * offset_size)
+ raise CFFormatError.new("#{file}: Format error!") unless coded_offset_table.bytesize == number_of_objects * offset_size
+
+ @count_objects = number_of_objects
+
+ # decode offset table
+ if(offset_size != 3)
+ formats = ["","C*","n*","","N*"]
+ @offsets = coded_offset_table.unpack(formats[offset_size])
+ else
+ @offsets = coded_offset_table.unpack("C*").each_slice(3).map {
+ |x,y,z| (x << 16) | (y << 8) | z
+ }
+ end
+
+ @object_ref_size = object_ref_size
+ val = read_binary_object_at(file,fd,top_object)
+
+ fd.close
+ val
+ end
+
+
+ # Convert CFPropertyList to binary format; since we have to count our objects we simply unique CFDictionary and CFArray
+ def to_str(opts={})
+ @unique_table = {}
+ @count_objects = 0
+ @object_refs = 0
+
+ @written_object_count = 0
+ @object_table = []
+
+ @offsets = []
+
+ binary_str = "bplist00"
+
+ @object_refs = count_object_refs(opts[:root])
+
+ opts[:root].to_binary(self)
+
+ next_offset = 8
+ offsets = @object_table.map do |object|
+ offset = next_offset
+ next_offset += object.bytesize
+ offset
+ end
+ binary_str << @object_table.join
+
+ table_offset = next_offset
+ offset_size = Binary.bytes_needed(table_offset)
+
+ if offset_size < 8
+ # Fast path: encode the entire offset array at once.
+ binary_str << offsets.pack((%w(C n N N)[offset_size - 1]) + '*')
+ else
+ # Slow path: host may be little or big endian, must pack each offset
+ # separately.
+ offsets.each do |offset|
+ binary_str << "#{Binary.pack_it_with_size(offset_size,offset)}"
+ end
+ end
+
+ binary_str << [offset_size, object_ref_size(@object_refs)].pack("x6CC")
+ binary_str << [@object_table.size].pack("x4N")
+ binary_str << [0].pack("x4N")
+ binary_str << [table_offset].pack("x4N")
+
+ binary_str
+ end
+
+ def object_ref_size object_refs
+ Binary.bytes_needed(object_refs)
+ end
+
+ # read a „null” type (i.e. null byte, marker byte, bool value)
+ def read_binary_null_type(length)
+ case length
+ when 0 then 0 # null byte
+ when 8 then CFBoolean.new(false)
+ when 9 then CFBoolean.new(true)
+ when 15 then 15 # fill type
+ else
+ raise CFFormatError.new("unknown null type: #{length}")
+ end
+ end
+ protected :read_binary_null_type
+
+ # read a binary int value
+ def read_binary_int(fname,fd,length)
+ if length > 4
+ raise CFFormatError.new("Integer greater than 16 bytes: #{length}")
+ end
+
+ nbytes = 1 << length
+
+ buff = fd.read(nbytes)
+
+ CFInteger.new(
+ case length
+ when 0 then buff.unpack("C")[0]
+ when 1 then buff.unpack("n")[0]
+ when 2 then buff.unpack("N")[0]
+ # 8 byte integers are always signed
+ when 3 then buff.unpack("q>")[0]
+ # 16 byte integers are used to represent unsigned 8 byte integers
+ # where the unsigned value is stored in the lower 8 bytes and the
+ # upper 8 bytes are unused.
+ when 4 then buff.unpack("Q>Q>")[1]
+ end
+ )
+ end
+ protected :read_binary_int
+
+ # read a binary real value
+ def read_binary_real(fname,fd,length)
+ raise CFFormatError.new("Real greater than 8 bytes: #{length}") if length > 3
+
+ nbytes = 1 << length
+ buff = fd.read(nbytes)
+
+ CFReal.new(
+ case length
+ when 0 # 1 byte float? must be an error
+ raise CFFormatError.new("got #{length+1} byte float, must be an error!")
+ when 1 # 2 byte float? must be an error
+ raise CFFormatError.new("got #{length+1} byte float, must be an error!")
+ when 2 then
+ buff.reverse.unpack("e")[0]
+ when 3 then
+ buff.reverse.unpack("E")[0]
+ else
+ fail "unexpected length: #{length}"
+ end
+ )
+ end
+ protected :read_binary_real
+
+ # read a binary date value
+ def read_binary_date(fname,fd,length)
+ raise CFFormatError.new("Date greater than 8 bytes: #{length}") if length > 3
+
+ nbytes = 1 << length
+ buff = fd.read(nbytes)
+
+ CFDate.new(
+ case length
+ when 0 then # 1 byte CFDate is an error
+ raise CFFormatError.new("#{length+1} byte CFDate, error")
+ when 1 then # 2 byte CFDate is an error
+ raise CFFormatError.new("#{length+1} byte CFDate, error")
+ when 2 then
+ buff.reverse.unpack("e")[0]
+ when 3 then
+ buff.reverse.unpack("E")[0]
+ end,
+ CFDate::TIMESTAMP_APPLE
+ )
+ end
+ protected :read_binary_date
+
+ # Read a binary data value
+ def read_binary_data(fname,fd,length)
+ CFData.new(read_fd(fd, length), CFData::DATA_RAW)
+ end
+ protected :read_binary_data
+
+ def read_fd fd, length
+ length > 0 ? fd.read(length) : ""
+ end
+
+ # Read a binary string value
+ def read_binary_string(fname,fd,length)
+ buff = read_fd fd, length
+ @unique_table[buff] = true unless @unique_table.has_key?(buff)
+ CFString.new(buff)
+ end
+ protected :read_binary_string
+
+ # Convert the given string from one charset to another
+ def Binary.charset_convert(str,from,to="UTF-8")
+ return str.dup.force_encoding(from).encode(to) if str.respond_to?("encode")
+ Iconv.conv(to,from,str)
+ end
+
+ # Count characters considering character set
+ def Binary.charset_strlen(str,charset="UTF-8")
+ if str.respond_to?(:encode)
+ size = str.length
+ else
+ utf8_str = Iconv.conv("UTF-8",charset,str)
+ size = utf8_str.scan(/./mu).size
+ end
+
+ # UTF-16 code units in the range D800-DBFF are the beginning of
+ # a surrogate pair, and count as one additional character for
+ # length calculation.
+ if charset =~ /^UTF-16/
+ if str.respond_to?(:encode)
+ str.bytes.to_a.each_slice(2) { |pair| size += 1 if (0xd8..0xdb).include?(pair[0]) }
+ else
+ str.split('').each_slice(2) { |pair| size += 1 if ("\xd8".."\xdb").include?(pair[0]) }
+ end
+ end
+
+ size
+ end
+
+ # Read a unicode string value, coded as UTF-16BE
+ def read_binary_unicode_string(fname,fd,length)
+ # The problem is: we get the length of the string IN CHARACTERS;
+ # since a char in UTF-16 can be 16 or 32 bit long, we don't really know
+ # how long the string is in bytes
+ buff = fd.read(2*length)
+
+ @unique_table[buff] = true unless @unique_table.has_key?(buff)
+ CFString.new(Binary.charset_convert(buff,"UTF-16BE","UTF-8"))
+ end
+ protected :read_binary_unicode_string
+
+ def unpack_with_size(nbytes, buff)
+ format = ["C*", "n*", "N*", "N*"][nbytes - 1];
+
+ if nbytes == 3
+ buff = "\0" + buff.scan(/.{1,3}/).join("\0")
+ end
+
+ return buff.unpack(format)
+ end
+
+ # Read an binary array value, including contained objects
+ def read_binary_array(fname,fd,length)
+ ary = []
+
+ # first: read object refs
+ if(length != 0)
+ buff = fd.read(length * @object_ref_size)
+ objects = unpack_with_size(@object_ref_size, buff) #buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
+
+ # now: read objects
+ 0.upto(length-1) do |i|
+ object = read_binary_object_at(fname,fd,objects[i])
+ ary.push object
+ end
+ end
+
+ CFArray.new(ary)
+ end
+ protected :read_binary_array
+
+ # Read a dictionary value, including contained objects
+ def read_binary_dict(fname,fd,length)
+ dict = {}
+
+ # first: read keys
+ if(length != 0) then
+ buff = fd.read(length * @object_ref_size)
+ keys = unpack_with_size(@object_ref_size, buff)
+
+ # second: read object refs
+ buff = fd.read(length * @object_ref_size)
+ objects = unpack_with_size(@object_ref_size, buff)
+
+ # read real keys and objects
+ 0.upto(length-1) do |i|
+ key = read_binary_object_at(fname,fd,keys[i])
+ object = read_binary_object_at(fname,fd,objects[i])
+ dict[key.value] = object
+ end
+ end
+
+ CFDictionary.new(dict)
+ end
+ protected :read_binary_dict
+
+ # Read an object type byte, decode it and delegate to the correct
+ # reader function
+ def read_binary_object(fname,fd)
+ # first: read the marker byte
+ buff = fd.read(1)
+
+ object_length = buff.unpack("C*")
+ object_length = object_length[0] & 0xF
+
+ buff = buff.unpack("H*")
+ object_type = buff[0][0].chr
+
+ if(object_type != "0" && object_length == 15) then
+ object_length = read_binary_object(fname,fd)
+ object_length = object_length.value
+ end
+
+ case object_type
+ when '0' # null, false, true, fillbyte
+ read_binary_null_type(object_length)
+ when '1' # integer
+ read_binary_int(fname,fd,object_length)
+ when '2' # real
+ read_binary_real(fname,fd,object_length)
+ when '3' # date
+ read_binary_date(fname,fd,object_length)
+ when '4' # data
+ read_binary_data(fname,fd,object_length)
+ when '5' # byte string, usually utf8 encoded
+ read_binary_string(fname,fd,object_length)
+ when '6' # unicode string (utf16be)
+ read_binary_unicode_string(fname,fd,object_length)
+ when '8'
+ CFUid.new(read_binary_int(fname, fd, object_length).value)
+ when 'a' # array
+ read_binary_array(fname,fd,object_length)
+ when 'd' # dictionary
+ read_binary_dict(fname,fd,object_length)
+ end
+ end
+ protected :read_binary_object
+
+ # Read an object type byte at position $pos, decode it and delegate to the correct reader function
+ def read_binary_object_at(fname,fd,pos)
+ position = @offsets[pos]
+ fd.seek(position,IO::SEEK_SET)
+ read_binary_object(fname,fd)
+ end
+ protected :read_binary_object_at
+
+ # pack an +int+ of +nbytes+ with size
+ def Binary.pack_it_with_size(nbytes,int)
+ case nbytes
+ when 1 then [int].pack('c')
+ when 2 then [int].pack('n')
+ when 4 then [int].pack('N')
+ when 8
+ [int >> 32, int & 0xFFFFFFFF].pack('NN')
+ else
+ raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
+ end
+ end
+
+ def Binary.pack_int_array_with_size(nbytes, array)
+ case nbytes
+ when 1 then array.pack('C*')
+ when 2 then array.pack('n*')
+ when 4 then array.pack('N*')
+ when 8
+ array.map { |int| [int >> 32, int & 0xFFFFFFFF].pack('NN') }.join
+ else
+ raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
+ end
+ end
+
+ # calculate how many bytes are needed to save +count+
+ def Binary.bytes_needed(count)
+ case
+ when count < 2**8 then 1
+ when count < 2**16 then 2
+ when count < 2**32 then 4
+ when count < 2**64 then 8
+ else
+ raise CFFormatError.new("Data size too large: #{count}")
+ end
+ end
+
+ # Create a type byte for binary format as defined by apple
+ def Binary.type_bytes(type, length)
+ if length < 15
+ [(type << 4) | length].pack('C')
+ else
+ bytes = [(type << 4) | 0xF]
+ if length <= 0xFF
+ bytes.push(0x10, length).pack('CCC') # 1 byte length
+ elsif length <= 0xFFFF
+ bytes.push(0x11, length).pack('CCn') # 2 byte length
+ elsif length <= 0xFFFFFFFF
+ bytes.push(0x12, length).pack('CCN') # 4 byte length
+ elsif length <= 0x7FFFFFFFFFFFFFFF
+ bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
+ else
+ raise CFFormatError.new("Integer too large: #{int}")
+ end
+ end
+ end
+
+ def count_object_refs(object)
+ case object
+ when CFArray
+ contained_refs = 0
+ object.value.each do |element|
+ if CFArray === element || CFDictionary === element
+ contained_refs += count_object_refs(element)
+ end
+ end
+ return object.value.size + contained_refs
+ when CFDictionary
+ contained_refs = 0
+ object.value.each_value do |value|
+ if CFArray === value || CFDictionary === value
+ contained_refs += count_object_refs(value)
+ end
+ end
+ return object.value.keys.size * 2 + contained_refs
+ else
+ return 0
+ end
+ end
+
+ def Binary.ascii_string?(str)
+ if str.respond_to?(:ascii_only?)
+ str.ascii_only?
+ else
+ str !~ /[\x80-\xFF]/mn
+ end
+ end
+
+ # Uniques and transforms a string value to binary format and adds it to the object table
+ def string_to_binary(val)
+ val = val.to_s
+
+ @unique_table[val] ||= begin
+ if !Binary.ascii_string?(val)
+ val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
+ bdata = Binary.type_bytes(0b0110, Binary.charset_strlen(val,"UTF-16BE"))
+
+ val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
+ @object_table[@written_object_count] = bdata << val
+ else
+ bdata = Binary.type_bytes(0b0101,val.bytesize)
+ @object_table[@written_object_count] = bdata << val
+ end
+
+ @written_object_count += 1
+ @written_object_count - 1
+ end
+ end
+
+ # Codes an integer to binary format
+ def int_to_binary(value)
+ # Note: nbytes is actually an exponent. number of bytes = 2**nbytes.
+ nbytes = 0
+ nbytes = 1 if value > 0xFF # 1 byte unsigned integer
+ nbytes += 1 if value > 0xFFFF # 4 byte unsigned integer
+ nbytes += 1 if value > 0xFFFFFFFF # 8 byte unsigned integer
+ nbytes += 1 if value > 0x7FFFFFFFFFFFFFFF # 8 byte unsigned integer, stored in lower half of 16 bytes
+ nbytes = 3 if value < 0 # signed integers always stored in 8 bytes
+
+ Binary.type_bytes(0b0001, nbytes) <<
+ if nbytes < 4
+ [value].pack(["C", "n", "N", "q>"][nbytes])
+ else # nbytes == 4
+ [0,value].pack("Q>Q>")
+ end
+ end
+
+ # Codes a real value to binary format
+ def real_to_binary(val)
+ Binary.type_bytes(0b0010,3) << [val].pack("E").reverse
+ end
+
+ # Converts a numeric value to binary and adds it to the object table
+ def num_to_binary(value)
+ @object_table[@written_object_count] =
+ if value.is_a?(CFInteger)
+ int_to_binary(value.value)
+ else
+ real_to_binary(value.value)
+ end
+
+ @written_object_count += 1
+ @written_object_count - 1
+ end
+
+ def uid_to_binary(value)
+ nbytes = 0
+ nbytes = 1 if value > 0xFF # 1 byte integer
+ nbytes += 1 if value > 0xFFFF # 4 byte integer
+ nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
+ nbytes = 3 if value < 0 # 8 byte integer, since signed
+
+ @object_table[@written_object_count] = Binary.type_bytes(0b1000, nbytes) <<
+ if nbytes < 3
+ [value].pack(
+ if nbytes == 0 then "C"
+ elsif nbytes == 1 then "n"
+ else "N"
+ end
+ )
+ else
+ # 64 bit signed integer; we need the higher and the lower 32 bit of the value
+ high_word = value >> 32
+ low_word = value & 0xFFFFFFFF
+ [high_word,low_word].pack("NN")
+ end
+
+ @written_object_count += 1
+ @written_object_count - 1
+ end
+
+ # Convert date value (apple format) to binary and adds it to the object table
+ def date_to_binary(val)
+ val = val.getutc.to_f - CFDate::DATE_DIFF_APPLE_UNIX # CFDate is a real, number of seconds since 01/01/2001 00:00:00 GMT
+
+ @object_table[@written_object_count] =
+ (Binary.type_bytes(0b0011, 3) << [val].pack("E").reverse)
+
+ @written_object_count += 1
+ @written_object_count - 1
+ end
+
+ # Convert a bool value to binary and add it to the object table
+ def bool_to_binary(val)
+
+ @object_table[@written_object_count] = val ? "\x9" : "\x8" # 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false
+ @written_object_count += 1
+ @written_object_count - 1
+ end
+
+ # Convert data value to binary format and add it to the object table
+ def data_to_binary(val)
+ @object_table[@written_object_count] =
+ (Binary.type_bytes(0b0100, val.bytesize) << val)
+
+ @written_object_count += 1
+ @written_object_count - 1
+ end
+
+ # Convert array to binary format and add it to the object table
+ def array_to_binary(val)
+ saved_object_count = @written_object_count
+ @written_object_count += 1
+ #@object_refs += val.value.size
+
+ values = val.value.map { |v| v.to_binary(self) }
+ bdata = Binary.type_bytes(0b1010, val.value.size) <<
+ Binary.pack_int_array_with_size(object_ref_size(@object_refs),
+ values)
+
+ @object_table[saved_object_count] = bdata
+ saved_object_count
+ end
+
+ # Convert dictionary to binary format and add it to the object table
+ def dict_to_binary(val)
+ saved_object_count = @written_object_count
+ @written_object_count += 1
+
+ #@object_refs += val.value.keys.size * 2
+
+ keys_and_values = val.value.keys.map { |k| CFString.new(k).to_binary(self) }
+ keys_and_values.concat(val.value.values.map { |v| v.to_binary(self) })
+
+ bdata = Binary.type_bytes(0b1101,val.value.size) <<
+ Binary.pack_int_array_with_size(object_ref_size(@object_refs), keys_and_values)
+
+ @object_table[saved_object_count] = bdata
+ return saved_object_count
+ end
+ end
+end
+
+# eof
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbCFPlistError.rb b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbCFPlistError.rb
new file mode 100644
index 0000000..e276565
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbCFPlistError.rb
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+#
+# Exceptions used:
+# CFPlistError:: General base exception
+# CFFormatError:: Format error
+# CFTypeError:: Type error
+#
+# Easy and simple :-)
+#
+# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
+# Copyright:: Copyright (c) 2010
+# License:: MIT License
+
+# general plist error. All exceptions thrown are derived from this class.
+class CFPlistError < StandardError
+end
+
+# Exception thrown when format errors occur
+class CFFormatError < CFPlistError
+end
+
+# Exception thrown when type errors occur
+class CFTypeError < CFPlistError
+end
+
+# eof
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbCFPropertyList.rb b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbCFPropertyList.rb
new file mode 100644
index 0000000..43eb1d3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbCFPropertyList.rb
@@ -0,0 +1,449 @@
+# -*- coding: utf-8 -*-
+
+require 'kconv'
+require 'date'
+require 'time'
+
+#
+# CFPropertyList implementation
+#
+# class to read, manipulate and write both XML and binary property list
+# files (plist(5)) as defined by Apple. Have a look at CFPropertyList::List
+# for more documentation.
+#
+# == Example
+# require 'cfpropertylist'
+#
+# # create a arbitrary data structure of basic data types
+# data = {
+# 'name' => 'John Doe',
+# 'missing' => true,
+# 'last_seen' => Time.now,
+# 'friends' => ['Jane Doe','Julian Doe'],
+# 'likes' => {
+# 'me' => false
+# }
+# }
+#
+# # create CFPropertyList::List object
+# plist = CFPropertyList::List.new
+#
+# # call CFPropertyList.guess() to create corresponding CFType values
+# # pass in optional :convert_unknown_to_string => true to convert things like symbols into strings.
+# plist.value = CFPropertyList.guess(data)
+#
+# # write plist to file
+# plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
+#
+# # … later, read it again
+# plist = CFPropertyList::List.new(:file => "example.plist")
+# data = CFPropertyList.native_types(plist.value)
+#
+# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
+# Copyright:: Copyright (c) 2010
+# License:: MIT License
+module CFPropertyList
+ class << self
+ attr_accessor :xml_parser_interface
+ end
+
+ # interface class for PList parsers
+ class ParserInterface
+ # load a plist
+ def load(opts={})
+ return ""
+ end
+
+ # convert a plist to string
+ def to_str(opts={})
+ return true
+ end
+ end
+
+ class XMLParserInterface < ParserInterface
+ def new_node(name)
+ end
+
+ def new_text(val)
+ end
+
+ def append_node(parent, child)
+ end
+ end
+end
+
+dirname = File.dirname(__FILE__)
+require dirname + '/rbCFPlistError.rb'
+require dirname + '/rbCFTypes.rb'
+require dirname + '/rbBinaryCFPropertyList.rb'
+require dirname + '/rbPlainCFPropertyList.rb'
+
+begin
+ require dirname + '/rbLibXMLParser.rb'
+ temp = LibXML::XML::Parser::Options::NOBLANKS # check if we have a version with parser options
+ temp = false # avoid a warning
+ try_nokogiri = false
+ CFPropertyList.xml_parser_interface = CFPropertyList::LibXMLParser
+rescue LoadError, NameError
+ try_nokogiri = true
+end
+
+if try_nokogiri then
+ begin
+ require dirname + '/rbNokogiriParser.rb'
+ CFPropertyList.xml_parser_interface = CFPropertyList::NokogiriXMLParser
+ rescue LoadError
+ require dirname + '/rbREXMLParser.rb'
+ CFPropertyList.xml_parser_interface = CFPropertyList::ReXMLParser
+ end
+end
+
+
+module CFPropertyList
+ # Create CFType hierarchy by guessing the correct CFType, e.g.
+ #
+ # x = {
+ # 'a' => ['b','c','d']
+ # }
+ # cftypes = CFPropertyList.guess(x)
+ #
+ # pass optional options hash. Only possible value actually:
+ # +convert_unknown_to_string+:: Convert unknown objects to string calling to_str()
+ # +converter_method+:: Convert unknown objects to known objects calling +method_name+
+ #
+ # cftypes = CFPropertyList.guess(x,:convert_unknown_to_string => true,:converter_method => :to_hash, :converter_with_opts => true)
+ def guess(object, options = {})
+ case object
+ when Integer then CFInteger.new(object)
+ when UidFixnum then CFUid.new(object)
+ when Float then CFReal.new(object)
+ when TrueClass, FalseClass then CFBoolean.new(object)
+
+ when Blob
+ CFData.new(object, CFData::DATA_RAW)
+
+ when String, Symbol
+ CFString.new(object.to_s)
+
+ when Time, DateTime, Date
+ CFDate.new(object)
+
+ when Array, Enumerator
+ ary = Array.new
+ object.each do |o|
+ ary.push CFPropertyList.guess(o, options)
+ end
+ CFArray.new(ary)
+
+ when Hash
+ hsh = Hash.new
+ object.each_pair do |k,v|
+ k = k.to_s if k.is_a?(Symbol)
+ hsh[k] = CFPropertyList.guess(v, options)
+ end
+ CFDictionary.new(hsh)
+ else
+ case
+ when Object.const_defined?('BigDecimal') && object.is_a?(BigDecimal)
+ CFReal.new(object)
+ when object.respond_to?(:read)
+ raw_data = object.read
+ # treat the data as a bytestring (ASCII-8BIT) if Ruby supports it. Do this by forcing
+ # the encoding, on the assumption that the bytes were read correctly, and just tagged with
+ # an inappropriate encoding, rather than transcoding.
+ raw_data.force_encoding(Encoding::ASCII_8BIT) if raw_data.respond_to?(:force_encoding)
+ CFData.new(raw_data, CFData::DATA_RAW)
+ when options[:converter_method] && object.respond_to?(options[:converter_method])
+ if options[:converter_with_opts]
+ CFPropertyList.guess(object.send(options[:converter_method],options),options)
+ else
+ CFPropertyList.guess(object.send(options[:converter_method]),options)
+ end
+ when options[:convert_unknown_to_string]
+ CFString.new(object.to_s)
+ else
+ raise CFTypeError.new("Unknown class #{object.class.to_s}. Try using :convert_unknown_to_string if you want to use unknown object types!")
+ end
+ end
+ end
+
+ # Converts a CFType hiercharchy to native Ruby types
+ def native_types(object,keys_as_symbols=false)
+ return if object.nil?
+
+ if(object.is_a?(CFDate) || object.is_a?(CFString) || object.is_a?(CFInteger) || object.is_a?(CFReal) || object.is_a?(CFBoolean)) || object.is_a?(CFUid) then
+ return object.value
+ elsif(object.is_a?(CFData)) then
+ return CFPropertyList::Blob.new(object.decoded_value)
+ elsif(object.is_a?(CFArray)) then
+ ary = []
+ object.value.each do
+ |v|
+ ary.push CFPropertyList.native_types(v)
+ end
+
+ return ary
+ elsif(object.is_a?(CFDictionary)) then
+ hsh = {}
+ object.value.each_pair do
+ |k,v|
+ k = k.to_sym if keys_as_symbols
+ hsh[k] = CFPropertyList.native_types(v)
+ end
+
+ return hsh
+ end
+ end
+
+ module_function :guess, :native_types
+
+ # Class representing a CFPropertyList. Instantiate with #new
+ class List
+ # Format constant for binary format
+ FORMAT_BINARY = 1
+
+ # Format constant for XML format
+ FORMAT_XML = 2
+
+ # Format constant for the old plain format
+ FORMAT_PLAIN = 3
+
+ # Format constant for automatic format recognizing
+ FORMAT_AUTO = 0
+
+ @@parsers = [Binary, CFPropertyList.xml_parser_interface, PlainParser]
+
+ # Path of PropertyList
+ attr_accessor :filename
+ # the original format of the PropertyList
+ attr_accessor :format
+ # the root value in the plist file
+ attr_accessor :value
+ # default value for XML generation; if true generate formatted XML
+ attr_accessor :formatted
+
+ # initialize a new CFPropertyList, arguments are:
+ #
+ # :file:: Parse a file
+ # :format:: Format is one of FORMAT_BINARY or FORMAT_XML. Defaults to FORMAT_AUTO
+ # :data:: Parse a string
+ #
+ # All arguments are optional
+ def initialize(opts={})
+ @filename = opts[:file]
+ @format = opts[:format] || FORMAT_AUTO
+ @data = opts[:data]
+ @formatted = opts[:formatted]
+
+ load(@filename) unless @filename.nil?
+ load_str(@data) unless @data.nil?
+ end
+
+ # returns a list of registered parsers
+ def self.parsers
+ @@parsers
+ end
+
+ # set a list of parsers
+ def self.parsers=(val)
+ @@parsers = val
+ end
+
+ # Load an XML PropertyList
+ # filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
+ def load_xml(filename=nil)
+ load(filename,List::FORMAT_XML)
+ end
+
+ # read a binary plist file
+ # filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
+ def load_binary(filename=nil)
+ load(filename,List::FORMAT_BINARY)
+ end
+
+ # read a plain plist file
+ # filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
+ def load_plain(filename=nil)
+ load(filename,List::FORMAT_PLAIN)
+ end
+
+ # load a plist from a XML string
+ # str:: The string containing the plist
+ def load_xml_str(str=nil)
+ load_str(str,List::FORMAT_XML)
+ end
+
+ # load a plist from a binary string
+ # str:: The string containing the plist
+ def load_binary_str(str=nil)
+ load_str(str,List::FORMAT_BINARY)
+ end
+
+ # load a plist from a plain string
+ # str:: The string containing the plist
+ def load_plain_str(str=nil)
+ load_str(str,List::FORMAT_PLAIN)
+ end
+
+ # load a plist from a string
+ # str = nil:: The string containing the plist
+ # format = nil:: The format of the plist
+ def load_str(str=nil,format=nil)
+ str = @data if str.nil?
+ format = @format if format.nil?
+
+ @value = {}
+ case format
+ when List::FORMAT_BINARY, List::FORMAT_XML, List::FORMAT_PLAIN then
+ prsr = @@parsers[format-1].new
+ @value = prsr.load({:data => str})
+
+ when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
+ filetype = str[0..5]
+ version = str[6..7]
+
+ prsr = nil
+
+ if filetype == "bplist" then
+ raise CFFormatError.new("Wrong file version #{version}") unless version == "00"
+ prsr = Binary.new
+ @format = List::FORMAT_BINARY
+ else
+ if str =~ /^<(\?xml|!DOCTYPE|plist)/
+ prsr = CFPropertyList.xml_parser_interface.new
+ @format = List::FORMAT_XML
+ else
+ prsr = PlainParser.new
+ @format = List::FORMAT_PLAIN
+ end
+ end
+
+ @value = prsr.load({:data => str})
+ end
+ end
+
+ # Read a plist file
+ # file = nil:: The filename of the file to read. If nil, use +filename+ instance variable
+ # format = nil:: The format of the plist file. Auto-detect if nil
+ def load(file=nil,format=nil)
+ file = @filename if file.nil?
+ format = @format if format.nil?
+ @value = {}
+
+ raise IOError.new("File #{file} not readable!") unless File.readable? file
+
+ case format
+ when List::FORMAT_BINARY, List::FORMAT_XML, List::FORMAT_PLAIN then
+ prsr = @@parsers[format-1].new
+ @value = prsr.load({:file => file})
+
+ when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
+ magic_number = IO.read(file,12)
+ raise IOError.new("File #{file} is empty.") unless magic_number
+ filetype = magic_number[0..5]
+ version = magic_number[6..7]
+
+ prsr = nil
+ if filetype == "bplist" then
+ raise CFFormatError.new("Wrong file version #{version}") unless version == "00"
+ prsr = Binary.new
+ @format = List::FORMAT_BINARY
+ else
+ if magic_number =~ /^<(\?xml|!DOCTYPE|plist)/
+ prsr = CFPropertyList.xml_parser_interface.new
+ @format = List::FORMAT_XML
+ else
+ prsr = PlainParser.new
+ @format = List::FORMAT_PLAIN
+ end
+ end
+
+ @value = prsr.load({:file => file})
+ end
+
+ raise CFFormatError.new("Invalid format or parser error!") if @value.nil?
+ end
+
+ # Serialize CFPropertyList object to specified format and write it to file
+ # file = nil:: The filename of the file to write to. Uses +filename+ instance variable if nil
+ # format = nil:: The format to save in. Uses +format+ instance variable if nil
+ def save(file=nil,format=nil,opts={})
+ format = @format if format.nil?
+ file = @filename if file.nil?
+
+ if format != FORMAT_BINARY && format != FORMAT_XML && format != FORMAT_PLAIN
+ raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML")
+ end
+
+ if(!File.exists?(file)) then
+ raise IOError.new("File #{file} not writable!") unless File.writable?(File.dirname(file))
+ elsif(!File.writable?(file)) then
+ raise IOError.new("File #{file} not writable!")
+ end
+
+ opts[:root] = @value
+ opts[:formatted] = @formatted unless opts.has_key?(:formatted)
+
+ prsr = @@parsers[format-1].new
+
+ content = prsr.to_str(opts)
+
+ File.open(file, 'wb') {
+ |fd|
+ fd.write content
+ }
+ end
+
+ # convert plist to string
+ # format = List::FORMAT_BINARY:: The format to save the plist
+ # opts={}:: Pass parser options
+ def to_str(format=List::FORMAT_BINARY,opts={})
+ if format != FORMAT_BINARY && format != FORMAT_XML && format != FORMAT_PLAIN
+ raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML")
+ end
+
+ prsr = @@parsers[format-1].new
+
+ opts[:root] = @value
+ opts[:formatted] = @formatted unless opts.has_key?(:formatted)
+
+ return prsr.to_str(opts)
+ end
+ end
+end
+
+
+class Array
+ # convert an array to plist format
+ def to_plist(options={})
+ options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
+
+ plist = CFPropertyList::List.new
+ plist.value = CFPropertyList.guess(self, options)
+ plist.to_str(options[:plist_format], options)
+ end
+end
+
+class Enumerator
+ # convert an array to plist format
+ def to_plist(options={})
+ options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
+
+ plist = CFPropertyList::List.new
+ plist.value = CFPropertyList.guess(self, options)
+ plist.to_str(options[:plist_format], options)
+ end
+end
+
+class Hash
+ # convert a hash to plist format
+ def to_plist(options={})
+ options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
+
+ plist = CFPropertyList::List.new
+ plist.value = CFPropertyList.guess(self, options)
+ plist.to_str(options[:plist_format], options)
+ end
+end
+
+# eof
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbCFTypes.rb b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbCFTypes.rb
new file mode 100644
index 0000000..8563721
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbCFTypes.rb
@@ -0,0 +1,349 @@
+# -*- coding: utf-8 -*-
+#
+# CFTypes, e.g. CFString, CFInteger
+# needed to create unambiguous plists
+#
+# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
+# Copyright:: Copyright (c) 2009
+# License:: MIT License
+
+require 'base64'
+
+module CFPropertyList
+ ##
+ # Blob is intended to distinguish between a Ruby String instance that should
+ # be converted to a CFString type and a Ruby String instance that should be
+ # converted to a CFData type
+ class Blob < String
+ end
+
+ ##
+ # UidFixnum is intended to distinguish between a Ruby Integer
+ # instance that should be converted to a CFInteger/CFReal type and a
+ # Ruby Integer instance that should be converted to a CFUid type.
+ class UidFixnum < Integer
+ end
+
+ # This class defines the base class for all CFType classes
+ #
+ class CFType
+ # value of the type
+ attr_accessor :value
+
+ def initialize(value=nil)
+ @value = value
+ end
+
+ def to_xml(parser)
+ end
+
+ def to_binary(bplist)
+ end
+
+ def to_plain(plist)
+ end
+ end
+
+ # This class holds string values, both, UTF-8 and UTF-16BE
+ # It will convert the value to UTF-16BE if necessary (i.e. if non-ascii char contained)
+ class CFString < CFType
+ # convert to XML
+ def to_xml(parser)
+ n = parser.new_node('string')
+ n = parser.append_node(n, parser.new_text(@value)) unless @value.nil?
+ n
+ end
+
+ # convert to binary
+ def to_binary(bplist)
+ bplist.string_to_binary(@value);
+ end
+
+ def to_plain(plist)
+ if @value =~ /^\w+$/
+ @value
+ else
+ quoted
+ end
+ end
+
+ def quoted
+ str = '"'
+ @value.each_char do |c|
+ str << case c
+ when '"'
+ '\\"'
+ when '\\'
+ '\\'
+ when "\a"
+ "\\a"
+ when "\b"
+ "\\b"
+ when "\f"
+ "\\f"
+ when "\n"
+ "\n"
+ when "\v"
+ "\\v"
+ when "\r"
+ "\\r"
+ when "\t"
+ "\\t"
+ else
+ c
+ end
+ end
+
+ str << '"'
+ end
+ end
+
+ # This class holds integer/fixnum values
+ class CFInteger < CFType
+ # convert to XML
+ def to_xml(parser)
+ n = parser.new_node('integer')
+ n = parser.append_node(n, parser.new_text(@value.to_s))
+ n
+ end
+
+ # convert to binary
+ def to_binary(bplist)
+ bplist.num_to_binary(self)
+ end
+
+ def to_plain(plist)
+ @value.to_s
+ end
+ end
+
+ # This class holds float values
+ class CFReal < CFType
+ # convert to XML
+ def to_xml(parser)
+ n = parser.new_node('real')
+ n = parser.append_node(n, parser.new_text(@value.to_s))
+ n
+ end
+
+ # convert to binary
+ def to_binary(bplist)
+ bplist.num_to_binary(self)
+ end
+
+ def to_plain(plist)
+ @value.to_s
+ end
+ end
+
+ # This class holds Time values. While Apple uses seconds since 2001,
+ # the rest of the world uses seconds since 1970. So if you access value
+ # directly, you get the Time class. If you access via get_value you either
+ # geht the timestamp or the Apple timestamp
+ class CFDate < CFType
+ TIMESTAMP_APPLE = 0
+ TIMESTAMP_UNIX = 1
+ DATE_DIFF_APPLE_UNIX = 978307200
+
+ # create a XML date strimg from a time object
+ def CFDate.date_string(val)
+ # 2009-05-13T20:23:43Z
+ val.getutc.strftime("%Y-%m-%dT%H:%M:%SZ")
+ end
+
+ # parse a XML date string
+ def CFDate.parse_date(val)
+ # 2009-05-13T20:23:43Z
+ val =~ %r{^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$}
+ year,month,day,hour,min,sec = $1, $2, $3, $4, $5, $6
+ return Time.utc(year,month,day,hour,min,sec).getlocal
+ end
+
+ # set value to defined state
+ def initialize(value = nil,format=CFDate::TIMESTAMP_UNIX)
+ if(value.is_a?(Time) || value.nil?) then
+ @value = value.nil? ? Time.now : value
+ elsif value.instance_of? Date
+ @value = Time.utc(value.year, value.month, value.day, 0, 0, 0)
+ elsif value.instance_of? DateTime
+ @value = value.to_time.utc
+ else
+ set_value(value,format)
+ end
+ end
+
+ # set value with timestamp, either Apple or UNIX
+ def set_value(value,format=CFDate::TIMESTAMP_UNIX)
+ if(format == CFDate::TIMESTAMP_UNIX) then
+ @value = Time.at(value)
+ else
+ @value = Time.at(value + CFDate::DATE_DIFF_APPLE_UNIX)
+ end
+ end
+
+ # get timestamp, either UNIX or Apple timestamp
+ def get_value(format=CFDate::TIMESTAMP_UNIX)
+ if(format == CFDate::TIMESTAMP_UNIX) then
+ @value.to_i
+ else
+ @value.to_f - CFDate::DATE_DIFF_APPLE_UNIX
+ end
+ end
+
+ # convert to XML
+ def to_xml(parser)
+ n = parser.new_node('date')
+ n = parser.append_node(n, parser.new_text(CFDate::date_string(@value)))
+ n
+ end
+
+ # convert to binary
+ def to_binary(bplist)
+ bplist.date_to_binary(@value)
+ end
+
+ def to_plain(plist)
+ @value.strftime("%Y-%m-%d %H:%M:%S %z")
+ end
+ end
+
+ # This class contains a boolean value
+ class CFBoolean < CFType
+ # convert to XML
+ def to_xml(parser)
+ parser.new_node(@value ? 'true' : 'false')
+ end
+
+ # convert to binary
+ def to_binary(bplist)
+ bplist.bool_to_binary(@value);
+ end
+
+ def to_plain(plist)
+ @value ? "true" : "false"
+ end
+ end
+
+ # This class contains binary data values
+ class CFData < CFType
+ # Base64 encoded data
+ DATA_BASE64 = 0
+ # Raw data
+ DATA_RAW = 1
+
+ # set value to defined state, either base64 encoded or raw
+ def initialize(value=nil,format=DATA_BASE64)
+ if(format == DATA_RAW)
+ @raw_value = value
+ else
+ @value = value
+ end
+ end
+
+ # get base64 encoded value
+ def encoded_value
+ @value ||= "\n#{Base64.encode64(@raw_value).gsub("\n", '').scan(/.{1,76}/).join("\n")}\n"
+ end
+
+ # get base64 decoded value
+ def decoded_value
+ @raw_value ||= Blob.new(Base64.decode64(@value))
+ end
+
+ # convert to XML
+ def to_xml(parser)
+ n = parser.new_node('data')
+ n = parser.append_node(n, parser.new_text(encoded_value()))
+ n
+ end
+
+ # convert to binary
+ def to_binary(bplist)
+ bplist.data_to_binary(decoded_value())
+ end
+
+ def to_plain(plist)
+ "<" + decoded_value.unpack("H*").join("") + ">"
+ end
+ end
+
+ # This class contains an array of values
+ class CFArray < CFType
+ # create a new array CFType
+ def initialize(val=[])
+ @value = val
+ end
+
+ # convert to XML
+ def to_xml(parser)
+ n = parser.new_node('array')
+ @value.each do |v|
+ n = parser.append_node(n, v.to_xml(parser))
+ end
+ n
+ end
+
+ # convert to binary
+ def to_binary(bplist)
+ bplist.array_to_binary(self)
+ end
+
+ def to_plain(plist)
+ ary = @value.map { |v| v.to_plain(plist) }
+ "( " + ary.join(", ") + " )"
+ end
+ end
+
+ # this class contains a hash of values
+ class CFDictionary < CFType
+ # Create new CFDictonary type.
+ def initialize(value={})
+ @value = value
+ end
+
+ # convert to XML
+ def to_xml(parser)
+ n = parser.new_node('dict')
+ @value.each_pair do |key, value|
+ k = parser.append_node(parser.new_node('key'), parser.new_text(key.to_s))
+ n = parser.append_node(n, k)
+ n = parser.append_node(n, value.to_xml(parser))
+ end
+ n
+ end
+
+ # convert to binary
+ def to_binary(bplist)
+ bplist.dict_to_binary(self)
+ end
+
+ def to_plain(plist)
+ str = "{ "
+ cfstr = CFString.new()
+
+ @value.each do |k,v|
+ cfstr.value = k
+ str << cfstr.to_plain(plist) + " = " + v.to_plain(plist) + "; "
+ end
+
+ str << "}"
+ end
+ end
+
+ class CFUid < CFType
+ def to_xml(parser)
+ CFDictionary.new({'CF$UID' => CFInteger.new(@value)}).to_xml(parser)
+ end
+
+ # convert to binary
+ def to_binary(bplist)
+ bplist.uid_to_binary(@value)
+ end
+
+ def to_plain(plist)
+ CFDictionary.new({'CF$UID' => CFInteger.new(@value)}).to_plain(plist)
+ end
+ end
+end
+
+# eof
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbLibXMLParser.rb b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbLibXMLParser.rb
new file mode 100644
index 0000000..0654f08
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbLibXMLParser.rb
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+
+require 'libxml'
+
+module CFPropertyList
+ # XML parser
+ class LibXMLParser < XMLParserInterface
+ PARSER_OPTIONS = LibXML::XML::Parser::Options::NOBLANKS|LibXML::XML::Parser::Options::NONET
+ # read a XML file
+ # opts::
+ # * :file - The filename of the file to load
+ # * :data - The data to parse
+ def load(opts)
+ doc = nil
+
+ if(opts.has_key?(:file)) then
+ doc = LibXML::XML::Document.file(opts[:file],:options => PARSER_OPTIONS)
+ else
+ doc = LibXML::XML::Document.string(opts[:data],:options => PARSER_OPTIONS)
+ end
+
+ if doc
+ root = doc.root.first
+ return import_xml(root)
+ end
+ rescue LibXML::XML::Error => e
+ raise CFFormatError.new('invalid XML: ' + e.message)
+ end
+
+ # serialize CFPropertyList object to XML
+ # opts = {}:: Specify options: :formatted - Use indention and line breaks
+ def to_str(opts={})
+ doc = LibXML::XML::Document.new
+
+ doc.root = LibXML::XML::Node.new('plist')
+ doc.encoding = LibXML::XML::Encoding::UTF_8
+
+ doc.root['version'] = '1.0'
+ doc.root << opts[:root].to_xml(self)
+
+ # ugly hack, but there's no other possibility I know
+ str = doc.to_s(:indent => opts[:formatted])
+ str1 = String.new
+ first = false
+ str.each_line do |line|
+ str1 << line
+ unless(first) then
+ str1 << "\n" if line =~ /^\s*<\?xml/
+ end
+
+ first = true
+ end
+
+ str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
+ return str1
+ end
+
+ def new_node(name)
+ LibXML::XML::Node.new(name)
+ end
+
+ def new_text(val)
+ LibXML::XML::Node.new_text(val)
+ end
+
+ def append_node(parent, child)
+ parent << child
+ end
+
+ protected
+
+ # get the value of a DOM node
+ def get_value(n)
+ content = if n.children?
+ n.first.content
+ else
+ n.content
+ end
+
+ content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
+ content
+ end
+
+ # import the XML values
+ def import_xml(node)
+ ret = nil
+
+ case node.name
+ when 'dict'
+ hsh = Hash.new
+ key = nil
+
+ if node.children? then
+ node.children.each do |n|
+ next if n.text? # avoid a bug of libxml
+ next if n.comment?
+
+ if n.name == "key" then
+ key = get_value(n)
+ else
+ raise CFFormatError.new("Format error!") if key.nil?
+ hsh[key] = import_xml(n)
+ key = nil
+ end
+ end
+ end
+
+ if hsh['CF$UID'] and hsh.keys.length == 1
+ ret = CFUid.new(hsh['CF$UID'].value)
+ else
+ ret = CFDictionary.new(hsh)
+ end
+
+ when 'array'
+ ary = Array.new
+
+ if node.children? then
+ node.children.each do |n|
+ next if n.text? # avoid a bug of libxml
+ next if n.comment?
+ ary.push import_xml(n)
+ end
+ end
+
+ ret = CFArray.new(ary)
+
+ when 'true'
+ ret = CFBoolean.new(true)
+ when 'false'
+ ret = CFBoolean.new(false)
+ when 'real'
+ ret = CFReal.new(get_value(node).to_f)
+ when 'integer'
+ ret = CFInteger.new(get_value(node).to_i)
+ when 'string'
+ ret = CFString.new(get_value(node))
+ when 'data'
+ ret = CFData.new(get_value(node))
+ when 'date'
+ ret = CFDate.new(CFDate.parse_date(get_value(node)))
+ end
+
+ return ret
+ end
+ end
+end
+
+# eof
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbNokogiriParser.rb b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbNokogiriParser.rb
new file mode 100644
index 0000000..e2de688
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbNokogiriParser.rb
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+
+require 'nokogiri'
+
+module CFPropertyList
+ # XML parser
+ class NokogiriXMLParser < ParserInterface
+ PARSER_OPTIONS = Nokogiri::XML::ParseOptions::NOBLANKS|Nokogiri::XML::ParseOptions::NONET
+ # read a XML file
+ # opts::
+ # * :file - The filename of the file to load
+ # * :data - The data to parse
+ def load(opts)
+ doc = nil
+ if(opts.has_key?(:file)) then
+ File.open(opts[:file], "rb") { |fd| doc = Nokogiri::XML::Document.parse(fd, nil, nil, PARSER_OPTIONS) }
+ else
+ doc = Nokogiri::XML::Document.parse(opts[:data], nil, nil, PARSER_OPTIONS)
+ end
+
+ if doc
+ root = doc.root.children.first
+ return import_xml(root)
+ end
+ rescue Nokogiri::XML::SyntaxError => e
+ raise CFFormatError.new('invalid XML: ' + e.message)
+ end
+
+ # serialize CFPropertyList object to XML
+ # opts = {}:: Specify options: :formatted - Use indention and line breaks
+ def to_str(opts={})
+ doc = Nokogiri::XML::Document.new
+ @doc = doc
+
+ doc.root = doc.create_element 'plist', :version => '1.0'
+ doc.encoding = 'UTF-8'
+
+ doc.root << opts[:root].to_xml(self)
+
+ # ugly hack, but there's no other possibility I know
+ s_opts = Nokogiri::XML::Node::SaveOptions::AS_XML
+ s_opts |= Nokogiri::XML::Node::SaveOptions::FORMAT if opts[:formatted]
+
+ str = doc.serialize(:save_with => s_opts)
+ str1 = String.new
+ first = false
+ str.each_line do |line|
+ str1 << line
+ unless(first) then
+ str1 << "\n" if line =~ /^\s*<\?xml/
+ end
+
+ first = true
+ end
+
+ str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
+ return str1
+ end
+
+ def new_node(name)
+ @doc.create_element name
+ end
+
+ def new_text(val)
+ @doc.create_text_node val
+ end
+
+ def append_node(parent, child)
+ parent << child
+ end
+
+ protected
+
+ # get the value of a DOM node
+ def get_value(n)
+ content = if n.children.empty?
+ n.content
+ else
+ n.children.first.content
+ end
+
+ content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
+ content
+ end
+
+ # import the XML values
+ def import_xml(node)
+ ret = nil
+
+ case node.name
+ when 'dict'
+ hsh = Hash.new
+ key = nil
+ children = node.children
+
+ unless children.empty? then
+ children.each do |n|
+ next if n.text? # avoid a bug of libxml
+ next if n.comment?
+
+ if n.name == "key" then
+ key = get_value(n)
+ else
+ raise CFFormatError.new("Format error!") if key.nil?
+ hsh[key] = import_xml(n)
+ key = nil
+ end
+ end
+ end
+
+ if hsh['CF$UID'] and hsh.keys.length == 1
+ ret = CFUid.new(hsh['CF$UID'].value)
+ else
+ ret = CFDictionary.new(hsh)
+ end
+
+ when 'array'
+ ary = Array.new
+ children = node.children
+
+ unless children.empty? then
+ children.each do |n|
+ next if n.text? # avoid a bug of libxml
+ next if n.comment?
+ ary.push import_xml(n)
+ end
+ end
+
+ ret = CFArray.new(ary)
+
+ when 'true'
+ ret = CFBoolean.new(true)
+ when 'false'
+ ret = CFBoolean.new(false)
+ when 'real'
+ ret = CFReal.new(get_value(node).to_f)
+ when 'integer'
+ ret = CFInteger.new(get_value(node).to_i)
+ when 'string'
+ ret = CFString.new(get_value(node))
+ when 'data'
+ ret = CFData.new(get_value(node))
+ when 'date'
+ ret = CFDate.new(CFDate.parse_date(get_value(node)))
+ end
+
+ return ret
+ end
+ end
+end
+
+# eof
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbPlainCFPropertyList.rb b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbPlainCFPropertyList.rb
new file mode 100644
index 0000000..fa698ce
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbPlainCFPropertyList.rb
@@ -0,0 +1,199 @@
+# -*- coding: utf-8 -*-
+
+require 'strscan'
+
+module CFPropertyList
+ # XML parser
+ class PlainParser < XMLParserInterface
+ # read a XML file
+ # opts::
+ # * :file - The filename of the file to load
+ # * :data - The data to parse
+ def load(opts)
+ @doc = nil
+
+ if(opts.has_key?(:file)) then
+ File.open(opts[:file], :external_encoding => "ASCII") do |fd|
+ @doc = StringScanner.new(fd.read)
+ end
+ else
+ @doc = StringScanner.new(opts[:data])
+ end
+
+ if @doc
+ root = import_plain
+ raise CFFormatError.new('content after root object') unless @doc.eos?
+
+ return root
+ end
+
+ raise CFFormatError.new('invalid plist string or file not found')
+ end
+
+ SPACES_AND_COMMENTS = %r{((?:/\*.*?\*/)|(?://.*?$\n?)|(?:\s*))+}x
+
+ # serialize CFPropertyList object to XML
+ # opts = {}:: Specify options: :formatted - Use indention and line breaks
+ def to_str(opts={})
+ opts[:root].to_plain(self)
+ end
+
+ protected
+ def skip_whitespaces
+ @doc.skip SPACES_AND_COMMENTS
+ end
+
+ def read_dict
+ skip_whitespaces
+ hsh = {}
+
+ while not @doc.scan(/\}/)
+ key = import_plain
+ raise CFFormatError.new("invalid dictionary format") if !key
+
+ if key.is_a?(CFString)
+ key = key.value
+ elsif key.is_a?(CFInteger) or key.is_a?(CFReal)
+ key = key.value.to_s
+ else
+ raise CFFormatError.new("invalid key format")
+ end
+
+ skip_whitespaces
+
+ raise CFFormatError.new("invalid dictionary format") unless @doc.scan(/=/)
+
+ skip_whitespaces
+ val = import_plain
+
+ skip_whitespaces
+ raise CFFormatError.new("invalid dictionary format") unless @doc.scan(/;/)
+ skip_whitespaces
+
+ hsh[key] = val
+ raise CFFormatError.new("invalid dictionary format") if @doc.eos?
+ end
+
+ CFDictionary.new(hsh)
+ end
+
+ def read_array
+ skip_whitespaces
+ ary = []
+
+ while not @doc.scan(/\)/)
+ val = import_plain
+
+ return nil if not val or not val.value
+ skip_whitespaces
+
+ if not @doc.skip(/,\s*/)
+ if @doc.scan(/\)/)
+ ary << val
+ return CFArray.new(ary)
+ end
+
+ raise CFFormatError.new("invalid array format")
+ end
+
+ ary << val
+ raise CFFormatError.new("invalid array format") if @doc.eos?
+ end
+
+ CFArray.new(ary)
+ end
+
+ def escape_char
+ case @doc.matched
+ when '"'
+ '"'
+ when '\\'
+ '\\'
+ when 'a'
+ "\a"
+ when 'b'
+ "\b"
+ when 'f'
+ "\f"
+ when 'n'
+ "\n"
+ when 'v'
+ "\v"
+ when 'r'
+ "\r"
+ when 't'
+ "\t"
+ when 'U'
+ @doc.scan(/.{4}/).hex.chr('utf-8')
+ end
+ end
+
+ def read_quoted
+ str = ''
+
+ while not @doc.scan(/"/)
+ if @doc.scan(/\\/)
+ @doc.scan(/./)
+ str << escape_char
+
+ elsif @doc.eos?
+ raise CFFormatError.new("unterminated string")
+
+ else @doc.scan(/./)
+ str << @doc.matched
+ end
+ end
+
+ CFString.new(str)
+ end
+
+ def read_unquoted
+ raise CFFormatError.new("unexpected end of file") if @doc.eos?
+
+ if @doc.scan(/(\d\d\d\d)-(\d\d)-(\d\d)\s+(\d\d):(\d\d):(\d\d)(?:\s+(\+|-)(\d\d)(\d\d))?/)
+ year,month,day,hour,min,sec,pl_min,tz_hour, tz_min = @doc[1], @doc[2], @doc[3], @doc[4], @doc[5], @doc[6], @doc[7], @doc[8], @doc[9]
+ CFDate.new(Time.new(year, month, day, hour, min, sec, pl_min ? sprintf("%s%s:%s", pl_min, tz_hour, tz_min) : nil))
+
+ elsif @doc.scan(/-?\d+?\.\d+\b/)
+ CFReal.new(@doc.matched.to_f)
+
+ elsif @doc.scan(/-?\d+\b/)
+ CFInteger.new(@doc.matched.to_i)
+
+ elsif @doc.scan(/\b(true|false)\b/)
+ CFBoolean.new(@doc.matched == 'true')
+ else
+ CFString.new(@doc.scan(/\w+/))
+ end
+ end
+
+ def read_binary
+ @doc.scan(/(.*?)>/)
+
+ hex_str = @doc[1].gsub(/ /, '')
+ CFData.new([hex_str].pack("H*"), CFData::DATA_RAW)
+ end
+
+ # import the XML values
+ def import_plain
+ skip_whitespaces
+ ret = nil
+
+ if @doc.scan(/\{/) # dict
+ ret = read_dict
+ elsif @doc.scan(/\(/) # array
+ ret = read_array
+ elsif @doc.scan(/"/) # string
+ ret = read_quoted
+ elsif @doc.scan(/) # binary
+ ret = read_binary
+ else # string w/o quotes
+ ret = read_unquoted
+ end
+
+ return ret
+ end
+ end
+end
+
+# eof
diff --git a/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbREXMLParser.rb b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbREXMLParser.rb
new file mode 100644
index 0000000..225b168
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/CFPropertyList-3.0.2/lib/cfpropertylist/rbREXMLParser.rb
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+
+require 'rexml/document'
+
+module CFPropertyList
+ # XML parser
+ class ReXMLParser < ParserInterface
+ # read a XML file
+ # opts::
+ # * :file - The filename of the file to load
+ # * :data - The data to parse
+ def load(opts)
+
+ doc = nil
+ if(opts.has_key?(:file)) then
+ File.open(opts[:file], "rb") { |fd| doc = REXML::Document.new(fd) }
+ else
+ doc = REXML::Document.new(opts[:data])
+ end
+
+ if doc
+ root = doc.root.elements[1]
+ return import_xml(root)
+ end
+ rescue REXML::ParseException => e
+ raise CFFormatError.new('invalid XML: ' + e.message)
+ end
+
+ # serialize CFPropertyList object to XML
+ # opts = {}:: Specify options: :formatted - Use indention and line breaks
+ def to_str(opts={})
+ doc = REXML::Document.new
+ @doc = doc
+
+ doc.context[:attribute_quote] = :quote
+
+ doc.add_element 'plist', {'version' => '1.0'}
+ doc.root << opts[:root].to_xml(self)
+
+ formatter = if opts[:formatted] then
+ f = REXML::Formatters::Pretty.new(2)
+ f.compact = true
+ f.width = Float::INFINITY
+ f
+ else
+ REXML::Formatters::Default.new
+ end
+
+ str = formatter.write(doc.root, "")
+ str1 = "\n\n" + str + "\n"
+ str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
+
+ return str1
+ end
+
+ def new_node(name)
+ REXML::Element.new(name)
+ end
+
+ def new_text(val)
+ val
+ end
+
+ def append_node(parent, child)
+ if child.is_a?(String) then
+ parent.add_text child
+ else
+ parent.elements << child
+ end
+ parent
+ end
+
+ protected
+
+ # get the value of a DOM node
+ def get_value(n)
+ content = n.text
+
+ content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
+ content
+ end
+
+ # import the XML values
+ def import_xml(node)
+ ret = nil
+
+ case node.name
+ when 'dict'
+ hsh = Hash.new
+ key = nil
+
+ if node.has_elements? then
+ node.elements.each do |n|
+ next if n.name == '#text' # avoid a bug of libxml
+ next if n.name == '#comment'
+
+ if n.name == "key" then
+ key = get_value(n)
+ key = '' if key.nil? # REXML returns nil if key is empty
+ else
+ raise CFFormatError.new("Format error!") if key.nil?
+ hsh[key] = import_xml(n)
+ key = nil
+ end
+ end
+ end
+
+ if hsh['CF$UID'] and hsh.keys.length == 1
+ ret = CFUid.new(hsh['CF$UID'].value)
+ else
+ ret = CFDictionary.new(hsh)
+ end
+
+ when 'array'
+ ary = Array.new
+
+ if node.has_elements? then
+ node.elements.each do |n|
+ next if n.name == '#text' # avoid a bug of libxml
+ ary.push import_xml(n)
+ end
+ end
+
+ ret = CFArray.new(ary)
+
+ when 'true'
+ ret = CFBoolean.new(true)
+ when 'false'
+ ret = CFBoolean.new(false)
+ when 'real'
+ ret = CFReal.new(get_value(node).to_f)
+ when 'integer'
+ ret = CFInteger.new(get_value(node).to_i)
+ when 'string'
+ ret = CFString.new(get_value(node))
+ ret.value = '' if ret.value.nil? # REXML returns nil for empty elements' .text attribute
+ when 'data'
+ ret = CFData.new(get_value(node))
+ when 'date'
+ ret = CFDate.new(CFDate.parse_date(get_value(node)))
+ end
+
+ return ret
+ end
+ end
+end
+
+# eof
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/CHANGELOG.md b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/CHANGELOG.md
new file mode 100644
index 0000000..686b31d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/CHANGELOG.md
@@ -0,0 +1,653 @@
+## Rails 5.2.4.4 (September 09, 2020) ##
+
+* No changes.
+
+
+## Rails 5.2.4.3 (May 18, 2020) ##
+
+* [CVE-2020-8165] Deprecate Marshal.load on raw cache read in RedisCacheStore
+
+* [CVE-2020-8165] Avoid Marshal.load on raw cache value in MemCacheStore
+
+## Rails 5.2.4.1 (December 18, 2019) ##
+
+* No changes.
+
+
+## Rails 5.2.4 (November 27, 2019) ##
+
+* Make ActiveSupport::Logger Fiber-safe. Fixes #36752.
+
+ Use `Fiber.current.__id__` in `ActiveSupport::Logger#local_level=` in order
+ to make log level local to Ruby Fibers in addition to Threads.
+
+ Example:
+
+ logger = ActiveSupport::Logger.new(STDOUT)
+ logger.level = 1
+ p "Main is debug? #{logger.debug?}"
+
+ Fiber.new {
+ logger.local_level = 0
+ p "Thread is debug? #{logger.debug?}"
+ }.resume
+
+ p "Main is debug? #{logger.debug?}"
+
+ Before:
+
+ Main is debug? false
+ Thread is debug? true
+ Main is debug? true
+
+ After:
+
+ Main is debug? false
+ Thread is debug? true
+ Main is debug? false
+
+ *Alexander Varnin*
+
+
+## Rails 5.2.3 (March 27, 2019) ##
+
+* Add `ActiveSupport::HashWithIndifferentAccess#assoc`.
+
+ `assoc` can now be called with either a string or a symbol.
+
+ *Stefan Schüßler*
+
+* Fix `String#safe_constantize` throwing a `LoadError` for incorrectly cased constant references.
+
+ *Keenan Brock*
+
+* Allow Range#=== and Range#cover? on Range
+
+ `Range#cover?` can now accept a range argument like `Range#include?` and
+ `Range#===`. `Range#===` works correctly on Ruby 2.6. `Range#include?` is moved
+ into a new file, with these two methods.
+
+ *utilum*
+
+* If the same block is `included` multiple times for a Concern, an exception is no longer raised.
+
+ *Mark J. Titorenko*, *Vlad Bokov*
+
+
+## Rails 5.2.2.1 (March 11, 2019) ##
+
+* No changes.
+
+
+## Rails 5.2.2 (December 04, 2018) ##
+
+* Fix bug where `#to_options` for `ActiveSupport::HashWithIndifferentAccess`
+ would not act as alias for `#symbolize_keys`.
+
+ *Nick Weiland*
+
+* Improve the logic that detects non-autoloaded constants.
+
+ *Jan Habermann*, *Xavier Noria*
+
+* Fix bug where `URI.unescape` would fail with mixed Unicode/escaped character input:
+
+ URI.unescape("\xe3\x83\x90") # => "バ"
+ URI.unescape("%E3%83%90") # => "バ"
+ URI.unescape("\xe3\x83\x90%E3%83%90") # => Encoding::CompatibilityError
+
+ *Ashe Connor*, *Aaron Patterson*
+
+
+## Rails 5.2.1.1 (November 27, 2018) ##
+
+* No changes.
+
+
+## Rails 5.2.1 (August 07, 2018) ##
+
+* Redis cache store: `delete_matched` no longer blocks the Redis server.
+ (Switches from evaled Lua to a batched SCAN + DEL loop.)
+
+ *Gleb Mazovetskiy*
+
+* Fix bug where `ActiveSupport::Timezone.all` would fail when tzinfo data for
+ any timezone defined in `ActiveSupport::TimeZone::MAPPING` is missing.
+
+ *Dominik Sander*
+
+* Fix bug where `ActiveSupport::Cache` will massively inflate the storage
+ size when compression is enabled (which is true by default). This patch
+ does not attempt to repair existing data: please manually flush the cache
+ to clear out the problematic entries.
+
+ *Godfrey Chan*
+
+* Fix `ActiveSupport::Cache#read_multi` bug with local cache enabled that was
+ returning instances of `ActiveSupport::Cache::Entry` instead of the raw values.
+
+ *Jason Lee*
+
+
+## Rails 5.2.0 (April 09, 2018) ##
+
+* Caching: MemCache and Redis `read_multi` and `fetch_multi` speedup.
+ Read from the local in-memory cache before consulting the backend.
+
+ *Gabriel Sobrinho*
+
+* Return all mappings for a timezone identifier in `country_zones`.
+
+ Some timezones like `Europe/London` have multiple mappings in
+ `ActiveSupport::TimeZone::MAPPING` so return all of them instead
+ of the first one found by using `Hash#value`. e.g:
+
+ # Before
+ ActiveSupport::TimeZone.country_zones("GB") # => ["Edinburgh"]
+
+ # After
+ ActiveSupport::TimeZone.country_zones("GB") # => ["Edinburgh", "London"]
+
+ Fixes #31668.
+
+ *Andrew White*
+
+* Add support for connection pooling on RedisCacheStore.
+
+ *fatkodima*
+
+* Support hash as first argument in `assert_difference`. This allows to specify multiple
+ numeric differences in the same assertion.
+
+ assert_difference ->{ Article.count } => 1, ->{ Post.count } => 2
+
+ *Julien Meichelbeck*
+
+* Add missing instrumentation for `read_multi` in `ActiveSupport::Cache::Store`.
+
+ *Ignatius Reza Lesmana*
+
+* `assert_changes` will always assert that the expression changes,
+ regardless of `from:` and `to:` argument combinations.
+
+ *Daniel Ma*
+
+* Use SHA-1 to generate non-sensitive digests, such as the ETag header.
+
+ Enabled by default for new apps; upgrading apps can opt in by setting
+ `config.active_support.use_sha1_digests = true`.
+
+ *Dmitri Dolguikh*, *Eugene Kenny*
+
+* Changed default behaviour of `ActiveSupport::SecurityUtils.secure_compare`,
+ to make it not leak length information even for variable length string.
+
+ Renamed old `ActiveSupport::SecurityUtils.secure_compare` to `fixed_length_secure_compare`,
+ and started raising `ArgumentError` in case of length mismatch of passed strings.
+
+ *Vipul A M*
+
+* Make `ActiveSupport::TimeZone.all` return only time zones that are in
+ `ActiveSupport::TimeZone::MAPPING`.
+
+ Fixes #7245.
+
+ *Chris LaRose*
+
+* MemCacheStore: Support expiring counters.
+
+ Pass `expires_in: [seconds]` to `#increment` and `#decrement` options
+ to set the Memcached TTL (time-to-live) if the counter doesn't exist.
+ If the counter exists, Memcached doesn't extend its expiry when it's
+ incremented or decremented.
+
+ ```
+ Rails.cache.increment("my_counter", 1, expires_in: 2.minutes)
+ ```
+
+ *Takumasa Ochi*
+
+* Handle `TZInfo::AmbiguousTime` errors.
+
+ Make `ActiveSupport::TimeWithZone` match Ruby's handling of ambiguous
+ times by choosing the later period, e.g.
+
+ Ruby:
+ ```
+ ENV["TZ"] = "Europe/Moscow"
+ Time.local(2014, 10, 26, 1, 0, 0) # => 2014-10-26 01:00:00 +0300
+ ```
+
+ Before:
+ ```
+ >> "2014-10-26 01:00:00".in_time_zone("Moscow")
+ TZInfo::AmbiguousTime: 26/10/2014 01:00 is an ambiguous local time.
+ ```
+
+ After:
+ ```
+ >> "2014-10-26 01:00:00".in_time_zone("Moscow")
+ => Sun, 26 Oct 2014 01:00:00 MSK +03:00
+ ```
+
+ Fixes #17395.
+
+ *Andrew White*
+
+* Redis cache store.
+
+ ```
+ # Defaults to `redis://localhost:6379/0`. Only use for dev/test.
+ config.cache_store = :redis_cache_store
+
+ # Supports all common cache store options (:namespace, :compress,
+ # :compress_threshold, :expires_in, :race_condition_ttl) and all
+ # Redis options.
+ cache_password = Rails.application.secrets.redis_cache_password
+ config.cache_store = :redis_cache_store, driver: :hiredis,
+ namespace: 'myapp-cache', compress: true, timeout: 1,
+ url: "redis://:#{cache_password}@myapp-cache-1:6379/0"
+
+ # Supports Redis::Distributed with multiple hosts
+ config.cache_store = :redis_cache_store, driver: :hiredis
+ namespace: 'myapp-cache', compress: true,
+ url: %w[
+ redis://myapp-cache-1:6379/0
+ redis://myapp-cache-1:6380/0
+ redis://myapp-cache-2:6379/0
+ redis://myapp-cache-2:6380/0
+ redis://myapp-cache-3:6379/0
+ redis://myapp-cache-3:6380/0
+ ]
+
+ # Or pass a builder block
+ config.cache_store = :redis_cache_store,
+ namespace: 'myapp-cache', compress: true,
+ redis: -> { Redis.new … }
+ ```
+
+ Deployment note: Take care to use a *dedicated Redis cache* rather
+ than pointing this at your existing Redis server. It won't cope well
+ with mixed usage patterns and it won't expire cache entries by default.
+
+ Redis cache server setup guide: https://redis.io/topics/lru-cache
+
+ *Jeremy Daer*
+
+* Cache: Enable compression by default for values > 1kB.
+
+ Compression has long been available, but opt-in and at a 16kB threshold.
+ It wasn't enabled by default due to CPU cost. Today it's cheap and typical
+ cache data is eminently compressible, such as HTML or JSON fragments.
+ Compression dramatically reduces Memcached/Redis mem usage, which means
+ the same cache servers can store more data, which means higher hit rates.
+
+ To disable compression, pass `compress: false` to the initializer.
+
+ *Jeremy Daer*
+
+* Allow `Range#include?` on TWZ ranges.
+
+ In #11474 we prevented TWZ ranges being iterated over which matched
+ Ruby's handling of Time ranges and as a consequence `include?`
+ stopped working with both Time ranges and TWZ ranges. However in
+ ruby/ruby@b061634 support was added for `include?` to use `cover?`
+ for 'linear' objects. Since we have no way of making Ruby consider
+ TWZ instances as 'linear' we have to override `Range#include?`.
+
+ Fixes #30799.
+
+ *Andrew White*
+
+* Fix acronym support in `humanize`.
+
+ Acronym inflections are stored with lowercase keys in the hash but
+ the match wasn't being lowercased before being looked up in the hash.
+ This shouldn't have any performance impact because before it would
+ fail to find the acronym and perform the `downcase` operation anyway.
+
+ Fixes #31052.
+
+ *Andrew White*
+
+* Add same method signature for `Time#prev_year` and `Time#next_year`
+ in accordance with `Date#prev_year`, `Date#next_year`.
+
+ Allows pass argument for `Time#prev_year` and `Time#next_year`.
+
+ Before:
+ ```
+ Time.new(2017, 9, 16, 17, 0).prev_year # => 2016-09-16 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).prev_year(1)
+ # => ArgumentError: wrong number of arguments (given 1, expected 0)
+
+ Time.new(2017, 9, 16, 17, 0).next_year # => 2018-09-16 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).next_year(1)
+ # => ArgumentError: wrong number of arguments (given 1, expected 0)
+ ```
+
+ After:
+ ```
+ Time.new(2017, 9, 16, 17, 0).prev_year # => 2016-09-16 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).prev_year(1) # => 2016-09-16 17:00:00 +0300
+
+ Time.new(2017, 9, 16, 17, 0).next_year # => 2018-09-16 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).next_year(1) # => 2018-09-16 17:00:00 +0300
+ ```
+
+ *bogdanvlviv*
+
+* Add same method signature for `Time#prev_month` and `Time#next_month`
+ in accordance with `Date#prev_month`, `Date#next_month`.
+
+ Allows pass argument for `Time#prev_month` and `Time#next_month`.
+
+ Before:
+ ```
+ Time.new(2017, 9, 16, 17, 0).prev_month # => 2017-08-16 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).prev_month(1)
+ # => ArgumentError: wrong number of arguments (given 1, expected 0)
+
+ Time.new(2017, 9, 16, 17, 0).next_month # => 2017-10-16 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).next_month(1)
+ # => ArgumentError: wrong number of arguments (given 1, expected 0)
+ ```
+
+ After:
+ ```
+ Time.new(2017, 9, 16, 17, 0).prev_month # => 2017-08-16 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).prev_month(1) # => 2017-08-16 17:00:00 +0300
+
+ Time.new(2017, 9, 16, 17, 0).next_month # => 2017-10-16 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).next_month(1) # => 2017-10-16 17:00:00 +0300
+ ```
+
+ *bogdanvlviv*
+
+* Add same method signature for `Time#prev_day` and `Time#next_day`
+ in accordance with `Date#prev_day`, `Date#next_day`.
+
+ Allows pass argument for `Time#prev_day` and `Time#next_day`.
+
+ Before:
+ ```
+ Time.new(2017, 9, 16, 17, 0).prev_day # => 2017-09-15 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).prev_day(1)
+ # => ArgumentError: wrong number of arguments (given 1, expected 0)
+
+ Time.new(2017, 9, 16, 17, 0).next_day # => 2017-09-17 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).next_day(1)
+ # => ArgumentError: wrong number of arguments (given 1, expected 0)
+ ```
+
+ After:
+ ```
+ Time.new(2017, 9, 16, 17, 0).prev_day # => 2017-09-15 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).prev_day(1) # => 2017-09-15 17:00:00 +0300
+
+ Time.new(2017, 9, 16, 17, 0).next_day # => 2017-09-17 17:00:00 +0300
+ Time.new(2017, 9, 16, 17, 0).next_day(1) # => 2017-09-17 17:00:00 +0300
+ ```
+
+ *bogdanvlviv*
+
+* `IO#to_json` now returns the `to_s` representation, rather than
+ attempting to convert to an array. This fixes a bug where `IO#to_json`
+ would raise an `IOError` when called on an unreadable object.
+
+ Fixes #26132.
+
+ *Paul Kuruvilla*
+
+* Remove deprecated `halt_callback_chains_on_return_false` option.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `:if` and `:unless` string filter for callbacks.
+
+ *Rafael Mendonça França*
+
+* `Hash#slice` now falls back to Ruby 2.5+'s built-in definition if defined.
+
+ *Akira Matsuda*
+
+* Deprecate `secrets.secret_token`.
+
+ The architecture for secrets had a big upgrade between Rails 3 and Rails 4,
+ when the default changed from using `secret_token` to `secret_key_base`.
+
+ `secret_token` has been soft deprecated in documentation for four years
+ but is still in place to support apps created before Rails 4.
+ Deprecation warnings have been added to help developers upgrade their
+ applications to `secret_key_base`.
+
+ *claudiob*, *Kasper Timm Hansen*
+
+* Return an instance of `HashWithIndifferentAccess` from `HashWithIndifferentAccess#transform_keys`.
+
+ *Yuji Yaginuma*
+
+* Add key rotation support to `MessageEncryptor` and `MessageVerifier`.
+
+ This change introduces a `rotate` method to both the `MessageEncryptor` and
+ `MessageVerifier` classes. This method accepts the same arguments and
+ options as the given classes' constructor. The `encrypt_and_verify` method
+ for `MessageEncryptor` and the `verified` method for `MessageVerifier` also
+ accept an optional keyword argument `:on_rotation` block which is called
+ when a rotated instance is used to decrypt or verify the message.
+
+ *Michael J Coyne*
+
+* Deprecate `Module#reachable?` method.
+
+ *bogdanvlviv*
+
+* Add `config/credentials.yml.enc` to store production app secrets.
+
+ Allows saving any authentication credentials for third party services
+ directly in repo encrypted with `config/master.key` or `ENV["RAILS_MASTER_KEY"]`.
+
+ This will eventually replace `Rails.application.secrets` and the encrypted
+ secrets introduced in Rails 5.1.
+
+ *DHH*, *Kasper Timm Hansen*
+
+* Add `ActiveSupport::EncryptedFile` and `ActiveSupport::EncryptedConfiguration`.
+
+ Allows for stashing encrypted files or configuration directly in repo by
+ encrypting it with a key.
+
+ Backs the new credentials setup above, but can also be used independently.
+
+ *DHH*, *Kasper Timm Hansen*
+
+* `Module#delegate_missing_to` now raises `DelegationError` if target is nil,
+ similar to `Module#delegate`.
+
+ *Anton Khamets*
+
+* Update `String#camelize` to provide feedback when wrong option is passed.
+
+ `String#camelize` was returning nil without any feedback when an
+ invalid option was passed as a parameter.
+
+ Previously:
+
+ 'one_two'.camelize(true)
+ # => nil
+
+ Now:
+
+ 'one_two'.camelize(true)
+ # => ArgumentError: Invalid option, use either :upper or :lower.
+
+ *Ricardo Díaz*
+
+* Fix modulo operations involving durations.
+
+ Rails 5.1 introduced `ActiveSupport::Duration::Scalar` as a wrapper
+ around numeric values as a way of ensuring a duration was the outcome of
+ an expression. However, the implementation was missing support for modulo
+ operations. This support has now been added and should result in a duration
+ being returned from expressions involving modulo operations.
+
+ Prior to Rails 5.1:
+
+ 5.minutes % 2.minutes
+ # => 60
+
+ Now:
+
+ 5.minutes % 2.minutes
+ # => 1 minute
+
+ Fixes #29603 and #29743.
+
+ *Sayan Chakraborty*, *Andrew White*
+
+* Fix division where a duration is the denominator.
+
+ PR #29163 introduced a change in behavior when a duration was the denominator
+ in a calculation - this was incorrect as dividing by a duration should always
+ return a `Numeric`. The behavior of previous versions of Rails has been restored.
+
+ Fixes #29592.
+
+ *Andrew White*
+
+* Add purpose and expiry support to `ActiveSupport::MessageVerifier` and
+ `ActiveSupport::MessageEncryptor`.
+
+ For instance, to ensure a message is only usable for one intended purpose:
+
+ token = @verifier.generate("x", purpose: :shipping)
+
+ @verifier.verified(token, purpose: :shipping) # => "x"
+ @verifier.verified(token) # => nil
+
+ Or make it expire after a set time:
+
+ @verifier.generate("x", expires_in: 1.month)
+ @verifier.generate("y", expires_at: Time.now.end_of_year)
+
+ Showcased with `ActiveSupport::MessageVerifier`, but works the same for
+ `ActiveSupport::MessageEncryptor`'s `encrypt_and_sign` and `decrypt_and_verify`.
+
+ Pull requests: #29599, #29854
+
+ *Assain Jaleel*
+
+* Make the order of `Hash#reverse_merge!` consistent with `HashWithIndifferentAccess`.
+
+ *Erol Fornoles*
+
+* Add `freeze_time` helper which freezes time to `Time.now` in tests.
+
+ *Prathamesh Sonpatki*
+
+* Default `ActiveSupport::MessageEncryptor` to use AES 256 GCM encryption.
+
+ On for new Rails 5.2 apps. Upgrading apps can find the config as a new
+ framework default.
+
+ *Assain Jaleel*
+
+* Cache: `write_multi`.
+
+ Rails.cache.write_multi foo: 'bar', baz: 'qux'
+
+ Plus faster fetch_multi with stores that implement `write_multi_entries`.
+ Keys that aren't found may be written to the cache store in one shot
+ instead of separate writes.
+
+ The default implementation simply calls `write_entry` for each entry.
+ Stores may override if they're capable of one-shot bulk writes, like
+ Redis `MSET`.
+
+ *Jeremy Daer*
+
+* Add default option to module and class attribute accessors.
+
+ mattr_accessor :settings, default: {}
+
+ Works for `mattr_reader`, `mattr_writer`, `cattr_accessor`, `cattr_reader`,
+ and `cattr_writer` as well.
+
+ *Genadi Samokovarov*
+
+* Add `Date#prev_occurring` and `Date#next_occurring` to return specified next/previous occurring day of week.
+
+ *Shota Iguchi*
+
+* Add default option to `class_attribute`.
+
+ Before:
+
+ class_attribute :settings
+ self.settings = {}
+
+ Now:
+
+ class_attribute :settings, default: {}
+
+ *DHH*
+
+* `#singularize` and `#pluralize` now respect uncountables for the specified locale.
+
+ *Eilis Hamilton*
+
+* Add `ActiveSupport::CurrentAttributes` to provide a thread-isolated attributes singleton.
+ Primary use case is keeping all the per-request attributes easily available to the whole system.
+
+ *DHH*
+
+* Fix implicit coercion calculations with scalars and durations.
+
+ Previously, calculations where the scalar is first would be converted to a duration
+ of seconds, but this causes issues with dates being converted to times, e.g:
+
+ Time.zone = "Beijing" # => Asia/Shanghai
+ date = Date.civil(2017, 5, 20) # => Mon, 20 May 2017
+ 2 * 1.day # => 172800 seconds
+ date + 2 * 1.day # => Mon, 22 May 2017 00:00:00 CST +08:00
+
+ Now, the `ActiveSupport::Duration::Scalar` calculation methods will try to maintain
+ the part structure of the duration where possible, e.g:
+
+ Time.zone = "Beijing" # => Asia/Shanghai
+ date = Date.civil(2017, 5, 20) # => Mon, 20 May 2017
+ 2 * 1.day # => 2 days
+ date + 2 * 1.day # => Mon, 22 May 2017
+
+ Fixes #29160, #28970.
+
+ *Andrew White*
+
+* Add support for versioned cache entries. This enables the cache stores to recycle cache keys, greatly saving
+ on storage in cases with frequent churn. Works together with the separation of `#cache_key` and `#cache_version`
+ in Active Record and its use in Action Pack's fragment caching.
+
+ *DHH*
+
+* Pass gem name and deprecation horizon to deprecation notifications.
+
+ *Willem van Bergen*
+
+* Add support for `:offset` and `:zone` to `ActiveSupport::TimeWithZone#change`.
+
+ *Andrew White*
+
+* Add support for `:offset` to `Time#change`.
+
+ Fixes #28723.
+
+ *Andrew White*
+
+* Add `fetch_values` for `HashWithIndifferentAccess`.
+
+ The method was originally added to `Hash` in Ruby 2.3.0.
+
+ *Josh Pencheon*
+
+
+Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/activesupport/CHANGELOG.md) for previous changes.
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/MIT-LICENSE b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/MIT-LICENSE
new file mode 100644
index 0000000..8f769c0
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2005-2018 David Heinemeier Hansson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/README.rdoc b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/README.rdoc
new file mode 100644
index 0000000..bbe25ac
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/README.rdoc
@@ -0,0 +1,39 @@
+= Active Support -- Utility classes and Ruby extensions from Rails
+
+Active Support is a collection of utility classes and standard library
+extensions that were found useful for the Rails framework. These additions
+reside in this package so they can be loaded as needed in Ruby projects
+outside of Rails.
+
+
+== Download and installation
+
+The latest version of Active Support can be installed with RubyGems:
+
+ $ gem install activesupport
+
+Source code can be downloaded as part of the Rails project on GitHub:
+
+* https://github.com/rails/rails/tree/5-2-stable/activesupport
+
+
+== License
+
+Active Support is released under the MIT license:
+
+* https://opensource.org/licenses/MIT
+
+
+== Support
+
+API documentation is at:
+
+* http://api.rubyonrails.org
+
+Bug reports for the Ruby on Rails project can be filed here:
+
+* https://github.com/rails/rails/issues
+
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support.rb
new file mode 100644
index 0000000..a4fb697
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+#--
+# Copyright (c) 2005-2018 David Heinemeier Hansson
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#++
+
+require "securerandom"
+require "active_support/dependencies/autoload"
+require "active_support/version"
+require "active_support/logger"
+require "active_support/lazy_load_hooks"
+require "active_support/core_ext/date_and_time/compatibility"
+
+module ActiveSupport
+ extend ActiveSupport::Autoload
+
+ autoload :Concern
+ autoload :CurrentAttributes
+ autoload :Dependencies
+ autoload :DescendantsTracker
+ autoload :ExecutionWrapper
+ autoload :Executor
+ autoload :FileUpdateChecker
+ autoload :EventedFileUpdateChecker
+ autoload :LogSubscriber
+ autoload :Notifications
+ autoload :Reloader
+
+ eager_autoload do
+ autoload :BacktraceCleaner
+ autoload :ProxyObject
+ autoload :Benchmarkable
+ autoload :Cache
+ autoload :Callbacks
+ autoload :Configurable
+ autoload :Deprecation
+ autoload :Digest
+ autoload :Gzip
+ autoload :Inflector
+ autoload :JSON
+ autoload :KeyGenerator
+ autoload :MessageEncryptor
+ autoload :MessageVerifier
+ autoload :Multibyte
+ autoload :NumberHelper
+ autoload :OptionMerger
+ autoload :OrderedHash
+ autoload :OrderedOptions
+ autoload :StringInquirer
+ autoload :TaggedLogging
+ autoload :XmlMini
+ autoload :ArrayInquirer
+ end
+
+ autoload :Rescuable
+ autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
+ autoload :TestCase
+
+ def self.eager_load!
+ super
+
+ NumberHelper.eager_load!
+ end
+
+ cattr_accessor :test_order # :nodoc:
+
+ def self.to_time_preserves_timezone
+ DateAndTime::Compatibility.preserve_timezone
+ end
+
+ def self.to_time_preserves_timezone=(value)
+ DateAndTime::Compatibility.preserve_timezone = value
+ end
+end
+
+autoload :I18n, "active_support/i18n"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/all.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/all.rb
new file mode 100644
index 0000000..4adf446
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/all.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+require "active_support"
+require "active_support/time"
+require "active_support/core_ext"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/array_inquirer.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/array_inquirer.rb
new file mode 100644
index 0000000..b2b9e9c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/array_inquirer.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
+ # its string-like contents:
+ #
+ # variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
+ #
+ # variants.phone? # => true
+ # variants.tablet? # => true
+ # variants.desktop? # => false
+ class ArrayInquirer < Array
+ # Passes each element of +candidates+ collection to ArrayInquirer collection.
+ # The method returns true if any element from the ArrayInquirer collection
+ # is equal to the stringified or symbolized form of any element in the +candidates+ collection.
+ #
+ # If +candidates+ collection is not given, method returns true.
+ #
+ # variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
+ #
+ # variants.any? # => true
+ # variants.any?(:phone, :tablet) # => true
+ # variants.any?('phone', 'desktop') # => true
+ # variants.any?(:desktop, :watch) # => false
+ def any?(*candidates)
+ if candidates.none?
+ super
+ else
+ candidates.any? do |candidate|
+ include?(candidate.to_sym) || include?(candidate.to_s)
+ end
+ end
+ end
+
+ private
+ def respond_to_missing?(name, include_private = false)
+ (name[-1] == "?") || super
+ end
+
+ def method_missing(name, *args)
+ if name[-1] == "?"
+ any?(name[0..-2])
+ else
+ super
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/backtrace_cleaner.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/backtrace_cleaner.rb
new file mode 100644
index 0000000..16dd733
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/backtrace_cleaner.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # Backtraces often include many lines that are not relevant for the context
+ # under review. This makes it hard to find the signal amongst the backtrace
+ # noise, and adds debugging time. With a BacktraceCleaner, filters and
+ # silencers are used to remove the noisy lines, so that only the most relevant
+ # lines remain.
+ #
+ # Filters are used to modify lines of data, while silencers are used to remove
+ # lines entirely. The typical filter use case is to remove lengthy path
+ # information from the start of each line, and view file paths relevant to the
+ # app directory instead of the file system root. The typical silencer use case
+ # is to exclude the output of a noisy library from the backtrace, so that you
+ # can focus on the rest.
+ #
+ # bc = ActiveSupport::BacktraceCleaner.new
+ # bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
+ # bc.add_silencer { |line| line =~ /puma|rubygems/ } # skip any lines from puma or rubygems
+ # bc.clean(exception.backtrace) # perform the cleanup
+ #
+ # To reconfigure an existing BacktraceCleaner (like the default one in Rails)
+ # and show as much data as possible, you can always call
+ # BacktraceCleaner#remove_silencers!, which will restore the
+ # backtrace to a pristine state. If you need to reconfigure an existing
+ # BacktraceCleaner so that it does not filter or modify the paths of any lines
+ # of the backtrace, you can call BacktraceCleaner#remove_filters!
+ # These two methods will give you a completely untouched backtrace.
+ #
+ # Inspired by the Quiet Backtrace gem by thoughtbot.
+ class BacktraceCleaner
+ def initialize
+ @filters, @silencers = [], []
+ end
+
+ # Returns the backtrace after all filters and silencers have been run
+ # against it. Filters run first, then silencers.
+ def clean(backtrace, kind = :silent)
+ filtered = filter_backtrace(backtrace)
+
+ case kind
+ when :silent
+ silence(filtered)
+ when :noise
+ noise(filtered)
+ else
+ filtered
+ end
+ end
+ alias :filter :clean
+
+ # Adds a filter from the block provided. Each line in the backtrace will be
+ # mapped against this filter.
+ #
+ # # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb"
+ # backtrace_cleaner.add_filter { |line| line.gsub(Rails.root, '') }
+ def add_filter(&block)
+ @filters << block
+ end
+
+ # Adds a silencer from the block provided. If the silencer returns +true+
+ # for a given line, it will be excluded from the clean backtrace.
+ #
+ # # Will reject all lines that include the word "puma", like "/gems/puma/server.rb" or "/app/my_puma_server/rb"
+ # backtrace_cleaner.add_silencer { |line| line =~ /puma/ }
+ def add_silencer(&block)
+ @silencers << block
+ end
+
+ # Removes all silencers, but leaves in the filters. Useful if your
+ # context of debugging suddenly expands as you suspect a bug in one of
+ # the libraries you use.
+ def remove_silencers!
+ @silencers = []
+ end
+
+ # Removes all filters, but leaves in the silencers. Useful if you suddenly
+ # need to see entire filepaths in the backtrace that you had already
+ # filtered out.
+ def remove_filters!
+ @filters = []
+ end
+
+ private
+ def filter_backtrace(backtrace)
+ @filters.each do |f|
+ backtrace = backtrace.map { |line| f.call(line) }
+ end
+
+ backtrace
+ end
+
+ def silence(backtrace)
+ @silencers.each do |s|
+ backtrace = backtrace.reject { |line| s.call(line) }
+ end
+
+ backtrace
+ end
+
+ def noise(backtrace)
+ backtrace - silence(backtrace)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/benchmarkable.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/benchmarkable.rb
new file mode 100644
index 0000000..f481d68
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/benchmarkable.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/benchmark"
+require "active_support/core_ext/hash/keys"
+
+module ActiveSupport
+ module Benchmarkable
+ # Allows you to measure the execution time of a block in a template and
+ # records the result to the log. Wrap this block around expensive operations
+ # or possible bottlenecks to get a time reading for the operation. For
+ # example, let's say you thought your file processing method was taking too
+ # long; you could wrap it in a benchmark block.
+ #
+ # <% benchmark 'Process data files' do %>
+ # <%= expensive_files_operation %>
+ # <% end %>
+ #
+ # That would add something like "Process data files (345.2ms)" to the log,
+ # which you can then use to compare timings when optimizing your code.
+ #
+ # You may give an optional logger level (:debug, :info,
+ # :warn, :error) as the :level option. The
+ # default logger level value is :info.
+ #
+ # <% benchmark 'Low-level files', level: :debug do %>
+ # <%= lowlevel_files_operation %>
+ # <% end %>
+ #
+ # Finally, you can pass true as the third argument to silence all log
+ # activity (other than the timing information) from inside the block. This
+ # is great for boiling down a noisy block to just a single statement that
+ # produces one log line:
+ #
+ # <% benchmark 'Process data files', level: :info, silence: true do %>
+ # <%= expensive_and_chatty_files_operation %>
+ # <% end %>
+ def benchmark(message = "Benchmarking", options = {})
+ if logger
+ options.assert_valid_keys(:level, :silence)
+ options[:level] ||= :info
+
+ result = nil
+ ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
+ logger.send(options[:level], "%s (%.1fms)" % [ message, ms ])
+ result
+ else
+ yield
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/builder.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/builder.rb
new file mode 100644
index 0000000..3fa7e6b
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/builder.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+begin
+ require "builder"
+rescue LoadError => e
+ $stderr.puts "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install"
+ raise e
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache.rb
new file mode 100644
index 0000000..d06a497
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache.rb
@@ -0,0 +1,808 @@
+# frozen_string_literal: true
+
+require "zlib"
+require "active_support/core_ext/array/extract_options"
+require "active_support/core_ext/array/wrap"
+require "active_support/core_ext/module/attribute_accessors"
+require "active_support/core_ext/numeric/bytes"
+require "active_support/core_ext/numeric/time"
+require "active_support/core_ext/object/to_param"
+require "active_support/core_ext/string/inflections"
+
+module ActiveSupport
+ # See ActiveSupport::Cache::Store for documentation.
+ module Cache
+ autoload :FileStore, "active_support/cache/file_store"
+ autoload :MemoryStore, "active_support/cache/memory_store"
+ autoload :MemCacheStore, "active_support/cache/mem_cache_store"
+ autoload :NullStore, "active_support/cache/null_store"
+ autoload :RedisCacheStore, "active_support/cache/redis_cache_store"
+
+ # These options mean something to all cache implementations. Individual cache
+ # implementations may support additional options.
+ UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
+
+ module Strategy
+ autoload :LocalCache, "active_support/cache/strategy/local_cache"
+ end
+
+ class << self
+ # Creates a new Store object according to the given options.
+ #
+ # If no arguments are passed to this method, then a new
+ # ActiveSupport::Cache::MemoryStore object will be returned.
+ #
+ # If you pass a Symbol as the first argument, then a corresponding cache
+ # store class under the ActiveSupport::Cache namespace will be created.
+ # For example:
+ #
+ # ActiveSupport::Cache.lookup_store(:memory_store)
+ # # => returns a new ActiveSupport::Cache::MemoryStore object
+ #
+ # ActiveSupport::Cache.lookup_store(:mem_cache_store)
+ # # => returns a new ActiveSupport::Cache::MemCacheStore object
+ #
+ # Any additional arguments will be passed to the corresponding cache store
+ # class's constructor:
+ #
+ # ActiveSupport::Cache.lookup_store(:file_store, '/tmp/cache')
+ # # => same as: ActiveSupport::Cache::FileStore.new('/tmp/cache')
+ #
+ # If the first argument is not a Symbol, then it will simply be returned:
+ #
+ # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
+ # # => returns MyOwnCacheStore.new
+ def lookup_store(*store_option)
+ store, *parameters = *Array.wrap(store_option).flatten
+
+ case store
+ when Symbol
+ retrieve_store_class(store).new(*parameters)
+ when nil
+ ActiveSupport::Cache::MemoryStore.new
+ else
+ store
+ end
+ end
+
+ # Expands out the +key+ argument into a key that can be used for the
+ # cache store. Optionally accepts a namespace, and all keys will be
+ # scoped within that namespace.
+ #
+ # If the +key+ argument provided is an array, or responds to +to_a+, then
+ # each of elements in the array will be turned into parameters/keys and
+ # concatenated into a single key. For example:
+ #
+ # ActiveSupport::Cache.expand_cache_key([:foo, :bar]) # => "foo/bar"
+ # ActiveSupport::Cache.expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
+ #
+ # The +key+ argument can also respond to +cache_key+ or +to_param+.
+ def expand_cache_key(key, namespace = nil)
+ expanded_cache_key = (namespace ? "#{namespace}/" : "").dup
+
+ if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
+ expanded_cache_key << "#{prefix}/"
+ end
+
+ expanded_cache_key << retrieve_cache_key(key)
+ expanded_cache_key
+ end
+
+ private
+ def retrieve_cache_key(key)
+ case
+ when key.respond_to?(:cache_key_with_version) then key.cache_key_with_version
+ when key.respond_to?(:cache_key) then key.cache_key
+ when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
+ when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
+ else key.to_param
+ end.to_s
+ end
+
+ # Obtains the specified cache store class, given the name of the +store+.
+ # Raises an error when the store class cannot be found.
+ def retrieve_store_class(store)
+ # require_relative cannot be used here because the class might be
+ # provided by another gem, like redis-activesupport for example.
+ require "active_support/cache/#{store}"
+ rescue LoadError => e
+ raise "Could not find cache store adapter for #{store} (#{e})"
+ else
+ ActiveSupport::Cache.const_get(store.to_s.camelize)
+ end
+ end
+
+ # An abstract cache store class. There are multiple cache store
+ # implementations, each having its own additional features. See the classes
+ # under the ActiveSupport::Cache module, e.g.
+ # ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most
+ # popular cache store for large production websites.
+ #
+ # Some implementations may not support all methods beyond the basic cache
+ # methods of +fetch+, +write+, +read+, +exist?+, and +delete+.
+ #
+ # ActiveSupport::Cache::Store can store any serializable Ruby object.
+ #
+ # cache = ActiveSupport::Cache::MemoryStore.new
+ #
+ # cache.read('city') # => nil
+ # cache.write('city', "Duckburgh")
+ # cache.read('city') # => "Duckburgh"
+ #
+ # Keys are always translated into Strings and are case sensitive. When an
+ # object is specified as a key and has a +cache_key+ method defined, this
+ # method will be called to define the key. Otherwise, the +to_param+
+ # method will be called. Hashes and Arrays can also be used as keys. The
+ # elements will be delimited by slashes, and the elements within a Hash
+ # will be sorted by key so they are consistent.
+ #
+ # cache.read('city') == cache.read(:city) # => true
+ #
+ # Nil values can be cached.
+ #
+ # If your cache is on a shared infrastructure, you can define a namespace
+ # for your cache entries. If a namespace is defined, it will be prefixed on
+ # to every key. The namespace can be either a static value or a Proc. If it
+ # is a Proc, it will be invoked when each key is evaluated so that you can
+ # use application logic to invalidate keys.
+ #
+ # cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
+ # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
+ #
+ # Cached data larger than 1kB are compressed by default. To turn off
+ # compression, pass compress: false to the initializer or to
+ # individual +fetch+ or +write+ method calls. The 1kB compression
+ # threshold is configurable with the :compress_threshold option,
+ # specified in bytes.
+ class Store
+ cattr_accessor :logger, instance_writer: true
+
+ attr_reader :silence, :options
+ alias :silence? :silence
+
+ class << self
+ private
+ def retrieve_pool_options(options)
+ {}.tap do |pool_options|
+ pool_options[:size] = options.delete(:pool_size) if options[:pool_size]
+ pool_options[:timeout] = options.delete(:pool_timeout) if options[:pool_timeout]
+ end
+ end
+
+ def ensure_connection_pool_added!
+ require "connection_pool"
+ rescue LoadError => e
+ $stderr.puts "You don't have connection_pool installed in your application. Please add it to your Gemfile and run bundle install"
+ raise e
+ end
+ end
+
+ # Creates a new cache. The options will be passed to any write method calls
+ # except for :namespace which can be used to set the global
+ # namespace for the cache.
+ def initialize(options = nil)
+ @options = options ? options.dup : {}
+ end
+
+ # Silences the logger.
+ def silence!
+ @silence = true
+ self
+ end
+
+ # Silences the logger within a block.
+ def mute
+ previous_silence, @silence = defined?(@silence) && @silence, true
+ yield
+ ensure
+ @silence = previous_silence
+ end
+
+ # Fetches data from the cache, using the given key. If there is data in
+ # the cache with the given key, then that data is returned.
+ #
+ # If there is no such data in the cache (a cache miss), then +nil+ will be
+ # returned. However, if a block has been passed, that block will be passed
+ # the key and executed in the event of a cache miss. The return value of the
+ # block will be written to the cache under the given cache key, and that
+ # return value will be returned.
+ #
+ # cache.write('today', 'Monday')
+ # cache.fetch('today') # => "Monday"
+ #
+ # cache.fetch('city') # => nil
+ # cache.fetch('city') do
+ # 'Duckburgh'
+ # end
+ # cache.fetch('city') # => "Duckburgh"
+ #
+ # You may also specify additional options via the +options+ argument.
+ # Setting force: true forces a cache "miss," meaning we treat
+ # the cache value as missing even if it's present. Passing a block is
+ # required when +force+ is true so this always results in a cache write.
+ #
+ # cache.write('today', 'Monday')
+ # cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday'
+ # cache.fetch('today', force: true) # => ArgumentError
+ #
+ # The +:force+ option is useful when you're calling some other method to
+ # ask whether you should force a cache write. Otherwise, it's clearer to
+ # just call Cache#write.
+ #
+ # Setting compress: false disables compression of the cache entry.
+ #
+ # Setting :expires_in will set an expiration time on the cache.
+ # All caches support auto-expiring content after a specified number of
+ # seconds. This value can be specified as an option to the constructor
+ # (in which case all entries will be affected), or it can be supplied to
+ # the +fetch+ or +write+ method to effect just one entry.
+ #
+ # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
+ # cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
+ #
+ # Setting :version verifies the cache stored under name
+ # is of the same version. nil is returned on mismatches despite contents.
+ # This feature is used to support recyclable cache keys.
+ #
+ # Setting :race_condition_ttl is very useful in situations where
+ # a cache entry is used very frequently and is under heavy load. If a
+ # cache expires and due to heavy load several different processes will try
+ # to read data natively and then they all will try to write to cache. To
+ # avoid that case the first process to find an expired cache entry will
+ # bump the cache expiration time by the value set in :race_condition_ttl.
+ # Yes, this process is extending the time for a stale value by another few
+ # seconds. Because of extended life of the previous cache, other processes
+ # will continue to use slightly stale data for a just a bit longer. In the
+ # meantime that first process will go ahead and will write into cache the
+ # new value. After that all the processes will start getting the new value.
+ # The key is to keep :race_condition_ttl small.
+ #
+ # If the process regenerating the entry errors out, the entry will be
+ # regenerated after the specified number of seconds. Also note that the
+ # life of stale cache is extended only if it expired recently. Otherwise
+ # a new value is generated and :race_condition_ttl does not play
+ # any role.
+ #
+ # # Set all values to expire after one minute.
+ # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
+ #
+ # cache.write('foo', 'original value')
+ # val_1 = nil
+ # val_2 = nil
+ # sleep 60
+ #
+ # Thread.new do
+ # val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
+ # sleep 1
+ # 'new value 1'
+ # end
+ # end
+ #
+ # Thread.new do
+ # val_2 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
+ # 'new value 2'
+ # end
+ # end
+ #
+ # cache.fetch('foo') # => "original value"
+ # sleep 10 # First thread extended the life of cache by another 10 seconds
+ # cache.fetch('foo') # => "new value 1"
+ # val_1 # => "new value 1"
+ # val_2 # => "original value"
+ #
+ # Other options will be handled by the specific cache store implementation.
+ # Internally, #fetch calls #read_entry, and calls #write_entry on a cache
+ # miss. +options+ will be passed to the #read and #write calls.
+ #
+ # For example, MemCacheStore's #write method supports the +:raw+
+ # option, which tells the memcached server to store all values as strings.
+ # We can use this option with #fetch too:
+ #
+ # cache = ActiveSupport::Cache::MemCacheStore.new
+ # cache.fetch("foo", force: true, raw: true) do
+ # :bar
+ # end
+ # cache.fetch('foo') # => "bar"
+ def fetch(name, options = nil)
+ if block_given?
+ options = merged_options(options)
+ key = normalize_key(name, options)
+
+ entry = nil
+ instrument(:read, name, options) do |payload|
+ cached_entry = read_entry(key, options) unless options[:force]
+ entry = handle_expired_entry(cached_entry, key, options)
+ entry = nil if entry && entry.mismatched?(normalize_version(name, options))
+ payload[:super_operation] = :fetch if payload
+ payload[:hit] = !!entry if payload
+ end
+
+ if entry
+ get_entry_value(entry, name, options)
+ else
+ save_block_result_to_cache(name, options) { |_name| yield _name }
+ end
+ elsif options && options[:force]
+ raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
+ else
+ read(name, options)
+ end
+ end
+
+ # Reads data from the cache, using the given key. If there is data in
+ # the cache with the given key, then that data is returned. Otherwise,
+ # +nil+ is returned.
+ #
+ # Note, if data was written with the :expires_in or :version options,
+ # both of these conditions are applied before the data is returned.
+ #
+ # Options are passed to the underlying cache implementation.
+ def read(name, options = nil)
+ options = merged_options(options)
+ key = normalize_key(name, options)
+ version = normalize_version(name, options)
+
+ instrument(:read, name, options) do |payload|
+ entry = read_entry(key, options)
+
+ if entry
+ if entry.expired?
+ delete_entry(key, options)
+ payload[:hit] = false if payload
+ nil
+ elsif entry.mismatched?(version)
+ payload[:hit] = false if payload
+ nil
+ else
+ payload[:hit] = true if payload
+ entry.value
+ end
+ else
+ payload[:hit] = false if payload
+ nil
+ end
+ end
+ end
+
+ # Reads multiple values at once from the cache. Options can be passed
+ # in the last argument.
+ #
+ # Some cache implementation may optimize this method.
+ #
+ # Returns a hash mapping the names provided to the values found.
+ def read_multi(*names)
+ options = names.extract_options!
+ options = merged_options(options)
+
+ instrument :read_multi, names, options do |payload|
+ read_multi_entries(names, options).tap do |results|
+ payload[:hits] = results.keys
+ end
+ end
+ end
+
+ # Cache Storage API to write multiple values at once.
+ def write_multi(hash, options = nil)
+ options = merged_options(options)
+
+ instrument :write_multi, hash, options do |payload|
+ entries = hash.each_with_object({}) do |(name, value), memo|
+ memo[normalize_key(name, options)] = Entry.new(value, options.merge(version: normalize_version(name, options)))
+ end
+
+ write_multi_entries entries, options
+ end
+ end
+
+ # Fetches data from the cache, using the given keys. If there is data in
+ # the cache with the given keys, then that data is returned. Otherwise,
+ # the supplied block is called for each key for which there was no data,
+ # and the result will be written to the cache and returned.
+ # Therefore, you need to pass a block that returns the data to be written
+ # to the cache. If you do not want to write the cache when the cache is
+ # not found, use #read_multi.
+ #
+ # Options are passed to the underlying cache implementation.
+ #
+ # Returns a hash with the data for each of the names. For example:
+ #
+ # cache.write("bim", "bam")
+ # cache.fetch_multi("bim", "unknown_key") do |key|
+ # "Fallback value for key: #{key}"
+ # end
+ # # => { "bim" => "bam",
+ # # "unknown_key" => "Fallback value for key: unknown_key" }
+ #
+ def fetch_multi(*names)
+ raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
+
+ options = names.extract_options!
+ options = merged_options(options)
+
+ instrument :read_multi, names, options do |payload|
+ read_multi_entries(names, options).tap do |results|
+ payload[:hits] = results.keys
+ payload[:super_operation] = :fetch_multi
+
+ writes = {}
+
+ (names - results.keys).each do |name|
+ results[name] = writes[name] = yield(name)
+ end
+
+ write_multi writes, options
+ end
+ end
+ end
+
+ # Writes the value to the cache, with the key.
+ #
+ # Options are passed to the underlying cache implementation.
+ def write(name, value, options = nil)
+ options = merged_options(options)
+
+ instrument(:write, name, options) do
+ entry = Entry.new(value, options.merge(version: normalize_version(name, options)))
+ write_entry(normalize_key(name, options), entry, options)
+ end
+ end
+
+ # Deletes an entry in the cache. Returns +true+ if an entry is deleted.
+ #
+ # Options are passed to the underlying cache implementation.
+ def delete(name, options = nil)
+ options = merged_options(options)
+
+ instrument(:delete, name) do
+ delete_entry(normalize_key(name, options), options)
+ end
+ end
+
+ # Returns +true+ if the cache contains an entry for the given key.
+ #
+ # Options are passed to the underlying cache implementation.
+ def exist?(name, options = nil)
+ options = merged_options(options)
+
+ instrument(:exist?, name) do
+ entry = read_entry(normalize_key(name, options), options)
+ (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
+ end
+ end
+
+ # Deletes all entries with keys matching the pattern.
+ #
+ # Options are passed to the underlying cache implementation.
+ #
+ # All implementations may not support this method.
+ def delete_matched(matcher, options = nil)
+ raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
+ end
+
+ # Increments an integer value in the cache.
+ #
+ # Options are passed to the underlying cache implementation.
+ #
+ # All implementations may not support this method.
+ def increment(name, amount = 1, options = nil)
+ raise NotImplementedError.new("#{self.class.name} does not support increment")
+ end
+
+ # Decrements an integer value in the cache.
+ #
+ # Options are passed to the underlying cache implementation.
+ #
+ # All implementations may not support this method.
+ def decrement(name, amount = 1, options = nil)
+ raise NotImplementedError.new("#{self.class.name} does not support decrement")
+ end
+
+ # Cleanups the cache by removing expired entries.
+ #
+ # Options are passed to the underlying cache implementation.
+ #
+ # All implementations may not support this method.
+ def cleanup(options = nil)
+ raise NotImplementedError.new("#{self.class.name} does not support cleanup")
+ end
+
+ # Clears the entire cache. Be careful with this method since it could
+ # affect other processes if shared cache is being used.
+ #
+ # The options hash is passed to the underlying cache implementation.
+ #
+ # All implementations may not support this method.
+ def clear(options = nil)
+ raise NotImplementedError.new("#{self.class.name} does not support clear")
+ end
+
+ private
+ # Adds the namespace defined in the options to a pattern designed to
+ # match keys. Implementations that support delete_matched should call
+ # this method to translate a pattern that matches names into one that
+ # matches namespaced keys.
+ def key_matcher(pattern, options) # :doc:
+ prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
+ if prefix
+ source = pattern.source
+ if source.start_with?("^")
+ source = source[1, source.length]
+ else
+ source = ".*#{source[0, source.length]}"
+ end
+ Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
+ else
+ pattern
+ end
+ end
+
+ # Reads an entry from the cache implementation. Subclasses must implement
+ # this method.
+ def read_entry(key, options)
+ raise NotImplementedError.new
+ end
+
+ # Writes an entry to the cache implementation. Subclasses must implement
+ # this method.
+ def write_entry(key, entry, options)
+ raise NotImplementedError.new
+ end
+
+ # Reads multiple entries from the cache implementation. Subclasses MAY
+ # implement this method.
+ def read_multi_entries(names, options)
+ results = {}
+ names.each do |name|
+ key = normalize_key(name, options)
+ version = normalize_version(name, options)
+ entry = read_entry(key, options)
+
+ if entry
+ if entry.expired?
+ delete_entry(key, options)
+ elsif entry.mismatched?(version)
+ # Skip mismatched versions
+ else
+ results[name] = entry.value
+ end
+ end
+ end
+ results
+ end
+
+ # Writes multiple entries to the cache implementation. Subclasses MAY
+ # implement this method.
+ def write_multi_entries(hash, options)
+ hash.each do |key, entry|
+ write_entry key, entry, options
+ end
+ end
+
+ # Deletes an entry from the cache implementation. Subclasses must
+ # implement this method.
+ def delete_entry(key, options)
+ raise NotImplementedError.new
+ end
+
+ # Merges the default options with ones specific to a method call.
+ def merged_options(call_options)
+ if call_options
+ options.merge(call_options)
+ else
+ options.dup
+ end
+ end
+
+ # Expands and namespaces the cache key. May be overridden by
+ # cache stores to do additional normalization.
+ def normalize_key(key, options = nil)
+ namespace_key expanded_key(key), options
+ end
+
+ # Prefix the key with a namespace string:
+ #
+ # namespace_key 'foo', namespace: 'cache'
+ # # => 'cache:foo'
+ #
+ # With a namespace block:
+ #
+ # namespace_key 'foo', namespace: -> { 'cache' }
+ # # => 'cache:foo'
+ def namespace_key(key, options = nil)
+ options = merged_options(options)
+ namespace = options[:namespace]
+
+ if namespace.respond_to?(:call)
+ namespace = namespace.call
+ end
+
+ if namespace
+ "#{namespace}:#{key}"
+ else
+ key
+ end
+ end
+
+ # Expands key to be a consistent string value. Invokes +cache_key+ if
+ # object responds to +cache_key+. Otherwise, +to_param+ method will be
+ # called. If the key is a Hash, then keys will be sorted alphabetically.
+ def expanded_key(key)
+ return key.cache_key.to_s if key.respond_to?(:cache_key)
+
+ case key
+ when Array
+ if key.size > 1
+ key = key.collect { |element| expanded_key(element) }
+ else
+ key = key.first
+ end
+ when Hash
+ key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
+ end
+
+ key.to_param
+ end
+
+ def normalize_version(key, options = nil)
+ (options && options[:version].try(:to_param)) || expanded_version(key)
+ end
+
+ def expanded_version(key)
+ case
+ when key.respond_to?(:cache_version) then key.cache_version.to_param
+ when key.is_a?(Array) then key.map { |element| expanded_version(element) }.compact.to_param
+ when key.respond_to?(:to_a) then expanded_version(key.to_a)
+ end
+ end
+
+ def instrument(operation, key, options = nil)
+ log { "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" }
+
+ payload = { key: key }
+ payload.merge!(options) if options.is_a?(Hash)
+ ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
+ end
+
+ def log
+ return unless logger && logger.debug? && !silence?
+ logger.debug(yield)
+ end
+
+ def handle_expired_entry(entry, key, options)
+ if entry && entry.expired?
+ race_ttl = options[:race_condition_ttl].to_i
+ if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
+ # When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
+ # for a brief period while the entry is being recalculated.
+ entry.expires_at = Time.now + race_ttl
+ write_entry(key, entry, expires_in: race_ttl * 2)
+ else
+ delete_entry(key, options)
+ end
+ entry = nil
+ end
+ entry
+ end
+
+ def get_entry_value(entry, name, options)
+ instrument(:fetch_hit, name, options) {}
+ entry.value
+ end
+
+ def save_block_result_to_cache(name, options)
+ result = instrument(:generate, name, options) do
+ yield(name)
+ end
+
+ write(name, result, options)
+ result
+ end
+ end
+
+ # This class is used to represent cache entries. Cache entries have a value, an optional
+ # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
+ # on the cache. The version is used to support the :version option on the cache for rejecting
+ # mismatches.
+ #
+ # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
+ # using short instance variable names that are lazily defined.
+ class Entry # :nodoc:
+ attr_reader :version
+
+ DEFAULT_COMPRESS_LIMIT = 1.kilobyte
+
+ # Creates a new cache entry for the specified value. Options supported are
+ # +:compress+, +:compress_threshold+, +:version+ and +:expires_in+.
+ def initialize(value, compress: true, compress_threshold: DEFAULT_COMPRESS_LIMIT, version: nil, expires_in: nil, **)
+ @value = value
+ @version = version
+ @created_at = Time.now.to_f
+ @expires_in = expires_in && expires_in.to_f
+
+ compress!(compress_threshold) if compress
+ end
+
+ def value
+ compressed? ? uncompress(@value) : @value
+ end
+
+ def mismatched?(version)
+ @version && version && @version != version
+ end
+
+ # Checks if the entry is expired. The +expires_in+ parameter can override
+ # the value set when the entry was created.
+ def expired?
+ @expires_in && @created_at + @expires_in <= Time.now.to_f
+ end
+
+ def expires_at
+ @expires_in ? @created_at + @expires_in : nil
+ end
+
+ def expires_at=(value)
+ if value
+ @expires_in = value.to_f - @created_at
+ else
+ @expires_in = nil
+ end
+ end
+
+ # Returns the size of the cached value. This could be less than
+ # value.size if the data is compressed.
+ def size
+ case value
+ when NilClass
+ 0
+ when String
+ @value.bytesize
+ else
+ @s ||= Marshal.dump(@value).bytesize
+ end
+ end
+
+ # Duplicates the value in a class. This is used by cache implementations that don't natively
+ # serialize entries to protect against accidental cache modifications.
+ def dup_value!
+ if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
+ if @value.is_a?(String)
+ @value = @value.dup
+ else
+ @value = Marshal.load(Marshal.dump(@value))
+ end
+ end
+ end
+
+ private
+ def compress!(compress_threshold)
+ case @value
+ when nil, true, false, Numeric
+ uncompressed_size = 0
+ when String
+ uncompressed_size = @value.bytesize
+ else
+ serialized = Marshal.dump(@value)
+ uncompressed_size = serialized.bytesize
+ end
+
+ if uncompressed_size >= compress_threshold
+ serialized ||= Marshal.dump(@value)
+ compressed = Zlib::Deflate.deflate(serialized)
+
+ if compressed.bytesize < uncompressed_size
+ @value = compressed
+ @compressed = true
+ end
+ end
+ end
+
+ def compressed?
+ defined?(@compressed)
+ end
+
+ def uncompress(value)
+ Marshal.load(Zlib::Inflate.inflate(value))
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/file_store.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/file_store.rb
new file mode 100644
index 0000000..a0f44aa
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/file_store.rb
@@ -0,0 +1,196 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/marshal"
+require "active_support/core_ext/file/atomic"
+require "active_support/core_ext/string/conversions"
+require "uri/common"
+
+module ActiveSupport
+ module Cache
+ # A cache store implementation which stores everything on the filesystem.
+ #
+ # FileStore implements the Strategy::LocalCache strategy which implements
+ # an in-memory cache inside of a block.
+ class FileStore < Store
+ prepend Strategy::LocalCache
+ attr_reader :cache_path
+
+ DIR_FORMATTER = "%03X"
+ FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
+ FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
+ EXCLUDED_DIRS = [".", ".."].freeze
+ GITKEEP_FILES = [".gitkeep", ".keep"].freeze
+
+ def initialize(cache_path, options = nil)
+ super(options)
+ @cache_path = cache_path.to_s
+ end
+
+ # Deletes all items from the cache. In this case it deletes all the entries in the specified
+ # file store directory except for .keep or .gitkeep. Be careful which directory is specified in your
+ # config file when using +FileStore+ because everything in that directory will be deleted.
+ def clear(options = nil)
+ root_dirs = exclude_from(cache_path, EXCLUDED_DIRS + GITKEEP_FILES)
+ FileUtils.rm_r(root_dirs.collect { |f| File.join(cache_path, f) })
+ rescue Errno::ENOENT
+ end
+
+ # Preemptively iterates through all stored keys and removes the ones which have expired.
+ def cleanup(options = nil)
+ options = merged_options(options)
+ search_dir(cache_path) do |fname|
+ entry = read_entry(fname, options)
+ delete_entry(fname, options) if entry && entry.expired?
+ end
+ end
+
+ # Increments an already existing integer value that is stored in the cache.
+ # If the key is not found nothing is done.
+ def increment(name, amount = 1, options = nil)
+ modify_value(name, amount, options)
+ end
+
+ # Decrements an already existing integer value that is stored in the cache.
+ # If the key is not found nothing is done.
+ def decrement(name, amount = 1, options = nil)
+ modify_value(name, -amount, options)
+ end
+
+ def delete_matched(matcher, options = nil)
+ options = merged_options(options)
+ instrument(:delete_matched, matcher.inspect) do
+ matcher = key_matcher(matcher, options)
+ search_dir(cache_path) do |path|
+ key = file_path_key(path)
+ delete_entry(path, options) if key.match(matcher)
+ end
+ end
+ end
+
+ private
+
+ def read_entry(key, options)
+ if File.exist?(key)
+ File.open(key) { |f| Marshal.load(f) }
+ end
+ rescue => e
+ logger.error("FileStoreError (#{e}): #{e.message}") if logger
+ nil
+ end
+
+ def write_entry(key, entry, options)
+ return false if options[:unless_exist] && File.exist?(key)
+ ensure_cache_path(File.dirname(key))
+ File.atomic_write(key, cache_path) { |f| Marshal.dump(entry, f) }
+ true
+ end
+
+ def delete_entry(key, options)
+ if File.exist?(key)
+ begin
+ File.delete(key)
+ delete_empty_directories(File.dirname(key))
+ true
+ rescue => e
+ # Just in case the error was caused by another process deleting the file first.
+ raise e if File.exist?(key)
+ false
+ end
+ end
+ end
+
+ # Lock a file for a block so only one process can modify it at a time.
+ def lock_file(file_name, &block)
+ if File.exist?(file_name)
+ File.open(file_name, "r+") do |f|
+ begin
+ f.flock File::LOCK_EX
+ yield
+ ensure
+ f.flock File::LOCK_UN
+ end
+ end
+ else
+ yield
+ end
+ end
+
+ # Translate a key into a file path.
+ def normalize_key(key, options)
+ key = super
+ fname = URI.encode_www_form_component(key)
+
+ if fname.size > FILEPATH_MAX_SIZE
+ fname = ActiveSupport::Digest.hexdigest(key)
+ end
+
+ hash = Zlib.adler32(fname)
+ hash, dir_1 = hash.divmod(0x1000)
+ dir_2 = hash.modulo(0x1000)
+ fname_paths = []
+
+ # Make sure file name doesn't exceed file system limits.
+ begin
+ fname_paths << fname[0, FILENAME_MAX_SIZE]
+ fname = fname[FILENAME_MAX_SIZE..-1]
+ end until fname.blank?
+
+ File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
+ end
+
+ # Translate a file path into a key.
+ def file_path_key(path)
+ fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
+ URI.decode_www_form_component(fname, Encoding::UTF_8)
+ end
+
+ # Delete empty directories in the cache.
+ def delete_empty_directories(dir)
+ return if File.realpath(dir) == File.realpath(cache_path)
+ if exclude_from(dir, EXCLUDED_DIRS).empty?
+ Dir.delete(dir) rescue nil
+ delete_empty_directories(File.dirname(dir))
+ end
+ end
+
+ # Make sure a file path's directories exist.
+ def ensure_cache_path(path)
+ FileUtils.makedirs(path) unless File.exist?(path)
+ end
+
+ def search_dir(dir, &callback)
+ return if !File.exist?(dir)
+ Dir.foreach(dir) do |d|
+ next if EXCLUDED_DIRS.include?(d)
+ name = File.join(dir, d)
+ if File.directory?(name)
+ search_dir(name, &callback)
+ else
+ callback.call name
+ end
+ end
+ end
+
+ # Modifies the amount of an already existing integer value that is stored in the cache.
+ # If the key is not found nothing is done.
+ def modify_value(name, amount, options)
+ file_name = normalize_key(name, options)
+
+ lock_file(file_name) do
+ options = merged_options(options)
+
+ if num = read(name, options)
+ num = num.to_i + amount
+ write(name, num, options)
+ num
+ end
+ end
+ end
+
+ # Exclude entries from source directory
+ def exclude_from(source, excludes)
+ Dir.entries(source).reject { |f| excludes.include?(f) }
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/mem_cache_store.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/mem_cache_store.rb
new file mode 100644
index 0000000..22c47e4
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/mem_cache_store.rb
@@ -0,0 +1,197 @@
+# frozen_string_literal: true
+
+begin
+ require "dalli"
+rescue LoadError => e
+ $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
+ raise e
+end
+
+require "active_support/core_ext/array/extract_options"
+
+module ActiveSupport
+ module Cache
+ # A cache store implementation which stores data in Memcached:
+ # https://memcached.org
+ #
+ # This is currently the most popular cache store for production websites.
+ #
+ # Special features:
+ # - Clustering and load balancing. One can specify multiple memcached servers,
+ # and MemCacheStore will load balance between all available servers. If a
+ # server goes down, then MemCacheStore will ignore it until it comes back up.
+ #
+ # MemCacheStore implements the Strategy::LocalCache strategy which implements
+ # an in-memory cache inside of a block.
+ class MemCacheStore < Store
+ # Provide support for raw values in the local cache strategy.
+ module LocalCacheWithRaw # :nodoc:
+ private
+ def write_entry(key, entry, options)
+ if options[:raw] && local_cache
+ raw_entry = Entry.new(entry.value.to_s)
+ raw_entry.expires_at = entry.expires_at
+ super(key, raw_entry, options)
+ else
+ super
+ end
+ end
+ end
+
+ prepend Strategy::LocalCache
+ prepend LocalCacheWithRaw
+
+ ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
+
+ # Creates a new Dalli::Client instance with specified addresses and options.
+ # By default address is equal localhost:11211.
+ #
+ # ActiveSupport::Cache::MemCacheStore.build_mem_cache
+ # # => #
+ # ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
+ # # => #
+ def self.build_mem_cache(*addresses) # :nodoc:
+ addresses = addresses.flatten
+ options = addresses.extract_options!
+ addresses = ["localhost:11211"] if addresses.empty?
+ pool_options = retrieve_pool_options(options)
+
+ if pool_options.empty?
+ Dalli::Client.new(addresses, options)
+ else
+ ensure_connection_pool_added!
+ ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
+ end
+ end
+
+ # Creates a new MemCacheStore object, with the given memcached server
+ # addresses. Each address is either a host name, or a host-with-port string
+ # in the form of "host_name:port". For example:
+ #
+ # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
+ #
+ # If no addresses are specified, then MemCacheStore will connect to
+ # localhost port 11211 (the default memcached port).
+ def initialize(*addresses)
+ addresses = addresses.flatten
+ options = addresses.extract_options!
+ super(options)
+
+ unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
+ raise ArgumentError, "First argument must be an empty array, an array of hosts or a Dalli::Client instance."
+ end
+ if addresses.first.is_a?(Dalli::Client)
+ @data = addresses.first
+ else
+ mem_cache_options = options.dup
+ UNIVERSAL_OPTIONS.each { |name| mem_cache_options.delete(name) }
+ @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
+ end
+ end
+
+ # Increment a cached value. This method uses the memcached incr atomic
+ # operator and can only be used on values written with the :raw option.
+ # Calling it on a value not stored with :raw will initialize that value
+ # to zero.
+ def increment(name, amount = 1, options = nil)
+ options = merged_options(options)
+ instrument(:increment, name, amount: amount) do
+ rescue_error_with nil do
+ @data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in]) }
+ end
+ end
+ end
+
+ # Decrement a cached value. This method uses the memcached decr atomic
+ # operator and can only be used on values written with the :raw option.
+ # Calling it on a value not stored with :raw will initialize that value
+ # to zero.
+ def decrement(name, amount = 1, options = nil)
+ options = merged_options(options)
+ instrument(:decrement, name, amount: amount) do
+ rescue_error_with nil do
+ @data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in]) }
+ end
+ end
+ end
+
+ # Clear the entire cache on all memcached servers. This method should
+ # be used with care when shared cache is being used.
+ def clear(options = nil)
+ rescue_error_with(nil) { @data.with { |c| c.flush_all } }
+ end
+
+ # Get the statistics from the memcached servers.
+ def stats
+ @data.with { |c| c.stats }
+ end
+
+ private
+ # Read an entry from the cache.
+ def read_entry(key, options)
+ rescue_error_with(nil) { deserialize_entry(@data.with { |c| c.get(key, options) }) }
+ end
+
+ # Write an entry to the cache.
+ def write_entry(key, entry, options)
+ method = options && options[:unless_exist] ? :add : :set
+ value = options[:raw] ? entry.value.to_s : entry
+ expires_in = options[:expires_in].to_i
+ if expires_in > 0 && !options[:raw]
+ # Set the memcache expire a few minutes in the future to support race condition ttls on read
+ expires_in += 5.minutes
+ end
+ rescue_error_with false do
+ @data.with { |c| c.send(method, key, value, expires_in, options) }
+ end
+ end
+
+ # Reads multiple entries from the cache implementation.
+ def read_multi_entries(names, options)
+ keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
+
+ raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
+ values = {}
+
+ raw_values.each do |key, value|
+ entry = deserialize_entry(value)
+
+ unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
+ values[keys_to_names[key]] = entry.value
+ end
+ end
+
+ values
+ end
+
+ # Delete an entry from the cache.
+ def delete_entry(key, options)
+ rescue_error_with(false) { @data.with { |c| c.delete(key) } }
+ end
+
+ # Memcache keys are binaries. So we need to force their encoding to binary
+ # before applying the regular expression to ensure we are escaping all
+ # characters properly.
+ def normalize_key(key, options)
+ key = super.dup
+ key = key.force_encoding(Encoding::ASCII_8BIT)
+ key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
+ key = "#{key[0, 213]}:md5:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
+ key
+ end
+
+ def deserialize_entry(entry)
+ if entry
+ entry.is_a?(Entry) ? entry : Entry.new(entry)
+ end
+ end
+
+ def rescue_error_with(fallback)
+ yield
+ rescue Dalli::DalliError => e
+ logger.error("DalliError (#{e}): #{e.message}") if logger
+ fallback
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/memory_store.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/memory_store.rb
new file mode 100644
index 0000000..564ac17
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/memory_store.rb
@@ -0,0 +1,169 @@
+# frozen_string_literal: true
+
+require "monitor"
+
+module ActiveSupport
+ module Cache
+ # A cache store implementation which stores everything into memory in the
+ # same process. If you're running multiple Ruby on Rails server processes
+ # (which is the case if you're using Phusion Passenger or puma clustered mode),
+ # then this means that Rails server process instances won't be able
+ # to share cache data with each other and this may not be the most
+ # appropriate cache in that scenario.
+ #
+ # This cache has a bounded size specified by the :size options to the
+ # initializer (default is 32Mb). When the cache exceeds the allotted size,
+ # a cleanup will occur which tries to prune the cache down to three quarters
+ # of the maximum size by removing the least recently used entries.
+ #
+ # MemoryStore is thread-safe.
+ class MemoryStore < Store
+ def initialize(options = nil)
+ options ||= {}
+ super(options)
+ @data = {}
+ @key_access = {}
+ @max_size = options[:size] || 32.megabytes
+ @max_prune_time = options[:max_prune_time] || 2
+ @cache_size = 0
+ @monitor = Monitor.new
+ @pruning = false
+ end
+
+ # Delete all data stored in a given cache store.
+ def clear(options = nil)
+ synchronize do
+ @data.clear
+ @key_access.clear
+ @cache_size = 0
+ end
+ end
+
+ # Preemptively iterates through all stored keys and removes the ones which have expired.
+ def cleanup(options = nil)
+ options = merged_options(options)
+ instrument(:cleanup, size: @data.size) do
+ keys = synchronize { @data.keys }
+ keys.each do |key|
+ entry = @data[key]
+ delete_entry(key, options) if entry && entry.expired?
+ end
+ end
+ end
+
+ # To ensure entries fit within the specified memory prune the cache by removing the least
+ # recently accessed entries.
+ def prune(target_size, max_time = nil)
+ return if pruning?
+ @pruning = true
+ begin
+ start_time = Time.now
+ cleanup
+ instrument(:prune, target_size, from: @cache_size) do
+ keys = synchronize { @key_access.keys.sort { |a, b| @key_access[a].to_f <=> @key_access[b].to_f } }
+ keys.each do |key|
+ delete_entry(key, options)
+ return if @cache_size <= target_size || (max_time && Time.now - start_time > max_time)
+ end
+ end
+ ensure
+ @pruning = false
+ end
+ end
+
+ # Returns true if the cache is currently being pruned.
+ def pruning?
+ @pruning
+ end
+
+ # Increment an integer value in the cache.
+ def increment(name, amount = 1, options = nil)
+ modify_value(name, amount, options)
+ end
+
+ # Decrement an integer value in the cache.
+ def decrement(name, amount = 1, options = nil)
+ modify_value(name, -amount, options)
+ end
+
+ # Deletes cache entries if the cache key matches a given pattern.
+ def delete_matched(matcher, options = nil)
+ options = merged_options(options)
+ instrument(:delete_matched, matcher.inspect) do
+ matcher = key_matcher(matcher, options)
+ keys = synchronize { @data.keys }
+ keys.each do |key|
+ delete_entry(key, options) if key.match(matcher)
+ end
+ end
+ end
+
+ def inspect # :nodoc:
+ "<##{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
+ end
+
+ # Synchronize calls to the cache. This should be called wherever the underlying cache implementation
+ # is not thread safe.
+ def synchronize(&block) # :nodoc:
+ @monitor.synchronize(&block)
+ end
+
+ private
+
+ PER_ENTRY_OVERHEAD = 240
+
+ def cached_size(key, entry)
+ key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
+ end
+
+ def read_entry(key, options)
+ entry = @data[key]
+ synchronize do
+ if entry
+ @key_access[key] = Time.now.to_f
+ else
+ @key_access.delete(key)
+ end
+ end
+ entry
+ end
+
+ def write_entry(key, entry, options)
+ entry.dup_value!
+ synchronize do
+ old_entry = @data[key]
+ return false if @data.key?(key) && options[:unless_exist]
+ if old_entry
+ @cache_size -= (old_entry.size - entry.size)
+ else
+ @cache_size += cached_size(key, entry)
+ end
+ @key_access[key] = Time.now.to_f
+ @data[key] = entry
+ prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
+ true
+ end
+ end
+
+ def delete_entry(key, options)
+ synchronize do
+ @key_access.delete(key)
+ entry = @data.delete(key)
+ @cache_size -= cached_size(key, entry) if entry
+ !!entry
+ end
+ end
+
+ def modify_value(name, amount, options)
+ synchronize do
+ options = merged_options(options)
+ if num = read(name, options)
+ num = num.to_i + amount
+ write(name, num, options)
+ num
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/null_store.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/null_store.rb
new file mode 100644
index 0000000..1a5983d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/null_store.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Cache
+ # A cache store implementation which doesn't actually store anything. Useful in
+ # development and test environments where you don't want caching turned on but
+ # need to go through the caching interface.
+ #
+ # This cache does implement the local cache strategy, so values will actually
+ # be cached inside blocks that utilize this strategy. See
+ # ActiveSupport::Cache::Strategy::LocalCache for more details.
+ class NullStore < Store
+ prepend Strategy::LocalCache
+
+ def clear(options = nil)
+ end
+
+ def cleanup(options = nil)
+ end
+
+ def increment(name, amount = 1, options = nil)
+ end
+
+ def decrement(name, amount = 1, options = nil)
+ end
+
+ def delete_matched(matcher, options = nil)
+ end
+
+ private
+ def read_entry(key, options)
+ end
+
+ def write_entry(key, entry, options)
+ true
+ end
+
+ def delete_entry(key, options)
+ false
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/redis_cache_store.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/redis_cache_store.rb
new file mode 100644
index 0000000..825fc9f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/redis_cache_store.rb
@@ -0,0 +1,466 @@
+# frozen_string_literal: true
+
+begin
+ gem "redis", ">= 4.0.1"
+ require "redis"
+ require "redis/distributed"
+rescue LoadError
+ warn "The Redis cache store requires the redis gem, version 4.0.1 or later. Please add it to your Gemfile: `gem \"redis\", \"~> 4.0\"`"
+ raise
+end
+
+# Prefer the hiredis driver but don't require it.
+begin
+ require "redis/connection/hiredis"
+rescue LoadError
+end
+
+require "digest/sha2"
+require "active_support/core_ext/marshal"
+require "active_support/core_ext/hash/transform_values"
+
+module ActiveSupport
+ module Cache
+ module ConnectionPoolLike
+ def with
+ yield self
+ end
+ end
+
+ ::Redis.include(ConnectionPoolLike)
+ ::Redis::Distributed.include(ConnectionPoolLike)
+
+ # Redis cache store.
+ #
+ # Deployment note: Take care to use a *dedicated Redis cache* rather
+ # than pointing this at your existing Redis server. It won't cope well
+ # with mixed usage patterns and it won't expire cache entries by default.
+ #
+ # Redis cache server setup guide: https://redis.io/topics/lru-cache
+ #
+ # * Supports vanilla Redis, hiredis, and Redis::Distributed.
+ # * Supports Memcached-like sharding across Redises with Redis::Distributed.
+ # * Fault tolerant. If the Redis server is unavailable, no exceptions are
+ # raised. Cache fetches are all misses and writes are dropped.
+ # * Local cache. Hot in-memory primary cache within block/middleware scope.
+ # * +read_multi+ and +write_multi+ support for Redis mget/mset. Use Redis::Distributed
+ # 4.0.1+ for distributed mget support.
+ # * +delete_matched+ support for Redis KEYS globs.
+ class RedisCacheStore < Store
+ # Keys are truncated with their own SHA2 digest if they exceed 1kB
+ MAX_KEY_BYTESIZE = 1024
+
+ DEFAULT_REDIS_OPTIONS = {
+ connect_timeout: 20,
+ read_timeout: 1,
+ write_timeout: 1,
+ reconnect_attempts: 0,
+ }
+
+ DEFAULT_ERROR_HANDLER = -> (method:, returning:, exception:) do
+ if logger
+ logger.error { "RedisCacheStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
+ end
+ end
+
+ # The maximum number of entries to receive per SCAN call.
+ SCAN_BATCH_SIZE = 1000
+ private_constant :SCAN_BATCH_SIZE
+
+ # Support raw values in the local cache strategy.
+ module LocalCacheWithRaw # :nodoc:
+ private
+ def write_entry(key, entry, options)
+ if options[:raw] && local_cache
+ raw_entry = Entry.new(serialize_entry(entry, raw: true))
+ raw_entry.expires_at = entry.expires_at
+ super(key, raw_entry, options)
+ else
+ super
+ end
+ end
+
+ def write_multi_entries(entries, options)
+ if options[:raw] && local_cache
+ raw_entries = entries.map do |key, entry|
+ raw_entry = Entry.new(serialize_entry(entry, raw: true))
+ raw_entry.expires_at = entry.expires_at
+ end.to_h
+
+ super(raw_entries, options)
+ else
+ super
+ end
+ end
+ end
+
+ prepend Strategy::LocalCache
+ prepend LocalCacheWithRaw
+
+ class << self
+ # Factory method to create a new Redis instance.
+ #
+ # Handles four options: :redis block, :redis instance, single :url
+ # string, and multiple :url strings.
+ #
+ # Option Class Result
+ # :redis Proc -> options[:redis].call
+ # :redis Object -> options[:redis]
+ # :url String -> Redis.new(url: …)
+ # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
+ #
+ def build_redis(redis: nil, url: nil, **redis_options) #:nodoc:
+ urls = Array(url)
+
+ if redis.is_a?(Proc)
+ redis.call
+ elsif redis
+ redis
+ elsif urls.size > 1
+ build_redis_distributed_client urls: urls, **redis_options
+ else
+ build_redis_client url: urls.first, **redis_options
+ end
+ end
+
+ private
+ def build_redis_distributed_client(urls:, **redis_options)
+ ::Redis::Distributed.new([], DEFAULT_REDIS_OPTIONS.merge(redis_options)).tap do |dist|
+ urls.each { |u| dist.add_node url: u }
+ end
+ end
+
+ def build_redis_client(url:, **redis_options)
+ ::Redis.new DEFAULT_REDIS_OPTIONS.merge(redis_options.merge(url: url))
+ end
+ end
+
+ attr_reader :redis_options
+ attr_reader :max_key_bytesize
+
+ # Creates a new Redis cache store.
+ #
+ # Handles three options: block provided to instantiate, single URL
+ # provided, and multiple URLs provided.
+ #
+ # :redis Proc -> options[:redis].call
+ # :url String -> Redis.new(url: …)
+ # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
+ #
+ # No namespace is set by default. Provide one if the Redis cache
+ # server is shared with other apps: namespace: 'myapp-cache'.
+ #
+ # Compression is enabled by default with a 1kB threshold, so cached
+ # values larger than 1kB are automatically compressed. Disable by
+ # passing compress: false or change the threshold by passing
+ # compress_threshold: 4.kilobytes.
+ #
+ # No expiry is set on cache entries by default. Redis is expected to
+ # be configured with an eviction policy that automatically deletes
+ # least-recently or -frequently used keys when it reaches max memory.
+ # See https://redis.io/topics/lru-cache for cache server setup.
+ #
+ # Race condition TTL is not set by default. This can be used to avoid
+ # "thundering herd" cache writes when hot cache entries are expired.
+ # See ActiveSupport::Cache::Store#fetch for more.
+ def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
+ @redis_options = redis_options
+
+ @max_key_bytesize = MAX_KEY_BYTESIZE
+ @error_handler = error_handler
+
+ super namespace: namespace,
+ compress: compress, compress_threshold: compress_threshold,
+ expires_in: expires_in, race_condition_ttl: race_condition_ttl
+ end
+
+ def redis
+ @redis ||= begin
+ pool_options = self.class.send(:retrieve_pool_options, redis_options)
+
+ if pool_options.any?
+ self.class.send(:ensure_connection_pool_added!)
+ ::ConnectionPool.new(pool_options) { self.class.build_redis(**redis_options) }
+ else
+ self.class.build_redis(**redis_options)
+ end
+ end
+ end
+
+ def inspect
+ instance = @redis || @redis_options
+ "<##{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
+ end
+
+ # Cache Store API implementation.
+ #
+ # Read multiple values at once. Returns a hash of requested keys ->
+ # fetched values.
+ def read_multi(*names)
+ if mget_capable?
+ instrument(:read_multi, names, options) do |payload|
+ read_multi_mget(*names).tap do |results|
+ payload[:hits] = results.keys
+ end
+ end
+ else
+ super
+ end
+ end
+
+ # Cache Store API implementation.
+ #
+ # Supports Redis KEYS glob patterns:
+ #
+ # h?llo matches hello, hallo and hxllo
+ # h*llo matches hllo and heeeello
+ # h[ae]llo matches hello and hallo, but not hillo
+ # h[^e]llo matches hallo, hbllo, ... but not hello
+ # h[a-b]llo matches hallo and hbllo
+ #
+ # Use \ to escape special characters if you want to match them verbatim.
+ #
+ # See https://redis.io/commands/KEYS for more.
+ #
+ # Failsafe: Raises errors.
+ def delete_matched(matcher, options = nil)
+ instrument :delete_matched, matcher do
+ unless String === matcher
+ raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}"
+ end
+ redis.with do |c|
+ pattern = namespace_key(matcher, options)
+ cursor = "0"
+ # Fetch keys in batches using SCAN to avoid blocking the Redis server.
+ begin
+ cursor, keys = c.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
+ c.del(*keys) unless keys.empty?
+ end until cursor == "0"
+ end
+ end
+ end
+
+ # Cache Store API implementation.
+ #
+ # Increment a cached value. This method uses the Redis incr atomic
+ # operator and can only be used on values written with the :raw option.
+ # Calling it on a value not stored with :raw will initialize that value
+ # to zero.
+ #
+ # Failsafe: Raises errors.
+ def increment(name, amount = 1, options = nil)
+ instrument :increment, name, amount: amount do
+ failsafe :increment do
+ redis.with { |c| c.incrby normalize_key(name, options), amount }
+ end
+ end
+ end
+
+ # Cache Store API implementation.
+ #
+ # Decrement a cached value. This method uses the Redis decr atomic
+ # operator and can only be used on values written with the :raw option.
+ # Calling it on a value not stored with :raw will initialize that value
+ # to zero.
+ #
+ # Failsafe: Raises errors.
+ def decrement(name, amount = 1, options = nil)
+ instrument :decrement, name, amount: amount do
+ failsafe :decrement do
+ redis.with { |c| c.decrby normalize_key(name, options), amount }
+ end
+ end
+ end
+
+ # Cache Store API implementation.
+ #
+ # Removes expired entries. Handled natively by Redis least-recently-/
+ # least-frequently-used expiry, so manual cleanup is not supported.
+ def cleanup(options = nil)
+ super
+ end
+
+ # Clear the entire cache on all Redis servers. Safe to use on
+ # shared servers if the cache is namespaced.
+ #
+ # Failsafe: Raises errors.
+ def clear(options = nil)
+ failsafe :clear do
+ if namespace = merged_options(options)[:namespace]
+ delete_matched "*", namespace: namespace
+ else
+ redis.with { |c| c.flushdb }
+ end
+ end
+ end
+
+ def mget_capable? #:nodoc:
+ set_redis_capabilities unless defined? @mget_capable
+ @mget_capable
+ end
+
+ def mset_capable? #:nodoc:
+ set_redis_capabilities unless defined? @mset_capable
+ @mset_capable
+ end
+
+ private
+ def set_redis_capabilities
+ case redis
+ when Redis::Distributed
+ @mget_capable = true
+ @mset_capable = false
+ else
+ @mget_capable = true
+ @mset_capable = true
+ end
+ end
+
+ # Store provider interface:
+ # Read an entry from the cache.
+ def read_entry(key, options = nil)
+ failsafe :read_entry do
+ raw = options&.fetch(:raw, false)
+ deserialize_entry(redis.with { |c| c.get(key) }, raw: raw)
+ end
+ end
+
+ def read_multi_entries(names, _options)
+ if mget_capable?
+ read_multi_mget(*names)
+ else
+ super
+ end
+ end
+
+ def read_multi_mget(*names)
+ options = names.extract_options!
+ options = merged_options(options)
+ raw = options&.fetch(:raw, false)
+
+ keys = names.map { |name| normalize_key(name, options) }
+
+ values = failsafe(:read_multi_mget, returning: {}) do
+ redis.with { |c| c.mget(*keys) }
+ end
+
+ names.zip(values).each_with_object({}) do |(name, value), results|
+ if value
+ entry = deserialize_entry(value, raw: raw)
+ unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options))
+ results[name] = entry.value
+ end
+ end
+ end
+ end
+
+ # Write an entry to the cache.
+ #
+ # Requires Redis 2.6.12+ for extended SET options.
+ def write_entry(key, entry, unless_exist: false, raw: false, expires_in: nil, race_condition_ttl: nil, **options)
+ serialized_entry = serialize_entry(entry, raw: raw)
+
+ # If race condition TTL is in use, ensure that cache entries
+ # stick around a bit longer after they would have expired
+ # so we can purposefully serve stale entries.
+ if race_condition_ttl && expires_in && expires_in > 0 && !raw
+ expires_in += 5.minutes
+ end
+
+ failsafe :write_entry, returning: false do
+ if unless_exist || expires_in
+ modifiers = {}
+ modifiers[:nx] = unless_exist
+ modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
+
+ redis.with { |c| c.set key, serialized_entry, modifiers }
+ else
+ redis.with { |c| c.set key, serialized_entry }
+ end
+ end
+ end
+
+ # Delete an entry from the cache.
+ def delete_entry(key, options)
+ failsafe :delete_entry, returning: false do
+ redis.with { |c| c.del key }
+ end
+ end
+
+ # Nonstandard store provider API to write multiple values at once.
+ def write_multi_entries(entries, expires_in: nil, **options)
+ if entries.any?
+ if mset_capable? && expires_in.nil?
+ failsafe :write_multi_entries do
+ redis.with { |c| c.mapped_mset(serialize_entries(entries, raw: options[:raw])) }
+ end
+ else
+ super
+ end
+ end
+ end
+
+ # Truncate keys that exceed 1kB.
+ def normalize_key(key, options)
+ truncate_key super.b
+ end
+
+ def truncate_key(key)
+ if key.bytesize > max_key_bytesize
+ suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
+ truncate_at = max_key_bytesize - suffix.bytesize
+ "#{key.byteslice(0, truncate_at)}#{suffix}"
+ else
+ key
+ end
+ end
+
+ def deserialize_entry(serialized_entry, raw:)
+ if serialized_entry
+ entry = Marshal.load(serialized_entry) rescue serialized_entry
+
+ written_raw = serialized_entry.equal?(entry)
+ if raw != written_raw
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Using a different value for the raw option when reading and writing
+ to a cache key is deprecated for :redis_cache_store and Rails 6.0
+ will stop automatically detecting the format when reading to avoid
+ marshal loading untrusted raw strings.
+ MSG
+ end
+
+ entry.is_a?(Entry) ? entry : Entry.new(entry)
+ end
+ end
+
+ def serialize_entry(entry, raw: false)
+ if raw
+ entry.value.to_s
+ else
+ Marshal.dump(entry)
+ end
+ end
+
+ def serialize_entries(entries, raw: false)
+ entries.transform_values do |entry|
+ serialize_entry entry, raw: raw
+ end
+ end
+
+ def failsafe(method, returning: nil)
+ yield
+ rescue ::Redis::BaseConnectionError => e
+ handle_exception exception: e, method: method, returning: returning
+ returning
+ end
+
+ def handle_exception(exception:, method:, returning:)
+ if @error_handler
+ @error_handler.(method: method, exception: exception, returning: returning)
+ end
+ rescue => failsafe
+ warn "RedisCacheStore ignored exception in handle_exception: #{failsafe.class}: #{failsafe.message}\n #{failsafe.backtrace.join("\n ")}"
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/strategy/local_cache.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/strategy/local_cache.rb
new file mode 100644
index 0000000..39b32fc
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/strategy/local_cache.rb
@@ -0,0 +1,194 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/duplicable"
+require "active_support/core_ext/string/inflections"
+require "active_support/per_thread_registry"
+
+module ActiveSupport
+ module Cache
+ module Strategy
+ # Caches that implement LocalCache will be backed by an in-memory cache for the
+ # duration of a block. Repeated calls to the cache for the same key will hit the
+ # in-memory cache for faster access.
+ module LocalCache
+ autoload :Middleware, "active_support/cache/strategy/local_cache_middleware"
+
+ # Class for storing and registering the local caches.
+ class LocalCacheRegistry # :nodoc:
+ extend ActiveSupport::PerThreadRegistry
+
+ def initialize
+ @registry = {}
+ end
+
+ def cache_for(local_cache_key)
+ @registry[local_cache_key]
+ end
+
+ def set_cache_for(local_cache_key, value)
+ @registry[local_cache_key] = value
+ end
+
+ def self.set_cache_for(l, v); instance.set_cache_for l, v; end
+ def self.cache_for(l); instance.cache_for l; end
+ end
+
+ # Simple memory backed cache. This cache is not thread safe and is intended only
+ # for serving as a temporary memory cache for a single thread.
+ class LocalStore < Store
+ def initialize
+ super
+ @data = {}
+ end
+
+ # Don't allow synchronizing since it isn't thread safe.
+ def synchronize # :nodoc:
+ yield
+ end
+
+ def clear(options = nil)
+ @data.clear
+ end
+
+ def read_entry(key, options)
+ @data[key]
+ end
+
+ def read_multi_entries(keys, options)
+ values = {}
+
+ keys.each do |name|
+ entry = read_entry(name, options)
+ values[name] = entry.value if entry
+ end
+
+ values
+ end
+
+ def write_entry(key, value, options)
+ @data[key] = value
+ true
+ end
+
+ def delete_entry(key, options)
+ !!@data.delete(key)
+ end
+
+ def fetch_entry(key, options = nil) # :nodoc:
+ @data.fetch(key) { @data[key] = yield }
+ end
+ end
+
+ # Use a local cache for the duration of block.
+ def with_local_cache
+ use_temporary_local_cache(LocalStore.new) { yield }
+ end
+
+ # Middleware class can be inserted as a Rack handler to be local cache for the
+ # duration of request.
+ def middleware
+ @middleware ||= Middleware.new(
+ "ActiveSupport::Cache::Strategy::LocalCache",
+ local_cache_key)
+ end
+
+ def clear(options = nil) # :nodoc:
+ return super unless cache = local_cache
+ cache.clear(options)
+ super
+ end
+
+ def cleanup(options = nil) # :nodoc:
+ return super unless cache = local_cache
+ cache.clear
+ super
+ end
+
+ def increment(name, amount = 1, options = nil) # :nodoc:
+ return super unless local_cache
+ value = bypass_local_cache { super }
+ write_cache_value(name, value, options)
+ value
+ end
+
+ def decrement(name, amount = 1, options = nil) # :nodoc:
+ return super unless local_cache
+ value = bypass_local_cache { super }
+ write_cache_value(name, value, options)
+ value
+ end
+
+ private
+ def read_entry(key, options)
+ if cache = local_cache
+ cache.fetch_entry(key) { super }
+ else
+ super
+ end
+ end
+
+ def read_multi_entries(keys, options)
+ return super unless local_cache
+
+ local_entries = local_cache.read_multi_entries(keys, options)
+ missed_keys = keys - local_entries.keys
+
+ if missed_keys.any?
+ local_entries.merge!(super(missed_keys, options))
+ else
+ local_entries
+ end
+ end
+
+ def write_entry(key, entry, options)
+ if options[:unless_exist]
+ local_cache.delete_entry(key, options) if local_cache
+ else
+ local_cache.write_entry(key, entry, options) if local_cache
+ end
+
+ super
+ end
+
+ def delete_entry(key, options)
+ local_cache.delete_entry(key, options) if local_cache
+ super
+ end
+
+ def write_cache_value(name, value, options)
+ name = normalize_key(name, options)
+ cache = local_cache
+ cache.mute do
+ if value
+ cache.write(name, value, options)
+ else
+ cache.delete(name, options)
+ end
+ end
+ end
+
+ def local_cache_key
+ @local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, "_").to_sym
+ end
+
+ def local_cache
+ LocalCacheRegistry.cache_for(local_cache_key)
+ end
+
+ def bypass_local_cache
+ use_temporary_local_cache(nil) { yield }
+ end
+
+ def use_temporary_local_cache(temporary_cache)
+ save_cache = LocalCacheRegistry.cache_for(local_cache_key)
+ begin
+ LocalCacheRegistry.set_cache_for(local_cache_key, temporary_cache)
+ yield
+ ensure
+ LocalCacheRegistry.set_cache_for(local_cache_key, save_cache)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/strategy/local_cache_middleware.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/strategy/local_cache_middleware.rb
new file mode 100644
index 0000000..62542bd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/cache/strategy/local_cache_middleware.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require "rack/body_proxy"
+require "rack/utils"
+
+module ActiveSupport
+ module Cache
+ module Strategy
+ module LocalCache
+ #--
+ # This class wraps up local storage for middlewares. Only the middleware method should
+ # construct them.
+ class Middleware # :nodoc:
+ attr_reader :name, :local_cache_key
+
+ def initialize(name, local_cache_key)
+ @name = name
+ @local_cache_key = local_cache_key
+ @app = nil
+ end
+
+ def new(app)
+ @app = app
+ self
+ end
+
+ def call(env)
+ LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new)
+ response = @app.call(env)
+ response[2] = ::Rack::BodyProxy.new(response[2]) do
+ LocalCacheRegistry.set_cache_for(local_cache_key, nil)
+ end
+ cleanup_on_body_close = true
+ response
+ rescue Rack::Utils::InvalidParameterError
+ [400, {}, []]
+ ensure
+ LocalCacheRegistry.set_cache_for(local_cache_key, nil) unless
+ cleanup_on_body_close
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/callbacks.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/callbacks.rb
new file mode 100644
index 0000000..9a3728d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/callbacks.rb
@@ -0,0 +1,845 @@
+# frozen_string_literal: true
+
+require "active_support/concern"
+require "active_support/descendants_tracker"
+require "active_support/core_ext/array/extract_options"
+require "active_support/core_ext/class/attribute"
+require "active_support/core_ext/kernel/reporting"
+require "active_support/core_ext/kernel/singleton_class"
+require "active_support/core_ext/string/filters"
+require "active_support/deprecation"
+require "thread"
+
+module ActiveSupport
+ # Callbacks are code hooks that are run at key points in an object's life cycle.
+ # The typical use case is to have a base class define a set of callbacks
+ # relevant to the other functionality it supplies, so that subclasses can
+ # install callbacks that enhance or modify the base functionality without
+ # needing to override or redefine methods of the base class.
+ #
+ # Mixing in this module allows you to define the events in the object's
+ # life cycle that will support callbacks (via +ClassMethods.define_callbacks+),
+ # set the instance methods, procs, or callback objects to be called (via
+ # +ClassMethods.set_callback+), and run the installed callbacks at the
+ # appropriate times (via +run_callbacks+).
+ #
+ # Three kinds of callbacks are supported: before callbacks, run before a
+ # certain event; after callbacks, run after the event; and around callbacks,
+ # blocks that surround the event, triggering it when they yield. Callback code
+ # can be contained in instance methods, procs or lambdas, or callback objects
+ # that respond to certain predetermined methods. See +ClassMethods.set_callback+
+ # for details.
+ #
+ # class Record
+ # include ActiveSupport::Callbacks
+ # define_callbacks :save
+ #
+ # def save
+ # run_callbacks :save do
+ # puts "- save"
+ # end
+ # end
+ # end
+ #
+ # class PersonRecord < Record
+ # set_callback :save, :before, :saving_message
+ # def saving_message
+ # puts "saving..."
+ # end
+ #
+ # set_callback :save, :after do |object|
+ # puts "saved"
+ # end
+ # end
+ #
+ # person = PersonRecord.new
+ # person.save
+ #
+ # Output:
+ # saving...
+ # - save
+ # saved
+ module Callbacks
+ extend Concern
+
+ included do
+ extend ActiveSupport::DescendantsTracker
+ class_attribute :__callbacks, instance_writer: false, default: {}
+ end
+
+ CALLBACK_FILTER_TYPES = [:before, :after, :around]
+
+ # Runs the callbacks for the given event.
+ #
+ # Calls the before and around callbacks in the order they were set, yields
+ # the block (if given one), and then runs the after callbacks in reverse
+ # order.
+ #
+ # If the callback chain was halted, returns +false+. Otherwise returns the
+ # result of the block, +nil+ if no callbacks have been set, or +true+
+ # if callbacks have been set but no block is given.
+ #
+ # run_callbacks :save do
+ # save
+ # end
+ #
+ #--
+ #
+ # As this method is used in many places, and often wraps large portions of
+ # user code, it has an additional design goal of minimizing its impact on
+ # the visible call stack. An exception from inside a :before or :after
+ # callback can be as noisy as it likes -- but when control has passed
+ # smoothly through and into the supplied block, we want as little evidence
+ # as possible that we were here.
+ def run_callbacks(kind)
+ callbacks = __callbacks[kind.to_sym]
+
+ if callbacks.empty?
+ yield if block_given?
+ else
+ env = Filters::Environment.new(self, false, nil)
+ next_sequence = callbacks.compile
+
+ invoke_sequence = Proc.new do
+ skipped = nil
+ while true
+ current = next_sequence
+ current.invoke_before(env)
+ if current.final?
+ env.value = !env.halted && (!block_given? || yield)
+ elsif current.skip?(env)
+ (skipped ||= []) << current
+ next_sequence = next_sequence.nested
+ next
+ else
+ next_sequence = next_sequence.nested
+ begin
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
+ target.send(method, *arguments, &block)
+ ensure
+ next_sequence = current
+ end
+ end
+ current.invoke_after(env)
+ skipped.pop.invoke_after(env) while skipped && skipped.first
+ break env.value
+ end
+ end
+
+ # Common case: no 'around' callbacks defined
+ if next_sequence.final?
+ next_sequence.invoke_before(env)
+ env.value = !env.halted && (!block_given? || yield)
+ next_sequence.invoke_after(env)
+ env.value
+ else
+ invoke_sequence.call
+ end
+ end
+ end
+
+ private
+
+ # A hook invoked every time a before callback is halted.
+ # This can be overridden in ActiveSupport::Callbacks implementors in order
+ # to provide better debugging/logging.
+ def halted_callback_hook(filter)
+ end
+
+ module Conditionals # :nodoc:
+ class Value
+ def initialize(&block)
+ @block = block
+ end
+ def call(target, value); @block.call(value); end
+ end
+ end
+
+ module Filters
+ Environment = Struct.new(:target, :halted, :value)
+
+ class Before
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
+ halted_lambda = chain_config[:terminator]
+
+ if user_conditions.any?
+ halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
+ else
+ halting(callback_sequence, user_callback, halted_lambda, filter)
+ end
+ end
+
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
+ callback_sequence.before do |env|
+ target = env.target
+ value = env.value
+ halted = env.halted
+
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
+ result_lambda = -> { user_callback.call target, value }
+ env.halted = halted_lambda.call(target, result_lambda)
+ if env.halted
+ target.send :halted_callback_hook, filter
+ end
+ end
+
+ env
+ end
+ end
+ private_class_method :halting_and_conditional
+
+ def self.halting(callback_sequence, user_callback, halted_lambda, filter)
+ callback_sequence.before do |env|
+ target = env.target
+ value = env.value
+ halted = env.halted
+
+ unless halted
+ result_lambda = -> { user_callback.call target, value }
+ env.halted = halted_lambda.call(target, result_lambda)
+
+ if env.halted
+ target.send :halted_callback_hook, filter
+ end
+ end
+
+ env
+ end
+ end
+ private_class_method :halting
+ end
+
+ class After
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config)
+ if chain_config[:skip_after_callbacks_if_terminated]
+ if user_conditions.any?
+ halting_and_conditional(callback_sequence, user_callback, user_conditions)
+ else
+ halting(callback_sequence, user_callback)
+ end
+ else
+ if user_conditions.any?
+ conditional callback_sequence, user_callback, user_conditions
+ else
+ simple callback_sequence, user_callback
+ end
+ end
+ end
+
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
+ callback_sequence.after do |env|
+ target = env.target
+ value = env.value
+ halted = env.halted
+
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
+ user_callback.call target, value
+ end
+
+ env
+ end
+ end
+ private_class_method :halting_and_conditional
+
+ def self.halting(callback_sequence, user_callback)
+ callback_sequence.after do |env|
+ unless env.halted
+ user_callback.call env.target, env.value
+ end
+
+ env
+ end
+ end
+ private_class_method :halting
+
+ def self.conditional(callback_sequence, user_callback, user_conditions)
+ callback_sequence.after do |env|
+ target = env.target
+ value = env.value
+
+ if user_conditions.all? { |c| c.call(target, value) }
+ user_callback.call target, value
+ end
+
+ env
+ end
+ end
+ private_class_method :conditional
+
+ def self.simple(callback_sequence, user_callback)
+ callback_sequence.after do |env|
+ user_callback.call env.target, env.value
+
+ env
+ end
+ end
+ private_class_method :simple
+ end
+ end
+
+ class Callback #:nodoc:#
+ def self.build(chain, filter, kind, options)
+ if filter.is_a?(String)
+ raise ArgumentError, <<-MSG.squish
+ Passing string to define a callback is not supported. See the `.set_callback`
+ documentation to see supported values.
+ MSG
+ end
+
+ new chain.name, filter, kind, options, chain.config
+ end
+
+ attr_accessor :kind, :name
+ attr_reader :chain_config
+
+ def initialize(name, filter, kind, options, chain_config)
+ @chain_config = chain_config
+ @name = name
+ @kind = kind
+ @filter = filter
+ @key = compute_identifier filter
+ @if = check_conditionals(Array(options[:if]))
+ @unless = check_conditionals(Array(options[:unless]))
+ end
+
+ def filter; @key; end
+ def raw_filter; @filter; end
+
+ def merge_conditional_options(chain, if_option:, unless_option:)
+ options = {
+ if: @if.dup,
+ unless: @unless.dup
+ }
+
+ options[:if].concat Array(unless_option)
+ options[:unless].concat Array(if_option)
+
+ self.class.build chain, @filter, @kind, options
+ end
+
+ def matches?(_kind, _filter)
+ @kind == _kind && filter == _filter
+ end
+
+ def duplicates?(other)
+ case @filter
+ when Symbol
+ matches?(other.kind, other.filter)
+ else
+ false
+ end
+ end
+
+ # Wraps code with filter
+ def apply(callback_sequence)
+ user_conditions = conditions_lambdas
+ user_callback = CallTemplate.build(@filter, self)
+
+ case kind
+ when :before
+ Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
+ when :after
+ Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
+ when :around
+ callback_sequence.around(user_callback, user_conditions)
+ end
+ end
+
+ def current_scopes
+ Array(chain_config[:scope]).map { |s| public_send(s) }
+ end
+
+ private
+ def check_conditionals(conditionals)
+ if conditionals.any? { |c| c.is_a?(String) }
+ raise ArgumentError, <<-MSG.squish
+ Passing string to be evaluated in :if and :unless conditional
+ options is not supported. Pass a symbol for an instance method,
+ or a lambda, proc or block, instead.
+ MSG
+ end
+
+ conditionals
+ end
+
+ def compute_identifier(filter)
+ case filter
+ when ::Proc
+ filter.object_id
+ else
+ filter
+ end
+ end
+
+ def conditions_lambdas
+ @if.map { |c| CallTemplate.build(c, self).make_lambda } +
+ @unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
+ end
+ end
+
+ # A future invocation of user-supplied code (either as a callback,
+ # or a condition filter).
+ class CallTemplate # :nodoc:
+ def initialize(target, method, arguments, block)
+ @override_target = target
+ @method_name = method
+ @arguments = arguments
+ @override_block = block
+ end
+
+ # Return the parts needed to make this call, with the given
+ # input values.
+ #
+ # Returns an array of the form:
+ #
+ # [target, block, method, *arguments]
+ #
+ # This array can be used as such:
+ #
+ # target.send(method, *arguments, &block)
+ #
+ # The actual invocation is left up to the caller to minimize
+ # call stack pollution.
+ def expand(target, value, block)
+ result = @arguments.map { |arg|
+ case arg
+ when :value; value
+ when :target; target
+ when :block; block || raise(ArgumentError)
+ end
+ }
+
+ result.unshift @method_name
+ result.unshift @override_block || block
+ result.unshift @override_target || target
+
+ # target, block, method, *arguments = result
+ # target.send(method, *arguments, &block)
+ result
+ end
+
+ # Return a lambda that will make this call when given the input
+ # values.
+ def make_lambda
+ lambda do |target, value, &block|
+ target, block, method, *arguments = expand(target, value, block)
+ target.send(method, *arguments, &block)
+ end
+ end
+
+ # Return a lambda that will make this call when given the input
+ # values, but then return the boolean inverse of that result.
+ def inverted_lambda
+ lambda do |target, value, &block|
+ target, block, method, *arguments = expand(target, value, block)
+ ! target.send(method, *arguments, &block)
+ end
+ end
+
+ # Filters support:
+ #
+ # Symbols:: A method to call.
+ # Procs:: A proc to call with the object.
+ # Objects:: An object with a before_foo method on it to call.
+ #
+ # All of these objects are converted into a CallTemplate and handled
+ # the same after this point.
+ def self.build(filter, callback)
+ case filter
+ when Symbol
+ new(nil, filter, [], nil)
+ when Conditionals::Value
+ new(filter, :call, [:target, :value], nil)
+ when ::Proc
+ if filter.arity > 1
+ new(nil, :instance_exec, [:target, :block], filter)
+ elsif filter.arity > 0
+ new(nil, :instance_exec, [:target], filter)
+ else
+ new(nil, :instance_exec, [], filter)
+ end
+ else
+ method_to_call = callback.current_scopes.join("_")
+
+ new(filter, method_to_call, [:target], nil)
+ end
+ end
+ end
+
+ # Execute before and after filters in a sequence instead of
+ # chaining them with nested lambda calls, see:
+ # https://github.com/rails/rails/issues/18011
+ class CallbackSequence # :nodoc:
+ def initialize(nested = nil, call_template = nil, user_conditions = nil)
+ @nested = nested
+ @call_template = call_template
+ @user_conditions = user_conditions
+
+ @before = []
+ @after = []
+ end
+
+ def before(&before)
+ @before.unshift(before)
+ self
+ end
+
+ def after(&after)
+ @after.push(after)
+ self
+ end
+
+ def around(call_template, user_conditions)
+ CallbackSequence.new(self, call_template, user_conditions)
+ end
+
+ def skip?(arg)
+ arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
+ end
+
+ def nested
+ @nested
+ end
+
+ def final?
+ !@call_template
+ end
+
+ def expand_call_template(arg, block)
+ @call_template.expand(arg.target, arg.value, block)
+ end
+
+ def invoke_before(arg)
+ @before.each { |b| b.call(arg) }
+ end
+
+ def invoke_after(arg)
+ @after.each { |a| a.call(arg) }
+ end
+ end
+
+ class CallbackChain #:nodoc:#
+ include Enumerable
+
+ attr_reader :name, :config
+
+ def initialize(name, config)
+ @name = name
+ @config = {
+ scope: [:kind],
+ terminator: default_terminator
+ }.merge!(config)
+ @chain = []
+ @callbacks = nil
+ @mutex = Mutex.new
+ end
+
+ def each(&block); @chain.each(&block); end
+ def index(o); @chain.index(o); end
+ def empty?; @chain.empty?; end
+
+ def insert(index, o)
+ @callbacks = nil
+ @chain.insert(index, o)
+ end
+
+ def delete(o)
+ @callbacks = nil
+ @chain.delete(o)
+ end
+
+ def clear
+ @callbacks = nil
+ @chain.clear
+ self
+ end
+
+ def initialize_copy(other)
+ @callbacks = nil
+ @chain = other.chain.dup
+ @mutex = Mutex.new
+ end
+
+ def compile
+ @callbacks || @mutex.synchronize do
+ final_sequence = CallbackSequence.new
+ @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
+ callback.apply callback_sequence
+ end
+ end
+ end
+
+ def append(*callbacks)
+ callbacks.each { |c| append_one(c) }
+ end
+
+ def prepend(*callbacks)
+ callbacks.each { |c| prepend_one(c) }
+ end
+
+ protected
+ def chain; @chain; end
+
+ private
+
+ def append_one(callback)
+ @callbacks = nil
+ remove_duplicates(callback)
+ @chain.push(callback)
+ end
+
+ def prepend_one(callback)
+ @callbacks = nil
+ remove_duplicates(callback)
+ @chain.unshift(callback)
+ end
+
+ def remove_duplicates(callback)
+ @callbacks = nil
+ @chain.delete_if { |c| callback.duplicates?(c) }
+ end
+
+ def default_terminator
+ Proc.new do |target, result_lambda|
+ terminate = true
+ catch(:abort) do
+ result_lambda.call
+ terminate = false
+ end
+ terminate
+ end
+ end
+ end
+
+ module ClassMethods
+ def normalize_callback_params(filters, block) # :nodoc:
+ type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
+ options = filters.extract_options!
+ filters.unshift(block) if block
+ [type, filters, options.dup]
+ end
+
+ # This is used internally to append, prepend and skip callbacks to the
+ # CallbackChain.
+ def __update_callbacks(name) #:nodoc:
+ ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
+ chain = target.get_callbacks name
+ yield target, chain.dup
+ end
+ end
+
+ # Install a callback for the given event.
+ #
+ # set_callback :save, :before, :before_method
+ # set_callback :save, :after, :after_method, if: :condition
+ # set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
+ #
+ # The second argument indicates whether the callback is to be run +:before+,
+ # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
+ # means the first example above can also be written as:
+ #
+ # set_callback :save, :before_method
+ #
+ # The callback can be specified as a symbol naming an instance method; as a
+ # proc, lambda, or block; or as an object that responds to a certain method
+ # determined by the :scope argument to +define_callbacks+.
+ #
+ # If a proc, lambda, or block is given, its body is evaluated in the context
+ # of the current object. It can also optionally accept the current object as
+ # an argument.
+ #
+ # Before and around callbacks are called in the order that they are set;
+ # after callbacks are called in the reverse order.
+ #
+ # Around callbacks can access the return value from the event, if it
+ # wasn't halted, from the +yield+ call.
+ #
+ # ===== Options
+ #
+ # * :if - A symbol or an array of symbols, each naming an instance
+ # method or a proc; the callback will be called only when they all return
+ # a true value.
+ # * :unless - A symbol or an array of symbols, each naming an
+ # instance method or a proc; the callback will be called only when they
+ # all return a false value.
+ # * :prepend - If +true+, the callback will be prepended to the
+ # existing chain rather than appended.
+ def set_callback(name, *filter_list, &block)
+ type, filters, options = normalize_callback_params(filter_list, block)
+
+ self_chain = get_callbacks name
+ mapped = filters.map do |filter|
+ Callback.build(self_chain, filter, type, options)
+ end
+
+ __update_callbacks(name) do |target, chain|
+ options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
+ target.set_callbacks name, chain
+ end
+ end
+
+ # Skip a previously set callback. Like +set_callback+, :if or
+ # :unless options may be passed in order to control when the
+ # callback is skipped.
+ #
+ # class Writer < Person
+ # skip_callback :validate, :before, :check_membership, if: -> { age > 18 }
+ # end
+ #
+ # An ArgumentError will be raised if the callback has not
+ # already been set (unless the :raise option is set to false).
+ def skip_callback(name, *filter_list, &block)
+ type, filters, options = normalize_callback_params(filter_list, block)
+
+ options[:raise] = true unless options.key?(:raise)
+
+ __update_callbacks(name) do |target, chain|
+ filters.each do |filter|
+ callback = chain.find { |c| c.matches?(type, filter) }
+
+ if !callback && options[:raise]
+ raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
+ end
+
+ if callback && (options.key?(:if) || options.key?(:unless))
+ new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
+ chain.insert(chain.index(callback), new_callback)
+ end
+
+ chain.delete(callback)
+ end
+ target.set_callbacks name, chain
+ end
+ end
+
+ # Remove all set callbacks for the given event.
+ def reset_callbacks(name)
+ callbacks = get_callbacks name
+
+ ActiveSupport::DescendantsTracker.descendants(self).each do |target|
+ chain = target.get_callbacks(name).dup
+ callbacks.each { |c| chain.delete(c) }
+ target.set_callbacks name, chain
+ end
+
+ set_callbacks(name, callbacks.dup.clear)
+ end
+
+ # Define sets of events in the object life cycle that support callbacks.
+ #
+ # define_callbacks :validate
+ # define_callbacks :initialize, :save, :destroy
+ #
+ # ===== Options
+ #
+ # * :terminator - Determines when a before filter will halt the
+ # callback chain, preventing following before and around callbacks from
+ # being called and the event from being triggered.
+ # This should be a lambda to be executed.
+ # The current object and the result lambda of the callback will be provided
+ # to the terminator lambda.
+ #
+ # define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
+ #
+ # In this example, if any before validate callbacks returns +false+,
+ # any successive before and around callback is not executed.
+ #
+ # The default terminator halts the chain when a callback throws +:abort+.
+ #
+ # * :skip_after_callbacks_if_terminated - Determines if after
+ # callbacks should be terminated by the :terminator option. By
+ # default after callbacks are executed no matter if callback chain was
+ # terminated or not. This option has no effect if :terminator
+ # option is set to +nil+.
+ #
+ # * :scope - Indicates which methods should be executed when an
+ # object is used as a callback.
+ #
+ # class Audit
+ # def before(caller)
+ # puts 'Audit: before is called'
+ # end
+ #
+ # def before_save(caller)
+ # puts 'Audit: before_save is called'
+ # end
+ # end
+ #
+ # class Account
+ # include ActiveSupport::Callbacks
+ #
+ # define_callbacks :save
+ # set_callback :save, :before, Audit.new
+ #
+ # def save
+ # run_callbacks :save do
+ # puts 'save in main'
+ # end
+ # end
+ # end
+ #
+ # In the above case whenever you save an account the method
+ # Audit#before will be called. On the other hand
+ #
+ # define_callbacks :save, scope: [:kind, :name]
+ #
+ # would trigger Audit#before_save instead. That's constructed
+ # by calling #{kind}_#{name} on the given instance. In this
+ # case "kind" is "before" and "name" is "save". In this context +:kind+
+ # and +:name+ have special meanings: +:kind+ refers to the kind of
+ # callback (before/after/around) and +:name+ refers to the method on
+ # which callbacks are being defined.
+ #
+ # A declaration like
+ #
+ # define_callbacks :save, scope: [:name]
+ #
+ # would call Audit#save.
+ #
+ # ===== Notes
+ #
+ # +names+ passed to +define_callbacks+ must not end with
+ # !, ? or =.
+ #
+ # Calling +define_callbacks+ multiple times with the same +names+ will
+ # overwrite previous callbacks registered with +set_callback+.
+ def define_callbacks(*names)
+ options = names.extract_options!
+
+ names.each do |name|
+ name = name.to_sym
+
+ set_callbacks name, CallbackChain.new(name, options)
+
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def _run_#{name}_callbacks(&block)
+ run_callbacks #{name.inspect}, &block
+ end
+
+ def self._#{name}_callbacks
+ get_callbacks(#{name.inspect})
+ end
+
+ def self._#{name}_callbacks=(value)
+ set_callbacks(#{name.inspect}, value)
+ end
+
+ def _#{name}_callbacks
+ __callbacks[#{name.inspect}]
+ end
+ RUBY
+ end
+ end
+
+ protected
+
+ def get_callbacks(name) # :nodoc:
+ __callbacks[name.to_sym]
+ end
+
+ def set_callbacks(name, callbacks) # :nodoc:
+ self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/concern.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/concern.rb
new file mode 100644
index 0000000..5d356a0
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/concern.rb
@@ -0,0 +1,148 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # A typical module looks like this:
+ #
+ # module M
+ # def self.included(base)
+ # base.extend ClassMethods
+ # base.class_eval do
+ # scope :disabled, -> { where(disabled: true) }
+ # end
+ # end
+ #
+ # module ClassMethods
+ # ...
+ # end
+ # end
+ #
+ # By using ActiveSupport::Concern the above module could instead be
+ # written as:
+ #
+ # require 'active_support/concern'
+ #
+ # module M
+ # extend ActiveSupport::Concern
+ #
+ # included do
+ # scope :disabled, -> { where(disabled: true) }
+ # end
+ #
+ # class_methods do
+ # ...
+ # end
+ # end
+ #
+ # Moreover, it gracefully handles module dependencies. Given a +Foo+ module
+ # and a +Bar+ module which depends on the former, we would typically write the
+ # following:
+ #
+ # module Foo
+ # def self.included(base)
+ # base.class_eval do
+ # def self.method_injected_by_foo
+ # ...
+ # end
+ # end
+ # end
+ # end
+ #
+ # module Bar
+ # def self.included(base)
+ # base.method_injected_by_foo
+ # end
+ # end
+ #
+ # class Host
+ # include Foo # We need to include this dependency for Bar
+ # include Bar # Bar is the module that Host really needs
+ # end
+ #
+ # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
+ # could try to hide these from +Host+ directly including +Foo+ in +Bar+:
+ #
+ # module Bar
+ # include Foo
+ # def self.included(base)
+ # base.method_injected_by_foo
+ # end
+ # end
+ #
+ # class Host
+ # include Bar
+ # end
+ #
+ # Unfortunately this won't work, since when +Foo+ is included, its base
+ # is the +Bar+ module, not the +Host+ class. With ActiveSupport::Concern,
+ # module dependencies are properly resolved:
+ #
+ # require 'active_support/concern'
+ #
+ # module Foo
+ # extend ActiveSupport::Concern
+ # included do
+ # def self.method_injected_by_foo
+ # ...
+ # end
+ # end
+ # end
+ #
+ # module Bar
+ # extend ActiveSupport::Concern
+ # include Foo
+ #
+ # included do
+ # self.method_injected_by_foo
+ # end
+ # end
+ #
+ # class Host
+ # include Bar # It works, now Bar takes care of its dependencies
+ # end
+ module Concern
+ class MultipleIncludedBlocks < StandardError #:nodoc:
+ def initialize
+ super "Cannot define multiple 'included' blocks for a Concern"
+ end
+ end
+
+ def self.extended(base) #:nodoc:
+ base.instance_variable_set(:@_dependencies, [])
+ end
+
+ def append_features(base)
+ if base.instance_variable_defined?(:@_dependencies)
+ base.instance_variable_get(:@_dependencies) << self
+ false
+ else
+ return false if base < self
+ @_dependencies.each { |dep| base.include(dep) }
+ super
+ base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
+ base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
+ end
+ end
+
+ def included(base = nil, &block)
+ if base.nil?
+ if instance_variable_defined?(:@_included_block)
+ if @_included_block.source_location != block.source_location
+ raise MultipleIncludedBlocks
+ end
+ else
+ @_included_block = block
+ end
+ else
+ super
+ end
+ end
+
+ def class_methods(&class_methods_module_definition)
+ mod = const_defined?(:ClassMethods, false) ?
+ const_get(:ClassMethods) :
+ const_set(:ClassMethods, Module.new)
+
+ mod.module_eval(&class_methods_module_definition)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb
new file mode 100644
index 0000000..a8455c0
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "monitor"
+
+module ActiveSupport
+ module Concurrency
+ # A monitor that will permit dependency loading while blocked waiting for
+ # the lock.
+ class LoadInterlockAwareMonitor < Monitor
+ # Enters an exclusive section, but allows dependency loading while blocked
+ def mon_enter
+ mon_try_enter ||
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super }
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/concurrency/share_lock.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/concurrency/share_lock.rb
new file mode 100644
index 0000000..f18ccf1
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/concurrency/share_lock.rb
@@ -0,0 +1,227 @@
+# frozen_string_literal: true
+
+require "thread"
+require "monitor"
+
+module ActiveSupport
+ module Concurrency
+ # A share/exclusive lock, otherwise known as a read/write lock.
+ #
+ # https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
+ class ShareLock
+ include MonitorMixin
+
+ # We track Thread objects, instead of just using counters, because
+ # we need exclusive locks to be reentrant, and we need to be able
+ # to upgrade share locks to exclusive.
+
+ def raw_state # :nodoc:
+ synchronize do
+ threads = @sleeping.keys | @sharing.keys | @waiting.keys
+ threads |= [@exclusive_thread] if @exclusive_thread
+
+ data = {}
+
+ threads.each do |thread|
+ purpose, compatible = @waiting[thread]
+
+ data[thread] = {
+ thread: thread,
+ sharing: @sharing[thread],
+ exclusive: @exclusive_thread == thread,
+ purpose: purpose,
+ compatible: compatible,
+ waiting: !!@waiting[thread],
+ sleeper: @sleeping[thread],
+ }
+ end
+
+ # NB: Yields while holding our *internal* synchronize lock,
+ # which is supposed to be used only for a few instructions at
+ # a time. This allows the caller to inspect additional state
+ # without things changing out from underneath, but would have
+ # disastrous effects upon normal operation. Fortunately, this
+ # method is only intended to be called when things have
+ # already gone wrong.
+ yield data
+ end
+ end
+
+ def initialize
+ super()
+
+ @cv = new_cond
+
+ @sharing = Hash.new(0)
+ @waiting = {}
+ @sleeping = {}
+ @exclusive_thread = nil
+ @exclusive_depth = 0
+ end
+
+ # Returns false if +no_wait+ is set and the lock is not
+ # immediately available. Otherwise, returns true after the lock
+ # has been acquired.
+ #
+ # +purpose+ and +compatible+ work together; while this thread is
+ # waiting for the exclusive lock, it will yield its share (if any)
+ # to any other attempt whose +purpose+ appears in this attempt's
+ # +compatible+ list. This allows a "loose" upgrade, which, being
+ # less strict, prevents some classes of deadlocks.
+ #
+ # For many resources, loose upgrades are sufficient: if a thread
+ # is awaiting a lock, it is not running any other code. With
+ # +purpose+ matching, it is possible to yield only to other
+ # threads whose activity will not interfere.
+ def start_exclusive(purpose: nil, compatible: [], no_wait: false)
+ synchronize do
+ unless @exclusive_thread == Thread.current
+ if busy_for_exclusive?(purpose)
+ return false if no_wait
+
+ yield_shares(purpose: purpose, compatible: compatible, block_share: true) do
+ wait_for(:start_exclusive) { busy_for_exclusive?(purpose) }
+ end
+ end
+ @exclusive_thread = Thread.current
+ end
+ @exclusive_depth += 1
+
+ true
+ end
+ end
+
+ # Relinquish the exclusive lock. Must only be called by the thread
+ # that called start_exclusive (and currently holds the lock).
+ def stop_exclusive(compatible: [])
+ synchronize do
+ raise "invalid unlock" if @exclusive_thread != Thread.current
+
+ @exclusive_depth -= 1
+ if @exclusive_depth == 0
+ @exclusive_thread = nil
+
+ if eligible_waiters?(compatible)
+ yield_shares(compatible: compatible, block_share: true) do
+ wait_for(:stop_exclusive) { @exclusive_thread || eligible_waiters?(compatible) }
+ end
+ end
+ @cv.broadcast
+ end
+ end
+ end
+
+ def start_sharing
+ synchronize do
+ if @sharing[Thread.current] > 0 || @exclusive_thread == Thread.current
+ # We already hold a lock; nothing to wait for
+ elsif @waiting[Thread.current]
+ # We're nested inside a +yield_shares+ call: we'll resume as
+ # soon as there isn't an exclusive lock in our way
+ wait_for(:start_sharing) { @exclusive_thread }
+ else
+ # This is an initial / outermost share call: any outstanding
+ # requests for an exclusive lock get to go first
+ wait_for(:start_sharing) { busy_for_sharing?(false) }
+ end
+ @sharing[Thread.current] += 1
+ end
+ end
+
+ def stop_sharing
+ synchronize do
+ if @sharing[Thread.current] > 1
+ @sharing[Thread.current] -= 1
+ else
+ @sharing.delete Thread.current
+ @cv.broadcast
+ end
+ end
+ end
+
+ # Execute the supplied block while holding the Exclusive lock. If
+ # +no_wait+ is set and the lock is not immediately available,
+ # returns +nil+ without yielding. Otherwise, returns the result of
+ # the block.
+ #
+ # See +start_exclusive+ for other options.
+ def exclusive(purpose: nil, compatible: [], after_compatible: [], no_wait: false)
+ if start_exclusive(purpose: purpose, compatible: compatible, no_wait: no_wait)
+ begin
+ yield
+ ensure
+ stop_exclusive(compatible: after_compatible)
+ end
+ end
+ end
+
+ # Execute the supplied block while holding the Share lock.
+ def sharing
+ start_sharing
+ begin
+ yield
+ ensure
+ stop_sharing
+ end
+ end
+
+ # Temporarily give up all held Share locks while executing the
+ # supplied block, allowing any +compatible+ exclusive lock request
+ # to proceed.
+ def yield_shares(purpose: nil, compatible: [], block_share: false)
+ loose_shares = previous_wait = nil
+ synchronize do
+ if loose_shares = @sharing.delete(Thread.current)
+ if previous_wait = @waiting[Thread.current]
+ purpose = nil unless purpose == previous_wait[0]
+ compatible &= previous_wait[1]
+ end
+ compatible |= [false] unless block_share
+ @waiting[Thread.current] = [purpose, compatible]
+ end
+
+ @cv.broadcast
+ end
+
+ begin
+ yield
+ ensure
+ synchronize do
+ wait_for(:yield_shares) { @exclusive_thread && @exclusive_thread != Thread.current }
+
+ if previous_wait
+ @waiting[Thread.current] = previous_wait
+ else
+ @waiting.delete Thread.current
+ end
+ @sharing[Thread.current] = loose_shares if loose_shares
+ end
+ end
+ end
+
+ private
+
+ # Must be called within synchronize
+ def busy_for_exclusive?(purpose)
+ busy_for_sharing?(purpose) ||
+ @sharing.size > (@sharing[Thread.current] > 0 ? 1 : 0)
+ end
+
+ def busy_for_sharing?(purpose)
+ (@exclusive_thread && @exclusive_thread != Thread.current) ||
+ @waiting.any? { |t, (_, c)| t != Thread.current && !c.include?(purpose) }
+ end
+
+ def eligible_waiters?(compatible)
+ @waiting.any? { |t, (p, _)| compatible.include?(p) && @waiting.all? { |t2, (_, c2)| t == t2 || c2.include?(p) } }
+ end
+
+ def wait_for(method)
+ @sleeping[Thread.current] = method
+ @cv.wait_while { yield }
+ ensure
+ @sleeping.delete Thread.current
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/configurable.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/configurable.rb
new file mode 100644
index 0000000..4d6f781
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/configurable.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+require "active_support/concern"
+require "active_support/ordered_options"
+require "active_support/core_ext/array/extract_options"
+require "active_support/core_ext/regexp"
+
+module ActiveSupport
+ # Configurable provides a config method to store and retrieve
+ # configuration options as an OrderedHash.
+ module Configurable
+ extend ActiveSupport::Concern
+
+ class Configuration < ActiveSupport::InheritableOptions
+ def compile_methods!
+ self.class.compile_methods!(keys)
+ end
+
+ # Compiles reader methods so we don't have to go through method_missing.
+ def self.compile_methods!(keys)
+ keys.reject { |m| method_defined?(m) }.each do |key|
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{key}; _get(#{key.inspect}); end
+ RUBY
+ end
+ end
+ end
+
+ module ClassMethods
+ def config
+ @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
+ superclass.config.inheritable_copy
+ else
+ # create a new "anonymous" class that will host the compiled reader methods
+ Class.new(Configuration).new
+ end
+ end
+
+ def configure
+ yield config
+ end
+
+ # Allows you to add shortcut so that you don't have to refer to attribute
+ # through config. Also look at the example for config to contrast.
+ #
+ # Defines both class and instance config accessors.
+ #
+ # class User
+ # include ActiveSupport::Configurable
+ # config_accessor :allowed_access
+ # end
+ #
+ # User.allowed_access # => nil
+ # User.allowed_access = false
+ # User.allowed_access # => false
+ #
+ # user = User.new
+ # user.allowed_access # => false
+ # user.allowed_access = true
+ # user.allowed_access # => true
+ #
+ # User.allowed_access # => false
+ #
+ # The attribute name must be a valid method name in Ruby.
+ #
+ # class User
+ # include ActiveSupport::Configurable
+ # config_accessor :"1_Badname"
+ # end
+ # # => NameError: invalid config attribute name
+ #
+ # To opt out of the instance writer method, pass instance_writer: false.
+ # To opt out of the instance reader method, pass instance_reader: false.
+ #
+ # class User
+ # include ActiveSupport::Configurable
+ # config_accessor :allowed_access, instance_reader: false, instance_writer: false
+ # end
+ #
+ # User.allowed_access = false
+ # User.allowed_access # => false
+ #
+ # User.new.allowed_access = true # => NoMethodError
+ # User.new.allowed_access # => NoMethodError
+ #
+ # Or pass instance_accessor: false, to opt out both instance methods.
+ #
+ # class User
+ # include ActiveSupport::Configurable
+ # config_accessor :allowed_access, instance_accessor: false
+ # end
+ #
+ # User.allowed_access = false
+ # User.allowed_access # => false
+ #
+ # User.new.allowed_access = true # => NoMethodError
+ # User.new.allowed_access # => NoMethodError
+ #
+ # Also you can pass a block to set up the attribute with a default value.
+ #
+ # class User
+ # include ActiveSupport::Configurable
+ # config_accessor :hair_colors do
+ # [:brown, :black, :blonde, :red]
+ # end
+ # end
+ #
+ # User.hair_colors # => [:brown, :black, :blonde, :red]
+ def config_accessor(*names)
+ options = names.extract_options!
+
+ names.each do |name|
+ raise NameError.new("invalid config attribute name") unless /\A[_A-Za-z]\w*\z/.match?(name)
+
+ reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
+ writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
+
+ singleton_class.class_eval reader, __FILE__, reader_line
+ singleton_class.class_eval writer, __FILE__, writer_line
+
+ unless options[:instance_accessor] == false
+ class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
+ class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
+ end
+ send("#{name}=", yield) if block_given?
+ end
+ end
+ private :config_accessor
+ end
+
+ # Reads and writes attributes from a configuration OrderedHash.
+ #
+ # require 'active_support/configurable'
+ #
+ # class User
+ # include ActiveSupport::Configurable
+ # end
+ #
+ # user = User.new
+ #
+ # user.config.allowed_access = true
+ # user.config.level = 1
+ #
+ # user.config.allowed_access # => true
+ # user.config.level # => 1
+ def config
+ @_config ||= self.class.config.inheritable_copy
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext.rb
new file mode 100644
index 0000000..f590605
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Dir.glob(File.expand_path("core_ext/*.rb", __dir__)).each do |path|
+ require path
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array.rb
new file mode 100644
index 0000000..6d83b76
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/array/wrap"
+require "active_support/core_ext/array/access"
+require "active_support/core_ext/array/conversions"
+require "active_support/core_ext/array/extract_options"
+require "active_support/core_ext/array/grouping"
+require "active_support/core_ext/array/prepend_and_append"
+require "active_support/core_ext/array/inquiry"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/access.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/access.rb
new file mode 100644
index 0000000..b7ff7a3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/access.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+class Array
+ # Returns the tail of the array from +position+.
+ #
+ # %w( a b c d ).from(0) # => ["a", "b", "c", "d"]
+ # %w( a b c d ).from(2) # => ["c", "d"]
+ # %w( a b c d ).from(10) # => []
+ # %w().from(0) # => []
+ # %w( a b c d ).from(-2) # => ["c", "d"]
+ # %w( a b c ).from(-10) # => []
+ def from(position)
+ self[position, length] || []
+ end
+
+ # Returns the beginning of the array up to +position+.
+ #
+ # %w( a b c d ).to(0) # => ["a"]
+ # %w( a b c d ).to(2) # => ["a", "b", "c"]
+ # %w( a b c d ).to(10) # => ["a", "b", "c", "d"]
+ # %w().to(0) # => []
+ # %w( a b c d ).to(-2) # => ["a", "b", "c"]
+ # %w( a b c ).to(-10) # => []
+ def to(position)
+ if position >= 0
+ take position + 1
+ else
+ self[0..position]
+ end
+ end
+
+ # Returns a copy of the Array without the specified elements.
+ #
+ # people = ["David", "Rafael", "Aaron", "Todd"]
+ # people.without "Aaron", "Todd"
+ # # => ["David", "Rafael"]
+ #
+ # Note: This is an optimization of Enumerable#without that uses Array#-
+ # instead of Array#reject for performance reasons.
+ def without(*elements)
+ self - elements
+ end
+
+ # Equal to self[1].
+ #
+ # %w( a b c d e ).second # => "b"
+ def second
+ self[1]
+ end
+
+ # Equal to self[2].
+ #
+ # %w( a b c d e ).third # => "c"
+ def third
+ self[2]
+ end
+
+ # Equal to self[3].
+ #
+ # %w( a b c d e ).fourth # => "d"
+ def fourth
+ self[3]
+ end
+
+ # Equal to self[4].
+ #
+ # %w( a b c d e ).fifth # => "e"
+ def fifth
+ self[4]
+ end
+
+ # Equal to self[41]. Also known as accessing "the reddit".
+ #
+ # (1..42).to_a.forty_two # => 42
+ def forty_two
+ self[41]
+ end
+
+ # Equal to self[-3].
+ #
+ # %w( a b c d e ).third_to_last # => "c"
+ def third_to_last
+ self[-3]
+ end
+
+ # Equal to self[-2].
+ #
+ # %w( a b c d e ).second_to_last # => "d"
+ def second_to_last
+ self[-2]
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/conversions.rb
new file mode 100644
index 0000000..ea688ed
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/conversions.rb
@@ -0,0 +1,213 @@
+# frozen_string_literal: true
+
+require "active_support/xml_mini"
+require "active_support/core_ext/hash/keys"
+require "active_support/core_ext/string/inflections"
+require "active_support/core_ext/object/to_param"
+require "active_support/core_ext/object/to_query"
+
+class Array
+ # Converts the array to a comma-separated sentence where the last element is
+ # joined by the connector word.
+ #
+ # You can pass the following options to change the default behavior. If you
+ # pass an option key that doesn't exist in the list below, it will raise an
+ # ArgumentError.
+ #
+ # ==== Options
+ #
+ # * :words_connector - The sign or word used to join the elements
+ # in arrays with two or more elements (default: ", ").
+ # * :two_words_connector - The sign or word used to join the elements
+ # in arrays with two elements (default: " and ").
+ # * :last_word_connector - The sign or word used to join the last element
+ # in arrays with three or more elements (default: ", and ").
+ # * :locale - If +i18n+ is available, you can set a locale and use
+ # the connector options defined on the 'support.array' namespace in the
+ # corresponding dictionary file.
+ #
+ # ==== Examples
+ #
+ # [].to_sentence # => ""
+ # ['one'].to_sentence # => "one"
+ # ['one', 'two'].to_sentence # => "one and two"
+ # ['one', 'two', 'three'].to_sentence # => "one, two, and three"
+ #
+ # ['one', 'two'].to_sentence(passing: 'invalid option')
+ # # => ArgumentError: Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale
+ #
+ # ['one', 'two'].to_sentence(two_words_connector: '-')
+ # # => "one-two"
+ #
+ # ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
+ # # => "one or two or at least three"
+ #
+ # Using :locale option:
+ #
+ # # Given this locale dictionary:
+ # #
+ # # es:
+ # # support:
+ # # array:
+ # # words_connector: " o "
+ # # two_words_connector: " y "
+ # # last_word_connector: " o al menos "
+ #
+ # ['uno', 'dos'].to_sentence(locale: :es)
+ # # => "uno y dos"
+ #
+ # ['uno', 'dos', 'tres'].to_sentence(locale: :es)
+ # # => "uno o dos o al menos tres"
+ def to_sentence(options = {})
+ options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
+
+ default_connectors = {
+ words_connector: ", ",
+ two_words_connector: " and ",
+ last_word_connector: ", and "
+ }
+ if defined?(I18n)
+ i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
+ default_connectors.merge!(i18n_connectors)
+ end
+ options = default_connectors.merge!(options)
+
+ case length
+ when 0
+ ""
+ when 1
+ "#{self[0]}"
+ when 2
+ "#{self[0]}#{options[:two_words_connector]}#{self[1]}"
+ else
+ "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
+ end
+ end
+
+ # Extends Array#to_s to convert a collection of elements into a
+ # comma separated id list if :db argument is given as the format.
+ #
+ # Blog.all.to_formatted_s(:db) # => "1,2,3"
+ # Blog.none.to_formatted_s(:db) # => "null"
+ # [1,2].to_formatted_s # => "[1, 2]"
+ def to_formatted_s(format = :default)
+ case format
+ when :db
+ if empty?
+ "null"
+ else
+ collect(&:id).join(",")
+ end
+ else
+ to_default_s
+ end
+ end
+ alias_method :to_default_s, :to_s
+ alias_method :to_s, :to_formatted_s
+
+ # Returns a string that represents the array in XML by invoking +to_xml+
+ # on each element. Active Record collections delegate their representation
+ # in XML to this method.
+ #
+ # All elements are expected to respond to +to_xml+, if any of them does
+ # not then an exception is raised.
+ #
+ # The root node reflects the class name of the first element in plural
+ # if all elements belong to the same type and that's not Hash:
+ #
+ # customer.projects.to_xml
+ #
+ #
+ #
+ #
+ # 20000.0
+ # 1567
+ # 2008-04-09
+ # ...
+ #
+ #
+ # 57230.0
+ # 1567
+ # 2008-04-15
+ # ...
+ #
+ #
+ #
+ # Otherwise the root element is "objects":
+ #
+ # [{ foo: 1, bar: 2}, { baz: 3}].to_xml
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ # If the collection is empty the root element is "nil-classes" by default:
+ #
+ # [].to_xml
+ #
+ #
+ #
+ #
+ # To ensure a meaningful root element use the :root option:
+ #
+ # customer_with_no_projects.projects.to_xml(root: 'projects')
+ #
+ #
+ #
+ #
+ # By default name of the node for the children of root is root.singularize.
+ # You can change it with the :children option.
+ #
+ # The +options+ hash is passed downwards:
+ #
+ # Message.all.to_xml(skip_types: true)
+ #
+ #
+ #
+ #
+ # 2008-03-07T09:58:18+01:00
+ # 1
+ # 1
+ # 2008-03-07T09:58:18+01:00
+ # 1
+ #
+ #
+ #
+ def to_xml(options = {})
+ require "active_support/builder" unless defined?(Builder)
+
+ options = options.dup
+ options[:indent] ||= 2
+ options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
+ options[:root] ||= \
+ if first.class != Hash && all? { |e| e.is_a?(first.class) }
+ underscored = ActiveSupport::Inflector.underscore(first.class.name)
+ ActiveSupport::Inflector.pluralize(underscored).tr("/", "_")
+ else
+ "objects"
+ end
+
+ builder = options[:builder]
+ builder.instruct! unless options.delete(:skip_instruct)
+
+ root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
+ children = options.delete(:children) || root.singularize
+ attributes = options[:skip_types] ? {} : { type: "array" }
+
+ if empty?
+ builder.tag!(root, attributes)
+ else
+ builder.tag!(root, attributes) do
+ each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) }
+ yield builder if block_given?
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/extract_options.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/extract_options.rb
new file mode 100644
index 0000000..8c7cb2e
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/extract_options.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class Hash
+ # By default, only instances of Hash itself are extractable.
+ # Subclasses of Hash may implement this method and return
+ # true to declare themselves as extractable. If a Hash
+ # is extractable, Array#extract_options! pops it from
+ # the Array when it is the last element of the Array.
+ def extractable_options?
+ instance_of?(Hash)
+ end
+end
+
+class Array
+ # Extracts options from a set of arguments. Removes and returns the last
+ # element in the array if it's a hash, otherwise returns a blank hash.
+ #
+ # def options(*args)
+ # args.extract_options!
+ # end
+ #
+ # options(1, 2) # => {}
+ # options(1, 2, a: :b) # => {:a=>:b}
+ def extract_options!
+ if last.is_a?(Hash) && last.extractable_options?
+ pop
+ else
+ {}
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/grouping.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/grouping.rb
new file mode 100644
index 0000000..67e760b
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/grouping.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+class Array
+ # Splits or iterates over the array in groups of size +number+,
+ # padding any remaining slots with +fill_with+ unless it is +false+.
+ #
+ # %w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group}
+ # ["1", "2", "3"]
+ # ["4", "5", "6"]
+ # ["7", "8", "9"]
+ # ["10", nil, nil]
+ #
+ # %w(1 2 3 4 5).in_groups_of(2, ' ') {|group| p group}
+ # ["1", "2"]
+ # ["3", "4"]
+ # ["5", " "]
+ #
+ # %w(1 2 3 4 5).in_groups_of(2, false) {|group| p group}
+ # ["1", "2"]
+ # ["3", "4"]
+ # ["5"]
+ def in_groups_of(number, fill_with = nil)
+ if number.to_i <= 0
+ raise ArgumentError,
+ "Group size must be a positive integer, was #{number.inspect}"
+ end
+
+ if fill_with == false
+ collection = self
+ else
+ # size % number gives how many extra we have;
+ # subtracting from number gives how many to add;
+ # modulo number ensures we don't add group of just fill.
+ padding = (number - size % number) % number
+ collection = dup.concat(Array.new(padding, fill_with))
+ end
+
+ if block_given?
+ collection.each_slice(number) { |slice| yield(slice) }
+ else
+ collection.each_slice(number).to_a
+ end
+ end
+
+ # Splits or iterates over the array in +number+ of groups, padding any
+ # remaining slots with +fill_with+ unless it is +false+.
+ #
+ # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
+ # ["1", "2", "3", "4"]
+ # ["5", "6", "7", nil]
+ # ["8", "9", "10", nil]
+ #
+ # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3, ' ') {|group| p group}
+ # ["1", "2", "3", "4"]
+ # ["5", "6", "7", " "]
+ # ["8", "9", "10", " "]
+ #
+ # %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
+ # ["1", "2", "3"]
+ # ["4", "5"]
+ # ["6", "7"]
+ def in_groups(number, fill_with = nil)
+ # size.div number gives minor group size;
+ # size % number gives how many objects need extra accommodation;
+ # each group hold either division or division + 1 items.
+ division = size.div number
+ modulo = size % number
+
+ # create a new array avoiding dup
+ groups = []
+ start = 0
+
+ number.times do |index|
+ length = division + (modulo > 0 && modulo > index ? 1 : 0)
+ groups << last_group = slice(start, length)
+ last_group << fill_with if fill_with != false &&
+ modulo > 0 && length == division
+ start += length
+ end
+
+ if block_given?
+ groups.each { |g| yield(g) }
+ else
+ groups
+ end
+ end
+
+ # Divides the array into one or more subarrays based on a delimiting +value+
+ # or the result of an optional block.
+ #
+ # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
+ # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
+ def split(value = nil)
+ arr = dup
+ result = []
+ if block_given?
+ while (idx = arr.index { |i| yield i })
+ result << arr.shift(idx)
+ arr.shift
+ end
+ else
+ while (idx = arr.index(value))
+ result << arr.shift(idx)
+ arr.shift
+ end
+ end
+ result << arr
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/inquiry.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/inquiry.rb
new file mode 100644
index 0000000..92c61bf
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/inquiry.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require "active_support/array_inquirer"
+
+class Array
+ # Wraps the array in an +ArrayInquirer+ object, which gives a friendlier way
+ # to check its string-like contents.
+ #
+ # pets = [:cat, :dog].inquiry
+ #
+ # pets.cat? # => true
+ # pets.ferret? # => false
+ #
+ # pets.any?(:cat, :ferret) # => true
+ # pets.any?(:ferret, :alligator) # => false
+ def inquiry
+ ActiveSupport::ArrayInquirer.new(self)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/prepend_and_append.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/prepend_and_append.rb
new file mode 100644
index 0000000..661971d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/prepend_and_append.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Array
+ # The human way of thinking about adding stuff to the end of a list is with append.
+ alias_method :append, :push unless [].respond_to?(:append)
+
+ # The human way of thinking about adding stuff to the beginning of a list is with prepend.
+ alias_method :prepend, :unshift unless [].respond_to?(:prepend)
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/wrap.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/wrap.rb
new file mode 100644
index 0000000..d62f97e
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/array/wrap.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+class Array
+ # Wraps its argument in an array unless it is already an array (or array-like).
+ #
+ # Specifically:
+ #
+ # * If the argument is +nil+ an empty array is returned.
+ # * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
+ # * Otherwise, returns an array with the argument as its single element.
+ #
+ # Array.wrap(nil) # => []
+ # Array.wrap([1, 2, 3]) # => [1, 2, 3]
+ # Array.wrap(0) # => [0]
+ #
+ # This method is similar in purpose to Kernel#Array, but there are some differences:
+ #
+ # * If the argument responds to +to_ary+ the method is invoked. Kernel#Array
+ # moves on to try +to_a+ if the returned value is +nil+, but Array.wrap returns
+ # an array with the argument as its single element right away.
+ # * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, Kernel#Array
+ # raises an exception, while Array.wrap does not, it just returns the value.
+ # * It does not call +to_a+ on the argument, if the argument does not respond to +to_ary+
+ # it returns an array with the argument as its single element.
+ #
+ # The last point is easily explained with some enumerables:
+ #
+ # Array(foo: :bar) # => [[:foo, :bar]]
+ # Array.wrap(foo: :bar) # => [{:foo=>:bar}]
+ #
+ # There's also a related idiom that uses the splat operator:
+ #
+ # [*object]
+ #
+ # which returns [] for +nil+, but calls to Array(object) otherwise.
+ #
+ # The differences with Kernel#Array explained above
+ # apply to the rest of objects.
+ def self.wrap(object)
+ if object.nil?
+ []
+ elsif object.respond_to?(:to_ary)
+ object.to_ary || [object]
+ else
+ [object]
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/benchmark.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/benchmark.rb
new file mode 100644
index 0000000..641b58c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/benchmark.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require "benchmark"
+
+class << Benchmark
+ # Benchmark realtime in milliseconds.
+ #
+ # Benchmark.realtime { User.all }
+ # # => 8.0e-05
+ #
+ # Benchmark.ms { User.all }
+ # # => 0.074
+ def ms
+ 1000 * realtime { yield }
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/big_decimal.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/big_decimal.rb
new file mode 100644
index 0000000..9e6a9d6
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/big_decimal.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/big_decimal/conversions"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/big_decimal/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/big_decimal/conversions.rb
new file mode 100644
index 0000000..52bd229
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/big_decimal/conversions.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require "bigdecimal"
+require "bigdecimal/util"
+
+module ActiveSupport
+ module BigDecimalWithDefaultFormat #:nodoc:
+ def to_s(format = "F")
+ super(format)
+ end
+ end
+end
+
+BigDecimal.prepend(ActiveSupport::BigDecimalWithDefaultFormat)
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class.rb
new file mode 100644
index 0000000..1c110fd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/class/attribute"
+require "active_support/core_ext/class/subclasses"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class/attribute.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class/attribute.rb
new file mode 100644
index 0000000..7928efb
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class/attribute.rb
@@ -0,0 +1,146 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/kernel/singleton_class"
+require "active_support/core_ext/module/redefine_method"
+require "active_support/core_ext/array/extract_options"
+
+class Class
+ # Declare a class-level attribute whose value is inheritable by subclasses.
+ # Subclasses can change their own value and it will not impact parent class.
+ #
+ # ==== Options
+ #
+ # * :instance_reader - Sets the instance reader method (defaults to true).
+ # * :instance_writer - Sets the instance writer method (defaults to true).
+ # * :instance_accessor - Sets both instance methods (defaults to true).
+ # * :instance_predicate - Sets a predicate method (defaults to true).
+ # * :default - Sets a default value for the attribute (defaults to nil).
+ #
+ # ==== Examples
+ #
+ # class Base
+ # class_attribute :setting
+ # end
+ #
+ # class Subclass < Base
+ # end
+ #
+ # Base.setting = true
+ # Subclass.setting # => true
+ # Subclass.setting = false
+ # Subclass.setting # => false
+ # Base.setting # => true
+ #
+ # In the above case as long as Subclass does not assign a value to setting
+ # by performing Subclass.setting = _something_, Subclass.setting
+ # would read value assigned to parent class. Once Subclass assigns a value then
+ # the value assigned by Subclass would be returned.
+ #
+ # This matches normal Ruby method inheritance: think of writing an attribute
+ # on a subclass as overriding the reader method. However, you need to be aware
+ # when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
+ # In such cases, you don't want to do changes in place. Instead use setters:
+ #
+ # Base.setting = []
+ # Base.setting # => []
+ # Subclass.setting # => []
+ #
+ # # Appending in child changes both parent and child because it is the same object:
+ # Subclass.setting << :foo
+ # Base.setting # => [:foo]
+ # Subclass.setting # => [:foo]
+ #
+ # # Use setters to not propagate changes:
+ # Base.setting = []
+ # Subclass.setting += [:foo]
+ # Base.setting # => []
+ # Subclass.setting # => [:foo]
+ #
+ # For convenience, an instance predicate method is defined as well.
+ # To skip it, pass instance_predicate: false.
+ #
+ # Subclass.setting? # => false
+ #
+ # Instances may overwrite the class value in the same way:
+ #
+ # Base.setting = true
+ # object = Base.new
+ # object.setting # => true
+ # object.setting = false
+ # object.setting # => false
+ # Base.setting # => true
+ #
+ # To opt out of the instance reader method, pass instance_reader: false.
+ #
+ # object.setting # => NoMethodError
+ # object.setting? # => NoMethodError
+ #
+ # To opt out of the instance writer method, pass instance_writer: false.
+ #
+ # object.setting = false # => NoMethodError
+ #
+ # To opt out of both instance methods, pass instance_accessor: false.
+ #
+ # To set a default value for the attribute, pass default:, like so:
+ #
+ # class_attribute :settings, default: {}
+ def class_attribute(*attrs)
+ options = attrs.extract_options!
+ instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
+ instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
+ instance_predicate = options.fetch(:instance_predicate, true)
+ default_value = options.fetch(:default, nil)
+
+ attrs.each do |name|
+ singleton_class.silence_redefinition_of_method(name)
+ define_singleton_method(name) { nil }
+
+ singleton_class.silence_redefinition_of_method("#{name}?")
+ define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
+
+ ivar = "@#{name}"
+
+ singleton_class.silence_redefinition_of_method("#{name}=")
+ define_singleton_method("#{name}=") do |val|
+ singleton_class.class_eval do
+ redefine_method(name) { val }
+ end
+
+ if singleton_class?
+ class_eval do
+ redefine_method(name) do
+ if instance_variable_defined? ivar
+ instance_variable_get ivar
+ else
+ singleton_class.send name
+ end
+ end
+ end
+ end
+ val
+ end
+
+ if instance_reader
+ redefine_method(name) do
+ if instance_variable_defined?(ivar)
+ instance_variable_get ivar
+ else
+ self.class.public_send name
+ end
+ end
+
+ redefine_method("#{name}?") { !!public_send(name) } if instance_predicate
+ end
+
+ if instance_writer
+ redefine_method("#{name}=") do |val|
+ instance_variable_set ivar, val
+ end
+ end
+
+ unless default_value.nil?
+ self.send("#{name}=", default_value)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class/attribute_accessors.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class/attribute_accessors.rb
new file mode 100644
index 0000000..a77354e
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class/attribute_accessors.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+# cattr_* became mattr_* aliases in 7dfbd91b0780fbd6a1dd9bfbc176e10894871d2d,
+# but we keep this around for libraries that directly require it knowing they
+# want cattr_*. No need to deprecate.
+require "active_support/core_ext/module/attribute_accessors"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class/subclasses.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class/subclasses.rb
new file mode 100644
index 0000000..75e6533
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/class/subclasses.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+class Class
+ begin
+ # Test if this Ruby supports each_object against singleton_class
+ ObjectSpace.each_object(Numeric.singleton_class) {}
+
+ # Returns an array with all classes that are < than its receiver.
+ #
+ # class C; end
+ # C.descendants # => []
+ #
+ # class B < C; end
+ # C.descendants # => [B]
+ #
+ # class A < B; end
+ # C.descendants # => [B, A]
+ #
+ # class D < C; end
+ # C.descendants # => [B, A, D]
+ def descendants
+ descendants = []
+ ObjectSpace.each_object(singleton_class) do |k|
+ next if k.singleton_class?
+ descendants.unshift k unless k == self
+ end
+ descendants
+ end
+ rescue StandardError # JRuby 9.0.4.0 and earlier
+ def descendants
+ descendants = []
+ ObjectSpace.each_object(Class) do |k|
+ descendants.unshift k if k < self
+ end
+ descendants.uniq!
+ descendants
+ end
+ end
+
+ # Returns an array with the direct children of +self+.
+ #
+ # class Foo; end
+ # class Bar < Foo; end
+ # class Baz < Bar; end
+ #
+ # Foo.subclasses # => [Bar]
+ def subclasses
+ subclasses, chain = [], descendants
+ chain.each do |k|
+ subclasses << k unless chain.any? { |c| c > k }
+ end
+ subclasses
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date.rb
new file mode 100644
index 0000000..cce73f2
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/date/acts_like"
+require "active_support/core_ext/date/blank"
+require "active_support/core_ext/date/calculations"
+require "active_support/core_ext/date/conversions"
+require "active_support/core_ext/date/zones"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/acts_like.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/acts_like.rb
new file mode 100644
index 0000000..c8077f3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/acts_like.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/acts_like"
+
+class Date
+ # Duck-types as a Date-like class. See Object#acts_like?.
+ def acts_like_date?
+ true
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/blank.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/blank.rb
new file mode 100644
index 0000000..e6271c7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/blank.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require "date"
+
+class Date #:nodoc:
+ # No Date is blank:
+ #
+ # Date.today.blank? # => false
+ #
+ # @return [false]
+ def blank?
+ false
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/calculations.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/calculations.rb
new file mode 100644
index 0000000..1cd7acb
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/calculations.rb
@@ -0,0 +1,145 @@
+# frozen_string_literal: true
+
+require "date"
+require "active_support/duration"
+require "active_support/core_ext/object/acts_like"
+require "active_support/core_ext/date/zones"
+require "active_support/core_ext/time/zones"
+require "active_support/core_ext/date_and_time/calculations"
+
+class Date
+ include DateAndTime::Calculations
+
+ class << self
+ attr_accessor :beginning_of_week_default
+
+ # Returns the week start (e.g. :monday) for the current request, if this has been set (via Date.beginning_of_week=).
+ # If Date.beginning_of_week has not been set for the current request, returns the week start specified in config.beginning_of_week.
+ # If no config.beginning_of_week was specified, returns :monday.
+ def beginning_of_week
+ Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
+ end
+
+ # Sets Date.beginning_of_week to a week start (e.g. :monday) for current request/thread.
+ #
+ # This method accepts any of the following day symbols:
+ # :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
+ def beginning_of_week=(week_start)
+ Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
+ end
+
+ # Returns week start day symbol (e.g. :monday), or raises an +ArgumentError+ for invalid day symbol.
+ def find_beginning_of_week!(week_start)
+ raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
+ week_start
+ end
+
+ # Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
+ def yesterday
+ ::Date.current.yesterday
+ end
+
+ # Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
+ def tomorrow
+ ::Date.current.tomorrow
+ end
+
+ # Returns Time.zone.today when Time.zone or config.time_zone are set, otherwise just returns Date.today.
+ def current
+ ::Time.zone ? ::Time.zone.today : ::Date.today
+ end
+ end
+
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
+ # and then subtracts the specified number of seconds.
+ def ago(seconds)
+ in_time_zone.since(-seconds)
+ end
+
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
+ # and then adds the specified number of seconds
+ def since(seconds)
+ in_time_zone.since(seconds)
+ end
+ alias :in :since
+
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
+ def beginning_of_day
+ in_time_zone
+ end
+ alias :midnight :beginning_of_day
+ alias :at_midnight :beginning_of_day
+ alias :at_beginning_of_day :beginning_of_day
+
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the middle of the day (12:00)
+ def middle_of_day
+ in_time_zone.middle_of_day
+ end
+ alias :midday :middle_of_day
+ alias :noon :middle_of_day
+ alias :at_midday :middle_of_day
+ alias :at_noon :middle_of_day
+ alias :at_middle_of_day :middle_of_day
+
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
+ def end_of_day
+ in_time_zone.end_of_day
+ end
+ alias :at_end_of_day :end_of_day
+
+ def plus_with_duration(other) #:nodoc:
+ if ActiveSupport::Duration === other
+ other.since(self)
+ else
+ plus_without_duration(other)
+ end
+ end
+ alias_method :plus_without_duration, :+
+ alias_method :+, :plus_with_duration
+
+ def minus_with_duration(other) #:nodoc:
+ if ActiveSupport::Duration === other
+ plus_with_duration(-other)
+ else
+ minus_without_duration(other)
+ end
+ end
+ alias_method :minus_without_duration, :-
+ alias_method :-, :minus_with_duration
+
+ # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
+ # any of these keys: :years, :months, :weeks, :days.
+ def advance(options)
+ options = options.dup
+ d = self
+ d = d >> options.delete(:years) * 12 if options[:years]
+ d = d >> options.delete(:months) if options[:months]
+ d = d + options.delete(:weeks) * 7 if options[:weeks]
+ d = d + options.delete(:days) if options[:days]
+ d
+ end
+
+ # Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
+ # The +options+ parameter is a hash with a combination of these keys: :year, :month, :day.
+ #
+ # Date.new(2007, 5, 12).change(day: 1) # => Date.new(2007, 5, 1)
+ # Date.new(2007, 5, 12).change(year: 2005, month: 1) # => Date.new(2005, 1, 12)
+ def change(options)
+ ::Date.new(
+ options.fetch(:year, year),
+ options.fetch(:month, month),
+ options.fetch(:day, day)
+ )
+ end
+
+ # Allow Date to be compared with Time by converting to DateTime and relying on the <=> from there.
+ def compare_with_coercion(other)
+ if other.is_a?(Time)
+ to_datetime <=> other
+ else
+ compare_without_coercion(other)
+ end
+ end
+ alias_method :compare_without_coercion, :<=>
+ alias_method :<=>, :compare_with_coercion
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/conversions.rb
new file mode 100644
index 0000000..870119d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/conversions.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require "date"
+require "active_support/inflector/methods"
+require "active_support/core_ext/date/zones"
+require "active_support/core_ext/module/redefine_method"
+
+class Date
+ DATE_FORMATS = {
+ short: "%d %b",
+ long: "%B %d, %Y",
+ db: "%Y-%m-%d",
+ number: "%Y%m%d",
+ long_ordinal: lambda { |date|
+ day_format = ActiveSupport::Inflector.ordinalize(date.day)
+ date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007"
+ },
+ rfc822: "%d %b %Y",
+ iso8601: lambda { |date| date.iso8601 }
+ }
+
+ # Convert to a formatted string. See DATE_FORMATS for predefined formats.
+ #
+ # This method is aliased to to_s.
+ #
+ # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
+ #
+ # date.to_formatted_s(:db) # => "2007-11-10"
+ # date.to_s(:db) # => "2007-11-10"
+ #
+ # date.to_formatted_s(:short) # => "10 Nov"
+ # date.to_formatted_s(:number) # => "20071110"
+ # date.to_formatted_s(:long) # => "November 10, 2007"
+ # date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
+ # date.to_formatted_s(:rfc822) # => "10 Nov 2007"
+ # date.to_formatted_s(:iso8601) # => "2007-11-10"
+ #
+ # == Adding your own date formats to to_formatted_s
+ # You can add your own formats to the Date::DATE_FORMATS hash.
+ # Use the format name as the hash key and either a strftime string
+ # or Proc instance that takes a date argument as the value.
+ #
+ # # config/initializers/date_formats.rb
+ # Date::DATE_FORMATS[:month_and_year] = '%B %Y'
+ # Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
+ def to_formatted_s(format = :default)
+ if formatter = DATE_FORMATS[format]
+ if formatter.respond_to?(:call)
+ formatter.call(self).to_s
+ else
+ strftime(formatter)
+ end
+ else
+ to_default_s
+ end
+ end
+ alias_method :to_default_s, :to_s
+ alias_method :to_s, :to_formatted_s
+
+ # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
+ def readable_inspect
+ strftime("%a, %d %b %Y")
+ end
+ alias_method :default_inspect, :inspect
+ alias_method :inspect, :readable_inspect
+
+ silence_redefinition_of_method :to_time
+
+ # Converts a Date instance to a Time, where the time is set to the beginning of the day.
+ # The timezone can be either :local or :utc (default :local).
+ #
+ # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
+ #
+ # date.to_time # => 2007-11-10 00:00:00 0800
+ # date.to_time(:local) # => 2007-11-10 00:00:00 0800
+ #
+ # date.to_time(:utc) # => 2007-11-10 00:00:00 UTC
+ #
+ # NOTE: The :local timezone is Ruby's *process* timezone, i.e. ENV['TZ'].
+ # If the *application's* timezone is needed, then use +in_time_zone+ instead.
+ def to_time(form = :local)
+ raise ArgumentError, "Expected :local or :utc, got #{form.inspect}." unless [:local, :utc].include?(form)
+ ::Time.send(form, year, month, day)
+ end
+
+ silence_redefinition_of_method :xmlschema
+
+ # Returns a string which represents the time in used time zone as DateTime
+ # defined by XML Schema:
+ #
+ # date = Date.new(2015, 05, 23) # => Sat, 23 May 2015
+ # date.xmlschema # => "2015-05-23T00:00:00+04:00"
+ def xmlschema
+ in_time_zone.xmlschema
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/zones.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/zones.rb
new file mode 100644
index 0000000..2dcf97c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date/zones.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require "date"
+require "active_support/core_ext/date_and_time/zones"
+
+class Date
+ include DateAndTime::Zones
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_and_time/calculations.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_and_time/calculations.rb
new file mode 100644
index 0000000..f6cb1a3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_and_time/calculations.rb
@@ -0,0 +1,374 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/try"
+
+module DateAndTime
+ module Calculations
+ DAYS_INTO_WEEK = {
+ monday: 0,
+ tuesday: 1,
+ wednesday: 2,
+ thursday: 3,
+ friday: 4,
+ saturday: 5,
+ sunday: 6
+ }
+ WEEKEND_DAYS = [ 6, 0 ]
+
+ # Returns a new date/time representing yesterday.
+ def yesterday
+ advance(days: -1)
+ end
+
+ # Returns a new date/time the specified number of days ago.
+ def prev_day(days = 1)
+ advance(days: -days)
+ end
+
+ # Returns a new date/time representing tomorrow.
+ def tomorrow
+ advance(days: 1)
+ end
+
+ # Returns a new date/time the specified number of days in the future.
+ def next_day(days = 1)
+ advance(days: days)
+ end
+
+ # Returns true if the date/time is today.
+ def today?
+ to_date == ::Date.current
+ end
+
+ # Returns true if the date/time is in the past.
+ def past?
+ self < self.class.current
+ end
+
+ # Returns true if the date/time is in the future.
+ def future?
+ self > self.class.current
+ end
+
+ # Returns true if the date/time falls on a Saturday or Sunday.
+ def on_weekend?
+ WEEKEND_DAYS.include?(wday)
+ end
+
+ # Returns true if the date/time does not fall on a Saturday or Sunday.
+ def on_weekday?
+ !WEEKEND_DAYS.include?(wday)
+ end
+
+ # Returns a new date/time the specified number of days ago.
+ def days_ago(days)
+ advance(days: -days)
+ end
+
+ # Returns a new date/time the specified number of days in the future.
+ def days_since(days)
+ advance(days: days)
+ end
+
+ # Returns a new date/time the specified number of weeks ago.
+ def weeks_ago(weeks)
+ advance(weeks: -weeks)
+ end
+
+ # Returns a new date/time the specified number of weeks in the future.
+ def weeks_since(weeks)
+ advance(weeks: weeks)
+ end
+
+ # Returns a new date/time the specified number of months ago.
+ def months_ago(months)
+ advance(months: -months)
+ end
+
+ # Returns a new date/time the specified number of months in the future.
+ def months_since(months)
+ advance(months: months)
+ end
+
+ # Returns a new date/time the specified number of years ago.
+ def years_ago(years)
+ advance(years: -years)
+ end
+
+ # Returns a new date/time the specified number of years in the future.
+ def years_since(years)
+ advance(years: years)
+ end
+
+ # Returns a new date/time at the start of the month.
+ #
+ # today = Date.today # => Thu, 18 Jun 2015
+ # today.beginning_of_month # => Mon, 01 Jun 2015
+ #
+ # +DateTime+ objects will have a time set to 0:00.
+ #
+ # now = DateTime.current # => Thu, 18 Jun 2015 15:23:13 +0000
+ # now.beginning_of_month # => Mon, 01 Jun 2015 00:00:00 +0000
+ def beginning_of_month
+ first_hour(change(day: 1))
+ end
+ alias :at_beginning_of_month :beginning_of_month
+
+ # Returns a new date/time at the start of the quarter.
+ #
+ # today = Date.today # => Fri, 10 Jul 2015
+ # today.beginning_of_quarter # => Wed, 01 Jul 2015
+ #
+ # +DateTime+ objects will have a time set to 0:00.
+ #
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
+ # now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000
+ def beginning_of_quarter
+ first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
+ beginning_of_month.change(month: first_quarter_month)
+ end
+ alias :at_beginning_of_quarter :beginning_of_quarter
+
+ # Returns a new date/time at the end of the quarter.
+ #
+ # today = Date.today # => Fri, 10 Jul 2015
+ # today.end_of_quarter # => Wed, 30 Sep 2015
+ #
+ # +DateTime+ objects will have a time set to 23:59:59.
+ #
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
+ # now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000
+ def end_of_quarter
+ last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
+ beginning_of_month.change(month: last_quarter_month).end_of_month
+ end
+ alias :at_end_of_quarter :end_of_quarter
+
+ # Returns a new date/time at the beginning of the year.
+ #
+ # today = Date.today # => Fri, 10 Jul 2015
+ # today.beginning_of_year # => Thu, 01 Jan 2015
+ #
+ # +DateTime+ objects will have a time set to 0:00.
+ #
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
+ # now.beginning_of_year # => Thu, 01 Jan 2015 00:00:00 +0000
+ def beginning_of_year
+ change(month: 1).beginning_of_month
+ end
+ alias :at_beginning_of_year :beginning_of_year
+
+ # Returns a new date/time representing the given day in the next week.
+ #
+ # today = Date.today # => Thu, 07 May 2015
+ # today.next_week # => Mon, 11 May 2015
+ #
+ # The +given_day_in_next_week+ defaults to the beginning of the week
+ # which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+
+ # when set.
+ #
+ # today = Date.today # => Thu, 07 May 2015
+ # today.next_week(:friday) # => Fri, 15 May 2015
+ #
+ # +DateTime+ objects have their time set to 0:00 unless +same_time+ is true.
+ #
+ # now = DateTime.current # => Thu, 07 May 2015 13:31:16 +0000
+ # now.next_week # => Mon, 11 May 2015 00:00:00 +0000
+ def next_week(given_day_in_next_week = Date.beginning_of_week, same_time: false)
+ result = first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
+ same_time ? copy_time_to(result) : result
+ end
+
+ # Returns a new date/time representing the next weekday.
+ def next_weekday
+ if next_day.on_weekend?
+ next_week(:monday, same_time: true)
+ else
+ next_day
+ end
+ end
+
+ # Returns a new date/time the specified number of months in the future.
+ def next_month(months = 1)
+ advance(months: months)
+ end
+
+ # Short-hand for months_since(3)
+ def next_quarter
+ months_since(3)
+ end
+
+ # Returns a new date/time the specified number of years in the future.
+ def next_year(years = 1)
+ advance(years: years)
+ end
+
+ # Returns a new date/time representing the given day in the previous week.
+ # Week is assumed to start on +start_day+, default is
+ # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
+ # DateTime objects have their time set to 0:00 unless +same_time+ is true.
+ def prev_week(start_day = Date.beginning_of_week, same_time: false)
+ result = first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
+ same_time ? copy_time_to(result) : result
+ end
+ alias_method :last_week, :prev_week
+
+ # Returns a new date/time representing the previous weekday.
+ def prev_weekday
+ if prev_day.on_weekend?
+ copy_time_to(beginning_of_week(:friday))
+ else
+ prev_day
+ end
+ end
+ alias_method :last_weekday, :prev_weekday
+
+ # Returns a new date/time the specified number of months ago.
+ def prev_month(months = 1)
+ advance(months: -months)
+ end
+
+ # Short-hand for months_ago(1).
+ def last_month
+ months_ago(1)
+ end
+
+ # Short-hand for months_ago(3).
+ def prev_quarter
+ months_ago(3)
+ end
+ alias_method :last_quarter, :prev_quarter
+
+ # Returns a new date/time the specified number of years ago.
+ def prev_year(years = 1)
+ advance(years: -years)
+ end
+
+ # Short-hand for years_ago(1).
+ def last_year
+ years_ago(1)
+ end
+
+ # Returns the number of days to the start of the week on the given day.
+ # Week is assumed to start on +start_day+, default is
+ # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
+ def days_to_week_start(start_day = Date.beginning_of_week)
+ start_day_number = DAYS_INTO_WEEK[start_day]
+ current_day_number = wday != 0 ? wday - 1 : 6
+ (current_day_number - start_day_number) % 7
+ end
+
+ # Returns a new date/time representing the start of this week on the given day.
+ # Week is assumed to start on +start_day+, default is
+ # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
+ # +DateTime+ objects have their time set to 0:00.
+ def beginning_of_week(start_day = Date.beginning_of_week)
+ result = days_ago(days_to_week_start(start_day))
+ acts_like?(:time) ? result.midnight : result
+ end
+ alias :at_beginning_of_week :beginning_of_week
+
+ # Returns Monday of this week assuming that week starts on Monday.
+ # +DateTime+ objects have their time set to 0:00.
+ def monday
+ beginning_of_week(:monday)
+ end
+
+ # Returns a new date/time representing the end of this week on the given day.
+ # Week is assumed to start on +start_day+, default is
+ # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
+ # DateTime objects have their time set to 23:59:59.
+ def end_of_week(start_day = Date.beginning_of_week)
+ last_hour(days_since(6 - days_to_week_start(start_day)))
+ end
+ alias :at_end_of_week :end_of_week
+
+ # Returns Sunday of this week assuming that week starts on Monday.
+ # +DateTime+ objects have their time set to 23:59:59.
+ def sunday
+ end_of_week(:monday)
+ end
+
+ # Returns a new date/time representing the end of the month.
+ # DateTime objects will have a time set to 23:59:59.
+ def end_of_month
+ last_day = ::Time.days_in_month(month, year)
+ last_hour(days_since(last_day - day))
+ end
+ alias :at_end_of_month :end_of_month
+
+ # Returns a new date/time representing the end of the year.
+ # DateTime objects will have a time set to 23:59:59.
+ def end_of_year
+ change(month: 12).end_of_month
+ end
+ alias :at_end_of_year :end_of_year
+
+ # Returns a Range representing the whole day of the current date/time.
+ def all_day
+ beginning_of_day..end_of_day
+ end
+
+ # Returns a Range representing the whole week of the current date/time.
+ # Week starts on start_day, default is Date.beginning_of_week or config.beginning_of_week when set.
+ def all_week(start_day = Date.beginning_of_week)
+ beginning_of_week(start_day)..end_of_week(start_day)
+ end
+
+ # Returns a Range representing the whole month of the current date/time.
+ def all_month
+ beginning_of_month..end_of_month
+ end
+
+ # Returns a Range representing the whole quarter of the current date/time.
+ def all_quarter
+ beginning_of_quarter..end_of_quarter
+ end
+
+ # Returns a Range representing the whole year of the current date/time.
+ def all_year
+ beginning_of_year..end_of_year
+ end
+
+ # Returns a new date/time representing the next occurrence of the specified day of week.
+ #
+ # today = Date.today # => Thu, 14 Dec 2017
+ # today.next_occurring(:monday) # => Mon, 18 Dec 2017
+ # today.next_occurring(:thursday) # => Thu, 21 Dec 2017
+ def next_occurring(day_of_week)
+ current_day_number = wday != 0 ? wday - 1 : 6
+ from_now = DAYS_INTO_WEEK.fetch(day_of_week) - current_day_number
+ from_now += 7 unless from_now > 0
+ advance(days: from_now)
+ end
+
+ # Returns a new date/time representing the previous occurrence of the specified day of week.
+ #
+ # today = Date.today # => Thu, 14 Dec 2017
+ # today.prev_occurring(:monday) # => Mon, 11 Dec 2017
+ # today.prev_occurring(:thursday) # => Thu, 07 Dec 2017
+ def prev_occurring(day_of_week)
+ current_day_number = wday != 0 ? wday - 1 : 6
+ ago = current_day_number - DAYS_INTO_WEEK.fetch(day_of_week)
+ ago += 7 unless ago > 0
+ advance(days: -ago)
+ end
+
+ private
+ def first_hour(date_or_time)
+ date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
+ end
+
+ def last_hour(date_or_time)
+ date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
+ end
+
+ def days_span(day)
+ (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
+ end
+
+ def copy_time_to(other)
+ other.change(hour: hour, min: min, sec: sec, nsec: try(:nsec))
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_and_time/compatibility.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_and_time/compatibility.rb
new file mode 100644
index 0000000..d33c36e
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_and_time/compatibility.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/attribute_accessors"
+
+module DateAndTime
+ module Compatibility
+ # If true, +to_time+ preserves the timezone offset of receiver.
+ #
+ # NOTE: With Ruby 2.4+ the default for +to_time+ changed from
+ # converting to the local system time, to preserving the offset
+ # of the receiver. For backwards compatibility we're overriding
+ # this behavior, but new apps will have an initializer that sets
+ # this to true, because the new behavior is preferred.
+ mattr_accessor :preserve_timezone, instance_writer: false, default: false
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_and_time/zones.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_and_time/zones.rb
new file mode 100644
index 0000000..894fd9b
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_and_time/zones.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module DateAndTime
+ module Zones
+ # Returns the simultaneous time in Time.zone if a zone is given or
+ # if Time.zone_default is set. Otherwise, it returns the current time.
+ #
+ # Time.zone = 'Hawaii' # => 'Hawaii'
+ # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ # Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00
+ #
+ # This method is similar to Time#localtime, except that it uses Time.zone as the local zone
+ # instead of the operating system's time zone.
+ #
+ # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
+ # and the conversion will be based on that zone instead of Time.zone.
+ #
+ # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
+ # Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00
+ def in_time_zone(zone = ::Time.zone)
+ time_zone = ::Time.find_zone! zone
+ time = acts_like?(:time) ? self : nil
+
+ if time_zone
+ time_with_zone(time, time_zone)
+ else
+ time || to_time
+ end
+ end
+
+ private
+
+ def time_with_zone(time, zone)
+ if time
+ ActiveSupport::TimeWithZone.new(time.utc? ? time : time.getutc, zone)
+ else
+ ActiveSupport::TimeWithZone.new(nil, zone, to_time(:utc))
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time.rb
new file mode 100644
index 0000000..790dbee
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/date_time/acts_like"
+require "active_support/core_ext/date_time/blank"
+require "active_support/core_ext/date_time/calculations"
+require "active_support/core_ext/date_time/compatibility"
+require "active_support/core_ext/date_time/conversions"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/acts_like.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/acts_like.rb
new file mode 100644
index 0000000..5dccdfe
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/acts_like.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require "date"
+require "active_support/core_ext/object/acts_like"
+
+class DateTime
+ # Duck-types as a Date-like class. See Object#acts_like?.
+ def acts_like_date?
+ true
+ end
+
+ # Duck-types as a Time-like class. See Object#acts_like?.
+ def acts_like_time?
+ true
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/blank.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/blank.rb
new file mode 100644
index 0000000..a52c8bc
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/blank.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require "date"
+
+class DateTime #:nodoc:
+ # No DateTime is ever blank:
+ #
+ # DateTime.now.blank? # => false
+ #
+ # @return [false]
+ def blank?
+ false
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/calculations.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/calculations.rb
new file mode 100644
index 0000000..e61b23f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/calculations.rb
@@ -0,0 +1,211 @@
+# frozen_string_literal: true
+
+require "date"
+
+class DateTime
+ class << self
+ # Returns Time.zone.now.to_datetime when Time.zone or
+ # config.time_zone are set, otherwise returns
+ # Time.now.to_datetime.
+ def current
+ ::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
+ end
+ end
+
+ # Returns the number of seconds since 00:00:00.
+ #
+ # DateTime.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0
+ # DateTime.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296
+ # DateTime.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399
+ def seconds_since_midnight
+ sec + (min * 60) + (hour * 3600)
+ end
+
+ # Returns the number of seconds until 23:59:59.
+ #
+ # DateTime.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
+ # DateTime.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
+ # DateTime.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
+ def seconds_until_end_of_day
+ end_of_day.to_i - to_i
+ end
+
+ # Returns the fraction of a second as a +Rational+
+ #
+ # DateTime.new(2012, 8, 29, 0, 0, 0.5).subsec # => (1/2)
+ def subsec
+ sec_fraction
+ end
+
+ # Returns a new DateTime where one or more of the elements have been changed
+ # according to the +options+ parameter. The time options (:hour,
+ # :min, :sec) reset cascadingly, so if only the hour is
+ # passed, then minute and sec is set to 0. If the hour and minute is passed,
+ # then sec is set to 0. The +options+ parameter takes a hash with any of these
+ # keys: :year, :month, :day, :hour,
+ # :min, :sec, :offset, :start.
+ #
+ # DateTime.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => DateTime.new(2012, 8, 1, 22, 35, 0)
+ # DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => DateTime.new(1981, 8, 1, 22, 35, 0)
+ # DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => DateTime.new(1981, 8, 29, 0, 0, 0)
+ def change(options)
+ if new_nsec = options[:nsec]
+ raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec]
+ new_fraction = Rational(new_nsec, 1000000000)
+ else
+ new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
+ new_fraction = Rational(new_usec, 1000000)
+ end
+
+ raise ArgumentError, "argument out of range" if new_fraction >= 1
+
+ ::DateTime.civil(
+ options.fetch(:year, year),
+ options.fetch(:month, month),
+ options.fetch(:day, day),
+ options.fetch(:hour, hour),
+ options.fetch(:min, options[:hour] ? 0 : min),
+ options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec) + new_fraction,
+ options.fetch(:offset, offset),
+ options.fetch(:start, start)
+ )
+ end
+
+ # Uses Date to provide precise Time calculations for years, months, and days.
+ # The +options+ parameter takes a hash with any of these keys: :years,
+ # :months, :weeks, :days, :hours,
+ # :minutes, :seconds.
+ def advance(options)
+ unless options[:weeks].nil?
+ options[:weeks], partial_weeks = options[:weeks].divmod(1)
+ options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
+ end
+
+ unless options[:days].nil?
+ options[:days], partial_days = options[:days].divmod(1)
+ options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
+ end
+
+ d = to_date.advance(options)
+ datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
+ seconds_to_advance = \
+ options.fetch(:seconds, 0) +
+ options.fetch(:minutes, 0) * 60 +
+ options.fetch(:hours, 0) * 3600
+
+ if seconds_to_advance.zero?
+ datetime_advanced_by_date
+ else
+ datetime_advanced_by_date.since(seconds_to_advance)
+ end
+ end
+
+ # Returns a new DateTime representing the time a number of seconds ago.
+ # Do not use this method in combination with x.months, use months_ago instead!
+ def ago(seconds)
+ since(-seconds)
+ end
+
+ # Returns a new DateTime representing the time a number of seconds since the
+ # instance time. Do not use this method in combination with x.months, use
+ # months_since instead!
+ def since(seconds)
+ self + Rational(seconds.round, 86400)
+ end
+ alias :in :since
+
+ # Returns a new DateTime representing the start of the day (0:00).
+ def beginning_of_day
+ change(hour: 0)
+ end
+ alias :midnight :beginning_of_day
+ alias :at_midnight :beginning_of_day
+ alias :at_beginning_of_day :beginning_of_day
+
+ # Returns a new DateTime representing the middle of the day (12:00)
+ def middle_of_day
+ change(hour: 12)
+ end
+ alias :midday :middle_of_day
+ alias :noon :middle_of_day
+ alias :at_midday :middle_of_day
+ alias :at_noon :middle_of_day
+ alias :at_middle_of_day :middle_of_day
+
+ # Returns a new DateTime representing the end of the day (23:59:59).
+ def end_of_day
+ change(hour: 23, min: 59, sec: 59, usec: Rational(999999999, 1000))
+ end
+ alias :at_end_of_day :end_of_day
+
+ # Returns a new DateTime representing the start of the hour (hh:00:00).
+ def beginning_of_hour
+ change(min: 0)
+ end
+ alias :at_beginning_of_hour :beginning_of_hour
+
+ # Returns a new DateTime representing the end of the hour (hh:59:59).
+ def end_of_hour
+ change(min: 59, sec: 59, usec: Rational(999999999, 1000))
+ end
+ alias :at_end_of_hour :end_of_hour
+
+ # Returns a new DateTime representing the start of the minute (hh:mm:00).
+ def beginning_of_minute
+ change(sec: 0)
+ end
+ alias :at_beginning_of_minute :beginning_of_minute
+
+ # Returns a new DateTime representing the end of the minute (hh:mm:59).
+ def end_of_minute
+ change(sec: 59, usec: Rational(999999999, 1000))
+ end
+ alias :at_end_of_minute :end_of_minute
+
+ # Returns a Time instance of the simultaneous time in the system timezone.
+ def localtime(utc_offset = nil)
+ utc = new_offset(0)
+
+ Time.utc(
+ utc.year, utc.month, utc.day,
+ utc.hour, utc.min, utc.sec + utc.sec_fraction
+ ).getlocal(utc_offset)
+ end
+ alias_method :getlocal, :localtime
+
+ # Returns a Time instance of the simultaneous time in the UTC timezone.
+ #
+ # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
+ # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 UTC
+ def utc
+ utc = new_offset(0)
+
+ Time.utc(
+ utc.year, utc.month, utc.day,
+ utc.hour, utc.min, utc.sec + utc.sec_fraction
+ )
+ end
+ alias_method :getgm, :utc
+ alias_method :getutc, :utc
+ alias_method :gmtime, :utc
+
+ # Returns +true+ if offset == 0.
+ def utc?
+ offset == 0
+ end
+
+ # Returns the offset value in seconds.
+ def utc_offset
+ (offset * 86400).to_i
+ end
+
+ # Layers additional behavior on DateTime#<=> so that Time and
+ # ActiveSupport::TimeWithZone instances can be compared with a DateTime.
+ def <=>(other)
+ if other.respond_to? :to_datetime
+ super other.to_datetime rescue nil
+ else
+ super
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/compatibility.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/compatibility.rb
new file mode 100644
index 0000000..7600a06
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/compatibility.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/date_and_time/compatibility"
+require "active_support/core_ext/module/redefine_method"
+
+class DateTime
+ include DateAndTime::Compatibility
+
+ silence_redefinition_of_method :to_time
+
+ # Either return an instance of +Time+ with the same UTC offset
+ # as +self+ or an instance of +Time+ representing the same time
+ # in the local system timezone depending on the setting of
+ # on the setting of +ActiveSupport.to_time_preserves_timezone+.
+ def to_time
+ preserve_timezone ? getlocal(utc_offset) : getlocal
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/conversions.rb
new file mode 100644
index 0000000..29725c8
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/date_time/conversions.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+require "date"
+require "active_support/inflector/methods"
+require "active_support/core_ext/time/conversions"
+require "active_support/core_ext/date_time/calculations"
+require "active_support/values/time_zone"
+
+class DateTime
+ # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
+ #
+ # This method is aliased to to_s.
+ #
+ # === Examples
+ # datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
+ #
+ # datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
+ # datetime.to_s(:db) # => "2007-12-04 00:00:00"
+ # datetime.to_s(:number) # => "20071204000000"
+ # datetime.to_formatted_s(:short) # => "04 Dec 00:00"
+ # datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
+ # datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
+ # datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
+ # datetime.to_formatted_s(:iso8601) # => "2007-12-04T00:00:00+00:00"
+ #
+ # == Adding your own datetime formats to to_formatted_s
+ # DateTime formats are shared with Time. You can add your own to the
+ # Time::DATE_FORMATS hash. Use the format name as the hash key and
+ # either a strftime string or Proc instance that takes a time or
+ # datetime argument as the value.
+ #
+ # # config/initializers/time_formats.rb
+ # Time::DATE_FORMATS[:month_and_year] = '%B %Y'
+ # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
+ def to_formatted_s(format = :default)
+ if formatter = ::Time::DATE_FORMATS[format]
+ formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
+ else
+ to_default_s
+ end
+ end
+ alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
+ alias_method :to_s, :to_formatted_s
+
+ # Returns a formatted string of the offset from UTC, or an alternative
+ # string if the time zone is already UTC.
+ #
+ # datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
+ # datetime.formatted_offset # => "-06:00"
+ # datetime.formatted_offset(false) # => "-0600"
+ def formatted_offset(colon = true, alternate_utc_string = nil)
+ utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
+ end
+
+ # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
+ def readable_inspect
+ to_s(:rfc822)
+ end
+ alias_method :default_inspect, :inspect
+ alias_method :inspect, :readable_inspect
+
+ # Returns DateTime with local offset for given year if format is local else
+ # offset is zero.
+ #
+ # DateTime.civil_from_format :local, 2012
+ # # => Sun, 01 Jan 2012 00:00:00 +0300
+ # DateTime.civil_from_format :local, 2012, 12, 17
+ # # => Mon, 17 Dec 2012 00:00:00 +0000
+ def self.civil_from_format(utc_or_local, year, month = 1, day = 1, hour = 0, min = 0, sec = 0)
+ if utc_or_local.to_sym == :local
+ offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
+ else
+ offset = 0
+ end
+ civil(year, month, day, hour, min, sec, offset)
+ end
+
+ # Converts +self+ to a floating-point number of seconds, including fractional microseconds, since the Unix epoch.
+ def to_f
+ seconds_since_unix_epoch.to_f + sec_fraction
+ end
+
+ # Converts +self+ to an integer number of seconds since the Unix epoch.
+ def to_i
+ seconds_since_unix_epoch.to_i
+ end
+
+ # Returns the fraction of a second as microseconds
+ def usec
+ (sec_fraction * 1_000_000).to_i
+ end
+
+ # Returns the fraction of a second as nanoseconds
+ def nsec
+ (sec_fraction * 1_000_000_000).to_i
+ end
+
+ private
+
+ def offset_in_seconds
+ (offset * 86400).to_i
+ end
+
+ def seconds_since_unix_epoch
+ (jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/digest.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/digest.rb
new file mode 100644
index 0000000..ce1427e
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/digest.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/digest/uuid"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/digest/uuid.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/digest/uuid.rb
new file mode 100644
index 0000000..6e949a2
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/digest/uuid.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require "securerandom"
+
+module Digest
+ module UUID
+ DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
+ URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
+ OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
+ X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
+
+ # Generates a v5 non-random UUID (Universally Unique IDentifier).
+ #
+ # Using Digest::MD5 generates version 3 UUIDs; Digest::SHA1 generates version 5 UUIDs.
+ # uuid_from_hash always generates the same UUID for a given name and namespace combination.
+ #
+ # See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt
+ def self.uuid_from_hash(hash_class, uuid_namespace, name)
+ if hash_class == Digest::MD5
+ version = 3
+ elsif hash_class == Digest::SHA1
+ version = 5
+ else
+ raise ArgumentError, "Expected Digest::SHA1 or Digest::MD5, got #{hash_class.name}."
+ end
+
+ hash = hash_class.new
+ hash.update(uuid_namespace)
+ hash.update(name)
+
+ ary = hash.digest.unpack("NnnnnN")
+ ary[2] = (ary[2] & 0x0FFF) | (version << 12)
+ ary[3] = (ary[3] & 0x3FFF) | 0x8000
+
+ "%08x-%04x-%04x-%04x-%04x%08x" % ary
+ end
+
+ # Convenience method for uuid_from_hash using Digest::MD5.
+ def self.uuid_v3(uuid_namespace, name)
+ uuid_from_hash(Digest::MD5, uuid_namespace, name)
+ end
+
+ # Convenience method for uuid_from_hash using Digest::SHA1.
+ def self.uuid_v5(uuid_namespace, name)
+ uuid_from_hash(Digest::SHA1, uuid_namespace, name)
+ end
+
+ # Convenience method for SecureRandom.uuid.
+ def self.uuid_v4
+ SecureRandom.uuid
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/enumerable.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/enumerable.rb
new file mode 100644
index 0000000..cea6f98
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/enumerable.rb
@@ -0,0 +1,164 @@
+# frozen_string_literal: true
+
+module Enumerable
+ # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
+ # when we omit an identity.
+ #
+ # We tried shimming it to attempt the fast native method, rescue TypeError,
+ # and fall back to the compatible implementation, but that's much slower than
+ # just calling the compat method in the first place.
+ if Enumerable.instance_methods(false).include?(:sum) && !((?a..?b).sum rescue false)
+ # :stopdoc:
+
+ # We can't use Refinements here because Refinements with Module which will be prepended
+ # doesn't work well https://bugs.ruby-lang.org/issues/13446
+ alias :_original_sum_with_required_identity :sum
+ private :_original_sum_with_required_identity
+
+ # :startdoc:
+
+ # Calculates a sum from the elements.
+ #
+ # payments.sum { |p| p.price * p.tax_rate }
+ # payments.sum(&:price)
+ #
+ # The latter is a shortcut for:
+ #
+ # payments.inject(0) { |sum, p| sum + p.price }
+ #
+ # It can also calculate the sum without the use of a block.
+ #
+ # [5, 15, 10].sum # => 30
+ # ['foo', 'bar'].sum # => "foobar"
+ # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
+ #
+ # The default sum of an empty list is zero. You can override this default:
+ #
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
+ def sum(identity = nil, &block)
+ if identity
+ _original_sum_with_required_identity(identity, &block)
+ elsif block_given?
+ map(&block).sum(identity)
+ else
+ inject(:+) || 0
+ end
+ end
+ else
+ def sum(identity = nil, &block)
+ if block_given?
+ map(&block).sum(identity)
+ else
+ sum = identity ? inject(identity, :+) : inject(:+)
+ sum || identity || 0
+ end
+ end
+ end
+
+ # Convert an enumerable to a hash.
+ #
+ # people.index_by(&:login)
+ # # => { "nextangle" => , "chade-" => , ...}
+ # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
+ # # => { "Chade- Fowlersburg-e" => , "David Heinemeier Hansson" => , ...}
+ def index_by
+ if block_given?
+ result = {}
+ each { |elem| result[yield(elem)] = elem }
+ result
+ else
+ to_enum(:index_by) { size if respond_to?(:size) }
+ end
+ end
+
+ # Returns +true+ if the enumerable has more than 1 element. Functionally
+ # equivalent to enum.to_a.size > 1. Can be called with a block too,
+ # much like any?, so people.many? { |p| p.age > 26 } returns +true+
+ # if more than one person is over 26.
+ def many?
+ cnt = 0
+ if block_given?
+ any? do |element|
+ cnt += 1 if yield element
+ cnt > 1
+ end
+ else
+ any? { (cnt += 1) > 1 }
+ end
+ end
+
+ # The negative of the Enumerable#include?. Returns +true+ if the
+ # collection does not include the object.
+ def exclude?(object)
+ !include?(object)
+ end
+
+ # Returns a copy of the enumerable without the specified elements.
+ #
+ # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
+ # # => ["David", "Rafael"]
+ #
+ # {foo: 1, bar: 2, baz: 3}.without :bar
+ # # => {foo: 1, baz: 3}
+ def without(*elements)
+ reject { |element| elements.include?(element) }
+ end
+
+ # Convert an enumerable to an array based on the given key.
+ #
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
+ # # => ["David", "Rafael", "Aaron"]
+ #
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
+ # # => [[1, "David"], [2, "Rafael"]]
+ def pluck(*keys)
+ if keys.many?
+ map { |element| keys.map { |key| element[key] } }
+ else
+ map { |element| element[keys.first] }
+ end
+ end
+end
+
+class Range #:nodoc:
+ # Optimize range sum to use arithmetic progression if a block is not given and
+ # we have a range of numeric values.
+ def sum(identity = nil)
+ if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
+ super
+ else
+ actual_last = exclude_end? ? (last - 1) : last
+ if actual_last >= first
+ sum = identity || 0
+ sum + (actual_last - first + 1) * (actual_last + first) / 2
+ else
+ identity || 0
+ end
+ end
+ end
+end
+
+# Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
+#
+# We tried shimming it to attempt the fast native method, rescue TypeError,
+# and fall back to the compatible implementation, but that's much slower than
+# just calling the compat method in the first place.
+if Array.instance_methods(false).include?(:sum) && !(%w[a].sum rescue false)
+ # Using Refinements here in order not to expose our internal method
+ using Module.new {
+ refine Array do
+ alias :orig_sum :sum
+ end
+ }
+
+ class Array
+ def sum(init = nil, &block) #:nodoc:
+ if init.is_a?(Numeric) || first.is_a?(Numeric)
+ init ||= 0
+ orig_sum(init, &block)
+ else
+ super
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/file.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/file.rb
new file mode 100644
index 0000000..64553bf
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/file.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/file/atomic"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/file/atomic.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/file/atomic.rb
new file mode 100644
index 0000000..9deceb1
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/file/atomic.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require "fileutils"
+
+class File
+ # Write to a file atomically. Useful for situations where you don't
+ # want other processes or threads to see half-written files.
+ #
+ # File.atomic_write('important.file') do |file|
+ # file.write('hello')
+ # end
+ #
+ # This method needs to create a temporary file. By default it will create it
+ # in the same directory as the destination file. If you don't like this
+ # behavior you can provide a different directory but it must be on the
+ # same physical filesystem as the file you're trying to write.
+ #
+ # File.atomic_write('/data/something.important', '/data/tmp') do |file|
+ # file.write('hello')
+ # end
+ def self.atomic_write(file_name, temp_dir = dirname(file_name))
+ require "tempfile" unless defined?(Tempfile)
+
+ Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
+ temp_file.binmode
+ return_val = yield temp_file
+ temp_file.close
+
+ old_stat = if exist?(file_name)
+ # Get original file permissions
+ stat(file_name)
+ else
+ # If not possible, probe which are the default permissions in the
+ # destination directory.
+ probe_stat_in(dirname(file_name))
+ end
+
+ if old_stat
+ # Set correct permissions on new file
+ begin
+ chown(old_stat.uid, old_stat.gid, temp_file.path)
+ # This operation will affect filesystem ACL's
+ chmod(old_stat.mode, temp_file.path)
+ rescue Errno::EPERM, Errno::EACCES
+ # Changing file ownership failed, moving on.
+ end
+ end
+
+ # Overwrite original file with temp file
+ rename(temp_file.path, file_name)
+ return_val
+ end
+ end
+
+ # Private utility method.
+ def self.probe_stat_in(dir) #:nodoc:
+ basename = [
+ ".permissions_check",
+ Thread.current.object_id,
+ Process.pid,
+ rand(1000000)
+ ].join(".")
+
+ file_name = join(dir, basename)
+ FileUtils.touch(file_name)
+ stat(file_name)
+ ensure
+ FileUtils.rm_f(file_name) if file_name
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash.rb
new file mode 100644
index 0000000..e19aeaa
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/hash/compact"
+require "active_support/core_ext/hash/conversions"
+require "active_support/core_ext/hash/deep_merge"
+require "active_support/core_ext/hash/except"
+require "active_support/core_ext/hash/indifferent_access"
+require "active_support/core_ext/hash/keys"
+require "active_support/core_ext/hash/reverse_merge"
+require "active_support/core_ext/hash/slice"
+require "active_support/core_ext/hash/transform_values"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/compact.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/compact.rb
new file mode 100644
index 0000000..d6364dd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/compact.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class Hash
+ unless Hash.instance_methods(false).include?(:compact)
+ # Returns a hash with non +nil+ values.
+ #
+ # hash = { a: true, b: false, c: nil }
+ # hash.compact # => { a: true, b: false }
+ # hash # => { a: true, b: false, c: nil }
+ # { c: nil }.compact # => {}
+ # { c: true }.compact # => { c: true }
+ def compact
+ select { |_, value| !value.nil? }
+ end
+ end
+
+ unless Hash.instance_methods(false).include?(:compact!)
+ # Replaces current hash with non +nil+ values.
+ # Returns +nil+ if no changes were made, otherwise returns the hash.
+ #
+ # hash = { a: true, b: false, c: nil }
+ # hash.compact! # => { a: true, b: false }
+ # hash # => { a: true, b: false }
+ # { c: true }.compact! # => nil
+ def compact!
+ reject! { |_, value| value.nil? }
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/conversions.rb
new file mode 100644
index 0000000..5b48254
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/conversions.rb
@@ -0,0 +1,263 @@
+# frozen_string_literal: true
+
+require "active_support/xml_mini"
+require "active_support/time"
+require "active_support/core_ext/object/blank"
+require "active_support/core_ext/object/to_param"
+require "active_support/core_ext/object/to_query"
+require "active_support/core_ext/array/wrap"
+require "active_support/core_ext/hash/reverse_merge"
+require "active_support/core_ext/string/inflections"
+
+class Hash
+ # Returns a string containing an XML representation of its receiver:
+ #
+ # { foo: 1, bar: 2 }.to_xml
+ # # =>
+ # #
+ # #
+ # # 1
+ # # 2
+ # #
+ #
+ # To do so, the method loops over the pairs and builds nodes that depend on
+ # the _values_. Given a pair +key+, +value+:
+ #
+ # * If +value+ is a hash there's a recursive call with +key+ as :root.
+ #
+ # * If +value+ is an array there's a recursive call with +key+ as :root,
+ # and +key+ singularized as :children.
+ #
+ # * If +value+ is a callable object it must expect one or two arguments. Depending
+ # on the arity, the callable is invoked with the +options+ hash as first argument
+ # with +key+ as :root, and +key+ singularized as second argument. The
+ # callable can add nodes by using options[:builder].
+ #
+ # {foo: lambda { |options, key| options[:builder].b(key) }}.to_xml
+ # # => "foo"
+ #
+ # * If +value+ responds to +to_xml+ the method is invoked with +key+ as :root.
+ #
+ # class Foo
+ # def to_xml(options)
+ # options[:builder].bar 'fooing!'
+ # end
+ # end
+ #
+ # { foo: Foo.new }.to_xml(skip_instruct: true)
+ # # =>
+ # #
+ # # fooing!
+ # #
+ #
+ # * Otherwise, a node with +key+ as tag is created with a string representation of
+ # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
+ # Unless the option :skip_types exists and is true, an attribute "type" is
+ # added as well according to the following mapping:
+ #
+ # XML_TYPE_NAMES = {
+ # "Symbol" => "symbol",
+ # "Integer" => "integer",
+ # "BigDecimal" => "decimal",
+ # "Float" => "float",
+ # "TrueClass" => "boolean",
+ # "FalseClass" => "boolean",
+ # "Date" => "date",
+ # "DateTime" => "dateTime",
+ # "Time" => "dateTime"
+ # }
+ #
+ # By default the root node is "hash", but that's configurable via the :root option.
+ #
+ # The default XML builder is a fresh instance of Builder::XmlMarkup. You can
+ # configure your own builder with the :builder option. The method also accepts
+ # options like :dasherize and friends, they are forwarded to the builder.
+ def to_xml(options = {})
+ require "active_support/builder" unless defined?(Builder)
+
+ options = options.dup
+ options[:indent] ||= 2
+ options[:root] ||= "hash"
+ options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
+
+ builder = options[:builder]
+ builder.instruct! unless options.delete(:skip_instruct)
+
+ root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
+
+ builder.tag!(root) do
+ each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
+ yield builder if block_given?
+ end
+ end
+
+ class << self
+ # Returns a Hash containing a collection of pairs when the key is the node name and the value is
+ # its content
+ #
+ # xml = <<-XML
+ #
+ #
+ # 1
+ # 2
+ #
+ # XML
+ #
+ # hash = Hash.from_xml(xml)
+ # # => {"hash"=>{"foo"=>1, "bar"=>2}}
+ #
+ # +DisallowedType+ is raised if the XML contains attributes with type="yaml" or
+ # type="symbol". Use Hash.from_trusted_xml to
+ # parse this XML.
+ #
+ # Custom +disallowed_types+ can also be passed in the form of an
+ # array.
+ #
+ # xml = <<-XML
+ #
+ #
+ # 1
+ # "David"
+ #
+ # XML
+ #
+ # hash = Hash.from_xml(xml, ['integer'])
+ # # => ActiveSupport::XMLConverter::DisallowedType: Disallowed type attribute: "integer"
+ #
+ # Note that passing custom disallowed types will override the default types,
+ # which are Symbol and YAML.
+ def from_xml(xml, disallowed_types = nil)
+ ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
+ end
+
+ # Builds a Hash from XML just like Hash.from_xml, but also allows Symbol and YAML.
+ def from_trusted_xml(xml)
+ from_xml xml, []
+ end
+ end
+end
+
+module ActiveSupport
+ class XMLConverter # :nodoc:
+ # Raised if the XML contains attributes with type="yaml" or
+ # type="symbol". Read Hash#from_xml for more details.
+ class DisallowedType < StandardError
+ def initialize(type)
+ super "Disallowed type attribute: #{type.inspect}"
+ end
+ end
+
+ DISALLOWED_TYPES = %w(symbol yaml)
+
+ def initialize(xml, disallowed_types = nil)
+ @xml = normalize_keys(XmlMini.parse(xml))
+ @disallowed_types = disallowed_types || DISALLOWED_TYPES
+ end
+
+ def to_h
+ deep_to_h(@xml)
+ end
+
+ private
+ def normalize_keys(params)
+ case params
+ when Hash
+ Hash[params.map { |k, v| [k.to_s.tr("-", "_"), normalize_keys(v)] } ]
+ when Array
+ params.map { |v| normalize_keys(v) }
+ else
+ params
+ end
+ end
+
+ def deep_to_h(value)
+ case value
+ when Hash
+ process_hash(value)
+ when Array
+ process_array(value)
+ when String
+ value
+ else
+ raise "can't typecast #{value.class.name} - #{value.inspect}"
+ end
+ end
+
+ def process_hash(value)
+ if value.include?("type") && !value["type"].is_a?(Hash) && @disallowed_types.include?(value["type"])
+ raise DisallowedType, value["type"]
+ end
+
+ if become_array?(value)
+ _, entries = Array.wrap(value.detect { |k, v| not v.is_a?(String) })
+ if entries.nil? || value["__content__"].try(:empty?)
+ []
+ else
+ case entries
+ when Array
+ entries.collect { |v| deep_to_h(v) }
+ when Hash
+ [deep_to_h(entries)]
+ else
+ raise "can't typecast #{entries.inspect}"
+ end
+ end
+ elsif become_content?(value)
+ process_content(value)
+
+ elsif become_empty_string?(value)
+ ""
+ elsif become_hash?(value)
+ xml_value = Hash[value.map { |k, v| [k, deep_to_h(v)] }]
+
+ # Turn { files: { file: # } } into { files: # } so it is compatible with
+ # how multipart uploaded files from HTML appear
+ xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
+ end
+ end
+
+ def become_content?(value)
+ value["type"] == "file" || (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?))
+ end
+
+ def become_array?(value)
+ value["type"] == "array"
+ end
+
+ def become_empty_string?(value)
+ # { "string" => true }
+ # No tests fail when the second term is removed.
+ value["type"] == "string" && value["nil"] != "true"
+ end
+
+ def become_hash?(value)
+ !nothing?(value) && !garbage?(value)
+ end
+
+ def nothing?(value)
+ # blank or nil parsed values are represented by nil
+ value.blank? || value["nil"] == "true"
+ end
+
+ def garbage?(value)
+ # If the type is the only element which makes it then
+ # this still makes the value nil, except if type is
+ # an XML node(where type['value'] is a Hash)
+ value["type"] && !value["type"].is_a?(::Hash) && value.size == 1
+ end
+
+ def process_content(value)
+ content = value["__content__"]
+ if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
+ parser.arity == 1 ? parser.call(content) : parser.call(content, value)
+ else
+ content
+ end
+ end
+
+ def process_array(value)
+ value.map! { |i| deep_to_h(i) }
+ value.length > 1 ? value : value.first
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/deep_merge.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/deep_merge.rb
new file mode 100644
index 0000000..9bc50b7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/deep_merge.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class Hash
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
+ #
+ # h1 = { a: true, b: { c: [1, 2, 3] } }
+ # h2 = { a: false, b: { x: [3, 4, 5] } }
+ #
+ # h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
+ #
+ # Like with Hash#merge in the standard library, a block can be provided
+ # to merge values:
+ #
+ # h1 = { a: 100, b: 200, c: { c1: 100 } }
+ # h2 = { b: 250, c: { c1: 200 } }
+ # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
+ # # => { a: 100, b: 450, c: { c1: 300 } }
+ def deep_merge(other_hash, &block)
+ dup.deep_merge!(other_hash, &block)
+ end
+
+ # Same as +deep_merge+, but modifies +self+.
+ def deep_merge!(other_hash, &block)
+ merge!(other_hash) do |key, this_val, other_val|
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
+ this_val.deep_merge(other_val, &block)
+ elsif block_given?
+ block.call(key, this_val, other_val)
+ else
+ other_val
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/except.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/except.rb
new file mode 100644
index 0000000..6258610
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/except.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class Hash
+ # Returns a hash that includes everything except given keys.
+ # hash = { a: true, b: false, c: nil }
+ # hash.except(:c) # => { a: true, b: false }
+ # hash.except(:a, :b) # => { c: nil }
+ # hash # => { a: true, b: false, c: nil }
+ #
+ # This is useful for limiting a set of parameters to everything but a few known toggles:
+ # @person.update(params[:person].except(:admin))
+ def except(*keys)
+ dup.except!(*keys)
+ end
+
+ # Removes the given keys from hash and returns it.
+ # hash = { a: true, b: false, c: nil }
+ # hash.except!(:c) # => { a: true, b: false }
+ # hash # => { a: true, b: false }
+ def except!(*keys)
+ keys.each { |key| delete(key) }
+ self
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/indifferent_access.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/indifferent_access.rb
new file mode 100644
index 0000000..a38f33f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require "active_support/hash_with_indifferent_access"
+
+class Hash
+ # Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver:
+ #
+ # { a: 1 }.with_indifferent_access['a'] # => 1
+ def with_indifferent_access
+ ActiveSupport::HashWithIndifferentAccess.new(self)
+ end
+
+ # Called when object is nested under an object that receives
+ # #with_indifferent_access. This method will be called on the current object
+ # by the enclosing object and is aliased to #with_indifferent_access by
+ # default. Subclasses of Hash may overwrite this method to return +self+ if
+ # converting to an ActiveSupport::HashWithIndifferentAccess would not be
+ # desirable.
+ #
+ # b = { b: 1 }
+ # { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
+ # # => {"b"=>1}
+ alias nested_under_indifferent_access with_indifferent_access
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/keys.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/keys.rb
new file mode 100644
index 0000000..bdf196e
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/keys.rb
@@ -0,0 +1,172 @@
+# frozen_string_literal: true
+
+class Hash
+ # Returns a new hash with all keys converted using the +block+ operation.
+ #
+ # hash = { name: 'Rob', age: '28' }
+ #
+ # hash.transform_keys { |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"}
+ #
+ # If you do not provide a +block+, it will return an Enumerator
+ # for chaining with other methods:
+ #
+ # hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"}
+ def transform_keys
+ return enum_for(:transform_keys) { size } unless block_given?
+ result = {}
+ each_key do |key|
+ result[yield(key)] = self[key]
+ end
+ result
+ end unless method_defined? :transform_keys
+
+ # Destructively converts all keys using the +block+ operations.
+ # Same as +transform_keys+ but modifies +self+.
+ def transform_keys!
+ return enum_for(:transform_keys!) { size } unless block_given?
+ keys.each do |key|
+ self[yield(key)] = delete(key)
+ end
+ self
+ end unless method_defined? :transform_keys!
+
+ # Returns a new hash with all keys converted to strings.
+ #
+ # hash = { name: 'Rob', age: '28' }
+ #
+ # hash.stringify_keys
+ # # => {"name"=>"Rob", "age"=>"28"}
+ def stringify_keys
+ transform_keys(&:to_s)
+ end
+
+ # Destructively converts all keys to strings. Same as
+ # +stringify_keys+, but modifies +self+.
+ def stringify_keys!
+ transform_keys!(&:to_s)
+ end
+
+ # Returns a new hash with all keys converted to symbols, as long as
+ # they respond to +to_sym+.
+ #
+ # hash = { 'name' => 'Rob', 'age' => '28' }
+ #
+ # hash.symbolize_keys
+ # # => {:name=>"Rob", :age=>"28"}
+ def symbolize_keys
+ transform_keys { |key| key.to_sym rescue key }
+ end
+ alias_method :to_options, :symbolize_keys
+
+ # Destructively converts all keys to symbols, as long as they respond
+ # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
+ def symbolize_keys!
+ transform_keys! { |key| key.to_sym rescue key }
+ end
+ alias_method :to_options!, :symbolize_keys!
+
+ # Validates all keys in a hash match *valid_keys, raising
+ # +ArgumentError+ on a mismatch.
+ #
+ # Note that keys are treated differently than HashWithIndifferentAccess,
+ # meaning that string and symbol keys will not match.
+ #
+ # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
+ # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
+ # { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
+ def assert_valid_keys(*valid_keys)
+ valid_keys.flatten!
+ each_key do |k|
+ unless valid_keys.include?(k)
+ raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
+ end
+ end
+ end
+
+ # Returns a new hash with all keys converted by the block operation.
+ # This includes the keys from the root hash and from all
+ # nested hashes and arrays.
+ #
+ # hash = { person: { name: 'Rob', age: '28' } }
+ #
+ # hash.deep_transform_keys{ |key| key.to_s.upcase }
+ # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
+ def deep_transform_keys(&block)
+ _deep_transform_keys_in_object(self, &block)
+ end
+
+ # Destructively converts all keys by using the block operation.
+ # This includes the keys from the root hash and from all
+ # nested hashes and arrays.
+ def deep_transform_keys!(&block)
+ _deep_transform_keys_in_object!(self, &block)
+ end
+
+ # Returns a new hash with all keys converted to strings.
+ # This includes the keys from the root hash and from all
+ # nested hashes and arrays.
+ #
+ # hash = { person: { name: 'Rob', age: '28' } }
+ #
+ # hash.deep_stringify_keys
+ # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
+ def deep_stringify_keys
+ deep_transform_keys(&:to_s)
+ end
+
+ # Destructively converts all keys to strings.
+ # This includes the keys from the root hash and from all
+ # nested hashes and arrays.
+ def deep_stringify_keys!
+ deep_transform_keys!(&:to_s)
+ end
+
+ # Returns a new hash with all keys converted to symbols, as long as
+ # they respond to +to_sym+. This includes the keys from the root hash
+ # and from all nested hashes and arrays.
+ #
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
+ #
+ # hash.deep_symbolize_keys
+ # # => {:person=>{:name=>"Rob", :age=>"28"}}
+ def deep_symbolize_keys
+ deep_transform_keys { |key| key.to_sym rescue key }
+ end
+
+ # Destructively converts all keys to symbols, as long as they respond
+ # to +to_sym+. This includes the keys from the root hash and from all
+ # nested hashes and arrays.
+ def deep_symbolize_keys!
+ deep_transform_keys! { |key| key.to_sym rescue key }
+ end
+
+ private
+ # support methods for deep transforming nested hashes and arrays
+ def _deep_transform_keys_in_object(object, &block)
+ case object
+ when Hash
+ object.each_with_object({}) do |(key, value), result|
+ result[yield(key)] = _deep_transform_keys_in_object(value, &block)
+ end
+ when Array
+ object.map { |e| _deep_transform_keys_in_object(e, &block) }
+ else
+ object
+ end
+ end
+
+ def _deep_transform_keys_in_object!(object, &block)
+ case object
+ when Hash
+ object.keys.each do |key|
+ value = object.delete(key)
+ object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
+ end
+ object
+ when Array
+ object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
+ else
+ object
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/reverse_merge.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/reverse_merge.rb
new file mode 100644
index 0000000..ef8d592
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/reverse_merge.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class Hash
+ # Merges the caller into +other_hash+. For example,
+ #
+ # options = options.reverse_merge(size: 25, velocity: 10)
+ #
+ # is equivalent to
+ #
+ # options = { size: 25, velocity: 10 }.merge(options)
+ #
+ # This is particularly useful for initializing an options hash
+ # with default values.
+ def reverse_merge(other_hash)
+ other_hash.merge(self)
+ end
+ alias_method :with_defaults, :reverse_merge
+
+ # Destructive +reverse_merge+.
+ def reverse_merge!(other_hash)
+ replace(reverse_merge(other_hash))
+ end
+ alias_method :reverse_update, :reverse_merge!
+ alias_method :with_defaults!, :reverse_merge!
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/slice.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/slice.rb
new file mode 100644
index 0000000..2bd0a56
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/slice.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+class Hash
+ # Slices a hash to include only the given keys. Returns a hash containing
+ # the given keys.
+ #
+ # { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
+ # # => {:a=>1, :b=>2}
+ #
+ # This is useful for limiting an options hash to valid keys before
+ # passing to a method:
+ #
+ # def search(criteria = {})
+ # criteria.assert_valid_keys(:mass, :velocity, :time)
+ # end
+ #
+ # search(options.slice(:mass, :velocity, :time))
+ #
+ # If you have an array of keys you want to limit to, you should splat them:
+ #
+ # valid_keys = [:mass, :velocity, :time]
+ # search(options.slice(*valid_keys))
+ def slice(*keys)
+ keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
+ end unless method_defined?(:slice)
+
+ # Replaces the hash with only the given keys.
+ # Returns a hash containing the removed key/value pairs.
+ #
+ # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
+ # # => {:c=>3, :d=>4}
+ def slice!(*keys)
+ omit = slice(*self.keys - keys)
+ hash = slice(*keys)
+ hash.default = default
+ hash.default_proc = default_proc if default_proc
+ replace(hash)
+ omit
+ end
+
+ # Removes and returns the key/value pairs matching the given keys.
+ #
+ # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
+ # { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
+ def extract!(*keys)
+ keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/transform_values.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/transform_values.rb
new file mode 100644
index 0000000..4b19c9f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/hash/transform_values.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+class Hash
+ # Returns a new hash with the results of running +block+ once for every value.
+ # The keys are unchanged.
+ #
+ # { a: 1, b: 2, c: 3 }.transform_values { |x| x * 2 } # => { a: 2, b: 4, c: 6 }
+ #
+ # If you do not provide a +block+, it will return an Enumerator
+ # for chaining with other methods:
+ #
+ # { a: 1, b: 2 }.transform_values.with_index { |v, i| [v, i].join.to_i } # => { a: 10, b: 21 }
+ def transform_values
+ return enum_for(:transform_values) { size } unless block_given?
+ return {} if empty?
+ result = self.class.new
+ each do |key, value|
+ result[key] = yield(value)
+ end
+ result
+ end unless method_defined? :transform_values
+
+ # Destructively converts all values using the +block+ operations.
+ # Same as +transform_values+ but modifies +self+.
+ def transform_values!
+ return enum_for(:transform_values!) { size } unless block_given?
+ each do |key, value|
+ self[key] = yield(value)
+ end
+ end unless method_defined? :transform_values!
+ # TODO: Remove this file when supporting only Ruby 2.4+.
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer.rb
new file mode 100644
index 0000000..d227013
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/integer/multiple"
+require "active_support/core_ext/integer/inflections"
+require "active_support/core_ext/integer/time"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer/inflections.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer/inflections.rb
new file mode 100644
index 0000000..aef3266
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer/inflections.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require "active_support/inflector"
+
+class Integer
+ # Ordinalize turns a number into an ordinal string used to denote the
+ # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
+ #
+ # 1.ordinalize # => "1st"
+ # 2.ordinalize # => "2nd"
+ # 1002.ordinalize # => "1002nd"
+ # 1003.ordinalize # => "1003rd"
+ # -11.ordinalize # => "-11th"
+ # -1001.ordinalize # => "-1001st"
+ def ordinalize
+ ActiveSupport::Inflector.ordinalize(self)
+ end
+
+ # Ordinal returns the suffix used to denote the position
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
+ #
+ # 1.ordinal # => "st"
+ # 2.ordinal # => "nd"
+ # 1002.ordinal # => "nd"
+ # 1003.ordinal # => "rd"
+ # -11.ordinal # => "th"
+ # -1001.ordinal # => "st"
+ def ordinal
+ ActiveSupport::Inflector.ordinal(self)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer/multiple.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer/multiple.rb
new file mode 100644
index 0000000..e760666
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer/multiple.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class Integer
+ # Check whether the integer is evenly divisible by the argument.
+ #
+ # 0.multiple_of?(0) # => true
+ # 6.multiple_of?(5) # => false
+ # 10.multiple_of?(2) # => true
+ def multiple_of?(number)
+ number != 0 ? self % number == 0 : zero?
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer/time.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer/time.rb
new file mode 100644
index 0000000..5efb89c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/integer/time.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "active_support/duration"
+require "active_support/core_ext/numeric/time"
+
+class Integer
+ # Returns a Duration instance matching the number of months provided.
+ #
+ # 2.months # => 2 months
+ def months
+ ActiveSupport::Duration.months(self)
+ end
+ alias :month :months
+
+ # Returns a Duration instance matching the number of years provided.
+ #
+ # 2.years # => 2 years
+ def years
+ ActiveSupport::Duration.years(self)
+ end
+ alias :year :years
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel.rb
new file mode 100644
index 0000000..0f4356f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/kernel/agnostics"
+require "active_support/core_ext/kernel/concern"
+require "active_support/core_ext/kernel/reporting"
+require "active_support/core_ext/kernel/singleton_class"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/agnostics.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/agnostics.rb
new file mode 100644
index 0000000..403b5f3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/agnostics.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class Object
+ # Makes backticks behave (somewhat more) similarly on all platforms.
+ # On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
+ # spawned shell prints a message to stderr and sets $?. We emulate
+ # Unix on the former but not the latter.
+ def `(command) #:nodoc:
+ super
+ rescue Errno::ENOENT => e
+ STDERR.puts "#$0: #{e}"
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/concern.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/concern.rb
new file mode 100644
index 0000000..0b2baed
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/concern.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/concerning"
+
+module Kernel
+ module_function
+
+ # A shortcut to define a toplevel concern, not within a module.
+ #
+ # See Module::Concerning for more.
+ def concern(topic, &module_definition)
+ Object.concern topic, &module_definition
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/reporting.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/reporting.rb
new file mode 100644
index 0000000..9155bd6
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/reporting.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Kernel
+ module_function
+
+ # Sets $VERBOSE to +nil+ for the duration of the block and back to its original
+ # value afterwards.
+ #
+ # silence_warnings do
+ # value = noisy_call # no warning voiced
+ # end
+ #
+ # noisy_call # warning voiced
+ def silence_warnings
+ with_warnings(nil) { yield }
+ end
+
+ # Sets $VERBOSE to +true+ for the duration of the block and back to its
+ # original value afterwards.
+ def enable_warnings
+ with_warnings(true) { yield }
+ end
+
+ # Sets $VERBOSE for the duration of the block and back to its original
+ # value afterwards.
+ def with_warnings(flag)
+ old_verbose, $VERBOSE = $VERBOSE, flag
+ yield
+ ensure
+ $VERBOSE = old_verbose
+ end
+
+ # Blocks and ignores any exception passed as argument if raised within the block.
+ #
+ # suppress(ZeroDivisionError) do
+ # 1/0
+ # puts 'This code is NOT reached'
+ # end
+ #
+ # puts 'This code gets executed and nothing related to ZeroDivisionError was seen'
+ def suppress(*exception_classes)
+ yield
+ rescue *exception_classes
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/singleton_class.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/singleton_class.rb
new file mode 100644
index 0000000..6715eba
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/kernel/singleton_class.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Kernel
+ # class_eval on an object acts like singleton_class.class_eval.
+ def class_eval(*args, &block)
+ singleton_class.class_eval(*args, &block)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/load_error.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/load_error.rb
new file mode 100644
index 0000000..750f858
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/load_error.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class LoadError
+ # Returns true if the given path name (except perhaps for the ".rb"
+ # extension) is the missing file which caused the exception to be raised.
+ def is_missing?(location)
+ location.sub(/\.rb$/, "".freeze) == path.sub(/\.rb$/, "".freeze)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/marshal.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/marshal.rb
new file mode 100644
index 0000000..0c72cd7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/marshal.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module MarshalWithAutoloading # :nodoc:
+ def load(source, proc = nil)
+ super(source, proc)
+ rescue ArgumentError, NameError => exc
+ if exc.message.match(%r|undefined class/module (.+?)(?:::)?\z|)
+ # try loading the class/module
+ loaded = $1.constantize
+
+ raise unless $1 == loaded.name
+
+ # if it is an IO we need to go back to read the object
+ source.rewind if source.respond_to?(:rewind)
+ retry
+ else
+ raise exc
+ end
+ end
+ end
+end
+
+Marshal.singleton_class.prepend(ActiveSupport::MarshalWithAutoloading)
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module.rb
new file mode 100644
index 0000000..d91e3fb
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/aliasing"
+require "active_support/core_ext/module/introspection"
+require "active_support/core_ext/module/anonymous"
+require "active_support/core_ext/module/reachable"
+require "active_support/core_ext/module/attribute_accessors"
+require "active_support/core_ext/module/attribute_accessors_per_thread"
+require "active_support/core_ext/module/attr_internal"
+require "active_support/core_ext/module/concerning"
+require "active_support/core_ext/module/delegation"
+require "active_support/core_ext/module/deprecation"
+require "active_support/core_ext/module/redefine_method"
+require "active_support/core_ext/module/remove_method"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/aliasing.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/aliasing.rb
new file mode 100644
index 0000000..6f64d11
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/aliasing.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class Module
+ # Allows you to make aliases for attributes, which includes
+ # getter, setter, and a predicate.
+ #
+ # class Content < ActiveRecord::Base
+ # # has a title attribute
+ # end
+ #
+ # class Email < Content
+ # alias_attribute :subject, :title
+ # end
+ #
+ # e = Email.find(1)
+ # e.title # => "Superstars"
+ # e.subject # => "Superstars"
+ # e.subject? # => true
+ # e.subject = "Megastars"
+ # e.title # => "Megastars"
+ def alias_attribute(new_name, old_name)
+ # The following reader methods use an explicit `self` receiver in order to
+ # support aliases that start with an uppercase letter. Otherwise, they would
+ # be resolved as constants instead.
+ module_eval <<-STR, __FILE__, __LINE__ + 1
+ def #{new_name}; self.#{old_name}; end # def subject; self.title; end
+ def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
+ def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
+ STR
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/anonymous.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/anonymous.rb
new file mode 100644
index 0000000..d1c86b8
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/anonymous.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class Module
+ # A module may or may not have a name.
+ #
+ # module M; end
+ # M.name # => "M"
+ #
+ # m = Module.new
+ # m.name # => nil
+ #
+ # +anonymous?+ method returns true if module does not have a name, false otherwise:
+ #
+ # Module.new.anonymous? # => true
+ #
+ # module M; end
+ # M.anonymous? # => false
+ #
+ # A module gets a name when it is first assigned to a constant. Either
+ # via the +module+ or +class+ keyword or by an explicit assignment:
+ #
+ # m = Module.new # creates an anonymous module
+ # m.anonymous? # => true
+ # M = m # m gets a name here as a side-effect
+ # m.name # => "M"
+ # m.anonymous? # => false
+ def anonymous?
+ name.nil?
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/attr_internal.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/attr_internal.rb
new file mode 100644
index 0000000..7801f6d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/attr_internal.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+class Module
+ # Declares an attribute reader backed by an internally-named instance variable.
+ def attr_internal_reader(*attrs)
+ attrs.each { |attr_name| attr_internal_define(attr_name, :reader) }
+ end
+
+ # Declares an attribute writer backed by an internally-named instance variable.
+ def attr_internal_writer(*attrs)
+ attrs.each { |attr_name| attr_internal_define(attr_name, :writer) }
+ end
+
+ # Declares an attribute reader and writer backed by an internally-named instance
+ # variable.
+ def attr_internal_accessor(*attrs)
+ attr_internal_reader(*attrs)
+ attr_internal_writer(*attrs)
+ end
+ alias_method :attr_internal, :attr_internal_accessor
+
+ class << self; attr_accessor :attr_internal_naming_format end
+ self.attr_internal_naming_format = "@_%s"
+
+ private
+ def attr_internal_ivar_name(attr)
+ Module.attr_internal_naming_format % attr
+ end
+
+ def attr_internal_define(attr_name, type)
+ internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, "")
+ # use native attr_* methods as they are faster on some Ruby implementations
+ send("attr_#{type}", internal_name)
+ attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
+ alias_method attr_name, internal_name
+ remove_method internal_name
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/attribute_accessors.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/attribute_accessors.rb
new file mode 100644
index 0000000..580baff
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -0,0 +1,215 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/array/extract_options"
+require "active_support/core_ext/regexp"
+
+# Extends the module object with class/module and instance accessors for
+# class/module attributes, just like the native attr* accessors for instance
+# attributes.
+class Module
+ # Defines a class attribute and creates a class and instance reader methods.
+ # The underlying class variable is set to +nil+, if it is not previously
+ # defined. All class and instance methods created will be public, even if
+ # this method is called with a private or protected access modifier.
+ #
+ # module HairColors
+ # mattr_reader :hair_colors
+ # end
+ #
+ # HairColors.hair_colors # => nil
+ # HairColors.class_variable_set("@@hair_colors", [:brown, :black])
+ # HairColors.hair_colors # => [:brown, :black]
+ #
+ # The attribute name must be a valid method name in Ruby.
+ #
+ # module Foo
+ # mattr_reader :"1_Badname"
+ # end
+ # # => NameError: invalid attribute name: 1_Badname
+ #
+ # If you want to opt out the creation on the instance reader method, pass
+ # instance_reader: false or instance_accessor: false.
+ #
+ # module HairColors
+ # mattr_reader :hair_colors, instance_reader: false
+ # end
+ #
+ # class Person
+ # include HairColors
+ # end
+ #
+ # Person.new.hair_colors # => NoMethodError
+ #
+ # You can set a default value for the attribute.
+ #
+ # module HairColors
+ # mattr_reader :hair_colors, default: [:brown, :black, :blonde, :red]
+ # end
+ #
+ # class Person
+ # include HairColors
+ # end
+ #
+ # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
+ def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil)
+ syms.each do |sym|
+ raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ @@#{sym} = nil unless defined? @@#{sym}
+
+ def self.#{sym}
+ @@#{sym}
+ end
+ EOS
+
+ if instance_reader && instance_accessor
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{sym}
+ @@#{sym}
+ end
+ EOS
+ end
+
+ sym_default_value = (block_given? && default.nil?) ? yield : default
+ class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil?
+ end
+ end
+ alias :cattr_reader :mattr_reader
+
+ # Defines a class attribute and creates a class and instance writer methods to
+ # allow assignment to the attribute. All class and instance methods created
+ # will be public, even if this method is called with a private or protected
+ # access modifier.
+ #
+ # module HairColors
+ # mattr_writer :hair_colors
+ # end
+ #
+ # class Person
+ # include HairColors
+ # end
+ #
+ # HairColors.hair_colors = [:brown, :black]
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black]
+ # Person.new.hair_colors = [:blonde, :red]
+ # HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
+ #
+ # If you want to opt out the instance writer method, pass
+ # instance_writer: false or instance_accessor: false.
+ #
+ # module HairColors
+ # mattr_writer :hair_colors, instance_writer: false
+ # end
+ #
+ # class Person
+ # include HairColors
+ # end
+ #
+ # Person.new.hair_colors = [:blonde, :red] # => NoMethodError
+ #
+ # You can set a default value for the attribute.
+ #
+ # module HairColors
+ # mattr_writer :hair_colors, default: [:brown, :black, :blonde, :red]
+ # end
+ #
+ # class Person
+ # include HairColors
+ # end
+ #
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
+ def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil)
+ syms.each do |sym|
+ raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ @@#{sym} = nil unless defined? @@#{sym}
+
+ def self.#{sym}=(obj)
+ @@#{sym} = obj
+ end
+ EOS
+
+ if instance_writer && instance_accessor
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{sym}=(obj)
+ @@#{sym} = obj
+ end
+ EOS
+ end
+
+ sym_default_value = (block_given? && default.nil?) ? yield : default
+ send("#{sym}=", sym_default_value) unless sym_default_value.nil?
+ end
+ end
+ alias :cattr_writer :mattr_writer
+
+ # Defines both class and instance accessors for class attributes.
+ # All class and instance methods created will be public, even if
+ # this method is called with a private or protected access modifier.
+ #
+ # module HairColors
+ # mattr_accessor :hair_colors
+ # end
+ #
+ # class Person
+ # include HairColors
+ # end
+ #
+ # HairColors.hair_colors = [:brown, :black, :blonde, :red]
+ # HairColors.hair_colors # => [:brown, :black, :blonde, :red]
+ # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
+ #
+ # If a subclass changes the value then that would also change the value for
+ # parent class. Similarly if parent class changes the value then that would
+ # change the value of subclasses too.
+ #
+ # class Male < Person
+ # end
+ #
+ # Male.new.hair_colors << :blue
+ # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
+ #
+ # To opt out of the instance writer method, pass instance_writer: false.
+ # To opt out of the instance reader method, pass instance_reader: false.
+ #
+ # module HairColors
+ # mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
+ # end
+ #
+ # class Person
+ # include HairColors
+ # end
+ #
+ # Person.new.hair_colors = [:brown] # => NoMethodError
+ # Person.new.hair_colors # => NoMethodError
+ #
+ # Or pass instance_accessor: false, to opt out both instance methods.
+ #
+ # module HairColors
+ # mattr_accessor :hair_colors, instance_accessor: false
+ # end
+ #
+ # class Person
+ # include HairColors
+ # end
+ #
+ # Person.new.hair_colors = [:brown] # => NoMethodError
+ # Person.new.hair_colors # => NoMethodError
+ #
+ # You can set a default value for the attribute.
+ #
+ # module HairColors
+ # mattr_accessor :hair_colors, default: [:brown, :black, :blonde, :red]
+ # end
+ #
+ # class Person
+ # include HairColors
+ # end
+ #
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
+ def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
+ mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, &blk)
+ mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default)
+ end
+ alias :cattr_accessor :mattr_accessor
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
new file mode 100644
index 0000000..4b9b6ea
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/array/extract_options"
+require "active_support/core_ext/regexp"
+
+# Extends the module object with class/module and instance accessors for
+# class/module attributes, just like the native attr* accessors for instance
+# attributes, but does so on a per-thread basis.
+#
+# So the values are scoped within the Thread.current space under the class name
+# of the module.
+class Module
+ # Defines a per-thread class attribute and creates class and instance reader methods.
+ # The underlying per-thread class variable is set to +nil+, if it is not previously defined.
+ #
+ # module Current
+ # thread_mattr_reader :user
+ # end
+ #
+ # Current.user # => nil
+ # Thread.current[:attr_Current_user] = "DHH"
+ # Current.user # => "DHH"
+ #
+ # The attribute name must be a valid method name in Ruby.
+ #
+ # module Foo
+ # thread_mattr_reader :"1_Badname"
+ # end
+ # # => NameError: invalid attribute name: 1_Badname
+ #
+ # If you want to opt out of the creation of the instance reader method, pass
+ # instance_reader: false or instance_accessor: false.
+ #
+ # class Current
+ # thread_mattr_reader :user, instance_reader: false
+ # end
+ #
+ # Current.new.user # => NoMethodError
+ def thread_mattr_reader(*syms) # :nodoc:
+ options = syms.extract_options!
+
+ syms.each do |sym|
+ raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
+
+ # The following generated method concatenates `name` because we want it
+ # to work with inheritance via polymorphism.
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def self.#{sym}
+ Thread.current["attr_" + name + "_#{sym}"]
+ end
+ EOS
+
+ unless options[:instance_reader] == false || options[:instance_accessor] == false
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{sym}
+ self.class.#{sym}
+ end
+ EOS
+ end
+ end
+ end
+ alias :thread_cattr_reader :thread_mattr_reader
+
+ # Defines a per-thread class attribute and creates a class and instance writer methods to
+ # allow assignment to the attribute.
+ #
+ # module Current
+ # thread_mattr_writer :user
+ # end
+ #
+ # Current.user = "DHH"
+ # Thread.current[:attr_Current_user] # => "DHH"
+ #
+ # If you want to opt out of the creation of the instance writer method, pass
+ # instance_writer: false or instance_accessor: false.
+ #
+ # class Current
+ # thread_mattr_writer :user, instance_writer: false
+ # end
+ #
+ # Current.new.user = "DHH" # => NoMethodError
+ def thread_mattr_writer(*syms) # :nodoc:
+ options = syms.extract_options!
+ syms.each do |sym|
+ raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
+
+ # The following generated method concatenates `name` because we want it
+ # to work with inheritance via polymorphism.
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def self.#{sym}=(obj)
+ Thread.current["attr_" + name + "_#{sym}"] = obj
+ end
+ EOS
+
+ unless options[:instance_writer] == false || options[:instance_accessor] == false
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{sym}=(obj)
+ self.class.#{sym} = obj
+ end
+ EOS
+ end
+ end
+ end
+ alias :thread_cattr_writer :thread_mattr_writer
+
+ # Defines both class and instance accessors for class attributes.
+ #
+ # class Account
+ # thread_mattr_accessor :user
+ # end
+ #
+ # Account.user = "DHH"
+ # Account.user # => "DHH"
+ # Account.new.user # => "DHH"
+ #
+ # If a subclass changes the value, the parent class' value is not changed.
+ # Similarly, if the parent class changes the value, the value of subclasses
+ # is not changed.
+ #
+ # class Customer < Account
+ # end
+ #
+ # Customer.user = "Rafael"
+ # Customer.user # => "Rafael"
+ # Account.user # => "DHH"
+ #
+ # To opt out of the instance writer method, pass instance_writer: false.
+ # To opt out of the instance reader method, pass instance_reader: false.
+ #
+ # class Current
+ # thread_mattr_accessor :user, instance_writer: false, instance_reader: false
+ # end
+ #
+ # Current.new.user = "DHH" # => NoMethodError
+ # Current.new.user # => NoMethodError
+ #
+ # Or pass instance_accessor: false, to opt out both instance methods.
+ #
+ # class Current
+ # mattr_accessor :user, instance_accessor: false
+ # end
+ #
+ # Current.new.user = "DHH" # => NoMethodError
+ # Current.new.user # => NoMethodError
+ def thread_mattr_accessor(*syms)
+ thread_mattr_reader(*syms)
+ thread_mattr_writer(*syms)
+ end
+ alias :thread_cattr_accessor :thread_mattr_accessor
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/concerning.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/concerning.rb
new file mode 100644
index 0000000..7bbbf32
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/concerning.rb
@@ -0,0 +1,134 @@
+# frozen_string_literal: true
+
+require "active_support/concern"
+
+class Module
+ # = Bite-sized separation of concerns
+ #
+ # We often find ourselves with a medium-sized chunk of behavior that we'd
+ # like to extract, but only mix in to a single class.
+ #
+ # Extracting a plain old Ruby object to encapsulate it and collaborate or
+ # delegate to the original object is often a good choice, but when there's
+ # no additional state to encapsulate or we're making DSL-style declarations
+ # about the parent class, introducing new collaborators can obfuscate rather
+ # than simplify.
+ #
+ # The typical route is to just dump everything in a monolithic class, perhaps
+ # with a comment, as a least-bad alternative. Using modules in separate files
+ # means tedious sifting to get a big-picture view.
+ #
+ # = Dissatisfying ways to separate small concerns
+ #
+ # == Using comments:
+ #
+ # class Todo < ApplicationRecord
+ # # Other todo implementation
+ # # ...
+ #
+ # ## Event tracking
+ # has_many :events
+ #
+ # before_create :track_creation
+ #
+ # private
+ # def track_creation
+ # # ...
+ # end
+ # end
+ #
+ # == With an inline module:
+ #
+ # Noisy syntax.
+ #
+ # class Todo < ApplicationRecord
+ # # Other todo implementation
+ # # ...
+ #
+ # module EventTracking
+ # extend ActiveSupport::Concern
+ #
+ # included do
+ # has_many :events
+ # before_create :track_creation
+ # end
+ #
+ # private
+ # def track_creation
+ # # ...
+ # end
+ # end
+ # include EventTracking
+ # end
+ #
+ # == Mix-in noise exiled to its own file:
+ #
+ # Once our chunk of behavior starts pushing the scroll-to-understand-it
+ # boundary, we give in and move it to a separate file. At this size, the
+ # increased overhead can be a reasonable tradeoff even if it reduces our
+ # at-a-glance perception of how things work.
+ #
+ # class Todo < ApplicationRecord
+ # # Other todo implementation
+ # # ...
+ #
+ # include TodoEventTracking
+ # end
+ #
+ # = Introducing Module#concerning
+ #
+ # By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
+ # separate bite-sized concerns.
+ #
+ # class Todo < ApplicationRecord
+ # # Other todo implementation
+ # # ...
+ #
+ # concerning :EventTracking do
+ # included do
+ # has_many :events
+ # before_create :track_creation
+ # end
+ #
+ # private
+ # def track_creation
+ # # ...
+ # end
+ # end
+ # end
+ #
+ # Todo.ancestors
+ # # => [Todo, Todo::EventTracking, ApplicationRecord, Object]
+ #
+ # This small step has some wonderful ripple effects. We can
+ # * grok the behavior of our class in one glance,
+ # * clean up monolithic junk-drawer classes by separating their concerns, and
+ # * stop leaning on protected/private for crude "this is internal stuff" modularity.
+ module Concerning
+ # Define a new concern and mix it in.
+ def concerning(topic, &block)
+ include concern(topic, &block)
+ end
+
+ # A low-cruft shortcut to define a concern.
+ #
+ # concern :EventTracking do
+ # ...
+ # end
+ #
+ # is equivalent to
+ #
+ # module EventTracking
+ # extend ActiveSupport::Concern
+ #
+ # ...
+ # end
+ def concern(topic, &module_definition)
+ const_set topic, Module.new {
+ extend ::ActiveSupport::Concern
+ module_eval(&module_definition)
+ }
+ end
+ end
+ include Concerning
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/delegation.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/delegation.rb
new file mode 100644
index 0000000..4310df3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/delegation.rb
@@ -0,0 +1,287 @@
+# frozen_string_literal: true
+
+require "set"
+require "active_support/core_ext/regexp"
+
+class Module
+ # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
+ # option is not used.
+ class DelegationError < NoMethodError; end
+
+ RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do
+ else elsif END end ensure false for if in module next nil not or redo rescue retry
+ return self super then true undef unless until when while yield)
+ DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
+ DELEGATION_RESERVED_METHOD_NAMES = Set.new(
+ RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
+ ).freeze
+
+ # Provides a +delegate+ class method to easily expose contained objects'
+ # public methods as your own.
+ #
+ # ==== Options
+ # * :to - Specifies the target object
+ # * :prefix - Prefixes the new method with the target name or a custom prefix
+ # * :allow_nil - if set to true, prevents a +Module::DelegationError+
+ # from being raised
+ #
+ # The macro receives one or more method names (specified as symbols or
+ # strings) and the name of the target object via the :to option
+ # (also a symbol or string).
+ #
+ # Delegation is particularly useful with Active Record associations:
+ #
+ # class Greeter < ActiveRecord::Base
+ # def hello
+ # 'hello'
+ # end
+ #
+ # def goodbye
+ # 'goodbye'
+ # end
+ # end
+ #
+ # class Foo < ActiveRecord::Base
+ # belongs_to :greeter
+ # delegate :hello, to: :greeter
+ # end
+ #
+ # Foo.new.hello # => "hello"
+ # Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #
+ #
+ # Multiple delegates to the same target are allowed:
+ #
+ # class Foo < ActiveRecord::Base
+ # belongs_to :greeter
+ # delegate :hello, :goodbye, to: :greeter
+ # end
+ #
+ # Foo.new.goodbye # => "goodbye"
+ #
+ # Methods can be delegated to instance variables, class variables, or constants
+ # by providing them as a symbols:
+ #
+ # class Foo
+ # CONSTANT_ARRAY = [0,1,2,3]
+ # @@class_array = [4,5,6,7]
+ #
+ # def initialize
+ # @instance_array = [8,9,10,11]
+ # end
+ # delegate :sum, to: :CONSTANT_ARRAY
+ # delegate :min, to: :@@class_array
+ # delegate :max, to: :@instance_array
+ # end
+ #
+ # Foo.new.sum # => 6
+ # Foo.new.min # => 4
+ # Foo.new.max # => 11
+ #
+ # It's also possible to delegate a method to the class by using +:class+:
+ #
+ # class Foo
+ # def self.hello
+ # "world"
+ # end
+ #
+ # delegate :hello, to: :class
+ # end
+ #
+ # Foo.new.hello # => "world"
+ #
+ # Delegates can optionally be prefixed using the :prefix option. If the value
+ # is true, the delegate methods are prefixed with the name of the object being
+ # delegated to.
+ #
+ # Person = Struct.new(:name, :address)
+ #
+ # class Invoice < Struct.new(:client)
+ # delegate :name, :address, to: :client, prefix: true
+ # end
+ #
+ # john_doe = Person.new('John Doe', 'Vimmersvej 13')
+ # invoice = Invoice.new(john_doe)
+ # invoice.client_name # => "John Doe"
+ # invoice.client_address # => "Vimmersvej 13"
+ #
+ # It is also possible to supply a custom prefix.
+ #
+ # class Invoice < Struct.new(:client)
+ # delegate :name, :address, to: :client, prefix: :customer
+ # end
+ #
+ # invoice = Invoice.new(john_doe)
+ # invoice.customer_name # => 'John Doe'
+ # invoice.customer_address # => 'Vimmersvej 13'
+ #
+ # If the target is +nil+ and does not respond to the delegated method a
+ # +Module::DelegationError+ is raised. If you wish to instead return +nil+,
+ # use the :allow_nil option.
+ #
+ # class User < ActiveRecord::Base
+ # has_one :profile
+ # delegate :age, to: :profile
+ # end
+ #
+ # User.new.age
+ # # => Module::DelegationError: User#age delegated to profile.age, but profile is nil
+ #
+ # But if not having a profile yet is fine and should not be an error
+ # condition:
+ #
+ # class User < ActiveRecord::Base
+ # has_one :profile
+ # delegate :age, to: :profile, allow_nil: true
+ # end
+ #
+ # User.new.age # nil
+ #
+ # Note that if the target is not +nil+ then the call is attempted regardless of the
+ # :allow_nil option, and thus an exception is still raised if said object
+ # does not respond to the method:
+ #
+ # class Foo
+ # def initialize(bar)
+ # @bar = bar
+ # end
+ #
+ # delegate :name, to: :@bar, allow_nil: true
+ # end
+ #
+ # Foo.new("Bar").name # raises NoMethodError: undefined method `name'
+ #
+ # The target method must be public, otherwise it will raise +NoMethodError+.
+ def delegate(*methods, to: nil, prefix: nil, allow_nil: nil)
+ unless to
+ raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter)."
+ end
+
+ if prefix == true && /^[^a-z_]/.match?(to)
+ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
+ end
+
+ method_prefix = \
+ if prefix
+ "#{prefix == true ? to : prefix}_"
+ else
+ ""
+ end
+
+ location = caller_locations(1, 1).first
+ file, line = location.path, location.lineno
+
+ to = to.to_s
+ to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
+
+ methods.map do |method|
+ # Attribute writer methods only accept one argument. Makes sure []=
+ # methods still accept two arguments.
+ definition = /[^\]]=$/.match?(method) ? "arg" : "*args, &block"
+
+ # The following generated method calls the target exactly once, storing
+ # the returned value in a dummy variable.
+ #
+ # Reason is twofold: On one hand doing less calls is in general better.
+ # On the other hand it could be that the target has side-effects,
+ # whereas conceptually, from the user point of view, the delegator should
+ # be doing one call.
+ if allow_nil
+ method_def = [
+ "def #{method_prefix}#{method}(#{definition})",
+ "_ = #{to}",
+ "if !_.nil? || nil.respond_to?(:#{method})",
+ " _.#{method}(#{definition})",
+ "end",
+ "end"
+ ].join ";"
+ else
+ exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
+
+ method_def = [
+ "def #{method_prefix}#{method}(#{definition})",
+ " _ = #{to}",
+ " _.#{method}(#{definition})",
+ "rescue NoMethodError => e",
+ " if _.nil? && e.name == :#{method}",
+ " #{exception}",
+ " else",
+ " raise",
+ " end",
+ "end"
+ ].join ";"
+ end
+
+ module_eval(method_def, file, line)
+ end
+ end
+
+ # When building decorators, a common pattern may emerge:
+ #
+ # class Partition
+ # def initialize(event)
+ # @event = event
+ # end
+ #
+ # def person
+ # @event.detail.person || @event.creator
+ # end
+ #
+ # private
+ # def respond_to_missing?(name, include_private = false)
+ # @event.respond_to?(name, include_private)
+ # end
+ #
+ # def method_missing(method, *args, &block)
+ # @event.send(method, *args, &block)
+ # end
+ # end
+ #
+ # With Module#delegate_missing_to, the above is condensed to:
+ #
+ # class Partition
+ # delegate_missing_to :@event
+ #
+ # def initialize(event)
+ # @event = event
+ # end
+ #
+ # def person
+ # @event.detail.person || @event.creator
+ # end
+ # end
+ #
+ # The target can be anything callable within the object, e.g. instance
+ # variables, methods, constants, etc.
+ #
+ # The delegated method must be public on the target, otherwise it will
+ # raise +NoMethodError+.
+ def delegate_missing_to(target)
+ target = target.to_s
+ target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
+
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def respond_to_missing?(name, include_private = false)
+ # It may look like an oversight, but we deliberately do not pass
+ # +include_private+, because they do not get delegated.
+
+ #{target}.respond_to?(name) || super
+ end
+
+ def method_missing(method, *args, &block)
+ if #{target}.respond_to?(method)
+ #{target}.public_send(method, *args, &block)
+ else
+ begin
+ super
+ rescue NoMethodError
+ if #{target}.nil?
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
+ else
+ raise
+ end
+ end
+ end
+ end
+ RUBY
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/deprecation.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/deprecation.rb
new file mode 100644
index 0000000..71c42eb
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/deprecation.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class Module
+ # deprecate :foo
+ # deprecate bar: 'message'
+ # deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
+ #
+ # You can also use custom deprecator instance:
+ #
+ # deprecate :foo, deprecator: MyLib::Deprecator.new
+ # deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
+ #
+ # \Custom deprecators must respond to deprecation_warning(deprecated_method_name, message, caller_backtrace)
+ # method where you can implement your custom warning behavior.
+ #
+ # class MyLib::Deprecator
+ # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
+ # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
+ # Kernel.warn message
+ # end
+ # end
+ def deprecate(*method_names)
+ ActiveSupport::Deprecation.deprecate_methods(self, *method_names)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/introspection.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/introspection.rb
new file mode 100644
index 0000000..c5bb598
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/introspection.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require "active_support/inflector"
+
+class Module
+ # Returns the name of the module containing this one.
+ #
+ # M::N.parent_name # => "M"
+ def parent_name
+ if defined?(@parent_name)
+ @parent_name
+ else
+ parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
+ @parent_name = parent_name unless frozen?
+ parent_name
+ end
+ end
+
+ # Returns the module which contains this one according to its name.
+ #
+ # module M
+ # module N
+ # end
+ # end
+ # X = M::N
+ #
+ # M::N.parent # => M
+ # X.parent # => M
+ #
+ # The parent of top-level and anonymous modules is Object.
+ #
+ # M.parent # => Object
+ # Module.new.parent # => Object
+ def parent
+ parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
+ end
+
+ # Returns all the parents of this module according to its name, ordered from
+ # nested outwards. The receiver is not contained within the result.
+ #
+ # module M
+ # module N
+ # end
+ # end
+ # X = M::N
+ #
+ # M.parents # => [Object]
+ # M::N.parents # => [M, Object]
+ # X.parents # => [M, Object]
+ def parents
+ parents = []
+ if parent_name
+ parts = parent_name.split("::")
+ until parts.empty?
+ parents << ActiveSupport::Inflector.constantize(parts * "::")
+ parts.pop
+ end
+ end
+ parents << Object unless parents.include? Object
+ parents
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/reachable.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/reachable.rb
new file mode 100644
index 0000000..e9cbda5
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/reachable.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/anonymous"
+require "active_support/core_ext/string/inflections"
+
+class Module
+ def reachable? #:nodoc:
+ !anonymous? && name.safe_constantize.equal?(self)
+ end
+ deprecate :reachable?
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/redefine_method.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/redefine_method.rb
new file mode 100644
index 0000000..a0a6622
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/redefine_method.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+class Module
+ if RUBY_VERSION >= "2.3"
+ # Marks the named method as intended to be redefined, if it exists.
+ # Suppresses the Ruby method redefinition warning. Prefer
+ # #redefine_method where possible.
+ def silence_redefinition_of_method(method)
+ if method_defined?(method) || private_method_defined?(method)
+ # This suppresses the "method redefined" warning; the self-alias
+ # looks odd, but means we don't need to generate a unique name
+ alias_method method, method
+ end
+ end
+ else
+ def silence_redefinition_of_method(method)
+ if method_defined?(method) || private_method_defined?(method)
+ alias_method :__rails_redefine, method
+ remove_method :__rails_redefine
+ end
+ end
+ end
+
+ # Replaces the existing method definition, if there is one, with the passed
+ # block as its body.
+ def redefine_method(method, &block)
+ visibility = method_visibility(method)
+ silence_redefinition_of_method(method)
+ define_method(method, &block)
+ send(visibility, method)
+ end
+
+ # Replaces the existing singleton method definition, if there is one, with
+ # the passed block as its body.
+ def redefine_singleton_method(method, &block)
+ singleton_class.redefine_method(method, &block)
+ end
+
+ def method_visibility(method) # :nodoc:
+ case
+ when private_method_defined?(method)
+ :private
+ when protected_method_defined?(method)
+ :protected
+ else
+ :public
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/remove_method.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/remove_method.rb
new file mode 100644
index 0000000..97eb5f9
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/module/remove_method.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/redefine_method"
+
+class Module
+ # Removes the named method, if it exists.
+ def remove_possible_method(method)
+ if method_defined?(method) || private_method_defined?(method)
+ undef_method(method)
+ end
+ end
+
+ # Removes the named singleton method, if it exists.
+ def remove_possible_singleton_method(method)
+ singleton_class.remove_possible_method(method)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/name_error.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/name_error.rb
new file mode 100644
index 0000000..6d37cd9
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/name_error.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+class NameError
+ # Extract the name of the missing constant from the exception message.
+ #
+ # begin
+ # HelloWorld
+ # rescue NameError => e
+ # e.missing_name
+ # end
+ # # => "HelloWorld"
+ def missing_name
+ # Since ruby v2.3.0 `did_you_mean` gem is loaded by default.
+ # It extends NameError#message with spell corrections which are SLOW.
+ # We should use original_message message instead.
+ message = respond_to?(:original_message) ? original_message : self.message
+
+ if /undefined local variable or method/ !~ message
+ $1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
+ end
+ end
+
+ # Was this exception raised because the given name was missing?
+ #
+ # begin
+ # HelloWorld
+ # rescue NameError => e
+ # e.missing_name?("HelloWorld")
+ # end
+ # # => true
+ def missing_name?(name)
+ if name.is_a? Symbol
+ self.name == name
+ else
+ missing_name == name.to_s
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric.rb
new file mode 100644
index 0000000..0b04e35
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/numeric/bytes"
+require "active_support/core_ext/numeric/time"
+require "active_support/core_ext/numeric/inquiry"
+require "active_support/core_ext/numeric/conversions"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/bytes.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/bytes.rb
new file mode 100644
index 0000000..b002eba
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/bytes.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+class Numeric
+ KILOBYTE = 1024
+ MEGABYTE = KILOBYTE * 1024
+ GIGABYTE = MEGABYTE * 1024
+ TERABYTE = GIGABYTE * 1024
+ PETABYTE = TERABYTE * 1024
+ EXABYTE = PETABYTE * 1024
+
+ # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
+ #
+ # 2.bytes # => 2
+ def bytes
+ self
+ end
+ alias :byte :bytes
+
+ # Returns the number of bytes equivalent to the kilobytes provided.
+ #
+ # 2.kilobytes # => 2048
+ def kilobytes
+ self * KILOBYTE
+ end
+ alias :kilobyte :kilobytes
+
+ # Returns the number of bytes equivalent to the megabytes provided.
+ #
+ # 2.megabytes # => 2_097_152
+ def megabytes
+ self * MEGABYTE
+ end
+ alias :megabyte :megabytes
+
+ # Returns the number of bytes equivalent to the gigabytes provided.
+ #
+ # 2.gigabytes # => 2_147_483_648
+ def gigabytes
+ self * GIGABYTE
+ end
+ alias :gigabyte :gigabytes
+
+ # Returns the number of bytes equivalent to the terabytes provided.
+ #
+ # 2.terabytes # => 2_199_023_255_552
+ def terabytes
+ self * TERABYTE
+ end
+ alias :terabyte :terabytes
+
+ # Returns the number of bytes equivalent to the petabytes provided.
+ #
+ # 2.petabytes # => 2_251_799_813_685_248
+ def petabytes
+ self * PETABYTE
+ end
+ alias :petabyte :petabytes
+
+ # Returns the number of bytes equivalent to the exabytes provided.
+ #
+ # 2.exabytes # => 2_305_843_009_213_693_952
+ def exabytes
+ self * EXABYTE
+ end
+ alias :exabyte :exabytes
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/conversions.rb
new file mode 100644
index 0000000..f6c2713
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/conversions.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/big_decimal/conversions"
+require "active_support/number_helper"
+require "active_support/core_ext/module/deprecation"
+
+module ActiveSupport::NumericWithFormat
+ # Provides options for converting numbers into formatted strings.
+ # Options are provided for phone numbers, currency, percentage,
+ # precision, positional notation, file size and pretty printing.
+ #
+ # ==== Options
+ #
+ # For details on which formats use which options, see ActiveSupport::NumberHelper
+ #
+ # ==== Examples
+ #
+ # Phone Numbers:
+ # 5551234.to_s(:phone) # => "555-1234"
+ # 1235551234.to_s(:phone) # => "123-555-1234"
+ # 1235551234.to_s(:phone, area_code: true) # => "(123) 555-1234"
+ # 1235551234.to_s(:phone, delimiter: ' ') # => "123 555 1234"
+ # 1235551234.to_s(:phone, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
+ # 1235551234.to_s(:phone, country_code: 1) # => "+1-123-555-1234"
+ # 1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
+ # # => "+1.123.555.1234 x 1343"
+ #
+ # Currency:
+ # 1234567890.50.to_s(:currency) # => "$1,234,567,890.50"
+ # 1234567890.506.to_s(:currency) # => "$1,234,567,890.51"
+ # 1234567890.506.to_s(:currency, precision: 3) # => "$1,234,567,890.506"
+ # 1234567890.506.to_s(:currency, locale: :fr) # => "1 234 567 890,51 €"
+ # -1234567890.50.to_s(:currency, negative_format: '(%u%n)')
+ # # => "($1,234,567,890.50)"
+ # 1234567890.50.to_s(:currency, unit: '£', separator: ',', delimiter: '')
+ # # => "£1234567890,50"
+ # 1234567890.50.to_s(:currency, unit: '£', separator: ',', delimiter: '', format: '%n %u')
+ # # => "1234567890,50 £"
+ #
+ # Percentage:
+ # 100.to_s(:percentage) # => "100.000%"
+ # 100.to_s(:percentage, precision: 0) # => "100%"
+ # 1000.to_s(:percentage, delimiter: '.', separator: ',') # => "1.000,000%"
+ # 302.24398923423.to_s(:percentage, precision: 5) # => "302.24399%"
+ # 1000.to_s(:percentage, locale: :fr) # => "1 000,000%"
+ # 100.to_s(:percentage, format: '%n %') # => "100.000 %"
+ #
+ # Delimited:
+ # 12345678.to_s(:delimited) # => "12,345,678"
+ # 12345678.05.to_s(:delimited) # => "12,345,678.05"
+ # 12345678.to_s(:delimited, delimiter: '.') # => "12.345.678"
+ # 12345678.to_s(:delimited, delimiter: ',') # => "12,345,678"
+ # 12345678.05.to_s(:delimited, separator: ' ') # => "12,345,678 05"
+ # 12345678.05.to_s(:delimited, locale: :fr) # => "12 345 678,05"
+ # 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
+ # # => "98 765 432,98"
+ #
+ # Rounded:
+ # 111.2345.to_s(:rounded) # => "111.235"
+ # 111.2345.to_s(:rounded, precision: 2) # => "111.23"
+ # 13.to_s(:rounded, precision: 5) # => "13.00000"
+ # 389.32314.to_s(:rounded, precision: 0) # => "389"
+ # 111.2345.to_s(:rounded, significant: true) # => "111"
+ # 111.2345.to_s(:rounded, precision: 1, significant: true) # => "100"
+ # 13.to_s(:rounded, precision: 5, significant: true) # => "13.000"
+ # 111.234.to_s(:rounded, locale: :fr) # => "111,234"
+ # 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
+ # # => "13"
+ # 389.32314.to_s(:rounded, precision: 4, significant: true) # => "389.3"
+ # 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
+ # # => "1.111,23"
+ #
+ # Human-friendly size in Bytes:
+ # 123.to_s(:human_size) # => "123 Bytes"
+ # 1234.to_s(:human_size) # => "1.21 KB"
+ # 12345.to_s(:human_size) # => "12.1 KB"
+ # 1234567.to_s(:human_size) # => "1.18 MB"
+ # 1234567890.to_s(:human_size) # => "1.15 GB"
+ # 1234567890123.to_s(:human_size) # => "1.12 TB"
+ # 1234567890123456.to_s(:human_size) # => "1.1 PB"
+ # 1234567890123456789.to_s(:human_size) # => "1.07 EB"
+ # 1234567.to_s(:human_size, precision: 2) # => "1.2 MB"
+ # 483989.to_s(:human_size, precision: 2) # => "470 KB"
+ # 1234567.to_s(:human_size, precision: 2, separator: ',') # => "1,2 MB"
+ # 1234567890123.to_s(:human_size, precision: 5) # => "1.1228 TB"
+ # 524288000.to_s(:human_size, precision: 5) # => "500 MB"
+ #
+ # Human-friendly format:
+ # 123.to_s(:human) # => "123"
+ # 1234.to_s(:human) # => "1.23 Thousand"
+ # 12345.to_s(:human) # => "12.3 Thousand"
+ # 1234567.to_s(:human) # => "1.23 Million"
+ # 1234567890.to_s(:human) # => "1.23 Billion"
+ # 1234567890123.to_s(:human) # => "1.23 Trillion"
+ # 1234567890123456.to_s(:human) # => "1.23 Quadrillion"
+ # 1234567890123456789.to_s(:human) # => "1230 Quadrillion"
+ # 489939.to_s(:human, precision: 2) # => "490 Thousand"
+ # 489939.to_s(:human, precision: 4) # => "489.9 Thousand"
+ # 1234567.to_s(:human, precision: 4,
+ # significant: false) # => "1.2346 Million"
+ # 1234567.to_s(:human, precision: 1,
+ # separator: ',',
+ # significant: false) # => "1,2 Million"
+ def to_s(format = nil, options = nil)
+ case format
+ when nil
+ super()
+ when Integer, String
+ super(format)
+ when :phone
+ ActiveSupport::NumberHelper.number_to_phone(self, options || {})
+ when :currency
+ ActiveSupport::NumberHelper.number_to_currency(self, options || {})
+ when :percentage
+ ActiveSupport::NumberHelper.number_to_percentage(self, options || {})
+ when :delimited
+ ActiveSupport::NumberHelper.number_to_delimited(self, options || {})
+ when :rounded
+ ActiveSupport::NumberHelper.number_to_rounded(self, options || {})
+ when :human
+ ActiveSupport::NumberHelper.number_to_human(self, options || {})
+ when :human_size
+ ActiveSupport::NumberHelper.number_to_human_size(self, options || {})
+ when Symbol
+ super()
+ else
+ super(format)
+ end
+ end
+end
+
+# Ruby 2.4+ unifies Fixnum & Bignum into Integer.
+if 0.class == Integer
+ Integer.prepend ActiveSupport::NumericWithFormat
+else
+ Fixnum.prepend ActiveSupport::NumericWithFormat
+ Bignum.prepend ActiveSupport::NumericWithFormat
+end
+Float.prepend ActiveSupport::NumericWithFormat
+BigDecimal.prepend ActiveSupport::NumericWithFormat
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/inquiry.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/inquiry.rb
new file mode 100644
index 0000000..15334c9
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/inquiry.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+unless 1.respond_to?(:positive?) # TODO: Remove this file when we drop support to ruby < 2.3
+ class Numeric
+ # Returns true if the number is positive.
+ #
+ # 1.positive? # => true
+ # 0.positive? # => false
+ # -1.positive? # => false
+ def positive?
+ self > 0
+ end
+
+ # Returns true if the number is negative.
+ #
+ # -1.negative? # => true
+ # 0.negative? # => false
+ # 1.negative? # => false
+ def negative?
+ self < 0
+ end
+ end
+
+ class Complex
+ undef :positive?
+ undef :negative?
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/time.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/time.rb
new file mode 100644
index 0000000..bc4627f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/numeric/time.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require "active_support/duration"
+require "active_support/core_ext/time/calculations"
+require "active_support/core_ext/time/acts_like"
+require "active_support/core_ext/date/calculations"
+require "active_support/core_ext/date/acts_like"
+
+class Numeric
+ # Returns a Duration instance matching the number of seconds provided.
+ #
+ # 2.seconds # => 2 seconds
+ def seconds
+ ActiveSupport::Duration.seconds(self)
+ end
+ alias :second :seconds
+
+ # Returns a Duration instance matching the number of minutes provided.
+ #
+ # 2.minutes # => 2 minutes
+ def minutes
+ ActiveSupport::Duration.minutes(self)
+ end
+ alias :minute :minutes
+
+ # Returns a Duration instance matching the number of hours provided.
+ #
+ # 2.hours # => 2 hours
+ def hours
+ ActiveSupport::Duration.hours(self)
+ end
+ alias :hour :hours
+
+ # Returns a Duration instance matching the number of days provided.
+ #
+ # 2.days # => 2 days
+ def days
+ ActiveSupport::Duration.days(self)
+ end
+ alias :day :days
+
+ # Returns a Duration instance matching the number of weeks provided.
+ #
+ # 2.weeks # => 2 weeks
+ def weeks
+ ActiveSupport::Duration.weeks(self)
+ end
+ alias :week :weeks
+
+ # Returns a Duration instance matching the number of fortnights provided.
+ #
+ # 2.fortnights # => 4 weeks
+ def fortnights
+ ActiveSupport::Duration.weeks(self * 2)
+ end
+ alias :fortnight :fortnights
+
+ # Returns the number of milliseconds equivalent to the seconds provided.
+ # Used with the standard time durations.
+ #
+ # 2.in_milliseconds # => 2000
+ # 1.hour.in_milliseconds # => 3600000
+ def in_milliseconds
+ self * 1000
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object.rb
new file mode 100644
index 0000000..efd34cc
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/acts_like"
+require "active_support/core_ext/object/blank"
+require "active_support/core_ext/object/duplicable"
+require "active_support/core_ext/object/deep_dup"
+require "active_support/core_ext/object/try"
+require "active_support/core_ext/object/inclusion"
+
+require "active_support/core_ext/object/conversions"
+require "active_support/core_ext/object/instance_variables"
+
+require "active_support/core_ext/object/json"
+require "active_support/core_ext/object/to_param"
+require "active_support/core_ext/object/to_query"
+require "active_support/core_ext/object/with_options"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/acts_like.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/acts_like.rb
new file mode 100644
index 0000000..403ee20
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/acts_like.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class Object
+ # A duck-type assistant method. For example, Active Support extends Date
+ # to define an acts_like_date? method, and extends Time to define
+ # acts_like_time?. As a result, we can do x.acts_like?(:time) and
+ # x.acts_like?(:date) to do duck-type-safe comparisons, since classes that
+ # we want to act like Time simply need to define an acts_like_time? method.
+ def acts_like?(duck)
+ case duck
+ when :time
+ respond_to? :acts_like_time?
+ when :date
+ respond_to? :acts_like_date?
+ when :string
+ respond_to? :acts_like_string?
+ else
+ respond_to? :"acts_like_#{duck}?"
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/blank.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/blank.rb
new file mode 100644
index 0000000..2ca431a
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/blank.rb
@@ -0,0 +1,156 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/regexp"
+require "concurrent/map"
+
+class Object
+ # An object is blank if it's false, empty, or a whitespace string.
+ # For example, +false+, '', ' ', +nil+, [], and {} are all blank.
+ #
+ # This simplifies
+ #
+ # !address || address.empty?
+ #
+ # to
+ #
+ # address.blank?
+ #
+ # @return [true, false]
+ def blank?
+ respond_to?(:empty?) ? !!empty? : !self
+ end
+
+ # An object is present if it's not blank.
+ #
+ # @return [true, false]
+ def present?
+ !blank?
+ end
+
+ # Returns the receiver if it's present otherwise returns +nil+.
+ # object.presence is equivalent to
+ #
+ # object.present? ? object : nil
+ #
+ # For example, something like
+ #
+ # state = params[:state] if params[:state].present?
+ # country = params[:country] if params[:country].present?
+ # region = state || country || 'US'
+ #
+ # becomes
+ #
+ # region = params[:state].presence || params[:country].presence || 'US'
+ #
+ # @return [Object]
+ def presence
+ self if present?
+ end
+end
+
+class NilClass
+ # +nil+ is blank:
+ #
+ # nil.blank? # => true
+ #
+ # @return [true]
+ def blank?
+ true
+ end
+end
+
+class FalseClass
+ # +false+ is blank:
+ #
+ # false.blank? # => true
+ #
+ # @return [true]
+ def blank?
+ true
+ end
+end
+
+class TrueClass
+ # +true+ is not blank:
+ #
+ # true.blank? # => false
+ #
+ # @return [false]
+ def blank?
+ false
+ end
+end
+
+class Array
+ # An array is blank if it's empty:
+ #
+ # [].blank? # => true
+ # [1,2,3].blank? # => false
+ #
+ # @return [true, false]
+ alias_method :blank?, :empty?
+end
+
+class Hash
+ # A hash is blank if it's empty:
+ #
+ # {}.blank? # => true
+ # { key: 'value' }.blank? # => false
+ #
+ # @return [true, false]
+ alias_method :blank?, :empty?
+end
+
+class String
+ BLANK_RE = /\A[[:space:]]*\z/
+ ENCODED_BLANKS = Concurrent::Map.new do |h, enc|
+ h[enc] = Regexp.new(BLANK_RE.source.encode(enc), BLANK_RE.options | Regexp::FIXEDENCODING)
+ end
+
+ # A string is blank if it's empty or contains whitespaces only:
+ #
+ # ''.blank? # => true
+ # ' '.blank? # => true
+ # "\t\n\r".blank? # => true
+ # ' blah '.blank? # => false
+ #
+ # Unicode whitespace is supported:
+ #
+ # "\u00a0".blank? # => true
+ #
+ # @return [true, false]
+ def blank?
+ # The regexp that matches blank strings is expensive. For the case of empty
+ # strings we can speed up this method (~3.5x) with an empty? call. The
+ # penalty for the rest of strings is marginal.
+ empty? ||
+ begin
+ BLANK_RE.match?(self)
+ rescue Encoding::CompatibilityError
+ ENCODED_BLANKS[self.encoding].match?(self)
+ end
+ end
+end
+
+class Numeric #:nodoc:
+ # No number is blank:
+ #
+ # 1.blank? # => false
+ # 0.blank? # => false
+ #
+ # @return [false]
+ def blank?
+ false
+ end
+end
+
+class Time #:nodoc:
+ # No Time is blank:
+ #
+ # Time.now.blank? # => false
+ #
+ # @return [false]
+ def blank?
+ false
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/conversions.rb
new file mode 100644
index 0000000..624fb8d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/conversions.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/to_param"
+require "active_support/core_ext/object/to_query"
+require "active_support/core_ext/array/conversions"
+require "active_support/core_ext/hash/conversions"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/deep_dup.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/deep_dup.rb
new file mode 100644
index 0000000..c66c5eb
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/deep_dup.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/duplicable"
+
+class Object
+ # Returns a deep copy of object if it's duplicable. If it's
+ # not duplicable, returns +self+.
+ #
+ # object = Object.new
+ # dup = object.deep_dup
+ # dup.instance_variable_set(:@a, 1)
+ #
+ # object.instance_variable_defined?(:@a) # => false
+ # dup.instance_variable_defined?(:@a) # => true
+ def deep_dup
+ duplicable? ? dup : self
+ end
+end
+
+class Array
+ # Returns a deep copy of array.
+ #
+ # array = [1, [2, 3]]
+ # dup = array.deep_dup
+ # dup[1][2] = 4
+ #
+ # array[1][2] # => nil
+ # dup[1][2] # => 4
+ def deep_dup
+ map(&:deep_dup)
+ end
+end
+
+class Hash
+ # Returns a deep copy of hash.
+ #
+ # hash = { a: { b: 'b' } }
+ # dup = hash.deep_dup
+ # dup[:a][:c] = 'c'
+ #
+ # hash[:a][:c] # => nil
+ # dup[:a][:c] # => "c"
+ def deep_dup
+ hash = dup
+ each_pair do |key, value|
+ if key.frozen? && ::String === key
+ hash[key] = value.deep_dup
+ else
+ hash.delete(key)
+ hash[key.deep_dup] = value.deep_dup
+ end
+ end
+ hash
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/duplicable.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/duplicable.rb
new file mode 100644
index 0000000..9bb9908
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/duplicable.rb
@@ -0,0 +1,156 @@
+# frozen_string_literal: true
+
+#--
+# Most objects are cloneable, but not all. For example you can't dup methods:
+#
+# method(:puts).dup # => TypeError: allocator undefined for Method
+#
+# Classes may signal their instances are not duplicable removing +dup+/+clone+
+# or raising exceptions from them. So, to dup an arbitrary object you normally
+# use an optimistic approach and are ready to catch an exception, say:
+#
+# arbitrary_object.dup rescue object
+#
+# Rails dups objects in a few critical spots where they are not that arbitrary.
+# That rescue is very expensive (like 40 times slower than a predicate), and it
+# is often triggered.
+#
+# That's why we hardcode the following cases and check duplicable? instead of
+# using that rescue idiom.
+#++
+class Object
+ # Can you safely dup this object?
+ #
+ # False for method objects;
+ # true otherwise.
+ def duplicable?
+ true
+ end
+end
+
+class NilClass
+ begin
+ nil.dup
+ rescue TypeError
+
+ # +nil+ is not duplicable:
+ #
+ # nil.duplicable? # => false
+ # nil.dup # => TypeError: can't dup NilClass
+ def duplicable?
+ false
+ end
+ end
+end
+
+class FalseClass
+ begin
+ false.dup
+ rescue TypeError
+
+ # +false+ is not duplicable:
+ #
+ # false.duplicable? # => false
+ # false.dup # => TypeError: can't dup FalseClass
+ def duplicable?
+ false
+ end
+ end
+end
+
+class TrueClass
+ begin
+ true.dup
+ rescue TypeError
+
+ # +true+ is not duplicable:
+ #
+ # true.duplicable? # => false
+ # true.dup # => TypeError: can't dup TrueClass
+ def duplicable?
+ false
+ end
+ end
+end
+
+class Symbol
+ begin
+ :symbol.dup # Ruby 2.4.x.
+ "symbol_from_string".to_sym.dup # Some symbols can't `dup` in Ruby 2.4.0.
+ rescue TypeError
+
+ # Symbols are not duplicable:
+ #
+ # :my_symbol.duplicable? # => false
+ # :my_symbol.dup # => TypeError: can't dup Symbol
+ def duplicable?
+ false
+ end
+ end
+end
+
+class Numeric
+ begin
+ 1.dup
+ rescue TypeError
+
+ # Numbers are not duplicable:
+ #
+ # 3.duplicable? # => false
+ # 3.dup # => TypeError: can't dup Integer
+ def duplicable?
+ false
+ end
+ end
+end
+
+require "bigdecimal"
+class BigDecimal
+ # BigDecimals are duplicable:
+ #
+ # BigDecimal("1.2").duplicable? # => true
+ # BigDecimal("1.2").dup # => #
+ def duplicable?
+ true
+ end
+end
+
+class Method
+ # Methods are not duplicable:
+ #
+ # method(:puts).duplicable? # => false
+ # method(:puts).dup # => TypeError: allocator undefined for Method
+ def duplicable?
+ false
+ end
+end
+
+class Complex
+ begin
+ Complex(1).dup
+ rescue TypeError
+
+ # Complexes are not duplicable:
+ #
+ # Complex(1).duplicable? # => false
+ # Complex(1).dup # => TypeError: can't copy Complex
+ def duplicable?
+ false
+ end
+ end
+end
+
+class Rational
+ begin
+ Rational(1).dup
+ rescue TypeError
+
+ # Rationals are not duplicable:
+ #
+ # Rational(1).duplicable? # => false
+ # Rational(1).dup # => TypeError: can't copy Rational
+ def duplicable?
+ false
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/inclusion.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/inclusion.rb
new file mode 100644
index 0000000..6064e92
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/inclusion.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class Object
+ # Returns true if this object is included in the argument. Argument must be
+ # any object which responds to +#include?+. Usage:
+ #
+ # characters = ["Konata", "Kagami", "Tsukasa"]
+ # "Konata".in?(characters) # => true
+ #
+ # This will throw an +ArgumentError+ if the argument doesn't respond
+ # to +#include?+.
+ def in?(another_object)
+ another_object.include?(self)
+ rescue NoMethodError
+ raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
+ end
+
+ # Returns the receiver if it's included in the argument otherwise returns +nil+.
+ # Argument must be any object which responds to +#include?+. Usage:
+ #
+ # params[:bucket_type].presence_in %w( project calendar )
+ #
+ # This will throw an +ArgumentError+ if the argument doesn't respond to +#include?+.
+ #
+ # @return [Object]
+ def presence_in(another_object)
+ in?(another_object) ? self : nil
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/instance_variables.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/instance_variables.rb
new file mode 100644
index 0000000..12fdf84
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/instance_variables.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class Object
+ # Returns a hash with string keys that maps instance variable names without "@" to their
+ # corresponding values.
+ #
+ # class C
+ # def initialize(x, y)
+ # @x, @y = x, y
+ # end
+ # end
+ #
+ # C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
+ def instance_values
+ Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
+ end
+
+ # Returns an array of instance variable names as strings including "@".
+ #
+ # class C
+ # def initialize(x, y)
+ # @x, @y = x, y
+ # end
+ # end
+ #
+ # C.new(0, 1).instance_variable_names # => ["@y", "@x"]
+ def instance_variable_names
+ instance_variables.map(&:to_s)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/json.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/json.rb
new file mode 100644
index 0000000..f7c623f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/json.rb
@@ -0,0 +1,227 @@
+# frozen_string_literal: true
+
+# Hack to load json gem first so we can overwrite its to_json.
+require "json"
+require "bigdecimal"
+require "uri/generic"
+require "pathname"
+require "active_support/core_ext/big_decimal/conversions" # for #to_s
+require "active_support/core_ext/hash/except"
+require "active_support/core_ext/hash/slice"
+require "active_support/core_ext/object/instance_variables"
+require "time"
+require "active_support/core_ext/time/conversions"
+require "active_support/core_ext/date_time/conversions"
+require "active_support/core_ext/date/conversions"
+
+# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
+# their default behavior. That said, we need to define the basic to_json method in all of them,
+# otherwise they will always use to_json gem implementation, which is backwards incompatible in
+# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
+# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
+#
+# On the other hand, we should avoid conflict with ::JSON.{generate,dump}(obj). Unfortunately, the
+# JSON gem's encoder relies on its own to_json implementation to encode objects. Since it always
+# passes a ::JSON::State object as the only argument to to_json, we can detect that and forward the
+# calls to the original to_json method.
+#
+# It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
+# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
+# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
+# should give exactly the same results with or without active support.
+
+module ActiveSupport
+ module ToJsonWithActiveSupportEncoder # :nodoc:
+ def to_json(options = nil)
+ if options.is_a?(::JSON::State)
+ # Called from JSON.{generate,dump}, forward it to JSON gem's to_json
+ super(options)
+ else
+ # to_json is being invoked directly, use ActiveSupport's encoder
+ ActiveSupport::JSON.encode(self, options)
+ end
+ end
+ end
+end
+
+[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass, Enumerable].reverse_each do |klass|
+ klass.prepend(ActiveSupport::ToJsonWithActiveSupportEncoder)
+end
+
+class Object
+ def as_json(options = nil) #:nodoc:
+ if respond_to?(:to_hash)
+ to_hash.as_json(options)
+ else
+ instance_values.as_json(options)
+ end
+ end
+end
+
+class Struct #:nodoc:
+ def as_json(options = nil)
+ Hash[members.zip(values)].as_json(options)
+ end
+end
+
+class TrueClass
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+end
+
+class FalseClass
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+end
+
+class NilClass
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+end
+
+class String
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+end
+
+class Symbol
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
+end
+
+class Numeric
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+end
+
+class Float
+ # Encoding Infinity or NaN to JSON should return "null". The default returns
+ # "Infinity" or "NaN" which are not valid JSON.
+ def as_json(options = nil) #:nodoc:
+ finite? ? self : nil
+ end
+end
+
+class BigDecimal
+ # A BigDecimal would be naturally represented as a JSON number. Most libraries,
+ # however, parse non-integer JSON numbers directly as floats. Clients using
+ # those libraries would get in general a wrong number and no way to recover
+ # other than manually inspecting the string with the JSON code itself.
+ #
+ # That's why a JSON string is returned. The JSON literal is not numeric, but
+ # if the other end knows by contract that the data is supposed to be a
+ # BigDecimal, it still has the chance to post-process the string and get the
+ # real value.
+ def as_json(options = nil) #:nodoc:
+ finite? ? to_s : nil
+ end
+end
+
+class Regexp
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
+end
+
+module Enumerable
+ def as_json(options = nil) #:nodoc:
+ to_a.as_json(options)
+ end
+end
+
+class IO
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
+end
+
+class Range
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
+end
+
+class Array
+ def as_json(options = nil) #:nodoc:
+ map { |v| options ? v.as_json(options.dup) : v.as_json }
+ end
+end
+
+class Hash
+ def as_json(options = nil) #:nodoc:
+ # create a subset of the hash by applying :only or :except
+ subset = if options
+ if attrs = options[:only]
+ slice(*Array(attrs))
+ elsif attrs = options[:except]
+ except(*Array(attrs))
+ else
+ self
+ end
+ else
+ self
+ end
+
+ Hash[subset.map { |k, v| [k.to_s, options ? v.as_json(options.dup) : v.as_json] }]
+ end
+end
+
+class Time
+ def as_json(options = nil) #:nodoc:
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ xmlschema(ActiveSupport::JSON::Encoding.time_precision)
+ else
+ %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
+ end
+ end
+end
+
+class Date
+ def as_json(options = nil) #:nodoc:
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ strftime("%Y-%m-%d")
+ else
+ strftime("%Y/%m/%d")
+ end
+ end
+end
+
+class DateTime
+ def as_json(options = nil) #:nodoc:
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ xmlschema(ActiveSupport::JSON::Encoding.time_precision)
+ else
+ strftime("%Y/%m/%d %H:%M:%S %z")
+ end
+ end
+end
+
+class URI::Generic #:nodoc:
+ def as_json(options = nil)
+ to_s
+ end
+end
+
+class Pathname #:nodoc:
+ def as_json(options = nil)
+ to_s
+ end
+end
+
+class Process::Status #:nodoc:
+ def as_json(options = nil)
+ { exitstatus: exitstatus, pid: pid }
+ end
+end
+
+class Exception
+ def as_json(options = nil)
+ to_s
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/to_param.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/to_param.rb
new file mode 100644
index 0000000..6d2bdd7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/to_param.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/to_query"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/to_query.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/to_query.rb
new file mode 100644
index 0000000..bac6ff9
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/to_query.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require "cgi"
+
+class Object
+ # Alias of to_s.
+ def to_param
+ to_s
+ end
+
+ # Converts an object into a string suitable for use as a URL query string,
+ # using the given key as the param name.
+ def to_query(key)
+ "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
+ end
+end
+
+class NilClass
+ # Returns +self+.
+ def to_param
+ self
+ end
+end
+
+class TrueClass
+ # Returns +self+.
+ def to_param
+ self
+ end
+end
+
+class FalseClass
+ # Returns +self+.
+ def to_param
+ self
+ end
+end
+
+class Array
+ # Calls to_param on all its elements and joins the result with
+ # slashes. This is used by url_for in Action Pack.
+ def to_param
+ collect(&:to_param).join "/"
+ end
+
+ # Converts an array into a string suitable for use as a URL query string,
+ # using the given +key+ as the param name.
+ #
+ # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
+ def to_query(key)
+ prefix = "#{key}[]"
+
+ if empty?
+ nil.to_query(prefix)
+ else
+ collect { |value| value.to_query(prefix) }.join "&"
+ end
+ end
+end
+
+class Hash
+ # Returns a string representation of the receiver suitable for use as a URL
+ # query string:
+ #
+ # {name: 'David', nationality: 'Danish'}.to_query
+ # # => "name=David&nationality=Danish"
+ #
+ # An optional namespace can be passed to enclose key names:
+ #
+ # {name: 'David', nationality: 'Danish'}.to_query('user')
+ # # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
+ #
+ # The string pairs "key=value" that conform the query string
+ # are sorted lexicographically in ascending order.
+ #
+ # This method is also aliased as +to_param+.
+ def to_query(namespace = nil)
+ query = collect do |key, value|
+ unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
+ value.to_query(namespace ? "#{namespace}[#{key}]" : key)
+ end
+ end.compact
+
+ query.sort! unless namespace.to_s.include?("[]")
+ query.join("&")
+ end
+
+ alias_method :to_param, :to_query
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/try.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/try.rb
new file mode 100644
index 0000000..c874691
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/try.rb
@@ -0,0 +1,148 @@
+# frozen_string_literal: true
+
+require "delegate"
+
+module ActiveSupport
+ module Tryable #:nodoc:
+ def try(*a, &b)
+ try!(*a, &b) if a.empty? || respond_to?(a.first)
+ end
+
+ def try!(*a, &b)
+ if a.empty? && block_given?
+ if b.arity == 0
+ instance_eval(&b)
+ else
+ yield self
+ end
+ else
+ public_send(*a, &b)
+ end
+ end
+ end
+end
+
+class Object
+ include ActiveSupport::Tryable
+
+ ##
+ # :method: try
+ #
+ # :call-seq:
+ # try(*a, &b)
+ #
+ # Invokes the public method whose name goes as first argument just like
+ # +public_send+ does, except that if the receiver does not respond to it the
+ # call returns +nil+ rather than raising an exception.
+ #
+ # This method is defined to be able to write
+ #
+ # @person.try(:name)
+ #
+ # instead of
+ #
+ # @person.name if @person
+ #
+ # +try+ calls can be chained:
+ #
+ # @person.try(:spouse).try(:name)
+ #
+ # instead of
+ #
+ # @person.spouse.name if @person && @person.spouse
+ #
+ # +try+ will also return +nil+ if the receiver does not respond to the method:
+ #
+ # @person.try(:non_existing_method) # => nil
+ #
+ # instead of
+ #
+ # @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil
+ #
+ # +try+ returns +nil+ when called on +nil+ regardless of whether it responds
+ # to the method:
+ #
+ # nil.try(:to_i) # => nil, rather than 0
+ #
+ # Arguments and blocks are forwarded to the method if invoked:
+ #
+ # @posts.try(:each_slice, 2) do |a, b|
+ # ...
+ # end
+ #
+ # The number of arguments in the signature must match. If the object responds
+ # to the method the call is attempted and +ArgumentError+ is still raised
+ # in case of argument mismatch.
+ #
+ # If +try+ is called without arguments it yields the receiver to a given
+ # block unless it is +nil+:
+ #
+ # @person.try do |p|
+ # ...
+ # end
+ #
+ # You can also call try with a block without accepting an argument, and the block
+ # will be instance_eval'ed instead:
+ #
+ # @person.try { upcase.truncate(50) }
+ #
+ # Please also note that +try+ is defined on +Object+. Therefore, it won't work
+ # with instances of classes that do not have +Object+ among their ancestors,
+ # like direct subclasses of +BasicObject+.
+
+ ##
+ # :method: try!
+ #
+ # :call-seq:
+ # try!(*a, &b)
+ #
+ # Same as #try, but raises a +NoMethodError+ exception if the receiver is
+ # not +nil+ and does not implement the tried method.
+ #
+ # "a".try!(:upcase) # => "A"
+ # nil.try!(:upcase) # => nil
+ # 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Integer
+end
+
+class Delegator
+ include ActiveSupport::Tryable
+
+ ##
+ # :method: try
+ #
+ # :call-seq:
+ # try(a*, &b)
+ #
+ # See Object#try
+
+ ##
+ # :method: try!
+ #
+ # :call-seq:
+ # try!(a*, &b)
+ #
+ # See Object#try!
+end
+
+class NilClass
+ # Calling +try+ on +nil+ always returns +nil+.
+ # It becomes especially helpful when navigating through associations that may return +nil+.
+ #
+ # nil.try(:name) # => nil
+ #
+ # Without +try+
+ # @person && @person.children.any? && @person.children.first.name
+ #
+ # With +try+
+ # @person.try(:children).try(:first).try(:name)
+ def try(*args)
+ nil
+ end
+
+ # Calling +try!+ on +nil+ always returns +nil+.
+ #
+ # nil.try!(:name) # => nil
+ def try!(*args)
+ nil
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/with_options.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/with_options.rb
new file mode 100644
index 0000000..2838fd7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/object/with_options.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require "active_support/option_merger"
+
+class Object
+ # An elegant way to factor duplication out of options passed to a series of
+ # method calls. Each method called in the block, with the block variable as
+ # the receiver, will have its options merged with the default +options+ hash
+ # provided. Each method called on the block variable must take an options
+ # hash as its final argument.
+ #
+ # Without with_options, this code contains duplication:
+ #
+ # class Account < ActiveRecord::Base
+ # has_many :customers, dependent: :destroy
+ # has_many :products, dependent: :destroy
+ # has_many :invoices, dependent: :destroy
+ # has_many :expenses, dependent: :destroy
+ # end
+ #
+ # Using with_options, we can remove the duplication:
+ #
+ # class Account < ActiveRecord::Base
+ # with_options dependent: :destroy do |assoc|
+ # assoc.has_many :customers
+ # assoc.has_many :products
+ # assoc.has_many :invoices
+ # assoc.has_many :expenses
+ # end
+ # end
+ #
+ # It can also be used with an explicit receiver:
+ #
+ # I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n|
+ # subject i18n.t :subject
+ # body i18n.t :body, user_name: user.name
+ # end
+ #
+ # When you don't pass an explicit receiver, it executes the whole block
+ # in merging options context:
+ #
+ # class Account < ActiveRecord::Base
+ # with_options dependent: :destroy do
+ # has_many :customers
+ # has_many :products
+ # has_many :invoices
+ # has_many :expenses
+ # end
+ # end
+ #
+ # with_options can also be nested since the call is forwarded to its receiver.
+ #
+ # NOTE: Each nesting level will merge inherited defaults in addition to their own.
+ #
+ # class Post < ActiveRecord::Base
+ # with_options if: :persisted?, length: { minimum: 50 } do
+ # validates :content, if: -> { content.present? }
+ # end
+ # end
+ #
+ # The code is equivalent to:
+ #
+ # validates :content, length: { minimum: 50 }, if: -> { content.present? }
+ #
+ # Hence the inherited default for +if+ key is ignored.
+ #
+ # NOTE: You cannot call class methods implicitly inside of with_options.
+ # You can access these methods using the class name instead:
+ #
+ # class Phone < ActiveRecord::Base
+ # enum phone_number_type: [home: 0, office: 1, mobile: 2]
+ #
+ # with_options presence: true do
+ # validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
+ # end
+ # end
+ #
+ def with_options(options, &block)
+ option_merger = ActiveSupport::OptionMerger.new(self, options)
+ block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range.rb
new file mode 100644
index 0000000..78814fd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/range/conversions"
+require "active_support/core_ext/range/compare_range"
+require "active_support/core_ext/range/include_time_with_zone"
+require "active_support/core_ext/range/overlaps"
+require "active_support/core_ext/range/each"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/compare_range.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/compare_range.rb
new file mode 100644
index 0000000..704041f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/compare_range.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module CompareWithRange #:nodoc:
+ # Extends the default Range#=== to support range comparisons.
+ # (1..5) === (1..5) # => true
+ # (1..5) === (2..3) # => true
+ # (1..5) === (2..6) # => false
+ #
+ # The native Range#=== behavior is untouched.
+ # ('a'..'f') === ('c') # => true
+ # (5..9) === (11) # => false
+ def ===(value)
+ if value.is_a?(::Range)
+ # 1...10 includes 1..9 but it does not include 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.send(operator, last)
+ else
+ super
+ end
+ end
+
+ # Extends the default Range#include? to support range comparisons.
+ # (1..5).include?(1..5) # => true
+ # (1..5).include?(2..3) # => true
+ # (1..5).include?(2..6) # => false
+ #
+ # The native Range#include? behavior is untouched.
+ # ('a'..'f').include?('c') # => true
+ # (5..9).include?(11) # => false
+ def include?(value)
+ if value.is_a?(::Range)
+ # 1...10 includes 1..9 but it does not include 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.send(operator, last)
+ else
+ super
+ end
+ end
+
+ # Extends the default Range#cover? to support range comparisons.
+ # (1..5).cover?(1..5) # => true
+ # (1..5).cover?(2..3) # => true
+ # (1..5).cover?(2..6) # => false
+ #
+ # The native Range#cover? behavior is untouched.
+ # ('a'..'f').cover?('c') # => true
+ # (5..9).cover?(11) # => false
+ def cover?(value)
+ if value.is_a?(::Range)
+ # 1...10 covers 1..9 but it does not cover 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.send(operator, last)
+ else
+ super
+ end
+ end
+ end
+end
+
+Range.prepend(ActiveSupport::CompareWithRange)
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/conversions.rb
new file mode 100644
index 0000000..8832fbc
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/conversions.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module ActiveSupport::RangeWithFormat
+ RANGE_FORMATS = {
+ db: -> (start, stop) do
+ case start
+ when String then "BETWEEN '#{start}' AND '#{stop}'"
+ else
+ "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'"
+ end
+ end
+ }
+
+ # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
+ #
+ # range = (1..100) # => 1..100
+ #
+ # range.to_s # => "1..100"
+ # range.to_s(:db) # => "BETWEEN '1' AND '100'"
+ #
+ # == Adding your own range formats to to_s
+ # You can add your own formats to the Range::RANGE_FORMATS hash.
+ # Use the format name as the hash key and a Proc instance.
+ #
+ # # config/initializers/range_formats.rb
+ # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
+ def to_s(format = :default)
+ if formatter = RANGE_FORMATS[format]
+ formatter.call(first, last)
+ else
+ super()
+ end
+ end
+
+ alias_method :to_default_s, :to_s
+ alias_method :to_formatted_s, :to_s
+end
+
+Range.prepend(ActiveSupport::RangeWithFormat)
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/each.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/each.rb
new file mode 100644
index 0000000..2f22cd0
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/each.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require "active_support/time_with_zone"
+
+module ActiveSupport
+ module EachTimeWithZone #:nodoc:
+ def each(&block)
+ ensure_iteration_allowed
+ super
+ end
+
+ def step(n = 1, &block)
+ ensure_iteration_allowed
+ super
+ end
+
+ private
+
+ def ensure_iteration_allowed
+ raise TypeError, "can't iterate from #{first.class}" if first.is_a?(TimeWithZone)
+ end
+ end
+end
+
+Range.prepend(ActiveSupport::EachTimeWithZone)
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/include_range.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/include_range.rb
new file mode 100644
index 0000000..4812e27
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/include_range.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/range/compare_range"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/include_time_with_zone.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/include_time_with_zone.rb
new file mode 100644
index 0000000..5f80acf
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/include_time_with_zone.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require "active_support/time_with_zone"
+
+module ActiveSupport
+ module IncludeTimeWithZone #:nodoc:
+ # Extends the default Range#include? to support ActiveSupport::TimeWithZone.
+ #
+ # (1.hour.ago..1.hour.from_now).include?(Time.current) # => true
+ #
+ def include?(value)
+ if first.is_a?(TimeWithZone)
+ cover?(value)
+ elsif last.is_a?(TimeWithZone)
+ cover?(value)
+ else
+ super
+ end
+ end
+ end
+end
+
+Range.prepend(ActiveSupport::IncludeTimeWithZone)
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/overlaps.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/overlaps.rb
new file mode 100644
index 0000000..f753607
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/range/overlaps.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class Range
+ # Compare two ranges and see if they overlap each other
+ # (1..5).overlaps?(4..6) # => true
+ # (1..5).overlaps?(7..9) # => false
+ def overlaps?(other)
+ cover?(other.first) || other.cover?(first)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/regexp.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/regexp.rb
new file mode 100644
index 0000000..efbd708
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/regexp.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class Regexp #:nodoc:
+ def multiline?
+ options & MULTILINE == MULTILINE
+ end
+
+ def match?(string, pos = 0)
+ !!match(string, pos)
+ end unless //.respond_to?(:match?)
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/securerandom.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/securerandom.rb
new file mode 100644
index 0000000..b4a491f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/securerandom.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require "securerandom"
+
+module SecureRandom
+ BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"]
+ # SecureRandom.base58 generates a random base58 string.
+ #
+ # The argument _n_ specifies the length, of the random string to be generated.
+ #
+ # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
+ #
+ # The result may contain alphanumeric characters except 0, O, I and l
+ #
+ # p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
+ # p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
+ #
+ def self.base58(n = 16)
+ SecureRandom.random_bytes(n).unpack("C*").map do |byte|
+ idx = byte % 64
+ idx = SecureRandom.random_number(58) if idx >= 58
+ BASE58_ALPHABET[idx]
+ end.join
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string.rb
new file mode 100644
index 0000000..757d15c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/string/conversions"
+require "active_support/core_ext/string/filters"
+require "active_support/core_ext/string/multibyte"
+require "active_support/core_ext/string/starts_ends_with"
+require "active_support/core_ext/string/inflections"
+require "active_support/core_ext/string/access"
+require "active_support/core_ext/string/behavior"
+require "active_support/core_ext/string/output_safety"
+require "active_support/core_ext/string/exclude"
+require "active_support/core_ext/string/strip"
+require "active_support/core_ext/string/inquiry"
+require "active_support/core_ext/string/indent"
+require "active_support/core_ext/string/zones"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/access.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/access.rb
new file mode 100644
index 0000000..58591bb
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/access.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+class String
+ # If you pass a single integer, returns a substring of one character at that
+ # position. The first character of the string is at position 0, the next at
+ # position 1, and so on. If a range is supplied, a substring containing
+ # characters at offsets given by the range is returned. In both cases, if an
+ # offset is negative, it is counted from the end of the string. Returns +nil+
+ # if the initial offset falls outside the string. Returns an empty string if
+ # the beginning of the range is greater than the end of the string.
+ #
+ # str = "hello"
+ # str.at(0) # => "h"
+ # str.at(1..3) # => "ell"
+ # str.at(-2) # => "l"
+ # str.at(-2..-1) # => "lo"
+ # str.at(5) # => nil
+ # str.at(5..-1) # => ""
+ #
+ # If a Regexp is given, the matching portion of the string is returned.
+ # If a String is given, that given string is returned if it occurs in
+ # the string. In both cases, +nil+ is returned if there is no match.
+ #
+ # str = "hello"
+ # str.at(/lo/) # => "lo"
+ # str.at(/ol/) # => nil
+ # str.at("lo") # => "lo"
+ # str.at("ol") # => nil
+ def at(position)
+ self[position]
+ end
+
+ # Returns a substring from the given position to the end of the string.
+ # If the position is negative, it is counted from the end of the string.
+ #
+ # str = "hello"
+ # str.from(0) # => "hello"
+ # str.from(3) # => "lo"
+ # str.from(-2) # => "lo"
+ #
+ # You can mix it with +to+ method and do fun things like:
+ #
+ # str = "hello"
+ # str.from(0).to(-1) # => "hello"
+ # str.from(1).to(-2) # => "ell"
+ def from(position)
+ self[position..-1]
+ end
+
+ # Returns a substring from the beginning of the string to the given position.
+ # If the position is negative, it is counted from the end of the string.
+ #
+ # str = "hello"
+ # str.to(0) # => "h"
+ # str.to(3) # => "hell"
+ # str.to(-2) # => "hell"
+ #
+ # You can mix it with +from+ method and do fun things like:
+ #
+ # str = "hello"
+ # str.from(0).to(-1) # => "hello"
+ # str.from(1).to(-2) # => "ell"
+ def to(position)
+ self[0..position]
+ end
+
+ # Returns the first character. If a limit is supplied, returns a substring
+ # from the beginning of the string until it reaches the limit value. If the
+ # given limit is greater than or equal to the string length, returns a copy of self.
+ #
+ # str = "hello"
+ # str.first # => "h"
+ # str.first(1) # => "h"
+ # str.first(2) # => "he"
+ # str.first(0) # => ""
+ # str.first(6) # => "hello"
+ def first(limit = 1)
+ if limit == 0
+ ""
+ elsif limit >= size
+ dup
+ else
+ to(limit - 1)
+ end
+ end
+
+ # Returns the last character of the string. If a limit is supplied, returns a substring
+ # from the end of the string until it reaches the limit value (counting backwards). If
+ # the given limit is greater than or equal to the string length, returns a copy of self.
+ #
+ # str = "hello"
+ # str.last # => "o"
+ # str.last(1) # => "o"
+ # str.last(2) # => "lo"
+ # str.last(0) # => ""
+ # str.last(6) # => "hello"
+ def last(limit = 1)
+ if limit == 0
+ ""
+ elsif limit >= size
+ dup
+ else
+ from(-limit)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/behavior.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/behavior.rb
new file mode 100644
index 0000000..35a5aa7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/behavior.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class String
+ # Enables more predictable duck-typing on String-like classes. See Object#acts_like?.
+ def acts_like_string?
+ true
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/conversions.rb
new file mode 100644
index 0000000..29a88b0
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/conversions.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require "date"
+require "active_support/core_ext/time/calculations"
+
+class String
+ # Converts a string to a Time value.
+ # The +form+ can be either :utc or :local (default :local).
+ #
+ # The time is parsed using Time.parse method.
+ # If +form+ is :local, then the time is in the system timezone.
+ # If the date part is missing then the current date is used and if
+ # the time part is missing then it is assumed to be 00:00:00.
+ #
+ # "13-12-2012".to_time # => 2012-12-13 00:00:00 +0100
+ # "06:12".to_time # => 2012-12-13 06:12:00 +0100
+ # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100
+ # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
+ # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC
+ # "12/13/2012".to_time # => ArgumentError: argument out of range
+ def to_time(form = :local)
+ parts = Date._parse(self, false)
+ used_keys = %i(year mon mday hour min sec sec_fraction offset)
+ return if (parts.keys & used_keys).empty?
+
+ now = Time.now
+ time = Time.new(
+ parts.fetch(:year, now.year),
+ parts.fetch(:mon, now.month),
+ parts.fetch(:mday, now.day),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset, form == :utc ? 0 : nil)
+ )
+
+ form == :utc ? time.utc : time.to_time
+ end
+
+ # Converts a string to a Date value.
+ #
+ # "1-1-2012".to_date # => Sun, 01 Jan 2012
+ # "01/01/2012".to_date # => Sun, 01 Jan 2012
+ # "2012-12-13".to_date # => Thu, 13 Dec 2012
+ # "12/13/2012".to_date # => ArgumentError: invalid date
+ def to_date
+ ::Date.parse(self, false) unless blank?
+ end
+
+ # Converts a string to a DateTime value.
+ #
+ # "1-1-2012".to_datetime # => Sun, 01 Jan 2012 00:00:00 +0000
+ # "01/01/2012 23:59:59".to_datetime # => Sun, 01 Jan 2012 23:59:59 +0000
+ # "2012-12-13 12:50".to_datetime # => Thu, 13 Dec 2012 12:50:00 +0000
+ # "12/13/2012".to_datetime # => ArgumentError: invalid date
+ def to_datetime
+ ::DateTime.parse(self, false) unless blank?
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/exclude.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/exclude.rb
new file mode 100644
index 0000000..8e46268
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/exclude.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class String
+ # The inverse of String#include?. Returns true if the string
+ # does not include the other string.
+ #
+ # "hello".exclude? "lo" # => false
+ # "hello".exclude? "ol" # => true
+ # "hello".exclude? ?h # => false
+ def exclude?(string)
+ !include?(string)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/filters.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/filters.rb
new file mode 100644
index 0000000..66e721e
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/filters.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+class String
+ # Returns the string, first removing all whitespace on both ends of
+ # the string, and then changing remaining consecutive whitespace
+ # groups into one space each.
+ #
+ # Note that it handles both ASCII and Unicode whitespace.
+ #
+ # %{ Multi-line
+ # string }.squish # => "Multi-line string"
+ # " foo bar \n \t boo".squish # => "foo bar boo"
+ def squish
+ dup.squish!
+ end
+
+ # Performs a destructive squish. See String#squish.
+ # str = " foo bar \n \t boo"
+ # str.squish! # => "foo bar boo"
+ # str # => "foo bar boo"
+ def squish!
+ gsub!(/[[:space:]]+/, " ")
+ strip!
+ self
+ end
+
+ # Returns a new string with all occurrences of the patterns removed.
+ # str = "foo bar test"
+ # str.remove(" test") # => "foo bar"
+ # str.remove(" test", /bar/) # => "foo "
+ # str # => "foo bar test"
+ def remove(*patterns)
+ dup.remove!(*patterns)
+ end
+
+ # Alters the string by removing all occurrences of the patterns.
+ # str = "foo bar test"
+ # str.remove!(" test", /bar/) # => "foo "
+ # str # => "foo "
+ def remove!(*patterns)
+ patterns.each do |pattern|
+ gsub! pattern, ""
+ end
+
+ self
+ end
+
+ # Truncates a given +text+ after a given length if +text+ is longer than length:
+ #
+ # 'Once upon a time in a world far far away'.truncate(27)
+ # # => "Once upon a time in a wo..."
+ #
+ # Pass a string or regexp :separator to truncate +text+ at a natural break:
+ #
+ # 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
+ # # => "Once upon a time in a..."
+ #
+ # 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
+ # # => "Once upon a time in a..."
+ #
+ # The last characters will be replaced with the :omission string (defaults to "...")
+ # for a total length not exceeding length:
+ #
+ # 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
+ # # => "And they f... (continued)"
+ def truncate(truncate_at, options = {})
+ return dup unless length > truncate_at
+
+ omission = options[:omission] || "..."
+ length_with_room_for_omission = truncate_at - omission.length
+ stop = \
+ if options[:separator]
+ rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
+ else
+ length_with_room_for_omission
+ end
+
+ "#{self[0, stop]}#{omission}"
+ end
+
+ # Truncates a given +text+ after a given number of words (words_count):
+ #
+ # 'Once upon a time in a world far far away'.truncate_words(4)
+ # # => "Once upon a time..."
+ #
+ # Pass a string or regexp :separator to specify a different separator of words:
+ #
+ # 'Once upon a time in a world'.truncate_words(5, separator: ' ')
+ # # => "Once upon a time in..."
+ #
+ # The last characters will be replaced with the :omission string (defaults to "..."):
+ #
+ # 'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)')
+ # # => "And they found that many... (continued)"
+ def truncate_words(words_count, options = {})
+ sep = options[:separator] || /\s+/
+ sep = Regexp.escape(sep.to_s) unless Regexp === sep
+ if self =~ /\A((?>.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
+ $1 + (options[:omission] || "...")
+ else
+ dup
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/indent.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/indent.rb
new file mode 100644
index 0000000..af9d181
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/indent.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+class String
+ # Same as +indent+, except it indents the receiver in-place.
+ #
+ # Returns the indented string, or +nil+ if there was nothing to indent.
+ def indent!(amount, indent_string = nil, indent_empty_lines = false)
+ indent_string = indent_string || self[/^[ \t]/] || " "
+ re = indent_empty_lines ? /^/ : /^(?!$)/
+ gsub!(re, indent_string * amount)
+ end
+
+ # Indents the lines in the receiver:
+ #
+ # <
+ # def some_method
+ # some_code
+ # end
+ #
+ # The second argument, +indent_string+, specifies which indent string to
+ # use. The default is +nil+, which tells the method to make a guess by
+ # peeking at the first indented line, and fallback to a space if there is
+ # none.
+ #
+ # " foo".indent(2) # => " foo"
+ # "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
+ # "foo".indent(2, "\t") # => "\t\tfoo"
+ #
+ # While +indent_string+ is typically one space or tab, it may be any string.
+ #
+ # The third argument, +indent_empty_lines+, is a flag that says whether
+ # empty lines should be indented. Default is false.
+ #
+ # "foo\n\nbar".indent(2) # => " foo\n\n bar"
+ # "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
+ #
+ def indent(amount, indent_string = nil, indent_empty_lines = false)
+ dup.tap { |_| _.indent!(amount, indent_string, indent_empty_lines) }
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/inflections.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/inflections.rb
new file mode 100644
index 0000000..8af3017
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/inflections.rb
@@ -0,0 +1,254 @@
+# frozen_string_literal: true
+
+require "active_support/inflector/methods"
+require "active_support/inflector/transliterate"
+
+# String inflections define new methods on the String class to transform names for different purposes.
+# For instance, you can figure out the name of a table from the name of a class.
+#
+# 'ScaleScore'.tableize # => "scale_scores"
+#
+class String
+ # Returns the plural form of the word in the string.
+ #
+ # If the optional parameter +count+ is specified,
+ # the singular form will be returned if count == 1.
+ # For any other value of +count+ the plural will be returned.
+ #
+ # If the optional parameter +locale+ is specified,
+ # the word will be pluralized as a word of that language.
+ # By default, this parameter is set to :en.
+ # You must define your own inflection rules for languages other than English.
+ #
+ # 'post'.pluralize # => "posts"
+ # 'octopus'.pluralize # => "octopi"
+ # 'sheep'.pluralize # => "sheep"
+ # 'words'.pluralize # => "words"
+ # 'the blue mailman'.pluralize # => "the blue mailmen"
+ # 'CamelOctopus'.pluralize # => "CamelOctopi"
+ # 'apple'.pluralize(1) # => "apple"
+ # 'apple'.pluralize(2) # => "apples"
+ # 'ley'.pluralize(:es) # => "leyes"
+ # 'ley'.pluralize(1, :es) # => "ley"
+ def pluralize(count = nil, locale = :en)
+ locale = count if count.is_a?(Symbol)
+ if count == 1
+ dup
+ else
+ ActiveSupport::Inflector.pluralize(self, locale)
+ end
+ end
+
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
+ #
+ # If the optional parameter +locale+ is specified,
+ # the word will be singularized as a word of that language.
+ # By default, this parameter is set to :en.
+ # You must define your own inflection rules for languages other than English.
+ #
+ # 'posts'.singularize # => "post"
+ # 'octopi'.singularize # => "octopus"
+ # 'sheep'.singularize # => "sheep"
+ # 'word'.singularize # => "word"
+ # 'the blue mailmen'.singularize # => "the blue mailman"
+ # 'CamelOctopi'.singularize # => "CamelOctopus"
+ # 'leyes'.singularize(:es) # => "ley"
+ def singularize(locale = :en)
+ ActiveSupport::Inflector.singularize(self, locale)
+ end
+
+ # +constantize+ tries to find a declared constant with the name specified
+ # in the string. It raises a NameError when the name is not in CamelCase
+ # or is not initialized. See ActiveSupport::Inflector.constantize
+ #
+ # 'Module'.constantize # => Module
+ # 'Class'.constantize # => Class
+ # 'blargle'.constantize # => NameError: wrong constant name blargle
+ def constantize
+ ActiveSupport::Inflector.constantize(self)
+ end
+
+ # +safe_constantize+ tries to find a declared constant with the name specified
+ # in the string. It returns +nil+ when the name is not in CamelCase
+ # or is not initialized. See ActiveSupport::Inflector.safe_constantize
+ #
+ # 'Module'.safe_constantize # => Module
+ # 'Class'.safe_constantize # => Class
+ # 'blargle'.safe_constantize # => nil
+ def safe_constantize
+ ActiveSupport::Inflector.safe_constantize(self)
+ end
+
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
+ # is set to :lower then camelize produces lowerCamelCase.
+ #
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
+ #
+ # 'active_record'.camelize # => "ActiveRecord"
+ # 'active_record'.camelize(:lower) # => "activeRecord"
+ # 'active_record/errors'.camelize # => "ActiveRecord::Errors"
+ # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
+ def camelize(first_letter = :upper)
+ case first_letter
+ when :upper
+ ActiveSupport::Inflector.camelize(self, true)
+ when :lower
+ ActiveSupport::Inflector.camelize(self, false)
+ else
+ raise ArgumentError, "Invalid option, use either :upper or :lower."
+ end
+ end
+ alias_method :camelcase, :camelize
+
+ # Capitalizes all the words and replaces some characters in the string to create
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
+ # used in the Rails internals.
+ #
+ # The trailing '_id','Id'.. can be kept and capitalized by setting the
+ # optional parameter +keep_id_suffix+ to true.
+ # By default, this parameter is false.
+ #
+ # +titleize+ is also aliased as +titlecase+.
+ #
+ # 'man from the boondocks'.titleize # => "Man From The Boondocks"
+ # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
+ # 'string_ending_with_id'.titleize(keep_id_suffix: true) # => "String Ending With Id"
+ def titleize(keep_id_suffix: false)
+ ActiveSupport::Inflector.titleize(self, keep_id_suffix: keep_id_suffix)
+ end
+ alias_method :titlecase, :titleize
+
+ # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
+ #
+ # +underscore+ will also change '::' to '/' to convert namespaces to paths.
+ #
+ # 'ActiveModel'.underscore # => "active_model"
+ # 'ActiveModel::Errors'.underscore # => "active_model/errors"
+ def underscore
+ ActiveSupport::Inflector.underscore(self)
+ end
+
+ # Replaces underscores with dashes in the string.
+ #
+ # 'puni_puni'.dasherize # => "puni-puni"
+ def dasherize
+ ActiveSupport::Inflector.dasherize(self)
+ end
+
+ # Removes the module part from the constant expression in the string.
+ #
+ # 'ActiveSupport::Inflector::Inflections'.demodulize # => "Inflections"
+ # 'Inflections'.demodulize # => "Inflections"
+ # '::Inflections'.demodulize # => "Inflections"
+ # ''.demodulize # => ''
+ #
+ # See also +deconstantize+.
+ def demodulize
+ ActiveSupport::Inflector.demodulize(self)
+ end
+
+ # Removes the rightmost segment from the constant expression in the string.
+ #
+ # 'Net::HTTP'.deconstantize # => "Net"
+ # '::Net::HTTP'.deconstantize # => "::Net"
+ # 'String'.deconstantize # => ""
+ # '::String'.deconstantize # => ""
+ # ''.deconstantize # => ""
+ #
+ # See also +demodulize+.
+ def deconstantize
+ ActiveSupport::Inflector.deconstantize(self)
+ end
+
+ # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
+ #
+ # class Person
+ # def to_param
+ # "#{id}-#{name.parameterize}"
+ # end
+ # end
+ #
+ # @person = Person.find(1)
+ # # => #
+ #
+ # <%= link_to(@person.name, person_path) %>
+ # # => Donald E. Knuth
+ #
+ # To preserve the case of the characters in a string, use the +preserve_case+ argument.
+ #
+ # class Person
+ # def to_param
+ # "#{id}-#{name.parameterize(preserve_case: true)}"
+ # end
+ # end
+ #
+ # @person = Person.find(1)
+ # # => #
+ #
+ # <%= link_to(@person.name, person_path) %>
+ # # => Donald E. Knuth
+ def parameterize(separator: "-", preserve_case: false)
+ ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
+ end
+
+ # Creates the name of a table like Rails does for models to table names. This method
+ # uses the +pluralize+ method on the last word in the string.
+ #
+ # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
+ # 'ham_and_egg'.tableize # => "ham_and_eggs"
+ # 'fancyCategory'.tableize # => "fancy_categories"
+ def tableize
+ ActiveSupport::Inflector.tableize(self)
+ end
+
+ # Creates a class name from a plural table name like Rails does for table names to models.
+ # Note that this returns a string and not a class. (To convert to an actual class
+ # follow +classify+ with +constantize+.)
+ #
+ # 'ham_and_eggs'.classify # => "HamAndEgg"
+ # 'posts'.classify # => "Post"
+ def classify
+ ActiveSupport::Inflector.classify(self)
+ end
+
+ # Capitalizes the first word, turns underscores into spaces, and (by default)strips a
+ # trailing '_id' if present.
+ # Like +titleize+, this is meant for creating pretty output.
+ #
+ # The capitalization of the first word can be turned off by setting the
+ # optional parameter +capitalize+ to false.
+ # By default, this parameter is true.
+ #
+ # The trailing '_id' can be kept and capitalized by setting the
+ # optional parameter +keep_id_suffix+ to true.
+ # By default, this parameter is false.
+ #
+ # 'employee_salary'.humanize # => "Employee salary"
+ # 'author_id'.humanize # => "Author"
+ # 'author_id'.humanize(capitalize: false) # => "author"
+ # '_id'.humanize # => "Id"
+ # 'author_id'.humanize(keep_id_suffix: true) # => "Author Id"
+ def humanize(capitalize: true, keep_id_suffix: false)
+ ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix)
+ end
+
+ # Converts just the first character to uppercase.
+ #
+ # 'what a Lovely Day'.upcase_first # => "What a Lovely Day"
+ # 'w'.upcase_first # => "W"
+ # ''.upcase_first # => ""
+ def upcase_first
+ ActiveSupport::Inflector.upcase_first(self)
+ end
+
+ # Creates a foreign key name from a class name.
+ # +separate_class_name_and_id_with_underscore+ sets whether
+ # the method should put '_' between the name and 'id'.
+ #
+ # 'Message'.foreign_key # => "message_id"
+ # 'Message'.foreign_key(false) # => "messageid"
+ # 'Admin::Post'.foreign_key # => "post_id"
+ def foreign_key(separate_class_name_and_id_with_underscore = true)
+ ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/inquiry.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/inquiry.rb
new file mode 100644
index 0000000..a796d5f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/inquiry.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require "active_support/string_inquirer"
+
+class String
+ # Wraps the current string in the ActiveSupport::StringInquirer class,
+ # which gives you a prettier way to test for equality.
+ #
+ # env = 'production'.inquiry
+ # env.production? # => true
+ # env.development? # => false
+ def inquiry
+ ActiveSupport::StringInquirer.new(self)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/multibyte.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/multibyte.rb
new file mode 100644
index 0000000..07c0d16
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/multibyte.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require "active_support/multibyte"
+
+class String
+ # == Multibyte proxy
+ #
+ # +mb_chars+ is a multibyte safe proxy for string methods.
+ #
+ # It creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
+ # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
+ # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
+ #
+ # >> "lj".upcase
+ # => "lj"
+ # >> "lj".mb_chars.upcase.to_s
+ # => "LJ"
+ #
+ # NOTE: An above example is useful for pre Ruby 2.4. Ruby 2.4 supports Unicode case mappings.
+ #
+ # == Method chaining
+ #
+ # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
+ # method chaining on the result of any of these methods.
+ #
+ # name.mb_chars.reverse.length # => 12
+ #
+ # == Interoperability and configuration
+ #
+ # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
+ # String and Char work like expected. The bang! methods change the internal string representation in the Chars
+ # object. Interoperability problems can be resolved easily with a +to_s+ call.
+ #
+ # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
+ # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
+ def mb_chars
+ ActiveSupport::Multibyte.proxy_class.new(self)
+ end
+
+ # Returns +true+ if string has utf_8 encoding.
+ #
+ # utf_8_str = "some string".encode "UTF-8"
+ # iso_str = "some string".encode "ISO-8859-1"
+ #
+ # utf_8_str.is_utf8? # => true
+ # iso_str.is_utf8? # => false
+ def is_utf8?
+ case encoding
+ when Encoding::UTF_8
+ valid_encoding?
+ when Encoding::ASCII_8BIT, Encoding::US_ASCII
+ dup.force_encoding(Encoding::UTF_8).valid_encoding?
+ else
+ false
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/output_safety.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/output_safety.rb
new file mode 100644
index 0000000..f3bdc29
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/output_safety.rb
@@ -0,0 +1,258 @@
+# frozen_string_literal: true
+
+require "erb"
+require "active_support/core_ext/kernel/singleton_class"
+require "active_support/core_ext/module/redefine_method"
+require "active_support/multibyte/unicode"
+
+class ERB
+ module Util
+ HTML_ESCAPE = { "&" => "&", ">" => ">", "<" => "<", '"' => """, "'" => "'" }
+ JSON_ESCAPE = { "&" => '\u0026', ">" => '\u003e', "<" => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
+ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
+ JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
+
+ # A utility method for escaping HTML tag characters.
+ # This method is also aliased as h.
+ #
+ # puts html_escape('is a > 0 & a < 10?')
+ # # => is a > 0 & a < 10?
+ def html_escape(s)
+ unwrapped_html_escape(s).html_safe
+ end
+
+ silence_redefinition_of_method :h
+ alias h html_escape
+
+ module_function :h
+
+ singleton_class.silence_redefinition_of_method :html_escape
+ module_function :html_escape
+
+ # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
+ # This method is not for public consumption! Seriously!
+ def unwrapped_html_escape(s) # :nodoc:
+ s = s.to_s
+ if s.html_safe?
+ s
+ else
+ CGI.escapeHTML(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))
+ end
+ end
+ module_function :unwrapped_html_escape
+
+ # A utility method for escaping HTML without affecting existing escaped entities.
+ #
+ # html_escape_once('1 < 2 & 3')
+ # # => "1 < 2 & 3"
+ #
+ # html_escape_once('<< Accept & Checkout')
+ # # => "<< Accept & Checkout"
+ def html_escape_once(s)
+ result = ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
+ s.html_safe? ? result.html_safe : result
+ end
+
+ module_function :html_escape_once
+
+ # A utility method for escaping HTML entities in JSON strings. Specifically, the
+ # &, > and < characters are replaced with their equivalent unicode escaped form -
+ # \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
+ # escaped as they are treated as newline characters in some JavaScript engines.
+ # These sequences have identical meaning as the original characters inside the
+ # context of a JSON string, so assuming the input is a valid and well-formed
+ # JSON value, the output will have equivalent meaning when parsed:
+ #
+ # json = JSON.generate({ name: ""})
+ # # => "{\"name\":\"\"}"
+ #
+ # json_escape(json)
+ # # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
+ #
+ # JSON.parse(json) == JSON.parse(json_escape(json))
+ # # => true
+ #
+ # The intended use case for this method is to escape JSON strings before including
+ # them inside a script tag to avoid XSS vulnerability:
+ #
+ #
+ #
+ # It is necessary to +raw+ the result of +json_escape+, so that quotation marks
+ # don't get converted to " entities. +json_escape+ doesn't
+ # automatically flag the result as HTML safe, since the raw value is unsafe to
+ # use inside HTML attributes.
+ #
+ # If your JSON is being used downstream for insertion into the DOM, be aware of
+ # whether or not it is being inserted via +html()+. Most jQuery plugins do this.
+ # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
+ # content returned by your JSON.
+ #
+ # If you need to output JSON elsewhere in your HTML, you can just do something
+ # like this, as any unsafe characters (including quotation marks) will be
+ # automatically escaped for you:
+ #
+ #
...
+ #
+ # WARNING: this helper only works with valid JSON. Using this on non-JSON values
+ # will open up serious XSS vulnerabilities. For example, if you replace the
+ # +current_user.to_json+ in the example above with user input instead, the browser
+ # will happily eval() that string as JavaScript.
+ #
+ # The escaping performed in this method is identical to those performed in the
+ # Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
+ # set to true. Because this transformation is idempotent, this helper can be
+ # applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
+ #
+ # Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
+ # is enabled, or if you are unsure where your JSON string originated from, it
+ # is recommended that you always apply this helper (other libraries, such as the
+ # JSON gem, do not provide this kind of protection by default; also some gems
+ # might override +to_json+ to bypass Active Support's encoder).
+ def json_escape(s)
+ result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
+ s.html_safe? ? result.html_safe : result
+ end
+
+ module_function :json_escape
+ end
+end
+
+class Object
+ def html_safe?
+ false
+ end
+end
+
+class Numeric
+ def html_safe?
+ true
+ end
+end
+
+module ActiveSupport #:nodoc:
+ class SafeBuffer < String
+ UNSAFE_STRING_METHODS = %w(
+ capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
+ slice squeeze strip sub succ swapcase tr tr_s upcase
+ )
+
+ alias_method :original_concat, :concat
+ private :original_concat
+
+ # Raised when ActiveSupport::SafeBuffer#safe_concat is called on unsafe buffers.
+ class SafeConcatError < StandardError
+ def initialize
+ super "Could not concatenate to the buffer because it is not html safe."
+ end
+ end
+
+ def [](*args)
+ if args.size < 2
+ super
+ elsif html_safe?
+ new_safe_buffer = super
+
+ if new_safe_buffer
+ new_safe_buffer.instance_variable_set :@html_safe, true
+ end
+
+ new_safe_buffer
+ else
+ to_str[*args]
+ end
+ end
+
+ def safe_concat(value)
+ raise SafeConcatError unless html_safe?
+ original_concat(value)
+ end
+
+ def initialize(str = "")
+ @html_safe = true
+ super
+ end
+
+ def initialize_copy(other)
+ super
+ @html_safe = other.html_safe?
+ end
+
+ def clone_empty
+ self[0, 0]
+ end
+
+ def concat(value)
+ super(html_escape_interpolated_argument(value))
+ end
+ alias << concat
+
+ def prepend(value)
+ super(html_escape_interpolated_argument(value))
+ end
+
+ def +(other)
+ dup.concat(other)
+ end
+
+ def %(args)
+ case args
+ when Hash
+ escaped_args = Hash[args.map { |k, arg| [k, html_escape_interpolated_argument(arg)] }]
+ else
+ escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
+ end
+
+ self.class.new(super(escaped_args))
+ end
+
+ def html_safe?
+ defined?(@html_safe) && @html_safe
+ end
+
+ def to_s
+ self
+ end
+
+ def to_param
+ to_str
+ end
+
+ def encode_with(coder)
+ coder.represent_object nil, to_str
+ end
+
+ UNSAFE_STRING_METHODS.each do |unsafe_method|
+ if unsafe_method.respond_to?(unsafe_method)
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
+ def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
+ to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
+ end # end
+
+ def #{unsafe_method}!(*args) # def capitalize!(*args)
+ @html_safe = false # @html_safe = false
+ super # super
+ end # end
+ EOT
+ end
+ end
+
+ private
+
+ def html_escape_interpolated_argument(arg)
+ (!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
+ end
+ end
+end
+
+class String
+ # Marks a string as trusted safe. It will be inserted into HTML with no
+ # additional escaping performed. It is your responsibility to ensure that the
+ # string contains no malicious content. This method is equivalent to the
+ # +raw+ helper in views. It is recommended that you use +sanitize+ instead of
+ # this method. It should never be called on user input.
+ def html_safe
+ ActiveSupport::SafeBuffer.new(self)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/starts_ends_with.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/starts_ends_with.rb
new file mode 100644
index 0000000..919eb7a
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/starts_ends_with.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class String
+ alias_method :starts_with?, :start_with?
+ alias_method :ends_with?, :end_with?
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/strip.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/strip.rb
new file mode 100644
index 0000000..cc26274
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/strip.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class String
+ # Strips indentation in heredocs.
+ #
+ # For example in
+ #
+ # if options[:usage]
+ # puts <<-USAGE.strip_heredoc
+ # This command does such and such.
+ #
+ # Supported options are:
+ # -h This message
+ # ...
+ # USAGE
+ # end
+ #
+ # the user would see the usage message aligned against the left margin.
+ #
+ # Technically, it looks for the least indented non-empty line
+ # in the whole string, and removes that amount of leading whitespace.
+ def strip_heredoc
+ gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/zones.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/zones.rb
new file mode 100644
index 0000000..55dc231
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/string/zones.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/string/conversions"
+require "active_support/core_ext/time/zones"
+
+class String
+ # Converts String to a TimeWithZone in the current zone if Time.zone or Time.zone_default
+ # is set, otherwise converts String to a Time via String#to_time
+ def in_time_zone(zone = ::Time.zone)
+ if zone
+ ::Time.find_zone!(zone).parse(self)
+ else
+ to_time
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time.rb
new file mode 100644
index 0000000..c809def
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/time/acts_like"
+require "active_support/core_ext/time/calculations"
+require "active_support/core_ext/time/compatibility"
+require "active_support/core_ext/time/conversions"
+require "active_support/core_ext/time/zones"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/acts_like.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/acts_like.rb
new file mode 100644
index 0000000..8572b49
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/acts_like.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/acts_like"
+
+class Time
+ # Duck-types as a Time-like class. See Object#acts_like?.
+ def acts_like_time?
+ true
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/calculations.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/calculations.rb
new file mode 100644
index 0000000..120768d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/calculations.rb
@@ -0,0 +1,315 @@
+# frozen_string_literal: true
+
+require "active_support/duration"
+require "active_support/core_ext/time/conversions"
+require "active_support/time_with_zone"
+require "active_support/core_ext/time/zones"
+require "active_support/core_ext/date_and_time/calculations"
+require "active_support/core_ext/date/calculations"
+
+class Time
+ include DateAndTime::Calculations
+
+ COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+
+ class << self
+ # Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
+ def ===(other)
+ super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone))
+ end
+
+ # Returns the number of days in the given month.
+ # If no year is specified, it will use the current year.
+ def days_in_month(month, year = current.year)
+ if month == 2 && ::Date.gregorian_leap?(year)
+ 29
+ else
+ COMMON_YEAR_DAYS_IN_MONTH[month]
+ end
+ end
+
+ # Returns the number of days in the given year.
+ # If no year is specified, it will use the current year.
+ def days_in_year(year = current.year)
+ days_in_month(2, year) + 337
+ end
+
+ # Returns Time.zone.now when Time.zone or config.time_zone are set, otherwise just returns Time.now.
+ def current
+ ::Time.zone ? ::Time.zone.now : ::Time.now
+ end
+
+ # Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
+ # instances can be used when called with a single argument
+ def at_with_coercion(*args)
+ return at_without_coercion(*args) if args.size != 1
+
+ # Time.at can be called with a time or numerical value
+ time_or_number = args.first
+
+ if time_or_number.is_a?(ActiveSupport::TimeWithZone) || time_or_number.is_a?(DateTime)
+ at_without_coercion(time_or_number.to_f).getlocal
+ else
+ at_without_coercion(time_or_number)
+ end
+ end
+ alias_method :at_without_coercion, :at
+ alias_method :at, :at_with_coercion
+
+ # Creates a +Time+ instance from an RFC 3339 string.
+ #
+ # Time.rfc3339('1999-12-31T14:00:00-10:00') # => 2000-01-01 00:00:00 -1000
+ #
+ # If the time or offset components are missing then an +ArgumentError+ will be raised.
+ #
+ # Time.rfc3339('1999-12-31') # => ArgumentError: invalid date
+ def rfc3339(str)
+ parts = Date._rfc3339(str)
+
+ raise ArgumentError, "invalid date" if parts.empty?
+
+ Time.new(
+ parts.fetch(:year),
+ parts.fetch(:mon),
+ parts.fetch(:mday),
+ parts.fetch(:hour),
+ parts.fetch(:min),
+ parts.fetch(:sec) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset)
+ )
+ end
+ end
+
+ # Returns the number of seconds since 00:00:00.
+ #
+ # Time.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0.0
+ # Time.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296.0
+ # Time.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399.0
+ def seconds_since_midnight
+ to_i - change(hour: 0).to_i + (usec / 1.0e+6)
+ end
+
+ # Returns the number of seconds until 23:59:59.
+ #
+ # Time.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
+ # Time.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
+ # Time.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
+ def seconds_until_end_of_day
+ end_of_day.to_i - to_i
+ end
+
+ # Returns the fraction of a second as a +Rational+
+ #
+ # Time.new(2012, 8, 29, 0, 0, 0.5).sec_fraction # => (1/2)
+ def sec_fraction
+ subsec
+ end
+
+ # Returns a new Time where one or more of the elements have been changed according
+ # to the +options+ parameter. The time options (:hour, :min,
+ # :sec, :usec, :nsec) reset cascadingly, so if only
+ # the hour is passed, then minute, sec, usec and nsec is set to 0. If the hour
+ # and minute is passed, then sec, usec and nsec is set to 0. The +options+ parameter
+ # takes a hash with any of these keys: :year, :month, :day,
+ # :hour, :min, :sec, :usec, :nsec,
+ # :offset. Pass either :usec or :nsec, not both.
+ #
+ # Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
+ # Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
+ # Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
+ def change(options)
+ new_year = options.fetch(:year, year)
+ new_month = options.fetch(:month, month)
+ new_day = options.fetch(:day, day)
+ new_hour = options.fetch(:hour, hour)
+ new_min = options.fetch(:min, options[:hour] ? 0 : min)
+ new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
+ new_offset = options.fetch(:offset, nil)
+
+ if new_nsec = options[:nsec]
+ raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec]
+ new_usec = Rational(new_nsec, 1000)
+ else
+ new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
+ end
+
+ raise ArgumentError, "argument out of range" if new_usec >= 1000000
+
+ new_sec += Rational(new_usec, 1000000)
+
+ if new_offset
+ ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, new_offset)
+ elsif utc?
+ ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec)
+ elsif zone
+ ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec)
+ else
+ ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, utc_offset)
+ end
+ end
+
+ # Uses Date to provide precise Time calculations for years, months, and days
+ # according to the proleptic Gregorian calendar. The +options+ parameter
+ # takes a hash with any of these keys: :years, :months,
+ # :weeks, :days, :hours, :minutes,
+ # :seconds.
+ #
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(seconds: 1) # => 2015-08-01 14:35:01 -0700
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(minutes: 1) # => 2015-08-01 14:36:00 -0700
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(hours: 1) # => 2015-08-01 15:35:00 -0700
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(days: 1) # => 2015-08-02 14:35:00 -0700
+ # Time.new(2015, 8, 1, 14, 35, 0).advance(weeks: 1) # => 2015-08-08 14:35:00 -0700
+ def advance(options)
+ unless options[:weeks].nil?
+ options[:weeks], partial_weeks = options[:weeks].divmod(1)
+ options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
+ end
+
+ unless options[:days].nil?
+ options[:days], partial_days = options[:days].divmod(1)
+ options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
+ end
+
+ d = to_date.advance(options)
+ d = d.gregorian if d.julian?
+ time_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
+ seconds_to_advance = \
+ options.fetch(:seconds, 0) +
+ options.fetch(:minutes, 0) * 60 +
+ options.fetch(:hours, 0) * 3600
+
+ if seconds_to_advance.zero?
+ time_advanced_by_date
+ else
+ time_advanced_by_date.since(seconds_to_advance)
+ end
+ end
+
+ # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
+ def ago(seconds)
+ since(-seconds)
+ end
+
+ # Returns a new Time representing the time a number of seconds since the instance time
+ def since(seconds)
+ self + seconds
+ rescue
+ to_datetime.since(seconds)
+ end
+ alias :in :since
+
+ # Returns a new Time representing the start of the day (0:00)
+ def beginning_of_day
+ change(hour: 0)
+ end
+ alias :midnight :beginning_of_day
+ alias :at_midnight :beginning_of_day
+ alias :at_beginning_of_day :beginning_of_day
+
+ # Returns a new Time representing the middle of the day (12:00)
+ def middle_of_day
+ change(hour: 12)
+ end
+ alias :midday :middle_of_day
+ alias :noon :middle_of_day
+ alias :at_midday :middle_of_day
+ alias :at_noon :middle_of_day
+ alias :at_middle_of_day :middle_of_day
+
+ # Returns a new Time representing the end of the day, 23:59:59.999999
+ def end_of_day
+ change(
+ hour: 23,
+ min: 59,
+ sec: 59,
+ usec: Rational(999999999, 1000)
+ )
+ end
+ alias :at_end_of_day :end_of_day
+
+ # Returns a new Time representing the start of the hour (x:00)
+ def beginning_of_hour
+ change(min: 0)
+ end
+ alias :at_beginning_of_hour :beginning_of_hour
+
+ # Returns a new Time representing the end of the hour, x:59:59.999999
+ def end_of_hour
+ change(
+ min: 59,
+ sec: 59,
+ usec: Rational(999999999, 1000)
+ )
+ end
+ alias :at_end_of_hour :end_of_hour
+
+ # Returns a new Time representing the start of the minute (x:xx:00)
+ def beginning_of_minute
+ change(sec: 0)
+ end
+ alias :at_beginning_of_minute :beginning_of_minute
+
+ # Returns a new Time representing the end of the minute, x:xx:59.999999
+ def end_of_minute
+ change(
+ sec: 59,
+ usec: Rational(999999999, 1000)
+ )
+ end
+ alias :at_end_of_minute :end_of_minute
+
+ def plus_with_duration(other) #:nodoc:
+ if ActiveSupport::Duration === other
+ other.since(self)
+ else
+ plus_without_duration(other)
+ end
+ end
+ alias_method :plus_without_duration, :+
+ alias_method :+, :plus_with_duration
+
+ def minus_with_duration(other) #:nodoc:
+ if ActiveSupport::Duration === other
+ other.until(self)
+ else
+ minus_without_duration(other)
+ end
+ end
+ alias_method :minus_without_duration, :-
+ alias_method :-, :minus_with_duration
+
+ # Time#- can also be used to determine the number of seconds between two Time instances.
+ # We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
+ # are coerced into values that Time#- will recognize
+ def minus_with_coercion(other)
+ other = other.comparable_time if other.respond_to?(:comparable_time)
+ other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
+ end
+ alias_method :minus_without_coercion, :-
+ alias_method :-, :minus_with_coercion
+
+ # Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
+ # can be chronologically compared with a Time
+ def compare_with_coercion(other)
+ # we're avoiding Time#to_datetime and Time#to_time because they're expensive
+ if other.class == Time
+ compare_without_coercion(other)
+ elsif other.is_a?(Time)
+ compare_without_coercion(other.to_time)
+ else
+ to_datetime <=> other
+ end
+ end
+ alias_method :compare_without_coercion, :<=>
+ alias_method :<=>, :compare_with_coercion
+
+ # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
+ # can be eql? to an equivalent Time
+ def eql_with_coercion(other)
+ # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
+ other = other.comparable_time if other.respond_to?(:comparable_time)
+ eql_without_coercion(other)
+ end
+ alias_method :eql_without_coercion, :eql?
+ alias_method :eql?, :eql_with_coercion
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/compatibility.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/compatibility.rb
new file mode 100644
index 0000000..495e4f3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/compatibility.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/date_and_time/compatibility"
+require "active_support/core_ext/module/redefine_method"
+
+class Time
+ include DateAndTime::Compatibility
+
+ silence_redefinition_of_method :to_time
+
+ # Either return +self+ or the time in the local system timezone depending
+ # on the setting of +ActiveSupport.to_time_preserves_timezone+.
+ def to_time
+ preserve_timezone ? self : getlocal
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/conversions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/conversions.rb
new file mode 100644
index 0000000..345cb28
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/conversions.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require "active_support/inflector/methods"
+require "active_support/values/time_zone"
+
+class Time
+ DATE_FORMATS = {
+ db: "%Y-%m-%d %H:%M:%S",
+ number: "%Y%m%d%H%M%S",
+ nsec: "%Y%m%d%H%M%S%9N",
+ usec: "%Y%m%d%H%M%S%6N",
+ time: "%H:%M",
+ short: "%d %b %H:%M",
+ long: "%B %d, %Y %H:%M",
+ long_ordinal: lambda { |time|
+ day_format = ActiveSupport::Inflector.ordinalize(time.day)
+ time.strftime("%B #{day_format}, %Y %H:%M")
+ },
+ rfc822: lambda { |time|
+ offset_format = time.formatted_offset(false)
+ time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
+ },
+ iso8601: lambda { |time| time.iso8601 }
+ }
+
+ # Converts to a formatted string. See DATE_FORMATS for built-in formats.
+ #
+ # This method is aliased to to_s.
+ #
+ # time = Time.now # => 2007-01-18 06:10:17 -06:00
+ #
+ # time.to_formatted_s(:time) # => "06:10"
+ # time.to_s(:time) # => "06:10"
+ #
+ # time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
+ # time.to_formatted_s(:number) # => "20070118061017"
+ # time.to_formatted_s(:short) # => "18 Jan 06:10"
+ # time.to_formatted_s(:long) # => "January 18, 2007 06:10"
+ # time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
+ # time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
+ # time.to_formatted_s(:iso8601) # => "2007-01-18T06:10:17-06:00"
+ #
+ # == Adding your own time formats to +to_formatted_s+
+ # You can add your own formats to the Time::DATE_FORMATS hash.
+ # Use the format name as the hash key and either a strftime string
+ # or Proc instance that takes a time argument as the value.
+ #
+ # # config/initializers/time_formats.rb
+ # Time::DATE_FORMATS[:month_and_year] = '%B %Y'
+ # Time::DATE_FORMATS[:short_ordinal] = ->(time) { time.strftime("%B #{time.day.ordinalize}") }
+ def to_formatted_s(format = :default)
+ if formatter = DATE_FORMATS[format]
+ formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
+ else
+ to_default_s
+ end
+ end
+ alias_method :to_default_s, :to_s
+ alias_method :to_s, :to_formatted_s
+
+ # Returns a formatted string of the offset from UTC, or an alternative
+ # string if the time zone is already UTC.
+ #
+ # Time.local(2000).formatted_offset # => "-06:00"
+ # Time.local(2000).formatted_offset(false) # => "-0600"
+ def formatted_offset(colon = true, alternate_utc_string = nil)
+ utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
+ end
+
+ # Aliased to +xmlschema+ for compatibility with +DateTime+
+ alias_method :rfc3339, :xmlschema
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/zones.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/zones.rb
new file mode 100644
index 0000000..a5588fd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/time/zones.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+require "active_support/time_with_zone"
+require "active_support/core_ext/time/acts_like"
+require "active_support/core_ext/date_and_time/zones"
+
+class Time
+ include DateAndTime::Zones
+ class << self
+ attr_accessor :zone_default
+
+ # Returns the TimeZone for the current request, if this has been set (via Time.zone=).
+ # If Time.zone has not been set for the current request, returns the TimeZone specified in config.time_zone.
+ def zone
+ Thread.current[:time_zone] || zone_default
+ end
+
+ # Sets Time.zone to a TimeZone object for the current request/thread.
+ #
+ # This method accepts any of the following:
+ #
+ # * A Rails TimeZone object.
+ # * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", -5.hours).
+ # * A TZInfo::Timezone object.
+ # * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
+ #
+ # Here's an example of how you might set Time.zone on a per request basis and reset it when the request is done.
+ # current_user.time_zone just needs to return a string identifying the user's preferred time zone:
+ #
+ # class ApplicationController < ActionController::Base
+ # around_action :set_time_zone
+ #
+ # def set_time_zone
+ # if logged_in?
+ # Time.use_zone(current_user.time_zone) { yield }
+ # else
+ # yield
+ # end
+ # end
+ # end
+ def zone=(time_zone)
+ Thread.current[:time_zone] = find_zone!(time_zone)
+ end
+
+ # Allows override of Time.zone locally inside supplied block;
+ # resets Time.zone to existing value when done.
+ #
+ # class ApplicationController < ActionController::Base
+ # around_action :set_time_zone
+ #
+ # private
+ #
+ # def set_time_zone
+ # Time.use_zone(current_user.timezone) { yield }
+ # end
+ # end
+ #
+ # NOTE: This won't affect any ActiveSupport::TimeWithZone
+ # objects that have already been created, e.g. any model timestamp
+ # attributes that have been read before the block will remain in
+ # the application's default timezone.
+ def use_zone(time_zone)
+ new_zone = find_zone!(time_zone)
+ begin
+ old_zone, ::Time.zone = ::Time.zone, new_zone
+ yield
+ ensure
+ ::Time.zone = old_zone
+ end
+ end
+
+ # Returns a TimeZone instance matching the time zone provided.
+ # Accepts the time zone in any format supported by Time.zone=.
+ # Raises an +ArgumentError+ for invalid time zones.
+ #
+ # Time.find_zone! "America/New_York" # => #
+ # Time.find_zone! "EST" # => #
+ # Time.find_zone! -5.hours # => #
+ # Time.find_zone! nil # => nil
+ # Time.find_zone! false # => false
+ # Time.find_zone! "NOT-A-TIMEZONE" # => ArgumentError: Invalid Timezone: NOT-A-TIMEZONE
+ def find_zone!(time_zone)
+ if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone)
+ time_zone
+ else
+ # Look up the timezone based on the identifier (unless we've been
+ # passed a TZInfo::Timezone)
+ unless time_zone.respond_to?(:period_for_local)
+ time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
+ end
+
+ # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
+ if time_zone.is_a?(ActiveSupport::TimeZone)
+ time_zone
+ else
+ ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
+ end
+ end
+ rescue TZInfo::InvalidTimezoneIdentifier
+ raise ArgumentError, "Invalid Timezone: #{time_zone}"
+ end
+
+ # Returns a TimeZone instance matching the time zone provided.
+ # Accepts the time zone in any format supported by Time.zone=.
+ # Returns +nil+ for invalid time zones.
+ #
+ # Time.find_zone "America/New_York" # => #
+ # Time.find_zone "NOT-A-TIMEZONE" # => nil
+ def find_zone(time_zone)
+ find_zone!(time_zone) rescue nil
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/uri.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/uri.rb
new file mode 100644
index 0000000..9def947
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/core_ext/uri.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require "uri"
+if RUBY_VERSION < "2.6.0"
+ require "active_support/core_ext/module/redefine_method"
+ URI::Parser.class_eval do
+ silence_redefinition_of_method :unescape
+ def unescape(str, escaped = /%[a-fA-F\d]{2}/)
+ # TODO: Are we actually sure that ASCII == UTF-8?
+ # YK: My initial experiments say yes, but let's be sure please
+ enc = str.encoding
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
+ str.dup.force_encoding(Encoding::ASCII_8BIT).gsub(escaped) { |match| [match[1, 2].hex].pack("C") }.force_encoding(enc)
+ end
+ end
+end
+
+module URI
+ class << self
+ def parser
+ @parser ||= URI::Parser.new
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/current_attributes.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/current_attributes.rb
new file mode 100644
index 0000000..4e6d8e4
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/current_attributes.rb
@@ -0,0 +1,195 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # Abstract super class that provides a thread-isolated attributes singleton, which resets automatically
+ # before and after each request. This allows you to keep all the per-request attributes easily
+ # available to the whole system.
+ #
+ # The following full app-like example demonstrates how to use a Current class to
+ # facilitate easy access to the global, per-request attributes without passing them deeply
+ # around everywhere:
+ #
+ # # app/models/current.rb
+ # class Current < ActiveSupport::CurrentAttributes
+ # attribute :account, :user
+ # attribute :request_id, :user_agent, :ip_address
+ #
+ # resets { Time.zone = nil }
+ #
+ # def user=(user)
+ # super
+ # self.account = user.account
+ # Time.zone = user.time_zone
+ # end
+ # end
+ #
+ # # app/controllers/concerns/authentication.rb
+ # module Authentication
+ # extend ActiveSupport::Concern
+ #
+ # included do
+ # before_action :authenticate
+ # end
+ #
+ # private
+ # def authenticate
+ # if authenticated_user = User.find_by(id: cookies.encrypted[:user_id])
+ # Current.user = authenticated_user
+ # else
+ # redirect_to new_session_url
+ # end
+ # end
+ # end
+ #
+ # # app/controllers/concerns/set_current_request_details.rb
+ # module SetCurrentRequestDetails
+ # extend ActiveSupport::Concern
+ #
+ # included do
+ # before_action do
+ # Current.request_id = request.uuid
+ # Current.user_agent = request.user_agent
+ # Current.ip_address = request.ip
+ # end
+ # end
+ # end
+ #
+ # class ApplicationController < ActionController::Base
+ # include Authentication
+ # include SetCurrentRequestDetails
+ # end
+ #
+ # class MessagesController < ApplicationController
+ # def create
+ # Current.account.messages.create(message_params)
+ # end
+ # end
+ #
+ # class Message < ApplicationRecord
+ # belongs_to :creator, default: -> { Current.user }
+ # after_create { |message| Event.create(record: message) }
+ # end
+ #
+ # class Event < ApplicationRecord
+ # before_create do
+ # self.request_id = Current.request_id
+ # self.user_agent = Current.user_agent
+ # self.ip_address = Current.ip_address
+ # end
+ # end
+ #
+ # A word of caution: It's easy to overdo a global singleton like Current and tangle your model as a result.
+ # Current should only be used for a few, top-level globals, like account, user, and request details.
+ # The attributes stuck in Current should be used by more or less all actions on all requests. If you start
+ # sticking controller-specific attributes in there, you're going to create a mess.
+ class CurrentAttributes
+ include ActiveSupport::Callbacks
+ define_callbacks :reset
+
+ class << self
+ # Returns singleton instance for this class in this thread. If none exists, one is created.
+ def instance
+ current_instances[name] ||= new
+ end
+
+ # Declares one or more attributes that will be given both class and instance accessor methods.
+ def attribute(*names)
+ generated_attribute_methods.module_eval do
+ names.each do |name|
+ define_method(name) do
+ attributes[name.to_sym]
+ end
+
+ define_method("#{name}=") do |attribute|
+ attributes[name.to_sym] = attribute
+ end
+ end
+ end
+
+ names.each do |name|
+ define_singleton_method(name) do
+ instance.public_send(name)
+ end
+
+ define_singleton_method("#{name}=") do |attribute|
+ instance.public_send("#{name}=", attribute)
+ end
+ end
+ end
+
+ # Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
+ def resets(&block)
+ set_callback :reset, :after, &block
+ end
+
+ delegate :set, :reset, to: :instance
+
+ def reset_all # :nodoc:
+ current_instances.each_value(&:reset)
+ end
+
+ def clear_all # :nodoc:
+ reset_all
+ current_instances.clear
+ end
+
+ private
+ def generated_attribute_methods
+ @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
+ end
+
+ def current_instances
+ Thread.current[:current_attributes_instances] ||= {}
+ end
+
+ def method_missing(name, *args, &block)
+ # Caches the method definition as a singleton method of the receiver.
+ #
+ # By letting #delegate handle it, we avoid an enclosure that'll capture args.
+ singleton_class.delegate name, to: :instance
+
+ send(name, *args, &block)
+ end
+ end
+
+ attr_accessor :attributes
+
+ def initialize
+ @attributes = {}
+ end
+
+ # Expose one or more attributes within a block. Old values are returned after the block concludes.
+ # Example demonstrating the common use of needing to set Current attributes outside the request-cycle:
+ #
+ # class Chat::PublicationJob < ApplicationJob
+ # def perform(attributes, room_number, creator)
+ # Current.set(person: creator) do
+ # Chat::Publisher.publish(attributes: attributes, room_number: room_number)
+ # end
+ # end
+ # end
+ def set(set_attributes)
+ old_attributes = compute_attributes(set_attributes.keys)
+ assign_attributes(set_attributes)
+ yield
+ ensure
+ assign_attributes(old_attributes)
+ end
+
+ # Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
+ def reset
+ run_callbacks :reset do
+ self.attributes = {}
+ end
+ end
+
+ private
+ def assign_attributes(new_attributes)
+ new_attributes.each { |key, value| public_send("#{key}=", value) }
+ end
+
+ def compute_attributes(keys)
+ keys.collect { |key| [ key, public_send(key) ] }.to_h
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/dependencies.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/dependencies.rb
new file mode 100644
index 0000000..3957700
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/dependencies.rb
@@ -0,0 +1,753 @@
+# frozen_string_literal: true
+
+require "set"
+require "thread"
+require "concurrent/map"
+require "pathname"
+require "active_support/core_ext/module/aliasing"
+require "active_support/core_ext/module/attribute_accessors"
+require "active_support/core_ext/module/introspection"
+require "active_support/core_ext/module/anonymous"
+require "active_support/core_ext/object/blank"
+require "active_support/core_ext/kernel/reporting"
+require "active_support/core_ext/load_error"
+require "active_support/core_ext/name_error"
+require "active_support/core_ext/string/starts_ends_with"
+require "active_support/dependencies/interlock"
+require "active_support/inflector"
+
+module ActiveSupport #:nodoc:
+ module Dependencies #:nodoc:
+ extend self
+
+ mattr_accessor :interlock, default: Interlock.new
+
+ # :doc:
+
+ # Execute the supplied block without interference from any
+ # concurrent loads.
+ def self.run_interlock
+ Dependencies.interlock.running { yield }
+ end
+
+ # Execute the supplied block while holding an exclusive lock,
+ # preventing any other thread from being inside a #run_interlock
+ # block at the same time.
+ def self.load_interlock
+ Dependencies.interlock.loading { yield }
+ end
+
+ # Execute the supplied block while holding an exclusive lock,
+ # preventing any other thread from being inside a #run_interlock
+ # block at the same time.
+ def self.unload_interlock
+ Dependencies.interlock.unloading { yield }
+ end
+
+ # :nodoc:
+
+ # Should we turn on Ruby warnings on the first load of dependent files?
+ mattr_accessor :warnings_on_first_load, default: false
+
+ # All files ever loaded.
+ mattr_accessor :history, default: Set.new
+
+ # All files currently loaded.
+ mattr_accessor :loaded, default: Set.new
+
+ # Stack of files being loaded.
+ mattr_accessor :loading, default: []
+
+ # Should we load files or require them?
+ mattr_accessor :mechanism, default: ENV["NO_RELOAD"] ? :require : :load
+
+ # The set of directories from which we may automatically load files. Files
+ # under these directories will be reloaded on each request in development mode,
+ # unless the directory also appears in autoload_once_paths.
+ mattr_accessor :autoload_paths, default: []
+
+ # The set of directories from which automatically loaded constants are loaded
+ # only once. All directories in this set must also be present in +autoload_paths+.
+ mattr_accessor :autoload_once_paths, default: []
+
+ # An array of qualified constant names that have been loaded. Adding a name
+ # to this array will cause it to be unloaded the next time Dependencies are
+ # cleared.
+ mattr_accessor :autoloaded_constants, default: []
+
+ # An array of constant names that need to be unloaded on every request. Used
+ # to allow arbitrary constants to be marked for unloading.
+ mattr_accessor :explicitly_unloadable_constants, default: []
+
+ # The WatchStack keeps a stack of the modules being watched as files are
+ # loaded. If a file in the process of being loaded (parent.rb) triggers the
+ # load of another file (child.rb) the stack will ensure that child.rb
+ # handles the new constants.
+ #
+ # If child.rb is being autoloaded, its constants will be added to
+ # autoloaded_constants. If it was being required, they will be discarded.
+ #
+ # This is handled by walking back up the watch stack and adding the constants
+ # found by child.rb to the list of original constants in parent.rb.
+ class WatchStack
+ include Enumerable
+
+ # @watching is a stack of lists of constants being watched. For instance,
+ # if parent.rb is autoloaded, the stack will look like [[Object]]. If
+ # parent.rb then requires namespace/child.rb, the stack will look like
+ # [[Object], [Namespace]].
+
+ attr_reader :watching
+
+ def initialize
+ @watching = []
+ @stack = Hash.new { |h, k| h[k] = [] }
+ end
+
+ def each(&block)
+ @stack.each(&block)
+ end
+
+ def watching?
+ !@watching.empty?
+ end
+
+ # Returns a list of new constants found since the last call to
+ # watch_namespaces.
+ def new_constants
+ constants = []
+
+ # Grab the list of namespaces that we're looking for new constants under
+ @watching.last.each do |namespace|
+ # Retrieve the constants that were present under the namespace when watch_namespaces
+ # was originally called
+ original_constants = @stack[namespace].last
+
+ mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
+ next unless mod.is_a?(Module)
+
+ # Get a list of the constants that were added
+ new_constants = mod.constants(false) - original_constants
+
+ # @stack[namespace] returns an Array of the constants that are being evaluated
+ # for that namespace. For instance, if parent.rb requires child.rb, the first
+ # element of @stack[Object] will be an Array of the constants that were present
+ # before parent.rb was required. The second element will be an Array of the
+ # constants that were present before child.rb was required.
+ @stack[namespace].each do |namespace_constants|
+ namespace_constants.concat(new_constants)
+ end
+
+ # Normalize the list of new constants, and add them to the list we will return
+ new_constants.each do |suffix|
+ constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
+ end
+ end
+ constants
+ ensure
+ # A call to new_constants is always called after a call to watch_namespaces
+ pop_modules(@watching.pop)
+ end
+
+ # Add a set of modules to the watch stack, remembering the initial
+ # constants.
+ def watch_namespaces(namespaces)
+ @watching << namespaces.map do |namespace|
+ module_name = Dependencies.to_constant_name(namespace)
+ original_constants = Dependencies.qualified_const_defined?(module_name) ?
+ Inflector.constantize(module_name).constants(false) : []
+
+ @stack[module_name] << original_constants
+ module_name
+ end
+ end
+
+ private
+ def pop_modules(modules)
+ modules.each { |mod| @stack[mod].pop }
+ end
+ end
+
+ # An internal stack used to record which constants are loaded by any block.
+ mattr_accessor :constant_watch_stack, default: WatchStack.new
+
+ # Module includes this module.
+ module ModuleConstMissing #:nodoc:
+ def self.append_features(base)
+ base.class_eval do
+ # Emulate #exclude via an ivar
+ return if defined?(@_const_missing) && @_const_missing
+ @_const_missing = instance_method(:const_missing)
+ remove_method(:const_missing)
+ end
+ super
+ end
+
+ def self.exclude_from(base)
+ base.class_eval do
+ define_method :const_missing, @_const_missing
+ @_const_missing = nil
+ end
+ end
+
+ def const_missing(const_name)
+ from_mod = anonymous? ? guess_for_anonymous(const_name) : self
+ Dependencies.load_missing_constant(from_mod, const_name)
+ end
+
+ # We assume that the name of the module reflects the nesting
+ # (unless it can be proven that is not the case) and the path to the file
+ # that defines the constant. Anonymous modules cannot follow these
+ # conventions and therefore we assume that the user wants to refer to a
+ # top-level constant.
+ def guess_for_anonymous(const_name)
+ if Object.const_defined?(const_name)
+ raise NameError.new "#{const_name} cannot be autoloaded from an anonymous class or module", const_name
+ else
+ Object
+ end
+ end
+
+ def unloadable(const_desc = self)
+ super(const_desc)
+ end
+ end
+
+ # Object includes this module.
+ module Loadable #:nodoc:
+ def self.exclude_from(base)
+ base.class_eval do
+ define_method(:load, Kernel.instance_method(:load))
+ private :load
+ end
+ end
+
+ def require_or_load(file_name)
+ Dependencies.require_or_load(file_name)
+ end
+
+ # :doc:
+
+ # Interprets a file using mechanism and marks its defined
+ # constants as autoloaded. file_name can be either a string or
+ # respond to to_path.
+ #
+ # Use this method in code that absolutely needs a certain constant to be
+ # defined at that point. A typical use case is to make constant name
+ # resolution deterministic for constants with the same relative name in
+ # different namespaces whose evaluation would depend on load order
+ # otherwise.
+ def require_dependency(file_name, message = "No such file to load -- %s.rb")
+ file_name = file_name.to_path if file_name.respond_to?(:to_path)
+ unless file_name.is_a?(String)
+ raise ArgumentError, "the file name must either be a String or implement #to_path -- you passed #{file_name.inspect}"
+ end
+
+ Dependencies.depend_on(file_name, message)
+ end
+
+ # :nodoc:
+
+ def load_dependency(file)
+ if Dependencies.load? && Dependencies.constant_watch_stack.watching?
+ descs = Dependencies.constant_watch_stack.watching.flatten.uniq
+
+ Dependencies.new_constants_in(*descs) { yield }
+ else
+ yield
+ end
+ rescue Exception => exception # errors from loading file
+ exception.blame_file! file if exception.respond_to? :blame_file!
+ raise
+ end
+
+ # Mark the given constant as unloadable. Unloadable constants are removed
+ # each time dependencies are cleared.
+ #
+ # Note that marking a constant for unloading need only be done once. Setup
+ # or init scripts may list each unloadable constant that may need unloading;
+ # each constant will be removed for every subsequent clear, as opposed to
+ # for the first clear.
+ #
+ # The provided constant descriptor may be a (non-anonymous) module or class,
+ # or a qualified constant name as a string or symbol.
+ #
+ # Returns +true+ if the constant was not previously marked for unloading,
+ # +false+ otherwise.
+ def unloadable(const_desc)
+ Dependencies.mark_for_unload const_desc
+ end
+
+ private
+
+ def load(file, wrap = false)
+ result = false
+ load_dependency(file) { result = super }
+ result
+ end
+
+ def require(file)
+ result = false
+ load_dependency(file) { result = super }
+ result
+ end
+ end
+
+ # Exception file-blaming.
+ module Blamable #:nodoc:
+ def blame_file!(file)
+ (@blamed_files ||= []).unshift file
+ end
+
+ def blamed_files
+ @blamed_files ||= []
+ end
+
+ def describe_blame
+ return nil if blamed_files.empty?
+ "This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
+ end
+
+ def copy_blame!(exc)
+ @blamed_files = exc.blamed_files.clone
+ self
+ end
+ end
+
+ def hook!
+ Object.class_eval { include Loadable }
+ Module.class_eval { include ModuleConstMissing }
+ Exception.class_eval { include Blamable }
+ end
+
+ def unhook!
+ ModuleConstMissing.exclude_from(Module)
+ Loadable.exclude_from(Object)
+ end
+
+ def load?
+ mechanism == :load
+ end
+
+ def depend_on(file_name, message = "No such file to load -- %s.rb")
+ path = search_for_file(file_name)
+ require_or_load(path || file_name)
+ rescue LoadError => load_error
+ if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
+ load_error.message.replace(message % file_name)
+ load_error.copy_blame!(load_error)
+ end
+ raise
+ end
+
+ def clear
+ Dependencies.unload_interlock do
+ loaded.clear
+ loading.clear
+ remove_unloadable_constants!
+ end
+ end
+
+ def require_or_load(file_name, const_path = nil)
+ file_name = $` if file_name =~ /\.rb\z/
+ expanded = File.expand_path(file_name)
+ return if loaded.include?(expanded)
+
+ Dependencies.load_interlock do
+ # Maybe it got loaded while we were waiting for our lock:
+ return if loaded.include?(expanded)
+
+ # Record that we've seen this file *before* loading it to avoid an
+ # infinite loop with mutual dependencies.
+ loaded << expanded
+ loading << expanded
+
+ begin
+ if load?
+ # Enable warnings if this file has not been loaded before and
+ # warnings_on_first_load is set.
+ load_args = ["#{file_name}.rb"]
+ load_args << const_path unless const_path.nil?
+
+ if !warnings_on_first_load || history.include?(expanded)
+ result = load_file(*load_args)
+ else
+ enable_warnings { result = load_file(*load_args) }
+ end
+ else
+ result = require file_name
+ end
+ rescue Exception
+ loaded.delete expanded
+ raise
+ ensure
+ loading.pop
+ end
+
+ # Record history *after* loading so first load gets warnings.
+ history << expanded
+ result
+ end
+ end
+
+ # Is the provided constant path defined?
+ def qualified_const_defined?(path)
+ Object.const_defined?(path, false)
+ end
+
+ # Given +path+, a filesystem path to a ruby file, return an array of
+ # constant paths which would cause Dependencies to attempt to load this
+ # file.
+ def loadable_constants_for_path(path, bases = autoload_paths)
+ path = $` if path =~ /\.rb\z/
+ expanded_path = File.expand_path(path)
+ paths = []
+
+ bases.each do |root|
+ expanded_root = File.expand_path(root)
+ next unless expanded_path.start_with?(expanded_root)
+
+ root_size = expanded_root.size
+ next if expanded_path[root_size] != ?/.freeze
+
+ nesting = expanded_path[(root_size + 1)..-1]
+ paths << nesting.camelize unless nesting.blank?
+ end
+
+ paths.uniq!
+ paths
+ end
+
+ # Search for a file in autoload_paths matching the provided suffix.
+ def search_for_file(path_suffix)
+ path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb".freeze)
+
+ autoload_paths.each do |root|
+ path = File.join(root, path_suffix)
+ return path if File.file? path
+ end
+ nil # Gee, I sure wish we had first_match ;-)
+ end
+
+ # Does the provided path_suffix correspond to an autoloadable module?
+ # Instead of returning a boolean, the autoload base for this module is
+ # returned.
+ def autoloadable_module?(path_suffix)
+ autoload_paths.each do |load_path|
+ return load_path if File.directory? File.join(load_path, path_suffix)
+ end
+ nil
+ end
+
+ def load_once_path?(path)
+ # to_s works around a ruby issue where String#starts_with?(Pathname)
+ # will raise a TypeError: no implicit conversion of Pathname into String
+ autoload_once_paths.any? { |base| path.starts_with? base.to_s }
+ end
+
+ # Attempt to autoload the provided module name by searching for a directory
+ # matching the expected path suffix. If found, the module is created and
+ # assigned to +into+'s constants with the name +const_name+. Provided that
+ # the directory was loaded from a reloadable base path, it is added to the
+ # set of constants that are to be unloaded.
+ def autoload_module!(into, const_name, qualified_name, path_suffix)
+ return nil unless base_path = autoloadable_module?(path_suffix)
+ mod = Module.new
+ into.const_set const_name, mod
+ autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
+ autoloaded_constants.uniq!
+ mod
+ end
+
+ # Load the file at the provided path. +const_paths+ is a set of qualified
+ # constant names. When loading the file, Dependencies will watch for the
+ # addition of these constants. Each that is defined will be marked as
+ # autoloaded, and will be removed when Dependencies.clear is next called.
+ #
+ # If the second parameter is left off, then Dependencies will construct a
+ # set of names that the file at +path+ may define. See
+ # +loadable_constants_for_path+ for more details.
+ def load_file(path, const_paths = loadable_constants_for_path(path))
+ const_paths = [const_paths].compact unless const_paths.is_a? Array
+ parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object }
+
+ result = nil
+ newly_defined_paths = new_constants_in(*parent_paths) do
+ result = Kernel.load path
+ end
+
+ autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
+ autoloaded_constants.uniq!
+ result
+ end
+
+ # Returns the constant path for the provided parent and constant name.
+ def qualified_name_for(mod, name)
+ mod_name = to_constant_name mod
+ mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
+ end
+
+ # Load the constant named +const_name+ which is missing from +from_mod+. If
+ # it is not possible to load the constant into from_mod, try its parent
+ # module using +const_missing+.
+ def load_missing_constant(from_mod, const_name)
+ unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
+ raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
+ end
+
+ qualified_name = qualified_name_for from_mod, const_name
+ path_suffix = qualified_name.underscore
+
+ file_path = search_for_file(path_suffix)
+
+ if file_path
+ expanded = File.expand_path(file_path)
+ expanded.sub!(/\.rb\z/, "".freeze)
+
+ if loading.include?(expanded)
+ raise "Circular dependency detected while autoloading constant #{qualified_name}"
+ else
+ require_or_load(expanded, qualified_name)
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
+ return from_mod.const_get(const_name)
+ end
+ elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
+ return mod
+ elsif (parent = from_mod.parent) && parent != from_mod &&
+ ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
+ # If our parents do not have a constant named +const_name+ then we are free
+ # to attempt to load upwards. If they do have such a constant, then this
+ # const_missing must be due to from_mod::const_name, which should not
+ # return constants from from_mod's parents.
+ begin
+ # Since Ruby does not pass the nesting at the point the unknown
+ # constant triggered the callback we cannot fully emulate constant
+ # name lookup and need to make a trade-off: we are going to assume
+ # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
+ # though it might not be. Counterexamples are
+ #
+ # class Foo::Bar
+ # Module.nesting # => [Foo::Bar]
+ # end
+ #
+ # or
+ #
+ # module M::N
+ # module S::T
+ # Module.nesting # => [S::T, M::N]
+ # end
+ # end
+ #
+ # for example.
+ return parent.const_missing(const_name)
+ rescue NameError => e
+ raise unless e.missing_name? qualified_name_for(parent, const_name)
+ end
+ end
+
+ name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
+ name_error.set_backtrace(caller.reject { |l| l.starts_with? __FILE__ })
+ raise name_error
+ end
+
+ # Remove the constants that have been autoloaded, and those that have been
+ # marked for unloading. Before each constant is removed a callback is sent
+ # to its class/module if it implements +before_remove_const+.
+ #
+ # The callback implementation should be restricted to cleaning up caches, etc.
+ # as the environment will be in an inconsistent state, e.g. other constants
+ # may have already been unloaded and not accessible.
+ def remove_unloadable_constants!
+ autoloaded_constants.each { |const| remove_constant const }
+ autoloaded_constants.clear
+ Reference.clear!
+ explicitly_unloadable_constants.each { |const| remove_constant const }
+ end
+
+ class ClassCache
+ def initialize
+ @store = Concurrent::Map.new
+ end
+
+ def empty?
+ @store.empty?
+ end
+
+ def key?(key)
+ @store.key?(key)
+ end
+
+ def get(key)
+ key = key.name if key.respond_to?(:name)
+ @store[key] ||= Inflector.constantize(key)
+ end
+ alias :[] :get
+
+ def safe_get(key)
+ key = key.name if key.respond_to?(:name)
+ @store[key] ||= Inflector.safe_constantize(key)
+ end
+
+ def store(klass)
+ return self unless klass.respond_to?(:name)
+ raise(ArgumentError, "anonymous classes cannot be cached") if klass.name.empty?
+ @store[klass.name] = klass
+ self
+ end
+
+ def clear!
+ @store.clear
+ end
+ end
+
+ Reference = ClassCache.new
+
+ # Store a reference to a class +klass+.
+ def reference(klass)
+ Reference.store klass
+ end
+
+ # Get the reference for class named +name+.
+ # Raises an exception if referenced class does not exist.
+ def constantize(name)
+ Reference.get(name)
+ end
+
+ # Get the reference for class named +name+ if one exists.
+ # Otherwise returns +nil+.
+ def safe_constantize(name)
+ Reference.safe_get(name)
+ end
+
+ # Determine if the given constant has been automatically loaded.
+ def autoloaded?(desc)
+ return false if desc.is_a?(Module) && desc.anonymous?
+ name = to_constant_name desc
+ return false unless qualified_const_defined?(name)
+ autoloaded_constants.include?(name)
+ end
+
+ # Will the provided constant descriptor be unloaded?
+ def will_unload?(const_desc)
+ autoloaded?(const_desc) ||
+ explicitly_unloadable_constants.include?(to_constant_name(const_desc))
+ end
+
+ # Mark the provided constant name for unloading. This constant will be
+ # unloaded on each request, not just the next one.
+ def mark_for_unload(const_desc)
+ name = to_constant_name const_desc
+ if explicitly_unloadable_constants.include? name
+ false
+ else
+ explicitly_unloadable_constants << name
+ true
+ end
+ end
+
+ # Run the provided block and detect the new constants that were loaded during
+ # its execution. Constants may only be regarded as 'new' once -- so if the
+ # block calls +new_constants_in+ again, then the constants defined within the
+ # inner call will not be reported in this one.
+ #
+ # If the provided block does not run to completion, and instead raises an
+ # exception, any new constants are regarded as being only partially defined
+ # and will be removed immediately.
+ def new_constants_in(*descs)
+ constant_watch_stack.watch_namespaces(descs)
+ success = false
+
+ begin
+ yield # Now yield to the code that is to define new constants.
+ success = true
+ ensure
+ new_constants = constant_watch_stack.new_constants
+
+ return new_constants if success
+
+ # Remove partially loaded constants.
+ new_constants.each { |c| remove_constant(c) }
+ end
+ end
+
+ # Convert the provided const desc to a qualified constant name (as a string).
+ # A module, class, symbol, or string may be provided.
+ def to_constant_name(desc) #:nodoc:
+ case desc
+ when String then desc.sub(/^::/, "")
+ when Symbol then desc.to_s
+ when Module
+ desc.name ||
+ raise(ArgumentError, "Anonymous modules have no name to be referenced by")
+ else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
+ end
+ end
+
+ def remove_constant(const) #:nodoc:
+ # Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
+ normalized = const.to_s.sub(/\A::/, "")
+ normalized.sub!(/\A(Object::)+/, "")
+
+ constants = normalized.split("::")
+ to_remove = constants.pop
+
+ # Remove the file path from the loaded list.
+ file_path = search_for_file(const.underscore)
+ if file_path
+ expanded = File.expand_path(file_path)
+ expanded.sub!(/\.rb\z/, "")
+ loaded.delete(expanded)
+ end
+
+ if constants.empty?
+ parent = Object
+ else
+ # This method is robust to non-reachable constants.
+ #
+ # Non-reachable constants may be passed if some of the parents were
+ # autoloaded and already removed. It is easier to do a sanity check
+ # here than require the caller to be clever. We check the parent
+ # rather than the very const argument because we do not want to
+ # trigger Kernel#autoloads, see the comment below.
+ parent_name = constants.join("::")
+ return unless qualified_const_defined?(parent_name)
+ parent = constantize(parent_name)
+ end
+
+ # In an autoloaded user.rb like this
+ #
+ # autoload :Foo, 'foo'
+ #
+ # class User < ActiveRecord::Base
+ # end
+ #
+ # we correctly register "Foo" as being autoloaded. But if the app does
+ # not use the "Foo" constant we need to be careful not to trigger
+ # loading "foo.rb" ourselves. While #const_defined? and #const_get? do
+ # require the file, #autoload? and #remove_const don't.
+ #
+ # We are going to remove the constant nonetheless ---which exists as
+ # far as Ruby is concerned--- because if the user removes the macro
+ # call from a class or module that were not autoloaded, as in the
+ # example above with Object, accessing to that constant must err.
+ unless parent.autoload?(to_remove)
+ begin
+ constantized = parent.const_get(to_remove, false)
+ rescue NameError
+ # The constant is no longer reachable, just skip it.
+ return
+ else
+ constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
+ end
+ end
+
+ begin
+ parent.instance_eval { remove_const to_remove }
+ rescue NameError
+ # The constant is no longer reachable, just skip it.
+ end
+ end
+ end
+end
+
+ActiveSupport::Dependencies.hook!
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/dependencies/autoload.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/dependencies/autoload.rb
new file mode 100644
index 0000000..1cee85d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/dependencies/autoload.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require "active_support/inflector/methods"
+
+module ActiveSupport
+ # Autoload and eager load conveniences for your library.
+ #
+ # This module allows you to define autoloads based on
+ # Rails conventions (i.e. no need to define the path
+ # it is automatically guessed based on the filename)
+ # and also define a set of constants that needs to be
+ # eager loaded:
+ #
+ # module MyLib
+ # extend ActiveSupport::Autoload
+ #
+ # autoload :Model
+ #
+ # eager_autoload do
+ # autoload :Cache
+ # end
+ # end
+ #
+ # Then your library can be eager loaded by simply calling:
+ #
+ # MyLib.eager_load!
+ module Autoload
+ def self.extended(base) # :nodoc:
+ base.class_eval do
+ @_autoloads = {}
+ @_under_path = nil
+ @_at_path = nil
+ @_eager_autoload = false
+ end
+ end
+
+ def autoload(const_name, path = @_at_path)
+ unless path
+ full = [name, @_under_path, const_name.to_s].compact.join("::")
+ path = Inflector.underscore(full)
+ end
+
+ if @_eager_autoload
+ @_autoloads[const_name] = path
+ end
+
+ super const_name, path
+ end
+
+ def autoload_under(path)
+ @_under_path, old_path = path, @_under_path
+ yield
+ ensure
+ @_under_path = old_path
+ end
+
+ def autoload_at(path)
+ @_at_path, old_path = path, @_at_path
+ yield
+ ensure
+ @_at_path = old_path
+ end
+
+ def eager_autoload
+ old_eager, @_eager_autoload = @_eager_autoload, true
+ yield
+ ensure
+ @_eager_autoload = old_eager
+ end
+
+ def eager_load!
+ @_autoloads.each_value { |file| require file }
+ end
+
+ def autoloads
+ @_autoloads
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/dependencies/interlock.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/dependencies/interlock.rb
new file mode 100644
index 0000000..948be75
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/dependencies/interlock.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require "active_support/concurrency/share_lock"
+
+module ActiveSupport #:nodoc:
+ module Dependencies #:nodoc:
+ class Interlock
+ def initialize # :nodoc:
+ @lock = ActiveSupport::Concurrency::ShareLock.new
+ end
+
+ def loading
+ @lock.exclusive(purpose: :load, compatible: [:load], after_compatible: [:load]) do
+ yield
+ end
+ end
+
+ def unloading
+ @lock.exclusive(purpose: :unload, compatible: [:load, :unload], after_compatible: [:load, :unload]) do
+ yield
+ end
+ end
+
+ def start_unloading
+ @lock.start_exclusive(purpose: :unload, compatible: [:load, :unload])
+ end
+
+ def done_unloading
+ @lock.stop_exclusive(compatible: [:load, :unload])
+ end
+
+ def start_running
+ @lock.start_sharing
+ end
+
+ def done_running
+ @lock.stop_sharing
+ end
+
+ def running
+ @lock.sharing do
+ yield
+ end
+ end
+
+ def permit_concurrent_loads
+ @lock.yield_shares(compatible: [:load]) do
+ yield
+ end
+ end
+
+ def raw_state(&block) # :nodoc:
+ @lock.raw_state(&block)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation.rb
new file mode 100644
index 0000000..a1ad2ca
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require "singleton"
+
+module ActiveSupport
+ # \Deprecation specifies the API used by Rails to deprecate methods, instance
+ # variables, objects and constants.
+ class Deprecation
+ # active_support.rb sets an autoload for ActiveSupport::Deprecation.
+ #
+ # If these requires were at the top of the file the constant would not be
+ # defined by the time their files were loaded. Since some of them reopen
+ # ActiveSupport::Deprecation its autoload would be triggered, resulting in
+ # a circular require warning for active_support/deprecation.rb.
+ #
+ # So, we define the constant first, and load dependencies later.
+ require "active_support/deprecation/instance_delegator"
+ require "active_support/deprecation/behaviors"
+ require "active_support/deprecation/reporting"
+ require "active_support/deprecation/constant_accessor"
+ require "active_support/deprecation/method_wrappers"
+ require "active_support/deprecation/proxy_wrappers"
+ require "active_support/core_ext/module/deprecation"
+
+ include Singleton
+ include InstanceDelegator
+ include Behavior
+ include Reporting
+ include MethodWrapper
+
+ # The version number in which the deprecated behavior will be removed, by default.
+ attr_accessor :deprecation_horizon
+
+ # It accepts two parameters on initialization. The first is a version of library
+ # and the second is a library name.
+ #
+ # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
+ def initialize(deprecation_horizon = "6.0", gem_name = "Rails")
+ self.gem_name = gem_name
+ self.deprecation_horizon = deprecation_horizon
+ # By default, warnings are not silenced and debugging is off.
+ self.silenced = false
+ self.debug = false
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/behaviors.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/behaviors.rb
new file mode 100644
index 0000000..3abd25a
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/behaviors.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require "active_support/notifications"
+
+module ActiveSupport
+ # Raised when ActiveSupport::Deprecation::Behavior#behavior is set with :raise.
+ # You would set :raise, as a behavior to raise errors and proactively report exceptions from deprecations.
+ class DeprecationException < StandardError
+ end
+
+ class Deprecation
+ # Default warning behaviors per Rails.env.
+ DEFAULT_BEHAVIORS = {
+ raise: ->(message, callstack, deprecation_horizon, gem_name) {
+ e = DeprecationException.new(message)
+ e.set_backtrace(callstack.map(&:to_s))
+ raise e
+ },
+
+ stderr: ->(message, callstack, deprecation_horizon, gem_name) {
+ $stderr.puts(message)
+ $stderr.puts callstack.join("\n ") if debug
+ },
+
+ log: ->(message, callstack, deprecation_horizon, gem_name) {
+ logger =
+ if defined?(Rails.logger) && Rails.logger
+ Rails.logger
+ else
+ require "active_support/logger"
+ ActiveSupport::Logger.new($stderr)
+ end
+ logger.warn message
+ logger.debug callstack.join("\n ") if debug
+ },
+
+ notify: ->(message, callstack, deprecation_horizon, gem_name) {
+ notification_name = "deprecation.#{gem_name.underscore.tr('/', '_')}"
+ ActiveSupport::Notifications.instrument(notification_name,
+ message: message,
+ callstack: callstack,
+ gem_name: gem_name,
+ deprecation_horizon: deprecation_horizon)
+ },
+
+ silence: ->(message, callstack, deprecation_horizon, gem_name) {},
+ }
+
+ # Behavior module allows to determine how to display deprecation messages.
+ # You can create a custom behavior or set any from the +DEFAULT_BEHAVIORS+
+ # constant. Available behaviors are:
+ #
+ # [+raise+] Raise ActiveSupport::DeprecationException.
+ # [+stderr+] Log all deprecation warnings to +$stderr+.
+ # [+log+] Log all deprecation warnings to +Rails.logger+.
+ # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
+ # [+silence+] Do nothing.
+ #
+ # Setting behaviors only affects deprecations that happen after boot time.
+ # For more information you can read the documentation of the +behavior=+ method.
+ module Behavior
+ # Whether to print a backtrace along with the warning.
+ attr_accessor :debug
+
+ # Returns the current behavior or if one isn't set, defaults to +:stderr+.
+ def behavior
+ @behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
+ end
+
+ # Sets the behavior to the specified value. Can be a single value, array,
+ # or an object that responds to +call+.
+ #
+ # Available behaviors:
+ #
+ # [+raise+] Raise ActiveSupport::DeprecationException.
+ # [+stderr+] Log all deprecation warnings to +$stderr+.
+ # [+log+] Log all deprecation warnings to +Rails.logger+.
+ # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
+ # [+silence+] Do nothing.
+ #
+ # Setting behaviors only affects deprecations that happen after boot time.
+ # Deprecation warnings raised by gems are not affected by this setting
+ # because they happen before Rails boots up.
+ #
+ # ActiveSupport::Deprecation.behavior = :stderr
+ # ActiveSupport::Deprecation.behavior = [:stderr, :log]
+ # ActiveSupport::Deprecation.behavior = MyCustomHandler
+ # ActiveSupport::Deprecation.behavior = ->(message, callstack, deprecation_horizon, gem_name) {
+ # # custom stuff
+ # }
+ def behavior=(behavior)
+ @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
+ end
+
+ private
+ def arity_coerce(behavior)
+ unless behavior.respond_to?(:call)
+ raise ArgumentError, "#{behavior.inspect} is not a valid deprecation behavior."
+ end
+
+ if behavior.arity == 4 || behavior.arity == -1
+ behavior
+ else
+ -> message, callstack, _, _ { behavior.call(message, callstack) }
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/constant_accessor.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/constant_accessor.rb
new file mode 100644
index 0000000..1ed0015
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/constant_accessor.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ class Deprecation
+ # DeprecatedConstantAccessor transforms a constant into a deprecated one by
+ # hooking +const_missing+.
+ #
+ # It takes the names of an old (deprecated) constant and of a new constant
+ # (both in string form) and optionally a deprecator. The deprecator defaults
+ # to +ActiveSupport::Deprecator+ if none is specified.
+ #
+ # The deprecated constant now returns the same object as the new one rather
+ # than a proxy object, so it can be used transparently in +rescue+ blocks
+ # etc.
+ #
+ # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto)
+ #
+ # # (In a later update, the original implementation of `PLANETS` has been removed.)
+ #
+ # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
+ # include ActiveSupport::Deprecation::DeprecatedConstantAccessor
+ # deprecate_constant 'PLANETS', 'PLANETS_POST_2006'
+ #
+ # PLANETS.map { |planet| planet.capitalize }
+ # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
+ # (Backtrace information…)
+ # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
+ module DeprecatedConstantAccessor
+ def self.included(base)
+ require "active_support/inflector/methods"
+
+ extension = Module.new do
+ def const_missing(missing_const_name)
+ if class_variable_defined?(:@@_deprecated_constants)
+ if (replacement = class_variable_get(:@@_deprecated_constants)[missing_const_name.to_s])
+ replacement[:deprecator].warn(replacement[:message] || "#{name}::#{missing_const_name} is deprecated! Use #{replacement[:new]} instead.", caller_locations)
+ return ActiveSupport::Inflector.constantize(replacement[:new].to_s)
+ end
+ end
+ super
+ end
+
+ def deprecate_constant(const_name, new_constant, message: nil, deprecator: ActiveSupport::Deprecation.instance)
+ class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants)
+ class_variable_get(:@@_deprecated_constants)[const_name.to_s] = { new: new_constant, message: message, deprecator: deprecator }
+ end
+ end
+ base.singleton_class.prepend extension
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/instance_delegator.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/instance_delegator.rb
new file mode 100644
index 0000000..8beda37
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/instance_delegator.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/kernel/singleton_class"
+require "active_support/core_ext/module/delegation"
+
+module ActiveSupport
+ class Deprecation
+ module InstanceDelegator # :nodoc:
+ def self.included(base)
+ base.extend(ClassMethods)
+ base.singleton_class.prepend(OverrideDelegators)
+ base.public_class_method :new
+ end
+
+ module ClassMethods # :nodoc:
+ def include(included_module)
+ included_module.instance_methods.each { |m| method_added(m) }
+ super
+ end
+
+ def method_added(method_name)
+ singleton_class.delegate(method_name, to: :instance)
+ end
+ end
+
+ module OverrideDelegators # :nodoc:
+ def warn(message = nil, callstack = nil)
+ callstack ||= caller_locations(2)
+ super
+ end
+
+ def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
+ caller_backtrace ||= caller_locations(2)
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/method_wrappers.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/method_wrappers.rb
new file mode 100644
index 0000000..3c5d9ce
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/method_wrappers.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/aliasing"
+require "active_support/core_ext/array/extract_options"
+
+module ActiveSupport
+ class Deprecation
+ module MethodWrapper
+ # Declare that a method has been deprecated.
+ #
+ # class Fred
+ # def aaa; end
+ # def bbb; end
+ # def ccc; end
+ # def ddd; end
+ # def eee; end
+ # end
+ #
+ # Using the default deprecator:
+ # ActiveSupport::Deprecation.deprecate_methods(Fred, :aaa, bbb: :zzz, ccc: 'use Bar#ccc instead')
+ # # => Fred
+ #
+ # Fred.new.aaa
+ # # DEPRECATION WARNING: aaa is deprecated and will be removed from Rails 5.1. (called from irb_binding at (irb):10)
+ # # => nil
+ #
+ # Fred.new.bbb
+ # # DEPRECATION WARNING: bbb is deprecated and will be removed from Rails 5.1 (use zzz instead). (called from irb_binding at (irb):11)
+ # # => nil
+ #
+ # Fred.new.ccc
+ # # DEPRECATION WARNING: ccc is deprecated and will be removed from Rails 5.1 (use Bar#ccc instead). (called from irb_binding at (irb):12)
+ # # => nil
+ #
+ # Passing in a custom deprecator:
+ # custom_deprecator = ActiveSupport::Deprecation.new('next-release', 'MyGem')
+ # ActiveSupport::Deprecation.deprecate_methods(Fred, ddd: :zzz, deprecator: custom_deprecator)
+ # # => [:ddd]
+ #
+ # Fred.new.ddd
+ # DEPRECATION WARNING: ddd is deprecated and will be removed from MyGem next-release (use zzz instead). (called from irb_binding at (irb):15)
+ # # => nil
+ #
+ # Using a custom deprecator directly:
+ # custom_deprecator = ActiveSupport::Deprecation.new('next-release', 'MyGem')
+ # custom_deprecator.deprecate_methods(Fred, eee: :zzz)
+ # # => [:eee]
+ #
+ # Fred.new.eee
+ # DEPRECATION WARNING: eee is deprecated and will be removed from MyGem next-release (use zzz instead). (called from irb_binding at (irb):18)
+ # # => nil
+ def deprecate_methods(target_module, *method_names)
+ options = method_names.extract_options!
+ deprecator = options.delete(:deprecator) || self
+ method_names += options.keys
+ mod = Module.new
+
+ method_names.each do |method_name|
+ if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name)
+ aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ""), $1
+ with_method = "#{aliased_method}_with_deprecation#{punctuation}"
+ without_method = "#{aliased_method}_without_deprecation#{punctuation}"
+
+ target_module.send(:define_method, with_method) do |*args, &block|
+ deprecator.deprecation_warning(method_name, options[method_name])
+ send(without_method, *args, &block)
+ end
+
+ target_module.send(:alias_method, without_method, method_name)
+ target_module.send(:alias_method, method_name, with_method)
+
+ case
+ when target_module.protected_method_defined?(without_method)
+ target_module.send(:protected, method_name)
+ when target_module.private_method_defined?(without_method)
+ target_module.send(:private, method_name)
+ end
+ else
+ mod.send(:define_method, method_name) do |*args, &block|
+ deprecator.deprecation_warning(method_name, options[method_name])
+ super(*args, &block)
+ end
+ end
+ end
+
+ target_module.prepend(mod) unless mod.instance_methods(false).empty?
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/proxy_wrappers.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/proxy_wrappers.rb
new file mode 100644
index 0000000..896c0d2
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/proxy_wrappers.rb
@@ -0,0 +1,154 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/regexp"
+
+module ActiveSupport
+ class Deprecation
+ class DeprecationProxy #:nodoc:
+ def self.new(*args, &block)
+ object = args.first
+
+ return object unless object
+ super
+ end
+
+ instance_methods.each { |m| undef_method m unless /^__|^object_id$/.match?(m) }
+
+ # Don't give a deprecation warning on inspect since test/unit and error
+ # logs rely on it for diagnostics.
+ def inspect
+ target.inspect
+ end
+
+ private
+ def method_missing(called, *args, &block)
+ warn caller_locations, called, args
+ target.__send__(called, *args, &block)
+ end
+ end
+
+ # DeprecatedObjectProxy transforms an object into a deprecated one. It
+ # takes an object, a deprecation message and optionally a deprecator. The
+ # deprecator defaults to +ActiveSupport::Deprecator+ if none is specified.
+ #
+ # deprecated_object = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, "This object is now deprecated")
+ # # => #
+ #
+ # deprecated_object.to_s
+ # DEPRECATION WARNING: This object is now deprecated.
+ # (Backtrace)
+ # # => "#"
+ class DeprecatedObjectProxy < DeprecationProxy
+ def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance)
+ @object = object
+ @message = message
+ @deprecator = deprecator
+ end
+
+ private
+ def target
+ @object
+ end
+
+ def warn(callstack, called, args)
+ @deprecator.warn(@message, callstack)
+ end
+ end
+
+ # DeprecatedInstanceVariableProxy transforms an instance variable into a
+ # deprecated one. It takes an instance of a class, a method on that class
+ # and an instance variable. It optionally takes a deprecator as the last
+ # argument. The deprecator defaults to +ActiveSupport::Deprecator+ if none
+ # is specified.
+ #
+ # class Example
+ # def initialize
+ # @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request)
+ # @_request = :special_request
+ # end
+ #
+ # def request
+ # @_request
+ # end
+ #
+ # def old_request
+ # @request
+ # end
+ # end
+ #
+ # example = Example.new
+ # # => #
+ #
+ # example.old_request.to_s
+ # # => DEPRECATION WARNING: @request is deprecated! Call request.to_s instead of
+ # @request.to_s
+ # (Backtrace information…)
+ # "special_request"
+ #
+ # example.request.to_s
+ # # => "special_request"
+ class DeprecatedInstanceVariableProxy < DeprecationProxy
+ def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance)
+ @instance = instance
+ @method = method
+ @var = var
+ @deprecator = deprecator
+ end
+
+ private
+ def target
+ @instance.__send__(@method)
+ end
+
+ def warn(callstack, called, args)
+ @deprecator.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
+ end
+ end
+
+ # DeprecatedConstantProxy transforms a constant into a deprecated one. It
+ # takes the names of an old (deprecated) constant and of a new constant
+ # (both in string form) and optionally a deprecator. The deprecator defaults
+ # to +ActiveSupport::Deprecator+ if none is specified. The deprecated constant
+ # now returns the value of the new one.
+ #
+ # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto)
+ #
+ # # (In a later update, the original implementation of `PLANETS` has been removed.)
+ #
+ # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
+ # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006')
+ #
+ # PLANETS.map { |planet| planet.capitalize }
+ # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
+ # (Backtrace information…)
+ # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
+ class DeprecatedConstantProxy < DeprecationProxy
+ def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance, message: "#{old_const} is deprecated! Use #{new_const} instead.")
+ require "active_support/inflector/methods"
+
+ @old_const = old_const
+ @new_const = new_const
+ @deprecator = deprecator
+ @message = message
+ end
+
+ # Returns the class of the new constant.
+ #
+ # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
+ # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006')
+ # PLANETS.class # => Array
+ def class
+ target.class
+ end
+
+ private
+ def target
+ ActiveSupport::Inflector.constantize(@new_const.to_s)
+ end
+
+ def warn(callstack, called, args)
+ @deprecator.warn(@message, callstack)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/reporting.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/reporting.rb
new file mode 100644
index 0000000..7075b5b
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/deprecation/reporting.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+require "rbconfig"
+
+module ActiveSupport
+ class Deprecation
+ module Reporting
+ # Whether to print a message (silent mode)
+ attr_accessor :silenced
+ # Name of gem where method is deprecated
+ attr_accessor :gem_name
+
+ # Outputs a deprecation warning to the output configured by
+ # ActiveSupport::Deprecation.behavior.
+ #
+ # ActiveSupport::Deprecation.warn('something broke!')
+ # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
+ def warn(message = nil, callstack = nil)
+ return if silenced
+
+ callstack ||= caller_locations(2)
+ deprecation_message(callstack, message).tap do |m|
+ behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
+ end
+ end
+
+ # Silence deprecation warnings within the block.
+ #
+ # ActiveSupport::Deprecation.warn('something broke!')
+ # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
+ #
+ # ActiveSupport::Deprecation.silence do
+ # ActiveSupport::Deprecation.warn('something broke!')
+ # end
+ # # => nil
+ def silence
+ old_silenced, @silenced = @silenced, true
+ yield
+ ensure
+ @silenced = old_silenced
+ end
+
+ def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
+ caller_backtrace ||= caller_locations(2)
+ deprecated_method_warning(deprecated_method_name, message).tap do |msg|
+ warn(msg, caller_backtrace)
+ end
+ end
+
+ private
+ # Outputs a deprecation warning message
+ #
+ # deprecated_method_warning(:method_name)
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
+ # deprecated_method_warning(:method_name, :another_method)
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
+ # deprecated_method_warning(:method_name, "Optional message")
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
+ def deprecated_method_warning(method_name, message = nil)
+ warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
+ case message
+ when Symbol then "#{warning} (use #{message} instead)"
+ when String then "#{warning} (#{message})"
+ else warning
+ end
+ end
+
+ def deprecation_message(callstack, message = nil)
+ message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
+ "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
+ end
+
+ def deprecation_caller_message(callstack)
+ file, line, method = extract_callstack(callstack)
+ if file
+ if line && method
+ "(called from #{method} at #{file}:#{line})"
+ else
+ "(called from #{file}:#{line})"
+ end
+ end
+ end
+
+ def extract_callstack(callstack)
+ return _extract_callstack(callstack) if callstack.first.is_a? String
+
+ offending_line = callstack.find { |frame|
+ frame.absolute_path && !ignored_callstack(frame.absolute_path)
+ } || callstack.first
+
+ [offending_line.path, offending_line.lineno, offending_line.label]
+ end
+
+ def _extract_callstack(callstack)
+ warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
+ offending_line = callstack.find { |line| !ignored_callstack(line) } || callstack.first
+
+ if offending_line
+ if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
+ md.captures
+ else
+ offending_line
+ end
+ end
+ end
+
+ RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
+
+ def ignored_callstack(path)
+ path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"])
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/descendants_tracker.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/descendants_tracker.rb
new file mode 100644
index 0000000..a4cee78
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/descendants_tracker.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # This module provides an internal implementation to track descendants
+ # which is faster than iterating through ObjectSpace.
+ module DescendantsTracker
+ @@direct_descendants = {}
+
+ class << self
+ def direct_descendants(klass)
+ @@direct_descendants[klass] || []
+ end
+
+ def descendants(klass)
+ arr = []
+ accumulate_descendants(klass, arr)
+ arr
+ end
+
+ def clear
+ if defined? ActiveSupport::Dependencies
+ @@direct_descendants.each do |klass, descendants|
+ if ActiveSupport::Dependencies.autoloaded?(klass)
+ @@direct_descendants.delete(klass)
+ else
+ descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
+ end
+ end
+ else
+ @@direct_descendants.clear
+ end
+ end
+
+ # This is the only method that is not thread safe, but is only ever called
+ # during the eager loading phase.
+ def store_inherited(klass, descendant)
+ (@@direct_descendants[klass] ||= []) << descendant
+ end
+
+ private
+ def accumulate_descendants(klass, acc)
+ if direct_descendants = @@direct_descendants[klass]
+ acc.concat(direct_descendants)
+ direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
+ end
+ end
+ end
+
+ def inherited(base)
+ DescendantsTracker.store_inherited(self, base)
+ super
+ end
+
+ def direct_descendants
+ DescendantsTracker.direct_descendants(self)
+ end
+
+ def descendants
+ DescendantsTracker.descendants(self)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/digest.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/digest.rb
new file mode 100644
index 0000000..fba10fb
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/digest.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ class Digest #:nodoc:
+ class <(other)
+ if Scalar === other || Duration === other
+ value <=> other.value
+ elsif Numeric === other
+ value <=> other
+ else
+ nil
+ end
+ end
+
+ def +(other)
+ if Duration === other
+ seconds = value + other.parts[:seconds]
+ new_parts = other.parts.merge(seconds: seconds)
+ new_value = value + other.value
+
+ Duration.new(new_value, new_parts)
+ else
+ calculate(:+, other)
+ end
+ end
+
+ def -(other)
+ if Duration === other
+ seconds = value - other.parts[:seconds]
+ new_parts = other.parts.map { |part, other_value| [part, -other_value] }.to_h
+ new_parts = new_parts.merge(seconds: seconds)
+ new_value = value - other.value
+
+ Duration.new(new_value, new_parts)
+ else
+ calculate(:-, other)
+ end
+ end
+
+ def *(other)
+ if Duration === other
+ new_parts = other.parts.map { |part, other_value| [part, value * other_value] }.to_h
+ new_value = value * other.value
+
+ Duration.new(new_value, new_parts)
+ else
+ calculate(:*, other)
+ end
+ end
+
+ def /(other)
+ if Duration === other
+ value / other.value
+ else
+ calculate(:/, other)
+ end
+ end
+
+ def %(other)
+ if Duration === other
+ Duration.build(value % other.value)
+ else
+ calculate(:%, other)
+ end
+ end
+
+ private
+ def calculate(op, other)
+ if Scalar === other
+ Scalar.new(value.public_send(op, other.value))
+ elsif Numeric === other
+ Scalar.new(value.public_send(op, other))
+ else
+ raise_type_error(other)
+ end
+ end
+
+ def raise_type_error(other)
+ raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
+ end
+ end
+
+ SECONDS_PER_MINUTE = 60
+ SECONDS_PER_HOUR = 3600
+ SECONDS_PER_DAY = 86400
+ SECONDS_PER_WEEK = 604800
+ SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
+ SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days)
+
+ PARTS_IN_SECONDS = {
+ seconds: 1,
+ minutes: SECONDS_PER_MINUTE,
+ hours: SECONDS_PER_HOUR,
+ days: SECONDS_PER_DAY,
+ weeks: SECONDS_PER_WEEK,
+ months: SECONDS_PER_MONTH,
+ years: SECONDS_PER_YEAR
+ }.freeze
+
+ PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
+
+ attr_accessor :value, :parts
+
+ autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
+ autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
+
+ class << self
+ # Creates a new Duration from string formatted according to ISO 8601 Duration.
+ #
+ # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
+ # This method allows negative parts to be present in pattern.
+ # If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
+ def parse(iso8601duration)
+ parts = ISO8601Parser.new(iso8601duration).parse!
+ new(calculate_total_seconds(parts), parts)
+ end
+
+ def ===(other) #:nodoc:
+ other.is_a?(Duration)
+ rescue ::NoMethodError
+ false
+ end
+
+ def seconds(value) #:nodoc:
+ new(value, [[:seconds, value]])
+ end
+
+ def minutes(value) #:nodoc:
+ new(value * SECONDS_PER_MINUTE, [[:minutes, value]])
+ end
+
+ def hours(value) #:nodoc:
+ new(value * SECONDS_PER_HOUR, [[:hours, value]])
+ end
+
+ def days(value) #:nodoc:
+ new(value * SECONDS_PER_DAY, [[:days, value]])
+ end
+
+ def weeks(value) #:nodoc:
+ new(value * SECONDS_PER_WEEK, [[:weeks, value]])
+ end
+
+ def months(value) #:nodoc:
+ new(value * SECONDS_PER_MONTH, [[:months, value]])
+ end
+
+ def years(value) #:nodoc:
+ new(value * SECONDS_PER_YEAR, [[:years, value]])
+ end
+
+ # Creates a new Duration from a seconds value that is converted
+ # to the individual parts:
+ #
+ # ActiveSupport::Duration.build(31556952).parts # => {:years=>1}
+ # ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
+ #
+ def build(value)
+ parts = {}
+ remainder = value.to_f
+
+ PARTS.each do |part|
+ unless part == :seconds
+ part_in_seconds = PARTS_IN_SECONDS[part]
+ parts[part] = remainder.div(part_in_seconds)
+ remainder = (remainder % part_in_seconds).round(9)
+ end
+ end
+
+ parts[:seconds] = remainder
+
+ new(value, parts)
+ end
+
+ private
+
+ def calculate_total_seconds(parts)
+ parts.inject(0) do |total, (part, value)|
+ total + value * PARTS_IN_SECONDS[part]
+ end
+ end
+ end
+
+ def initialize(value, parts) #:nodoc:
+ @value, @parts = value, parts.to_h
+ @parts.default = 0
+ @parts.reject! { |k, v| v.zero? }
+ end
+
+ def coerce(other) #:nodoc:
+ if Scalar === other
+ [other, self]
+ else
+ [Scalar.new(other), self]
+ end
+ end
+
+ # Compares one Duration with another or a Numeric to this Duration.
+ # Numeric values are treated as seconds.
+ def <=>(other)
+ if Duration === other
+ value <=> other.value
+ elsif Numeric === other
+ value <=> other
+ end
+ end
+
+ # Adds another Duration or a Numeric to this Duration. Numeric values
+ # are treated as seconds.
+ def +(other)
+ if Duration === other
+ parts = @parts.dup
+ other.parts.each do |(key, value)|
+ parts[key] += value
+ end
+ Duration.new(value + other.value, parts)
+ else
+ seconds = @parts[:seconds] + other
+ Duration.new(value + other, @parts.merge(seconds: seconds))
+ end
+ end
+
+ # Subtracts another Duration or a Numeric from this Duration. Numeric
+ # values are treated as seconds.
+ def -(other)
+ self + (-other)
+ end
+
+ # Multiplies this Duration by a Numeric and returns a new Duration.
+ def *(other)
+ if Scalar === other || Duration === other
+ Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] })
+ elsif Numeric === other
+ Duration.new(value * other, parts.map { |type, number| [type, number * other] })
+ else
+ raise_type_error(other)
+ end
+ end
+
+ # Divides this Duration by a Numeric and returns a new Duration.
+ def /(other)
+ if Scalar === other
+ Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] })
+ elsif Duration === other
+ value / other.value
+ elsif Numeric === other
+ Duration.new(value / other, parts.map { |type, number| [type, number / other] })
+ else
+ raise_type_error(other)
+ end
+ end
+
+ # Returns the modulo of this Duration by another Duration or Numeric.
+ # Numeric values are treated as seconds.
+ def %(other)
+ if Duration === other || Scalar === other
+ Duration.build(value % other.value)
+ elsif Numeric === other
+ Duration.build(value % other)
+ else
+ raise_type_error(other)
+ end
+ end
+
+ def -@ #:nodoc:
+ Duration.new(-value, parts.map { |type, number| [type, -number] })
+ end
+
+ def is_a?(klass) #:nodoc:
+ Duration == klass || value.is_a?(klass)
+ end
+ alias :kind_of? :is_a?
+
+ def instance_of?(klass) # :nodoc:
+ Duration == klass || value.instance_of?(klass)
+ end
+
+ # Returns +true+ if +other+ is also a Duration instance with the
+ # same +value+, or if other == value.
+ def ==(other)
+ if Duration === other
+ other.value == value
+ else
+ other == value
+ end
+ end
+
+ # Returns the amount of seconds a duration covers as a string.
+ # For more information check to_i method.
+ #
+ # 1.day.to_s # => "86400"
+ def to_s
+ @value.to_s
+ end
+
+ # Returns the number of seconds that this Duration represents.
+ #
+ # 1.minute.to_i # => 60
+ # 1.hour.to_i # => 3600
+ # 1.day.to_i # => 86400
+ #
+ # Note that this conversion makes some assumptions about the
+ # duration of some periods, e.g. months are always 1/12 of year
+ # and years are 365.2425 days:
+ #
+ # # equivalent to (1.year / 12).to_i
+ # 1.month.to_i # => 2629746
+ #
+ # # equivalent to 365.2425.days.to_i
+ # 1.year.to_i # => 31556952
+ #
+ # In such cases, Ruby's core
+ # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
+ # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
+ # date and time arithmetic.
+ def to_i
+ @value.to_i
+ end
+
+ # Returns +true+ if +other+ is also a Duration instance, which has the
+ # same parts as this one.
+ def eql?(other)
+ Duration === other && other.value.eql?(value)
+ end
+
+ def hash
+ @value.hash
+ end
+
+ # Calculates a new Time or Date that is as far in the future
+ # as this Duration represents.
+ def since(time = ::Time.current)
+ sum(1, time)
+ end
+ alias :from_now :since
+ alias :after :since
+
+ # Calculates a new Time or Date that is as far in the past
+ # as this Duration represents.
+ def ago(time = ::Time.current)
+ sum(-1, time)
+ end
+ alias :until :ago
+ alias :before :ago
+
+ def inspect #:nodoc:
+ return "0 seconds" if parts.empty?
+
+ parts.
+ reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
+ sort_by { |unit, _ | PARTS.index(unit) }.
+ map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
+ to_sentence(locale: ::I18n.default_locale)
+ end
+
+ def as_json(options = nil) #:nodoc:
+ to_i
+ end
+
+ def init_with(coder) #:nodoc:
+ initialize(coder["value"], coder["parts"])
+ end
+
+ def encode_with(coder) #:nodoc:
+ coder.map = { "value" => @value, "parts" => @parts }
+ end
+
+ # Build ISO 8601 Duration string for this duration.
+ # The +precision+ parameter can be used to limit seconds' precision of duration.
+ def iso8601(precision: nil)
+ ISO8601Serializer.new(self, precision: precision).serialize
+ end
+
+ private
+
+ def sum(sign, time = ::Time.current)
+ parts.inject(time) do |t, (type, number)|
+ if t.acts_like?(:time) || t.acts_like?(:date)
+ if type == :seconds
+ t.since(sign * number)
+ elsif type == :minutes
+ t.since(sign * number * 60)
+ elsif type == :hours
+ t.since(sign * number * 3600)
+ else
+ t.advance(type => sign * number)
+ end
+ else
+ raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
+ end
+ end
+ end
+
+ def respond_to_missing?(method, _)
+ value.respond_to?(method)
+ end
+
+ def method_missing(method, *args, &block)
+ value.public_send(method, *args, &block)
+ end
+
+ def raise_type_error(other)
+ raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/duration/iso8601_parser.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/duration/iso8601_parser.rb
new file mode 100644
index 0000000..1847eea
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/duration/iso8601_parser.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+require "strscan"
+require "active_support/core_ext/regexp"
+
+module ActiveSupport
+ class Duration
+ # Parses a string formatted according to ISO 8601 Duration into the hash.
+ #
+ # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
+ #
+ # This parser allows negative parts to be present in pattern.
+ class ISO8601Parser # :nodoc:
+ class ParsingError < ::ArgumentError; end
+
+ PERIOD_OR_COMMA = /\.|,/
+ PERIOD = ".".freeze
+ COMMA = ",".freeze
+
+ SIGN_MARKER = /\A\-|\+|/
+ DATE_MARKER = /P/
+ TIME_MARKER = /T/
+ DATE_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(Y|M|D|W)/
+ TIME_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(H|M|S)/
+
+ DATE_TO_PART = { "Y" => :years, "M" => :months, "W" => :weeks, "D" => :days }
+ TIME_TO_PART = { "H" => :hours, "M" => :minutes, "S" => :seconds }
+
+ DATE_COMPONENTS = [:years, :months, :days]
+ TIME_COMPONENTS = [:hours, :minutes, :seconds]
+
+ attr_reader :parts, :scanner
+ attr_accessor :mode, :sign
+
+ def initialize(string)
+ @scanner = StringScanner.new(string)
+ @parts = {}
+ @mode = :start
+ @sign = 1
+ end
+
+ def parse!
+ while !finished?
+ case mode
+ when :start
+ if scan(SIGN_MARKER)
+ self.sign = (scanner.matched == "-") ? -1 : 1
+ self.mode = :sign
+ else
+ raise_parsing_error
+ end
+
+ when :sign
+ if scan(DATE_MARKER)
+ self.mode = :date
+ else
+ raise_parsing_error
+ end
+
+ when :date
+ if scan(TIME_MARKER)
+ self.mode = :time
+ elsif scan(DATE_COMPONENT)
+ parts[DATE_TO_PART[scanner[2]]] = number * sign
+ else
+ raise_parsing_error
+ end
+
+ when :time
+ if scan(TIME_COMPONENT)
+ parts[TIME_TO_PART[scanner[2]]] = number * sign
+ else
+ raise_parsing_error
+ end
+
+ end
+ end
+
+ validate!
+ parts
+ end
+
+ private
+
+ def finished?
+ scanner.eos?
+ end
+
+ # Parses number which can be a float with either comma or period.
+ def number
+ PERIOD_OR_COMMA.match?(scanner[1]) ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i
+ end
+
+ def scan(pattern)
+ scanner.scan(pattern)
+ end
+
+ def raise_parsing_error(reason = nil)
+ raise ParsingError, "Invalid ISO 8601 duration: #{scanner.string.inspect} #{reason}".strip
+ end
+
+ # Checks for various semantic errors as stated in ISO 8601 standard.
+ def validate!
+ raise_parsing_error("is empty duration") if parts.empty?
+
+ # Mixing any of Y, M, D with W is invalid.
+ if parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
+ raise_parsing_error("mixing weeks with other date parts not allowed")
+ end
+
+ # Specifying an empty T part is invalid.
+ if mode == :time && (parts.keys & TIME_COMPONENTS).empty?
+ raise_parsing_error("time part marker is present but time part is empty")
+ end
+
+ fractions = parts.values.reject(&:zero?).select { |a| (a % 1) != 0 }
+ unless fractions.empty? || (fractions.size == 1 && fractions.last == @parts.values.reject(&:zero?).last)
+ raise_parsing_error "(only last part can be fractional)"
+ end
+
+ true
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/duration/iso8601_serializer.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/duration/iso8601_serializer.rb
new file mode 100644
index 0000000..bb177ae
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/duration/iso8601_serializer.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/blank"
+require "active_support/core_ext/hash/transform_values"
+
+module ActiveSupport
+ class Duration
+ # Serializes duration to string according to ISO 8601 Duration format.
+ class ISO8601Serializer # :nodoc:
+ def initialize(duration, precision: nil)
+ @duration = duration
+ @precision = precision
+ end
+
+ # Builds and returns output string.
+ def serialize
+ parts, sign = normalize
+ return "PT0S".freeze if parts.empty?
+
+ output = "P".dup
+ output << "#{parts[:years]}Y" if parts.key?(:years)
+ output << "#{parts[:months]}M" if parts.key?(:months)
+ output << "#{parts[:weeks]}W" if parts.key?(:weeks)
+ output << "#{parts[:days]}D" if parts.key?(:days)
+ time = "".dup
+ time << "#{parts[:hours]}H" if parts.key?(:hours)
+ time << "#{parts[:minutes]}M" if parts.key?(:minutes)
+ if parts.key?(:seconds)
+ time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
+ end
+ output << "T#{time}" unless time.empty?
+ "#{sign}#{output}"
+ end
+
+ private
+
+ # Return pair of duration's parts and whole duration sign.
+ # Parts are summarized (as they can become repetitive due to addition, etc).
+ # Zero parts are removed as not significant.
+ # If all parts are negative it will negate all of them and return minus as a sign.
+ def normalize
+ parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
+ p[k] += v unless v.zero?
+ end
+ # If all parts are negative - let's make a negative duration
+ sign = ""
+ if parts.values.all? { |v| v < 0 }
+ sign = "-"
+ parts.transform_values!(&:-@)
+ end
+ [parts, sign]
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/encrypted_configuration.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/encrypted_configuration.rb
new file mode 100644
index 0000000..b17695f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/encrypted_configuration.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require "yaml"
+require "active_support/encrypted_file"
+require "active_support/ordered_options"
+require "active_support/core_ext/object/inclusion"
+require "active_support/core_ext/module/delegation"
+
+module ActiveSupport
+ class EncryptedConfiguration < EncryptedFile
+ delegate :[], :fetch, to: :config
+ delegate_missing_to :options
+
+ def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
+ super content_path: config_path, key_path: key_path,
+ env_key: env_key, raise_if_missing_key: raise_if_missing_key
+ end
+
+ # Allow a config to be started without a file present
+ def read
+ super
+ rescue ActiveSupport::EncryptedFile::MissingContentError
+ ""
+ end
+
+ def write(contents)
+ deserialize(contents)
+
+ super
+ end
+
+ def config
+ @config ||= deserialize(read).deep_symbolize_keys
+ end
+
+ private
+ def options
+ @options ||= ActiveSupport::InheritableOptions.new(config)
+ end
+
+ def serialize(config)
+ config.present? ? YAML.dump(config) : ""
+ end
+
+ def deserialize(config)
+ YAML.load(config).presence || {}
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/encrypted_file.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/encrypted_file.rb
new file mode 100644
index 0000000..c66f1b5
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/encrypted_file.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+require "pathname"
+require "active_support/message_encryptor"
+
+module ActiveSupport
+ class EncryptedFile
+ class MissingContentError < RuntimeError
+ def initialize(content_path)
+ super "Missing encrypted content file in #{content_path}."
+ end
+ end
+
+ class MissingKeyError < RuntimeError
+ def initialize(key_path:, env_key:)
+ super \
+ "Missing encryption key to decrypt file with. " +
+ "Ask your team for your master key and write it to #{key_path} or put it in the ENV['#{env_key}']."
+ end
+ end
+
+ CIPHER = "aes-128-gcm"
+
+ def self.generate_key
+ SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
+ end
+
+
+ attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
+
+ def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
+ @content_path, @key_path = Pathname.new(content_path), Pathname.new(key_path)
+ @env_key, @raise_if_missing_key = env_key, raise_if_missing_key
+ end
+
+ def key
+ read_env_key || read_key_file || handle_missing_key
+ end
+
+ def read
+ if !key.nil? && content_path.exist?
+ decrypt content_path.binread
+ else
+ raise MissingContentError, content_path
+ end
+ end
+
+ def write(contents)
+ IO.binwrite "#{content_path}.tmp", encrypt(contents)
+ FileUtils.mv "#{content_path}.tmp", content_path
+ end
+
+ def change(&block)
+ writing read, &block
+ end
+
+
+ private
+ def writing(contents)
+ tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
+ tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
+ tmp_path.binwrite contents
+
+ yield tmp_path
+
+ updated_contents = tmp_path.binread
+
+ write(updated_contents) if updated_contents != contents
+ ensure
+ FileUtils.rm(tmp_path) if tmp_path.exist?
+ end
+
+
+ def encrypt(contents)
+ encryptor.encrypt_and_sign contents
+ end
+
+ def decrypt(contents)
+ encryptor.decrypt_and_verify contents
+ end
+
+ def encryptor
+ @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
+ end
+
+
+ def read_env_key
+ ENV[env_key]
+ end
+
+ def read_key_file
+ key_path.binread.strip if key_path.exist?
+ end
+
+ def handle_missing_key
+ raise MissingKeyError, key_path: key_path, env_key: env_key if raise_if_missing_key
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/evented_file_update_checker.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/evented_file_update_checker.rb
new file mode 100644
index 0000000..97e982e
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/evented_file_update_checker.rb
@@ -0,0 +1,205 @@
+# frozen_string_literal: true
+
+require "set"
+require "pathname"
+require "concurrent/atomic/atomic_boolean"
+
+module ActiveSupport
+ # Allows you to "listen" to changes in a file system.
+ # The evented file updater does not hit disk when checking for updates
+ # instead it uses platform specific file system events to trigger a change
+ # in state.
+ #
+ # The file checker takes an array of files to watch or a hash specifying directories
+ # and file extensions to watch. It also takes a block that is called when
+ # EventedFileUpdateChecker#execute is run or when EventedFileUpdateChecker#execute_if_updated
+ # is run and there have been changes to the file system.
+ #
+ # Note: Forking will cause the first call to `updated?` to return `true`.
+ #
+ # Example:
+ #
+ # checker = ActiveSupport::EventedFileUpdateChecker.new(["/tmp/foo"]) { puts "changed" }
+ # checker.updated?
+ # # => false
+ # checker.execute_if_updated
+ # # => nil
+ #
+ # FileUtils.touch("/tmp/foo")
+ #
+ # checker.updated?
+ # # => true
+ # checker.execute_if_updated
+ # # => "changed"
+ #
+ class EventedFileUpdateChecker #:nodoc: all
+ def initialize(files, dirs = {}, &block)
+ unless block
+ raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker"
+ end
+
+ @ph = PathHelper.new
+ @files = files.map { |f| @ph.xpath(f) }.to_set
+
+ @dirs = {}
+ dirs.each do |dir, exts|
+ @dirs[@ph.xpath(dir)] = Array(exts).map { |ext| @ph.normalize_extension(ext) }
+ end
+
+ @block = block
+ @updated = Concurrent::AtomicBoolean.new(false)
+ @lcsp = @ph.longest_common_subpath(@dirs.keys)
+ @pid = Process.pid
+ @boot_mutex = Mutex.new
+
+ if (@dtw = directories_to_watch).any?
+ # Loading listen triggers warnings. These are originated by a legit
+ # usage of attr_* macros for private attributes, but adds a lot of noise
+ # to our test suite. Thus, we lazy load it and disable warnings locally.
+ silence_warnings do
+ begin
+ require "listen"
+ rescue LoadError => e
+ raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
+ end
+ end
+ end
+ boot!
+ end
+
+ def updated?
+ @boot_mutex.synchronize do
+ if @pid != Process.pid
+ boot!
+ @pid = Process.pid
+ @updated.make_true
+ end
+ end
+ @updated.true?
+ end
+
+ def execute
+ @updated.make_false
+ @block.call
+ end
+
+ def execute_if_updated
+ if updated?
+ yield if block_given?
+ execute
+ true
+ end
+ end
+
+ private
+ def boot!
+ Listen.to(*@dtw, &method(:changed)).start
+ end
+
+ def changed(modified, added, removed)
+ unless updated?
+ @updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
+ end
+ end
+
+ def watching?(file)
+ file = @ph.xpath(file)
+
+ if @files.member?(file)
+ true
+ elsif file.directory?
+ false
+ else
+ ext = @ph.normalize_extension(file.extname)
+
+ file.dirname.ascend do |dir|
+ if @dirs.fetch(dir, []).include?(ext)
+ break true
+ elsif dir == @lcsp || dir.root?
+ break false
+ end
+ end
+ end
+ end
+
+ def directories_to_watch
+ dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
+ dtw.compact!
+ dtw.uniq!
+
+ normalized_gem_paths = Gem.path.map { |path| File.join path, "" }
+ dtw = dtw.reject do |path|
+ normalized_gem_paths.any? { |gem_path| path.to_s.start_with?(gem_path) }
+ end
+
+ @ph.filter_out_descendants(dtw)
+ end
+
+ class PathHelper
+ def xpath(path)
+ Pathname.new(path).expand_path
+ end
+
+ def normalize_extension(ext)
+ ext.to_s.sub(/\A\./, "")
+ end
+
+ # Given a collection of Pathname objects returns the longest subpath
+ # common to all of them, or +nil+ if there is none.
+ def longest_common_subpath(paths)
+ return if paths.empty?
+
+ lcsp = Pathname.new(paths[0])
+
+ paths[1..-1].each do |path|
+ until ascendant_of?(lcsp, path)
+ if lcsp.root?
+ # If we get here a root directory is not an ascendant of path.
+ # This may happen if there are paths in different drives on
+ # Windows.
+ return
+ else
+ lcsp = lcsp.parent
+ end
+ end
+ end
+
+ lcsp
+ end
+
+ # Returns the deepest existing ascendant, which could be the argument itself.
+ def existing_parent(dir)
+ dir.ascend do |ascendant|
+ break ascendant if ascendant.directory?
+ end
+ end
+
+ # Filters out directories which are descendants of others in the collection (stable).
+ def filter_out_descendants(dirs)
+ return dirs if dirs.length < 2
+
+ dirs_sorted_by_nparts = dirs.sort_by { |dir| dir.each_filename.to_a.length }
+ descendants = []
+
+ until dirs_sorted_by_nparts.empty?
+ dir = dirs_sorted_by_nparts.shift
+
+ dirs_sorted_by_nparts.reject! do |possible_descendant|
+ ascendant_of?(dir, possible_descendant) && descendants << possible_descendant
+ end
+ end
+
+ # Array#- preserves order.
+ dirs - descendants
+ end
+
+ private
+
+ def ascendant_of?(base, other)
+ base != other && other.ascend do |ascendant|
+ break true if base == ascendant
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/execution_wrapper.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/execution_wrapper.rb
new file mode 100644
index 0000000..f48c586
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/execution_wrapper.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+require "active_support/callbacks"
+
+module ActiveSupport
+ class ExecutionWrapper
+ include ActiveSupport::Callbacks
+
+ Null = Object.new # :nodoc:
+ def Null.complete! # :nodoc:
+ end
+
+ define_callbacks :run
+ define_callbacks :complete
+
+ def self.to_run(*args, &block)
+ set_callback(:run, *args, &block)
+ end
+
+ def self.to_complete(*args, &block)
+ set_callback(:complete, *args, &block)
+ end
+
+ RunHook = Struct.new(:hook) do # :nodoc:
+ def before(target)
+ hook_state = target.send(:hook_state)
+ hook_state[hook] = hook.run
+ end
+ end
+
+ CompleteHook = Struct.new(:hook) do # :nodoc:
+ def before(target)
+ hook_state = target.send(:hook_state)
+ if hook_state.key?(hook)
+ hook.complete hook_state[hook]
+ end
+ end
+ alias after before
+ end
+
+ # Register an object to be invoked during both the +run+ and
+ # +complete+ steps.
+ #
+ # +hook.complete+ will be passed the value returned from +hook.run+,
+ # and will only be invoked if +run+ has previously been called.
+ # (Mostly, this means it won't be invoked if an exception occurs in
+ # a preceding +to_run+ block; all ordinary +to_complete+ blocks are
+ # invoked in that situation.)
+ def self.register_hook(hook, outer: false)
+ if outer
+ to_run RunHook.new(hook), prepend: true
+ to_complete :after, CompleteHook.new(hook)
+ else
+ to_run RunHook.new(hook)
+ to_complete CompleteHook.new(hook)
+ end
+ end
+
+ # Run this execution.
+ #
+ # Returns an instance, whose +complete!+ method *must* be invoked
+ # after the work has been performed.
+ #
+ # Where possible, prefer +wrap+.
+ def self.run!
+ if active?
+ Null
+ else
+ new.tap do |instance|
+ success = nil
+ begin
+ instance.run!
+ success = true
+ ensure
+ instance.complete! unless success
+ end
+ end
+ end
+ end
+
+ # Perform the work in the supplied block as an execution.
+ def self.wrap
+ return yield if active?
+
+ instance = run!
+ begin
+ yield
+ ensure
+ instance.complete!
+ end
+ end
+
+ class << self # :nodoc:
+ attr_accessor :active
+ end
+
+ def self.inherited(other) # :nodoc:
+ super
+ other.active = Concurrent::Hash.new
+ end
+
+ self.active = Concurrent::Hash.new
+
+ def self.active? # :nodoc:
+ @active[Thread.current]
+ end
+
+ def run! # :nodoc:
+ self.class.active[Thread.current] = true
+ run_callbacks(:run)
+ end
+
+ # Complete this in-flight execution. This method *must* be called
+ # exactly once on the result of any call to +run!+.
+ #
+ # Where possible, prefer +wrap+.
+ def complete!
+ run_callbacks(:complete)
+ ensure
+ self.class.active.delete Thread.current
+ end
+
+ private
+ def hook_state
+ @_hook_state ||= {}
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/executor.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/executor.rb
new file mode 100644
index 0000000..ce391b0
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/executor.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require "active_support/execution_wrapper"
+
+module ActiveSupport
+ class Executor < ExecutionWrapper
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/file_update_checker.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/file_update_checker.rb
new file mode 100644
index 0000000..1a0bb10
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/file_update_checker.rb
@@ -0,0 +1,163 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/time/calculations"
+
+module ActiveSupport
+ # FileUpdateChecker specifies the API used by Rails to watch files
+ # and control reloading. The API depends on four methods:
+ #
+ # * +initialize+ which expects two parameters and one block as
+ # described below.
+ #
+ # * +updated?+ which returns a boolean if there were updates in
+ # the filesystem or not.
+ #
+ # * +execute+ which executes the given block on initialization
+ # and updates the latest watched files and timestamp.
+ #
+ # * +execute_if_updated+ which just executes the block if it was updated.
+ #
+ # After initialization, a call to +execute_if_updated+ must execute
+ # the block only if there was really a change in the filesystem.
+ #
+ # This class is used by Rails to reload the I18n framework whenever
+ # they are changed upon a new request.
+ #
+ # i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
+ # I18n.reload!
+ # end
+ #
+ # ActiveSupport::Reloader.to_prepare do
+ # i18n_reloader.execute_if_updated
+ # end
+ class FileUpdateChecker
+ # It accepts two parameters on initialization. The first is an array
+ # of files and the second is an optional hash of directories. The hash must
+ # have directories as keys and the value is an array of extensions to be
+ # watched under that directory.
+ #
+ # This method must also receive a block that will be called once a path
+ # changes. The array of files and list of directories cannot be changed
+ # after FileUpdateChecker has been initialized.
+ def initialize(files, dirs = {}, &block)
+ unless block
+ raise ArgumentError, "A block is required to initialize a FileUpdateChecker"
+ end
+
+ @files = files.freeze
+ @glob = compile_glob(dirs)
+ @block = block
+
+ @watched = nil
+ @updated_at = nil
+
+ @last_watched = watched
+ @last_update_at = updated_at(@last_watched)
+ end
+
+ # Check if any of the entries were updated. If so, the watched and/or
+ # updated_at values are cached until the block is executed via +execute+
+ # or +execute_if_updated+.
+ def updated?
+ current_watched = watched
+ if @last_watched.size != current_watched.size
+ @watched = current_watched
+ true
+ else
+ current_updated_at = updated_at(current_watched)
+ if @last_update_at < current_updated_at
+ @watched = current_watched
+ @updated_at = current_updated_at
+ true
+ else
+ false
+ end
+ end
+ end
+
+ # Executes the given block and updates the latest watched files and
+ # timestamp.
+ def execute
+ @last_watched = watched
+ @last_update_at = updated_at(@last_watched)
+ @block.call
+ ensure
+ @watched = nil
+ @updated_at = nil
+ end
+
+ # Execute the block given if updated.
+ def execute_if_updated
+ if updated?
+ yield if block_given?
+ execute
+ true
+ else
+ false
+ end
+ end
+
+ private
+
+ def watched
+ @watched || begin
+ all = @files.select { |f| File.exist?(f) }
+ all.concat(Dir[@glob]) if @glob
+ all
+ end
+ end
+
+ def updated_at(paths)
+ @updated_at || max_mtime(paths) || Time.at(0)
+ end
+
+ # This method returns the maximum mtime of the files in +paths+, or +nil+
+ # if the array is empty.
+ #
+ # Files with a mtime in the future are ignored. Such abnormal situation
+ # can happen for example if the user changes the clock by hand. It is
+ # healthy to consider this edge case because with mtimes in the future
+ # reloading is not triggered.
+ def max_mtime(paths)
+ time_now = Time.now
+ max_mtime = nil
+
+ # Time comparisons are performed with #compare_without_coercion because
+ # AS redefines these operators in a way that is much slower and does not
+ # bring any benefit in this particular code.
+ #
+ # Read t1.compare_without_coercion(t2) < 0 as t1 < t2.
+ paths.each do |path|
+ mtime = File.mtime(path)
+
+ next if time_now.compare_without_coercion(mtime) < 0
+
+ if max_mtime.nil? || max_mtime.compare_without_coercion(mtime) < 0
+ max_mtime = mtime
+ end
+ end
+
+ max_mtime
+ end
+
+ def compile_glob(hash)
+ hash.freeze # Freeze so changes aren't accidentally pushed
+ return if hash.empty?
+
+ globs = hash.map do |key, value|
+ "#{escape(key)}/**/*#{compile_ext(value)}"
+ end
+ "{#{globs.join(",")}}"
+ end
+
+ def escape(key)
+ key.gsub(",", '\,')
+ end
+
+ def compile_ext(array)
+ array = Array(array)
+ return if array.empty?
+ ".{#{array.join(",")}}"
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/gem_version.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/gem_version.rb
new file mode 100644
index 0000000..9e53a55
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/gem_version.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # Returns the version of the currently loaded Active Support as a Gem::Version.
+ def self.gem_version
+ Gem::Version.new VERSION::STRING
+ end
+
+ module VERSION
+ MAJOR = 5
+ MINOR = 2
+ TINY = 4
+ PRE = "4"
+
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/gzip.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/gzip.rb
new file mode 100644
index 0000000..7ffa6d9
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/gzip.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require "zlib"
+require "stringio"
+
+module ActiveSupport
+ # A convenient wrapper for the zlib standard library that allows
+ # compression/decompression of strings with gzip.
+ #
+ # gzip = ActiveSupport::Gzip.compress('compress me!')
+ # # => "\x1F\x8B\b\x00o\x8D\xCDO\x00\x03K\xCE\xCF-(J-.V\xC8MU\x04\x00R>n\x83\f\x00\x00\x00"
+ #
+ # ActiveSupport::Gzip.decompress(gzip)
+ # # => "compress me!"
+ module Gzip
+ class Stream < StringIO
+ def initialize(*)
+ super
+ set_encoding "BINARY"
+ end
+ def close; rewind; end
+ end
+
+ # Decompresses a gzipped string.
+ def self.decompress(source)
+ Zlib::GzipReader.wrap(StringIO.new(source), &:read)
+ end
+
+ # Compresses a string using gzip.
+ def self.compress(source, level = Zlib::DEFAULT_COMPRESSION, strategy = Zlib::DEFAULT_STRATEGY)
+ output = Stream.new
+ gz = Zlib::GzipWriter.new(output, level, strategy)
+ gz.write(source)
+ gz.close
+ output.string
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/hash_with_indifferent_access.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/hash_with_indifferent_access.rb
new file mode 100644
index 0000000..24d3aed
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/hash_with_indifferent_access.rb
@@ -0,0 +1,395 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/hash/keys"
+require "active_support/core_ext/hash/reverse_merge"
+
+module ActiveSupport
+ # Implements a hash where keys :foo and "foo" are considered
+ # to be the same.
+ #
+ # rgb = ActiveSupport::HashWithIndifferentAccess.new
+ #
+ # rgb[:black] = '#000000'
+ # rgb[:black] # => '#000000'
+ # rgb['black'] # => '#000000'
+ #
+ # rgb['white'] = '#FFFFFF'
+ # rgb[:white] # => '#FFFFFF'
+ # rgb['white'] # => '#FFFFFF'
+ #
+ # Internally symbols are mapped to strings when used as keys in the entire
+ # writing interface (calling []=, merge, etc). This
+ # mapping belongs to the public interface. For example, given:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
+ #
+ # You are guaranteed that the key is returned as a string:
+ #
+ # hash.keys # => ["a"]
+ #
+ # Technically other types of keys are accepted:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
+ # hash[0] = 0
+ # hash # => {"a"=>1, 0=>0}
+ #
+ # but this class is intended for use cases where strings or symbols are the
+ # expected keys and it is convenient to understand both as the same. For
+ # example the +params+ hash in Ruby on Rails.
+ #
+ # Note that core extensions define Hash#with_indifferent_access:
+ #
+ # rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
+ #
+ # which may be handy.
+ #
+ # To access this class outside of Rails, require the core extension with:
+ #
+ # require "active_support/core_ext/hash/indifferent_access"
+ #
+ # which will, in turn, require this file.
+ class HashWithIndifferentAccess < Hash
+ # Returns +true+ so that Array#extract_options! finds members of
+ # this class.
+ def extractable_options?
+ true
+ end
+
+ def with_indifferent_access
+ dup
+ end
+
+ def nested_under_indifferent_access
+ self
+ end
+
+ def initialize(constructor = {})
+ if constructor.respond_to?(:to_hash)
+ super()
+ update(constructor)
+
+ hash = constructor.to_hash
+ self.default = hash.default if hash.default
+ self.default_proc = hash.default_proc if hash.default_proc
+ else
+ super(constructor)
+ end
+ end
+
+ def self.[](*args)
+ new.merge!(Hash[*args])
+ end
+
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
+
+ # Assigns a new value to the hash:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
+ # hash[:key] = 'value'
+ #
+ # This value can be later fetched using either +:key+ or 'key'.
+ def []=(key, value)
+ regular_writer(convert_key(key), convert_value(value, for: :assignment))
+ end
+
+ alias_method :store, :[]=
+
+ # Updates the receiver in-place, merging in the hash passed as argument:
+ #
+ # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
+ # hash_1[:key] = 'value'
+ #
+ # hash_2 = ActiveSupport::HashWithIndifferentAccess.new
+ # hash_2[:key] = 'New Value!'
+ #
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
+ #
+ # The argument can be either an
+ # ActiveSupport::HashWithIndifferentAccess or a regular +Hash+.
+ # In either case the merge respects the semantics of indifferent access.
+ #
+ # If the argument is a regular hash with keys +:key+ and +"key"+ only one
+ # of the values end up in the receiver, but which one is unspecified.
+ #
+ # When given a block, the value for duplicated keys will be determined
+ # by the result of invoking the block with the duplicated key, the value
+ # in the receiver, and the value in +other_hash+. The rules for duplicated
+ # keys follow the semantics of indifferent access:
+ #
+ # hash_1[:key] = 10
+ # hash_2['key'] = 12
+ # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
+ def update(other_hash)
+ if other_hash.is_a? HashWithIndifferentAccess
+ super(other_hash)
+ else
+ other_hash.to_hash.each_pair do |key, value|
+ if block_given? && key?(key)
+ value = yield(convert_key(key), self[key], value)
+ end
+ regular_writer(convert_key(key), convert_value(value))
+ end
+ self
+ end
+ end
+
+ alias_method :merge!, :update
+
+ # Checks the hash for a key matching the argument passed in:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
+ # hash['key'] = 'value'
+ # hash.key?(:key) # => true
+ # hash.key?('key') # => true
+ def key?(key)
+ super(convert_key(key))
+ end
+
+ alias_method :include?, :key?
+ alias_method :has_key?, :key?
+ alias_method :member?, :key?
+
+ # Same as Hash#[] where the key passed as argument can be
+ # either a string or a symbol:
+ #
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
+ # counters[:foo] = 1
+ #
+ # counters['foo'] # => 1
+ # counters[:foo] # => 1
+ # counters[:zoo] # => nil
+ def [](key)
+ super(convert_key(key))
+ end
+
+ # Same as Hash#assoc where the key passed as argument can be
+ # either a string or a symbol:
+ #
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
+ # counters[:foo] = 1
+ #
+ # counters.assoc('foo') # => ["foo", 1]
+ # counters.assoc(:foo) # => ["foo", 1]
+ # counters.assoc(:zoo) # => nil
+ def assoc(key)
+ super(convert_key(key))
+ end
+
+ # Same as Hash#fetch where the key passed as argument can be
+ # either a string or a symbol:
+ #
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
+ # counters[:foo] = 1
+ #
+ # counters.fetch('foo') # => 1
+ # counters.fetch(:bar, 0) # => 0
+ # counters.fetch(:bar) { |key| 0 } # => 0
+ # counters.fetch(:zoo) # => KeyError: key not found: "zoo"
+ def fetch(key, *extras)
+ super(convert_key(key), *extras)
+ end
+
+ if Hash.new.respond_to?(:dig)
+ # Same as Hash#dig where the key passed as argument can be
+ # either a string or a symbol:
+ #
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
+ # counters[:foo] = { bar: 1 }
+ #
+ # counters.dig('foo', 'bar') # => 1
+ # counters.dig(:foo, :bar) # => 1
+ # counters.dig(:zoo) # => nil
+ def dig(*args)
+ args[0] = convert_key(args[0]) if args.size > 0
+ super(*args)
+ end
+ end
+
+ # Same as Hash#default where the key passed as argument can be
+ # either a string or a symbol:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(1)
+ # hash.default # => 1
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new { |hash, key| key }
+ # hash.default # => nil
+ # hash.default('foo') # => 'foo'
+ # hash.default(:foo) # => 'foo'
+ def default(*args)
+ super(*args.map { |arg| convert_key(arg) })
+ end
+
+ # Returns an array of the values at the specified indices:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
+ # hash[:a] = 'x'
+ # hash[:b] = 'y'
+ # hash.values_at('a', 'b') # => ["x", "y"]
+ def values_at(*indices)
+ indices.collect { |key| self[convert_key(key)] }
+ end
+
+ # Returns an array of the values at the specified indices, but also
+ # raises an exception when one of the keys can't be found.
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
+ # hash[:a] = 'x'
+ # hash[:b] = 'y'
+ # hash.fetch_values('a', 'b') # => ["x", "y"]
+ # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
+ # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
+ def fetch_values(*indices, &block)
+ indices.collect { |key| fetch(key, &block) }
+ end if Hash.method_defined?(:fetch_values)
+
+ # Returns a shallow copy of the hash.
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new({ a: { b: 'b' } })
+ # dup = hash.dup
+ # dup[:a][:c] = 'c'
+ #
+ # hash[:a][:c] # => "c"
+ # dup[:a][:c] # => "c"
+ def dup
+ self.class.new(self).tap do |new_hash|
+ set_defaults(new_hash)
+ end
+ end
+
+ # This method has the same semantics of +update+, except it does not
+ # modify the receiver but rather returns a new hash with indifferent
+ # access with the result of the merge.
+ def merge(hash, &block)
+ dup.update(hash, &block)
+ end
+
+ # Like +merge+ but the other way around: Merges the receiver into the
+ # argument and returns a new hash with indifferent access as result:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
+ # hash['a'] = nil
+ # hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
+ def reverse_merge(other_hash)
+ super(self.class.new(other_hash))
+ end
+ alias_method :with_defaults, :reverse_merge
+
+ # Same semantics as +reverse_merge+ but modifies the receiver in-place.
+ def reverse_merge!(other_hash)
+ super(self.class.new(other_hash))
+ end
+ alias_method :with_defaults!, :reverse_merge!
+
+ # Replaces the contents of this hash with other_hash.
+ #
+ # h = { "a" => 100, "b" => 200 }
+ # h.replace({ "c" => 300, "d" => 400 }) # => {"c"=>300, "d"=>400}
+ def replace(other_hash)
+ super(self.class.new(other_hash))
+ end
+
+ # Removes the specified key from the hash.
+ def delete(key)
+ super(convert_key(key))
+ end
+
+ def stringify_keys!; self end
+ def deep_stringify_keys!; self end
+ def stringify_keys; dup end
+ def deep_stringify_keys; dup end
+ undef :symbolize_keys!
+ undef :deep_symbolize_keys!
+ def symbolize_keys; to_hash.symbolize_keys! end
+ alias_method :to_options, :symbolize_keys
+ def deep_symbolize_keys; to_hash.deep_symbolize_keys! end
+ def to_options!; self end
+
+ def select(*args, &block)
+ return to_enum(:select) unless block_given?
+ dup.tap { |hash| hash.select!(*args, &block) }
+ end
+
+ def reject(*args, &block)
+ return to_enum(:reject) unless block_given?
+ dup.tap { |hash| hash.reject!(*args, &block) }
+ end
+
+ def transform_values(*args, &block)
+ return to_enum(:transform_values) unless block_given?
+ dup.tap { |hash| hash.transform_values!(*args, &block) }
+ end
+
+ def transform_keys(*args, &block)
+ return to_enum(:transform_keys) unless block_given?
+ dup.tap { |hash| hash.transform_keys!(*args, &block) }
+ end
+
+ def transform_keys!
+ return enum_for(:transform_keys!) { size } unless block_given?
+ keys.each do |key|
+ self[yield(key)] = delete(key)
+ end
+ self
+ end
+
+ def slice(*keys)
+ keys.map! { |key| convert_key(key) }
+ self.class.new(super)
+ end
+
+ def slice!(*keys)
+ keys.map! { |key| convert_key(key) }
+ super
+ end
+
+ def compact
+ dup.tap(&:compact!)
+ end
+
+ # Convert to a regular hash with string keys.
+ def to_hash
+ _new_hash = Hash.new
+ set_defaults(_new_hash)
+
+ each do |key, value|
+ _new_hash[key] = convert_value(value, for: :to_hash)
+ end
+ _new_hash
+ end
+
+ private
+ def convert_key(key) # :doc:
+ key.kind_of?(Symbol) ? key.to_s : key
+ end
+
+ def convert_value(value, options = {}) # :doc:
+ if value.is_a? Hash
+ if options[:for] == :to_hash
+ value.to_hash
+ else
+ value.nested_under_indifferent_access
+ end
+ elsif value.is_a?(Array)
+ if options[:for] != :assignment || value.frozen?
+ value = value.dup
+ end
+ value.map! { |e| convert_value(e, options) }
+ else
+ value
+ end
+ end
+
+ def set_defaults(target) # :doc:
+ if default_proc
+ target.default_proc = default_proc.dup
+ else
+ target.default = default
+ end
+ end
+ end
+end
+
+# :stopdoc:
+
+HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/i18n.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/i18n.rb
new file mode 100644
index 0000000..d60b3ef
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/i18n.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/hash/deep_merge"
+require "active_support/core_ext/hash/except"
+require "active_support/core_ext/hash/slice"
+begin
+ require "i18n"
+rescue LoadError => e
+ $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
+ raise e
+end
+require "active_support/lazy_load_hooks"
+
+ActiveSupport.run_load_hooks(:i18n)
+I18n.load_path << File.expand_path("locale/en.yml", __dir__)
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/i18n_railtie.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/i18n_railtie.rb
new file mode 100644
index 0000000..ee24ebd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/i18n_railtie.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require "active_support"
+require "active_support/file_update_checker"
+require "active_support/core_ext/array/wrap"
+
+# :enddoc:
+
+module I18n
+ class Railtie < Rails::Railtie
+ config.i18n = ActiveSupport::OrderedOptions.new
+ config.i18n.railties_load_path = []
+ config.i18n.load_path = []
+ config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
+
+ # Set the i18n configuration after initialization since a lot of
+ # configuration is still usually done in application initializers.
+ config.after_initialize do |app|
+ I18n::Railtie.initialize_i18n(app)
+ end
+
+ # Trigger i18n config before any eager loading has happened
+ # so it's ready if any classes require it when eager loaded.
+ config.before_eager_load do |app|
+ I18n::Railtie.initialize_i18n(app)
+ end
+
+ @i18n_inited = false
+
+ # Setup i18n configuration.
+ def self.initialize_i18n(app)
+ return if @i18n_inited
+
+ fallbacks = app.config.i18n.delete(:fallbacks)
+
+ # Avoid issues with setting the default_locale by disabling available locales
+ # check while configuring.
+ enforce_available_locales = app.config.i18n.delete(:enforce_available_locales)
+ enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil?
+ I18n.enforce_available_locales = false
+
+ reloadable_paths = []
+ app.config.i18n.each do |setting, value|
+ case setting
+ when :railties_load_path
+ reloadable_paths = value
+ app.config.i18n.load_path.unshift(*value.flat_map(&:existent))
+ when :load_path
+ I18n.load_path += value
+ else
+ I18n.send("#{setting}=", value)
+ end
+ end
+
+ init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
+
+ # Restore available locales check so it will take place from now on.
+ I18n.enforce_available_locales = enforce_available_locales
+
+ directories = watched_dirs_with_extensions(reloadable_paths)
+ reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
+ I18n.load_path.keep_if { |p| File.exist?(p) }
+ I18n.load_path |= reloadable_paths.flat_map(&:existent)
+
+ I18n.reload!
+ end
+
+ app.reloaders << reloader
+ app.reloader.to_run do
+ reloader.execute_if_updated { require_unload_lock! }
+ end
+ reloader.execute
+
+ @i18n_inited = true
+ end
+
+ def self.include_fallbacks_module
+ I18n.backend.class.include(I18n::Backend::Fallbacks)
+ end
+
+ def self.init_fallbacks(fallbacks)
+ include_fallbacks_module
+
+ args = \
+ case fallbacks
+ when ActiveSupport::OrderedOptions
+ [*(fallbacks[:defaults] || []) << fallbacks[:map]].compact
+ when Hash, Array
+ Array.wrap(fallbacks)
+ else # TrueClass
+ [I18n.default_locale]
+ end
+
+ if args.empty? || args.first.is_a?(Hash)
+ args.unshift I18n.default_locale
+ end
+
+ I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
+ end
+
+ def self.validate_fallbacks(fallbacks)
+ case fallbacks
+ when ActiveSupport::OrderedOptions
+ !fallbacks.empty?
+ when TrueClass, Array, Hash
+ true
+ else
+ raise "Unexpected fallback type #{fallbacks.inspect}"
+ end
+ end
+
+ def self.watched_dirs_with_extensions(paths)
+ paths.each_with_object({}) do |path, result|
+ result[path.absolute_current] = path.extensions
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflections.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflections.rb
new file mode 100644
index 0000000..baf1cb3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflections.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require "active_support/inflector/inflections"
+
+#--
+# Defines the standard inflection rules. These are the starting point for
+# new projects and are not considered complete. The current set of inflection
+# rules is frozen. This means, we do not change them to become more complete.
+# This is a safety measure to keep existing applications from breaking.
+#++
+module ActiveSupport
+ Inflector.inflections(:en) do |inflect|
+ inflect.plural(/$/, "s")
+ inflect.plural(/s$/i, "s")
+ inflect.plural(/^(ax|test)is$/i, '\1es')
+ inflect.plural(/(octop|vir)us$/i, '\1i')
+ inflect.plural(/(octop|vir)i$/i, '\1i')
+ inflect.plural(/(alias|status)$/i, '\1es')
+ inflect.plural(/(bu)s$/i, '\1ses')
+ inflect.plural(/(buffal|tomat)o$/i, '\1oes')
+ inflect.plural(/([ti])um$/i, '\1a')
+ inflect.plural(/([ti])a$/i, '\1a')
+ inflect.plural(/sis$/i, "ses")
+ inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
+ inflect.plural(/(hive)$/i, '\1s')
+ inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
+ inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
+ inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
+ inflect.plural(/^(m|l)ouse$/i, '\1ice')
+ inflect.plural(/^(m|l)ice$/i, '\1ice')
+ inflect.plural(/^(ox)$/i, '\1en')
+ inflect.plural(/^(oxen)$/i, '\1')
+ inflect.plural(/(quiz)$/i, '\1zes')
+
+ inflect.singular(/s$/i, "")
+ inflect.singular(/(ss)$/i, '\1')
+ inflect.singular(/(n)ews$/i, '\1ews')
+ inflect.singular(/([ti])a$/i, '\1um')
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
+ inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
+ inflect.singular(/([^f])ves$/i, '\1fe')
+ inflect.singular(/(hive)s$/i, '\1')
+ inflect.singular(/(tive)s$/i, '\1')
+ inflect.singular(/([lr])ves$/i, '\1f')
+ inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
+ inflect.singular(/(s)eries$/i, '\1eries')
+ inflect.singular(/(m)ovies$/i, '\1ovie')
+ inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
+ inflect.singular(/^(m|l)ice$/i, '\1ouse')
+ inflect.singular(/(bus)(es)?$/i, '\1')
+ inflect.singular(/(o)es$/i, '\1')
+ inflect.singular(/(shoe)s$/i, '\1')
+ inflect.singular(/(cris|test)(is|es)$/i, '\1is')
+ inflect.singular(/^(a)x[ie]s$/i, '\1xis')
+ inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
+ inflect.singular(/(alias|status)(es)?$/i, '\1')
+ inflect.singular(/^(ox)en/i, '\1')
+ inflect.singular(/(vert|ind)ices$/i, '\1ex')
+ inflect.singular(/(matr)ices$/i, '\1ix')
+ inflect.singular(/(quiz)zes$/i, '\1')
+ inflect.singular(/(database)s$/i, '\1')
+
+ inflect.irregular("person", "people")
+ inflect.irregular("man", "men")
+ inflect.irregular("child", "children")
+ inflect.irregular("sex", "sexes")
+ inflect.irregular("move", "moves")
+ inflect.irregular("zombie", "zombies")
+
+ inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector.rb
new file mode 100644
index 0000000..d77f04c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+# in case active_support/inflector is required without the rest of active_support
+require "active_support/inflector/inflections"
+require "active_support/inflector/transliterate"
+require "active_support/inflector/methods"
+
+require "active_support/inflections"
+require "active_support/core_ext/string/inflections"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector/inflections.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector/inflections.rb
new file mode 100644
index 0000000..7e5dff1
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector/inflections.rb
@@ -0,0 +1,260 @@
+# frozen_string_literal: true
+
+require "concurrent/map"
+require "active_support/core_ext/array/prepend_and_append"
+require "active_support/core_ext/regexp"
+require "active_support/i18n"
+require "active_support/deprecation"
+
+module ActiveSupport
+ module Inflector
+ extend self
+
+ # A singleton instance of this class is yielded by Inflector.inflections,
+ # which can then be used to specify additional inflection rules. If passed
+ # an optional locale, rules for other languages can be specified. The
+ # default locale is :en. Only rules for English are provided.
+ #
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
+ # inflect.plural /^(ox)$/i, '\1\2en'
+ # inflect.singular /^(ox)en/i, '\1'
+ #
+ # inflect.irregular 'octopus', 'octopi'
+ #
+ # inflect.uncountable 'equipment'
+ # end
+ #
+ # New rules are added at the top. So in the example above, the irregular
+ # rule for octopus will now be the first of the pluralization and
+ # singularization rules that is runs. This guarantees that your rules run
+ # before any of the rules that may already have been loaded.
+ class Inflections
+ @__instance__ = Concurrent::Map.new
+
+ class Uncountables < Array
+ def initialize
+ @regex_array = []
+ super
+ end
+
+ def delete(entry)
+ super entry
+ @regex_array.delete(to_regex(entry))
+ end
+
+ def <<(*word)
+ add(word)
+ end
+
+ def add(words)
+ words = words.flatten.map(&:downcase)
+ concat(words)
+ @regex_array += words.map { |word| to_regex(word) }
+ self
+ end
+
+ def uncountable?(str)
+ @regex_array.any? { |regex| regex.match? str }
+ end
+
+ private
+ def to_regex(string)
+ /\b#{::Regexp.escape(string)}\Z/i
+ end
+ end
+
+ def self.instance(locale = :en)
+ @__instance__[locale] ||= new
+ end
+
+ attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
+ deprecate :acronym_regex
+
+ attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
+
+ def initialize
+ @plurals, @singulars, @uncountables, @humans, @acronyms = [], [], Uncountables.new, [], {}
+ define_acronym_regex_patterns
+ end
+
+ # Private, for the test suite.
+ def initialize_dup(orig) # :nodoc:
+ %w(plurals singulars uncountables humans acronyms).each do |scope|
+ instance_variable_set("@#{scope}", orig.send(scope).dup)
+ end
+ define_acronym_regex_patterns
+ end
+
+ # Specifies a new acronym. An acronym must be specified as it will appear
+ # in a camelized string. An underscore string that contains the acronym
+ # will retain the acronym when passed to +camelize+, +humanize+, or
+ # +titleize+. A camelized string that contains the acronym will maintain
+ # the acronym when titleized or humanized, and will convert the acronym
+ # into a non-delimited single lowercase word when passed to +underscore+.
+ #
+ # acronym 'HTML'
+ # titleize 'html' # => 'HTML'
+ # camelize 'html' # => 'HTML'
+ # underscore 'MyHTML' # => 'my_html'
+ #
+ # The acronym, however, must occur as a delimited unit and not be part of
+ # another word for conversions to recognize it:
+ #
+ # acronym 'HTTP'
+ # camelize 'my_http_delimited' # => 'MyHTTPDelimited'
+ # camelize 'https' # => 'Https', not 'HTTPs'
+ # underscore 'HTTPS' # => 'http_s', not 'https'
+ #
+ # acronym 'HTTPS'
+ # camelize 'https' # => 'HTTPS'
+ # underscore 'HTTPS' # => 'https'
+ #
+ # Note: Acronyms that are passed to +pluralize+ will no longer be
+ # recognized, since the acronym will not occur as a delimited unit in the
+ # pluralized result. To work around this, you must specify the pluralized
+ # form as an acronym as well:
+ #
+ # acronym 'API'
+ # camelize(pluralize('api')) # => 'Apis'
+ #
+ # acronym 'APIs'
+ # camelize(pluralize('api')) # => 'APIs'
+ #
+ # +acronym+ may be used to specify any word that contains an acronym or
+ # otherwise needs to maintain a non-standard capitalization. The only
+ # restriction is that the word must begin with a capital letter.
+ #
+ # acronym 'RESTful'
+ # underscore 'RESTful' # => 'restful'
+ # underscore 'RESTfulController' # => 'restful_controller'
+ # titleize 'RESTfulController' # => 'RESTful Controller'
+ # camelize 'restful' # => 'RESTful'
+ # camelize 'restful_controller' # => 'RESTfulController'
+ #
+ # acronym 'McDonald'
+ # underscore 'McDonald' # => 'mcdonald'
+ # camelize 'mcdonald' # => 'McDonald'
+ def acronym(word)
+ @acronyms[word.downcase] = word
+ define_acronym_regex_patterns
+ end
+
+ # Specifies a new pluralization rule and its replacement. The rule can
+ # either be a string or a regular expression. The replacement should
+ # always be a string that may include references to the matched data from
+ # the rule.
+ def plural(rule, replacement)
+ @uncountables.delete(rule) if rule.is_a?(String)
+ @uncountables.delete(replacement)
+ @plurals.prepend([rule, replacement])
+ end
+
+ # Specifies a new singularization rule and its replacement. The rule can
+ # either be a string or a regular expression. The replacement should
+ # always be a string that may include references to the matched data from
+ # the rule.
+ def singular(rule, replacement)
+ @uncountables.delete(rule) if rule.is_a?(String)
+ @uncountables.delete(replacement)
+ @singulars.prepend([rule, replacement])
+ end
+
+ # Specifies a new irregular that applies to both pluralization and
+ # singularization at the same time. This can only be used for strings, not
+ # regular expressions. You simply pass the irregular in singular and
+ # plural form.
+ #
+ # irregular 'octopus', 'octopi'
+ # irregular 'person', 'people'
+ def irregular(singular, plural)
+ @uncountables.delete(singular)
+ @uncountables.delete(plural)
+
+ s0 = singular[0]
+ srest = singular[1..-1]
+
+ p0 = plural[0]
+ prest = plural[1..-1]
+
+ if s0.upcase == p0.upcase
+ plural(/(#{s0})#{srest}$/i, '\1' + prest)
+ plural(/(#{p0})#{prest}$/i, '\1' + prest)
+
+ singular(/(#{s0})#{srest}$/i, '\1' + srest)
+ singular(/(#{p0})#{prest}$/i, '\1' + srest)
+ else
+ plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
+ plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
+ plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
+ plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
+
+ singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
+ singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
+ singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
+ singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
+ end
+ end
+
+ # Specifies words that are uncountable and should not be inflected.
+ #
+ # uncountable 'money'
+ # uncountable 'money', 'information'
+ # uncountable %w( money information rice )
+ def uncountable(*words)
+ @uncountables.add(words)
+ end
+
+ # Specifies a humanized form of a string by a regular expression rule or
+ # by a string mapping. When using a regular expression based replacement,
+ # the normal humanize formatting is called after the replacement. When a
+ # string is used, the human form should be specified as desired (example:
+ # 'The name', not 'the_name').
+ #
+ # human /_cnt$/i, '\1_count'
+ # human 'legacy_col_person_name', 'Name'
+ def human(rule, replacement)
+ @humans.prepend([rule, replacement])
+ end
+
+ # Clears the loaded inflections within a given scope (default is
+ # :all). Give the scope as a symbol of the inflection type, the
+ # options are: :plurals, :singulars, :uncountables,
+ # :humans.
+ #
+ # clear :all
+ # clear :plurals
+ def clear(scope = :all)
+ case scope
+ when :all
+ @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
+ else
+ instance_variable_set "@#{scope}", []
+ end
+ end
+
+ private
+
+ def define_acronym_regex_patterns
+ @acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/
+ @acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
+ @acronyms_underscore_regex = /(?:(?<=([A-Za-z\d]))|\b)(#{@acronym_regex})(?=\b|[^a-z])/
+ end
+ end
+
+ # Yields a singleton instance of Inflector::Inflections so you can specify
+ # additional inflector rules. If passed an optional locale, rules for other
+ # languages can be specified. If not specified, defaults to :en.
+ # Only rules for English are provided.
+ #
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
+ # inflect.uncountable 'rails'
+ # end
+ def inflections(locale = :en)
+ if block_given?
+ yield Inflections.instance(locale)
+ else
+ Inflections.instance(locale)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector/methods.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector/methods.rb
new file mode 100644
index 0000000..ad90a0b
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector/methods.rb
@@ -0,0 +1,410 @@
+# frozen_string_literal: true
+
+require "active_support/inflections"
+require "active_support/core_ext/regexp"
+
+module ActiveSupport
+ # The Inflector transforms words from singular to plural, class names to table
+ # names, modularized class names to ones without, and class names to foreign
+ # keys. The default inflections for pluralization, singularization, and
+ # uncountable words are kept in inflections.rb.
+ #
+ # The Rails core team has stated patches for the inflections library will not
+ # be accepted in order to avoid breaking legacy applications which may be
+ # relying on errant inflections. If you discover an incorrect inflection and
+ # require it for your application or wish to define rules for languages other
+ # than English, please correct or add them yourself (explained below).
+ module Inflector
+ extend self
+
+ # Returns the plural form of the word in the string.
+ #
+ # If passed an optional +locale+ parameter, the word will be
+ # pluralized using rules defined for that language. By default,
+ # this parameter is set to :en.
+ #
+ # pluralize('post') # => "posts"
+ # pluralize('octopus') # => "octopi"
+ # pluralize('sheep') # => "sheep"
+ # pluralize('words') # => "words"
+ # pluralize('CamelOctopus') # => "CamelOctopi"
+ # pluralize('ley', :es) # => "leyes"
+ def pluralize(word, locale = :en)
+ apply_inflections(word, inflections(locale).plurals, locale)
+ end
+
+ # The reverse of #pluralize, returns the singular form of a word in a
+ # string.
+ #
+ # If passed an optional +locale+ parameter, the word will be
+ # singularized using rules defined for that language. By default,
+ # this parameter is set to :en.
+ #
+ # singularize('posts') # => "post"
+ # singularize('octopi') # => "octopus"
+ # singularize('sheep') # => "sheep"
+ # singularize('word') # => "word"
+ # singularize('CamelOctopi') # => "CamelOctopus"
+ # singularize('leyes', :es) # => "ley"
+ def singularize(word, locale = :en)
+ apply_inflections(word, inflections(locale).singulars, locale)
+ end
+
+ # Converts strings to UpperCamelCase.
+ # If the +uppercase_first_letter+ parameter is set to false, then produces
+ # lowerCamelCase.
+ #
+ # Also converts '/' to '::' which is useful for converting
+ # paths to namespaces.
+ #
+ # camelize('active_model') # => "ActiveModel"
+ # camelize('active_model', false) # => "activeModel"
+ # camelize('active_model/errors') # => "ActiveModel::Errors"
+ # camelize('active_model/errors', false) # => "activeModel::Errors"
+ #
+ # As a rule of thumb you can think of +camelize+ as the inverse of
+ # #underscore, though there are cases where that does not hold:
+ #
+ # camelize(underscore('SSLError')) # => "SslError"
+ def camelize(term, uppercase_first_letter = true)
+ string = term.to_s
+ if uppercase_first_letter
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
+ else
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
+ end
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
+ string.gsub!("/".freeze, "::".freeze)
+ string
+ end
+
+ # Makes an underscored, lowercase form from the expression in the string.
+ #
+ # Changes '::' to '/' to convert namespaces to paths.
+ #
+ # underscore('ActiveModel') # => "active_model"
+ # underscore('ActiveModel::Errors') # => "active_model/errors"
+ #
+ # As a rule of thumb you can think of +underscore+ as the inverse of
+ # #camelize, though there are cases where that does not hold:
+ #
+ # camelize(underscore('SSLError')) # => "SslError"
+ def underscore(camel_cased_word)
+ return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
+ word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'.freeze }#{$2.downcase}" }
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
+ word.tr!("-".freeze, "_".freeze)
+ word.downcase!
+ word
+ end
+
+ # Tweaks an attribute name for display to end users.
+ #
+ # Specifically, performs these transformations:
+ #
+ # * Applies human inflection rules to the argument.
+ # * Deletes leading underscores, if any.
+ # * Removes a "_id" suffix if present.
+ # * Replaces underscores with spaces, if any.
+ # * Downcases all words except acronyms.
+ # * Capitalizes the first word.
+ # The capitalization of the first word can be turned off by setting the
+ # +:capitalize+ option to false (default is true).
+ #
+ # The trailing '_id' can be kept and capitalized by setting the
+ # optional parameter +keep_id_suffix+ to true (default is false).
+ #
+ # humanize('employee_salary') # => "Employee salary"
+ # humanize('author_id') # => "Author"
+ # humanize('author_id', capitalize: false) # => "author"
+ # humanize('_id') # => "Id"
+ # humanize('author_id', keep_id_suffix: true) # => "Author Id"
+ #
+ # If "SSL" was defined to be an acronym:
+ #
+ # humanize('ssl_error') # => "SSL error"
+ #
+ def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false)
+ result = lower_case_and_underscored_word.to_s.dup
+
+ inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
+
+ result.sub!(/\A_+/, "".freeze)
+ unless keep_id_suffix
+ result.sub!(/_id\z/, "".freeze)
+ end
+ result.tr!("_".freeze, " ".freeze)
+
+ result.gsub!(/([a-z\d]*)/i) do |match|
+ "#{inflections.acronyms[match.downcase] || match.downcase}"
+ end
+
+ if capitalize
+ result.sub!(/\A\w/) { |match| match.upcase }
+ end
+
+ result
+ end
+
+ # Converts just the first character to uppercase.
+ #
+ # upcase_first('what a Lovely Day') # => "What a Lovely Day"
+ # upcase_first('w') # => "W"
+ # upcase_first('') # => ""
+ def upcase_first(string)
+ string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
+ end
+
+ # Capitalizes all the words and replaces some characters in the string to
+ # create a nicer looking title. +titleize+ is meant for creating pretty
+ # output. It is not used in the Rails internals.
+ #
+ # The trailing '_id','Id'.. can be kept and capitalized by setting the
+ # optional parameter +keep_id_suffix+ to true.
+ # By default, this parameter is false.
+ #
+ # +titleize+ is also aliased as +titlecase+.
+ #
+ # titleize('man from the boondocks') # => "Man From The Boondocks"
+ # titleize('x-men: the last stand') # => "X Men: The Last Stand"
+ # titleize('TheManWithoutAPast') # => "The Man Without A Past"
+ # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
+ # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
+ def titleize(word, keep_id_suffix: false)
+ humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(? "raw_scaled_scorers"
+ # tableize('ham_and_egg') # => "ham_and_eggs"
+ # tableize('fancyCategory') # => "fancy_categories"
+ def tableize(class_name)
+ pluralize(underscore(class_name))
+ end
+
+ # Creates a class name from a plural table name like Rails does for table
+ # names to models. Note that this returns a string and not a Class (To
+ # convert to an actual class follow +classify+ with #constantize).
+ #
+ # classify('ham_and_eggs') # => "HamAndEgg"
+ # classify('posts') # => "Post"
+ #
+ # Singular names are not handled correctly:
+ #
+ # classify('calculus') # => "Calculus"
+ def classify(table_name)
+ # strip out any leading schema name
+ camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
+ end
+
+ # Replaces underscores with dashes in the string.
+ #
+ # dasherize('puni_puni') # => "puni-puni"
+ def dasherize(underscored_word)
+ underscored_word.tr("_".freeze, "-".freeze)
+ end
+
+ # Removes the module part from the expression in the string.
+ #
+ # demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
+ # demodulize('Inflections') # => "Inflections"
+ # demodulize('::Inflections') # => "Inflections"
+ # demodulize('') # => ""
+ #
+ # See also #deconstantize.
+ def demodulize(path)
+ path = path.to_s
+ if i = path.rindex("::")
+ path[(i + 2)..-1]
+ else
+ path
+ end
+ end
+
+ # Removes the rightmost segment from the constant expression in the string.
+ #
+ # deconstantize('Net::HTTP') # => "Net"
+ # deconstantize('::Net::HTTP') # => "::Net"
+ # deconstantize('String') # => ""
+ # deconstantize('::String') # => ""
+ # deconstantize('') # => ""
+ #
+ # See also #demodulize.
+ def deconstantize(path)
+ path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename
+ end
+
+ # Creates a foreign key name from a class name.
+ # +separate_class_name_and_id_with_underscore+ sets whether
+ # the method should put '_' between the name and 'id'.
+ #
+ # foreign_key('Message') # => "message_id"
+ # foreign_key('Message', false) # => "messageid"
+ # foreign_key('Admin::Post') # => "post_id"
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
+ underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
+ end
+
+ # Tries to find a constant with the name specified in the argument string.
+ #
+ # constantize('Module') # => Module
+ # constantize('Foo::Bar') # => Foo::Bar
+ #
+ # The name is assumed to be the one of a top-level constant, no matter
+ # whether it starts with "::" or not. No lexical context is taken into
+ # account:
+ #
+ # C = 'outside'
+ # module M
+ # C = 'inside'
+ # C # => 'inside'
+ # constantize('C') # => 'outside', same as ::C
+ # end
+ #
+ # NameError is raised when the name is not in CamelCase or the constant is
+ # unknown.
+ def constantize(camel_cased_word)
+ names = camel_cased_word.split("::".freeze)
+
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
+ Object.const_get(camel_cased_word) if names.empty?
+
+ # Remove the first blank element in case of '::ClassName' notation.
+ names.shift if names.size > 1 && names.first.empty?
+
+ names.inject(Object) do |constant, name|
+ if constant == Object
+ constant.const_get(name)
+ else
+ candidate = constant.const_get(name)
+ next candidate if constant.const_defined?(name, false)
+ next candidate unless Object.const_defined?(name)
+
+ # Go down the ancestors to check if it is owned directly. The check
+ # stops when we reach Object or the end of ancestors tree.
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
+ break const if ancestor == Object
+ break ancestor if ancestor.const_defined?(name, false)
+ const
+ end
+
+ # owner is in Object, so raise
+ constant.const_get(name, false)
+ end
+ end
+ end
+
+ # Tries to find a constant with the name specified in the argument string.
+ #
+ # safe_constantize('Module') # => Module
+ # safe_constantize('Foo::Bar') # => Foo::Bar
+ #
+ # The name is assumed to be the one of a top-level constant, no matter
+ # whether it starts with "::" or not. No lexical context is taken into
+ # account:
+ #
+ # C = 'outside'
+ # module M
+ # C = 'inside'
+ # C # => 'inside'
+ # safe_constantize('C') # => 'outside', same as ::C
+ # end
+ #
+ # +nil+ is returned when the name is not in CamelCase or the constant (or
+ # part of it) is unknown.
+ #
+ # safe_constantize('blargle') # => nil
+ # safe_constantize('UnknownModule') # => nil
+ # safe_constantize('UnknownModule::Foo::Bar') # => nil
+ def safe_constantize(camel_cased_word)
+ constantize(camel_cased_word)
+ rescue NameError => e
+ raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
+ e.name.to_s == camel_cased_word.to_s)
+ rescue ArgumentError => e
+ raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
+ rescue LoadError => e
+ raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(e.message)
+ end
+
+ # Returns the suffix that should be added to a number to denote the position
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
+ #
+ # ordinal(1) # => "st"
+ # ordinal(2) # => "nd"
+ # ordinal(1002) # => "nd"
+ # ordinal(1003) # => "rd"
+ # ordinal(-11) # => "th"
+ # ordinal(-1021) # => "st"
+ def ordinal(number)
+ abs_number = number.to_i.abs
+
+ if (11..13).include?(abs_number % 100)
+ "th"
+ else
+ case abs_number % 10
+ when 1; "st"
+ when 2; "nd"
+ when 3; "rd"
+ else "th"
+ end
+ end
+ end
+
+ # Turns a number into an ordinal string used to denote the position in an
+ # ordered sequence such as 1st, 2nd, 3rd, 4th.
+ #
+ # ordinalize(1) # => "1st"
+ # ordinalize(2) # => "2nd"
+ # ordinalize(1002) # => "1002nd"
+ # ordinalize(1003) # => "1003rd"
+ # ordinalize(-11) # => "-11th"
+ # ordinalize(-1021) # => "-1021st"
+ def ordinalize(number)
+ "#{number}#{ordinal(number)}"
+ end
+
+ private
+
+ # Mounts a regular expression, returned as a string to ease interpolation,
+ # that will match part by part the given constant.
+ #
+ # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
+ # const_regexp("::") # => "::"
+ def const_regexp(camel_cased_word)
+ parts = camel_cased_word.split("::".freeze)
+
+ return Regexp.escape(camel_cased_word) if parts.blank?
+
+ last = parts.pop
+
+ parts.reverse.inject(last) do |acc, part|
+ part.empty? ? acc : "#{part}(::#{acc})?"
+ end
+ end
+
+ # Applies inflection rules for +singularize+ and +pluralize+.
+ #
+ # If passed an optional +locale+ parameter, the uncountables will be
+ # found for that locale.
+ #
+ # apply_inflections('post', inflections.plurals, :en) # => "posts"
+ # apply_inflections('posts', inflections.singulars, :en) # => "post"
+ def apply_inflections(word, rules, locale = :en)
+ result = word.to_s.dup
+
+ if word.empty? || inflections(locale).uncountables.uncountable?(result)
+ result
+ else
+ rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
+ result
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector/transliterate.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector/transliterate.rb
new file mode 100644
index 0000000..6f2ca49
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/inflector/transliterate.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/string/multibyte"
+require "active_support/i18n"
+
+module ActiveSupport
+ module Inflector
+ # Replaces non-ASCII characters with an ASCII approximation, or if none
+ # exists, a replacement character which defaults to "?".
+ #
+ # transliterate('Ærøskøbing')
+ # # => "AEroskobing"
+ #
+ # Default approximations are provided for Western/Latin characters,
+ # e.g, "ø", "ñ", "é", "ß", etc.
+ #
+ # This method is I18n aware, so you can set up custom approximations for a
+ # locale. This can be useful, for example, to transliterate German's "ü"
+ # and "ö" to "ue" and "oe", or to add support for transliterating Russian
+ # to ASCII.
+ #
+ # In order to make your custom transliterations available, you must set
+ # them as the i18n.transliterate.rule i18n key:
+ #
+ # # Store the transliterations in locales/de.yml
+ # i18n:
+ # transliterate:
+ # rule:
+ # ü: "ue"
+ # ö: "oe"
+ #
+ # # Or set them using Ruby
+ # I18n.backend.store_translations(:de, i18n: {
+ # transliterate: {
+ # rule: {
+ # 'ü' => 'ue',
+ # 'ö' => 'oe'
+ # }
+ # }
+ # })
+ #
+ # The value for i18n.transliterate.rule can be a simple Hash that
+ # maps characters to ASCII approximations as shown above, or, for more
+ # complex requirements, a Proc:
+ #
+ # I18n.backend.store_translations(:de, i18n: {
+ # transliterate: {
+ # rule: ->(string) { MyTransliterator.transliterate(string) }
+ # }
+ # })
+ #
+ # Now you can have different transliterations for each locale:
+ #
+ # I18n.locale = :en
+ # transliterate('Jürgen')
+ # # => "Jurgen"
+ #
+ # I18n.locale = :de
+ # transliterate('Jürgen')
+ # # => "Juergen"
+ def transliterate(string, replacement = "?".freeze)
+ raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
+
+ I18n.transliterate(
+ ActiveSupport::Multibyte::Unicode.normalize(
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
+ replacement: replacement)
+ end
+
+ # Replaces special characters in a string so that it may be used as part of
+ # a 'pretty' URL.
+ #
+ # parameterize("Donald E. Knuth") # => "donald-e-knuth"
+ # parameterize("^très|Jolie-- ") # => "tres-jolie"
+ #
+ # To use a custom separator, override the +separator+ argument.
+ #
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
+ #
+ # To preserve the case of the characters in a string, use the +preserve_case+ argument.
+ #
+ # parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
+ # parameterize("^très|Jolie-- ", preserve_case: true) # => "tres-Jolie"
+ #
+ # It preserves dashes and underscores unless they are used as separators:
+ #
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
+ #
+ def parameterize(string, separator: "-", preserve_case: false)
+ # Replace accented chars with their ASCII equivalents.
+ parameterized_string = transliterate(string)
+
+ # Turn unwanted chars into the separator.
+ parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
+
+ unless separator.nil? || separator.empty?
+ if separator == "-".freeze
+ re_duplicate_separator = /-{2,}/
+ re_leading_trailing_separator = /^-|-$/i
+ else
+ re_sep = Regexp.escape(separator)
+ re_duplicate_separator = /#{re_sep}{2,}/
+ re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i
+ end
+ # No more than one of the separator in a row.
+ parameterized_string.gsub!(re_duplicate_separator, separator)
+ # Remove leading/trailing separator.
+ parameterized_string.gsub!(re_leading_trailing_separator, "".freeze)
+ end
+
+ parameterized_string.downcase! unless preserve_case
+ parameterized_string
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/json.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/json.rb
new file mode 100644
index 0000000..d788717
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/json.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+require "active_support/json/decoding"
+require "active_support/json/encoding"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/json/decoding.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/json/decoding.rb
new file mode 100644
index 0000000..8c0e016
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/json/decoding.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/attribute_accessors"
+require "active_support/core_ext/module/delegation"
+require "json"
+
+module ActiveSupport
+ # Look for and parse json strings that look like ISO 8601 times.
+ mattr_accessor :parse_json_times
+
+ module JSON
+ # matches YAML-formatted dates
+ DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/
+ DATETIME_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)$/
+
+ class << self
+ # Parses a JSON string (JavaScript Object Notation) into a hash.
+ # See http://www.json.org for more info.
+ #
+ # ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
+ # => {"team" => "rails", "players" => "36"}
+ def decode(json)
+ data = ::JSON.parse(json, quirks_mode: true)
+
+ if ActiveSupport.parse_json_times
+ convert_dates_from(data)
+ else
+ data
+ end
+ end
+
+ # Returns the class of the error that will be raised when there is an
+ # error in decoding JSON. Using this method means you won't directly
+ # depend on the ActiveSupport's JSON implementation, in case it changes
+ # in the future.
+ #
+ # begin
+ # obj = ActiveSupport::JSON.decode(some_string)
+ # rescue ActiveSupport::JSON.parse_error
+ # Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}")
+ # end
+ def parse_error
+ ::JSON::ParserError
+ end
+
+ private
+
+ def convert_dates_from(data)
+ case data
+ when nil
+ nil
+ when DATE_REGEX
+ begin
+ Date.parse(data)
+ rescue ArgumentError
+ data
+ end
+ when DATETIME_REGEX
+ begin
+ Time.zone.parse(data)
+ rescue ArgumentError
+ data
+ end
+ when Array
+ data.map! { |d| convert_dates_from(d) }
+ when Hash
+ data.each do |key, value|
+ data[key] = convert_dates_from(value)
+ end
+ else
+ data
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/json/encoding.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/json/encoding.rb
new file mode 100644
index 0000000..1339c75
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/json/encoding.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/json"
+require "active_support/core_ext/module/delegation"
+
+module ActiveSupport
+ class << self
+ delegate :use_standard_json_time_format, :use_standard_json_time_format=,
+ :time_precision, :time_precision=,
+ :escape_html_entities_in_json, :escape_html_entities_in_json=,
+ :json_encoder, :json_encoder=,
+ to: :'ActiveSupport::JSON::Encoding'
+ end
+
+ module JSON
+ # Dumps objects in JSON (JavaScript Object Notation).
+ # See http://www.json.org for more info.
+ #
+ # ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
+ # # => "{\"team\":\"rails\",\"players\":\"36\"}"
+ def self.encode(value, options = nil)
+ Encoding.json_encoder.new(options).encode(value)
+ end
+
+ module Encoding #:nodoc:
+ class JSONGemEncoder #:nodoc:
+ attr_reader :options
+
+ def initialize(options = nil)
+ @options = options || {}
+ end
+
+ # Encode the given object into a JSON string
+ def encode(value)
+ stringify jsonify value.as_json(options.dup)
+ end
+
+ private
+ # Rails does more escaping than the JSON gem natively does (we
+ # escape \u2028 and \u2029 and optionally >, <, & to work around
+ # certain browser problems).
+ ESCAPED_CHARS = {
+ "\u2028" => '\u2028',
+ "\u2029" => '\u2029',
+ ">" => '\u003e',
+ "<" => '\u003c',
+ "&" => '\u0026',
+ }
+
+ ESCAPE_REGEX_WITH_HTML_ENTITIES = /[\u2028\u2029><&]/u
+ ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u
+
+ # This class wraps all the strings we see and does the extra escaping
+ class EscapedString < String #:nodoc:
+ def to_json(*)
+ if Encoding.escape_html_entities_in_json
+ super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
+ else
+ super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
+ end
+ end
+
+ def to_s
+ self
+ end
+ end
+
+ # Mark these as private so we don't leak encoding-specific constructs
+ private_constant :ESCAPED_CHARS, :ESCAPE_REGEX_WITH_HTML_ENTITIES,
+ :ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, :EscapedString
+
+ # Convert an object into a "JSON-ready" representation composed of
+ # primitives like Hash, Array, String, Numeric,
+ # and +true+/+false+/+nil+.
+ # Recursively calls #as_json to the object to recursively build a
+ # fully JSON-ready object.
+ #
+ # This allows developers to implement #as_json without having to
+ # worry about what base types of objects they are allowed to return
+ # or having to remember to call #as_json recursively.
+ #
+ # Note: the +options+ hash passed to +object.to_json+ is only passed
+ # to +object.as_json+, not any of this method's recursive +#as_json+
+ # calls.
+ def jsonify(value)
+ case value
+ when String
+ EscapedString.new(value)
+ when Numeric, NilClass, TrueClass, FalseClass
+ value.as_json
+ when Hash
+ Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
+ when Array
+ value.map { |v| jsonify(v) }
+ else
+ jsonify value.as_json
+ end
+ end
+
+ # Encode a "jsonified" Ruby data structure using the JSON gem
+ def stringify(jsonified)
+ ::JSON.generate(jsonified, quirks_mode: true, max_nesting: false)
+ end
+ end
+
+ class << self
+ # If true, use ISO 8601 format for dates and times. Otherwise, fall back
+ # to the Active Support legacy format.
+ attr_accessor :use_standard_json_time_format
+
+ # If true, encode >, <, & as escaped unicode sequences (e.g. > as \u003e)
+ # as a safety measure.
+ attr_accessor :escape_html_entities_in_json
+
+ # Sets the precision of encoded time values.
+ # Defaults to 3 (equivalent to millisecond precision)
+ attr_accessor :time_precision
+
+ # Sets the encoder used by Rails to encode Ruby objects into JSON strings
+ # in +Object#to_json+ and +ActiveSupport::JSON.encode+.
+ attr_accessor :json_encoder
+ end
+
+ self.use_standard_json_time_format = true
+ self.escape_html_entities_in_json = true
+ self.json_encoder = JSONGemEncoder
+ self.time_precision = 3
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/key_generator.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/key_generator.rb
new file mode 100644
index 0000000..78f7d7c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/key_generator.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require "concurrent/map"
+require "openssl"
+
+module ActiveSupport
+ # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2.
+ # It can be used to derive a number of keys for various purposes from a given secret.
+ # This lets Rails applications have a single secure secret, but avoid reusing that
+ # key in multiple incompatible contexts.
+ class KeyGenerator
+ def initialize(secret, options = {})
+ @secret = secret
+ # The default iterations are higher than required for our key derivation uses
+ # on the off chance someone uses this for password storage
+ @iterations = options[:iterations] || 2**16
+ end
+
+ # Returns a derived key suitable for use. The default key_size is chosen
+ # to be compatible with the default settings of ActiveSupport::MessageVerifier.
+ # i.e. OpenSSL::Digest::SHA1#block_length
+ def generate_key(salt, key_size = 64)
+ OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
+ end
+ end
+
+ # CachingKeyGenerator is a wrapper around KeyGenerator which allows users to avoid
+ # re-executing the key generation process when it's called using the same salt and
+ # key_size.
+ class CachingKeyGenerator
+ def initialize(key_generator)
+ @key_generator = key_generator
+ @cache_keys = Concurrent::Map.new
+ end
+
+ # Returns a derived key suitable for use.
+ def generate_key(*args)
+ @cache_keys[args.join] ||= @key_generator.generate_key(*args)
+ end
+ end
+
+ class LegacyKeyGenerator # :nodoc:
+ SECRET_MIN_LENGTH = 30 # Characters
+
+ def initialize(secret)
+ ensure_secret_secure(secret)
+ @secret = secret
+ end
+
+ def generate_key(salt)
+ @secret
+ end
+
+ private
+
+ # To prevent users from using something insecure like "Password" we make sure that the
+ # secret they've provided is at least 30 characters in length.
+ def ensure_secret_secure(secret)
+ if secret.blank?
+ raise ArgumentError, "A secret is required to generate an integrity hash " \
+ "for cookie session data. Set a secret_key_base of at least " \
+ "#{SECRET_MIN_LENGTH} characters in via `bin/rails credentials:edit`."
+ end
+
+ if secret.length < SECRET_MIN_LENGTH
+ raise ArgumentError, "Secret should be something secure, " \
+ "like \"#{SecureRandom.hex(16)}\". The value you " \
+ "provided, \"#{secret}\", is shorter than the minimum length " \
+ "of #{SECRET_MIN_LENGTH} characters."
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/lazy_load_hooks.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/lazy_load_hooks.rb
new file mode 100644
index 0000000..dc8080c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/lazy_load_hooks.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # lazy_load_hooks allows Rails to lazily load a lot of components and thus
+ # making the app boot faster. Because of this feature now there is no need to
+ # require ActiveRecord::Base at boot time purely to apply
+ # configuration. Instead a hook is registered that applies configuration once
+ # ActiveRecord::Base is loaded. Here ActiveRecord::Base is
+ # used as example but this feature can be applied elsewhere too.
+ #
+ # Here is an example where +on_load+ method is called to register a hook.
+ #
+ # initializer 'active_record.initialize_timezone' do
+ # ActiveSupport.on_load(:active_record) do
+ # self.time_zone_aware_attributes = true
+ # self.default_timezone = :utc
+ # end
+ # end
+ #
+ # When the entirety of +ActiveRecord::Base+ has been
+ # evaluated then +run_load_hooks+ is invoked. The very last line of
+ # +ActiveRecord::Base+ is:
+ #
+ # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
+ module LazyLoadHooks
+ def self.extended(base) # :nodoc:
+ base.class_eval do
+ @load_hooks = Hash.new { |h, k| h[k] = [] }
+ @loaded = Hash.new { |h, k| h[k] = [] }
+ @run_once = Hash.new { |h, k| h[k] = [] }
+ end
+ end
+
+ # Declares a block that will be executed when a Rails component is fully
+ # loaded.
+ #
+ # Options:
+ #
+ # * :yield - Yields the object that run_load_hooks to +block+.
+ # * :run_once - Given +block+ will run only once.
+ def on_load(name, options = {}, &block)
+ @loaded[name].each do |base|
+ execute_hook(name, base, options, block)
+ end
+
+ @load_hooks[name] << [block, options]
+ end
+
+ def run_load_hooks(name, base = Object)
+ @loaded[name] << base
+ @load_hooks[name].each do |hook, options|
+ execute_hook(name, base, options, hook)
+ end
+ end
+
+ private
+
+ def with_execution_control(name, block, once)
+ unless @run_once[name].include?(block)
+ @run_once[name] << block if once
+
+ yield
+ end
+ end
+
+ def execute_hook(name, base, options, block)
+ with_execution_control(name, block, options[:run_once]) do
+ if options[:yield]
+ block.call(base)
+ else
+ base.instance_eval(&block)
+ end
+ end
+ end
+ end
+
+ extend LazyLoadHooks
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/locale/en.yml b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/locale/en.yml
new file mode 100644
index 0000000..c64b759
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/locale/en.yml
@@ -0,0 +1,135 @@
+en:
+ date:
+ formats:
+ # Use the strftime parameters for formats.
+ # When no format has been given, it uses default.
+ # You can provide other formats here if you like!
+ default: "%Y-%m-%d"
+ short: "%b %d"
+ long: "%B %d, %Y"
+
+ day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
+ abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
+
+ # Don't forget the nil at the beginning; there's no such thing as a 0th month
+ month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
+ abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
+ # Used in date_select and datetime_select.
+ order:
+ - year
+ - month
+ - day
+
+ time:
+ formats:
+ default: "%a, %d %b %Y %H:%M:%S %z"
+ short: "%d %b %H:%M"
+ long: "%B %d, %Y %H:%M"
+ am: "am"
+ pm: "pm"
+
+# Used in array.to_sentence.
+ support:
+ array:
+ words_connector: ", "
+ two_words_connector: " and "
+ last_word_connector: ", and "
+ number:
+ # Used in NumberHelper.number_to_delimited()
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
+ format:
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
+ separator: "."
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
+ delimiter: ","
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
+ precision: 3
+ # If set to true, precision will mean the number of significant digits instead
+ # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
+ significant: false
+ # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
+ strip_insignificant_zeros: false
+
+ # Used in NumberHelper.number_to_currency()
+ currency:
+ format:
+ # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
+ format: "%u%n"
+ unit: "$"
+ # These five are to override number.format and are optional
+ separator: "."
+ delimiter: ","
+ precision: 2
+ significant: false
+ strip_insignificant_zeros: false
+
+ # Used in NumberHelper.number_to_percentage()
+ percentage:
+ format:
+ # These five are to override number.format and are optional
+ # separator:
+ delimiter: ""
+ # precision:
+ # significant: false
+ # strip_insignificant_zeros: false
+ format: "%n%"
+
+ # Used in NumberHelper.number_to_rounded()
+ precision:
+ format:
+ # These five are to override number.format and are optional
+ # separator:
+ delimiter: ""
+ # precision:
+ # significant: false
+ # strip_insignificant_zeros: false
+
+ # Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
+ human:
+ format:
+ # These five are to override number.format and are optional
+ # separator:
+ delimiter: ""
+ precision: 3
+ significant: true
+ strip_insignificant_zeros: true
+ # Used in number_to_human_size()
+ storage_units:
+ # Storage units output formatting.
+ # %u is the storage unit, %n is the number (default: 2 MB)
+ format: "%n %u"
+ units:
+ byte:
+ one: "Byte"
+ other: "Bytes"
+ kb: "KB"
+ mb: "MB"
+ gb: "GB"
+ tb: "TB"
+ pb: "PB"
+ eb: "EB"
+ # Used in NumberHelper.number_to_human()
+ decimal_units:
+ format: "%n %u"
+ # Decimal units output formatting
+ # By default we will only quantify some of the exponents
+ # but the commented ones might be defined or overridden
+ # by the user.
+ units:
+ # femto: Quadrillionth
+ # pico: Trillionth
+ # nano: Billionth
+ # micro: Millionth
+ # mili: Thousandth
+ # centi: Hundredth
+ # deci: Tenth
+ unit: ""
+ # ten:
+ # one: Ten
+ # other: Tens
+ # hundred: Hundred
+ thousand: Thousand
+ million: Million
+ billion: Billion
+ trillion: Trillion
+ quadrillion: Quadrillion
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/log_subscriber.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/log_subscriber.rb
new file mode 100644
index 0000000..0f7be06
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/log_subscriber.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/attribute_accessors"
+require "active_support/core_ext/class/attribute"
+require "active_support/subscriber"
+
+module ActiveSupport
+ # ActiveSupport::LogSubscriber is an object set to consume
+ # ActiveSupport::Notifications with the sole purpose of logging them.
+ # The log subscriber dispatches notifications to a registered object based
+ # on its given namespace.
+ #
+ # An example would be Active Record log subscriber responsible for logging
+ # queries:
+ #
+ # module ActiveRecord
+ # class LogSubscriber < ActiveSupport::LogSubscriber
+ # def sql(event)
+ # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
+ # end
+ # end
+ # end
+ #
+ # And it's finally registered as:
+ #
+ # ActiveRecord::LogSubscriber.attach_to :active_record
+ #
+ # Since we need to know all instance methods before attaching the log
+ # subscriber, the line above should be called after your
+ # ActiveRecord::LogSubscriber definition.
+ #
+ # After configured, whenever a "sql.active_record" notification is published,
+ # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
+ # the sql method.
+ #
+ # Log subscriber also has some helpers to deal with logging and automatically
+ # flushes all logs when the request finishes (via action_dispatch.callback
+ # notification) in a Rails environment.
+ class LogSubscriber < Subscriber
+ # Embed in a String to clear all previous ANSI sequences.
+ CLEAR = "\e[0m"
+ BOLD = "\e[1m"
+
+ # Colors
+ BLACK = "\e[30m"
+ RED = "\e[31m"
+ GREEN = "\e[32m"
+ YELLOW = "\e[33m"
+ BLUE = "\e[34m"
+ MAGENTA = "\e[35m"
+ CYAN = "\e[36m"
+ WHITE = "\e[37m"
+
+ mattr_accessor :colorize_logging, default: true
+
+ class << self
+ def logger
+ @logger ||= if defined?(Rails) && Rails.respond_to?(:logger)
+ Rails.logger
+ end
+ end
+
+ attr_writer :logger
+
+ def log_subscribers
+ subscribers
+ end
+
+ # Flush all log_subscribers' logger.
+ def flush_all!
+ logger.flush if logger.respond_to?(:flush)
+ end
+ end
+
+ def logger
+ LogSubscriber.logger
+ end
+
+ def start(name, id, payload)
+ super if logger
+ end
+
+ def finish(name, id, payload)
+ super if logger
+ rescue => e
+ if logger
+ logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
+ end
+ end
+
+ private
+
+ %w(info debug warn error fatal unknown).each do |level|
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{level}(progname = nil, &block)
+ logger.#{level}(progname, &block) if logger
+ end
+ METHOD
+ end
+
+ # Set color by using a symbol or one of the defined constants. If a third
+ # option is set to +true+, it also adds bold to the string. This is based
+ # on the Highline implementation and will automatically append CLEAR to the
+ # end of the returned String.
+ def color(text, color, bold = false) # :doc:
+ return text unless colorize_logging
+ color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
+ bold = bold ? BOLD : ""
+ "#{bold}#{color}#{text}#{CLEAR}"
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/log_subscriber/test_helper.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/log_subscriber/test_helper.rb
new file mode 100644
index 0000000..3f19ef5
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/log_subscriber/test_helper.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require "active_support/log_subscriber"
+require "active_support/logger"
+require "active_support/notifications"
+
+module ActiveSupport
+ class LogSubscriber
+ # Provides some helpers to deal with testing log subscribers by setting up
+ # notifications. Take for instance Active Record subscriber tests:
+ #
+ # class SyncLogSubscriberTest < ActiveSupport::TestCase
+ # include ActiveSupport::LogSubscriber::TestHelper
+ #
+ # setup do
+ # ActiveRecord::LogSubscriber.attach_to(:active_record)
+ # end
+ #
+ # def test_basic_query_logging
+ # Developer.all.to_a
+ # wait
+ # assert_equal 1, @logger.logged(:debug).size
+ # assert_match(/Developer Load/, @logger.logged(:debug).last)
+ # assert_match(/SELECT \* FROM "developers"/, @logger.logged(:debug).last)
+ # end
+ # end
+ #
+ # All you need to do is to ensure that your log subscriber is added to
+ # Rails::Subscriber, as in the second line of the code above. The test
+ # helpers are responsible for setting up the queue, subscriptions and
+ # turning colors in logs off.
+ #
+ # The messages are available in the @logger instance, which is a logger with
+ # limited powers (it actually does not send anything to your output), and
+ # you can collect them doing @logger.logged(level), where level is the level
+ # used in logging, like info, debug, warn and so on.
+ module TestHelper
+ def setup # :nodoc:
+ @logger = MockLogger.new
+ @notifier = ActiveSupport::Notifications::Fanout.new
+
+ ActiveSupport::LogSubscriber.colorize_logging = false
+
+ @old_notifier = ActiveSupport::Notifications.notifier
+ set_logger(@logger)
+ ActiveSupport::Notifications.notifier = @notifier
+ end
+
+ def teardown # :nodoc:
+ set_logger(nil)
+ ActiveSupport::Notifications.notifier = @old_notifier
+ end
+
+ class MockLogger
+ include ActiveSupport::Logger::Severity
+
+ attr_reader :flush_count
+ attr_accessor :level
+
+ def initialize(level = DEBUG)
+ @flush_count = 0
+ @level = level
+ @logged = Hash.new { |h, k| h[k] = [] }
+ end
+
+ def method_missing(level, message = nil)
+ if block_given?
+ @logged[level] << yield
+ else
+ @logged[level] << message
+ end
+ end
+
+ def logged(level)
+ @logged[level].compact.map { |l| l.to_s.strip }
+ end
+
+ def flush
+ @flush_count += 1
+ end
+
+ ActiveSupport::Logger::Severity.constants.each do |severity|
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
+ def #{severity.downcase}?
+ #{severity} >= @level
+ end
+ EOT
+ end
+ end
+
+ # Wait notifications to be published.
+ def wait
+ @notifier.wait
+ end
+
+ # Overwrite if you use another logger in your log subscriber.
+ #
+ # def logger
+ # ActiveRecord::Base.logger = @logger
+ # end
+ def set_logger(logger)
+ ActiveSupport::LogSubscriber.logger = logger
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/logger.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/logger.rb
new file mode 100644
index 0000000..8152a18
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/logger.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+require "active_support/logger_silence"
+require "active_support/logger_thread_safe_level"
+require "logger"
+
+module ActiveSupport
+ class Logger < ::Logger
+ include ActiveSupport::LoggerThreadSafeLevel
+ include LoggerSilence
+
+ # Returns true if the logger destination matches one of the sources
+ #
+ # logger = Logger.new(STDOUT)
+ # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
+ # # => true
+ def self.logger_outputs_to?(logger, *sources)
+ logdev = logger.instance_variable_get("@logdev")
+ logger_source = logdev.dev if logdev.respond_to?(:dev)
+ sources.any? { |source| source == logger_source }
+ end
+
+ # Broadcasts logs to multiple loggers.
+ def self.broadcast(logger) # :nodoc:
+ Module.new do
+ define_method(:add) do |*args, &block|
+ logger.add(*args, &block)
+ super(*args, &block)
+ end
+
+ define_method(:<<) do |x|
+ logger << x
+ super(x)
+ end
+
+ define_method(:close) do
+ logger.close
+ super()
+ end
+
+ define_method(:progname=) do |name|
+ logger.progname = name
+ super(name)
+ end
+
+ define_method(:formatter=) do |formatter|
+ logger.formatter = formatter
+ super(formatter)
+ end
+
+ define_method(:level=) do |level|
+ logger.level = level
+ super(level)
+ end
+
+ define_method(:local_level=) do |level|
+ logger.local_level = level if logger.respond_to?(:local_level=)
+ super(level) if respond_to?(:local_level=)
+ end
+
+ define_method(:silence) do |level = Logger::ERROR, &block|
+ if logger.respond_to?(:silence)
+ logger.silence(level) do
+ if defined?(super)
+ super(level, &block)
+ else
+ block.call(self)
+ end
+ end
+ else
+ if defined?(super)
+ super(level, &block)
+ else
+ block.call(self)
+ end
+ end
+ end
+ end
+ end
+
+ def initialize(*args)
+ super
+ @formatter = SimpleFormatter.new
+ after_initialize if respond_to? :after_initialize
+ end
+
+ def add(severity, message = nil, progname = nil, &block)
+ return true if @logdev.nil? || (severity || UNKNOWN) < level
+ super
+ end
+
+ Logger::Severity.constants.each do |severity|
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
+ def #{severity.downcase}? # def debug?
+ Logger::#{severity} >= level # DEBUG >= level
+ end # end
+ EOT
+ end
+
+ # Simple formatter which only displays the message.
+ class SimpleFormatter < ::Logger::Formatter
+ # This method is invoked when a log event occurs
+ def call(severity, timestamp, progname, msg)
+ "#{String === msg ? msg : msg.inspect}\n"
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/logger_silence.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/logger_silence.rb
new file mode 100644
index 0000000..89f32b6
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/logger_silence.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require "active_support/concern"
+require "active_support/core_ext/module/attribute_accessors"
+require "concurrent"
+
+module LoggerSilence
+ extend ActiveSupport::Concern
+
+ included do
+ cattr_accessor :silencer, default: true
+ end
+
+ # Silences the logger for the duration of the block.
+ def silence(temporary_level = Logger::ERROR)
+ if silencer
+ begin
+ old_local_level = local_level
+ self.local_level = temporary_level
+
+ yield self
+ ensure
+ self.local_level = old_local_level
+ end
+ else
+ yield self
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/logger_thread_safe_level.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/logger_thread_safe_level.rb
new file mode 100644
index 0000000..52295d7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/logger_thread_safe_level.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require "active_support/concern"
+require "fiber"
+
+module ActiveSupport
+ module LoggerThreadSafeLevel # :nodoc:
+ extend ActiveSupport::Concern
+
+ def after_initialize
+ @local_levels = Concurrent::Map.new(initial_capacity: 2)
+ end
+
+ def local_log_id
+ Fiber.current.__id__
+ end
+
+ def local_level
+ @local_levels[local_log_id]
+ end
+
+ def local_level=(level)
+ if level
+ @local_levels[local_log_id] = level
+ else
+ @local_levels.delete(local_log_id)
+ end
+ end
+
+ def level
+ local_level || super
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/message_encryptor.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/message_encryptor.rb
new file mode 100644
index 0000000..8b73270
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/message_encryptor.rb
@@ -0,0 +1,229 @@
+# frozen_string_literal: true
+
+require "openssl"
+require "base64"
+require "active_support/core_ext/array/extract_options"
+require "active_support/core_ext/module/attribute_accessors"
+require "active_support/message_verifier"
+require "active_support/messages/metadata"
+
+module ActiveSupport
+ # MessageEncryptor is a simple way to encrypt values which get stored
+ # somewhere you don't trust.
+ #
+ # The cipher text and initialization vector are base64 encoded and returned
+ # to you.
+ #
+ # This can be used in situations similar to the MessageVerifier, but
+ # where you don't want users to be able to determine the value of the payload.
+ #
+ # len = ActiveSupport::MessageEncryptor.key_len
+ # salt = SecureRandom.random_bytes(len)
+ # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, len) # => "\x89\xE0\x156\xAC..."
+ # crypt = ActiveSupport::MessageEncryptor.new(key) # => #
+ # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
+ # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
+ #
+ # === Confining messages to a specific purpose
+ #
+ # By default any message can be used throughout your app. But they can also be
+ # confined to a specific +:purpose+.
+ #
+ # token = crypt.encrypt_and_sign("this is the chair", purpose: :login)
+ #
+ # Then that same purpose must be passed when verifying to get the data back out:
+ #
+ # crypt.decrypt_and_verify(token, purpose: :login) # => "this is the chair"
+ # crypt.decrypt_and_verify(token, purpose: :shipping) # => nil
+ # crypt.decrypt_and_verify(token) # => nil
+ #
+ # Likewise, if a message has no purpose it won't be returned when verifying with
+ # a specific purpose.
+ #
+ # token = crypt.encrypt_and_sign("the conversation is lively")
+ # crypt.decrypt_and_verify(token, purpose: :scare_tactics) # => nil
+ # crypt.decrypt_and_verify(token) # => "the conversation is lively"
+ #
+ # === Making messages expire
+ #
+ # By default messages last forever and verifying one year from now will still
+ # return the original value. But messages can be set to expire at a given
+ # time with +:expires_in+ or +:expires_at+.
+ #
+ # crypt.encrypt_and_sign(parcel, expires_in: 1.month)
+ # crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
+ #
+ # Then the messages can be verified and returned upto the expire time.
+ # Thereafter, verifying returns +nil+.
+ #
+ # === Rotating keys
+ #
+ # MessageEncryptor also supports rotating out old configurations by falling
+ # back to a stack of encryptors. Call +rotate+ to build and add an encryptor
+ # so +decrypt_and_verify+ will also try the fallback.
+ #
+ # By default any rotated encryptors use the values of the primary
+ # encryptor unless specified otherwise.
+ #
+ # You'd give your encryptor the new defaults:
+ #
+ # crypt = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
+ #
+ # Then gradually rotate the old values out by adding them as fallbacks. Any message
+ # generated with the old values will then work until the rotation is removed.
+ #
+ # crypt.rotate old_secret # Fallback to an old secret instead of @secret.
+ # crypt.rotate cipher: "aes-256-cbc" # Fallback to an old cipher instead of aes-256-gcm.
+ #
+ # Though if both the secret and the cipher was changed at the same time,
+ # the above should be combined into:
+ #
+ # crypt.rotate old_secret, cipher: "aes-256-cbc"
+ class MessageEncryptor
+ prepend Messages::Rotator::Encryptor
+
+ cattr_accessor :use_authenticated_message_encryption, instance_accessor: false, default: false
+
+ class << self
+ def default_cipher #:nodoc:
+ if use_authenticated_message_encryption
+ "aes-256-gcm"
+ else
+ "aes-256-cbc"
+ end
+ end
+ end
+
+ module NullSerializer #:nodoc:
+ def self.load(value)
+ value
+ end
+
+ def self.dump(value)
+ value
+ end
+ end
+
+ module NullVerifier #:nodoc:
+ def self.verify(value)
+ value
+ end
+
+ def self.generate(value)
+ value
+ end
+ end
+
+ class InvalidMessage < StandardError; end
+ OpenSSLCipherError = OpenSSL::Cipher::CipherError
+
+ # Initialize a new MessageEncryptor. +secret+ must be at least as long as
+ # the cipher key size. For the default 'aes-256-gcm' cipher, this is 256
+ # bits. If you are using a user-entered secret, you can generate a suitable
+ # key by using ActiveSupport::KeyGenerator or a similar key
+ # derivation function.
+ #
+ # First additional parameter is used as the signature key for +MessageVerifier+.
+ # This allows you to specify keys to encrypt and sign data.
+ #
+ # ActiveSupport::MessageEncryptor.new('secret', 'signature_secret')
+ #
+ # Options:
+ # * :cipher - Cipher to use. Can be any cipher returned by
+ # OpenSSL::Cipher.ciphers. Default is 'aes-256-gcm'.
+ # * :digest - String of digest to use for signing. Default is
+ # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'.
+ # * :serializer - Object serializer to use. Default is +Marshal+.
+ def initialize(secret, *signature_key_or_options)
+ options = signature_key_or_options.extract_options!
+ sign_secret = signature_key_or_options.first
+ @secret = secret
+ @sign_secret = sign_secret
+ @cipher = options[:cipher] || self.class.default_cipher
+ @digest = options[:digest] || "SHA1" unless aead_mode?
+ @verifier = resolve_verifier
+ @serializer = options[:serializer] || Marshal
+ end
+
+ # Encrypt and sign a message. We need to sign the message in order to avoid
+ # padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
+ def encrypt_and_sign(value, expires_at: nil, expires_in: nil, purpose: nil)
+ verifier.generate(_encrypt(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose))
+ end
+
+ # Decrypt and verify a message. We need to verify the message in order to
+ # avoid padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
+ def decrypt_and_verify(data, purpose: nil, **)
+ _decrypt(verifier.verify(data), purpose)
+ end
+
+ # Given a cipher, returns the key length of the cipher to help generate the key of desired size
+ def self.key_len(cipher = default_cipher)
+ OpenSSL::Cipher.new(cipher).key_len
+ end
+
+ private
+ def _encrypt(value, **metadata_options)
+ cipher = new_cipher
+ cipher.encrypt
+ cipher.key = @secret
+
+ # Rely on OpenSSL for the initialization vector
+ iv = cipher.random_iv
+ cipher.auth_data = "" if aead_mode?
+
+ encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), metadata_options))
+ encrypted_data << cipher.final
+
+ blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
+ blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
+ blob
+ end
+
+ def _decrypt(encrypted_message, purpose)
+ cipher = new_cipher
+ encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }
+
+ # Currently the OpenSSL bindings do not raise an error if auth_tag is
+ # truncated, which would allow an attacker to easily forge it. See
+ # https://github.com/ruby/openssl/issues/63
+ raise InvalidMessage if aead_mode? && (auth_tag.nil? || auth_tag.bytes.length != 16)
+
+ cipher.decrypt
+ cipher.key = @secret
+ cipher.iv = iv
+ if aead_mode?
+ cipher.auth_tag = auth_tag
+ cipher.auth_data = ""
+ end
+
+ decrypted_data = cipher.update(encrypted_data)
+ decrypted_data << cipher.final
+
+ message = Messages::Metadata.verify(decrypted_data, purpose)
+ @serializer.load(message) if message
+ rescue OpenSSLCipherError, TypeError, ArgumentError
+ raise InvalidMessage
+ end
+
+ def new_cipher
+ OpenSSL::Cipher.new(@cipher)
+ end
+
+ def verifier
+ @verifier
+ end
+
+ def aead_mode?
+ @aead_mode ||= new_cipher.authenticated?
+ end
+
+ def resolve_verifier
+ if aead_mode?
+ NullVerifier
+ else
+ MessageVerifier.new(@sign_secret || @secret, digest: @digest, serializer: NullSerializer)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/message_verifier.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/message_verifier.rb
new file mode 100644
index 0000000..83c39c0
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/message_verifier.rb
@@ -0,0 +1,205 @@
+# frozen_string_literal: true
+
+require "base64"
+require "active_support/core_ext/object/blank"
+require "active_support/security_utils"
+require "active_support/messages/metadata"
+require "active_support/messages/rotator"
+
+module ActiveSupport
+ # +MessageVerifier+ makes it easy to generate and verify messages which are
+ # signed to prevent tampering.
+ #
+ # This is useful for cases like remember-me tokens and auto-unsubscribe links
+ # where the session store isn't suitable or available.
+ #
+ # Remember Me:
+ # cookies[:remember_me] = @verifier.generate([@user.id, 2.weeks.from_now])
+ #
+ # In the authentication filter:
+ #
+ # id, time = @verifier.verify(cookies[:remember_me])
+ # if Time.now < time
+ # self.current_user = User.find(id)
+ # end
+ #
+ # By default it uses Marshal to serialize the message. If you want to use
+ # another serialization method, you can set the serializer in the options
+ # hash upon initialization:
+ #
+ # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: YAML)
+ #
+ # +MessageVerifier+ creates HMAC signatures using SHA1 hash algorithm by default.
+ # If you want to use a different hash algorithm, you can change it by providing
+ # +:digest+ key as an option while initializing the verifier:
+ #
+ # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', digest: 'SHA256')
+ #
+ # === Confining messages to a specific purpose
+ #
+ # By default any message can be used throughout your app. But they can also be
+ # confined to a specific +:purpose+.
+ #
+ # token = @verifier.generate("this is the chair", purpose: :login)
+ #
+ # Then that same purpose must be passed when verifying to get the data back out:
+ #
+ # @verifier.verified(token, purpose: :login) # => "this is the chair"
+ # @verifier.verified(token, purpose: :shipping) # => nil
+ # @verifier.verified(token) # => nil
+ #
+ # @verifier.verify(token, purpose: :login) # => "this is the chair"
+ # @verifier.verify(token, purpose: :shipping) # => ActiveSupport::MessageVerifier::InvalidSignature
+ # @verifier.verify(token) # => ActiveSupport::MessageVerifier::InvalidSignature
+ #
+ # Likewise, if a message has no purpose it won't be returned when verifying with
+ # a specific purpose.
+ #
+ # token = @verifier.generate("the conversation is lively")
+ # @verifier.verified(token, purpose: :scare_tactics) # => nil
+ # @verifier.verified(token) # => "the conversation is lively"
+ #
+ # @verifier.verify(token, purpose: :scare_tactics) # => ActiveSupport::MessageVerifier::InvalidSignature
+ # @verifier.verify(token) # => "the conversation is lively"
+ #
+ # === Making messages expire
+ #
+ # By default messages last forever and verifying one year from now will still
+ # return the original value. But messages can be set to expire at a given
+ # time with +:expires_in+ or +:expires_at+.
+ #
+ # @verifier.generate(parcel, expires_in: 1.month)
+ # @verifier.generate(doowad, expires_at: Time.now.end_of_year)
+ #
+ # Then the messages can be verified and returned upto the expire time.
+ # Thereafter, the +verified+ method returns +nil+ while +verify+ raises
+ # ActiveSupport::MessageVerifier::InvalidSignature.
+ #
+ # === Rotating keys
+ #
+ # MessageVerifier also supports rotating out old configurations by falling
+ # back to a stack of verifiers. Call +rotate+ to build and add a verifier to
+ # so either +verified+ or +verify+ will also try verifying with the fallback.
+ #
+ # By default any rotated verifiers use the values of the primary
+ # verifier unless specified otherwise.
+ #
+ # You'd give your verifier the new defaults:
+ #
+ # verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512", serializer: JSON)
+ #
+ # Then gradually rotate the old values out by adding them as fallbacks. Any message
+ # generated with the old values will then work until the rotation is removed.
+ #
+ # verifier.rotate old_secret # Fallback to an old secret instead of @secret.
+ # verifier.rotate digest: "SHA256" # Fallback to an old digest instead of SHA512.
+ # verifier.rotate serializer: Marshal # Fallback to an old serializer instead of JSON.
+ #
+ # Though the above would most likely be combined into one rotation:
+ #
+ # verifier.rotate old_secret, digest: "SHA256", serializer: Marshal
+ class MessageVerifier
+ prepend Messages::Rotator::Verifier
+
+ class InvalidSignature < StandardError; end
+
+ def initialize(secret, options = {})
+ raise ArgumentError, "Secret should not be nil." unless secret
+ @secret = secret
+ @digest = options[:digest] || "SHA1"
+ @serializer = options[:serializer] || Marshal
+ end
+
+ # Checks if a signed message could have been generated by signing an object
+ # with the +MessageVerifier+'s secret.
+ #
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
+ # signed_message = verifier.generate 'a private message'
+ # verifier.valid_message?(signed_message) # => true
+ #
+ # tampered_message = signed_message.chop # editing the message invalidates the signature
+ # verifier.valid_message?(tampered_message) # => false
+ def valid_message?(signed_message)
+ return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
+
+ data, digest = signed_message.split("--".freeze)
+ data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
+ end
+
+ # Decodes the signed message using the +MessageVerifier+'s secret.
+ #
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
+ #
+ # signed_message = verifier.generate 'a private message'
+ # verifier.verified(signed_message) # => 'a private message'
+ #
+ # Returns +nil+ if the message was not signed with the same secret.
+ #
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
+ # other_verifier.verified(signed_message) # => nil
+ #
+ # Returns +nil+ if the message is not Base64-encoded.
+ #
+ # invalid_message = "f--46a0120593880c733a53b6dad75b42ddc1c8996d"
+ # verifier.verified(invalid_message) # => nil
+ #
+ # Raises any error raised while decoding the signed message.
+ #
+ # incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
+ # verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format
+ def verified(signed_message, purpose: nil, **)
+ if valid_message?(signed_message)
+ begin
+ data = signed_message.split("--".freeze)[0]
+ message = Messages::Metadata.verify(decode(data), purpose)
+ @serializer.load(message) if message
+ rescue ArgumentError => argument_error
+ return if argument_error.message.include?("invalid base64")
+ raise
+ end
+ end
+ end
+
+ # Decodes the signed message using the +MessageVerifier+'s secret.
+ #
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
+ # signed_message = verifier.generate 'a private message'
+ #
+ # verifier.verify(signed_message) # => 'a private message'
+ #
+ # Raises +InvalidSignature+ if the message was not signed with the same
+ # secret or was not Base64-encoded.
+ #
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
+ # other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature
+ def verify(*args)
+ verified(*args) || raise(InvalidSignature)
+ end
+
+ # Generates a signed message for the provided value.
+ #
+ # The message is signed with the +MessageVerifier+'s secret. Without knowing
+ # the secret, the original value cannot be extracted from the message.
+ #
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
+ # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
+ def generate(value, expires_at: nil, expires_in: nil, purpose: nil)
+ data = encode(Messages::Metadata.wrap(@serializer.dump(value), expires_at: expires_at, expires_in: expires_in, purpose: purpose))
+ "#{data}--#{generate_digest(data)}"
+ end
+
+ private
+ def encode(data)
+ ::Base64.strict_encode64(data)
+ end
+
+ def decode(data)
+ ::Base64.strict_decode64(data)
+ end
+
+ def generate_digest(data)
+ require "openssl" unless defined?(OpenSSL)
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/messages/metadata.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/messages/metadata.rb
new file mode 100644
index 0000000..e97caac
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/messages/metadata.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require "time"
+
+module ActiveSupport
+ module Messages #:nodoc:
+ class Metadata #:nodoc:
+ def initialize(message, expires_at = nil, purpose = nil)
+ @message, @expires_at, @purpose = message, expires_at, purpose
+ end
+
+ def as_json(options = {})
+ { _rails: { message: @message, exp: @expires_at, pur: @purpose } }
+ end
+
+ class << self
+ def wrap(message, expires_at: nil, expires_in: nil, purpose: nil)
+ if expires_at || expires_in || purpose
+ JSON.encode new(encode(message), pick_expiry(expires_at, expires_in), purpose)
+ else
+ message
+ end
+ end
+
+ def verify(message, purpose)
+ extract_metadata(message).verify(purpose)
+ end
+
+ private
+ def pick_expiry(expires_at, expires_in)
+ if expires_at
+ expires_at.utc.iso8601(3)
+ elsif expires_in
+ Time.now.utc.advance(seconds: expires_in).iso8601(3)
+ end
+ end
+
+ def extract_metadata(message)
+ data = JSON.decode(message) rescue nil
+
+ if data.is_a?(Hash) && data.key?("_rails")
+ new(decode(data["_rails"]["message"]), data["_rails"]["exp"], data["_rails"]["pur"])
+ else
+ new(message)
+ end
+ end
+
+ def encode(message)
+ ::Base64.strict_encode64(message)
+ end
+
+ def decode(message)
+ ::Base64.strict_decode64(message)
+ end
+ end
+
+ def verify(purpose)
+ @message if match?(purpose) && fresh?
+ end
+
+ private
+ def match?(purpose)
+ @purpose.to_s == purpose.to_s
+ end
+
+ def fresh?
+ @expires_at.nil? || Time.now.utc < Time.iso8601(@expires_at)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/messages/rotation_configuration.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/messages/rotation_configuration.rb
new file mode 100644
index 0000000..bd50d6d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/messages/rotation_configuration.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Messages
+ class RotationConfiguration # :nodoc:
+ attr_reader :signed, :encrypted
+
+ def initialize
+ @signed, @encrypted = [], []
+ end
+
+ def rotate(kind, *args)
+ case kind
+ when :signed
+ @signed << args
+ when :encrypted
+ @encrypted << args
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/messages/rotator.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/messages/rotator.rb
new file mode 100644
index 0000000..823a399
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/messages/rotator.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Messages
+ module Rotator # :nodoc:
+ def initialize(*, **options)
+ super
+
+ @options = options
+ @rotations = []
+ end
+
+ def rotate(*secrets, **options)
+ @rotations << build_rotation(*secrets, @options.merge(options))
+ end
+
+ module Encryptor
+ include Rotator
+
+ def decrypt_and_verify(*args, on_rotation: nil, **options)
+ super
+ rescue MessageEncryptor::InvalidMessage, MessageVerifier::InvalidSignature
+ run_rotations(on_rotation) { |encryptor| encryptor.decrypt_and_verify(*args, options) } || raise
+ end
+
+ private
+ def build_rotation(secret = @secret, sign_secret = @sign_secret, options)
+ self.class.new(secret, sign_secret, options)
+ end
+ end
+
+ module Verifier
+ include Rotator
+
+ def verified(*args, on_rotation: nil, **options)
+ super || run_rotations(on_rotation) { |verifier| verifier.verified(*args, options) }
+ end
+
+ private
+ def build_rotation(secret = @secret, options)
+ self.class.new(secret, options)
+ end
+ end
+
+ private
+ def run_rotations(on_rotation)
+ @rotations.find do |rotation|
+ if message = yield(rotation) rescue next
+ on_rotation.call if on_rotation
+ return message
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/multibyte.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/multibyte.rb
new file mode 100644
index 0000000..3fe3a05
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/multibyte.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module ActiveSupport #:nodoc:
+ module Multibyte
+ autoload :Chars, "active_support/multibyte/chars"
+ autoload :Unicode, "active_support/multibyte/unicode"
+
+ # The proxy class returned when calling mb_chars. You can use this accessor
+ # to configure your own proxy class so you can support other encodings. See
+ # the ActiveSupport::Multibyte::Chars implementation for an example how to
+ # do this.
+ #
+ # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
+ def self.proxy_class=(klass)
+ @proxy_class = klass
+ end
+
+ # Returns the current proxy class.
+ def self.proxy_class
+ @proxy_class ||= ActiveSupport::Multibyte::Chars
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/multibyte/chars.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/multibyte/chars.rb
new file mode 100644
index 0000000..8152b8f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/multibyte/chars.rb
@@ -0,0 +1,235 @@
+# frozen_string_literal: true
+
+require "active_support/json"
+require "active_support/core_ext/string/access"
+require "active_support/core_ext/string/behavior"
+require "active_support/core_ext/module/delegation"
+require "active_support/core_ext/regexp"
+
+module ActiveSupport #:nodoc:
+ module Multibyte #:nodoc:
+ # Chars enables you to work transparently with UTF-8 encoding in the Ruby
+ # String class without having extensive knowledge about the encoding. A
+ # Chars object accepts a string upon initialization and proxies String
+ # methods in an encoding safe manner. All the normal String methods are also
+ # implemented on the proxy.
+ #
+ # String methods are proxied through the Chars object, and can be accessed
+ # through the +mb_chars+ method. Methods which would normally return a
+ # String object now return a Chars object so methods can be chained.
+ #
+ # 'The Perfect String '.mb_chars.downcase.strip.normalize
+ # # => #
+ #
+ # Chars objects are perfectly interchangeable with String objects as long as
+ # no explicit class checks are made. If certain methods do explicitly check
+ # the class, call +to_s+ before you pass chars objects to them.
+ #
+ # bad.explicit_checking_method 'T'.mb_chars.downcase.to_s
+ #
+ # The default Chars implementation assumes that the encoding of the string
+ # is UTF-8, if you want to handle different encodings you can write your own
+ # multibyte string handler and configure it through
+ # ActiveSupport::Multibyte.proxy_class.
+ #
+ # class CharsForUTF32
+ # def size
+ # @wrapped_string.size / 4
+ # end
+ #
+ # def self.accepts?(string)
+ # string.length % 4 == 0
+ # end
+ # end
+ #
+ # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
+ class Chars
+ include Comparable
+ attr_reader :wrapped_string
+ alias to_s wrapped_string
+ alias to_str wrapped_string
+
+ delegate :<=>, :=~, :acts_like_string?, to: :wrapped_string
+
+ # Creates a new Chars instance by wrapping _string_.
+ def initialize(string)
+ @wrapped_string = string
+ @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
+ end
+
+ # Forward all undefined methods to the wrapped string.
+ def method_missing(method, *args, &block)
+ result = @wrapped_string.__send__(method, *args, &block)
+ if /!$/.match?(method)
+ self if result
+ else
+ result.kind_of?(String) ? chars(result) : result
+ end
+ end
+
+ # Returns +true+ if _obj_ responds to the given method. Private methods
+ # are included in the search only if the optional second parameter
+ # evaluates to +true+.
+ def respond_to_missing?(method, include_private)
+ @wrapped_string.respond_to?(method, include_private)
+ end
+
+ # Returns +true+ when the proxy class can handle the string. Returns
+ # +false+ otherwise.
+ def self.consumes?(string)
+ string.encoding == Encoding::UTF_8
+ end
+
+ # Works just like String#split, with the exception that the items
+ # in the resulting list are Chars instances instead of String. This makes
+ # chaining methods easier.
+ #
+ # 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
+ def split(*args)
+ @wrapped_string.split(*args).map { |i| self.class.new(i) }
+ end
+
+ # Works like String#slice!, but returns an instance of
+ # Chars, or +nil+ if the string was not modified. The string will not be
+ # modified if the range given is out of bounds
+ #
+ # string = 'Welcome'
+ # string.mb_chars.slice!(3) # => #
+ # string # => 'Welome'
+ # string.mb_chars.slice!(0..3) # => #
+ # string # => 'me'
+ def slice!(*args)
+ string_sliced = @wrapped_string.slice!(*args)
+ if string_sliced
+ chars(string_sliced)
+ end
+ end
+
+ # Reverses all characters in the string.
+ #
+ # 'Café'.mb_chars.reverse.to_s # => 'éfaC'
+ def reverse
+ chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack("U*"))
+ end
+
+ # Limits the byte size of the string to a number of bytes without breaking
+ # characters. Usable when the storage for a string is limited for some
+ # reason.
+ #
+ # 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
+ def limit(limit)
+ slice(0...translate_offset(limit))
+ end
+
+ # Converts characters in the string to uppercase.
+ #
+ # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
+ def upcase
+ chars Unicode.upcase(@wrapped_string)
+ end
+
+ # Converts characters in the string to lowercase.
+ #
+ # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
+ def downcase
+ chars Unicode.downcase(@wrapped_string)
+ end
+
+ # Converts characters in the string to the opposite case.
+ #
+ # 'El Cañón'.mb_chars.swapcase.to_s # => "eL cAÑÓN"
+ def swapcase
+ chars Unicode.swapcase(@wrapped_string)
+ end
+
+ # Converts the first character to uppercase and the remainder to lowercase.
+ #
+ # 'über'.mb_chars.capitalize.to_s # => "Über"
+ def capitalize
+ (slice(0) || chars("")).upcase + (slice(1..-1) || chars("")).downcase
+ end
+
+ # Capitalizes the first letter of every word, when possible.
+ #
+ # "ÉL QUE SE ENTERÓ".mb_chars.titleize.to_s # => "Él Que Se Enteró"
+ # "日本語".mb_chars.titleize.to_s # => "日本語"
+ def titleize
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1) })
+ end
+ alias_method :titlecase, :titleize
+
+ # Returns the KC normalization of the string by default. NFKC is
+ # considered the best normalization form for passing strings to databases
+ # and validations.
+ #
+ # * form - The form you want to normalize in. Should be one of the following:
+ # :c, :kc, :d, or :kd. Default is
+ # ActiveSupport::Multibyte::Unicode.default_normalization_form
+ def normalize(form = nil)
+ chars(Unicode.normalize(@wrapped_string, form))
+ end
+
+ # Performs canonical decomposition on all the characters.
+ #
+ # 'é'.length # => 2
+ # 'é'.mb_chars.decompose.to_s.length # => 3
+ def decompose
+ chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack("U*"))
+ end
+
+ # Performs composition on all the characters.
+ #
+ # 'é'.length # => 3
+ # 'é'.mb_chars.compose.to_s.length # => 2
+ def compose
+ chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack("U*"))
+ end
+
+ # Returns the number of grapheme clusters in the string.
+ #
+ # 'क्षि'.mb_chars.length # => 4
+ # 'क्षि'.mb_chars.grapheme_length # => 3
+ def grapheme_length
+ Unicode.unpack_graphemes(@wrapped_string).length
+ end
+
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
+ # resulting in a valid UTF-8 string.
+ #
+ # Passing +true+ will forcibly tidy all bytes, assuming that the string's
+ # encoding is entirely CP1252 or ISO-8859-1.
+ def tidy_bytes(force = false)
+ chars(Unicode.tidy_bytes(@wrapped_string, force))
+ end
+
+ def as_json(options = nil) #:nodoc:
+ to_s.as_json(options)
+ end
+
+ %w(capitalize downcase reverse tidy_bytes upcase).each do |method|
+ define_method("#{method}!") do |*args|
+ @wrapped_string = send(method, *args).to_s
+ self
+ end
+ end
+
+ private
+
+ def translate_offset(byte_offset)
+ return nil if byte_offset.nil?
+ return 0 if @wrapped_string == ""
+
+ begin
+ @wrapped_string.byteslice(0...byte_offset).unpack("U*").length
+ rescue ArgumentError
+ byte_offset -= 1
+ retry
+ end
+ end
+
+ def chars(string)
+ self.class.new(string)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/multibyte/unicode.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/multibyte/unicode.rb
new file mode 100644
index 0000000..f923061
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/multibyte/unicode.rb
@@ -0,0 +1,394 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Multibyte
+ module Unicode
+ extend self
+
+ # A list of all available normalization forms.
+ # See http://www.unicode.org/reports/tr15/tr15-29.html for more
+ # information about normalization.
+ NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
+
+ # The Unicode version that is supported by the implementation
+ UNICODE_VERSION = "9.0.0"
+
+ # The default normalization used for operations that require
+ # normalization. It can be set to any of the normalizations
+ # in NORMALIZATION_FORMS.
+ #
+ # ActiveSupport::Multibyte::Unicode.default_normalization_form = :c
+ attr_accessor :default_normalization_form
+ @default_normalization_form = :kc
+
+ # Hangul character boundaries and properties
+ HANGUL_SBASE = 0xAC00
+ HANGUL_LBASE = 0x1100
+ HANGUL_VBASE = 0x1161
+ HANGUL_TBASE = 0x11A7
+ HANGUL_LCOUNT = 19
+ HANGUL_VCOUNT = 21
+ HANGUL_TCOUNT = 28
+ HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
+ HANGUL_SCOUNT = 11172
+ HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
+
+ # Detect whether the codepoint is in a certain character class. Returns
+ # +true+ when it's in the specified character class and +false+ otherwise.
+ # Valid character classes are: :cr, :lf, :l,
+ # :v, :lv, :lvt and :t.
+ #
+ # Primarily used by the grapheme cluster support.
+ def in_char_class?(codepoint, classes)
+ classes.detect { |c| database.boundary[c] === codepoint } ? true : false
+ end
+
+ # Unpack the string at grapheme boundaries. Returns a list of character
+ # lists.
+ #
+ # Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
+ # Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
+ def unpack_graphemes(string)
+ codepoints = string.codepoints.to_a
+ unpacked = []
+ pos = 0
+ marker = 0
+ eoc = codepoints.length
+ while (pos < eoc)
+ pos += 1
+ previous = codepoints[pos - 1]
+ current = codepoints[pos]
+
+ # See http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
+ should_break =
+ if pos == eoc
+ true
+ # GB3. CR X LF
+ elsif previous == database.boundary[:cr] && current == database.boundary[:lf]
+ false
+ # GB4. (Control|CR|LF) ÷
+ elsif previous && in_char_class?(previous, [:control, :cr, :lf])
+ true
+ # GB5. ÷ (Control|CR|LF)
+ elsif in_char_class?(current, [:control, :cr, :lf])
+ true
+ # GB6. L X (L|V|LV|LVT)
+ elsif database.boundary[:l] === previous && in_char_class?(current, [:l, :v, :lv, :lvt])
+ false
+ # GB7. (LV|V) X (V|T)
+ elsif in_char_class?(previous, [:lv, :v]) && in_char_class?(current, [:v, :t])
+ false
+ # GB8. (LVT|T) X (T)
+ elsif in_char_class?(previous, [:lvt, :t]) && database.boundary[:t] === current
+ false
+ # GB9. X (Extend | ZWJ)
+ elsif in_char_class?(current, [:extend, :zwj])
+ false
+ # GB9a. X SpacingMark
+ elsif database.boundary[:spacingmark] === current
+ false
+ # GB9b. Prepend X
+ elsif database.boundary[:prepend] === previous
+ false
+ # GB10. (E_Base | EBG) Extend* X E_Modifier
+ elsif (marker...pos).any? { |i| in_char_class?(codepoints[i], [:e_base, :e_base_gaz]) && codepoints[i + 1...pos].all? { |c| database.boundary[:extend] === c } } && database.boundary[:e_modifier] === current
+ false
+ # GB11. ZWJ X (Glue_After_Zwj | EBG)
+ elsif database.boundary[:zwj] === previous && in_char_class?(current, [:glue_after_zwj, :e_base_gaz])
+ false
+ # GB12. ^ (RI RI)* RI X RI
+ # GB13. [^RI] (RI RI)* RI X RI
+ elsif codepoints[marker..pos].all? { |c| database.boundary[:regional_indicator] === c } && codepoints[marker..pos].count { |c| database.boundary[:regional_indicator] === c }.even?
+ false
+ # GB999. Any ÷ Any
+ else
+ true
+ end
+
+ if should_break
+ unpacked << codepoints[marker..pos - 1]
+ marker = pos
+ end
+ end
+ unpacked
+ end
+
+ # Reverse operation of unpack_graphemes.
+ #
+ # Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
+ def pack_graphemes(unpacked)
+ unpacked.flatten.pack("U*")
+ end
+
+ # Re-order codepoints so the string becomes canonical.
+ def reorder_characters(codepoints)
+ length = codepoints.length - 1
+ pos = 0
+ while pos < length do
+ cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos + 1]]
+ if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
+ codepoints[pos..pos + 1] = cp2.code, cp1.code
+ pos += (pos > 0 ? -1 : 1)
+ else
+ pos += 1
+ end
+ end
+ codepoints
+ end
+
+ # Decompose composed characters to the decomposed form.
+ def decompose(type, codepoints)
+ codepoints.inject([]) do |decomposed, cp|
+ # if it's a hangul syllable starter character
+ if HANGUL_SBASE <= cp && cp < HANGUL_SLAST
+ sindex = cp - HANGUL_SBASE
+ ncp = [] # new codepoints
+ ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
+ ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
+ tindex = sindex % HANGUL_TCOUNT
+ ncp << (HANGUL_TBASE + tindex) unless tindex == 0
+ decomposed.concat ncp
+ # if the codepoint is decomposable in with the current decomposition type
+ elsif (ncp = database.codepoints[cp].decomp_mapping) && (!database.codepoints[cp].decomp_type || type == :compatibility)
+ decomposed.concat decompose(type, ncp.dup)
+ else
+ decomposed << cp
+ end
+ end
+ end
+
+ # Compose decomposed characters to the composed form.
+ def compose(codepoints)
+ pos = 0
+ eoa = codepoints.length - 1
+ starter_pos = 0
+ starter_char = codepoints[0]
+ previous_combining_class = -1
+ while pos < eoa
+ pos += 1
+ lindex = starter_char - HANGUL_LBASE
+ # -- Hangul
+ if 0 <= lindex && lindex < HANGUL_LCOUNT
+ vindex = codepoints[starter_pos + 1] - HANGUL_VBASE rescue vindex = -1
+ if 0 <= vindex && vindex < HANGUL_VCOUNT
+ tindex = codepoints[starter_pos + 2] - HANGUL_TBASE rescue tindex = -1
+ if 0 <= tindex && tindex < HANGUL_TCOUNT
+ j = starter_pos + 2
+ eoa -= 2
+ else
+ tindex = 0
+ j = starter_pos + 1
+ eoa -= 1
+ end
+ codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
+ end
+ starter_pos += 1
+ starter_char = codepoints[starter_pos]
+ # -- Other characters
+ else
+ current_char = codepoints[pos]
+ current = database.codepoints[current_char]
+ if current.combining_class > previous_combining_class
+ if ref = database.composition_map[starter_char]
+ composition = ref[current_char]
+ else
+ composition = nil
+ end
+ unless composition.nil?
+ codepoints[starter_pos] = composition
+ starter_char = composition
+ codepoints.delete_at pos
+ eoa -= 1
+ pos -= 1
+ previous_combining_class = -1
+ else
+ previous_combining_class = current.combining_class
+ end
+ else
+ previous_combining_class = current.combining_class
+ end
+ if current.combining_class == 0
+ starter_pos = pos
+ starter_char = codepoints[pos]
+ end
+ end
+ end
+ codepoints
+ end
+
+ # Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
+ if !defined?(Rubinius)
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
+ # resulting in a valid UTF-8 string.
+ #
+ # Passing +true+ will forcibly tidy all bytes, assuming that the string's
+ # encoding is entirely CP1252 or ISO-8859-1.
+ def tidy_bytes(string, force = false)
+ return string if string.empty?
+ return recode_windows1252_chars(string) if force
+ string.scrub { |bad| recode_windows1252_chars(bad) }
+ end
+ else
+ def tidy_bytes(string, force = false)
+ return string if string.empty?
+ return recode_windows1252_chars(string) if force
+
+ # We can't transcode to the same format, so we choose a nearly-identical encoding.
+ # We're going to 'transcode' bytes from UTF-8 when possible, then fall back to
+ # CP1252 when we get errors. The final string will be 'converted' back to UTF-8
+ # before returning.
+ reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_16LE)
+
+ source = string.dup
+ out = "".force_encoding(Encoding::UTF_16LE)
+
+ loop do
+ reader.primitive_convert(source, out)
+ _, _, _, error_bytes, _ = reader.primitive_errinfo
+ break if error_bytes.nil?
+ out << error_bytes.encode(Encoding::UTF_16LE, Encoding::Windows_1252, invalid: :replace, undef: :replace)
+ end
+
+ reader.finish
+
+ out.encode!(Encoding::UTF_8)
+ end
+ end
+
+ # Returns the KC normalization of the string by default. NFKC is
+ # considered the best normalization form for passing strings to databases
+ # and validations.
+ #
+ # * string - The string to perform normalization on.
+ # * form - The form you want to normalize in. Should be one of
+ # the following: :c, :kc, :d, or :kd.
+ # Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
+ def normalize(string, form = nil)
+ form ||= @default_normalization_form
+ # See http://www.unicode.org/reports/tr15, Table 1
+ codepoints = string.codepoints.to_a
+ case form
+ when :d
+ reorder_characters(decompose(:canonical, codepoints))
+ when :c
+ compose(reorder_characters(decompose(:canonical, codepoints)))
+ when :kd
+ reorder_characters(decompose(:compatibility, codepoints))
+ when :kc
+ compose(reorder_characters(decompose(:compatibility, codepoints)))
+ else
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
+ end.pack("U*".freeze)
+ end
+
+ def downcase(string)
+ apply_mapping string, :lowercase_mapping
+ end
+
+ def upcase(string)
+ apply_mapping string, :uppercase_mapping
+ end
+
+ def swapcase(string)
+ apply_mapping string, :swapcase_mapping
+ end
+
+ # Holds data about a codepoint in the Unicode database.
+ class Codepoint
+ attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
+
+ # Initializing Codepoint object with default values
+ def initialize
+ @combining_class = 0
+ @uppercase_mapping = 0
+ @lowercase_mapping = 0
+ end
+
+ def swapcase_mapping
+ uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
+ end
+ end
+
+ # Holds static data from the Unicode database.
+ class UnicodeDatabase
+ ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
+
+ attr_writer(*ATTRIBUTES)
+
+ def initialize
+ @codepoints = Hash.new(Codepoint.new)
+ @composition_exclusion = []
+ @composition_map = {}
+ @boundary = {}
+ @cp1252 = {}
+ end
+
+ # Lazy load the Unicode database so it's only loaded when it's actually used
+ ATTRIBUTES.each do |attr_name|
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{attr_name} # def codepoints
+ load # load
+ @#{attr_name} # @codepoints
+ end # end
+ EOS
+ end
+
+ # Loads the Unicode database and returns all the internal objects of
+ # UnicodeDatabase.
+ def load
+ begin
+ @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, "rb") { |f| Marshal.load f.read }
+ rescue => e
+ raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
+ end
+
+ # Redefine the === method so we can write shorter rules for grapheme cluster breaks
+ @boundary.each_key do |k|
+ @boundary[k].instance_eval do
+ def ===(other)
+ detect { |i| i === other } ? true : false
+ end
+ end if @boundary[k].kind_of?(Array)
+ end
+
+ # define attr_reader methods for the instance variables
+ class << self
+ attr_reader(*ATTRIBUTES)
+ end
+ end
+
+ # Returns the directory in which the data files are stored.
+ def self.dirname
+ File.expand_path("../values", __dir__)
+ end
+
+ # Returns the filename for the data file for this version.
+ def self.filename
+ File.expand_path File.join(dirname, "unicode_tables.dat")
+ end
+ end
+
+ private
+
+ def apply_mapping(string, mapping)
+ database.codepoints
+ string.each_codepoint.map do |codepoint|
+ cp = database.codepoints[codepoint]
+ if cp && (ncp = cp.send(mapping)) && ncp > 0
+ ncp
+ else
+ codepoint
+ end
+ end.pack("U*")
+ end
+
+ def recode_windows1252_chars(string)
+ string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
+ end
+
+ def database
+ @database ||= UnicodeDatabase.new
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/notifications.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/notifications.rb
new file mode 100644
index 0000000..6207de8
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/notifications.rb
@@ -0,0 +1,216 @@
+# frozen_string_literal: true
+
+require "active_support/notifications/instrumenter"
+require "active_support/notifications/fanout"
+require "active_support/per_thread_registry"
+
+module ActiveSupport
+ # = Notifications
+ #
+ # ActiveSupport::Notifications provides an instrumentation API for
+ # Ruby.
+ #
+ # == Instrumenters
+ #
+ # To instrument an event you just need to do:
+ #
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
+ # render plain: 'Foo'
+ # end
+ #
+ # That first executes the block and then notifies all subscribers once done.
+ #
+ # In the example above +render+ is the name of the event, and the rest is called
+ # the _payload_. The payload is a mechanism that allows instrumenters to pass
+ # extra information to subscribers. Payloads consist of a hash whose contents
+ # are arbitrary and generally depend on the event.
+ #
+ # == Subscribers
+ #
+ # You can consume those events and the information they provide by registering
+ # a subscriber.
+ #
+ # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
+ # name # => String, name of the event (such as 'render' from above)
+ # start # => Time, when the instrumented block started execution
+ # finish # => Time, when the instrumented block ended execution
+ # id # => String, unique ID for this notification
+ # payload # => Hash, the payload
+ # end
+ #
+ # For instance, let's store all "render" events in an array:
+ #
+ # events = []
+ #
+ # ActiveSupport::Notifications.subscribe('render') do |*args|
+ # events << ActiveSupport::Notifications::Event.new(*args)
+ # end
+ #
+ # That code returns right away, you are just subscribing to "render" events.
+ # The block is saved and will be called whenever someone instruments "render":
+ #
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
+ # render plain: 'Foo'
+ # end
+ #
+ # event = events.first
+ # event.name # => "render"
+ # event.duration # => 10 (in milliseconds)
+ # event.payload # => { extra: :information }
+ #
+ # The block in the subscribe call gets the name of the event, start
+ # timestamp, end timestamp, a string with a unique identifier for that event
+ # (something like "535801666f04d0298cd6"), and a hash with the payload, in
+ # that order.
+ #
+ # If an exception happens during that particular instrumentation the payload will
+ # have a key :exception with an array of two elements as value: a string with
+ # the name of the exception class, and the exception message.
+ # The :exception_object key of the payload will have the exception
+ # itself as the value.
+ #
+ # As the previous example depicts, the class ActiveSupport::Notifications::Event
+ # is able to take the arguments as they come and provide an object-oriented
+ # interface to that data.
+ #
+ # It is also possible to pass an object which responds to call method
+ # as the second parameter to the subscribe method instead of a block:
+ #
+ # module ActionController
+ # class PageRequest
+ # def call(name, started, finished, unique_id, payload)
+ # Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
+ # end
+ # end
+ # end
+ #
+ # ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
+ #
+ # resulting in the following output within the logs including a hash with the payload:
+ #
+ # notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
+ # controller: "Devise::SessionsController",
+ # action: "new",
+ # params: {"action"=>"new", "controller"=>"devise/sessions"},
+ # format: :html,
+ # method: "GET",
+ # path: "/login/sign_in",
+ # status: 200,
+ # view_runtime: 279.3080806732178,
+ # db_runtime: 40.053
+ # }
+ #
+ # You can also subscribe to all events whose name matches a certain regexp:
+ #
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
+ # ...
+ # end
+ #
+ # and even pass no argument to subscribe, in which case you are subscribing
+ # to all events.
+ #
+ # == Temporary Subscriptions
+ #
+ # Sometimes you do not want to subscribe to an event for the entire life of
+ # the application. There are two ways to unsubscribe.
+ #
+ # WARNING: The instrumentation framework is designed for long-running subscribers,
+ # use this feature sparingly because it wipes some internal caches and that has
+ # a negative impact on performance.
+ #
+ # === Subscribe While a Block Runs
+ #
+ # You can subscribe to some event temporarily while some block runs. For
+ # example, in
+ #
+ # callback = lambda {|*args| ... }
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
+ # ...
+ # end
+ #
+ # the callback will be called for all "sql.active_record" events instrumented
+ # during the execution of the block. The callback is unsubscribed automatically
+ # after that.
+ #
+ # === Manual Unsubscription
+ #
+ # The +subscribe+ method returns a subscriber object:
+ #
+ # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
+ # ...
+ # end
+ #
+ # To prevent that block from being called anymore, just unsubscribe passing
+ # that reference:
+ #
+ # ActiveSupport::Notifications.unsubscribe(subscriber)
+ #
+ # You can also unsubscribe by passing the name of the subscriber object. Note
+ # that this will unsubscribe all subscriptions with the given name:
+ #
+ # ActiveSupport::Notifications.unsubscribe("render")
+ #
+ # == Default Queue
+ #
+ # Notifications ships with a queue implementation that consumes and publishes events
+ # to all log subscribers. You can use any queue implementation you want.
+ #
+ module Notifications
+ class << self
+ attr_accessor :notifier
+
+ def publish(name, *args)
+ notifier.publish(name, *args)
+ end
+
+ def instrument(name, payload = {})
+ if notifier.listening?(name)
+ instrumenter.instrument(name, payload) { yield payload if block_given? }
+ else
+ yield payload if block_given?
+ end
+ end
+
+ def subscribe(*args, &block)
+ notifier.subscribe(*args, &block)
+ end
+
+ def subscribed(callback, *args, &block)
+ subscriber = subscribe(*args, &callback)
+ yield
+ ensure
+ unsubscribe(subscriber)
+ end
+
+ def unsubscribe(subscriber_or_name)
+ notifier.unsubscribe(subscriber_or_name)
+ end
+
+ def instrumenter
+ InstrumentationRegistry.instance.instrumenter_for(notifier)
+ end
+ end
+
+ # This class is a registry which holds all of the +Instrumenter+ objects
+ # in a particular thread local. To access the +Instrumenter+ object for a
+ # particular +notifier+, you can call the following method:
+ #
+ # InstrumentationRegistry.instrumenter_for(notifier)
+ #
+ # The instrumenters for multiple notifiers are held in a single instance of
+ # this class.
+ class InstrumentationRegistry # :nodoc:
+ extend ActiveSupport::PerThreadRegistry
+
+ def initialize
+ @registry = {}
+ end
+
+ def instrumenter_for(notifier)
+ @registry[notifier] ||= Instrumenter.new(notifier)
+ end
+ end
+
+ self.notifier = Fanout.new
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/notifications/fanout.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/notifications/fanout.rb
new file mode 100644
index 0000000..f4a766f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/notifications/fanout.rb
@@ -0,0 +1,159 @@
+# frozen_string_literal: true
+
+require "mutex_m"
+require "concurrent/map"
+
+module ActiveSupport
+ module Notifications
+ # This is a default queue implementation that ships with Notifications.
+ # It just pushes events to all registered log subscribers.
+ #
+ # This class is thread safe. All methods are reentrant.
+ class Fanout
+ include Mutex_m
+
+ def initialize
+ @subscribers = []
+ @listeners_for = Concurrent::Map.new
+ super
+ end
+
+ def subscribe(pattern = nil, callable = nil, &block)
+ subscriber = Subscribers.new(pattern, callable || block)
+ synchronize do
+ @subscribers << subscriber
+ @listeners_for.clear
+ end
+ subscriber
+ end
+
+ def unsubscribe(subscriber_or_name)
+ synchronize do
+ case subscriber_or_name
+ when String
+ @subscribers.reject! { |s| s.matches?(subscriber_or_name) }
+ else
+ @subscribers.delete(subscriber_or_name)
+ end
+
+ @listeners_for.clear
+ end
+ end
+
+ def start(name, id, payload)
+ listeners_for(name).each { |s| s.start(name, id, payload) }
+ end
+
+ def finish(name, id, payload, listeners = listeners_for(name))
+ listeners.each { |s| s.finish(name, id, payload) }
+ end
+
+ def publish(name, *args)
+ listeners_for(name).each { |s| s.publish(name, *args) }
+ end
+
+ def listeners_for(name)
+ # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
+ @listeners_for[name] || synchronize do
+ # use synchronisation when accessing @subscribers
+ @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
+ end
+ end
+
+ def listening?(name)
+ listeners_for(name).any?
+ end
+
+ # This is a sync queue, so there is no waiting.
+ def wait
+ end
+
+ module Subscribers # :nodoc:
+ def self.new(pattern, listener)
+ if listener.respond_to?(:start) && listener.respond_to?(:finish)
+ subscriber = Evented.new pattern, listener
+ else
+ subscriber = Timed.new pattern, listener
+ end
+
+ unless pattern
+ AllMessages.new(subscriber)
+ else
+ subscriber
+ end
+ end
+
+ class Evented #:nodoc:
+ def initialize(pattern, delegate)
+ @pattern = pattern
+ @delegate = delegate
+ @can_publish = delegate.respond_to?(:publish)
+ end
+
+ def publish(name, *args)
+ if @can_publish
+ @delegate.publish name, *args
+ end
+ end
+
+ def start(name, id, payload)
+ @delegate.start name, id, payload
+ end
+
+ def finish(name, id, payload)
+ @delegate.finish name, id, payload
+ end
+
+ def subscribed_to?(name)
+ @pattern === name
+ end
+
+ def matches?(name)
+ @pattern && @pattern === name
+ end
+ end
+
+ class Timed < Evented # :nodoc:
+ def publish(name, *args)
+ @delegate.call name, *args
+ end
+
+ def start(name, id, payload)
+ timestack = Thread.current[:_timestack] ||= []
+ timestack.push Time.now
+ end
+
+ def finish(name, id, payload)
+ timestack = Thread.current[:_timestack]
+ started = timestack.pop
+ @delegate.call(name, started, Time.now, id, payload)
+ end
+ end
+
+ class AllMessages # :nodoc:
+ def initialize(delegate)
+ @delegate = delegate
+ end
+
+ def start(name, id, payload)
+ @delegate.start name, id, payload
+ end
+
+ def finish(name, id, payload)
+ @delegate.finish name, id, payload
+ end
+
+ def publish(name, *args)
+ @delegate.publish name, *args
+ end
+
+ def subscribed_to?(name)
+ true
+ end
+
+ alias :matches? :===
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/notifications/instrumenter.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/notifications/instrumenter.rb
new file mode 100644
index 0000000..e99f5ee
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/notifications/instrumenter.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require "securerandom"
+
+module ActiveSupport
+ module Notifications
+ # Instrumenters are stored in a thread local.
+ class Instrumenter
+ attr_reader :id
+
+ def initialize(notifier)
+ @id = unique_id
+ @notifier = notifier
+ end
+
+ # Instrument the given block by measuring the time taken to execute it
+ # and publish it. Notice that events get sent even if an error occurs
+ # in the passed-in block.
+ def instrument(name, payload = {})
+ # some of the listeners might have state
+ listeners_state = start name, payload
+ begin
+ yield payload
+ rescue Exception => e
+ payload[:exception] = [e.class.name, e.message]
+ payload[:exception_object] = e
+ raise e
+ ensure
+ finish_with_state listeners_state, name, payload
+ end
+ end
+
+ # Send a start notification with +name+ and +payload+.
+ def start(name, payload)
+ @notifier.start name, @id, payload
+ end
+
+ # Send a finish notification with +name+ and +payload+.
+ def finish(name, payload)
+ @notifier.finish name, @id, payload
+ end
+
+ def finish_with_state(listeners_state, name, payload)
+ @notifier.finish name, @id, payload, listeners_state
+ end
+
+ private
+
+ def unique_id
+ SecureRandom.hex(10)
+ end
+ end
+
+ class Event
+ attr_reader :name, :time, :transaction_id, :payload, :children
+ attr_accessor :end
+
+ def initialize(name, start, ending, transaction_id, payload)
+ @name = name
+ @payload = payload.dup
+ @time = start
+ @transaction_id = transaction_id
+ @end = ending
+ @children = []
+ @duration = nil
+ end
+
+ # Returns the difference in milliseconds between when the execution of the
+ # event started and when it ended.
+ #
+ # ActiveSupport::Notifications.subscribe('wait') do |*args|
+ # @event = ActiveSupport::Notifications::Event.new(*args)
+ # end
+ #
+ # ActiveSupport::Notifications.instrument('wait') do
+ # sleep 1
+ # end
+ #
+ # @event.duration # => 1000.138
+ def duration
+ @duration ||= 1000.0 * (self.end - time)
+ end
+
+ def <<(event)
+ @children << event
+ end
+
+ def parent_of?(event)
+ @children.include? event
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper.rb
new file mode 100644
index 0000000..8fd6e93
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper.rb
@@ -0,0 +1,371 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module NumberHelper
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :NumberConverter
+ autoload :RoundingHelper
+ autoload :NumberToRoundedConverter
+ autoload :NumberToDelimitedConverter
+ autoload :NumberToHumanConverter
+ autoload :NumberToHumanSizeConverter
+ autoload :NumberToPhoneConverter
+ autoload :NumberToCurrencyConverter
+ autoload :NumberToPercentageConverter
+ end
+
+ extend self
+
+ # Formats a +number+ into a phone number (US by default e.g., (555)
+ # 123-9876). You can customize the format in the +options+ hash.
+ #
+ # ==== Options
+ #
+ # * :area_code - Adds parentheses around the area code.
+ # * :delimiter - Specifies the delimiter to use
+ # (defaults to "-").
+ # * :extension - Specifies an extension to add to the
+ # end of the generated number.
+ # * :country_code - Sets the country code for the phone
+ # number.
+ # * :pattern - Specifies how the number is divided into three
+ # groups with the custom regexp to override the default format.
+ # ==== Examples
+ #
+ # number_to_phone(5551234) # => "555-1234"
+ # number_to_phone('5551234') # => "555-1234"
+ # number_to_phone(1235551234) # => "123-555-1234"
+ # number_to_phone(1235551234, area_code: true) # => "(123) 555-1234"
+ # number_to_phone(1235551234, delimiter: ' ') # => "123 555 1234"
+ # number_to_phone(1235551234, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
+ # number_to_phone(1235551234, country_code: 1) # => "+1-123-555-1234"
+ # number_to_phone('123a456') # => "123a456"
+ #
+ # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
+ # # => "+1.123.555.1234 x 1343"
+ #
+ # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
+ # # => "(755) 6123-4567"
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
+ # # => "133-1234-5678"
+ def number_to_phone(number, options = {})
+ NumberToPhoneConverter.convert(number, options)
+ end
+
+ # Formats a +number+ into a currency string (e.g., $13.65). You
+ # can customize the format in the +options+ hash.
+ #
+ # The currency unit and number formatting of the current locale will be used
+ # unless otherwise specified in the provided options. No currency conversion
+ # is performed. If the user is given a way to change their locale, they will
+ # also be able to change the relative value of the currency displayed with
+ # this helper. If your application will ever support multiple locales, you
+ # may want to specify a constant :locale option or consider
+ # using a library capable of currency conversion.
+ #
+ # ==== Options
+ #
+ # * :locale - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * :precision - Sets the level of precision (defaults
+ # to 2).
+ # * :unit - Sets the denomination of the currency
+ # (defaults to "$").
+ # * :separator - Sets the separator between the units
+ # (defaults to ".").
+ # * :delimiter - Sets the thousands delimiter (defaults
+ # to ",").
+ # * :format - Sets the format for non-negative numbers
+ # (defaults to "%u%n"). Fields are %u for the
+ # currency, and %n for the number.
+ # * :negative_format - Sets the format for negative
+ # numbers (defaults to prepending a hyphen to the formatted
+ # number given by :format). Accepts the same fields
+ # than :format, except %n is here the
+ # absolute value of the number.
+ #
+ # ==== Examples
+ #
+ # number_to_currency(1234567890.50) # => "$1,234,567,890.50"
+ # number_to_currency(1234567890.506) # => "$1,234,567,890.51"
+ # number_to_currency(1234567890.506, precision: 3) # => "$1,234,567,890.506"
+ # number_to_currency(1234567890.506, locale: :fr) # => "1 234 567 890,51 €"
+ # number_to_currency('123a456') # => "$123a456"
+ #
+ # number_to_currency(-1234567890.50, negative_format: '(%u%n)')
+ # # => "($1,234,567,890.50)"
+ # number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '')
+ # # => "£1234567890,50"
+ # number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '', format: '%n %u')
+ # # => "1234567890,50 £"
+ def number_to_currency(number, options = {})
+ NumberToCurrencyConverter.convert(number, options)
+ end
+
+ # Formats a +number+ as a percentage string (e.g., 65%). You can
+ # customize the format in the +options+ hash.
+ #
+ # ==== Options
+ #
+ # * :locale - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * :precision - Sets the precision of the number
+ # (defaults to 3). Keeps the number's precision if +nil+.
+ # * :significant - If +true+, precision will be the number
+ # of significant_digits. If +false+, the number of fractional
+ # digits (defaults to +false+).
+ # * :separator - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * :delimiter - Sets the thousands delimiter (defaults
+ # to "").
+ # * :strip_insignificant_zeros - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +false+).
+ # * :format - Specifies the format of the percentage
+ # string The number field is %n (defaults to "%n%").
+ #
+ # ==== Examples
+ #
+ # number_to_percentage(100) # => "100.000%"
+ # number_to_percentage('98') # => "98.000%"
+ # number_to_percentage(100, precision: 0) # => "100%"
+ # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
+ # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
+ # number_to_percentage(1000, locale: :fr) # => "1000,000%"
+ # number_to_percentage(1000, precision: nil) # => "1000%"
+ # number_to_percentage('98a') # => "98a%"
+ # number_to_percentage(100, format: '%n %') # => "100.000 %"
+ def number_to_percentage(number, options = {})
+ NumberToPercentageConverter.convert(number, options)
+ end
+
+ # Formats a +number+ with grouped thousands using +delimiter+
+ # (e.g., 12,324). You can customize the format in the +options+
+ # hash.
+ #
+ # ==== Options
+ #
+ # * :locale - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * :delimiter - Sets the thousands delimiter (defaults
+ # to ",").
+ # * :separator - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * :delimiter_pattern - Sets a custom regular expression used for
+ # deriving the placement of delimiter. Helpful when using currency formats
+ # like INR.
+ #
+ # ==== Examples
+ #
+ # number_to_delimited(12345678) # => "12,345,678"
+ # number_to_delimited('123456') # => "123,456"
+ # number_to_delimited(12345678.05) # => "12,345,678.05"
+ # number_to_delimited(12345678, delimiter: '.') # => "12.345.678"
+ # number_to_delimited(12345678, delimiter: ',') # => "12,345,678"
+ # number_to_delimited(12345678.05, separator: ' ') # => "12,345,678 05"
+ # number_to_delimited(12345678.05, locale: :fr) # => "12 345 678,05"
+ # number_to_delimited('112a') # => "112a"
+ # number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
+ # # => "98 765 432,98"
+ # number_to_delimited("123456.78",
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)
+ # # => "1,23,456.78"
+ def number_to_delimited(number, options = {})
+ NumberToDelimitedConverter.convert(number, options)
+ end
+
+ # Formats a +number+ with the specified level of
+ # :precision (e.g., 112.32 has a precision of 2 if
+ # +:significant+ is +false+, and 5 if +:significant+ is +true+).
+ # You can customize the format in the +options+ hash.
+ #
+ # ==== Options
+ #
+ # * :locale - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * :precision - Sets the precision of the number
+ # (defaults to 3). Keeps the number's precision if +nil+.
+ # * :significant - If +true+, precision will be the number
+ # of significant_digits. If +false+, the number of fractional
+ # digits (defaults to +false+).
+ # * :separator - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * :delimiter - Sets the thousands delimiter (defaults
+ # to "").
+ # * :strip_insignificant_zeros - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +false+).
+ #
+ # ==== Examples
+ #
+ # number_to_rounded(111.2345) # => "111.235"
+ # number_to_rounded(111.2345, precision: 2) # => "111.23"
+ # number_to_rounded(13, precision: 5) # => "13.00000"
+ # number_to_rounded(389.32314, precision: 0) # => "389"
+ # number_to_rounded(111.2345, significant: true) # => "111"
+ # number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
+ # number_to_rounded(13, precision: 5, significant: true) # => "13.000"
+ # number_to_rounded(13, precision: nil) # => "13"
+ # number_to_rounded(111.234, locale: :fr) # => "111,234"
+ #
+ # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
+ # # => "13"
+ #
+ # number_to_rounded(389.32314, precision: 4, significant: true) # => "389.3"
+ # number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
+ # # => "1.111,23"
+ def number_to_rounded(number, options = {})
+ NumberToRoundedConverter.convert(number, options)
+ end
+
+ # Formats the bytes in +number+ into a more understandable
+ # representation (e.g., giving it 1500 yields 1.5 KB). This
+ # method is useful for reporting file sizes to users. You can
+ # customize the format in the +options+ hash.
+ #
+ # See number_to_human if you want to pretty-print a
+ # generic number.
+ #
+ # ==== Options
+ #
+ # * :locale - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * :precision - Sets the precision of the number
+ # (defaults to 3).
+ # * :significant - If +true+, precision will be the number
+ # of significant_digits. If +false+, the number of fractional
+ # digits (defaults to +true+)
+ # * :separator - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * :delimiter - Sets the thousands delimiter (defaults
+ # to "").
+ # * :strip_insignificant_zeros - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +true+)
+ #
+ # ==== Examples
+ #
+ # number_to_human_size(123) # => "123 Bytes"
+ # number_to_human_size(1234) # => "1.21 KB"
+ # number_to_human_size(12345) # => "12.1 KB"
+ # number_to_human_size(1234567) # => "1.18 MB"
+ # number_to_human_size(1234567890) # => "1.15 GB"
+ # number_to_human_size(1234567890123) # => "1.12 TB"
+ # number_to_human_size(1234567890123456) # => "1.1 PB"
+ # number_to_human_size(1234567890123456789) # => "1.07 EB"
+ # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
+ # number_to_human_size(483989, precision: 2) # => "470 KB"
+ # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
+ # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
+ # number_to_human_size(524288000, precision: 5) # => "500 MB"
+ def number_to_human_size(number, options = {})
+ NumberToHumanSizeConverter.convert(number, options)
+ end
+
+ # Pretty prints (formats and approximates) a number in a way it
+ # is more readable by humans (eg.: 1200000000 becomes "1.2
+ # Billion"). This is useful for numbers that can get very large
+ # (and too hard to read).
+ #
+ # See number_to_human_size if you want to print a file
+ # size.
+ #
+ # You can also define your own unit-quantifier names if you want
+ # to use other decimal units (eg.: 1500 becomes "1.5
+ # kilometers", 0.150 becomes "150 milliliters", etc). You may
+ # define a wide range of unit quantifiers, even fractional ones
+ # (centi, deci, mili, etc).
+ #
+ # ==== Options
+ #
+ # * :locale - Sets the locale to be used for formatting
+ # (defaults to current locale).
+ # * :precision - Sets the precision of the number
+ # (defaults to 3).
+ # * :significant - If +true+, precision will be the number
+ # of significant_digits. If +false+, the number of fractional
+ # digits (defaults to +true+)
+ # * :separator - Sets the separator between the
+ # fractional and integer digits (defaults to ".").
+ # * :delimiter - Sets the thousands delimiter (defaults
+ # to "").
+ # * :strip_insignificant_zeros - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +true+)
+ # * :units - A Hash of unit quantifier names. Or a
+ # string containing an i18n scope where to find this hash. It
+ # might have the following keys:
+ # * *integers*: :unit, :ten,
+ # :hundred, :thousand, :million,
+ # :billion, :trillion,
+ # :quadrillion
+ # * *fractionals*: :deci, :centi,
+ # :mili, :micro, :nano,
+ # :pico, :femto
+ # * :format - Sets the format of the output string
+ # (defaults to "%n %u"). The field types are:
+ # * %u - The quantifier (ex.: 'thousand')
+ # * %n - The number
+ #
+ # ==== Examples
+ #
+ # number_to_human(123) # => "123"
+ # number_to_human(1234) # => "1.23 Thousand"
+ # number_to_human(12345) # => "12.3 Thousand"
+ # number_to_human(1234567) # => "1.23 Million"
+ # number_to_human(1234567890) # => "1.23 Billion"
+ # number_to_human(1234567890123) # => "1.23 Trillion"
+ # number_to_human(1234567890123456) # => "1.23 Quadrillion"
+ # number_to_human(1234567890123456789) # => "1230 Quadrillion"
+ # number_to_human(489939, precision: 2) # => "490 Thousand"
+ # number_to_human(489939, precision: 4) # => "489.9 Thousand"
+ # number_to_human(1234567, precision: 4,
+ # significant: false) # => "1.2346 Million"
+ # number_to_human(1234567, precision: 1,
+ # separator: ',',
+ # significant: false) # => "1,2 Million"
+ #
+ # number_to_human(500000000, precision: 5) # => "500 Million"
+ # number_to_human(12345012345, significant: false) # => "12.345 Billion"
+ #
+ # Non-significant zeros after the decimal separator are stripped
+ # out by default (set :strip_insignificant_zeros to
+ # +false+ to change that):
+ #
+ # number_to_human(12.00001) # => "12"
+ # number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0"
+ #
+ # ==== Custom Unit Quantifiers
+ #
+ # You can also use your own custom unit quantifiers:
+ # number_to_human(500000, units: { unit: 'ml', thousand: 'lt' }) # => "500 lt"
+ #
+ # If in your I18n locale you have:
+ #
+ # distance:
+ # centi:
+ # one: "centimeter"
+ # other: "centimeters"
+ # unit:
+ # one: "meter"
+ # other: "meters"
+ # thousand:
+ # one: "kilometer"
+ # other: "kilometers"
+ # billion: "gazillion-distance"
+ #
+ # Then you could do:
+ #
+ # number_to_human(543934, units: :distance) # => "544 kilometers"
+ # number_to_human(54393498, units: :distance) # => "54400 kilometers"
+ # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
+ # number_to_human(343, units: :distance, precision: 1) # => "300 meters"
+ # number_to_human(1, units: :distance) # => "1 meter"
+ # number_to_human(0.34, units: :distance) # => "34 centimeters"
+ def number_to_human(number, options = {})
+ NumberToHumanConverter.convert(number, options)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_converter.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_converter.rb
new file mode 100644
index 0000000..06ba797
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_converter.rb
@@ -0,0 +1,184 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/big_decimal/conversions"
+require "active_support/core_ext/object/blank"
+require "active_support/core_ext/hash/keys"
+require "active_support/i18n"
+require "active_support/core_ext/class/attribute"
+
+module ActiveSupport
+ module NumberHelper
+ class NumberConverter # :nodoc:
+ # Default and i18n option namespace per class
+ class_attribute :namespace
+
+ # Does the object need a number that is a valid float?
+ class_attribute :validate_float
+
+ attr_reader :number, :opts
+
+ DEFAULTS = {
+ # Used in number_to_delimited
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
+ format: {
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
+ separator: ".",
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
+ delimiter: ",",
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
+ precision: 3,
+ # If set to true, precision will mean the number of significant digits instead
+ # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
+ significant: false,
+ # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
+ strip_insignificant_zeros: false
+ },
+
+ # Used in number_to_currency
+ currency: {
+ format: {
+ format: "%u%n",
+ negative_format: "-%u%n",
+ unit: "$",
+ # These five are to override number.format and are optional
+ separator: ".",
+ delimiter: ",",
+ precision: 2,
+ significant: false,
+ strip_insignificant_zeros: false
+ }
+ },
+
+ # Used in number_to_percentage
+ percentage: {
+ format: {
+ delimiter: "",
+ format: "%n%"
+ }
+ },
+
+ # Used in number_to_rounded
+ precision: {
+ format: {
+ delimiter: ""
+ }
+ },
+
+ # Used in number_to_human_size and number_to_human
+ human: {
+ format: {
+ # These five are to override number.format and are optional
+ delimiter: "",
+ precision: 3,
+ significant: true,
+ strip_insignificant_zeros: true
+ },
+ # Used in number_to_human_size
+ storage_units: {
+ # Storage units output formatting.
+ # %u is the storage unit, %n is the number (default: 2 MB)
+ format: "%n %u",
+ units: {
+ byte: "Bytes",
+ kb: "KB",
+ mb: "MB",
+ gb: "GB",
+ tb: "TB"
+ }
+ },
+ # Used in number_to_human
+ decimal_units: {
+ format: "%n %u",
+ # Decimal units output formatting
+ # By default we will only quantify some of the exponents
+ # but the commented ones might be defined or overridden
+ # by the user.
+ units: {
+ # femto: Quadrillionth
+ # pico: Trillionth
+ # nano: Billionth
+ # micro: Millionth
+ # mili: Thousandth
+ # centi: Hundredth
+ # deci: Tenth
+ unit: "",
+ # ten:
+ # one: Ten
+ # other: Tens
+ # hundred: Hundred
+ thousand: "Thousand",
+ million: "Million",
+ billion: "Billion",
+ trillion: "Trillion",
+ quadrillion: "Quadrillion"
+ }
+ }
+ }
+ }
+
+ def self.convert(number, options)
+ new(number, options).execute
+ end
+
+ def initialize(number, options)
+ @number = number
+ @opts = options.symbolize_keys
+ end
+
+ def execute
+ if !number
+ nil
+ elsif validate_float? && !valid_float?
+ number
+ else
+ convert
+ end
+ end
+
+ private
+
+ def options
+ @options ||= format_options.merge(opts)
+ end
+
+ def format_options
+ default_format_options.merge!(i18n_format_options)
+ end
+
+ def default_format_options
+ options = DEFAULTS[:format].dup
+ options.merge!(DEFAULTS[namespace][:format]) if namespace
+ options
+ end
+
+ def i18n_format_options
+ locale = opts[:locale]
+ options = I18n.translate(:'number.format', locale: locale, default: {}).dup
+
+ if namespace
+ options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
+ end
+
+ options
+ end
+
+ def translate_number_value_with_default(key, i18n_options = {})
+ I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
+ end
+
+ def translate_in_locale(key, i18n_options = {})
+ translate_number_value_with_default(key, { locale: options[:locale] }.merge(i18n_options))
+ end
+
+ def default_value(key)
+ key.split(".").reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
+ end
+
+ def valid_float?
+ Float(number)
+ rescue ArgumentError, TypeError
+ false
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_currency_converter.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_currency_converter.rb
new file mode 100644
index 0000000..3f037c7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_currency_converter.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/numeric/inquiry"
+
+module ActiveSupport
+ module NumberHelper
+ class NumberToCurrencyConverter < NumberConverter # :nodoc:
+ self.namespace = :currency
+
+ def convert
+ number = self.number.to_s.strip
+ format = options[:format]
+
+ if number.to_f.negative?
+ format = options[:negative_format]
+ number = absolute_value(number)
+ end
+
+ rounded_number = NumberToRoundedConverter.convert(number, options)
+ format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, options[:unit])
+ end
+
+ private
+
+ def absolute_value(number)
+ number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "")
+ end
+
+ def options
+ @options ||= begin
+ defaults = default_format_options.merge(i18n_opts)
+ # Override negative format if format options are given
+ defaults[:negative_format] = "-#{opts[:format]}" if opts[:format]
+ defaults.merge!(opts)
+ end
+ end
+
+ def i18n_opts
+ # Set International negative format if it does not exist
+ i18n = i18n_format_options
+ i18n[:negative_format] ||= "-#{i18n[:format]}" if i18n[:format]
+ i18n
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_delimited_converter.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_delimited_converter.rb
new file mode 100644
index 0000000..d5b5706
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_delimited_converter.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module NumberHelper
+ class NumberToDelimitedConverter < NumberConverter #:nodoc:
+ self.validate_float = true
+
+ DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
+
+ def convert
+ parts.join(options[:separator])
+ end
+
+ private
+
+ def parts
+ left, right = number.to_s.split(".".freeze)
+ left.gsub!(delimiter_pattern) do |digit_to_delimit|
+ "#{digit_to_delimit}#{options[:delimiter]}"
+ end
+ [left, right].compact
+ end
+
+ def delimiter_pattern
+ options.fetch(:delimiter_pattern, DEFAULT_DELIMITER_REGEX)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_human_converter.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_human_converter.rb
new file mode 100644
index 0000000..03eb667
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_human_converter.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module NumberHelper
+ class NumberToHumanConverter < NumberConverter # :nodoc:
+ DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
+ -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
+ INVERTED_DECIMAL_UNITS = DECIMAL_UNITS.invert
+
+ self.namespace = :human
+ self.validate_float = true
+
+ def convert # :nodoc:
+ @number = RoundingHelper.new(options).round(number)
+ @number = Float(number)
+
+ # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
+ unless options.key?(:strip_insignificant_zeros)
+ options[:strip_insignificant_zeros] = true
+ end
+
+ units = opts[:units]
+ exponent = calculate_exponent(units)
+ @number = number / (10**exponent)
+
+ rounded_number = NumberToRoundedConverter.convert(number, options)
+ unit = determine_unit(units, exponent)
+ format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, unit).strip
+ end
+
+ private
+
+ def format
+ options[:format] || translate_in_locale("human.decimal_units.format")
+ end
+
+ def determine_unit(units, exponent)
+ exp = DECIMAL_UNITS[exponent]
+ case units
+ when Hash
+ units[exp] || ""
+ when String, Symbol
+ I18n.translate("#{units}.#{exp}", locale: options[:locale], count: number.to_i)
+ else
+ translate_in_locale("human.decimal_units.units.#{exp}", count: number.to_i)
+ end
+ end
+
+ def calculate_exponent(units)
+ exponent = number != 0 ? Math.log10(number.abs).floor : 0
+ unit_exponents(units).find { |e| exponent >= e } || 0
+ end
+
+ def unit_exponents(units)
+ case units
+ when Hash
+ units
+ when String, Symbol
+ I18n.translate(units.to_s, locale: options[:locale], raise: true)
+ when nil
+ translate_in_locale("human.decimal_units.units", raise: true)
+ else
+ raise ArgumentError, ":units must be a Hash or String translation scope."
+ end.keys.map { |e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by(&:-@)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_human_size_converter.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_human_size_converter.rb
new file mode 100644
index 0000000..842f2fc
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_human_size_converter.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module NumberHelper
+ class NumberToHumanSizeConverter < NumberConverter #:nodoc:
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb]
+
+ self.namespace = :human
+ self.validate_float = true
+
+ def convert
+ @number = Float(number)
+
+ # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
+ unless options.key?(:strip_insignificant_zeros)
+ options[:strip_insignificant_zeros] = true
+ end
+
+ if smaller_than_base?
+ number_to_format = number.to_i.to_s
+ else
+ human_size = number / (base**exponent)
+ number_to_format = NumberToRoundedConverter.convert(human_size, options)
+ end
+ conversion_format.gsub("%n".freeze, number_to_format).gsub("%u".freeze, unit)
+ end
+
+ private
+
+ def conversion_format
+ translate_number_value_with_default("human.storage_units.format", locale: options[:locale], raise: true)
+ end
+
+ def unit
+ translate_number_value_with_default(storage_unit_key, locale: options[:locale], count: number.to_i, raise: true)
+ end
+
+ def storage_unit_key
+ key_end = smaller_than_base? ? "byte" : STORAGE_UNITS[exponent]
+ "human.storage_units.units.#{key_end}"
+ end
+
+ def exponent
+ max = STORAGE_UNITS.size - 1
+ exp = (Math.log(number) / Math.log(base)).to_i
+ exp = max if exp > max # avoid overflow for the highest unit
+ exp
+ end
+
+ def smaller_than_base?
+ number.to_i < base
+ end
+
+ def base
+ 1024
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_percentage_converter.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_percentage_converter.rb
new file mode 100644
index 0000000..4dcdad2
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_percentage_converter.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module NumberHelper
+ class NumberToPercentageConverter < NumberConverter # :nodoc:
+ self.namespace = :percentage
+
+ def convert
+ rounded_number = NumberToRoundedConverter.convert(number, options)
+ options[:format].gsub("%n".freeze, rounded_number)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_phone_converter.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_phone_converter.rb
new file mode 100644
index 0000000..96410f4
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_phone_converter.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module NumberHelper
+ class NumberToPhoneConverter < NumberConverter #:nodoc:
+ def convert
+ str = country_code(opts[:country_code]).dup
+ str << convert_to_phone_number(number.to_s.strip)
+ str << phone_ext(opts[:extension])
+ end
+
+ private
+
+ def convert_to_phone_number(number)
+ if opts[:area_code]
+ convert_with_area_code(number)
+ else
+ convert_without_area_code(number)
+ end
+ end
+
+ def convert_with_area_code(number)
+ default_pattern = /(\d{1,3})(\d{3})(\d{4}$)/
+ number.gsub!(regexp_pattern(default_pattern),
+ "(\\1) \\2#{delimiter}\\3")
+ number
+ end
+
+ def convert_without_area_code(number)
+ default_pattern = /(\d{0,3})(\d{3})(\d{4})$/
+ number.gsub!(regexp_pattern(default_pattern),
+ "\\1#{delimiter}\\2#{delimiter}\\3")
+ number.slice!(0, 1) if start_with_delimiter?(number)
+ number
+ end
+
+ def start_with_delimiter?(number)
+ delimiter.present? && number.start_with?(delimiter)
+ end
+
+ def delimiter
+ opts[:delimiter] || "-"
+ end
+
+ def country_code(code)
+ code.blank? ? "" : "+#{code}#{delimiter}"
+ end
+
+ def phone_ext(ext)
+ ext.blank? ? "" : " x #{ext}"
+ end
+
+ def regexp_pattern(default_pattern)
+ opts.fetch :pattern, default_pattern
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_rounded_converter.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_rounded_converter.rb
new file mode 100644
index 0000000..eb528a0
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/number_to_rounded_converter.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module NumberHelper
+ class NumberToRoundedConverter < NumberConverter # :nodoc:
+ self.namespace = :precision
+ self.validate_float = true
+
+ def convert
+ helper = RoundingHelper.new(options)
+ rounded_number = helper.round(number)
+
+ if precision = options[:precision]
+ if options[:significant] && precision > 0
+ digits = helper.digit_count(rounded_number)
+ precision -= digits
+ precision = 0 if precision < 0 # don't let it be negative
+ end
+
+ formatted_string =
+ if BigDecimal === rounded_number && rounded_number.finite?
+ s = rounded_number.to_s("F")
+ s << "0".freeze * precision
+ a, b = s.split(".".freeze, 2)
+ a << ".".freeze
+ a << b[0, precision]
+ else
+ "%00.#{precision}f" % rounded_number
+ end
+ else
+ formatted_string = rounded_number
+ end
+
+ delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
+ format_number(delimited_number)
+ end
+
+ private
+
+ def strip_insignificant_zeros
+ options[:strip_insignificant_zeros]
+ end
+
+ def format_number(number)
+ if strip_insignificant_zeros
+ escaped_separator = Regexp.escape(options[:separator])
+ number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, "")
+ else
+ number
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/rounding_helper.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/rounding_helper.rb
new file mode 100644
index 0000000..2ad8d49
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/number_helper/rounding_helper.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module NumberHelper
+ class RoundingHelper # :nodoc:
+ attr_reader :options
+
+ def initialize(options)
+ @options = options
+ end
+
+ def round(number)
+ return number unless precision
+ number = convert_to_decimal(number)
+ if significant && precision > 0
+ round_significant(number)
+ else
+ round_without_significant(number)
+ end
+ end
+
+ def digit_count(number)
+ return 1 if number.zero?
+ (Math.log10(absolute_number(number)) + 1).floor
+ end
+
+ private
+ def round_without_significant(number)
+ number = number.round(precision)
+ number = number.to_i if precision == 0 && number.finite?
+ number = number.abs if number.zero? # prevent showing negative zeros
+ number
+ end
+
+ def round_significant(number)
+ return 0 if number.zero?
+ digits = digit_count(number)
+ multiplier = 10**(digits - precision)
+ (number / BigDecimal(multiplier.to_f.to_s)).round * multiplier
+ end
+
+ def convert_to_decimal(number)
+ case number
+ when Float, String
+ BigDecimal(number.to_s)
+ when Rational
+ BigDecimal(number, digit_count(number.to_i) + precision)
+ else
+ number.to_d
+ end
+ end
+
+ def precision
+ options[:precision]
+ end
+
+ def significant
+ options[:significant]
+ end
+
+ def absolute_number(number)
+ number.respond_to?(:abs) ? number.abs : number.to_d.abs
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/option_merger.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/option_merger.rb
new file mode 100644
index 0000000..ab9ca72
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/option_merger.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/hash/deep_merge"
+
+module ActiveSupport
+ class OptionMerger #:nodoc:
+ instance_methods.each do |method|
+ undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
+ end
+
+ def initialize(context, options)
+ @context, @options = context, options
+ end
+
+ private
+ def method_missing(method, *arguments, &block)
+ if arguments.first.is_a?(Proc)
+ proc = arguments.pop
+ arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
+ else
+ arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
+ end
+
+ @context.__send__(method, *arguments, &block)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/ordered_hash.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/ordered_hash.rb
new file mode 100644
index 0000000..5758513
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/ordered_hash.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require "yaml"
+
+YAML.add_builtin_type("omap") do |type, val|
+ ActiveSupport::OrderedHash[val.map { |v| v.to_a.first }]
+end
+
+module ActiveSupport
+ # DEPRECATED: ActiveSupport::OrderedHash implements a hash that preserves
+ # insertion order.
+ #
+ # oh = ActiveSupport::OrderedHash.new
+ # oh[:a] = 1
+ # oh[:b] = 2
+ # oh.keys # => [:a, :b], this order is guaranteed
+ #
+ # Also, maps the +omap+ feature for YAML files
+ # (See http://yaml.org/type/omap.html) to support ordered items
+ # when loading from yaml.
+ #
+ # ActiveSupport::OrderedHash is namespaced to prevent conflicts
+ # with other implementations.
+ class OrderedHash < ::Hash
+ def to_yaml_type
+ "!tag:yaml.org,2002:omap"
+ end
+
+ def encode_with(coder)
+ coder.represent_seq "!omap", map { |k, v| { k => v } }
+ end
+
+ def select(*args, &block)
+ dup.tap { |hash| hash.select!(*args, &block) }
+ end
+
+ def reject(*args, &block)
+ dup.tap { |hash| hash.reject!(*args, &block) }
+ end
+
+ def nested_under_indifferent_access
+ self
+ end
+
+ # Returns true to make sure that this hash is extractable via Array#extract_options!
+ def extractable_options?
+ true
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/ordered_options.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/ordered_options.rb
new file mode 100644
index 0000000..6ba1346
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/ordered_options.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/blank"
+
+module ActiveSupport
+ # Usually key value pairs are handled something like this:
+ #
+ # h = {}
+ # h[:boy] = 'John'
+ # h[:girl] = 'Mary'
+ # h[:boy] # => 'John'
+ # h[:girl] # => 'Mary'
+ # h[:dog] # => nil
+ #
+ # Using +OrderedOptions+, the above code could be reduced to:
+ #
+ # h = ActiveSupport::OrderedOptions.new
+ # h.boy = 'John'
+ # h.girl = 'Mary'
+ # h.boy # => 'John'
+ # h.girl # => 'Mary'
+ # h.dog # => nil
+ #
+ # To raise an exception when the value is blank, append a
+ # bang to the key name, like:
+ #
+ # h.dog! # => raises KeyError: :dog is blank
+ #
+ class OrderedOptions < Hash
+ alias_method :_get, :[] # preserve the original #[] method
+ protected :_get # make it protected
+
+ def []=(key, value)
+ super(key.to_sym, value)
+ end
+
+ def [](key)
+ super(key.to_sym)
+ end
+
+ def method_missing(name, *args)
+ name_string = name.to_s.dup
+ if name_string.chomp!("=")
+ self[name_string] = args.first
+ else
+ bangs = name_string.chomp!("!")
+
+ if bangs
+ self[name_string].presence || raise(KeyError.new(":#{name_string} is blank"))
+ else
+ self[name_string]
+ end
+ end
+ end
+
+ def respond_to_missing?(name, include_private)
+ true
+ end
+ end
+
+ # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
+ # hash inherited from another hash.
+ #
+ # Use this if you already have some hash and you want to create a new one based on it.
+ #
+ # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
+ # h.girl # => 'Mary'
+ # h.boy # => 'John'
+ class InheritableOptions < OrderedOptions
+ def initialize(parent = nil)
+ if parent.kind_of?(OrderedOptions)
+ # use the faster _get when dealing with OrderedOptions
+ super() { |h, k| parent._get(k) }
+ elsif parent
+ super() { |h, k| parent[k] }
+ else
+ super()
+ end
+ end
+
+ def inheritable_copy
+ self.class.new(self)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/per_thread_registry.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/per_thread_registry.rb
new file mode 100644
index 0000000..eb92fb4
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/per_thread_registry.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/delegation"
+
+module ActiveSupport
+ # NOTE: This approach has been deprecated for end-user code in favor of {thread_mattr_accessor}[rdoc-ref:Module#thread_mattr_accessor] and friends.
+ # Please use that approach instead.
+ #
+ # This module is used to encapsulate access to thread local variables.
+ #
+ # Instead of polluting the thread locals namespace:
+ #
+ # Thread.current[:connection_handler]
+ #
+ # you define a class that extends this module:
+ #
+ # module ActiveRecord
+ # class RuntimeRegistry
+ # extend ActiveSupport::PerThreadRegistry
+ #
+ # attr_accessor :connection_handler
+ # end
+ # end
+ #
+ # and invoke the declared instance accessors as class methods. So
+ #
+ # ActiveRecord::RuntimeRegistry.connection_handler = connection_handler
+ #
+ # sets a connection handler local to the current thread, and
+ #
+ # ActiveRecord::RuntimeRegistry.connection_handler
+ #
+ # returns a connection handler local to the current thread.
+ #
+ # This feature is accomplished by instantiating the class and storing the
+ # instance as a thread local keyed by the class name. In the example above
+ # a key "ActiveRecord::RuntimeRegistry" is stored in Thread.current.
+ # The class methods proxy to said thread local instance.
+ #
+ # If the class has an initializer, it must accept no arguments.
+ module PerThreadRegistry
+ def self.extended(object)
+ object.instance_variable_set "@per_thread_registry_key", object.name.freeze
+ end
+
+ def instance
+ Thread.current[@per_thread_registry_key] ||= new
+ end
+
+ private
+ def method_missing(name, *args, &block)
+ # Caches the method definition as a singleton method of the receiver.
+ #
+ # By letting #delegate handle it, we avoid an enclosure that'll capture args.
+ singleton_class.delegate name, to: :instance
+
+ send(name, *args, &block)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/proxy_object.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/proxy_object.rb
new file mode 100644
index 0000000..0965fcd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/proxy_object.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # A class with no predefined methods that behaves similarly to Builder's
+ # BlankSlate. Used for proxy classes.
+ class ProxyObject < ::BasicObject
+ undef_method :==
+ undef_method :equal?
+
+ # Let ActiveSupport::ProxyObject at least raise exceptions.
+ def raise(*args)
+ ::Object.send(:raise, *args)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/rails.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/rails.rb
new file mode 100644
index 0000000..5c34a0a
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/rails.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+# This is private interface.
+#
+# Rails components cherry pick from Active Support as needed, but there are a
+# few features that are used for sure in some way or another and it is not worth
+# putting individual requires absolutely everywhere. Think blank? for example.
+#
+# This file is loaded by every Rails component except Active Support itself,
+# but it does not belong to the Rails public interface. It is internal to
+# Rails and can change anytime.
+
+# Defines Object#blank? and Object#present?.
+require "active_support/core_ext/object/blank"
+
+# Rails own autoload, eager_load, etc.
+require "active_support/dependencies/autoload"
+
+# Support for ClassMethods and the included macro.
+require "active_support/concern"
+
+# Defines Class#class_attribute.
+require "active_support/core_ext/class/attribute"
+
+# Defines Module#delegate.
+require "active_support/core_ext/module/delegation"
+
+# Defines ActiveSupport::Deprecation.
+require "active_support/deprecation"
+
+# Defines Regexp#match?.
+#
+# This should be removed when Rails needs Ruby 2.4 or later, and the require
+# added where other Regexp extensions are being used (easy to grep).
+require "active_support/core_ext/regexp"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/railtie.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/railtie.rb
new file mode 100644
index 0000000..605b50d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/railtie.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require "active_support"
+require "active_support/i18n_railtie"
+
+module ActiveSupport
+ class Railtie < Rails::Railtie # :nodoc:
+ config.active_support = ActiveSupport::OrderedOptions.new
+
+ config.eager_load_namespaces << ActiveSupport
+
+ initializer "active_support.set_authenticated_message_encryption" do |app|
+ config.after_initialize do
+ unless app.config.active_support.use_authenticated_message_encryption.nil?
+ ActiveSupport::MessageEncryptor.use_authenticated_message_encryption =
+ app.config.active_support.use_authenticated_message_encryption
+ end
+ end
+ end
+
+ initializer "active_support.reset_all_current_attributes_instances" do |app|
+ app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all }
+ app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all }
+ app.executor.to_complete { ActiveSupport::CurrentAttributes.reset_all }
+ end
+
+ initializer "active_support.deprecation_behavior" do |app|
+ if deprecation = app.config.active_support.deprecation
+ ActiveSupport::Deprecation.behavior = deprecation
+ end
+ end
+
+ # Sets the default value for Time.zone
+ # If assigned value cannot be matched to a TimeZone, an exception will be raised.
+ initializer "active_support.initialize_time_zone" do |app|
+ begin
+ TZInfo::DataSource.get
+ rescue TZInfo::DataSourceNotFound => e
+ raise e.exception "tzinfo-data is not present. Please add gem 'tzinfo-data' to your Gemfile and run bundle install"
+ end
+ require "active_support/core_ext/time/zones"
+ Time.zone_default = Time.find_zone!(app.config.time_zone)
+ end
+
+ # Sets the default week start
+ # If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised.
+ initializer "active_support.initialize_beginning_of_week" do |app|
+ require "active_support/core_ext/date/calculations"
+ beginning_of_week_default = Date.find_beginning_of_week!(app.config.beginning_of_week)
+
+ Date.beginning_of_week_default = beginning_of_week_default
+ end
+
+ initializer "active_support.require_master_key" do |app|
+ if app.config.respond_to?(:require_master_key) && app.config.require_master_key
+ begin
+ app.credentials.key
+ rescue ActiveSupport::EncryptedFile::MissingKeyError => error
+ $stderr.puts error.message
+ exit 1
+ end
+ end
+ end
+
+ initializer "active_support.set_configs" do |app|
+ app.config.active_support.each do |k, v|
+ k = "#{k}="
+ ActiveSupport.send(k, v) if ActiveSupport.respond_to? k
+ end
+ end
+
+ initializer "active_support.set_hash_digest_class" do |app|
+ config.after_initialize do
+ if app.config.active_support.use_sha1_digests
+ ActiveSupport::Digest.hash_digest_class = ::Digest::SHA1
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/reloader.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/reloader.rb
new file mode 100644
index 0000000..b26d9c3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/reloader.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+require "active_support/execution_wrapper"
+
+module ActiveSupport
+ #--
+ # This class defines several callbacks:
+ #
+ # to_prepare -- Run once at application startup, and also from
+ # +to_run+.
+ #
+ # to_run -- Run before a work run that is reloading. If
+ # +reload_classes_only_on_change+ is true (the default), the class
+ # unload will have already occurred.
+ #
+ # to_complete -- Run after a work run that has reloaded. If
+ # +reload_classes_only_on_change+ is false, the class unload will
+ # have occurred after the work run, but before this callback.
+ #
+ # before_class_unload -- Run immediately before the classes are
+ # unloaded.
+ #
+ # after_class_unload -- Run immediately after the classes are
+ # unloaded.
+ #
+ class Reloader < ExecutionWrapper
+ define_callbacks :prepare
+
+ define_callbacks :class_unload
+
+ # Registers a callback that will run once at application startup and every time the code is reloaded.
+ def self.to_prepare(*args, &block)
+ set_callback(:prepare, *args, &block)
+ end
+
+ # Registers a callback that will run immediately before the classes are unloaded.
+ def self.before_class_unload(*args, &block)
+ set_callback(:class_unload, *args, &block)
+ end
+
+ # Registers a callback that will run immediately after the classes are unloaded.
+ def self.after_class_unload(*args, &block)
+ set_callback(:class_unload, :after, *args, &block)
+ end
+
+ to_run(:after) { self.class.prepare! }
+
+ # Initiate a manual reload
+ def self.reload!
+ executor.wrap do
+ new.tap do |instance|
+ begin
+ instance.run!
+ ensure
+ instance.complete!
+ end
+ end
+ end
+ prepare!
+ end
+
+ def self.run! # :nodoc:
+ if check!
+ super
+ else
+ Null
+ end
+ end
+
+ # Run the supplied block as a work unit, reloading code as needed
+ def self.wrap
+ executor.wrap do
+ super
+ end
+ end
+
+ class_attribute :executor, default: Executor
+ class_attribute :check, default: lambda { false }
+
+ def self.check! # :nodoc:
+ @should_reload ||= check.call
+ end
+
+ def self.reloaded! # :nodoc:
+ @should_reload = false
+ end
+
+ def self.prepare! # :nodoc:
+ new.run_callbacks(:prepare)
+ end
+
+ def initialize
+ super
+ @locked = false
+ end
+
+ # Acquire the ActiveSupport::Dependencies::Interlock unload lock,
+ # ensuring it will be released automatically
+ def require_unload_lock!
+ unless @locked
+ ActiveSupport::Dependencies.interlock.start_unloading
+ @locked = true
+ end
+ end
+
+ # Release the unload lock if it has been previously obtained
+ def release_unload_lock!
+ if @locked
+ @locked = false
+ ActiveSupport::Dependencies.interlock.done_unloading
+ end
+ end
+
+ def run! # :nodoc:
+ super
+ release_unload_lock!
+ end
+
+ def class_unload!(&block) # :nodoc:
+ require_unload_lock!
+ run_callbacks(:class_unload, &block)
+ end
+
+ def complete! # :nodoc:
+ super
+ self.class.reloaded!
+ ensure
+ release_unload_lock!
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/rescuable.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/rescuable.rb
new file mode 100644
index 0000000..e0fa29c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/rescuable.rb
@@ -0,0 +1,174 @@
+# frozen_string_literal: true
+
+require "active_support/concern"
+require "active_support/core_ext/class/attribute"
+require "active_support/core_ext/string/inflections"
+
+module ActiveSupport
+ # Rescuable module adds support for easier exception handling.
+ module Rescuable
+ extend Concern
+
+ included do
+ class_attribute :rescue_handlers, default: []
+ end
+
+ module ClassMethods
+ # Rescue exceptions raised in controller actions.
+ #
+ # rescue_from receives a series of exception classes or class
+ # names, and a trailing :with option with the name of a method
+ # or a Proc object to be called to handle them. Alternatively a block can
+ # be given.
+ #
+ # Handlers that take one argument will be called with the exception, so
+ # that the exception can be inspected when dealing with it.
+ #
+ # Handlers are inherited. They are searched from right to left, from
+ # bottom to top, and up the hierarchy. The handler of the first class for
+ # which exception.is_a?(klass) holds true is the one invoked, if
+ # any.
+ #
+ # class ApplicationController < ActionController::Base
+ # rescue_from User::NotAuthorized, with: :deny_access # self defined exception
+ # rescue_from ActiveRecord::RecordInvalid, with: :show_errors
+ #
+ # rescue_from 'MyAppError::Base' do |exception|
+ # render xml: exception, status: 500
+ # end
+ #
+ # private
+ # def deny_access
+ # ...
+ # end
+ #
+ # def show_errors(exception)
+ # exception.record.new_record? ? ...
+ # end
+ # end
+ #
+ # Exceptions raised inside exception handlers are not propagated up.
+ def rescue_from(*klasses, with: nil, &block)
+ unless with
+ if block_given?
+ with = block
+ else
+ raise ArgumentError, "Need a handler. Pass the with: keyword argument or provide a block."
+ end
+ end
+
+ klasses.each do |klass|
+ key = if klass.is_a?(Module) && klass.respond_to?(:===)
+ klass.name
+ elsif klass.is_a?(String)
+ klass
+ else
+ raise ArgumentError, "#{klass.inspect} must be an Exception class or a String referencing an Exception class"
+ end
+
+ # Put the new handler at the end because the list is read in reverse.
+ self.rescue_handlers += [[key, with]]
+ end
+ end
+
+ # Matches an exception to a handler based on the exception class.
+ #
+ # If no handler matches the exception, check for a handler matching the
+ # (optional) exception.cause. If no handler matches the exception or its
+ # cause, this returns +nil+, so you can deal with unhandled exceptions.
+ # Be sure to re-raise unhandled exceptions if this is what you expect.
+ #
+ # begin
+ # …
+ # rescue => exception
+ # rescue_with_handler(exception) || raise
+ # end
+ #
+ # Returns the exception if it was handled and +nil+ if it was not.
+ def rescue_with_handler(exception, object: self, visited_exceptions: [])
+ visited_exceptions << exception
+
+ if handler = handler_for_rescue(exception, object: object)
+ handler.call exception
+ exception
+ elsif exception
+ if visited_exceptions.include?(exception.cause)
+ nil
+ else
+ rescue_with_handler(exception.cause, object: object, visited_exceptions: visited_exceptions)
+ end
+ end
+ end
+
+ def handler_for_rescue(exception, object: self) #:nodoc:
+ case rescuer = find_rescue_handler(exception)
+ when Symbol
+ method = object.method(rescuer)
+ if method.arity == 0
+ -> e { method.call }
+ else
+ method
+ end
+ when Proc
+ if rescuer.arity == 0
+ -> e { object.instance_exec(&rescuer) }
+ else
+ -> e { object.instance_exec(e, &rescuer) }
+ end
+ end
+ end
+
+ private
+ def find_rescue_handler(exception)
+ if exception
+ # Handlers are in order of declaration but the most recently declared
+ # is the highest priority match, so we search for matching handlers
+ # in reverse.
+ _, handler = rescue_handlers.reverse_each.detect do |class_or_name, _|
+ if klass = constantize_rescue_handler_class(class_or_name)
+ klass === exception
+ end
+ end
+
+ handler
+ end
+ end
+
+ def constantize_rescue_handler_class(class_or_name)
+ case class_or_name
+ when String, Symbol
+ begin
+ # Try a lexical lookup first since we support
+ #
+ # class Super
+ # rescue_from 'Error', with: …
+ # end
+ #
+ # class Sub
+ # class Error < StandardError; end
+ # end
+ #
+ # so an Error raised in Sub will hit the 'Error' handler.
+ const_get class_or_name
+ rescue NameError
+ class_or_name.safe_constantize
+ end
+ else
+ class_or_name
+ end
+ end
+ end
+
+ # Delegates to the class method, but uses the instance as the subject for
+ # rescue_from handlers (method calls, instance_exec blocks).
+ def rescue_with_handler(exception)
+ self.class.rescue_with_handler exception, object: self
+ end
+
+ # Internal handler lookup. Delegates to class method. Some libraries call
+ # this directly, so keeping it around for compatibility.
+ def handler_for_rescue(exception) #:nodoc:
+ self.class.handler_for_rescue exception, object: self
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/security_utils.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/security_utils.rb
new file mode 100644
index 0000000..20b6b9c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/security_utils.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require "digest/sha2"
+
+module ActiveSupport
+ module SecurityUtils
+ # Constant time string comparison, for fixed length strings.
+ #
+ # The values compared should be of fixed length, such as strings
+ # that have already been processed by HMAC. Raises in case of length mismatch.
+ def fixed_length_secure_compare(a, b)
+ raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
+
+ l = a.unpack "C#{a.bytesize}"
+
+ res = 0
+ b.each_byte { |byte| res |= byte ^ l.shift }
+ res == 0
+ end
+ module_function :fixed_length_secure_compare
+
+ # Constant time string comparison, for variable length strings.
+ #
+ # The values are first processed by SHA256, so that we don't leak length info
+ # via timing attacks.
+ def secure_compare(a, b)
+ fixed_length_secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b)) && a == b
+ end
+ module_function :secure_compare
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/string_inquirer.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/string_inquirer.rb
new file mode 100644
index 0000000..a3af367
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/string_inquirer.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # Wrapping a string in this class gives you a prettier way to test
+ # for equality. The value returned by Rails.env is wrapped
+ # in a StringInquirer object, so instead of calling this:
+ #
+ # Rails.env == 'production'
+ #
+ # you can call this:
+ #
+ # Rails.env.production?
+ #
+ # == Instantiating a new StringInquirer
+ #
+ # vehicle = ActiveSupport::StringInquirer.new('car')
+ # vehicle.car? # => true
+ # vehicle.bike? # => false
+ class StringInquirer < String
+ private
+
+ def respond_to_missing?(method_name, include_private = false)
+ (method_name[-1] == "?") || super
+ end
+
+ def method_missing(method_name, *arguments)
+ if method_name[-1] == "?"
+ self == method_name[0..-2]
+ else
+ super
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/subscriber.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/subscriber.rb
new file mode 100644
index 0000000..9291b48
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/subscriber.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require "active_support/per_thread_registry"
+require "active_support/notifications"
+
+module ActiveSupport
+ # ActiveSupport::Subscriber is an object set to consume
+ # ActiveSupport::Notifications. The subscriber dispatches notifications to
+ # a registered object based on its given namespace.
+ #
+ # An example would be an Active Record subscriber responsible for collecting
+ # statistics about queries:
+ #
+ # module ActiveRecord
+ # class StatsSubscriber < ActiveSupport::Subscriber
+ # attach_to :active_record
+ #
+ # def sql(event)
+ # Statsd.timing("sql.#{event.payload[:name]}", event.duration)
+ # end
+ # end
+ # end
+ #
+ # After configured, whenever a "sql.active_record" notification is published,
+ # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
+ # the +sql+ method.
+ class Subscriber
+ class << self
+ # Attach the subscriber to a namespace.
+ def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications)
+ @namespace = namespace
+ @subscriber = subscriber
+ @notifier = notifier
+
+ subscribers << subscriber
+
+ # Add event subscribers for all existing methods on the class.
+ subscriber.public_methods(false).each do |event|
+ add_event_subscriber(event)
+ end
+ end
+
+ # Adds event subscribers for all new methods added to the class.
+ def method_added(event)
+ # Only public methods are added as subscribers, and only if a notifier
+ # has been set up. This means that subscribers will only be set up for
+ # classes that call #attach_to.
+ if public_method_defined?(event) && notifier
+ add_event_subscriber(event)
+ end
+ end
+
+ def subscribers
+ @@subscribers ||= []
+ end
+
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
+ protected
+
+ attr_reader :subscriber, :notifier, :namespace
+
+ private
+
+ def add_event_subscriber(event) # :doc:
+ return if %w{ start finish }.include?(event.to_s)
+
+ pattern = "#{event}.#{namespace}"
+
+ # Don't add multiple subscribers (eg. if methods are redefined).
+ return if subscriber.patterns.include?(pattern)
+
+ subscriber.patterns << pattern
+ notifier.subscribe(pattern, subscriber)
+ end
+ end
+
+ attr_reader :patterns # :nodoc:
+
+ def initialize
+ @queue_key = [self.class.name, object_id].join "-"
+ @patterns = []
+ super
+ end
+
+ def start(name, id, payload)
+ e = ActiveSupport::Notifications::Event.new(name, now, nil, id, payload)
+ parent = event_stack.last
+ parent << e if parent
+
+ event_stack.push e
+ end
+
+ def finish(name, id, payload)
+ finished = now
+ event = event_stack.pop
+ event.end = finished
+ event.payload.merge!(payload)
+
+ method = name.split(".".freeze).first
+ send(method, event)
+ end
+
+ private
+
+ def event_stack
+ SubscriberQueueRegistry.instance.get_queue(@queue_key)
+ end
+
+ def now
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end
+ end
+
+ # This is a registry for all the event stacks kept for subscribers.
+ #
+ # See the documentation of ActiveSupport::PerThreadRegistry
+ # for further details.
+ class SubscriberQueueRegistry # :nodoc:
+ extend PerThreadRegistry
+
+ def initialize
+ @registry = {}
+ end
+
+ def get_queue(queue_key)
+ @registry[queue_key] ||= []
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/tagged_logging.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/tagged_logging.rb
new file mode 100644
index 0000000..8561cba
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/tagged_logging.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/delegation"
+require "active_support/core_ext/object/blank"
+require "logger"
+require "active_support/logger"
+
+module ActiveSupport
+ # Wraps any standard Logger object to provide tagging capabilities.
+ #
+ # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
+ # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
+ # logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
+ # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
+ #
+ # This is used by the default Rails.logger as configured by Railties to make
+ # it easy to stamp log lines with subdomains, request ids, and anything else
+ # to aid debugging of multi-user production applications.
+ module TaggedLogging
+ module Formatter # :nodoc:
+ # This method is invoked when a log event occurs.
+ def call(severity, timestamp, progname, msg)
+ super(severity, timestamp, progname, "#{tags_text}#{msg}")
+ end
+
+ def tagged(*tags)
+ new_tags = push_tags(*tags)
+ yield self
+ ensure
+ pop_tags(new_tags.size)
+ end
+
+ def push_tags(*tags)
+ tags.flatten.reject(&:blank?).tap do |new_tags|
+ current_tags.concat new_tags
+ end
+ end
+
+ def pop_tags(size = 1)
+ current_tags.pop size
+ end
+
+ def clear_tags!
+ current_tags.clear
+ end
+
+ def current_tags
+ # We use our object ID here to avoid conflicting with other instances
+ thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}".freeze
+ Thread.current[thread_key] ||= []
+ end
+
+ def tags_text
+ tags = current_tags
+ if tags.any?
+ tags.collect { |tag| "[#{tag}] " }.join
+ end
+ end
+ end
+
+ def self.new(logger)
+ # Ensure we set a default formatter so we aren't extending nil!
+ logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
+ logger.formatter.extend Formatter
+ logger.extend(self)
+ end
+
+ delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
+
+ def tagged(*tags)
+ formatter.tagged(*tags) { yield self }
+ end
+
+ def flush
+ clear_tags!
+ super if defined?(super)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/test_case.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/test_case.rb
new file mode 100644
index 0000000..69d69c8
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/test_case.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+gem "minitest" # make sure we get the gem, not stdlib
+require "minitest"
+require "active_support/testing/tagged_logging"
+require "active_support/testing/setup_and_teardown"
+require "active_support/testing/assertions"
+require "active_support/testing/deprecation"
+require "active_support/testing/declarative"
+require "active_support/testing/isolation"
+require "active_support/testing/constant_lookup"
+require "active_support/testing/time_helpers"
+require "active_support/testing/file_fixtures"
+
+module ActiveSupport
+ class TestCase < ::Minitest::Test
+ Assertion = Minitest::Assertion
+
+ class << self
+ # Sets the order in which test cases are run.
+ #
+ # ActiveSupport::TestCase.test_order = :random # => :random
+ #
+ # Valid values are:
+ # * +:random+ (to run tests in random order)
+ # * +:parallel+ (to run tests in parallel)
+ # * +:sorted+ (to run tests alphabetically by method name)
+ # * +:alpha+ (equivalent to +:sorted+)
+ def test_order=(new_order)
+ ActiveSupport.test_order = new_order
+ end
+
+ # Returns the order in which test cases are run.
+ #
+ # ActiveSupport::TestCase.test_order # => :random
+ #
+ # Possible values are +:random+, +:parallel+, +:alpha+, +:sorted+.
+ # Defaults to +:random+.
+ def test_order
+ ActiveSupport.test_order ||= :random
+ end
+ end
+
+ alias_method :method_name, :name
+
+ include ActiveSupport::Testing::TaggedLogging
+ prepend ActiveSupport::Testing::SetupAndTeardown
+ include ActiveSupport::Testing::Assertions
+ include ActiveSupport::Testing::Deprecation
+ include ActiveSupport::Testing::TimeHelpers
+ include ActiveSupport::Testing::FileFixtures
+ extend ActiveSupport::Testing::Declarative
+
+ # test/unit backwards compatibility methods
+ alias :assert_raise :assert_raises
+ alias :assert_not_empty :refute_empty
+ alias :assert_not_equal :refute_equal
+ alias :assert_not_in_delta :refute_in_delta
+ alias :assert_not_in_epsilon :refute_in_epsilon
+ alias :assert_not_includes :refute_includes
+ alias :assert_not_instance_of :refute_instance_of
+ alias :assert_not_kind_of :refute_kind_of
+ alias :assert_no_match :refute_match
+ alias :assert_not_nil :refute_nil
+ alias :assert_not_operator :refute_operator
+ alias :assert_not_predicate :refute_predicate
+ alias :assert_not_respond_to :refute_respond_to
+ alias :assert_not_same :refute_same
+
+ ActiveSupport.run_load_hooks(:active_support_test_case, self)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/assertions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/assertions.rb
new file mode 100644
index 0000000..a891ff6
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/assertions.rb
@@ -0,0 +1,214 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Testing
+ module Assertions
+ UNTRACKED = Object.new # :nodoc:
+
+ # Asserts that an expression is not truthy. Passes if object is
+ # +nil+ or +false+. "Truthy" means "considered true in a conditional"
+ # like if foo.
+ #
+ # assert_not nil # => true
+ # assert_not false # => true
+ # assert_not 'foo' # => Expected "foo" to be nil or false
+ #
+ # An error message can be specified.
+ #
+ # assert_not foo, 'foo should be false'
+ def assert_not(object, message = nil)
+ message ||= "Expected #{mu_pp(object)} to be nil or false"
+ assert !object, message
+ end
+
+ # Assertion that the block should not raise an exception.
+ #
+ # Passes if evaluated code in the yielded block raises no exception.
+ #
+ # assert_nothing_raised do
+ # perform_service(param: 'no_exception')
+ # end
+ def assert_nothing_raised
+ yield
+ end
+
+ # Test numeric difference between the return value of an expression as a
+ # result of what is evaluated in the yielded block.
+ #
+ # assert_difference 'Article.count' do
+ # post :create, params: { article: {...} }
+ # end
+ #
+ # An arbitrary expression is passed in and evaluated.
+ #
+ # assert_difference 'Article.last.comments(:reload).size' do
+ # post :create, params: { comment: {...} }
+ # end
+ #
+ # An arbitrary positive or negative difference can be specified.
+ # The default is 1.
+ #
+ # assert_difference 'Article.count', -1 do
+ # post :delete, params: { id: ... }
+ # end
+ #
+ # An array of expressions can also be passed in and evaluated.
+ #
+ # assert_difference [ 'Article.count', 'Post.count' ], 2 do
+ # post :create, params: { article: {...} }
+ # end
+ #
+ # A hash of expressions/numeric differences can also be passed in and evaluated.
+ #
+ # assert_difference ->{ Article.count } => 1, ->{ Notification.count } => 2 do
+ # post :create, params: { article: {...} }
+ # end
+ #
+ # A lambda or a list of lambdas can be passed in and evaluated:
+ #
+ # assert_difference ->{ Article.count }, 2 do
+ # post :create, params: { article: {...} }
+ # end
+ #
+ # assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
+ # post :create, params: { article: {...} }
+ # end
+ #
+ # An error message can be specified.
+ #
+ # assert_difference 'Article.count', -1, 'An Article should be destroyed' do
+ # post :delete, params: { id: ... }
+ # end
+ def assert_difference(expression, *args, &block)
+ expressions =
+ if expression.is_a?(Hash)
+ message = args[0]
+ expression
+ else
+ difference = args[0] || 1
+ message = args[1]
+ Hash[Array(expression).map { |e| [e, difference] }]
+ end
+
+ exps = expressions.keys.map { |e|
+ e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
+ }
+ before = exps.map(&:call)
+
+ retval = yield
+
+ expressions.zip(exps, before) do |(code, diff), exp, before_value|
+ error = "#{code.inspect} didn't change by #{diff}"
+ error = "#{message}.\n#{error}" if message
+ assert_equal(before_value + diff, exp.call, error)
+ end
+
+ retval
+ end
+
+ # Assertion that the numeric result of evaluating an expression is not
+ # changed before and after invoking the passed in block.
+ #
+ # assert_no_difference 'Article.count' do
+ # post :create, params: { article: invalid_attributes }
+ # end
+ #
+ # An error message can be specified.
+ #
+ # assert_no_difference 'Article.count', 'An Article should not be created' do
+ # post :create, params: { article: invalid_attributes }
+ # end
+ def assert_no_difference(expression, message = nil, &block)
+ assert_difference expression, 0, message, &block
+ end
+
+ # Assertion that the result of evaluating an expression is changed before
+ # and after invoking the passed in block.
+ #
+ # assert_changes 'Status.all_good?' do
+ # post :create, params: { status: { ok: false } }
+ # end
+ #
+ # You can pass the block as a string to be evaluated in the context of
+ # the block. A lambda can be passed for the block as well.
+ #
+ # assert_changes -> { Status.all_good? } do
+ # post :create, params: { status: { ok: false } }
+ # end
+ #
+ # The assertion is useful to test side effects. The passed block can be
+ # anything that can be converted to string with #to_s.
+ #
+ # assert_changes :@object do
+ # @object = 42
+ # end
+ #
+ # The keyword arguments :from and :to can be given to specify the
+ # expected initial value and the expected value after the block was
+ # executed.
+ #
+ # assert_changes :@object, from: nil, to: :foo do
+ # @object = :foo
+ # end
+ #
+ # An error message can be specified.
+ #
+ # assert_changes -> { Status.all_good? }, 'Expected the status to be bad' do
+ # post :create, params: { status: { incident: true } }
+ # end
+ def assert_changes(expression, message = nil, from: UNTRACKED, to: UNTRACKED, &block)
+ exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
+
+ before = exp.call
+ retval = yield
+
+ unless from == UNTRACKED
+ error = "#{expression.inspect} isn't #{from.inspect}"
+ error = "#{message}.\n#{error}" if message
+ assert from === before, error
+ end
+
+ after = exp.call
+
+ error = "#{expression.inspect} didn't change"
+ error = "#{error}. It was already #{to}" if before == to
+ error = "#{message}.\n#{error}" if message
+ assert before != after, error
+
+ unless to == UNTRACKED
+ error = "#{expression.inspect} didn't change to #{to}"
+ error = "#{message}.\n#{error}" if message
+ assert to === after, error
+ end
+
+ retval
+ end
+
+ # Assertion that the result of evaluating an expression is not changed before
+ # and after invoking the passed in block.
+ #
+ # assert_no_changes 'Status.all_good?' do
+ # post :create, params: { status: { ok: true } }
+ # end
+ #
+ # An error message can be specified.
+ #
+ # assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
+ # post :create, params: { status: { ok: false } }
+ # end
+ def assert_no_changes(expression, message = nil, &block)
+ exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
+
+ before = exp.call
+ retval = yield
+ after = exp.call
+
+ error = "#{expression.inspect} did change to #{after}"
+ error = "#{message}.\n#{error}" if message
+ assert before == after, error
+
+ retval
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/autorun.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/autorun.rb
new file mode 100644
index 0000000..889b416
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/autorun.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+gem "minitest"
+
+require "minitest"
+
+Minitest.autorun
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/constant_lookup.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/constant_lookup.rb
new file mode 100644
index 0000000..51167e9
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/constant_lookup.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require "active_support/concern"
+require "active_support/inflector"
+
+module ActiveSupport
+ module Testing
+ # Resolves a constant from a minitest spec name.
+ #
+ # Given the following spec-style test:
+ #
+ # describe WidgetsController, :index do
+ # describe "authenticated user" do
+ # describe "returns widgets" do
+ # it "has a controller that exists" do
+ # assert_kind_of WidgetsController, @controller
+ # end
+ # end
+ # end
+ # end
+ #
+ # The test will have the following name:
+ #
+ # "WidgetsController::index::authenticated user::returns widgets"
+ #
+ # The constant WidgetsController can be resolved from the name.
+ # The following code will resolve the constant:
+ #
+ # controller = determine_constant_from_test_name(name) do |constant|
+ # Class === constant && constant < ::ActionController::Metal
+ # end
+ module ConstantLookup
+ extend ::ActiveSupport::Concern
+
+ module ClassMethods # :nodoc:
+ def determine_constant_from_test_name(test_name)
+ names = test_name.split "::"
+ while names.size > 0 do
+ names.last.sub!(/Test$/, "")
+ begin
+ constant = names.join("::").safe_constantize
+ break(constant) if yield(constant)
+ ensure
+ names.pop
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/declarative.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/declarative.rb
new file mode 100644
index 0000000..7c34036
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/declarative.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Testing
+ module Declarative
+ unless defined?(Spec)
+ # Helper to define a test method using a String. Under the hood, it replaces
+ # spaces with underscores and defines the test method.
+ #
+ # test "verify something" do
+ # ...
+ # end
+ def test(name, &block)
+ test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
+ defined = method_defined? test_name
+ raise "#{test_name} is already defined in #{self}" if defined
+ if block_given?
+ define_method(test_name, &block)
+ else
+ define_method(test_name) do
+ flunk "No implementation provided for #{name}"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/deprecation.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/deprecation.rb
new file mode 100644
index 0000000..f655435
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/deprecation.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "active_support/deprecation"
+require "active_support/core_ext/regexp"
+
+module ActiveSupport
+ module Testing
+ module Deprecation #:nodoc:
+ def assert_deprecated(match = nil, deprecator = nil, &block)
+ result, warnings = collect_deprecations(deprecator, &block)
+ assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
+ if match
+ match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
+ assert warnings.any? { |w| match.match?(w) }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
+ end
+ result
+ end
+
+ def assert_not_deprecated(deprecator = nil, &block)
+ result, deprecations = collect_deprecations(deprecator, &block)
+ assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
+ result
+ end
+
+ def collect_deprecations(deprecator = nil)
+ deprecator ||= ActiveSupport::Deprecation
+ old_behavior = deprecator.behavior
+ deprecations = []
+ deprecator.behavior = Proc.new do |message, callstack|
+ deprecations << message
+ end
+ result = yield
+ [result, deprecations]
+ ensure
+ deprecator.behavior = old_behavior
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/file_fixtures.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/file_fixtures.rb
new file mode 100644
index 0000000..ad923d1
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/file_fixtures.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Testing
+ # Adds simple access to sample files called file fixtures.
+ # File fixtures are normal files stored in
+ # ActiveSupport::TestCase.file_fixture_path.
+ #
+ # File fixtures are represented as +Pathname+ objects.
+ # This makes it easy to extract specific information:
+ #
+ # file_fixture("example.txt").read # get the file's content
+ # file_fixture("example.mp3").size # get the file size
+ module FileFixtures
+ extend ActiveSupport::Concern
+
+ included do
+ class_attribute :file_fixture_path, instance_writer: false
+ end
+
+ # Returns a +Pathname+ to the fixture file named +fixture_name+.
+ #
+ # Raises +ArgumentError+ if +fixture_name+ can't be found.
+ def file_fixture(fixture_name)
+ path = Pathname.new(File.join(file_fixture_path, fixture_name))
+
+ if path.exist?
+ path
+ else
+ msg = "the directory '%s' does not contain a file named '%s'"
+ raise ArgumentError, msg % [file_fixture_path, fixture_name]
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/isolation.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/isolation.rb
new file mode 100644
index 0000000..562f985
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/isolation.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Testing
+ module Isolation
+ require "thread"
+
+ def self.included(klass) #:nodoc:
+ klass.class_eval do
+ parallelize_me!
+ end
+ end
+
+ def self.forking_env?
+ !ENV["NO_FORK"] && Process.respond_to?(:fork)
+ end
+
+ def run
+ serialized = run_in_isolation do
+ super
+ end
+
+ Marshal.load(serialized)
+ end
+
+ module Forking
+ def run_in_isolation(&blk)
+ read, write = IO.pipe
+ read.binmode
+ write.binmode
+
+ pid = fork do
+ read.close
+ yield
+ begin
+ if error?
+ failures.map! { |e|
+ begin
+ Marshal.dump e
+ e
+ rescue TypeError
+ ex = Exception.new e.message
+ ex.set_backtrace e.backtrace
+ Minitest::UnexpectedError.new ex
+ end
+ }
+ end
+ test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
+ result = Marshal.dump(test_result)
+ end
+
+ write.puts [result].pack("m")
+ exit!
+ end
+
+ write.close
+ result = read.read
+ Process.wait2(pid)
+ result.unpack("m")[0]
+ end
+ end
+
+ module Subprocess
+ ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
+
+ # Crazy H4X to get this working in windows / jruby with
+ # no forking.
+ def run_in_isolation(&blk)
+ require "tempfile"
+
+ if ENV["ISOLATION_TEST"]
+ yield
+ test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
+ File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
+ file.puts [Marshal.dump(test_result)].pack("m")
+ end
+ exit!
+ else
+ Tempfile.open("isolation") do |tmpfile|
+ env = {
+ "ISOLATION_TEST" => self.class.name,
+ "ISOLATION_OUTPUT" => tmpfile.path
+ }
+
+ test_opts = "-n#{self.class.name}##{name}"
+
+ load_path_args = []
+ $-I.each do |p|
+ load_path_args << "-I"
+ load_path_args << File.expand_path(p)
+ end
+
+ child = IO.popen([env, Gem.ruby, *load_path_args, $0, *ORIG_ARGV, test_opts])
+
+ begin
+ Process.wait(child.pid)
+ rescue Errno::ECHILD # The child process may exit before we wait
+ nil
+ end
+
+ return tmpfile.read.unpack("m")[0]
+ end
+ end
+ end
+ end
+
+ include forking_env? ? Forking : Subprocess
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/method_call_assertions.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/method_call_assertions.rb
new file mode 100644
index 0000000..c635800
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/method_call_assertions.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require "minitest/mock"
+
+module ActiveSupport
+ module Testing
+ module MethodCallAssertions # :nodoc:
+ private
+ def assert_called(object, method_name, message = nil, times: 1, returns: nil)
+ times_called = 0
+
+ object.stub(method_name, proc { times_called += 1; returns }) { yield }
+
+ error = "Expected #{method_name} to be called #{times} times, " \
+ "but was called #{times_called} times"
+ error = "#{message}.\n#{error}" if message
+ assert_equal times, times_called, error
+ end
+
+ def assert_called_with(object, method_name, args = [], returns: nil)
+ mock = Minitest::Mock.new
+
+ if args.all? { |arg| arg.is_a?(Array) }
+ args.each { |arg| mock.expect(:call, returns, arg) }
+ else
+ mock.expect(:call, returns, args)
+ end
+
+ object.stub(method_name, mock) { yield }
+
+ mock.verify
+ end
+
+ def assert_not_called(object, method_name, message = nil, &block)
+ assert_called(object, method_name, message, times: 0, &block)
+ end
+
+ def stub_any_instance(klass, instance: klass.new)
+ klass.stub(:new, instance) { yield instance }
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/setup_and_teardown.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/setup_and_teardown.rb
new file mode 100644
index 0000000..35321cd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/setup_and_teardown.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require "active_support/callbacks"
+
+module ActiveSupport
+ module Testing
+ # Adds support for +setup+ and +teardown+ callbacks.
+ # These callbacks serve as a replacement to overwriting the
+ # #setup and #teardown methods of your TestCase.
+ #
+ # class ExampleTest < ActiveSupport::TestCase
+ # setup do
+ # # ...
+ # end
+ #
+ # teardown do
+ # # ...
+ # end
+ # end
+ module SetupAndTeardown
+ def self.prepended(klass)
+ klass.include ActiveSupport::Callbacks
+ klass.define_callbacks :setup, :teardown
+ klass.extend ClassMethods
+ end
+
+ module ClassMethods
+ # Add a callback, which runs before TestCase#setup.
+ def setup(*args, &block)
+ set_callback(:setup, :before, *args, &block)
+ end
+
+ # Add a callback, which runs after TestCase#teardown.
+ def teardown(*args, &block)
+ set_callback(:teardown, :after, *args, &block)
+ end
+ end
+
+ def before_setup # :nodoc:
+ super
+ run_callbacks :setup
+ end
+
+ def after_teardown # :nodoc:
+ begin
+ run_callbacks :teardown
+ rescue => e
+ self.failures << Minitest::UnexpectedError.new(e)
+ end
+
+ super
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/stream.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/stream.rb
new file mode 100644
index 0000000..d070a17
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/stream.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Testing
+ module Stream #:nodoc:
+ private
+
+ def silence_stream(stream)
+ old_stream = stream.dup
+ stream.reopen(IO::NULL)
+ stream.sync = true
+ yield
+ ensure
+ stream.reopen(old_stream)
+ old_stream.close
+ end
+
+ def quietly
+ silence_stream(STDOUT) do
+ silence_stream(STDERR) do
+ yield
+ end
+ end
+ end
+
+ def capture(stream)
+ stream = stream.to_s
+ captured_stream = Tempfile.new(stream)
+ stream_io = eval("$#{stream}")
+ origin_stream = stream_io.dup
+ stream_io.reopen(captured_stream)
+
+ yield
+
+ stream_io.rewind
+ return captured_stream.read
+ ensure
+ captured_stream.close
+ captured_stream.unlink
+ stream_io.reopen(origin_stream)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/tagged_logging.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/tagged_logging.rb
new file mode 100644
index 0000000..9ca50c7
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/tagged_logging.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module Testing
+ # Logs a "PostsControllerTest: test name" heading before each test to
+ # make test.log easier to search and follow along with.
+ module TaggedLogging #:nodoc:
+ attr_writer :tagged_logger
+
+ def before_setup
+ if tagged_logger && tagged_logger.info?
+ heading = "#{self.class}: #{name}"
+ divider = "-" * heading.size
+ tagged_logger.info divider
+ tagged_logger.info heading
+ tagged_logger.info divider
+ end
+ super
+ end
+
+ private
+ def tagged_logger
+ @tagged_logger ||= (defined?(Rails.logger) && Rails.logger)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/time_helpers.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/time_helpers.rb
new file mode 100644
index 0000000..998a51a
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/testing/time_helpers.rb
@@ -0,0 +1,200 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/redefine_method"
+require "active_support/core_ext/string/strip" # for strip_heredoc
+require "active_support/core_ext/time/calculations"
+require "concurrent/map"
+
+module ActiveSupport
+ module Testing
+ class SimpleStubs # :nodoc:
+ Stub = Struct.new(:object, :method_name, :original_method)
+
+ def initialize
+ @stubs = Concurrent::Map.new { |h, k| h[k] = {} }
+ end
+
+ def stub_object(object, method_name, &block)
+ if stub = stubbing(object, method_name)
+ unstub_object(stub)
+ end
+
+ new_name = "__simple_stub__#{method_name}"
+
+ @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
+
+ object.singleton_class.send :alias_method, new_name, method_name
+ object.define_singleton_method(method_name, &block)
+ end
+
+ def unstub_all!
+ @stubs.each_value do |object_stubs|
+ object_stubs.each_value do |stub|
+ unstub_object(stub)
+ end
+ end
+ @stubs.clear
+ end
+
+ def stubbing(object, method_name)
+ @stubs[object.object_id][method_name]
+ end
+
+ private
+
+ def unstub_object(stub)
+ singleton_class = stub.object.singleton_class
+ singleton_class.send :silence_redefinition_of_method, stub.method_name
+ singleton_class.send :alias_method, stub.method_name, stub.original_method
+ singleton_class.send :undef_method, stub.original_method
+ end
+ end
+
+ # Contains helpers that help you test passage of time.
+ module TimeHelpers
+ def after_teardown
+ travel_back
+ super
+ end
+
+ # Changes current time to the time in the future or in the past by a given time difference by
+ # stubbing +Time.now+, +Date.today+, and +DateTime.now+. The stubs are automatically removed
+ # at the end of the test.
+ #
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ # travel 1.day
+ # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
+ # Date.current # => Sun, 10 Nov 2013
+ # DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500
+ #
+ # This method also accepts a block, which will return the current time back to its original
+ # state at the end of the block:
+ #
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ # travel 1.day do
+ # User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00
+ # end
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ def travel(duration, &block)
+ travel_to Time.now + duration, &block
+ end
+
+ # Changes current time to the given time by stubbing +Time.now+,
+ # +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
+ # The stubs are automatically removed at the end of the test.
+ #
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44)
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
+ # Date.current # => Wed, 24 Nov 2004
+ # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
+ #
+ # Dates are taken as their timestamp at the beginning of the day in the
+ # application time zone. Time.current returns said timestamp,
+ # and Time.now its equivalent in the system time zone. Similarly,
+ # Date.current returns a date equal to the argument, and
+ # Date.today the date according to Time.now, which may
+ # be different. (Note that you rarely want to deal with Time.now,
+ # or Date.today, in order to honor the application time zone
+ # please always use Time.current and Date.current.)
+ #
+ # Note that the usec for the time passed will be set to 0 to prevent rounding
+ # errors with external services, like MySQL (which will round instead of floor,
+ # leading to off-by-one-second errors).
+ #
+ # This method also accepts a block, which will return the current time back to its original
+ # state at the end of the block:
+ #
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44) do
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
+ # end
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ def travel_to(date_or_time)
+ if block_given? && simple_stubs.stubbing(Time, :now)
+ travel_to_nested_block_call = <<-MSG.strip_heredoc
+
+ Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing.
+
+ Instead of:
+
+ travel_to 2.days.from_now do
+ # 2 days from today
+ travel_to 3.days.from_now do
+ # 5 days from today
+ end
+ end
+
+ preferred way to achieve above is:
+
+ travel 2.days do
+ # 2 days from today
+ end
+
+ travel 5.days do
+ # 5 days from today
+ end
+
+ MSG
+ raise travel_to_nested_block_call
+ end
+
+ if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
+ now = date_or_time.midnight.to_time
+ else
+ now = date_or_time.to_time.change(usec: 0)
+ end
+
+ simple_stubs.stub_object(Time, :now) { at(now.to_i) }
+ simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) }
+ simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) }
+
+ if block_given?
+ begin
+ yield
+ ensure
+ travel_back
+ end
+ end
+ end
+
+ # Returns the current time back to its original state, by removing the stubs added by
+ # +travel+ and +travel_to+.
+ #
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44)
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
+ # travel_back
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
+ def travel_back
+ simple_stubs.unstub_all!
+ end
+
+ # Calls +travel_to+ with +Time.now+.
+ #
+ # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
+ # freeze_time
+ # sleep(1)
+ # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
+ #
+ # This method also accepts a block, which will return the current time back to its original
+ # state at the end of the block:
+ #
+ # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
+ # freeze_time do
+ # sleep(1)
+ # User.create.created_at # => Sun, 09 Jul 2017 15:34:49 EST -05:00
+ # end
+ # Time.current # => Sun, 09 Jul 2017 15:34:50 EST -05:00
+ def freeze_time(&block)
+ travel_to Time.now, &block
+ end
+
+ private
+
+ def simple_stubs
+ @simple_stubs ||= SimpleStubs.new
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/time.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/time.rb
new file mode 100644
index 0000000..5185467
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/time.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ autoload :Duration, "active_support/duration"
+ autoload :TimeWithZone, "active_support/time_with_zone"
+ autoload :TimeZone, "active_support/values/time_zone"
+end
+
+require "date"
+require "time"
+
+require "active_support/core_ext/time"
+require "active_support/core_ext/date"
+require "active_support/core_ext/date_time"
+
+require "active_support/core_ext/integer/time"
+require "active_support/core_ext/numeric/time"
+
+require "active_support/core_ext/string/conversions"
+require "active_support/core_ext/string/zones"
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/time_with_zone.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/time_with_zone.rb
new file mode 100644
index 0000000..20650ce
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/time_with_zone.rb
@@ -0,0 +1,551 @@
+# frozen_string_literal: true
+
+require "active_support/duration"
+require "active_support/values/time_zone"
+require "active_support/core_ext/object/acts_like"
+require "active_support/core_ext/date_and_time/compatibility"
+
+module ActiveSupport
+ # A Time-like class that can represent a time in any time zone. Necessary
+ # because standard Ruby Time instances are limited to UTC and the
+ # system's ENV['TZ'] zone.
+ #
+ # You shouldn't ever need to create a TimeWithZone instance directly via +new+.
+ # Instead use methods +local+, +parse+, +at+ and +now+ on TimeZone instances,
+ # and +in_time_zone+ on Time and DateTime instances.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
+ # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
+ # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
+ # Time.zone.at(1171139445) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
+ # Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
+ # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
+ #
+ # See Time and TimeZone for further documentation of these methods.
+ #
+ # TimeWithZone instances implement the same API as Ruby Time instances, so
+ # that Time and TimeWithZone instances are interchangeable.
+ #
+ # t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
+ # t.hour # => 13
+ # t.dst? # => true
+ # t.utc_offset # => -14400
+ # t.zone # => "EDT"
+ # t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
+ # t + 1.day # => Mon, 19 May 2008 13:27:25 EDT -04:00
+ # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00 EST -05:00
+ # t > Time.utc(1999) # => true
+ # t.is_a?(Time) # => true
+ # t.is_a?(ActiveSupport::TimeWithZone) # => true
+ class TimeWithZone
+ # Report class name as 'Time' to thwart type checking.
+ def self.name
+ "Time"
+ end
+
+ PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N".freeze }
+ PRECISIONS[0] = "%FT%T".freeze
+
+ include Comparable, DateAndTime::Compatibility
+ attr_reader :time_zone
+
+ def initialize(utc_time, time_zone, local_time = nil, period = nil)
+ @utc = utc_time ? transfer_time_values_to_utc_constructor(utc_time) : nil
+ @time_zone, @time = time_zone, local_time
+ @period = @utc ? period : get_period_and_ensure_valid_local_time(period)
+ end
+
+ # Returns a Time instance that represents the time in +time_zone+.
+ def time
+ @time ||= period.to_local(@utc)
+ end
+
+ # Returns a Time instance of the simultaneous time in the UTC timezone.
+ def utc
+ @utc ||= period.to_utc(@time)
+ end
+ alias_method :comparable_time, :utc
+ alias_method :getgm, :utc
+ alias_method :getutc, :utc
+ alias_method :gmtime, :utc
+
+ # Returns the underlying TZInfo::TimezonePeriod.
+ def period
+ @period ||= time_zone.period_for_utc(@utc)
+ end
+
+ # Returns the simultaneous time in Time.zone, or the specified zone.
+ def in_time_zone(new_zone = ::Time.zone)
+ return self if time_zone == new_zone
+ utc.in_time_zone(new_zone)
+ end
+
+ # Returns a Time instance of the simultaneous time in the system timezone.
+ def localtime(utc_offset = nil)
+ utc.getlocal(utc_offset)
+ end
+ alias_method :getlocal, :localtime
+
+ # Returns true if the current time is within Daylight Savings Time for the
+ # specified time zone.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
+ # Time.zone.parse("2012-5-30").dst? # => true
+ # Time.zone.parse("2012-11-30").dst? # => false
+ def dst?
+ period.dst?
+ end
+ alias_method :isdst, :dst?
+
+ # Returns true if the current time zone is set to UTC.
+ #
+ # Time.zone = 'UTC' # => 'UTC'
+ # Time.zone.now.utc? # => true
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
+ # Time.zone.now.utc? # => false
+ def utc?
+ period.offset.abbreviation == :UTC || period.offset.abbreviation == :UCT
+ end
+ alias_method :gmt?, :utc?
+
+ # Returns the offset from current time to UTC time in seconds.
+ def utc_offset
+ period.utc_total_offset
+ end
+ alias_method :gmt_offset, :utc_offset
+ alias_method :gmtoff, :utc_offset
+
+ # Returns a formatted string of the offset from UTC, or an alternative
+ # string if the time zone is already UTC.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
+ # Time.zone.now.formatted_offset(true) # => "-05:00"
+ # Time.zone.now.formatted_offset(false) # => "-0500"
+ # Time.zone = 'UTC' # => "UTC"
+ # Time.zone.now.formatted_offset(true, "0") # => "0"
+ def formatted_offset(colon = true, alternate_utc_string = nil)
+ utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
+ end
+
+ # Returns the time zone abbreviation.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
+ # Time.zone.now.zone # => "EST"
+ def zone
+ period.zone_identifier.to_s
+ end
+
+ # Returns a string of the object's date, time, zone, and offset from UTC.
+ #
+ # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25 EST -05:00"
+ def inspect
+ "#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
+ end
+
+ # Returns a string of the object's date and time in the ISO 8601 standard
+ # format.
+ #
+ # Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
+ def xmlschema(fraction_digits = 0)
+ "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}"
+ end
+ alias_method :iso8601, :xmlschema
+ alias_method :rfc3339, :xmlschema
+
+ # Coerces time to a string for JSON encoding. The default format is ISO 8601.
+ # You can get %Y/%m/%d %H:%M:%S +offset style by setting
+ # ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ # to +false+.
+ #
+ # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
+ # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
+ # # => "2005-02-01T05:15:10.000-10:00"
+ #
+ # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
+ # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
+ # # => "2005/02/01 05:15:10 -1000"
+ def as_json(options = nil)
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ xmlschema(ActiveSupport::JSON::Encoding.time_precision)
+ else
+ %(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
+ end
+ end
+
+ def init_with(coder) #:nodoc:
+ initialize(coder["utc"], coder["zone"], coder["time"])
+ end
+
+ def encode_with(coder) #:nodoc:
+ coder.tag = "!ruby/object:ActiveSupport::TimeWithZone"
+ coder.map = { "utc" => utc, "zone" => time_zone, "time" => time }
+ end
+
+ # Returns a string of the object's date and time in the format used by
+ # HTTP requests.
+ #
+ # Time.zone.now.httpdate # => "Tue, 01 Jan 2013 04:39:43 GMT"
+ def httpdate
+ utc.httpdate
+ end
+
+ # Returns a string of the object's date and time in the RFC 2822 standard
+ # format.
+ #
+ # Time.zone.now.rfc2822 # => "Tue, 01 Jan 2013 04:51:39 +0000"
+ def rfc2822
+ to_s(:rfc822)
+ end
+ alias_method :rfc822, :rfc2822
+
+ # Returns a string of the object's date and time.
+ # Accepts an optional format:
+ # * :default - default value, mimics Ruby Time#to_s format.
+ # * :db - format outputs time in UTC :db time. See Time#to_formatted_s(:db).
+ # * Any key in Time::DATE_FORMATS can be used. See active_support/core_ext/time/conversions.rb.
+ def to_s(format = :default)
+ if format == :db
+ utc.to_s(format)
+ elsif formatter = ::Time::DATE_FORMATS[format]
+ formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
+ else
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format
+ end
+ end
+ alias_method :to_formatted_s, :to_s
+
+ # Replaces %Z directive with +zone before passing to Time#strftime,
+ # so that zone information is correct.
+ def strftime(format)
+ format = format.gsub(/((?:\A|[^%])(?:%%)*)%Z/, "\\1#{zone}")
+ getlocal(utc_offset).strftime(format)
+ end
+
+ # Use the time in UTC for comparisons.
+ def <=>(other)
+ utc <=> other
+ end
+
+ # Returns true if the current object's time is within the specified
+ # +min+ and +max+ time.
+ def between?(min, max)
+ utc.between?(min, max)
+ end
+
+ # Returns true if the current object's time is in the past.
+ def past?
+ utc.past?
+ end
+
+ # Returns true if the current object's time falls within
+ # the current day.
+ def today?
+ time.today?
+ end
+
+ # Returns true if the current object's time is in the future.
+ def future?
+ utc.future?
+ end
+
+ # Returns +true+ if +other+ is equal to current object.
+ def eql?(other)
+ other.eql?(utc)
+ end
+
+ def hash
+ utc.hash
+ end
+
+ # Adds an interval of time to the current object's time and returns that
+ # value as a new TimeWithZone object.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
+ # now + 1000 # => Sun, 02 Nov 2014 01:43:08 EDT -04:00
+ #
+ # If we're adding a Duration of variable length (i.e., years, months, days),
+ # move forward from #time, otherwise move forward from #utc, for accuracy
+ # when moving across DST boundaries.
+ #
+ # For instance, a time + 24.hours will advance exactly 24 hours, while a
+ # time + 1.day will advance 23-25 hours, depending on the day.
+ #
+ # now + 24.hours # => Mon, 03 Nov 2014 00:26:28 EST -05:00
+ # now + 1.day # => Mon, 03 Nov 2014 01:26:28 EST -05:00
+ def +(other)
+ if duration_of_variable_length?(other)
+ method_missing(:+, other)
+ else
+ result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other)
+ result.in_time_zone(time_zone)
+ end
+ end
+ alias_method :since, :+
+ alias_method :in, :+
+
+ # Returns a new TimeWithZone object that represents the difference between
+ # the current object's time and the +other+ time.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
+ # now - 1000 # => Mon, 03 Nov 2014 00:09:48 EST -05:00
+ #
+ # If subtracting a Duration of variable length (i.e., years, months, days),
+ # move backward from #time, otherwise move backward from #utc, for accuracy
+ # when moving across DST boundaries.
+ #
+ # For instance, a time - 24.hours will go subtract exactly 24 hours, while a
+ # time - 1.day will subtract 23-25 hours, depending on the day.
+ #
+ # now - 24.hours # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
+ # now - 1.day # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
+ def -(other)
+ if other.acts_like?(:time)
+ to_time - other.to_time
+ elsif duration_of_variable_length?(other)
+ method_missing(:-, other)
+ else
+ result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)
+ result.in_time_zone(time_zone)
+ end
+ end
+
+ # Subtracts an interval of time from the current object's time and returns
+ # the result as a new TimeWithZone object.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
+ # now.ago(1000) # => Mon, 03 Nov 2014 00:09:48 EST -05:00
+ #
+ # If we're subtracting a Duration of variable length (i.e., years, months,
+ # days), move backward from #time, otherwise move backward from #utc, for
+ # accuracy when moving across DST boundaries.
+ #
+ # For instance, time.ago(24.hours) will move back exactly 24 hours,
+ # while time.ago(1.day) will move back 23-25 hours, depending on
+ # the day.
+ #
+ # now.ago(24.hours) # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
+ # now.ago(1.day) # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
+ def ago(other)
+ since(-other)
+ end
+
+ # Returns a new +ActiveSupport::TimeWithZone+ where one or more of the elements have
+ # been changed according to the +options+ parameter. The time options (:hour,
+ # :min, :sec, :usec, :nsec) reset cascadingly,
+ # so if only the hour is passed, then minute, sec, usec and nsec is set to 0. If the
+ # hour and minute is passed, then sec, usec and nsec is set to 0. The +options+
+ # parameter takes a hash with any of these keys: :year, :month,
+ # :day, :hour, :min, :sec, :usec,
+ # :nsec, :offset, :zone. Pass either :usec
+ # or :nsec, not both. Similarly, pass either :zone or
+ # :offset, not both.
+ #
+ # t = Time.zone.now # => Fri, 14 Apr 2017 11:45:15 EST -05:00
+ # t.change(year: 2020) # => Tue, 14 Apr 2020 11:45:15 EST -05:00
+ # t.change(hour: 12) # => Fri, 14 Apr 2017 12:00:00 EST -05:00
+ # t.change(min: 30) # => Fri, 14 Apr 2017 11:30:00 EST -05:00
+ # t.change(offset: "-10:00") # => Fri, 14 Apr 2017 11:45:15 HST -10:00
+ # t.change(zone: "Hawaii") # => Fri, 14 Apr 2017 11:45:15 HST -10:00
+ def change(options)
+ if options[:zone] && options[:offset]
+ raise ArgumentError, "Can't change both :offset and :zone at the same time: #{options.inspect}"
+ end
+
+ new_time = time.change(options)
+
+ if options[:zone]
+ new_zone = ::Time.find_zone(options[:zone])
+ elsif options[:offset]
+ new_zone = ::Time.find_zone(new_time.utc_offset)
+ end
+
+ new_zone ||= time_zone
+ periods = new_zone.periods_for_local(new_time)
+
+ self.class.new(nil, new_zone, new_time, periods.include?(period) ? period : nil)
+ end
+
+ # Uses Date to provide precise Time calculations for years, months, and days
+ # according to the proleptic Gregorian calendar. The result is returned as a
+ # new TimeWithZone object.
+ #
+ # The +options+ parameter takes a hash with any of these keys:
+ # :years, :months, :weeks, :days,
+ # :hours, :minutes, :seconds.
+ #
+ # If advancing by a value of variable length (i.e., years, weeks, months,
+ # days), move forward from #time, otherwise move forward from #utc, for
+ # accuracy when moving across DST boundaries.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
+ # now.advance(seconds: 1) # => Sun, 02 Nov 2014 01:26:29 EDT -04:00
+ # now.advance(minutes: 1) # => Sun, 02 Nov 2014 01:27:28 EDT -04:00
+ # now.advance(hours: 1) # => Sun, 02 Nov 2014 01:26:28 EST -05:00
+ # now.advance(days: 1) # => Mon, 03 Nov 2014 01:26:28 EST -05:00
+ # now.advance(weeks: 1) # => Sun, 09 Nov 2014 01:26:28 EST -05:00
+ # now.advance(months: 1) # => Tue, 02 Dec 2014 01:26:28 EST -05:00
+ # now.advance(years: 1) # => Mon, 02 Nov 2015 01:26:28 EST -05:00
+ def advance(options)
+ # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
+ # otherwise advance from #utc, for accuracy when moving across DST boundaries
+ if options.values_at(:years, :weeks, :months, :days).any?
+ method_missing(:advance, options)
+ else
+ utc.advance(options).in_time_zone(time_zone)
+ end
+ end
+
+ %w(year mon month day mday wday yday hour min sec usec nsec to_date).each do |method_name|
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
+ def #{method_name} # def month
+ time.#{method_name} # time.month
+ end # end
+ EOV
+ end
+
+ # Returns Array of parts of Time in sequence of
+ # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone].
+ #
+ # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27 UTC +00:00
+ # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"]
+ def to_a
+ [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
+ end
+
+ # Returns the object's date and time as a floating point number of seconds
+ # since the Epoch (January 1, 1970 00:00 UTC).
+ #
+ # Time.zone.now.to_f # => 1417709320.285418
+ def to_f
+ utc.to_f
+ end
+
+ # Returns the object's date and time as an integer number of seconds
+ # since the Epoch (January 1, 1970 00:00 UTC).
+ #
+ # Time.zone.now.to_i # => 1417709320
+ def to_i
+ utc.to_i
+ end
+ alias_method :tv_sec, :to_i
+
+ # Returns the object's date and time as a rational number of seconds
+ # since the Epoch (January 1, 1970 00:00 UTC).
+ #
+ # Time.zone.now.to_r # => (708854548642709/500000)
+ def to_r
+ utc.to_r
+ end
+
+ # Returns an instance of DateTime with the timezone's UTC offset
+ #
+ # Time.zone.now.to_datetime # => Tue, 18 Aug 2015 02:32:20 +0000
+ # Time.current.in_time_zone('Hawaii').to_datetime # => Mon, 17 Aug 2015 16:32:20 -1000
+ def to_datetime
+ @to_datetime ||= utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
+ end
+
+ # Returns an instance of +Time+, either with the same UTC offset
+ # as +self+ or in the local system timezone depending on the setting
+ # of +ActiveSupport.to_time_preserves_timezone+.
+ def to_time
+ if preserve_timezone
+ @to_time_with_instance_offset ||= getlocal(utc_offset)
+ else
+ @to_time_with_system_offset ||= getlocal
+ end
+ end
+
+ # So that +self+ acts_like?(:time).
+ def acts_like_time?
+ true
+ end
+
+ # Say we're a Time to thwart type checking.
+ def is_a?(klass)
+ klass == ::Time || super
+ end
+ alias_method :kind_of?, :is_a?
+
+ # An instance of ActiveSupport::TimeWithZone is never blank
+ def blank?
+ false
+ end
+
+ def freeze
+ # preload instance variables before freezing
+ period; utc; time; to_datetime; to_time
+ super
+ end
+
+ def marshal_dump
+ [utc, time_zone.name, time]
+ end
+
+ def marshal_load(variables)
+ initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc)
+ end
+
+ # respond_to_missing? is not called in some cases, such as when type conversion is
+ # performed with Kernel#String
+ def respond_to?(sym, include_priv = false)
+ # ensure that we're not going to throw and rescue from NoMethodError in method_missing which is slow
+ return false if sym.to_sym == :to_str
+ super
+ end
+
+ # Ensure proxy class responds to all methods that underlying time instance
+ # responds to.
+ def respond_to_missing?(sym, include_priv)
+ return false if sym.to_sym == :acts_like_date?
+ time.respond_to?(sym, include_priv)
+ end
+
+ # Send the missing method to +time+ instance, and wrap result in a new
+ # TimeWithZone with the existing +time_zone+.
+ def method_missing(sym, *args, &block)
+ wrap_with_time_zone time.__send__(sym, *args, &block)
+ rescue NoMethodError => e
+ raise e, e.message.sub(time.inspect, inspect), e.backtrace
+ end
+
+ private
+ def get_period_and_ensure_valid_local_time(period)
+ # we don't want a Time.local instance enforcing its own DST rules as well,
+ # so transfer time values to a utc constructor if necessary
+ @time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
+ begin
+ period || @time_zone.period_for_local(@time)
+ rescue ::TZInfo::PeriodNotFound
+ # time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
+ @time += 1.hour
+ retry
+ end
+ end
+
+ def transfer_time_values_to_utc_constructor(time)
+ # avoid creating another Time object if possible
+ return time if time.instance_of?(::Time) && time.utc?
+ ::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec + time.subsec)
+ end
+
+ def duration_of_variable_length?(obj)
+ ActiveSupport::Duration === obj && obj.parts.any? { |p| [:years, :months, :weeks, :days].include?(p[0]) }
+ end
+
+ def wrap_with_time_zone(time)
+ if time.acts_like?(:time)
+ periods = time_zone.periods_for_local(time)
+ self.class.new(nil, time_zone, time, periods.include?(period) ? period : nil)
+ elsif time.is_a?(Range)
+ wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end)
+ else
+ time
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/values/time_zone.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/values/time_zone.rb
new file mode 100644
index 0000000..90501bc
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/values/time_zone.rb
@@ -0,0 +1,565 @@
+# frozen_string_literal: true
+
+require "tzinfo"
+require "concurrent/map"
+require "active_support/core_ext/object/blank"
+
+module ActiveSupport
+ # The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
+ # It allows us to do the following:
+ #
+ # * Limit the set of zones provided by TZInfo to a meaningful subset of 134
+ # zones.
+ # * Retrieve and display zones with a friendlier name
+ # (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
+ # * Lazily load TZInfo::Timezone instances only when they're needed.
+ # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+,
+ # +parse+, +at+ and +now+ methods.
+ #
+ # If you set config.time_zone in the Rails Application, you can
+ # access this TimeZone object via Time.zone:
+ #
+ # # application.rb:
+ # class Application < Rails::Application
+ # config.time_zone = 'Eastern Time (US & Canada)'
+ # end
+ #
+ # Time.zone # => #
+ # Time.zone.name # => "Eastern Time (US & Canada)"
+ # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
+ class TimeZone
+ # Keys are Rails TimeZone names, values are TZInfo identifiers.
+ MAPPING = {
+ "International Date Line West" => "Etc/GMT+12",
+ "Midway Island" => "Pacific/Midway",
+ "American Samoa" => "Pacific/Pago_Pago",
+ "Hawaii" => "Pacific/Honolulu",
+ "Alaska" => "America/Juneau",
+ "Pacific Time (US & Canada)" => "America/Los_Angeles",
+ "Tijuana" => "America/Tijuana",
+ "Mountain Time (US & Canada)" => "America/Denver",
+ "Arizona" => "America/Phoenix",
+ "Chihuahua" => "America/Chihuahua",
+ "Mazatlan" => "America/Mazatlan",
+ "Central Time (US & Canada)" => "America/Chicago",
+ "Saskatchewan" => "America/Regina",
+ "Guadalajara" => "America/Mexico_City",
+ "Mexico City" => "America/Mexico_City",
+ "Monterrey" => "America/Monterrey",
+ "Central America" => "America/Guatemala",
+ "Eastern Time (US & Canada)" => "America/New_York",
+ "Indiana (East)" => "America/Indiana/Indianapolis",
+ "Bogota" => "America/Bogota",
+ "Lima" => "America/Lima",
+ "Quito" => "America/Lima",
+ "Atlantic Time (Canada)" => "America/Halifax",
+ "Caracas" => "America/Caracas",
+ "La Paz" => "America/La_Paz",
+ "Santiago" => "America/Santiago",
+ "Newfoundland" => "America/St_Johns",
+ "Brasilia" => "America/Sao_Paulo",
+ "Buenos Aires" => "America/Argentina/Buenos_Aires",
+ "Montevideo" => "America/Montevideo",
+ "Georgetown" => "America/Guyana",
+ "Puerto Rico" => "America/Puerto_Rico",
+ "Greenland" => "America/Godthab",
+ "Mid-Atlantic" => "Atlantic/South_Georgia",
+ "Azores" => "Atlantic/Azores",
+ "Cape Verde Is." => "Atlantic/Cape_Verde",
+ "Dublin" => "Europe/Dublin",
+ "Edinburgh" => "Europe/London",
+ "Lisbon" => "Europe/Lisbon",
+ "London" => "Europe/London",
+ "Casablanca" => "Africa/Casablanca",
+ "Monrovia" => "Africa/Monrovia",
+ "UTC" => "Etc/UTC",
+ "Belgrade" => "Europe/Belgrade",
+ "Bratislava" => "Europe/Bratislava",
+ "Budapest" => "Europe/Budapest",
+ "Ljubljana" => "Europe/Ljubljana",
+ "Prague" => "Europe/Prague",
+ "Sarajevo" => "Europe/Sarajevo",
+ "Skopje" => "Europe/Skopje",
+ "Warsaw" => "Europe/Warsaw",
+ "Zagreb" => "Europe/Zagreb",
+ "Brussels" => "Europe/Brussels",
+ "Copenhagen" => "Europe/Copenhagen",
+ "Madrid" => "Europe/Madrid",
+ "Paris" => "Europe/Paris",
+ "Amsterdam" => "Europe/Amsterdam",
+ "Berlin" => "Europe/Berlin",
+ "Bern" => "Europe/Zurich",
+ "Zurich" => "Europe/Zurich",
+ "Rome" => "Europe/Rome",
+ "Stockholm" => "Europe/Stockholm",
+ "Vienna" => "Europe/Vienna",
+ "West Central Africa" => "Africa/Algiers",
+ "Bucharest" => "Europe/Bucharest",
+ "Cairo" => "Africa/Cairo",
+ "Helsinki" => "Europe/Helsinki",
+ "Kyiv" => "Europe/Kiev",
+ "Riga" => "Europe/Riga",
+ "Sofia" => "Europe/Sofia",
+ "Tallinn" => "Europe/Tallinn",
+ "Vilnius" => "Europe/Vilnius",
+ "Athens" => "Europe/Athens",
+ "Istanbul" => "Europe/Istanbul",
+ "Minsk" => "Europe/Minsk",
+ "Jerusalem" => "Asia/Jerusalem",
+ "Harare" => "Africa/Harare",
+ "Pretoria" => "Africa/Johannesburg",
+ "Kaliningrad" => "Europe/Kaliningrad",
+ "Moscow" => "Europe/Moscow",
+ "St. Petersburg" => "Europe/Moscow",
+ "Volgograd" => "Europe/Volgograd",
+ "Samara" => "Europe/Samara",
+ "Kuwait" => "Asia/Kuwait",
+ "Riyadh" => "Asia/Riyadh",
+ "Nairobi" => "Africa/Nairobi",
+ "Baghdad" => "Asia/Baghdad",
+ "Tehran" => "Asia/Tehran",
+ "Abu Dhabi" => "Asia/Muscat",
+ "Muscat" => "Asia/Muscat",
+ "Baku" => "Asia/Baku",
+ "Tbilisi" => "Asia/Tbilisi",
+ "Yerevan" => "Asia/Yerevan",
+ "Kabul" => "Asia/Kabul",
+ "Ekaterinburg" => "Asia/Yekaterinburg",
+ "Islamabad" => "Asia/Karachi",
+ "Karachi" => "Asia/Karachi",
+ "Tashkent" => "Asia/Tashkent",
+ "Chennai" => "Asia/Kolkata",
+ "Kolkata" => "Asia/Kolkata",
+ "Mumbai" => "Asia/Kolkata",
+ "New Delhi" => "Asia/Kolkata",
+ "Kathmandu" => "Asia/Kathmandu",
+ "Astana" => "Asia/Dhaka",
+ "Dhaka" => "Asia/Dhaka",
+ "Sri Jayawardenepura" => "Asia/Colombo",
+ "Almaty" => "Asia/Almaty",
+ "Novosibirsk" => "Asia/Novosibirsk",
+ "Rangoon" => "Asia/Rangoon",
+ "Bangkok" => "Asia/Bangkok",
+ "Hanoi" => "Asia/Bangkok",
+ "Jakarta" => "Asia/Jakarta",
+ "Krasnoyarsk" => "Asia/Krasnoyarsk",
+ "Beijing" => "Asia/Shanghai",
+ "Chongqing" => "Asia/Chongqing",
+ "Hong Kong" => "Asia/Hong_Kong",
+ "Urumqi" => "Asia/Urumqi",
+ "Kuala Lumpur" => "Asia/Kuala_Lumpur",
+ "Singapore" => "Asia/Singapore",
+ "Taipei" => "Asia/Taipei",
+ "Perth" => "Australia/Perth",
+ "Irkutsk" => "Asia/Irkutsk",
+ "Ulaanbaatar" => "Asia/Ulaanbaatar",
+ "Seoul" => "Asia/Seoul",
+ "Osaka" => "Asia/Tokyo",
+ "Sapporo" => "Asia/Tokyo",
+ "Tokyo" => "Asia/Tokyo",
+ "Yakutsk" => "Asia/Yakutsk",
+ "Darwin" => "Australia/Darwin",
+ "Adelaide" => "Australia/Adelaide",
+ "Canberra" => "Australia/Melbourne",
+ "Melbourne" => "Australia/Melbourne",
+ "Sydney" => "Australia/Sydney",
+ "Brisbane" => "Australia/Brisbane",
+ "Hobart" => "Australia/Hobart",
+ "Vladivostok" => "Asia/Vladivostok",
+ "Guam" => "Pacific/Guam",
+ "Port Moresby" => "Pacific/Port_Moresby",
+ "Magadan" => "Asia/Magadan",
+ "Srednekolymsk" => "Asia/Srednekolymsk",
+ "Solomon Is." => "Pacific/Guadalcanal",
+ "New Caledonia" => "Pacific/Noumea",
+ "Fiji" => "Pacific/Fiji",
+ "Kamchatka" => "Asia/Kamchatka",
+ "Marshall Is." => "Pacific/Majuro",
+ "Auckland" => "Pacific/Auckland",
+ "Wellington" => "Pacific/Auckland",
+ "Nuku'alofa" => "Pacific/Tongatapu",
+ "Tokelau Is." => "Pacific/Fakaofo",
+ "Chatham Is." => "Pacific/Chatham",
+ "Samoa" => "Pacific/Apia"
+ }
+
+ UTC_OFFSET_WITH_COLON = "%s%02d:%02d"
+ UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "")
+
+ @lazy_zones_map = Concurrent::Map.new
+ @country_zones = Concurrent::Map.new
+
+ class << self
+ # Assumes self represents an offset from UTC in seconds (as returned from
+ # Time#utc_offset) and turns this into an +HH:MM formatted string.
+ #
+ # ActiveSupport::TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00"
+ def seconds_to_utc_offset(seconds, colon = true)
+ format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON
+ sign = (seconds < 0 ? "-" : "+")
+ hours = seconds.abs / 3600
+ minutes = (seconds.abs % 3600) / 60
+ format % [sign, hours, minutes]
+ end
+
+ def find_tzinfo(name)
+ TZInfo::Timezone.new(MAPPING[name] || name)
+ end
+
+ alias_method :create, :new
+
+ # Returns a TimeZone instance with the given name, or +nil+ if no
+ # such TimeZone instance exists. (This exists to support the use of
+ # this class with the +composed_of+ macro.)
+ def new(name)
+ self[name]
+ end
+
+ # Returns an array of all TimeZone objects. There are multiple
+ # TimeZone objects per time zone, in many cases, to make it easier
+ # for users to find their own time zone.
+ def all
+ @zones ||= zones_map.values.sort
+ end
+
+ # Locate a specific time zone object. If the argument is a string, it
+ # is interpreted to mean the name of the timezone to locate. If it is a
+ # numeric value it is either the hour offset, or the second offset, of the
+ # timezone to find. (The first one with that offset will be returned.)
+ # Returns +nil+ if no such time zone is known to the system.
+ def [](arg)
+ case arg
+ when String
+ begin
+ @lazy_zones_map[arg] ||= create(arg)
+ rescue TZInfo::InvalidTimezoneIdentifier
+ nil
+ end
+ when Numeric, ActiveSupport::Duration
+ arg *= 3600 if arg.abs <= 13
+ all.find { |z| z.utc_offset == arg.to_i }
+ else
+ raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}"
+ end
+ end
+
+ # A convenience method for returning a collection of TimeZone objects
+ # for time zones in the USA.
+ def us_zones
+ country_zones(:us)
+ end
+
+ # A convenience method for returning a collection of TimeZone objects
+ # for time zones in the country specified by its ISO 3166-1 Alpha2 code.
+ def country_zones(country_code)
+ code = country_code.to_s.upcase
+ @country_zones[code] ||= load_country_zones(code)
+ end
+
+ def clear #:nodoc:
+ @lazy_zones_map = Concurrent::Map.new
+ @country_zones = Concurrent::Map.new
+ @zones = nil
+ @zones_map = nil
+ end
+
+ private
+ def load_country_zones(code)
+ country = TZInfo::Country.get(code)
+ country.zone_identifiers.map do |tz_id|
+ if MAPPING.value?(tz_id)
+ MAPPING.inject([]) do |memo, (key, value)|
+ memo << self[key] if value == tz_id
+ memo
+ end
+ else
+ create(tz_id, nil, TZInfo::Timezone.new(tz_id))
+ end
+ end.flatten(1).sort!
+ end
+
+ def zones_map
+ @zones_map ||= MAPPING.each_with_object({}) do |(name, _), zones|
+ timezone = self[name]
+ zones[name] = timezone if timezone
+ end
+ end
+ end
+
+ include Comparable
+ attr_reader :name
+ attr_reader :tzinfo
+
+ # Create a new TimeZone object with the given name and offset. The
+ # offset is the number of seconds that this time zone is offset from UTC
+ # (GMT). Seconds were chosen as the offset unit because that is the unit
+ # that Ruby uses to represent time zone offsets (see Time#utc_offset).
+ def initialize(name, utc_offset = nil, tzinfo = nil)
+ @name = name
+ @utc_offset = utc_offset
+ @tzinfo = tzinfo || TimeZone.find_tzinfo(name)
+ end
+
+ # Returns the offset of this time zone from UTC in seconds.
+ def utc_offset
+ if @utc_offset
+ @utc_offset
+ else
+ tzinfo.current_period.utc_offset if tzinfo && tzinfo.current_period
+ end
+ end
+
+ # Returns a formatted string of the offset from UTC, or an alternative
+ # string if the time zone is already UTC.
+ #
+ # zone = ActiveSupport::TimeZone['Central Time (US & Canada)']
+ # zone.formatted_offset # => "-06:00"
+ # zone.formatted_offset(false) # => "-0600"
+ def formatted_offset(colon = true, alternate_utc_string = nil)
+ utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon)
+ end
+
+ # Compare this time zone to the parameter. The two are compared first on
+ # their offsets, and then by name.
+ def <=>(zone)
+ return unless zone.respond_to? :utc_offset
+ result = (utc_offset <=> zone.utc_offset)
+ result = (name <=> zone.name) if result == 0
+ result
+ end
+
+ # Compare #name and TZInfo identifier to a supplied regexp, returning +true+
+ # if a match is found.
+ def =~(re)
+ re === name || re === MAPPING[name]
+ end
+
+ # Returns a textual representation of this time zone.
+ def to_s
+ "(GMT#{formatted_offset}) #{name}"
+ end
+
+ # Method for creating new ActiveSupport::TimeWithZone instance in time zone
+ # of +self+ from given values.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00
+ def local(*args)
+ time = Time.utc(*args)
+ ActiveSupport::TimeWithZone.new(nil, self, time)
+ end
+
+ # Method for creating new ActiveSupport::TimeWithZone instance in time zone
+ # of +self+ from number of seconds since the Unix epoch.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.utc(2000).to_f # => 946684800.0
+ # Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ def at(secs)
+ Time.at(secs).utc.in_time_zone(self)
+ end
+
+ # Method for creating new ActiveSupport::TimeWithZone instance in time zone
+ # of +self+ from an ISO 8601 string.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.iso8601('1999-12-31T14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ #
+ # If the time components are missing then they will be set to zero.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.iso8601('1999-12-31') # => Fri, 31 Dec 1999 00:00:00 HST -10:00
+ #
+ # If the string is invalid then an +ArgumentError+ will be raised unlike +parse+
+ # which usually returns +nil+ when given an invalid date string.
+ def iso8601(str)
+ parts = Date._iso8601(str)
+
+ raise ArgumentError, "invalid date" if parts.empty?
+
+ time = Time.new(
+ parts.fetch(:year),
+ parts.fetch(:mon),
+ parts.fetch(:mday),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset, 0)
+ )
+
+ if parts[:offset]
+ TimeWithZone.new(time.utc, self)
+ else
+ TimeWithZone.new(nil, self, time)
+ end
+ end
+
+ # Method for creating new ActiveSupport::TimeWithZone instance in time zone
+ # of +self+ from parsed string.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ #
+ # If upper components are missing from the string, they are supplied from
+ # TimeZone#now:
+ #
+ # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ # Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
+ #
+ # However, if the date component is not provided, but any other upper
+ # components are supplied, then the day of the month defaults to 1:
+ #
+ # Time.zone.parse('Mar 2000') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
+ #
+ # If the string is invalid then an +ArgumentError+ could be raised.
+ def parse(str, now = now())
+ parts_to_time(Date._parse(str, false), now)
+ end
+
+ # Method for creating new ActiveSupport::TimeWithZone instance in time zone
+ # of +self+ from an RFC 3339 string.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.rfc3339('2000-01-01T00:00:00Z') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ #
+ # If the time or zone components are missing then an +ArgumentError+ will
+ # be raised. This is much stricter than either +parse+ or +iso8601+ which
+ # allow for missing components.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.rfc3339('1999-12-31') # => ArgumentError: invalid date
+ def rfc3339(str)
+ parts = Date._rfc3339(str)
+
+ raise ArgumentError, "invalid date" if parts.empty?
+
+ time = Time.new(
+ parts.fetch(:year),
+ parts.fetch(:mon),
+ parts.fetch(:mday),
+ parts.fetch(:hour),
+ parts.fetch(:min),
+ parts.fetch(:sec) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset)
+ )
+
+ TimeWithZone.new(time.utc, self)
+ end
+
+ # Parses +str+ according to +format+ and returns an ActiveSupport::TimeWithZone.
+ #
+ # Assumes that +str+ is a time in the time zone +self+,
+ # unless +format+ includes an explicit time zone.
+ # (This is the same behavior as +parse+.)
+ # In either case, the returned TimeWithZone has the timezone of +self+.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.strptime('1999-12-31 14:00:00', '%Y-%m-%d %H:%M:%S') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ #
+ # If upper components are missing from the string, they are supplied from
+ # TimeZone#now:
+ #
+ # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
+ # Time.zone.strptime('22:30:00', '%H:%M:%S') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
+ #
+ # However, if the date component is not provided, but any other upper
+ # components are supplied, then the day of the month defaults to 1:
+ #
+ # Time.zone.strptime('Mar 2000', '%b %Y') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
+ def strptime(str, format, now = now())
+ parts_to_time(DateTime._strptime(str, format), now)
+ end
+
+ # Returns an ActiveSupport::TimeWithZone instance representing the current
+ # time in the time zone represented by +self+.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00
+ def now
+ time_now.utc.in_time_zone(self)
+ end
+
+ # Returns the current date in this time zone.
+ def today
+ tzinfo.now.to_date
+ end
+
+ # Returns the next date in this time zone.
+ def tomorrow
+ today + 1
+ end
+
+ # Returns the previous date in this time zone.
+ def yesterday
+ today - 1
+ end
+
+ # Adjust the given time to the simultaneous time in the time zone
+ # represented by +self+. Returns a Time.utc() instance -- if you want an
+ # ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead.
+ def utc_to_local(time)
+ tzinfo.utc_to_local(time)
+ end
+
+ # Adjust the given time to the simultaneous time in UTC. Returns a
+ # Time.utc() instance.
+ def local_to_utc(time, dst = true)
+ tzinfo.local_to_utc(time, dst)
+ end
+
+ # Available so that TimeZone instances respond like TZInfo::Timezone
+ # instances.
+ def period_for_utc(time)
+ tzinfo.period_for_utc(time)
+ end
+
+ # Available so that TimeZone instances respond like TZInfo::Timezone
+ # instances.
+ def period_for_local(time, dst = true)
+ tzinfo.period_for_local(time, dst) { |periods| periods.last }
+ end
+
+ def periods_for_local(time) #:nodoc:
+ tzinfo.periods_for_local(time)
+ end
+
+ def init_with(coder) #:nodoc:
+ initialize(coder["name"])
+ end
+
+ def encode_with(coder) #:nodoc:
+ coder.tag = "!ruby/object:#{self.class}"
+ coder.map = { "name" => tzinfo.name }
+ end
+
+ private
+ def parts_to_time(parts, now)
+ raise ArgumentError, "invalid date" if parts.nil?
+ return if parts.empty?
+
+ if parts[:seconds]
+ time = Time.at(parts[:seconds])
+ else
+ time = Time.new(
+ parts.fetch(:year, now.year),
+ parts.fetch(:mon, now.month),
+ parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset, 0)
+ )
+ end
+
+ if parts[:offset] || parts[:seconds]
+ TimeWithZone.new(time.utc, self)
+ else
+ TimeWithZone.new(nil, self, time)
+ end
+ end
+
+ def time_now
+ Time.now
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/values/unicode_tables.dat b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/values/unicode_tables.dat
new file mode 100644
index 0000000..f7d9c48
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/values/unicode_tables.dat differ
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/version.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/version.rb
new file mode 100644
index 0000000..928838c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/version.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require_relative "gem_version"
+
+module ActiveSupport
+ # Returns the version of the currently loaded ActiveSupport as a Gem::Version
+ def self.version
+ gem_version
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini.rb
new file mode 100644
index 0000000..f087e2c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini.rb
@@ -0,0 +1,209 @@
+# frozen_string_literal: true
+
+require "time"
+require "base64"
+require "bigdecimal"
+require "active_support/core_ext/module/delegation"
+require "active_support/core_ext/string/inflections"
+require "active_support/core_ext/date_time/calculations"
+
+module ActiveSupport
+ # = XmlMini
+ #
+ # To use the much faster libxml parser:
+ # gem 'libxml-ruby', '=0.9.7'
+ # XmlMini.backend = 'LibXML'
+ module XmlMini
+ extend self
+
+ # This module decorates files deserialized using Hash.from_xml with
+ # the original_filename and content_type methods.
+ module FileLike #:nodoc:
+ attr_writer :original_filename, :content_type
+
+ def original_filename
+ @original_filename || "untitled"
+ end
+
+ def content_type
+ @content_type || "application/octet-stream"
+ end
+ end
+
+ DEFAULT_ENCODINGS = {
+ "binary" => "base64"
+ } unless defined?(DEFAULT_ENCODINGS)
+
+ unless defined?(TYPE_NAMES)
+ TYPE_NAMES = {
+ "Symbol" => "symbol",
+ "Integer" => "integer",
+ "BigDecimal" => "decimal",
+ "Float" => "float",
+ "TrueClass" => "boolean",
+ "FalseClass" => "boolean",
+ "Date" => "date",
+ "DateTime" => "dateTime",
+ "Time" => "dateTime",
+ "Array" => "array",
+ "Hash" => "hash"
+ }
+
+ # No need to map these on Ruby 2.4+
+ TYPE_NAMES["Fixnum"] = "integer" unless 0.class == Integer
+ TYPE_NAMES["Bignum"] = "integer" unless 0.class == Integer
+ end
+
+ FORMATTING = {
+ "symbol" => Proc.new { |symbol| symbol.to_s },
+ "date" => Proc.new { |date| date.to_s(:db) },
+ "dateTime" => Proc.new { |time| time.xmlschema },
+ "binary" => Proc.new { |binary| ::Base64.encode64(binary) },
+ "yaml" => Proc.new { |yaml| yaml.to_yaml }
+ } unless defined?(FORMATTING)
+
+ # TODO use regexp instead of Date.parse
+ unless defined?(PARSING)
+ PARSING = {
+ "symbol" => Proc.new { |symbol| symbol.to_s.to_sym },
+ "date" => Proc.new { |date| ::Date.parse(date) },
+ "datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
+ "integer" => Proc.new { |integer| integer.to_i },
+ "float" => Proc.new { |float| float.to_f },
+ "decimal" => Proc.new do |number|
+ if String === number
+ begin
+ BigDecimal(number)
+ rescue ArgumentError
+ BigDecimal(number.to_f.to_s)
+ end
+ else
+ BigDecimal(number)
+ end
+ end,
+ "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.to_s.strip) },
+ "string" => Proc.new { |string| string.to_s },
+ "yaml" => Proc.new { |yaml| YAML.load(yaml) rescue yaml },
+ "base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
+ "binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) },
+ "file" => Proc.new { |file, entity| _parse_file(file, entity) }
+ }
+
+ PARSING.update(
+ "double" => PARSING["float"],
+ "dateTime" => PARSING["datetime"]
+ )
+ end
+
+ attr_accessor :depth
+ self.depth = 100
+
+ delegate :parse, to: :backend
+
+ def backend
+ current_thread_backend || @backend
+ end
+
+ def backend=(name)
+ backend = name && cast_backend_name_to_module(name)
+ self.current_thread_backend = backend if current_thread_backend
+ @backend = backend
+ end
+
+ def with_backend(name)
+ old_backend = current_thread_backend
+ self.current_thread_backend = name && cast_backend_name_to_module(name)
+ yield
+ ensure
+ self.current_thread_backend = old_backend
+ end
+
+ def to_tag(key, value, options)
+ type_name = options.delete(:type)
+ merged_options = options.merge(root: key, skip_instruct: true)
+
+ if value.is_a?(::Method) || value.is_a?(::Proc)
+ if value.arity == 1
+ value.call(merged_options)
+ else
+ value.call(merged_options, key.to_s.singularize)
+ end
+ elsif value.respond_to?(:to_xml)
+ value.to_xml(merged_options)
+ else
+ type_name ||= TYPE_NAMES[value.class.name]
+ type_name ||= value.class.name if value && !value.respond_to?(:to_str)
+ type_name = type_name.to_s if type_name
+ type_name = "dateTime" if type_name == "datetime"
+
+ key = rename_key(key.to_s, options)
+
+ attributes = options[:skip_types] || type_name.nil? ? {} : { type: type_name }
+ attributes[:nil] = true if value.nil?
+
+ encoding = options[:encoding] || DEFAULT_ENCODINGS[type_name]
+ attributes[:encoding] = encoding if encoding
+
+ formatted_value = FORMATTING[type_name] && !value.nil? ?
+ FORMATTING[type_name].call(value) : value
+
+ options[:builder].tag!(key, formatted_value, attributes)
+ end
+ end
+
+ def rename_key(key, options = {})
+ camelize = options[:camelize]
+ dasherize = !options.has_key?(:dasherize) || options[:dasherize]
+ if camelize
+ key = true == camelize ? key.camelize : key.camelize(camelize)
+ end
+ key = _dasherize(key) if dasherize
+ key
+ end
+
+ private
+
+ def _dasherize(key)
+ # $2 must be a non-greedy regex for this to work
+ left, middle, right = /\A(_*)(.*?)(_*)\Z/.match(key.strip)[1, 3]
+ "#{left}#{middle.tr('_ ', '--')}#{right}"
+ end
+
+ # TODO: Add support for other encodings
+ def _parse_binary(bin, entity)
+ case entity["encoding"]
+ when "base64"
+ ::Base64.decode64(bin)
+ else
+ bin
+ end
+ end
+
+ def _parse_file(file, entity)
+ f = StringIO.new(::Base64.decode64(file))
+ f.extend(FileLike)
+ f.original_filename = entity["name"]
+ f.content_type = entity["content_type"]
+ f
+ end
+
+ def current_thread_backend
+ Thread.current[:xml_mini_backend]
+ end
+
+ def current_thread_backend=(name)
+ Thread.current[:xml_mini_backend] = name && cast_backend_name_to_module(name)
+ end
+
+ def cast_backend_name_to_module(name)
+ if name.is_a?(Module)
+ name
+ else
+ require "active_support/xml_mini/#{name.downcase}"
+ ActiveSupport.const_get("XmlMini_#{name}")
+ end
+ end
+ end
+
+ XmlMini.backend = "REXML"
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/jdom.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/jdom.rb
new file mode 100644
index 0000000..7f94a64
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/jdom.rb
@@ -0,0 +1,183 @@
+# frozen_string_literal: true
+
+raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM.include?("java")
+
+require "jruby"
+include Java
+
+require "active_support/core_ext/object/blank"
+
+java_import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder
+java_import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory
+java_import java.io.StringReader unless defined? StringReader
+java_import org.xml.sax.InputSource unless defined? InputSource
+java_import org.xml.sax.Attributes unless defined? Attributes
+java_import org.w3c.dom.Node unless defined? Node
+
+module ActiveSupport
+ module XmlMini_JDOM #:nodoc:
+ extend self
+
+ CONTENT_KEY = "__content__".freeze
+
+ NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE
+ DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE
+ PROCESSING_INSTRUCTION_NODE TEXT_NODE}
+
+ node_type_map = {}
+ NODE_TYPE_NAMES.each { |type| node_type_map[Node.send(type)] = type }
+
+ # Parse an XML Document string or IO into a simple hash using Java's jdom.
+ # data::
+ # XML Document string or IO to parse
+ def parse(data)
+ if data.respond_to?(:read)
+ data = data.read
+ end
+
+ if data.blank?
+ {}
+ else
+ @dbf = DocumentBuilderFactory.new_instance
+ # secure processing of java xml
+ # https://archive.is/9xcQQ
+ @dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-general-entities", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
+ @dbf.setFeature(javax.xml.XMLConstants::FEATURE_SECURE_PROCESSING, true)
+ xml_string_reader = StringReader.new(data)
+ xml_input_source = InputSource.new(xml_string_reader)
+ doc = @dbf.new_document_builder.parse(xml_input_source)
+ merge_element!({ CONTENT_KEY => "" }, doc.document_element, XmlMini.depth)
+ end
+ end
+
+ private
+
+ # Convert an XML element and merge into the hash
+ #
+ # hash::
+ # Hash to merge the converted element into.
+ # element::
+ # XML element to merge into hash
+ def merge_element!(hash, element, depth)
+ raise "Document too deep!" if depth == 0
+ delete_empty(hash)
+ merge!(hash, element.tag_name, collapse(element, depth))
+ end
+
+ def delete_empty(hash)
+ hash.delete(CONTENT_KEY) if hash[CONTENT_KEY] == ""
+ end
+
+ # Actually converts an XML document element into a data structure.
+ #
+ # element::
+ # The document element to be collapsed.
+ def collapse(element, depth)
+ hash = get_attributes(element)
+
+ child_nodes = element.child_nodes
+ if child_nodes.length > 0
+ (0...child_nodes.length).each do |i|
+ child = child_nodes.item(i)
+ merge_element!(hash, child, depth - 1) unless child.node_type == Node.TEXT_NODE
+ end
+ merge_texts!(hash, element) unless empty_content?(element)
+ hash
+ else
+ merge_texts!(hash, element)
+ end
+ end
+
+ # Merge all the texts of an element into the hash
+ #
+ # hash::
+ # Hash to add the converted element to.
+ # element::
+ # XML element whose texts are to me merged into the hash
+ def merge_texts!(hash, element)
+ delete_empty(hash)
+ text_children = texts(element)
+ if text_children.join.empty?
+ hash
+ else
+ # must use value to prevent double-escaping
+ merge!(hash, CONTENT_KEY, text_children.join)
+ end
+ end
+
+ # Adds a new key/value pair to an existing Hash. If the key to be added
+ # already exists and the existing value associated with key is not
+ # an Array, it will be wrapped in an Array. Then the new value is
+ # appended to that Array.
+ #
+ # hash::
+ # Hash to add key/value pair to.
+ # key::
+ # Key to be added.
+ # value::
+ # Value to be associated with key.
+ def merge!(hash, key, value)
+ if hash.has_key?(key)
+ if hash[key].instance_of?(Array)
+ hash[key] << value
+ else
+ hash[key] = [hash[key], value]
+ end
+ elsif value.instance_of?(Array)
+ hash[key] = [value]
+ else
+ hash[key] = value
+ end
+ hash
+ end
+
+ # Converts the attributes array of an XML element into a hash.
+ # Returns an empty Hash if node has no attributes.
+ #
+ # element::
+ # XML element to extract attributes from.
+ def get_attributes(element)
+ attribute_hash = {}
+ attributes = element.attributes
+ (0...attributes.length).each do |i|
+ attribute_hash[CONTENT_KEY] ||= ""
+ attribute_hash[attributes.item(i).name] = attributes.item(i).value
+ end
+ attribute_hash
+ end
+
+ # Determines if a document element has text content
+ #
+ # element::
+ # XML element to be checked.
+ def texts(element)
+ texts = []
+ child_nodes = element.child_nodes
+ (0...child_nodes.length).each do |i|
+ item = child_nodes.item(i)
+ if item.node_type == Node.TEXT_NODE
+ texts << item.get_data
+ end
+ end
+ texts
+ end
+
+ # Determines if a document element has text content
+ #
+ # element::
+ # XML element to be checked.
+ def empty_content?(element)
+ text = "".dup
+ child_nodes = element.child_nodes
+ (0...child_nodes.length).each do |i|
+ item = child_nodes.item(i)
+ if item.node_type == Node.TEXT_NODE
+ text << item.get_data.strip
+ end
+ end
+ text.strip.length == 0
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/libxml.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/libxml.rb
new file mode 100644
index 0000000..0b000fe
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/libxml.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require "libxml"
+require "active_support/core_ext/object/blank"
+require "stringio"
+
+module ActiveSupport
+ module XmlMini_LibXML #:nodoc:
+ extend self
+
+ # Parse an XML Document string or IO into a simple hash using libxml.
+ # data::
+ # XML Document string or IO to parse
+ def parse(data)
+ if !data.respond_to?(:read)
+ data = StringIO.new(data || "")
+ end
+
+ if data.eof?
+ {}
+ else
+ LibXML::XML::Parser.io(data).parse.to_hash
+ end
+ end
+ end
+end
+
+module LibXML #:nodoc:
+ module Conversions #:nodoc:
+ module Document #:nodoc:
+ def to_hash
+ root.to_hash
+ end
+ end
+
+ module Node #:nodoc:
+ CONTENT_ROOT = "__content__".freeze
+
+ # Convert XML document to hash.
+ #
+ # hash::
+ # Hash to merge the converted element into.
+ def to_hash(hash = {})
+ node_hash = {}
+
+ # Insert node hash into parent hash correctly.
+ case hash[name]
+ when Array then hash[name] << node_hash
+ when Hash then hash[name] = [hash[name], node_hash]
+ when nil then hash[name] = node_hash
+ end
+
+ # Handle child elements
+ each_child do |c|
+ if c.element?
+ c.to_hash(node_hash)
+ elsif c.text? || c.cdata?
+ node_hash[CONTENT_ROOT] ||= "".dup
+ node_hash[CONTENT_ROOT] << c.content
+ end
+ end
+
+ # Remove content node if it is blank
+ if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank?
+ node_hash.delete(CONTENT_ROOT)
+ end
+
+ # Handle attributes
+ each_attr { |a| node_hash[a.name] = a.value }
+
+ hash
+ end
+ end
+ end
+end
+
+# :enddoc:
+
+LibXML::XML::Document.include(LibXML::Conversions::Document)
+LibXML::XML::Node.include(LibXML::Conversions::Node)
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/libxmlsax.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/libxmlsax.rb
new file mode 100644
index 0000000..dcf16e6
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/libxmlsax.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require "libxml"
+require "active_support/core_ext/object/blank"
+require "stringio"
+
+module ActiveSupport
+ module XmlMini_LibXMLSAX #:nodoc:
+ extend self
+
+ # Class that will build the hash while the XML document
+ # is being parsed using SAX events.
+ class HashBuilder
+ include LibXML::XML::SaxParser::Callbacks
+
+ CONTENT_KEY = "__content__".freeze
+ HASH_SIZE_KEY = "__hash_size__".freeze
+
+ attr_reader :hash
+
+ def current_hash
+ @hash_stack.last
+ end
+
+ def on_start_document
+ @hash = { CONTENT_KEY => "".dup }
+ @hash_stack = [@hash]
+ end
+
+ def on_end_document
+ @hash = @hash_stack.pop
+ @hash.delete(CONTENT_KEY)
+ end
+
+ def on_start_element(name, attrs = {})
+ new_hash = { CONTENT_KEY => "".dup }.merge!(attrs)
+ new_hash[HASH_SIZE_KEY] = new_hash.size + 1
+
+ case current_hash[name]
+ when Array then current_hash[name] << new_hash
+ when Hash then current_hash[name] = [current_hash[name], new_hash]
+ when nil then current_hash[name] = new_hash
+ end
+
+ @hash_stack.push(new_hash)
+ end
+
+ def on_end_element(name)
+ if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == ""
+ current_hash.delete(CONTENT_KEY)
+ end
+ @hash_stack.pop
+ end
+
+ def on_characters(string)
+ current_hash[CONTENT_KEY] << string
+ end
+
+ alias_method :on_cdata_block, :on_characters
+ end
+
+ attr_accessor :document_class
+ self.document_class = HashBuilder
+
+ def parse(data)
+ if !data.respond_to?(:read)
+ data = StringIO.new(data || "")
+ end
+
+ if data.eof?
+ {}
+ else
+ LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER)
+ parser = LibXML::XML::SaxParser.io(data)
+ document = document_class.new
+
+ parser.callbacks = document
+ parser.parse
+ document.hash
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/nokogiri.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/nokogiri.rb
new file mode 100644
index 0000000..5ee6fc8
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/nokogiri.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+begin
+ require "nokogiri"
+rescue LoadError => e
+ $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install"
+ raise e
+end
+require "active_support/core_ext/object/blank"
+require "stringio"
+
+module ActiveSupport
+ module XmlMini_Nokogiri #:nodoc:
+ extend self
+
+ # Parse an XML Document string or IO into a simple hash using libxml / nokogiri.
+ # data::
+ # XML Document string or IO to parse
+ def parse(data)
+ if !data.respond_to?(:read)
+ data = StringIO.new(data || "")
+ end
+
+ if data.eof?
+ {}
+ else
+ doc = Nokogiri::XML(data)
+ raise doc.errors.first if doc.errors.length > 0
+ doc.to_hash
+ end
+ end
+
+ module Conversions #:nodoc:
+ module Document #:nodoc:
+ def to_hash
+ root.to_hash
+ end
+ end
+
+ module Node #:nodoc:
+ CONTENT_ROOT = "__content__".freeze
+
+ # Convert XML document to hash.
+ #
+ # hash::
+ # Hash to merge the converted element into.
+ def to_hash(hash = {})
+ node_hash = {}
+
+ # Insert node hash into parent hash correctly.
+ case hash[name]
+ when Array then hash[name] << node_hash
+ when Hash then hash[name] = [hash[name], node_hash]
+ when nil then hash[name] = node_hash
+ end
+
+ # Handle child elements
+ children.each do |c|
+ if c.element?
+ c.to_hash(node_hash)
+ elsif c.text? || c.cdata?
+ node_hash[CONTENT_ROOT] ||= "".dup
+ node_hash[CONTENT_ROOT] << c.content
+ end
+ end
+
+ # Remove content node if it is blank and there are child tags
+ if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank?
+ node_hash.delete(CONTENT_ROOT)
+ end
+
+ # Handle attributes
+ attribute_nodes.each { |a| node_hash[a.node_name] = a.value }
+
+ hash
+ end
+ end
+ end
+
+ Nokogiri::XML::Document.include(Conversions::Document)
+ Nokogiri::XML::Node.include(Conversions::Node)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/nokogirisax.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/nokogirisax.rb
new file mode 100644
index 0000000..b01ed00
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/nokogirisax.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+begin
+ require "nokogiri"
+rescue LoadError => e
+ $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install"
+ raise e
+end
+require "active_support/core_ext/object/blank"
+require "stringio"
+
+module ActiveSupport
+ module XmlMini_NokogiriSAX #:nodoc:
+ extend self
+
+ # Class that will build the hash while the XML document
+ # is being parsed using SAX events.
+ class HashBuilder < Nokogiri::XML::SAX::Document
+ CONTENT_KEY = "__content__".freeze
+ HASH_SIZE_KEY = "__hash_size__".freeze
+
+ attr_reader :hash
+
+ def current_hash
+ @hash_stack.last
+ end
+
+ def start_document
+ @hash = {}
+ @hash_stack = [@hash]
+ end
+
+ def end_document
+ raise "Parse stack not empty!" if @hash_stack.size > 1
+ end
+
+ def error(error_message)
+ raise error_message
+ end
+
+ def start_element(name, attrs = [])
+ new_hash = { CONTENT_KEY => "".dup }.merge!(Hash[attrs])
+ new_hash[HASH_SIZE_KEY] = new_hash.size + 1
+
+ case current_hash[name]
+ when Array then current_hash[name] << new_hash
+ when Hash then current_hash[name] = [current_hash[name], new_hash]
+ when nil then current_hash[name] = new_hash
+ end
+
+ @hash_stack.push(new_hash)
+ end
+
+ def end_element(name)
+ if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == ""
+ current_hash.delete(CONTENT_KEY)
+ end
+ @hash_stack.pop
+ end
+
+ def characters(string)
+ current_hash[CONTENT_KEY] << string
+ end
+
+ alias_method :cdata_block, :characters
+ end
+
+ attr_accessor :document_class
+ self.document_class = HashBuilder
+
+ def parse(data)
+ if !data.respond_to?(:read)
+ data = StringIO.new(data || "")
+ end
+
+ if data.eof?
+ {}
+ else
+ document = document_class.new
+ parser = Nokogiri::XML::SAX::Parser.new(document)
+ parser.parse(data)
+ document.hash
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/rexml.rb b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/rexml.rb
new file mode 100644
index 0000000..32458d5
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.4.4/lib/active_support/xml_mini/rexml.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/kernel/reporting"
+require "active_support/core_ext/object/blank"
+require "stringio"
+
+module ActiveSupport
+ module XmlMini_REXML #:nodoc:
+ extend self
+
+ CONTENT_KEY = "__content__".freeze
+
+ # Parse an XML Document string or IO into a simple hash.
+ #
+ # Same as XmlSimple::xml_in but doesn't shoot itself in the foot,
+ # and uses the defaults from Active Support.
+ #
+ # data::
+ # XML Document string or IO to parse
+ def parse(data)
+ if !data.respond_to?(:read)
+ data = StringIO.new(data || "")
+ end
+
+ if data.eof?
+ {}
+ else
+ silence_warnings { require "rexml/document" } unless defined?(REXML::Document)
+ doc = REXML::Document.new(data)
+
+ if doc.root
+ merge_element!({}, doc.root, XmlMini.depth)
+ else
+ raise REXML::ParseException,
+ "The document #{doc.to_s.inspect} does not have a valid root"
+ end
+ end
+ end
+
+ private
+ # Convert an XML element and merge into the hash
+ #
+ # hash::
+ # Hash to merge the converted element into.
+ # element::
+ # XML element to merge into hash
+ def merge_element!(hash, element, depth)
+ raise REXML::ParseException, "The document is too deep" if depth == 0
+ merge!(hash, element.name, collapse(element, depth))
+ end
+
+ # Actually converts an XML document element into a data structure.
+ #
+ # element::
+ # The document element to be collapsed.
+ def collapse(element, depth)
+ hash = get_attributes(element)
+
+ if element.has_elements?
+ element.each_element { |child| merge_element!(hash, child, depth - 1) }
+ merge_texts!(hash, element) unless empty_content?(element)
+ hash
+ else
+ merge_texts!(hash, element)
+ end
+ end
+
+ # Merge all the texts of an element into the hash
+ #
+ # hash::
+ # Hash to add the converted element to.
+ # element::
+ # XML element whose texts are to me merged into the hash
+ def merge_texts!(hash, element)
+ unless element.has_text?
+ hash
+ else
+ # must use value to prevent double-escaping
+ texts = "".dup
+ element.texts.each { |t| texts << t.value }
+ merge!(hash, CONTENT_KEY, texts)
+ end
+ end
+
+ # Adds a new key/value pair to an existing Hash. If the key to be added
+ # already exists and the existing value associated with key is not
+ # an Array, it will be wrapped in an Array. Then the new value is
+ # appended to that Array.
+ #
+ # hash::
+ # Hash to add key/value pair to.
+ # key::
+ # Key to be added.
+ # value::
+ # Value to be associated with key.
+ def merge!(hash, key, value)
+ if hash.has_key?(key)
+ if hash[key].instance_of?(Array)
+ hash[key] << value
+ else
+ hash[key] = [hash[key], value]
+ end
+ elsif value.instance_of?(Array)
+ hash[key] = [value]
+ else
+ hash[key] = value
+ end
+ hash
+ end
+
+ # Converts the attributes array of an XML element into a hash.
+ # Returns an empty Hash if node has no attributes.
+ #
+ # element::
+ # XML element to extract attributes from.
+ def get_attributes(element)
+ attributes = {}
+ element.attributes.each { |n, v| attributes[n] = v }
+ attributes
+ end
+
+ # Determines if a document element has text content
+ #
+ # element::
+ # XML element to be checked.
+ def empty_content?(element)
+ element.texts.join.blank?
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/CHANGELOG.md b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/CHANGELOG.md
new file mode 100644
index 0000000..4a9f866
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/CHANGELOG.md
@@ -0,0 +1,246 @@
+# Addressable 2.8.0
+- fixes ReDoS vulnerability in Addressable::Template#match
+- no longer replaces `+` with spaces in queries for non-http(s) schemes
+- fixed encoding ipv6 literals
+- the `:compacted` flag for `normalized_query` now dedupes parameters
+- fix broken `escape_component` alias
+- dropping support for Ruby 2.0 and 2.1
+- adding Ruby 3.0 compatibility for development tasks
+- drop support for `rack-mount` and remove Addressable::Template#generate
+- performance improvements
+- switch CI/CD to GitHub Actions
+
+# Addressable 2.7.0
+- added `:compacted` flag to `normalized_query`
+- `heuristic_parse` handles `mailto:` more intuitively
+- dropped explicit support for JRuby 9.0.5.0
+- compatibility w/ public_suffix 4.x
+- performance improvements
+
+# Addressable 2.6.0
+- added `tld=` method to allow assignment to the public suffix
+- most `heuristic_parse` patterns are now case-insensitive
+- `heuristic_parse` handles more `file://` URI variations
+- fixes bug in `heuristic_parse` when uri starts with digit
+- fixes bug in `request_uri=` with query strings
+- fixes template issues with `nil` and `?` operator
+- `frozen_string_literal` pragmas added
+- minor performance improvements in regexps
+- fixes to eliminate warnings
+
+# Addressable 2.5.2
+- better support for frozen string literals
+- fixed bug w/ uppercase characters in scheme
+- IDNA errors w/ emoji URLs
+- compatibility w/ public_suffix 3.x
+
+# Addressable 2.5.1
+- allow unicode normalization to be disabled for URI Template expansion
+- removed duplicate test
+
+# Addressable 2.5.0
+- dropping support for Ruby 1.9
+- adding support for Ruby 2.4 preview
+- add support for public suffixes and tld; first runtime dependency
+- hostname escaping should match RFC; underscores in hostnames no longer escaped
+- paths beginning with // and missing an authority are now considered invalid
+- validation now also takes place after setting a path
+- handle backslashes in authority more like a browser for `heuristic_parse`
+- unescaped backslashes in host now raise an `InvalidURIError`
+- `merge!`, `join!`, `omit!` and `normalize!` don't disable deferred validation
+- `heuristic_parse` now trims whitespace before parsing
+- host parts longer than 63 bytes will be ignored and not passed to libidn
+- normalized values always encoded as UTF-8
+
+# Addressable 2.4.0
+- support for 1.8.x dropped
+- double quotes in a host now raises an error
+- newlines in host will no longer get unescaped during normalization
+- stricter handling of bogus scheme values
+- stricter handling of encoded port values
+- calling `require 'addressable'` will now load both the URI and Template files
+- assigning to the `hostname` component with an `IPAddr` object is now supported
+- assigning to the `origin` component is now supported
+- fixed minor bug where an exception would be thrown for a missing ACE suffix
+- better partial expansion of URI templates
+
+# Addressable 2.3.8
+- fix warnings
+- update dependency gems
+- support for 1.8.x officially deprecated
+
+# Addressable 2.3.7
+- fix scenario in which invalid URIs don't get an exception until inspected
+- handle hostnames with two adjacent periods correctly
+- upgrade of RSpec
+
+# Addressable 2.3.6
+- normalization drops empty query string
+- better handling in template extract for missing values
+- template modifier for `'?'` now treated as optional
+- fixed issue where character class parameters were modified
+- templates can now be tested for equality
+- added `:sorted` option to normalization of query strings
+- fixed issue with normalization of hosts given in `'example.com.'` form
+
+# Addressable 2.3.5
+- added Addressable::URI#empty? method
+- Addressable::URI#hostname methods now strip square brackets from IPv6 hosts
+- compatibility with Net::HTTP in Ruby 2.0.0
+- Addressable::URI#route_from should always give relative URIs
+
+# Addressable 2.3.4
+- fixed issue with encoding altering its inputs
+- query string normalization now leaves ';' characters alone
+- FakeFS is detected before attempting to load unicode tables
+- additional testing to ensure frozen objects don't cause problems
+
+# Addressable 2.3.3
+- fixed issue with converting common primitives during template expansion
+- fixed port encoding issue
+- removed a few warnings
+- normalize should now ignore %2B in query strings
+- the IDNA logic should now be handled by libidn in Ruby 1.9
+- no template match should now result in nil instead of an empty MatchData
+- added license information to gemspec
+
+# Addressable 2.3.2
+- added Addressable::URI#default_port method
+- fixed issue with Marshalling Unicode data on Windows
+- improved heuristic parsing to better handle IPv4 addresses
+
+# Addressable 2.3.1
+- fixed missing unicode data file
+
+# Addressable 2.3.0
+- updated Addressable::Template to use RFC 6570, level 4
+- fixed compatibility problems with some versions of Ruby
+- moved unicode tables into a data file for performance reasons
+- removing support for multiple query value notations
+
+# Addressable 2.2.8
+- fixed issues with dot segment removal code
+- form encoding can now handle multiple values per key
+- updated development environment
+
+# Addressable 2.2.7
+- fixed issues related to Addressable::URI#query_values=
+- the Addressable::URI.parse method is now polymorphic
+
+# Addressable 2.2.6
+- changed the way ambiguous paths are handled
+- fixed bug with frozen URIs
+- https supported in heuristic parsing
+
+# Addressable 2.2.5
+- 'parsing' a pre-parsed URI object is now a dup operation
+- introduced conditional support for libidn
+- fixed normalization issue on ampersands in query strings
+- added additional tests around handling of query strings
+
+# Addressable 2.2.4
+- added origin support from draft-ietf-websec-origin-00
+- resolved issue with attempting to navigate below root
+- fixed bug with string splitting in query strings
+
+# Addressable 2.2.3
+- added :flat_array notation for query strings
+
+# Addressable 2.2.2
+- fixed issue with percent escaping of '+' character in query strings
+
+# Addressable 2.2.1
+- added support for application/x-www-form-urlencoded.
+
+# Addressable 2.2.0
+- added site methods
+- improved documentation
+
+# Addressable 2.1.2
+- added HTTP request URI methods
+- better handling of Windows file paths
+- validation_deferred boolean replaced with defer_validation block
+- normalization of percent-encoded paths should now be correct
+- fixed issue with constructing URIs with relative paths
+- fixed warnings
+
+# Addressable 2.1.1
+- more type checking changes
+- fixed issue with unicode normalization
+- added method to find template defaults
+- symbolic keys are now allowed in template mappings
+- numeric values and symbolic values are now allowed in template mappings
+
+# Addressable 2.1.0
+- refactored URI template support out into its own class
+- removed extract method due to being useless and unreliable
+- removed Addressable::URI.expand_template
+- removed Addressable::URI#extract_mapping
+- added partial template expansion
+- fixed minor bugs in the parse and heuristic_parse methods
+- fixed incompatibility with Ruby 1.9.1
+- fixed bottleneck in Addressable::URI#hash and Addressable::URI#to_s
+- fixed unicode normalization exception
+- updated query_values methods to better handle subscript notation
+- worked around issue with freezing URIs
+- improved specs
+
+# Addressable 2.0.2
+- fixed issue with URI template expansion
+- fixed issue with percent escaping characters 0-15
+
+# Addressable 2.0.1
+- fixed issue with query string assignment
+- fixed issue with improperly encoded components
+
+# Addressable 2.0.0
+- the initialize method now takes an options hash as its only parameter
+- added query_values method to URI class
+- completely replaced IDNA implementation with pure Ruby
+- renamed Addressable::ADDRESSABLE_VERSION to Addressable::VERSION
+- completely reworked the Rakefile
+- changed the behavior of the port method significantly
+- Addressable::URI.encode_segment, Addressable::URI.unencode_segment renamed
+- documentation is now in YARD format
+- more rigorous type checking
+- to_str method implemented, implicit conversion to Strings now allowed
+- Addressable::URI#omit method added, Addressable::URI#merge method replaced
+- updated URI Template code to match v 03 of the draft spec
+- added a bunch of new specifications
+
+# Addressable 1.0.4
+- switched to using RSpec's pending system for specs that rely on IDN
+- fixed issue with creating URIs with paths that are not prefixed with '/'
+
+# Addressable 1.0.3
+- implemented a hash method
+
+# Addressable 1.0.2
+- fixed minor bug with the extract_mapping method
+
+# Addressable 1.0.1
+- fixed minor bug with the extract_mapping method
+
+# Addressable 1.0.0
+- heuristic parse method added
+- parsing is slightly more strict
+- replaced to_h with to_hash
+- fixed routing methods
+- improved specifications
+- improved heckle rake task
+- no surviving heckle mutations
+
+# Addressable 0.1.2
+- improved normalization
+- fixed bug in joining algorithm
+- updated specifications
+
+# Addressable 0.1.1
+- updated documentation
+- added URI Template variable extraction
+
+# Addressable 0.1.0
+- initial release
+- implementation based on RFC 3986, 3987
+- support for IRIs via libidn
+- support for the URI Template draft spec
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/Gemfile b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/Gemfile
new file mode 100644
index 0000000..2684c15
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/Gemfile
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+source 'https://rubygems.org'
+
+gemspec(path: __FILE__ == "(eval)" ? ".." : ".")
+
+group :test do
+ gem 'rspec', '~> 3.8'
+ gem 'rspec-its', '~> 1.3'
+end
+
+group :coverage do
+ gem "coveralls", "> 0.7", require: false, platforms: :mri
+ gem "simplecov", require: false
+end
+
+group :development do
+ gem 'launchy', '~> 2.4', '>= 2.4.3'
+ gem 'redcarpet', :platform => :mri_19
+ gem 'yard'
+end
+
+group :test, :development do
+ gem 'memory_profiler'
+ gem "rake", ">= 12.3.3"
+end
+
+gem "idn-ruby", platform: :mri
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/LICENSE.txt b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/LICENSE.txt
new file mode 100644
index 0000000..ef51da2
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/README.md b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/README.md
new file mode 100644
index 0000000..9892f61
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/README.md
@@ -0,0 +1,121 @@
+# Addressable
+
+
+
+[![Gem Version](https://img.shields.io/gem/dt/addressable.svg)][gem]
+[![Build Status](https://github.com/sporkmonger/addressable/workflows/CI/badge.svg)][actions]
+[![Test Coverage Status](https://img.shields.io/coveralls/sporkmonger/addressable.svg)][coveralls]
+[![Documentation Coverage Status](https://inch-ci.org/github/sporkmonger/addressable.svg?branch=master)][inch]
+
+[gem]: https://rubygems.org/gems/addressable
+[actions]: https://github.com/sporkmonger/addressable/actions
+[coveralls]: https://coveralls.io/r/sporkmonger/addressable
+[inch]: https://inch-ci.org/github/sporkmonger/addressable
+
+# Description
+
+Addressable is an alternative implementation to the URI implementation
+that is part of Ruby's standard library. It is flexible, offers heuristic
+parsing, and additionally provides extensive support for IRIs and URI templates.
+
+Addressable closely conforms to RFC 3986, RFC 3987, and RFC 6570 (level 4).
+
+# Reference
+
+- {Addressable::URI}
+- {Addressable::Template}
+
+# Example usage
+
+```ruby
+require "addressable/uri"
+
+uri = Addressable::URI.parse("http://example.com/path/to/resource/")
+uri.scheme
+#=> "http"
+uri.host
+#=> "example.com"
+uri.path
+#=> "/path/to/resource/"
+
+uri = Addressable::URI.parse("http://www.詹姆斯.com/")
+uri.normalize
+#=> #
+```
+
+
+# URI Templates
+
+For more details, see [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570.txt).
+
+
+```ruby
+
+require "addressable/template"
+
+template = Addressable::Template.new("http://example.com/{?query*}")
+template.expand({
+ "query" => {
+ 'foo' => 'bar',
+ 'color' => 'red'
+ }
+})
+#=> #
+
+template = Addressable::Template.new("http://example.com/{?one,two,three}")
+template.partial_expand({"one" => "1", "three" => 3}).pattern
+#=> "http://example.com/?one=1{&two}&three=3"
+
+template = Addressable::Template.new(
+ "http://{host}{/segments*}/{?one,two,bogus}{#fragment}"
+)
+uri = Addressable::URI.parse(
+ "http://example.com/a/b/c/?one=1&two=2#foo"
+)
+template.extract(uri)
+#=>
+# {
+# "host" => "example.com",
+# "segments" => ["a", "b", "c"],
+# "one" => "1",
+# "two" => "2",
+# "fragment" => "foo"
+# }
+```
+
+# Install
+
+```console
+$ gem install addressable
+```
+
+You may optionally turn on native IDN support by installing libidn and the
+idn gem:
+
+```console
+$ sudo apt-get install libidn11-dev # Debian/Ubuntu
+$ brew install libidn # OS X
+$ gem install idn-ruby
+```
+
+# Semantic Versioning
+
+This project uses [Semantic Versioning](https://semver.org/). You can (and should) specify your
+dependency using a pessimistic version constraint covering the major and minor
+values:
+
+```ruby
+spec.add_dependency 'addressable', '~> 2.7'
+```
+
+If you need a specific bug fix, you can also specify minimum tiny versions
+without preventing updates to the latest minor release:
+
+```ruby
+spec.add_dependency 'addressable', '~> 2.3', '>= 2.3.7'
+```
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/Rakefile b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/Rakefile
new file mode 100644
index 0000000..b7e0ff3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/Rakefile
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'rubygems'
+require 'rake'
+
+require File.join(File.dirname(__FILE__), 'lib', 'addressable', 'version')
+
+PKG_DISPLAY_NAME = 'Addressable'
+PKG_NAME = PKG_DISPLAY_NAME.downcase
+PKG_VERSION = Addressable::VERSION::STRING
+PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
+
+RELEASE_NAME = "REL #{PKG_VERSION}"
+
+PKG_SUMMARY = "URI Implementation"
+PKG_DESCRIPTION = <<-TEXT
+Addressable is an alternative implementation to the URI implementation that is
+part of Ruby's standard library. It is flexible, offers heuristic parsing, and
+additionally provides extensive support for IRIs and URI templates.
+TEXT
+
+PKG_FILES = FileList[
+ "lib/**/*", "spec/**/*", "vendor/**/*", "data/**/*",
+ "tasks/**/*",
+ "[A-Z]*", "Rakefile"
+].exclude(/pkg/).exclude(/database\.yml/).
+ exclude(/Gemfile\.lock/).exclude(/[_\.]git$/)
+
+task :default => "spec"
+
+WINDOWS = (RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/) rescue false
+SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
+
+Dir['tasks/**/*.rake'].each { |rake| load rake }
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/addressable.gemspec b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/addressable.gemspec
new file mode 100644
index 0000000..12666a0
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/addressable.gemspec
@@ -0,0 +1,37 @@
+# -*- encoding: utf-8 -*-
+# stub: addressable 2.8.0 ruby lib
+
+Gem::Specification.new do |s|
+ s.name = "addressable".freeze
+ s.version = "2.8.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+ s.require_paths = ["lib".freeze]
+ s.authors = ["Bob Aman".freeze]
+ s.date = "2021-07-03"
+ s.description = "Addressable is an alternative implementation to the URI implementation that is\npart of Ruby's standard library. It is flexible, offers heuristic parsing, and\nadditionally provides extensive support for IRIs and URI templates.\n".freeze
+ s.email = "bob@sporkmonger.com".freeze
+ s.extra_rdoc_files = ["README.md".freeze]
+ s.files = ["CHANGELOG.md".freeze, "Gemfile".freeze, "LICENSE.txt".freeze, "README.md".freeze, "Rakefile".freeze, "addressable.gemspec".freeze, "data/unicode.data".freeze, "lib/addressable.rb".freeze, "lib/addressable/idna.rb".freeze, "lib/addressable/idna/native.rb".freeze, "lib/addressable/idna/pure.rb".freeze, "lib/addressable/template.rb".freeze, "lib/addressable/uri.rb".freeze, "lib/addressable/version.rb".freeze, "spec/addressable/idna_spec.rb".freeze, "spec/addressable/net_http_compat_spec.rb".freeze, "spec/addressable/security_spec.rb".freeze, "spec/addressable/template_spec.rb".freeze, "spec/addressable/uri_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tasks/clobber.rake".freeze, "tasks/gem.rake".freeze, "tasks/git.rake".freeze, "tasks/metrics.rake".freeze, "tasks/profile.rake".freeze, "tasks/rspec.rake".freeze, "tasks/yard.rake".freeze]
+ s.homepage = "https://github.com/sporkmonger/addressable".freeze
+ s.licenses = ["Apache-2.0".freeze]
+ s.rdoc_options = ["--main".freeze, "README.md".freeze]
+ s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze)
+ s.rubygems_version = "3.0.3".freeze
+ s.summary = "URI Implementation".freeze
+
+ if s.respond_to? :specification_version then
+ s.specification_version = 4
+
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q.freeze, [">= 2.0.2", "< 5.0"])
+ s.add_development_dependency(%q.freeze, [">= 1.0", "< 3.0"])
+ else
+ s.add_dependency(%q.freeze, [">= 2.0.2", "< 5.0"])
+ s.add_dependency(%q.freeze, [">= 1.0", "< 3.0"])
+ end
+ else
+ s.add_dependency(%q.freeze, [">= 2.0.2", "< 5.0"])
+ s.add_dependency(%q.freeze, [">= 1.0", "< 3.0"])
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/data/unicode.data b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/data/unicode.data
new file mode 100644
index 0000000..cdfc224
Binary files /dev/null and b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/data/unicode.data differ
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable.rb
new file mode 100644
index 0000000..b4e98b6
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+require 'addressable/uri'
+require 'addressable/template'
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/idna.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/idna.rb
new file mode 100644
index 0000000..e41c1f5
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/idna.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+# encoding:utf-8
+#--
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#++
+
+
+begin
+ require "addressable/idna/native"
+rescue LoadError
+ # libidn or the idn gem was not available, fall back on a pure-Ruby
+ # implementation...
+ require "addressable/idna/pure"
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/idna/native.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/idna/native.rb
new file mode 100644
index 0000000..84de8e8
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/idna/native.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+# encoding:utf-8
+#--
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#++
+
+
+require "idn"
+
+module Addressable
+ module IDNA
+ def self.punycode_encode(value)
+ IDN::Punycode.encode(value.to_s)
+ end
+
+ def self.punycode_decode(value)
+ IDN::Punycode.decode(value.to_s)
+ end
+
+ def self.unicode_normalize_kc(value)
+ IDN::Stringprep.nfkc_normalize(value.to_s)
+ end
+
+ def self.to_ascii(value)
+ value.to_s.split('.', -1).map do |segment|
+ if segment.size > 0 && segment.size < 64
+ IDN::Idna.toASCII(segment, IDN::Idna::ALLOW_UNASSIGNED)
+ elsif segment.size >= 64
+ segment
+ else
+ ''
+ end
+ end.join('.')
+ end
+
+ def self.to_unicode(value)
+ value.to_s.split('.', -1).map do |segment|
+ if segment.size > 0 && segment.size < 64
+ IDN::Idna.toUnicode(segment, IDN::Idna::ALLOW_UNASSIGNED)
+ elsif segment.size >= 64
+ segment
+ else
+ ''
+ end
+ end.join('.')
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/idna/pure.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/idna/pure.rb
new file mode 100644
index 0000000..7a0c1fd
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/idna/pure.rb
@@ -0,0 +1,678 @@
+# frozen_string_literal: true
+
+# encoding:utf-8
+#--
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#++
+
+
+module Addressable
+ module IDNA
+ # This module is loosely based on idn_actionmailer by Mick Staugaard,
+ # the unicode library by Yoshida Masato, and the punycode implementation
+ # by Kazuhiro Nishiyama. Most of the code was copied verbatim, but
+ # some reformatting was done, and some translation from C was done.
+ #
+ # Without their code to work from as a base, we'd all still be relying
+ # on the presence of libidn. Which nobody ever seems to have installed.
+ #
+ # Original sources:
+ # http://github.com/staugaard/idn_actionmailer
+ # http://www.yoshidam.net/Ruby.html#unicode
+ # http://rubyforge.org/frs/?group_id=2550
+
+
+ UNICODE_TABLE = File.expand_path(
+ File.join(File.dirname(__FILE__), '../../..', 'data/unicode.data')
+ )
+
+ ACE_PREFIX = "xn--"
+
+ UTF8_REGEX = /\A(?:
+ [\x09\x0A\x0D\x20-\x7E] # ASCII
+ | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
+ | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
+ | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
+ | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
+ | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
+ | [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
+ | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
+ )*\z/mnx
+
+ UTF8_REGEX_MULTIBYTE = /(?:
+ [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
+ | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
+ | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
+ | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
+ | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
+ | [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
+ | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
+ )/mnx
+
+ # :startdoc:
+
+ # Converts from a Unicode internationalized domain name to an ASCII
+ # domain name as described in RFC 3490.
+ def self.to_ascii(input)
+ input = input.to_s unless input.is_a?(String)
+ input = input.dup
+ if input.respond_to?(:force_encoding)
+ input.force_encoding(Encoding::ASCII_8BIT)
+ end
+ if input =~ UTF8_REGEX && input =~ UTF8_REGEX_MULTIBYTE
+ parts = unicode_downcase(input).split('.')
+ parts.map! do |part|
+ if part.respond_to?(:force_encoding)
+ part.force_encoding(Encoding::ASCII_8BIT)
+ end
+ if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE
+ ACE_PREFIX + punycode_encode(unicode_normalize_kc(part))
+ else
+ part
+ end
+ end
+ parts.join('.')
+ else
+ input
+ end
+ end
+
+ # Converts from an ASCII domain name to a Unicode internationalized
+ # domain name as described in RFC 3490.
+ def self.to_unicode(input)
+ input = input.to_s unless input.is_a?(String)
+ parts = input.split('.')
+ parts.map! do |part|
+ if part =~ /^#{ACE_PREFIX}(.+)/
+ begin
+ punycode_decode(part[/^#{ACE_PREFIX}(.+)/, 1])
+ rescue Addressable::IDNA::PunycodeBadInput
+ # toUnicode is explicitly defined as never-fails by the spec
+ part
+ end
+ else
+ part
+ end
+ end
+ output = parts.join('.')
+ if output.respond_to?(:force_encoding)
+ output.force_encoding(Encoding::UTF_8)
+ end
+ output
+ end
+
+ # Unicode normalization form KC.
+ def self.unicode_normalize_kc(input)
+ input = input.to_s unless input.is_a?(String)
+ unpacked = input.unpack("U*")
+ unpacked =
+ unicode_compose(unicode_sort_canonical(unicode_decompose(unpacked)))
+ return unpacked.pack("U*")
+ end
+
+ ##
+ # Unicode aware downcase method.
+ #
+ # @api private
+ # @param [String] input
+ # The input string.
+ # @return [String] The downcased result.
+ def self.unicode_downcase(input)
+ input = input.to_s unless input.is_a?(String)
+ unpacked = input.unpack("U*")
+ unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) }
+ return unpacked.pack("U*")
+ end
+ private_class_method :unicode_downcase
+
+ def self.unicode_compose(unpacked)
+ unpacked_result = []
+ length = unpacked.length
+
+ return unpacked if length == 0
+
+ starter = unpacked[0]
+ starter_cc = lookup_unicode_combining_class(starter)
+ starter_cc = 256 if starter_cc != 0
+ for i in 1...length
+ ch = unpacked[i]
+
+ if (starter_cc == 0 &&
+ (composite = unicode_compose_pair(starter, ch)) != nil)
+ starter = composite
+ else
+ unpacked_result << starter
+ starter = ch
+ end
+ end
+ unpacked_result << starter
+ return unpacked_result
+ end
+ private_class_method :unicode_compose
+
+ def self.unicode_compose_pair(ch_one, ch_two)
+ if ch_one >= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT &&
+ ch_two >= HANGUL_VBASE && ch_two < HANGUL_VBASE + HANGUL_VCOUNT
+ # Hangul L + V
+ return HANGUL_SBASE + (
+ (ch_one - HANGUL_LBASE) * HANGUL_VCOUNT + (ch_two - HANGUL_VBASE)
+ ) * HANGUL_TCOUNT
+ elsif ch_one >= HANGUL_SBASE &&
+ ch_one < HANGUL_SBASE + HANGUL_SCOUNT &&
+ (ch_one - HANGUL_SBASE) % HANGUL_TCOUNT == 0 &&
+ ch_two >= HANGUL_TBASE && ch_two < HANGUL_TBASE + HANGUL_TCOUNT
+ # Hangul LV + T
+ return ch_one + (ch_two - HANGUL_TBASE)
+ end
+
+ p = []
+
+ ucs4_to_utf8(ch_one, p)
+ ucs4_to_utf8(ch_two, p)
+
+ return lookup_unicode_composition(p)
+ end
+ private_class_method :unicode_compose_pair
+
+ def self.ucs4_to_utf8(char, buffer)
+ if char < 128
+ buffer << char
+ elsif char < 2048
+ buffer << (char >> 6 | 192)
+ buffer << (char & 63 | 128)
+ elsif char < 0x10000
+ buffer << (char >> 12 | 224)
+ buffer << (char >> 6 & 63 | 128)
+ buffer << (char & 63 | 128)
+ elsif char < 0x200000
+ buffer << (char >> 18 | 240)
+ buffer << (char >> 12 & 63 | 128)
+ buffer << (char >> 6 & 63 | 128)
+ buffer << (char & 63 | 128)
+ elsif char < 0x4000000
+ buffer << (char >> 24 | 248)
+ buffer << (char >> 18 & 63 | 128)
+ buffer << (char >> 12 & 63 | 128)
+ buffer << (char >> 6 & 63 | 128)
+ buffer << (char & 63 | 128)
+ elsif char < 0x80000000
+ buffer << (char >> 30 | 252)
+ buffer << (char >> 24 & 63 | 128)
+ buffer << (char >> 18 & 63 | 128)
+ buffer << (char >> 12 & 63 | 128)
+ buffer << (char >> 6 & 63 | 128)
+ buffer << (char & 63 | 128)
+ end
+ end
+ private_class_method :ucs4_to_utf8
+
+ def self.unicode_sort_canonical(unpacked)
+ unpacked = unpacked.dup
+ i = 1
+ length = unpacked.length
+
+ return unpacked if length < 2
+
+ while i < length
+ last = unpacked[i-1]
+ ch = unpacked[i]
+ last_cc = lookup_unicode_combining_class(last)
+ cc = lookup_unicode_combining_class(ch)
+ if cc != 0 && last_cc != 0 && last_cc > cc
+ unpacked[i] = last
+ unpacked[i-1] = ch
+ i -= 1 if i > 1
+ else
+ i += 1
+ end
+ end
+ return unpacked
+ end
+ private_class_method :unicode_sort_canonical
+
+ def self.unicode_decompose(unpacked)
+ unpacked_result = []
+ for cp in unpacked
+ if cp >= HANGUL_SBASE && cp < HANGUL_SBASE + HANGUL_SCOUNT
+ l, v, t = unicode_decompose_hangul(cp)
+ unpacked_result << l
+ unpacked_result << v if v
+ unpacked_result << t if t
+ else
+ dc = lookup_unicode_compatibility(cp)
+ unless dc
+ unpacked_result << cp
+ else
+ unpacked_result.concat(unicode_decompose(dc.unpack("U*")))
+ end
+ end
+ end
+ return unpacked_result
+ end
+ private_class_method :unicode_decompose
+
+ def self.unicode_decompose_hangul(codepoint)
+ sindex = codepoint - HANGUL_SBASE;
+ if sindex < 0 || sindex >= HANGUL_SCOUNT
+ l = codepoint
+ v = t = nil
+ return l, v, t
+ end
+ l = HANGUL_LBASE + sindex / HANGUL_NCOUNT
+ v = HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
+ t = HANGUL_TBASE + sindex % HANGUL_TCOUNT
+ if t == HANGUL_TBASE
+ t = nil
+ end
+ return l, v, t
+ end
+ private_class_method :unicode_decompose_hangul
+
+ def self.lookup_unicode_combining_class(codepoint)
+ codepoint_data = UNICODE_DATA[codepoint]
+ (codepoint_data ?
+ (codepoint_data[UNICODE_DATA_COMBINING_CLASS] || 0) :
+ 0)
+ end
+ private_class_method :lookup_unicode_combining_class
+
+ def self.lookup_unicode_compatibility(codepoint)
+ codepoint_data = UNICODE_DATA[codepoint]
+ (codepoint_data ?
+ codepoint_data[UNICODE_DATA_COMPATIBILITY] : nil)
+ end
+ private_class_method :lookup_unicode_compatibility
+
+ def self.lookup_unicode_lowercase(codepoint)
+ codepoint_data = UNICODE_DATA[codepoint]
+ (codepoint_data ?
+ (codepoint_data[UNICODE_DATA_LOWERCASE] || codepoint) :
+ codepoint)
+ end
+ private_class_method :lookup_unicode_lowercase
+
+ def self.lookup_unicode_composition(unpacked)
+ return COMPOSITION_TABLE[unpacked]
+ end
+ private_class_method :lookup_unicode_composition
+
+ HANGUL_SBASE = 0xac00
+ HANGUL_LBASE = 0x1100
+ HANGUL_LCOUNT = 19
+ HANGUL_VBASE = 0x1161
+ HANGUL_VCOUNT = 21
+ HANGUL_TBASE = 0x11a7
+ HANGUL_TCOUNT = 28
+ HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT # 588
+ HANGUL_SCOUNT = HANGUL_LCOUNT * HANGUL_NCOUNT # 11172
+
+ UNICODE_DATA_COMBINING_CLASS = 0
+ UNICODE_DATA_EXCLUSION = 1
+ UNICODE_DATA_CANONICAL = 2
+ UNICODE_DATA_COMPATIBILITY = 3
+ UNICODE_DATA_UPPERCASE = 4
+ UNICODE_DATA_LOWERCASE = 5
+ UNICODE_DATA_TITLECASE = 6
+
+ begin
+ if defined?(FakeFS)
+ fakefs_state = FakeFS.activated?
+ FakeFS.deactivate!
+ end
+ # This is a sparse Unicode table. Codepoints without entries are
+ # assumed to have the value: [0, 0, nil, nil, nil, nil, nil]
+ UNICODE_DATA = File.open(UNICODE_TABLE, "rb") do |file|
+ Marshal.load(file.read)
+ end
+ ensure
+ if defined?(FakeFS)
+ FakeFS.activate! if fakefs_state
+ end
+ end
+
+ COMPOSITION_TABLE = {}
+ UNICODE_DATA.each do |codepoint, data|
+ canonical = data[UNICODE_DATA_CANONICAL]
+ exclusion = data[UNICODE_DATA_EXCLUSION]
+
+ if canonical && exclusion == 0
+ COMPOSITION_TABLE[canonical.unpack("C*")] = codepoint
+ end
+ end
+
+ UNICODE_MAX_LENGTH = 256
+ ACE_MAX_LENGTH = 256
+
+ PUNYCODE_BASE = 36
+ PUNYCODE_TMIN = 1
+ PUNYCODE_TMAX = 26
+ PUNYCODE_SKEW = 38
+ PUNYCODE_DAMP = 700
+ PUNYCODE_INITIAL_BIAS = 72
+ PUNYCODE_INITIAL_N = 0x80
+ PUNYCODE_DELIMITER = 0x2D
+
+ PUNYCODE_MAXINT = 1 << 64
+
+ PUNYCODE_PRINT_ASCII =
+ "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
+ "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
+ " !\"\#$%&'()*+,-./" +
+ "0123456789:;<=>?" +
+ "@ABCDEFGHIJKLMNO" +
+ "PQRSTUVWXYZ[\\]^_" +
+ "`abcdefghijklmno" +
+ "pqrstuvwxyz{|}~\n"
+
+ # Input is invalid.
+ class PunycodeBadInput < StandardError; end
+ # Output would exceed the space provided.
+ class PunycodeBigOutput < StandardError; end
+ # Input needs wider integers to process.
+ class PunycodeOverflow < StandardError; end
+
+ def self.punycode_encode(unicode)
+ unicode = unicode.to_s unless unicode.is_a?(String)
+ input = unicode.unpack("U*")
+ output = [0] * (ACE_MAX_LENGTH + 1)
+ input_length = input.size
+ output_length = [ACE_MAX_LENGTH]
+
+ # Initialize the state
+ n = PUNYCODE_INITIAL_N
+ delta = out = 0
+ max_out = output_length[0]
+ bias = PUNYCODE_INITIAL_BIAS
+
+ # Handle the basic code points:
+ input_length.times do |j|
+ if punycode_basic?(input[j])
+ if max_out - out < 2
+ raise PunycodeBigOutput,
+ "Output would exceed the space provided."
+ end
+ output[out] = input[j]
+ out += 1
+ end
+ end
+
+ h = b = out
+
+ # h is the number of code points that have been handled, b is the
+ # number of basic code points, and out is the number of characters
+ # that have been output.
+
+ if b > 0
+ output[out] = PUNYCODE_DELIMITER
+ out += 1
+ end
+
+ # Main encoding loop:
+
+ while h < input_length
+ # All non-basic code points < n have been
+ # handled already. Find the next larger one:
+
+ m = PUNYCODE_MAXINT
+ input_length.times do |j|
+ m = input[j] if (n...m) === input[j]
+ end
+
+ # Increase delta enough to advance the decoder's
+ # state to , but guard against overflow:
+
+ if m - n > (PUNYCODE_MAXINT - delta) / (h + 1)
+ raise PunycodeOverflow, "Input needs wider integers to process."
+ end
+ delta += (m - n) * (h + 1)
+ n = m
+
+ input_length.times do |j|
+ # Punycode does not need to check whether input[j] is basic:
+ if input[j] < n
+ delta += 1
+ if delta == 0
+ raise PunycodeOverflow,
+ "Input needs wider integers to process."
+ end
+ end
+
+ if input[j] == n
+ # Represent delta as a generalized variable-length integer:
+
+ q = delta; k = PUNYCODE_BASE
+ while true
+ if out >= max_out
+ raise PunycodeBigOutput,
+ "Output would exceed the space provided."
+ end
+ t = (
+ if k <= bias
+ PUNYCODE_TMIN
+ elsif k >= bias + PUNYCODE_TMAX
+ PUNYCODE_TMAX
+ else
+ k - bias
+ end
+ )
+ break if q < t
+ output[out] =
+ punycode_encode_digit(t + (q - t) % (PUNYCODE_BASE - t))
+ out += 1
+ q = (q - t) / (PUNYCODE_BASE - t)
+ k += PUNYCODE_BASE
+ end
+
+ output[out] = punycode_encode_digit(q)
+ out += 1
+ bias = punycode_adapt(delta, h + 1, h == b)
+ delta = 0
+ h += 1
+ end
+ end
+
+ delta += 1
+ n += 1
+ end
+
+ output_length[0] = out
+
+ outlen = out
+ outlen.times do |j|
+ c = output[j]
+ unless c >= 0 && c <= 127
+ raise StandardError, "Invalid output char."
+ end
+ unless PUNYCODE_PRINT_ASCII[c]
+ raise PunycodeBadInput, "Input is invalid."
+ end
+ end
+
+ output[0..outlen].map { |x| x.chr }.join("").sub(/\0+\z/, "")
+ end
+ private_class_method :punycode_encode
+
+ def self.punycode_decode(punycode)
+ input = []
+ output = []
+
+ if ACE_MAX_LENGTH * 2 < punycode.size
+ raise PunycodeBigOutput, "Output would exceed the space provided."
+ end
+ punycode.each_byte do |c|
+ unless c >= 0 && c <= 127
+ raise PunycodeBadInput, "Input is invalid."
+ end
+ input.push(c)
+ end
+
+ input_length = input.length
+ output_length = [UNICODE_MAX_LENGTH]
+
+ # Initialize the state
+ n = PUNYCODE_INITIAL_N
+
+ out = i = 0
+ max_out = output_length[0]
+ bias = PUNYCODE_INITIAL_BIAS
+
+ # Handle the basic code points: Let b be the number of input code
+ # points before the last delimiter, or 0 if there is none, then
+ # copy the first b code points to the output.
+
+ b = 0
+ input_length.times do |j|
+ b = j if punycode_delimiter?(input[j])
+ end
+ if b > max_out
+ raise PunycodeBigOutput, "Output would exceed the space provided."
+ end
+
+ b.times do |j|
+ unless punycode_basic?(input[j])
+ raise PunycodeBadInput, "Input is invalid."
+ end
+ output[out] = input[j]
+ out+=1
+ end
+
+ # Main decoding loop: Start just after the last delimiter if any
+ # basic code points were copied; start at the beginning otherwise.
+
+ in_ = b > 0 ? b + 1 : 0
+ while in_ < input_length
+
+ # in_ is the index of the next character to be consumed, and
+ # out is the number of code points in the output array.
+
+ # Decode a generalized variable-length integer into delta,
+ # which gets added to i. The overflow checking is easier
+ # if we increase i as we go, then subtract off its starting
+ # value at the end to obtain delta.
+
+ oldi = i; w = 1; k = PUNYCODE_BASE
+ while true
+ if in_ >= input_length
+ raise PunycodeBadInput, "Input is invalid."
+ end
+ digit = punycode_decode_digit(input[in_])
+ in_+=1
+ if digit >= PUNYCODE_BASE
+ raise PunycodeBadInput, "Input is invalid."
+ end
+ if digit > (PUNYCODE_MAXINT - i) / w
+ raise PunycodeOverflow, "Input needs wider integers to process."
+ end
+ i += digit * w
+ t = (
+ if k <= bias
+ PUNYCODE_TMIN
+ elsif k >= bias + PUNYCODE_TMAX
+ PUNYCODE_TMAX
+ else
+ k - bias
+ end
+ )
+ break if digit < t
+ if w > PUNYCODE_MAXINT / (PUNYCODE_BASE - t)
+ raise PunycodeOverflow, "Input needs wider integers to process."
+ end
+ w *= PUNYCODE_BASE - t
+ k += PUNYCODE_BASE
+ end
+
+ bias = punycode_adapt(i - oldi, out + 1, oldi == 0)
+
+ # I was supposed to wrap around from out + 1 to 0,
+ # incrementing n each time, so we'll fix that now:
+
+ if i / (out + 1) > PUNYCODE_MAXINT - n
+ raise PunycodeOverflow, "Input needs wider integers to process."
+ end
+ n += i / (out + 1)
+ i %= out + 1
+
+ # Insert n at position i of the output:
+
+ # not needed for Punycode:
+ # raise PUNYCODE_INVALID_INPUT if decode_digit(n) <= base
+ if out >= max_out
+ raise PunycodeBigOutput, "Output would exceed the space provided."
+ end
+
+ #memmove(output + i + 1, output + i, (out - i) * sizeof *output)
+ output[i + 1, out - i] = output[i, out - i]
+ output[i] = n
+ i += 1
+
+ out += 1
+ end
+
+ output_length[0] = out
+
+ output.pack("U*")
+ end
+ private_class_method :punycode_decode
+
+ def self.punycode_basic?(codepoint)
+ codepoint < 0x80
+ end
+ private_class_method :punycode_basic?
+
+ def self.punycode_delimiter?(codepoint)
+ codepoint == PUNYCODE_DELIMITER
+ end
+ private_class_method :punycode_delimiter?
+
+ def self.punycode_encode_digit(d)
+ d + 22 + 75 * ((d < 26) ? 1 : 0)
+ end
+ private_class_method :punycode_encode_digit
+
+ # Returns the numeric value of a basic codepoint
+ # (for use in representing integers) in the range 0 to
+ # base - 1, or PUNYCODE_BASE if codepoint does not represent a value.
+ def self.punycode_decode_digit(codepoint)
+ if codepoint - 48 < 10
+ codepoint - 22
+ elsif codepoint - 65 < 26
+ codepoint - 65
+ elsif codepoint - 97 < 26
+ codepoint - 97
+ else
+ PUNYCODE_BASE
+ end
+ end
+ private_class_method :punycode_decode_digit
+
+ # Bias adaptation method
+ def self.punycode_adapt(delta, numpoints, firsttime)
+ delta = firsttime ? delta / PUNYCODE_DAMP : delta >> 1
+ # delta >> 1 is a faster way of doing delta / 2
+ delta += delta / numpoints
+ difference = PUNYCODE_BASE - PUNYCODE_TMIN
+
+ k = 0
+ while delta > (difference * PUNYCODE_TMAX) / 2
+ delta /= difference
+ k += PUNYCODE_BASE
+ end
+
+ k + (difference + 1) * delta / (delta + PUNYCODE_SKEW)
+ end
+ private_class_method :punycode_adapt
+ end
+ # :startdoc:
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/template.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/template.rb
new file mode 100644
index 0000000..45f6ae6
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/template.rb
@@ -0,0 +1,1031 @@
+# frozen_string_literal: true
+
+# encoding:utf-8
+#--
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#++
+
+
+require "addressable/version"
+require "addressable/uri"
+
+module Addressable
+ ##
+ # This is an implementation of a URI template based on
+ # RFC 6570 (http://tools.ietf.org/html/rfc6570).
+ class Template
+ # Constants used throughout the template code.
+ anything =
+ Addressable::URI::CharacterClasses::RESERVED +
+ Addressable::URI::CharacterClasses::UNRESERVED
+
+
+ variable_char_class =
+ Addressable::URI::CharacterClasses::ALPHA +
+ Addressable::URI::CharacterClasses::DIGIT + '_'
+
+ var_char =
+ "(?>(?:[#{variable_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)"
+ RESERVED =
+ "(?:[#{anything}]|%[a-fA-F0-9][a-fA-F0-9])"
+ UNRESERVED =
+ "(?:[#{
+ Addressable::URI::CharacterClasses::UNRESERVED
+ }]|%[a-fA-F0-9][a-fA-F0-9])"
+ variable =
+ "(?:#{var_char}(?:\\.?#{var_char})*)"
+ varspec =
+ "(?:(#{variable})(\\*|:\\d+)?)"
+ VARNAME =
+ /^#{variable}$/
+ VARSPEC =
+ /^#{varspec}$/
+ VARIABLE_LIST =
+ /^#{varspec}(?:,#{varspec})*$/
+ operator =
+ "+#./;?&=,!@|"
+ EXPRESSION =
+ /\{([#{operator}])?(#{varspec}(?:,#{varspec})*)\}/
+
+
+ LEADERS = {
+ '?' => '?',
+ '/' => '/',
+ '#' => '#',
+ '.' => '.',
+ ';' => ';',
+ '&' => '&'
+ }
+ JOINERS = {
+ '?' => '&',
+ '.' => '.',
+ ';' => ';',
+ '&' => '&',
+ '/' => '/'
+ }
+
+ ##
+ # Raised if an invalid template value is supplied.
+ class InvalidTemplateValueError < StandardError
+ end
+
+ ##
+ # Raised if an invalid template operator is used in a pattern.
+ class InvalidTemplateOperatorError < StandardError
+ end
+
+ ##
+ # Raised if an invalid template operator is used in a pattern.
+ class TemplateOperatorAbortedError < StandardError
+ end
+
+ ##
+ # This class represents the data that is extracted when a Template
+ # is matched against a URI.
+ class MatchData
+ ##
+ # Creates a new MatchData object.
+ # MatchData objects should never be instantiated directly.
+ #
+ # @param [Addressable::URI] uri
+ # The URI that the template was matched against.
+ def initialize(uri, template, mapping)
+ @uri = uri.dup.freeze
+ @template = template
+ @mapping = mapping.dup.freeze
+ end
+
+ ##
+ # @return [Addressable::URI]
+ # The URI that the Template was matched against.
+ attr_reader :uri
+
+ ##
+ # @return [Addressable::Template]
+ # The Template used for the match.
+ attr_reader :template
+
+ ##
+ # @return [Hash]
+ # The mapping that resulted from the match.
+ # Note that this mapping does not include keys or values for
+ # variables that appear in the Template, but are not present
+ # in the URI.
+ attr_reader :mapping
+
+ ##
+ # @return [Array]
+ # The list of variables that were present in the Template.
+ # Note that this list will include variables which do not appear
+ # in the mapping because they were not present in URI.
+ def variables
+ self.template.variables
+ end
+ alias_method :keys, :variables
+ alias_method :names, :variables
+
+ ##
+ # @return [Array]
+ # The list of values that were captured by the Template.
+ # Note that this list will include nils for any variables which
+ # were in the Template, but did not appear in the URI.
+ def values
+ @values ||= self.variables.inject([]) do |accu, key|
+ accu << self.mapping[key]
+ accu
+ end
+ end
+ alias_method :captures, :values
+
+ ##
+ # Accesses captured values by name or by index.
+ #
+ # @param [String, Symbol, Fixnum] key
+ # Capture index or name. Note that when accessing by with index
+ # of 0, the full URI will be returned. The intention is to mimic
+ # the ::MatchData#[] behavior.
+ #
+ # @param [#to_int, nil] len
+ # If provided, an array of values will be returend with the given
+ # parameter used as length.
+ #
+ # @return [Array, String, nil]
+ # The captured value corresponding to the index or name. If the
+ # value was not provided or the key is unknown, nil will be
+ # returned.
+ #
+ # If the second parameter is provided, an array of that length will
+ # be returned instead.
+ def [](key, len = nil)
+ if len
+ to_a[key, len]
+ elsif String === key or Symbol === key
+ mapping[key.to_s]
+ else
+ to_a[key]
+ end
+ end
+
+ ##
+ # @return [Array]
+ # Array with the matched URI as first element followed by the captured
+ # values.
+ def to_a
+ [to_s, *values]
+ end
+
+ ##
+ # @return [String]
+ # The matched URI as String.
+ def to_s
+ uri.to_s
+ end
+ alias_method :string, :to_s
+
+ # Returns multiple captured values at once.
+ #
+ # @param [String, Symbol, Fixnum] *indexes
+ # Indices of the captures to be returned
+ #
+ # @return [Array]
+ # Values corresponding to given indices.
+ #
+ # @see Addressable::Template::MatchData#[]
+ def values_at(*indexes)
+ indexes.map { |i| self[i] }
+ end
+
+ ##
+ # Returns a String representation of the MatchData's state.
+ #
+ # @return [String] The MatchData's state, as a String.
+ def inspect
+ sprintf("#<%s:%#0x RESULT:%s>",
+ self.class.to_s, self.object_id, self.mapping.inspect)
+ end
+
+ ##
+ # Dummy method for code expecting a ::MatchData instance
+ #
+ # @return [String] An empty string.
+ def pre_match
+ ""
+ end
+ alias_method :post_match, :pre_match
+ end
+
+ ##
+ # Creates a new Addressable::Template object.
+ #
+ # @param [#to_str] pattern The URI Template pattern.
+ #
+ # @return [Addressable::Template] The initialized Template object.
+ def initialize(pattern)
+ if !pattern.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{pattern.class} into String."
+ end
+ @pattern = pattern.to_str.dup.freeze
+ end
+
+ ##
+ # Freeze URI, initializing instance variables.
+ #
+ # @return [Addressable::URI] The frozen URI object.
+ def freeze
+ self.variables
+ self.variable_defaults
+ self.named_captures
+ super
+ end
+
+ ##
+ # @return [String] The Template object's pattern.
+ attr_reader :pattern
+
+ ##
+ # Returns a String representation of the Template object's state.
+ #
+ # @return [String] The Template object's state, as a String.
+ def inspect
+ sprintf("#<%s:%#0x PATTERN:%s>",
+ self.class.to_s, self.object_id, self.pattern)
+ end
+
+ ##
+ # Returns true if the Template objects are equal. This method
+ # does NOT normalize either Template before doing the comparison.
+ #
+ # @param [Object] template The Template to compare.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the Templates are equivalent, false
+ # otherwise.
+ def ==(template)
+ return false unless template.kind_of?(Template)
+ return self.pattern == template.pattern
+ end
+
+ ##
+ # Addressable::Template makes no distinction between `==` and `eql?`.
+ #
+ # @see #==
+ alias_method :eql?, :==
+
+ ##
+ # Extracts a mapping from the URI using a URI Template pattern.
+ #
+ # @param [Addressable::URI, #to_str] uri
+ # The URI to extract from.
+ #
+ # @param [#restore, #match] processor
+ # A template processor object may optionally be supplied.
+ #
+ # The object should respond to either the restore or
+ # match messages or both. The restore method should
+ # take two parameters: `[String] name` and `[String] value`.
+ # The restore method should reverse any transformations that
+ # have been performed on the value to ensure a valid URI.
+ # The match method should take a single
+ # parameter: `[String] name`. The match method should return
+ # a String containing a regular expression capture group for
+ # matching on that particular variable. The default value is `".*?"`.
+ # The match method has no effect on multivariate operator
+ # expansions.
+ #
+ # @return [Hash, NilClass]
+ # The Hash mapping that was extracted from the URI, or
+ # nil if the URI didn't match the template.
+ #
+ # @example
+ # class ExampleProcessor
+ # def self.restore(name, value)
+ # return value.gsub(/\+/, " ") if name == "query"
+ # return value
+ # end
+ #
+ # def self.match(name)
+ # return ".*?" if name == "first"
+ # return ".*"
+ # end
+ # end
+ #
+ # uri = Addressable::URI.parse(
+ # "http://example.com/search/an+example+search+query/"
+ # )
+ # Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).extract(uri, ExampleProcessor)
+ # #=> {"query" => "an example search query"}
+ #
+ # uri = Addressable::URI.parse("http://example.com/a/b/c/")
+ # Addressable::Template.new(
+ # "http://example.com/{first}/{second}/"
+ # ).extract(uri, ExampleProcessor)
+ # #=> {"first" => "a", "second" => "b/c"}
+ #
+ # uri = Addressable::URI.parse("http://example.com/a/b/c/")
+ # Addressable::Template.new(
+ # "http://example.com/{first}/{-list|/|second}/"
+ # ).extract(uri)
+ # #=> {"first" => "a", "second" => ["b", "c"]}
+ def extract(uri, processor=nil)
+ match_data = self.match(uri, processor)
+ return (match_data ? match_data.mapping : nil)
+ end
+
+ ##
+ # Extracts match data from the URI using a URI Template pattern.
+ #
+ # @param [Addressable::URI, #to_str] uri
+ # The URI to extract from.
+ #
+ # @param [#restore, #match] processor
+ # A template processor object may optionally be supplied.
+ #
+ # The object should respond to either the restore or
+ # match messages or both. The restore method should
+ # take two parameters: `[String] name` and `[String] value`.
+ # The restore method should reverse any transformations that
+ # have been performed on the value to ensure a valid URI.
+ # The match method should take a single
+ # parameter: `[String] name`. The match method should return
+ # a String containing a regular expression capture group for
+ # matching on that particular variable. The default value is `".*?"`.
+ # The match method has no effect on multivariate operator
+ # expansions.
+ #
+ # @return [Hash, NilClass]
+ # The Hash mapping that was extracted from the URI, or
+ # nil if the URI didn't match the template.
+ #
+ # @example
+ # class ExampleProcessor
+ # def self.restore(name, value)
+ # return value.gsub(/\+/, " ") if name == "query"
+ # return value
+ # end
+ #
+ # def self.match(name)
+ # return ".*?" if name == "first"
+ # return ".*"
+ # end
+ # end
+ #
+ # uri = Addressable::URI.parse(
+ # "http://example.com/search/an+example+search+query/"
+ # )
+ # match = Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).match(uri, ExampleProcessor)
+ # match.variables
+ # #=> ["query"]
+ # match.captures
+ # #=> ["an example search query"]
+ #
+ # uri = Addressable::URI.parse("http://example.com/a/b/c/")
+ # match = Addressable::Template.new(
+ # "http://example.com/{first}/{+second}/"
+ # ).match(uri, ExampleProcessor)
+ # match.variables
+ # #=> ["first", "second"]
+ # match.captures
+ # #=> ["a", "b/c"]
+ #
+ # uri = Addressable::URI.parse("http://example.com/a/b/c/")
+ # match = Addressable::Template.new(
+ # "http://example.com/{first}{/second*}/"
+ # ).match(uri)
+ # match.variables
+ # #=> ["first", "second"]
+ # match.captures
+ # #=> ["a", ["b", "c"]]
+ def match(uri, processor=nil)
+ uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)
+ mapping = {}
+
+ # First, we need to process the pattern, and extract the values.
+ expansions, expansion_regexp =
+ parse_template_pattern(pattern, processor)
+
+ return nil unless uri.to_str.match(expansion_regexp)
+ unparsed_values = uri.to_str.scan(expansion_regexp).flatten
+
+ if uri.to_str == pattern
+ return Addressable::Template::MatchData.new(uri, self, mapping)
+ elsif expansions.size > 0
+ index = 0
+ expansions.each do |expansion|
+ _, operator, varlist = *expansion.match(EXPRESSION)
+ varlist.split(',').each do |varspec|
+ _, name, modifier = *varspec.match(VARSPEC)
+ mapping[name] ||= nil
+ case operator
+ when nil, '+', '#', '/', '.'
+ unparsed_value = unparsed_values[index]
+ name = varspec[VARSPEC, 1]
+ value = unparsed_value
+ value = value.split(JOINERS[operator]) if value && modifier == '*'
+ when ';', '?', '&'
+ if modifier == '*'
+ if unparsed_values[index]
+ value = unparsed_values[index].split(JOINERS[operator])
+ value = value.inject({}) do |acc, v|
+ key, val = v.split('=')
+ val = "" if val.nil?
+ acc[key] = val
+ acc
+ end
+ end
+ else
+ if (unparsed_values[index])
+ name, value = unparsed_values[index].split('=')
+ value = "" if value.nil?
+ end
+ end
+ end
+ if processor != nil && processor.respond_to?(:restore)
+ value = processor.restore(name, value)
+ end
+ if processor == nil
+ if value.is_a?(Hash)
+ value = value.inject({}){|acc, (k, v)|
+ acc[Addressable::URI.unencode_component(k)] =
+ Addressable::URI.unencode_component(v)
+ acc
+ }
+ elsif value.is_a?(Array)
+ value = value.map{|v| Addressable::URI.unencode_component(v) }
+ else
+ value = Addressable::URI.unencode_component(value)
+ end
+ end
+ if !mapping.has_key?(name) || mapping[name].nil?
+ # Doesn't exist, set to value (even if value is nil)
+ mapping[name] = value
+ end
+ index = index + 1
+ end
+ end
+ return Addressable::Template::MatchData.new(uri, self, mapping)
+ else
+ return nil
+ end
+ end
+
+ ##
+ # Expands a URI template into another URI template.
+ #
+ # @param [Hash] mapping The mapping that corresponds to the pattern.
+ # @param [#validate, #transform] processor
+ # An optional processor object may be supplied.
+ # @param [Boolean] normalize_values
+ # Optional flag to enable/disable unicode normalization. Default: true
+ #
+ # The object should respond to either the validate or
+ # transform messages or both. Both the validate and
+ # transform methods should take two parameters: name and
+ # value. The validate method should return true
+ # or false; true if the value of the variable is valid,
+ # false otherwise. An InvalidTemplateValueError
+ # exception will be raised if the value is invalid. The transform
+ # method should return the transformed variable value as a String.
+ # If a transform method is used, the value will not be percent
+ # encoded automatically. Unicode normalization will be performed both
+ # before and after sending the value to the transform method.
+ #
+ # @return [Addressable::Template] The partially expanded URI template.
+ #
+ # @example
+ # Addressable::Template.new(
+ # "http://example.com/{one}/{two}/"
+ # ).partial_expand({"one" => "1"}).pattern
+ # #=> "http://example.com/1/{two}/"
+ #
+ # Addressable::Template.new(
+ # "http://example.com/{?one,two}/"
+ # ).partial_expand({"one" => "1"}).pattern
+ # #=> "http://example.com/?one=1{&two}/"
+ #
+ # Addressable::Template.new(
+ # "http://example.com/{?one,two,three}/"
+ # ).partial_expand({"one" => "1", "three" => 3}).pattern
+ # #=> "http://example.com/?one=1{&two}&three=3"
+ def partial_expand(mapping, processor=nil, normalize_values=true)
+ result = self.pattern.dup
+ mapping = normalize_keys(mapping)
+ result.gsub!( EXPRESSION ) do |capture|
+ transform_partial_capture(mapping, capture, processor, normalize_values)
+ end
+ return Addressable::Template.new(result)
+ end
+
+ ##
+ # Expands a URI template into a full URI.
+ #
+ # @param [Hash] mapping The mapping that corresponds to the pattern.
+ # @param [#validate, #transform] processor
+ # An optional processor object may be supplied.
+ # @param [Boolean] normalize_values
+ # Optional flag to enable/disable unicode normalization. Default: true
+ #
+ # The object should respond to either the validate or
+ # transform messages or both. Both the validate and
+ # transform methods should take two parameters: name and
+ # value. The validate method should return true
+ # or false; true if the value of the variable is valid,
+ # false otherwise. An InvalidTemplateValueError
+ # exception will be raised if the value is invalid. The transform
+ # method should return the transformed variable value as a String.
+ # If a transform method is used, the value will not be percent
+ # encoded automatically. Unicode normalization will be performed both
+ # before and after sending the value to the transform method.
+ #
+ # @return [Addressable::URI] The expanded URI template.
+ #
+ # @example
+ # class ExampleProcessor
+ # def self.validate(name, value)
+ # return !!(value =~ /^[\w ]+$/) if name == "query"
+ # return true
+ # end
+ #
+ # def self.transform(name, value)
+ # return value.gsub(/ /, "+") if name == "query"
+ # return value
+ # end
+ # end
+ #
+ # Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).expand(
+ # {"query" => "an example search query"},
+ # ExampleProcessor
+ # ).to_str
+ # #=> "http://example.com/search/an+example+search+query/"
+ #
+ # Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).expand(
+ # {"query" => "an example search query"}
+ # ).to_str
+ # #=> "http://example.com/search/an%20example%20search%20query/"
+ #
+ # Addressable::Template.new(
+ # "http://example.com/search/{query}/"
+ # ).expand(
+ # {"query" => "bogus!"},
+ # ExampleProcessor
+ # ).to_str
+ # #=> Addressable::Template::InvalidTemplateValueError
+ def expand(mapping, processor=nil, normalize_values=true)
+ result = self.pattern.dup
+ mapping = normalize_keys(mapping)
+ result.gsub!( EXPRESSION ) do |capture|
+ transform_capture(mapping, capture, processor, normalize_values)
+ end
+ return Addressable::URI.parse(result)
+ end
+
+ ##
+ # Returns an Array of variables used within the template pattern.
+ # The variables are listed in the Array in the order they appear within
+ # the pattern. Multiple occurrences of a variable within a pattern are
+ # not represented in this Array.
+ #
+ # @return [Array] The variables present in the template's pattern.
+ def variables
+ @variables ||= ordered_variable_defaults.map { |var, val| var }.uniq
+ end
+ alias_method :keys, :variables
+ alias_method :names, :variables
+
+ ##
+ # Returns a mapping of variables to their default values specified
+ # in the template. Variables without defaults are not returned.
+ #
+ # @return [Hash] Mapping of template variables to their defaults
+ def variable_defaults
+ @variable_defaults ||=
+ Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten]
+ end
+
+ ##
+ # Coerces a template into a `Regexp` object. This regular expression will
+ # behave very similarly to the actual template, and should match the same
+ # URI values, but it cannot fully handle, for example, values that would
+ # extract to an `Array`.
+ #
+ # @return [Regexp] A regular expression which should match the template.
+ def to_regexp
+ _, source = parse_template_pattern(pattern)
+ Regexp.new(source)
+ end
+
+ ##
+ # Returns the source of the coerced `Regexp`.
+ #
+ # @return [String] The source of the `Regexp` given by {#to_regexp}.
+ #
+ # @api private
+ def source
+ self.to_regexp.source
+ end
+
+ ##
+ # Returns the named captures of the coerced `Regexp`.
+ #
+ # @return [Hash] The named captures of the `Regexp` given by {#to_regexp}.
+ #
+ # @api private
+ def named_captures
+ self.to_regexp.named_captures
+ end
+
+ private
+ def ordered_variable_defaults
+ @ordered_variable_defaults ||= begin
+ expansions, _ = parse_template_pattern(pattern)
+ expansions.map do |capture|
+ _, _, varlist = *capture.match(EXPRESSION)
+ varlist.split(',').map do |varspec|
+ varspec[VARSPEC, 1]
+ end
+ end.flatten
+ end
+ end
+
+
+ ##
+ # Loops through each capture and expands any values available in mapping
+ #
+ # @param [Hash] mapping
+ # Set of keys to expand
+ # @param [String] capture
+ # The expression to expand
+ # @param [#validate, #transform] processor
+ # An optional processor object may be supplied.
+ # @param [Boolean] normalize_values
+ # Optional flag to enable/disable unicode normalization. Default: true
+ #
+ # The object should respond to either the validate or
+ # transform messages or both. Both the validate and
+ # transform methods should take two parameters: name and
+ # value. The validate method should return true
+ # or false; true if the value of the variable is valid,
+ # false otherwise. An InvalidTemplateValueError exception
+ # will be raised if the value is invalid. The transform method
+ # should return the transformed variable value as a String. If a
+ # transform method is used, the value will not be percent encoded
+ # automatically. Unicode normalization will be performed both before and
+ # after sending the value to the transform method.
+ #
+ # @return [String] The expanded expression
+ def transform_partial_capture(mapping, capture, processor = nil,
+ normalize_values = true)
+ _, operator, varlist = *capture.match(EXPRESSION)
+
+ vars = varlist.split(",")
+
+ if operator == "?"
+ # partial expansion of form style query variables sometimes requires a
+ # slight reordering of the variables to produce a valid url.
+ first_to_expand = vars.find { |varspec|
+ _, name, _ = *varspec.match(VARSPEC)
+ mapping.key?(name) && !mapping[name].nil?
+ }
+
+ vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand
+ end
+
+ vars.
+ inject("".dup) do |acc, varspec|
+ _, name, _ = *varspec.match(VARSPEC)
+ next_val = if mapping.key? name
+ transform_capture(mapping, "{#{operator}#{varspec}}",
+ processor, normalize_values)
+ else
+ "{#{operator}#{varspec}}"
+ end
+ # If we've already expanded at least one '?' operator with non-empty
+ # value, change to '&'
+ operator = "&" if (operator == "?") && (next_val != "")
+ acc << next_val
+ end
+ end
+
+ ##
+ # Transforms a mapped value so that values can be substituted into the
+ # template.
+ #
+ # @param [Hash] mapping The mapping to replace captures
+ # @param [String] capture
+ # The expression to replace
+ # @param [#validate, #transform] processor
+ # An optional processor object may be supplied.
+ # @param [Boolean] normalize_values
+ # Optional flag to enable/disable unicode normalization. Default: true
+ #
+ #
+ # The object should respond to either the validate or
+ # transform messages or both. Both the validate and
+ # transform methods should take two parameters: name and
+ # value. The validate method should return true
+ # or false; true if the value of the variable is valid,
+ # false otherwise. An InvalidTemplateValueError exception
+ # will be raised if the value is invalid. The transform method
+ # should return the transformed variable value as a String. If a
+ # transform method is used, the value will not be percent encoded
+ # automatically. Unicode normalization will be performed both before and
+ # after sending the value to the transform method.
+ #
+ # @return [String] The expanded expression
+ def transform_capture(mapping, capture, processor=nil,
+ normalize_values=true)
+ _, operator, varlist = *capture.match(EXPRESSION)
+ return_value = varlist.split(',').inject([]) do |acc, varspec|
+ _, name, modifier = *varspec.match(VARSPEC)
+ value = mapping[name]
+ unless value == nil || value == {}
+ allow_reserved = %w(+ #).include?(operator)
+ # Common primitives where the .to_s output is well-defined
+ if Numeric === value || Symbol === value ||
+ value == true || value == false
+ value = value.to_s
+ end
+ length = modifier.gsub(':', '').to_i if modifier =~ /^:\d+/
+
+ unless (Hash === value) ||
+ value.respond_to?(:to_ary) || value.respond_to?(:to_str)
+ raise TypeError,
+ "Can't convert #{value.class} into String or Array."
+ end
+
+ value = normalize_value(value) if normalize_values
+
+ if processor == nil || !processor.respond_to?(:transform)
+ # Handle percent escaping
+ if allow_reserved
+ encode_map =
+ Addressable::URI::CharacterClasses::RESERVED +
+ Addressable::URI::CharacterClasses::UNRESERVED
+ else
+ encode_map = Addressable::URI::CharacterClasses::UNRESERVED
+ end
+ if value.kind_of?(Array)
+ transformed_value = value.map do |val|
+ if length
+ Addressable::URI.encode_component(val[0...length], encode_map)
+ else
+ Addressable::URI.encode_component(val, encode_map)
+ end
+ end
+ unless modifier == "*"
+ transformed_value = transformed_value.join(',')
+ end
+ elsif value.kind_of?(Hash)
+ transformed_value = value.map do |key, val|
+ if modifier == "*"
+ "#{
+ Addressable::URI.encode_component( key, encode_map)
+ }=#{
+ Addressable::URI.encode_component( val, encode_map)
+ }"
+ else
+ "#{
+ Addressable::URI.encode_component( key, encode_map)
+ },#{
+ Addressable::URI.encode_component( val, encode_map)
+ }"
+ end
+ end
+ unless modifier == "*"
+ transformed_value = transformed_value.join(',')
+ end
+ else
+ if length
+ transformed_value = Addressable::URI.encode_component(
+ value[0...length], encode_map)
+ else
+ transformed_value = Addressable::URI.encode_component(
+ value, encode_map)
+ end
+ end
+ end
+
+ # Process, if we've got a processor
+ if processor != nil
+ if processor.respond_to?(:validate)
+ if !processor.validate(name, value)
+ display_value = value.kind_of?(Array) ? value.inspect : value
+ raise InvalidTemplateValueError,
+ "#{name}=#{display_value} is an invalid template value."
+ end
+ end
+ if processor.respond_to?(:transform)
+ transformed_value = processor.transform(name, value)
+ if normalize_values
+ transformed_value = normalize_value(transformed_value)
+ end
+ end
+ end
+ acc << [name, transformed_value]
+ end
+ acc
+ end
+ return "" if return_value.empty?
+ join_values(operator, return_value)
+ end
+
+ ##
+ # Takes a set of values, and joins them together based on the
+ # operator.
+ #
+ # @param [String, Nil] operator One of the operators from the set
+ # (?,&,+,#,;,/,.), or nil if there wasn't one.
+ # @param [Array] return_value
+ # The set of return values (as [variable_name, value] tuples) that will
+ # be joined together.
+ #
+ # @return [String] The transformed mapped value
+ def join_values(operator, return_value)
+ leader = LEADERS.fetch(operator, '')
+ joiner = JOINERS.fetch(operator, ',')
+ case operator
+ when '&', '?'
+ leader + return_value.map{|k,v|
+ if v.is_a?(Array) && v.first =~ /=/
+ v.join(joiner)
+ elsif v.is_a?(Array)
+ v.map{|inner_value| "#{k}=#{inner_value}"}.join(joiner)
+ else
+ "#{k}=#{v}"
+ end
+ }.join(joiner)
+ when ';'
+ return_value.map{|k,v|
+ if v.is_a?(Array) && v.first =~ /=/
+ ';' + v.join(";")
+ elsif v.is_a?(Array)
+ ';' + v.map{|inner_value| "#{k}=#{inner_value}"}.join(";")
+ else
+ v && v != '' ? ";#{k}=#{v}" : ";#{k}"
+ end
+ }.join
+ else
+ leader + return_value.map{|k,v| v}.join(joiner)
+ end
+ end
+
+ ##
+ # Takes a set of values, and joins them together based on the
+ # operator.
+ #
+ # @param [Hash, Array, String] value
+ # Normalizes keys and values with IDNA#unicode_normalize_kc
+ #
+ # @return [Hash, Array, String] The normalized values
+ def normalize_value(value)
+ unless value.is_a?(Hash)
+ value = value.respond_to?(:to_ary) ? value.to_ary : value.to_str
+ end
+
+ # Handle unicode normalization
+ if value.kind_of?(Array)
+ value.map! { |val| Addressable::IDNA.unicode_normalize_kc(val) }
+ elsif value.kind_of?(Hash)
+ value = value.inject({}) { |acc, (k, v)|
+ acc[Addressable::IDNA.unicode_normalize_kc(k)] =
+ Addressable::IDNA.unicode_normalize_kc(v)
+ acc
+ }
+ else
+ value = Addressable::IDNA.unicode_normalize_kc(value)
+ end
+ value
+ end
+
+ ##
+ # Generates a hash with string keys
+ #
+ # @param [Hash] mapping A mapping hash to normalize
+ #
+ # @return [Hash]
+ # A hash with stringified keys
+ def normalize_keys(mapping)
+ return mapping.inject({}) do |accu, pair|
+ name, value = pair
+ if Symbol === name
+ name = name.to_s
+ elsif name.respond_to?(:to_str)
+ name = name.to_str
+ else
+ raise TypeError,
+ "Can't convert #{name.class} into String."
+ end
+ accu[name] = value
+ accu
+ end
+ end
+
+ ##
+ # Generates the Regexp that parses a template pattern. Memoizes the
+ # value if template processor not set (processors may not be deterministic)
+ #
+ # @param [String] pattern The URI template pattern.
+ # @param [#match] processor The template processor to use.
+ #
+ # @return [Array, Regexp]
+ # An array of expansion variables nad a regular expression which may be
+ # used to parse a template pattern
+ def parse_template_pattern(pattern, processor = nil)
+ if processor.nil? && pattern == @pattern
+ @cached_template_parse ||=
+ parse_new_template_pattern(pattern, processor)
+ else
+ parse_new_template_pattern(pattern, processor)
+ end
+ end
+
+ ##
+ # Generates the Regexp that parses a template pattern.
+ #
+ # @param [String] pattern The URI template pattern.
+ # @param [#match] processor The template processor to use.
+ #
+ # @return [Array, Regexp]
+ # An array of expansion variables nad a regular expression which may be
+ # used to parse a template pattern
+ def parse_new_template_pattern(pattern, processor = nil)
+ # Escape the pattern. The two gsubs restore the escaped curly braces
+ # back to their original form. Basically, escape everything that isn't
+ # within an expansion.
+ escaped_pattern = Regexp.escape(
+ pattern
+ ).gsub(/\\\{(.*?)\\\}/) do |escaped|
+ escaped.gsub(/\\(.)/, "\\1")
+ end
+
+ expansions = []
+
+ # Create a regular expression that captures the values of the
+ # variables in the URI.
+ regexp_string = escaped_pattern.gsub( EXPRESSION ) do |expansion|
+
+ expansions << expansion
+ _, operator, varlist = *expansion.match(EXPRESSION)
+ leader = Regexp.escape(LEADERS.fetch(operator, ''))
+ joiner = Regexp.escape(JOINERS.fetch(operator, ','))
+ combined = varlist.split(',').map do |varspec|
+ _, name, modifier = *varspec.match(VARSPEC)
+
+ result = processor && processor.respond_to?(:match) ? processor.match(name) : nil
+ if result
+ "(?<#{name}>#{ result })"
+ else
+ group = case operator
+ when '+'
+ "#{ RESERVED }*?"
+ when '#'
+ "#{ RESERVED }*?"
+ when '/'
+ "#{ UNRESERVED }*?"
+ when '.'
+ "#{ UNRESERVED.gsub('\.', '') }*?"
+ when ';'
+ "#{ UNRESERVED }*=?#{ UNRESERVED }*?"
+ when '?'
+ "#{ UNRESERVED }*=#{ UNRESERVED }*?"
+ when '&'
+ "#{ UNRESERVED }*=#{ UNRESERVED }*?"
+ else
+ "#{ UNRESERVED }*?"
+ end
+ if modifier == '*'
+ "(?<#{name}>#{group}(?:#{joiner}?#{group})*)?"
+ else
+ "(?<#{name}>#{group})?"
+ end
+ end
+ end.join("#{joiner}?")
+ "(?:|#{leader}#{combined})"
+ end
+
+ # Ensure that the regular expression matches the whole URI.
+ regexp_string = "^#{regexp_string}$"
+ return expansions, Regexp.new(regexp_string)
+ end
+
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/uri.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/uri.rb
new file mode 100644
index 0000000..6b5f4fa
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/uri.rb
@@ -0,0 +1,2556 @@
+# frozen_string_literal: true
+
+# encoding:utf-8
+#--
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#++
+
+
+require "addressable/version"
+require "addressable/idna"
+require "public_suffix"
+
+##
+# Addressable is a library for processing links and URIs.
+module Addressable
+ ##
+ # This is an implementation of a URI parser based on
+ # RFC 3986,
+ # RFC 3987.
+ class URI
+ ##
+ # Raised if something other than a uri is supplied.
+ class InvalidURIError < StandardError
+ end
+
+ ##
+ # Container for the character classes specified in
+ # RFC 3986.
+ module CharacterClasses
+ ALPHA = "a-zA-Z"
+ DIGIT = "0-9"
+ GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
+ SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
+ RESERVED = GEN_DELIMS + SUB_DELIMS
+ UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
+ PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
+ SCHEME = ALPHA + DIGIT + "\\-\\+\\."
+ HOST = UNRESERVED + SUB_DELIMS + "\\[\\:\\]"
+ AUTHORITY = PCHAR + "\\[\\:\\]"
+ PATH = PCHAR + "\\/"
+ QUERY = PCHAR + "\\/\\?"
+ FRAGMENT = PCHAR + "\\/\\?"
+ end
+
+ module NormalizeCharacterClasses
+ HOST = /[^#{CharacterClasses::HOST}]/
+ UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/
+ PCHAR = /[^#{CharacterClasses::PCHAR}]/
+ SCHEME = /[^#{CharacterClasses::SCHEME}]/
+ FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/
+ QUERY = %r{[^a-zA-Z0-9\-\.\_\~\!\$\'\(\)\*\+\,\=\:\@\/\?%]|%(?!2B|2b)}
+ end
+
+ SLASH = '/'
+ EMPTY_STR = ''
+
+ URIREGEX = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
+
+ PORT_MAPPING = {
+ "http" => 80,
+ "https" => 443,
+ "ftp" => 21,
+ "tftp" => 69,
+ "sftp" => 22,
+ "ssh" => 22,
+ "svn+ssh" => 22,
+ "telnet" => 23,
+ "nntp" => 119,
+ "gopher" => 70,
+ "wais" => 210,
+ "ldap" => 389,
+ "prospero" => 1525
+ }.freeze
+
+ ##
+ # Returns a URI object based on the parsed string.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI string to parse.
+ # No parsing is performed if the object is already an
+ # Addressable::URI.
+ #
+ # @return [Addressable::URI] The parsed URI.
+ def self.parse(uri)
+ # If we were given nil, return nil.
+ return nil unless uri
+ # If a URI object is passed, just return itself.
+ return uri.dup if uri.kind_of?(self)
+
+ # If a URI object of the Ruby standard library variety is passed,
+ # convert it to a string, then parse the string.
+ # We do the check this way because we don't want to accidentally
+ # cause a missing constant exception to be thrown.
+ if uri.class.name =~ /^URI\b/
+ uri = uri.to_s
+ end
+
+ # Otherwise, convert to a String
+ begin
+ uri = uri.to_str
+ rescue TypeError, NoMethodError
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end if not uri.is_a? String
+
+ # This Regexp supplied as an example in RFC 3986, and it works great.
+ scan = uri.scan(URIREGEX)
+ fragments = scan[0]
+ scheme = fragments[1]
+ authority = fragments[3]
+ path = fragments[4]
+ query = fragments[6]
+ fragment = fragments[8]
+ user = nil
+ password = nil
+ host = nil
+ port = nil
+ if authority != nil
+ # The Regexp above doesn't split apart the authority.
+ userinfo = authority[/^([^\[\]]*)@/, 1]
+ if userinfo != nil
+ user = userinfo.strip[/^([^:]*):?/, 1]
+ password = userinfo.strip[/:(.*)$/, 1]
+ end
+ host = authority.sub(
+ /^([^\[\]]*)@/, EMPTY_STR
+ ).sub(
+ /:([^:@\[\]]*?)$/, EMPTY_STR
+ )
+ port = authority[/:([^:@\[\]]*?)$/, 1]
+ end
+ if port == EMPTY_STR
+ port = nil
+ end
+
+ return new(
+ :scheme => scheme,
+ :user => user,
+ :password => password,
+ :host => host,
+ :port => port,
+ :path => path,
+ :query => query,
+ :fragment => fragment
+ )
+ end
+
+ ##
+ # Converts an input to a URI. The input does not have to be a valid
+ # URI — the method will use heuristics to guess what URI was intended.
+ # This is not standards-compliant, merely user-friendly.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI string to parse.
+ # No parsing is performed if the object is already an
+ # Addressable::URI.
+ # @param [Hash] hints
+ # A Hash of hints to the heuristic parser.
+ # Defaults to {:scheme => "http"}.
+ #
+ # @return [Addressable::URI] The parsed URI.
+ def self.heuristic_parse(uri, hints={})
+ # If we were given nil, return nil.
+ return nil unless uri
+ # If a URI object is passed, just return itself.
+ return uri.dup if uri.kind_of?(self)
+
+ # If a URI object of the Ruby standard library variety is passed,
+ # convert it to a string, then parse the string.
+ # We do the check this way because we don't want to accidentally
+ # cause a missing constant exception to be thrown.
+ if uri.class.name =~ /^URI\b/
+ uri = uri.to_s
+ end
+
+ if !uri.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end
+ # Otherwise, convert to a String
+ uri = uri.to_str.dup.strip
+ hints = {
+ :scheme => "http"
+ }.merge(hints)
+ case uri
+ when /^http:\//i
+ uri.sub!(/^http:\/+/i, "http://")
+ when /^https:\//i
+ uri.sub!(/^https:\/+/i, "https://")
+ when /^feed:\/+http:\//i
+ uri.sub!(/^feed:\/+http:\/+/i, "feed:http://")
+ when /^feed:\//i
+ uri.sub!(/^feed:\/+/i, "feed://")
+ when %r[^file:/{4}]i
+ uri.sub!(%r[^file:/+]i, "file:////")
+ when %r[^file://localhost/]i
+ uri.sub!(%r[^file://localhost/+]i, "file:///")
+ when %r[^file:/+]i
+ uri.sub!(%r[^file:/+]i, "file:///")
+ when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
+ uri.sub!(/^/, hints[:scheme] + "://")
+ when /\A\d+\..*:\d+\z/
+ uri = "#{hints[:scheme]}://#{uri}"
+ end
+ match = uri.match(URIREGEX)
+ fragments = match.captures
+ authority = fragments[3]
+ if authority && authority.length > 0
+ new_authority = authority.tr("\\", "/").gsub(" ", "%20")
+ # NOTE: We want offset 4, not 3!
+ offset = match.offset(4)
+ uri = uri.dup
+ uri[offset[0]...offset[1]] = new_authority
+ end
+ parsed = self.parse(uri)
+ if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
+ parsed = self.parse(hints[:scheme] + "://" + uri)
+ end
+ if parsed.path.include?(".")
+ if parsed.path[/\b@\b/]
+ parsed.scheme = "mailto" unless parsed.scheme
+ elsif new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
+ parsed.defer_validation do
+ new_path = parsed.path.sub(
+ Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR)
+ parsed.host = new_host
+ parsed.path = new_path
+ parsed.scheme = hints[:scheme] unless parsed.scheme
+ end
+ end
+ end
+ return parsed
+ end
+
+ ##
+ # Converts a path to a file scheme URI. If the path supplied is
+ # relative, it will be returned as a relative URI. If the path supplied
+ # is actually a non-file URI, it will parse the URI as if it had been
+ # parsed with Addressable::URI.parse. Handles all of the
+ # various Microsoft-specific formats for specifying paths.
+ #
+ # @param [String, Addressable::URI, #to_str] path
+ # Typically a String path to a file or directory, but
+ # will return a sensible return value if an absolute URI is supplied
+ # instead.
+ #
+ # @return [Addressable::URI]
+ # The parsed file scheme URI or the original URI if some other URI
+ # scheme was provided.
+ #
+ # @example
+ # base = Addressable::URI.convert_path("/absolute/path/")
+ # uri = Addressable::URI.convert_path("relative/path")
+ # (base + uri).to_s
+ # #=> "file:///absolute/path/relative/path"
+ #
+ # Addressable::URI.convert_path(
+ # "c:\\windows\\My Documents 100%20\\foo.txt"
+ # ).to_s
+ # #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
+ #
+ # Addressable::URI.convert_path("http://example.com/").to_s
+ # #=> "http://example.com/"
+ def self.convert_path(path)
+ # If we were given nil, return nil.
+ return nil unless path
+ # If a URI object is passed, just return itself.
+ return path if path.kind_of?(self)
+ if !path.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{path.class} into String."
+ end
+ # Otherwise, convert to a String
+ path = path.to_str.strip
+
+ path.sub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
+ path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
+ uri = self.parse(path)
+
+ if uri.scheme == nil
+ # Adjust windows-style uris
+ uri.path.sub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
+ "/#{$1.downcase}:/"
+ end
+ uri.path.tr!("\\", SLASH)
+ if File.exist?(uri.path) &&
+ File.stat(uri.path).directory?
+ uri.path.chomp!(SLASH)
+ uri.path = uri.path + '/'
+ end
+
+ # If the path is absolute, set the scheme and host.
+ if uri.path.start_with?(SLASH)
+ uri.scheme = "file"
+ uri.host = EMPTY_STR
+ end
+ uri.normalize!
+ end
+
+ return uri
+ end
+
+ ##
+ # Joins several URIs together.
+ #
+ # @param [String, Addressable::URI, #to_str] *uris
+ # The URIs to join.
+ #
+ # @return [Addressable::URI] The joined URI.
+ #
+ # @example
+ # base = "http://example.com/"
+ # uri = Addressable::URI.parse("relative/path")
+ # Addressable::URI.join(base, uri)
+ # #=> #
+ def self.join(*uris)
+ uri_objects = uris.collect do |uri|
+ if !uri.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end
+ uri.kind_of?(self) ? uri : self.parse(uri.to_str)
+ end
+ result = uri_objects.shift.dup
+ for uri in uri_objects
+ result.join!(uri)
+ end
+ return result
+ end
+
+ ##
+ # Tables used to optimize encoding operations in `self.encode_component`
+ # and `self.normalize_component`
+ SEQUENCE_ENCODING_TABLE = Hash.new do |hash, sequence|
+ hash[sequence] = sequence.unpack("C*").map do |c|
+ format("%02x", c)
+ end.join
+ end
+
+ SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = Hash.new do |hash, sequence|
+ hash[sequence] = sequence.unpack("C*").map do |c|
+ format("%%%02X", c)
+ end.join
+ end
+
+ ##
+ # Percent encodes a URI component.
+ #
+ # @param [String, #to_str] component The URI component to encode.
+ #
+ # @param [String, Regexp] character_class
+ # The characters which are not percent encoded. If a String
+ # is passed, the String must be formatted as a regular
+ # expression character class. (Do not include the surrounding square
+ # brackets.) For example, "b-zB-Z0-9" would cause
+ # everything but the letters 'b' through 'z' and the numbers '0' through
+ # '9' to be percent encoded. If a Regexp is passed, the
+ # value /[^b-zB-Z0-9]/ would have the same effect. A set of
+ # useful String values may be found in the
+ # Addressable::URI::CharacterClasses module. The default
+ # value is the reserved plus unreserved character classes specified in
+ # RFC 3986.
+ #
+ # @param [Regexp] upcase_encoded
+ # A string of characters that may already be percent encoded, and whose
+ # encodings should be upcased. This allows normalization of percent
+ # encodings for characters not included in the
+ # character_class.
+ #
+ # @return [String] The encoded component.
+ #
+ # @example
+ # Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
+ # => "simple%2Fex%61mple"
+ # Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
+ # => "simple%2Fex%61mple"
+ # Addressable::URI.encode_component(
+ # "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
+ # )
+ # => "simple%2Fexample"
+ def self.encode_component(component, character_class=
+ CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
+ upcase_encoded='')
+ return nil if component.nil?
+
+ begin
+ if component.kind_of?(Symbol) ||
+ component.kind_of?(Numeric) ||
+ component.kind_of?(TrueClass) ||
+ component.kind_of?(FalseClass)
+ component = component.to_s
+ else
+ component = component.to_str
+ end
+ rescue TypeError, NoMethodError
+ raise TypeError, "Can't convert #{component.class} into String."
+ end if !component.is_a? String
+
+ if ![String, Regexp].include?(character_class.class)
+ raise TypeError,
+ "Expected String or Regexp, got #{character_class.inspect}"
+ end
+ if character_class.kind_of?(String)
+ character_class = /[^#{character_class}]/
+ end
+ # We can't perform regexps on invalid UTF sequences, but
+ # here we need to, so switch to ASCII.
+ component = component.dup
+ component.force_encoding(Encoding::ASCII_8BIT)
+ # Avoiding gsub! because there are edge cases with frozen strings
+ component = component.gsub(character_class) do |sequence|
+ SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[sequence]
+ end
+ if upcase_encoded.length > 0
+ upcase_encoded_chars = upcase_encoded.chars.map do |char|
+ SEQUENCE_ENCODING_TABLE[char]
+ end
+ component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/,
+ &:upcase)
+ end
+ return component
+ end
+
+ class << self
+ alias_method :escape_component, :encode_component
+ end
+
+ ##
+ # Unencodes any percent encoded characters within a URI component.
+ # This method may be used for unencoding either components or full URIs,
+ # however, it is recommended to use the unencode_component
+ # alias when unencoding components.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI or component to unencode.
+ #
+ # @param [Class] return_type
+ # The type of object to return.
+ # This value may only be set to String or
+ # Addressable::URI. All other values are invalid. Defaults
+ # to String.
+ #
+ # @param [String] leave_encoded
+ # A string of characters to leave encoded. If a percent encoded character
+ # in this list is encountered then it will remain percent encoded.
+ #
+ # @return [String, Addressable::URI]
+ # The unencoded component or URI.
+ # The return type is determined by the return_type
+ # parameter.
+ def self.unencode(uri, return_type=String, leave_encoded='')
+ return nil if uri.nil?
+
+ begin
+ uri = uri.to_str
+ rescue NoMethodError, TypeError
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end if !uri.is_a? String
+ if ![String, ::Addressable::URI].include?(return_type)
+ raise TypeError,
+ "Expected Class (String or Addressable::URI), " +
+ "got #{return_type.inspect}"
+ end
+ uri = uri.dup
+ # Seriously, only use UTF-8. I'm really not kidding!
+ uri.force_encoding("utf-8")
+
+ unless leave_encoded.empty?
+ leave_encoded = leave_encoded.dup.force_encoding("utf-8")
+ end
+
+ result = uri.gsub(/%[0-9a-f]{2}/iu) do |sequence|
+ c = sequence[1..3].to_i(16).chr
+ c.force_encoding("utf-8")
+ leave_encoded.include?(c) ? sequence : c
+ end
+ result.force_encoding("utf-8")
+ if return_type == String
+ return result
+ elsif return_type == ::Addressable::URI
+ return ::Addressable::URI.parse(result)
+ end
+ end
+
+ class << self
+ alias_method :unescape, :unencode
+ alias_method :unencode_component, :unencode
+ alias_method :unescape_component, :unencode
+ end
+
+
+ ##
+ # Normalizes the encoding of a URI component.
+ #
+ # @param [String, #to_str] component The URI component to encode.
+ #
+ # @param [String, Regexp] character_class
+ # The characters which are not percent encoded. If a String
+ # is passed, the String must be formatted as a regular
+ # expression character class. (Do not include the surrounding square
+ # brackets.) For example, "b-zB-Z0-9" would cause
+ # everything but the letters 'b' through 'z' and the numbers '0'
+ # through '9' to be percent encoded. If a Regexp is passed,
+ # the value /[^b-zB-Z0-9]/ would have the same effect. A
+ # set of useful String values may be found in the
+ # Addressable::URI::CharacterClasses module. The default
+ # value is the reserved plus unreserved character classes specified in
+ # RFC 3986.
+ #
+ # @param [String] leave_encoded
+ # When character_class is a String then
+ # leave_encoded is a string of characters that should remain
+ # percent encoded while normalizing the component; if they appear percent
+ # encoded in the original component, then they will be upcased ("%2f"
+ # normalized to "%2F") but otherwise left alone.
+ #
+ # @return [String] The normalized component.
+ #
+ # @example
+ # Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
+ # => "simple%2Fex%61mple"
+ # Addressable::URI.normalize_component(
+ # "simpl%65/%65xampl%65", /[^b-zB-Z]/
+ # )
+ # => "simple%2Fex%61mple"
+ # Addressable::URI.normalize_component(
+ # "simpl%65/%65xampl%65",
+ # Addressable::URI::CharacterClasses::UNRESERVED
+ # )
+ # => "simple%2Fexample"
+ # Addressable::URI.normalize_component(
+ # "one%20two%2fthree%26four",
+ # "0-9a-zA-Z &/",
+ # "/"
+ # )
+ # => "one two%2Fthree&four"
+ def self.normalize_component(component, character_class=
+ CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
+ leave_encoded='')
+ return nil if component.nil?
+
+ begin
+ component = component.to_str
+ rescue NoMethodError, TypeError
+ raise TypeError, "Can't convert #{component.class} into String."
+ end if !component.is_a? String
+
+ if ![String, Regexp].include?(character_class.class)
+ raise TypeError,
+ "Expected String or Regexp, got #{character_class.inspect}"
+ end
+ if character_class.kind_of?(String)
+ leave_re = if leave_encoded.length > 0
+ character_class = "#{character_class}%" unless character_class.include?('%')
+
+ "|%(?!#{leave_encoded.chars.map do |char|
+ seq = SEQUENCE_ENCODING_TABLE[char]
+ [seq.upcase, seq.downcase]
+ end.flatten.join('|')})"
+ end
+
+ character_class = if leave_re
+ /[^#{character_class}]#{leave_re}/
+ else
+ /[^#{character_class}]/
+ end
+ end
+ # We can't perform regexps on invalid UTF sequences, but
+ # here we need to, so switch to ASCII.
+ component = component.dup
+ component.force_encoding(Encoding::ASCII_8BIT)
+ unencoded = self.unencode_component(component, String, leave_encoded)
+ begin
+ encoded = self.encode_component(
+ Addressable::IDNA.unicode_normalize_kc(unencoded),
+ character_class,
+ leave_encoded
+ )
+ rescue ArgumentError
+ encoded = self.encode_component(unencoded)
+ end
+ encoded.force_encoding(Encoding::UTF_8)
+ return encoded
+ end
+
+ ##
+ # Percent encodes any special characters in the URI.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI to encode.
+ #
+ # @param [Class] return_type
+ # The type of object to return.
+ # This value may only be set to String or
+ # Addressable::URI. All other values are invalid. Defaults
+ # to String.
+ #
+ # @return [String, Addressable::URI]
+ # The encoded URI.
+ # The return type is determined by the return_type
+ # parameter.
+ def self.encode(uri, return_type=String)
+ return nil if uri.nil?
+
+ begin
+ uri = uri.to_str
+ rescue NoMethodError, TypeError
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end if !uri.is_a? String
+
+ if ![String, ::Addressable::URI].include?(return_type)
+ raise TypeError,
+ "Expected Class (String or Addressable::URI), " +
+ "got #{return_type.inspect}"
+ end
+ uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
+ encoded_uri = Addressable::URI.new(
+ :scheme => self.encode_component(uri_object.scheme,
+ Addressable::URI::CharacterClasses::SCHEME),
+ :authority => self.encode_component(uri_object.authority,
+ Addressable::URI::CharacterClasses::AUTHORITY),
+ :path => self.encode_component(uri_object.path,
+ Addressable::URI::CharacterClasses::PATH),
+ :query => self.encode_component(uri_object.query,
+ Addressable::URI::CharacterClasses::QUERY),
+ :fragment => self.encode_component(uri_object.fragment,
+ Addressable::URI::CharacterClasses::FRAGMENT)
+ )
+ if return_type == String
+ return encoded_uri.to_s
+ elsif return_type == ::Addressable::URI
+ return encoded_uri
+ end
+ end
+
+ class << self
+ alias_method :escape, :encode
+ end
+
+ ##
+ # Normalizes the encoding of a URI. Characters within a hostname are
+ # not percent encoded to allow for internationalized domain names.
+ #
+ # @param [String, Addressable::URI, #to_str] uri
+ # The URI to encode.
+ #
+ # @param [Class] return_type
+ # The type of object to return.
+ # This value may only be set to String or
+ # Addressable::URI. All other values are invalid. Defaults
+ # to String.
+ #
+ # @return [String, Addressable::URI]
+ # The encoded URI.
+ # The return type is determined by the return_type
+ # parameter.
+ def self.normalized_encode(uri, return_type=String)
+ begin
+ uri = uri.to_str
+ rescue NoMethodError, TypeError
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end if !uri.is_a? String
+
+ if ![String, ::Addressable::URI].include?(return_type)
+ raise TypeError,
+ "Expected Class (String or Addressable::URI), " +
+ "got #{return_type.inspect}"
+ end
+ uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
+ components = {
+ :scheme => self.unencode_component(uri_object.scheme),
+ :user => self.unencode_component(uri_object.user),
+ :password => self.unencode_component(uri_object.password),
+ :host => self.unencode_component(uri_object.host),
+ :port => (uri_object.port.nil? ? nil : uri_object.port.to_s),
+ :path => self.unencode_component(uri_object.path),
+ :query => self.unencode_component(uri_object.query),
+ :fragment => self.unencode_component(uri_object.fragment)
+ }
+ components.each do |key, value|
+ if value != nil
+ begin
+ components[key] =
+ Addressable::IDNA.unicode_normalize_kc(value.to_str)
+ rescue ArgumentError
+ # Likely a malformed UTF-8 character, skip unicode normalization
+ components[key] = value.to_str
+ end
+ end
+ end
+ encoded_uri = Addressable::URI.new(
+ :scheme => self.encode_component(components[:scheme],
+ Addressable::URI::CharacterClasses::SCHEME),
+ :user => self.encode_component(components[:user],
+ Addressable::URI::CharacterClasses::UNRESERVED),
+ :password => self.encode_component(components[:password],
+ Addressable::URI::CharacterClasses::UNRESERVED),
+ :host => components[:host],
+ :port => components[:port],
+ :path => self.encode_component(components[:path],
+ Addressable::URI::CharacterClasses::PATH),
+ :query => self.encode_component(components[:query],
+ Addressable::URI::CharacterClasses::QUERY),
+ :fragment => self.encode_component(components[:fragment],
+ Addressable::URI::CharacterClasses::FRAGMENT)
+ )
+ if return_type == String
+ return encoded_uri.to_s
+ elsif return_type == ::Addressable::URI
+ return encoded_uri
+ end
+ end
+
+ ##
+ # Encodes a set of key/value pairs according to the rules for the
+ # application/x-www-form-urlencoded MIME type.
+ #
+ # @param [#to_hash, #to_ary] form_values
+ # The form values to encode.
+ #
+ # @param [TrueClass, FalseClass] sort
+ # Sort the key/value pairs prior to encoding.
+ # Defaults to false.
+ #
+ # @return [String]
+ # The encoded value.
+ def self.form_encode(form_values, sort=false)
+ if form_values.respond_to?(:to_hash)
+ form_values = form_values.to_hash.to_a
+ elsif form_values.respond_to?(:to_ary)
+ form_values = form_values.to_ary
+ else
+ raise TypeError, "Can't convert #{form_values.class} into Array."
+ end
+
+ form_values = form_values.inject([]) do |accu, (key, value)|
+ if value.kind_of?(Array)
+ value.each do |v|
+ accu << [key.to_s, v.to_s]
+ end
+ else
+ accu << [key.to_s, value.to_s]
+ end
+ accu
+ end
+
+ if sort
+ # Useful for OAuth and optimizing caching systems
+ form_values = form_values.sort
+ end
+ escaped_form_values = form_values.map do |(key, value)|
+ # Line breaks are CRLF pairs
+ [
+ self.encode_component(
+ key.gsub(/(\r\n|\n|\r)/, "\r\n"),
+ CharacterClasses::UNRESERVED
+ ).gsub("%20", "+"),
+ self.encode_component(
+ value.gsub(/(\r\n|\n|\r)/, "\r\n"),
+ CharacterClasses::UNRESERVED
+ ).gsub("%20", "+")
+ ]
+ end
+ return escaped_form_values.map do |(key, value)|
+ "#{key}=#{value}"
+ end.join("&")
+ end
+
+ ##
+ # Decodes a String according to the rules for the
+ # application/x-www-form-urlencoded MIME type.
+ #
+ # @param [String, #to_str] encoded_value
+ # The form values to decode.
+ #
+ # @return [Array]
+ # The decoded values.
+ # This is not a Hash because of the possibility for
+ # duplicate keys.
+ def self.form_unencode(encoded_value)
+ if !encoded_value.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{encoded_value.class} into String."
+ end
+ encoded_value = encoded_value.to_str
+ split_values = encoded_value.split("&").map do |pair|
+ pair.split("=", 2)
+ end
+ return split_values.map do |(key, value)|
+ [
+ key ? self.unencode_component(
+ key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil,
+ value ? (self.unencode_component(
+ value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil
+ ]
+ end
+ end
+
+ ##
+ # Creates a new uri object from component parts.
+ #
+ # @option [String, #to_str] scheme The scheme component.
+ # @option [String, #to_str] user The user component.
+ # @option [String, #to_str] password The password component.
+ # @option [String, #to_str] userinfo
+ # The userinfo component. If this is supplied, the user and password
+ # components must be omitted.
+ # @option [String, #to_str] host The host component.
+ # @option [String, #to_str] port The port component.
+ # @option [String, #to_str] authority
+ # The authority component. If this is supplied, the user, password,
+ # userinfo, host, and port components must be omitted.
+ # @option [String, #to_str] path The path component.
+ # @option [String, #to_str] query The query component.
+ # @option [String, #to_str] fragment The fragment component.
+ #
+ # @return [Addressable::URI] The constructed URI object.
+ def initialize(options={})
+ if options.has_key?(:authority)
+ if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
+ raise ArgumentError,
+ "Cannot specify both an authority and any of the components " +
+ "within the authority."
+ end
+ end
+ if options.has_key?(:userinfo)
+ if (options.keys & [:user, :password]).any?
+ raise ArgumentError,
+ "Cannot specify both a userinfo and either the user or password."
+ end
+ end
+
+ self.defer_validation do
+ # Bunch of crazy logic required because of the composite components
+ # like userinfo and authority.
+ self.scheme = options[:scheme] if options[:scheme]
+ self.user = options[:user] if options[:user]
+ self.password = options[:password] if options[:password]
+ self.userinfo = options[:userinfo] if options[:userinfo]
+ self.host = options[:host] if options[:host]
+ self.port = options[:port] if options[:port]
+ self.authority = options[:authority] if options[:authority]
+ self.path = options[:path] if options[:path]
+ self.query = options[:query] if options[:query]
+ self.query_values = options[:query_values] if options[:query_values]
+ self.fragment = options[:fragment] if options[:fragment]
+ end
+ self.to_s
+ end
+
+ ##
+ # Freeze URI, initializing instance variables.
+ #
+ # @return [Addressable::URI] The frozen URI object.
+ def freeze
+ self.normalized_scheme
+ self.normalized_user
+ self.normalized_password
+ self.normalized_userinfo
+ self.normalized_host
+ self.normalized_port
+ self.normalized_authority
+ self.normalized_site
+ self.normalized_path
+ self.normalized_query
+ self.normalized_fragment
+ self.hash
+ super
+ end
+
+ ##
+ # The scheme component for this URI.
+ #
+ # @return [String] The scheme component.
+ def scheme
+ return defined?(@scheme) ? @scheme : nil
+ end
+
+ ##
+ # The scheme component for this URI, normalized.
+ #
+ # @return [String] The scheme component, normalized.
+ def normalized_scheme
+ return nil unless self.scheme
+ @normalized_scheme ||= begin
+ if self.scheme =~ /^\s*ssh\+svn\s*$/i
+ "svn+ssh".dup
+ else
+ Addressable::URI.normalize_component(
+ self.scheme.strip.downcase,
+ Addressable::URI::NormalizeCharacterClasses::SCHEME
+ )
+ end
+ end
+ # All normalized values should be UTF-8
+ @normalized_scheme.force_encoding(Encoding::UTF_8) if @normalized_scheme
+ @normalized_scheme
+ end
+
+ ##
+ # Sets the scheme component for this URI.
+ #
+ # @param [String, #to_str] new_scheme The new scheme component.
+ def scheme=(new_scheme)
+ if new_scheme && !new_scheme.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_scheme.class} into String."
+ elsif new_scheme
+ new_scheme = new_scheme.to_str
+ end
+ if new_scheme && new_scheme !~ /\A[a-z][a-z0-9\.\+\-]*\z/i
+ raise InvalidURIError, "Invalid scheme format: '#{new_scheme}'"
+ end
+ @scheme = new_scheme
+ @scheme = nil if @scheme.to_s.strip.empty?
+
+ # Reset dependent values
+ remove_instance_variable(:@normalized_scheme) if defined?(@normalized_scheme)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The user component for this URI.
+ #
+ # @return [String] The user component.
+ def user
+ return defined?(@user) ? @user : nil
+ end
+
+ ##
+ # The user component for this URI, normalized.
+ #
+ # @return [String] The user component, normalized.
+ def normalized_user
+ return nil unless self.user
+ return @normalized_user if defined?(@normalized_user)
+ @normalized_user ||= begin
+ if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
+ (!self.password || self.password.strip.empty?)
+ nil
+ else
+ Addressable::URI.normalize_component(
+ self.user.strip,
+ Addressable::URI::NormalizeCharacterClasses::UNRESERVED
+ )
+ end
+ end
+ # All normalized values should be UTF-8
+ @normalized_user.force_encoding(Encoding::UTF_8) if @normalized_user
+ @normalized_user
+ end
+
+ ##
+ # Sets the user component for this URI.
+ #
+ # @param [String, #to_str] new_user The new user component.
+ def user=(new_user)
+ if new_user && !new_user.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_user.class} into String."
+ end
+ @user = new_user ? new_user.to_str : nil
+
+ # You can't have a nil user with a non-nil password
+ if password != nil
+ @user = EMPTY_STR if @user.nil?
+ end
+
+ # Reset dependent values
+ remove_instance_variable(:@userinfo) if defined?(@userinfo)
+ remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_user) if defined?(@normalized_user)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The password component for this URI.
+ #
+ # @return [String] The password component.
+ def password
+ return defined?(@password) ? @password : nil
+ end
+
+ ##
+ # The password component for this URI, normalized.
+ #
+ # @return [String] The password component, normalized.
+ def normalized_password
+ return nil unless self.password
+ return @normalized_password if defined?(@normalized_password)
+ @normalized_password ||= begin
+ if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
+ (!self.user || self.user.strip.empty?)
+ nil
+ else
+ Addressable::URI.normalize_component(
+ self.password.strip,
+ Addressable::URI::NormalizeCharacterClasses::UNRESERVED
+ )
+ end
+ end
+ # All normalized values should be UTF-8
+ if @normalized_password
+ @normalized_password.force_encoding(Encoding::UTF_8)
+ end
+ @normalized_password
+ end
+
+ ##
+ # Sets the password component for this URI.
+ #
+ # @param [String, #to_str] new_password The new password component.
+ def password=(new_password)
+ if new_password && !new_password.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_password.class} into String."
+ end
+ @password = new_password ? new_password.to_str : nil
+
+ # You can't have a nil user with a non-nil password
+ @password ||= nil
+ @user ||= nil
+ if @password != nil
+ @user = EMPTY_STR if @user.nil?
+ end
+
+ # Reset dependent values
+ remove_instance_variable(:@userinfo) if defined?(@userinfo)
+ remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_password) if defined?(@normalized_password)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The userinfo component for this URI.
+ # Combines the user and password components.
+ #
+ # @return [String] The userinfo component.
+ def userinfo
+ current_user = self.user
+ current_password = self.password
+ (current_user || current_password) && @userinfo ||= begin
+ if current_user && current_password
+ "#{current_user}:#{current_password}"
+ elsif current_user && !current_password
+ "#{current_user}"
+ end
+ end
+ end
+
+ ##
+ # The userinfo component for this URI, normalized.
+ #
+ # @return [String] The userinfo component, normalized.
+ def normalized_userinfo
+ return nil unless self.userinfo
+ return @normalized_userinfo if defined?(@normalized_userinfo)
+ @normalized_userinfo ||= begin
+ current_user = self.normalized_user
+ current_password = self.normalized_password
+ if !current_user && !current_password
+ nil
+ elsif current_user && current_password
+ "#{current_user}:#{current_password}".dup
+ elsif current_user && !current_password
+ "#{current_user}".dup
+ end
+ end
+ # All normalized values should be UTF-8
+ if @normalized_userinfo
+ @normalized_userinfo.force_encoding(Encoding::UTF_8)
+ end
+ @normalized_userinfo
+ end
+
+ ##
+ # Sets the userinfo component for this URI.
+ #
+ # @param [String, #to_str] new_userinfo The new userinfo component.
+ def userinfo=(new_userinfo)
+ if new_userinfo && !new_userinfo.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_userinfo.class} into String."
+ end
+ new_user, new_password = if new_userinfo
+ [
+ new_userinfo.to_str.strip[/^(.*):/, 1],
+ new_userinfo.to_str.strip[/:(.*)$/, 1]
+ ]
+ else
+ [nil, nil]
+ end
+
+ # Password assigned first to ensure validity in case of nil
+ self.password = new_password
+ self.user = new_user
+
+ # Reset dependent values
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The host component for this URI.
+ #
+ # @return [String] The host component.
+ def host
+ return defined?(@host) ? @host : nil
+ end
+
+ ##
+ # The host component for this URI, normalized.
+ #
+ # @return [String] The host component, normalized.
+ def normalized_host
+ return nil unless self.host
+
+ @normalized_host ||= begin
+ if !self.host.strip.empty?
+ result = ::Addressable::IDNA.to_ascii(
+ URI.unencode_component(self.host.strip.downcase)
+ )
+ if result =~ /[^\.]\.$/
+ # Single trailing dots are unnecessary.
+ result = result[0...-1]
+ end
+ result = Addressable::URI.normalize_component(
+ result,
+ NormalizeCharacterClasses::HOST
+ )
+ result
+ else
+ EMPTY_STR.dup
+ end
+ end
+ # All normalized values should be UTF-8
+ if @normalized_host && !@normalized_host.empty?
+ @normalized_host.force_encoding(Encoding::UTF_8)
+ end
+ @normalized_host
+ end
+
+ ##
+ # Sets the host component for this URI.
+ #
+ # @param [String, #to_str] new_host The new host component.
+ def host=(new_host)
+ if new_host && !new_host.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_host.class} into String."
+ end
+ @host = new_host ? new_host.to_str : nil
+
+ # Reset dependent values
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_host) if defined?(@normalized_host)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # This method is same as URI::Generic#host except
+ # brackets for IPv6 (and 'IPvFuture') addresses are removed.
+ #
+ # @see Addressable::URI#host
+ #
+ # @return [String] The hostname for this URI.
+ def hostname
+ v = self.host
+ /\A\[(.*)\]\z/ =~ v ? $1 : v
+ end
+
+ ##
+ # This method is same as URI::Generic#host= except
+ # the argument can be a bare IPv6 address (or 'IPvFuture').
+ #
+ # @see Addressable::URI#host=
+ #
+ # @param [String, #to_str] new_hostname The new hostname for this URI.
+ def hostname=(new_hostname)
+ if new_hostname &&
+ (new_hostname.respond_to?(:ipv4?) || new_hostname.respond_to?(:ipv6?))
+ new_hostname = new_hostname.to_s
+ elsif new_hostname && !new_hostname.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_hostname.class} into String."
+ end
+ v = new_hostname ? new_hostname.to_str : nil
+ v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
+ self.host = v
+ end
+
+ ##
+ # Returns the top-level domain for this host.
+ #
+ # @example
+ # Addressable::URI.parse("http://www.example.co.uk").tld # => "co.uk"
+ def tld
+ PublicSuffix.parse(self.host, ignore_private: true).tld
+ end
+
+ ##
+ # Sets the top-level domain for this URI.
+ #
+ # @param [String, #to_str] new_tld The new top-level domain.
+ def tld=(new_tld)
+ replaced_tld = host.sub(/#{tld}\z/, new_tld)
+ self.host = PublicSuffix::Domain.new(replaced_tld).to_s
+ end
+
+ ##
+ # Returns the public suffix domain for this host.
+ #
+ # @example
+ # Addressable::URI.parse("http://www.example.co.uk").domain # => "example.co.uk"
+ def domain
+ PublicSuffix.domain(self.host, ignore_private: true)
+ end
+
+ ##
+ # The authority component for this URI.
+ # Combines the user, password, host, and port components.
+ #
+ # @return [String] The authority component.
+ def authority
+ self.host && @authority ||= begin
+ authority = String.new
+ if self.userinfo != nil
+ authority << "#{self.userinfo}@"
+ end
+ authority << self.host
+ if self.port != nil
+ authority << ":#{self.port}"
+ end
+ authority
+ end
+ end
+
+ ##
+ # The authority component for this URI, normalized.
+ #
+ # @return [String] The authority component, normalized.
+ def normalized_authority
+ return nil unless self.authority
+ @normalized_authority ||= begin
+ authority = String.new
+ if self.normalized_userinfo != nil
+ authority << "#{self.normalized_userinfo}@"
+ end
+ authority << self.normalized_host
+ if self.normalized_port != nil
+ authority << ":#{self.normalized_port}"
+ end
+ authority
+ end
+ # All normalized values should be UTF-8
+ if @normalized_authority
+ @normalized_authority.force_encoding(Encoding::UTF_8)
+ end
+ @normalized_authority
+ end
+
+ ##
+ # Sets the authority component for this URI.
+ #
+ # @param [String, #to_str] new_authority The new authority component.
+ def authority=(new_authority)
+ if new_authority
+ if !new_authority.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_authority.class} into String."
+ end
+ new_authority = new_authority.to_str
+ new_userinfo = new_authority[/^([^\[\]]*)@/, 1]
+ if new_userinfo
+ new_user = new_userinfo.strip[/^([^:]*):?/, 1]
+ new_password = new_userinfo.strip[/:(.*)$/, 1]
+ end
+ new_host = new_authority.sub(
+ /^([^\[\]]*)@/, EMPTY_STR
+ ).sub(
+ /:([^:@\[\]]*?)$/, EMPTY_STR
+ )
+ new_port =
+ new_authority[/:([^:@\[\]]*?)$/, 1]
+ end
+
+ # Password assigned first to ensure validity in case of nil
+ self.password = defined?(new_password) ? new_password : nil
+ self.user = defined?(new_user) ? new_user : nil
+ self.host = defined?(new_host) ? new_host : nil
+ self.port = defined?(new_port) ? new_port : nil
+
+ # Reset dependent values
+ remove_instance_variable(:@userinfo) if defined?(@userinfo)
+ remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The origin for this URI, serialized to ASCII, as per
+ # RFC 6454, section 6.2.
+ #
+ # @return [String] The serialized origin.
+ def origin
+ if self.scheme && self.authority
+ if self.normalized_port
+ "#{self.normalized_scheme}://#{self.normalized_host}" +
+ ":#{self.normalized_port}"
+ else
+ "#{self.normalized_scheme}://#{self.normalized_host}"
+ end
+ else
+ "null"
+ end
+ end
+
+ ##
+ # Sets the origin for this URI, serialized to ASCII, as per
+ # RFC 6454, section 6.2. This assignment will reset the `userinfo`
+ # component.
+ #
+ # @param [String, #to_str] new_origin The new origin component.
+ def origin=(new_origin)
+ if new_origin
+ if !new_origin.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_origin.class} into String."
+ end
+ new_origin = new_origin.to_str
+ new_scheme = new_origin[/^([^:\/?#]+):\/\//, 1]
+ unless new_scheme
+ raise InvalidURIError, 'An origin cannot omit the scheme.'
+ end
+ new_host = new_origin[/:\/\/([^\/?#:]+)/, 1]
+ unless new_host
+ raise InvalidURIError, 'An origin cannot omit the host.'
+ end
+ new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1]
+ end
+
+ self.scheme = defined?(new_scheme) ? new_scheme : nil
+ self.host = defined?(new_host) ? new_host : nil
+ self.port = defined?(new_port) ? new_port : nil
+ self.userinfo = nil
+
+ # Reset dependent values
+ remove_instance_variable(:@userinfo) if defined?(@userinfo)
+ remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_authority) if defined?(@normalized_authority)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ # Returns an array of known ip-based schemes. These schemes typically
+ # use a similar URI form:
+ # //:@:/
+ def self.ip_based_schemes
+ return self.port_mapping.keys
+ end
+
+ # Returns a hash of common IP-based schemes and their default port
+ # numbers. Adding new schemes to this hash, as necessary, will allow
+ # for better URI normalization.
+ def self.port_mapping
+ PORT_MAPPING
+ end
+
+ ##
+ # The port component for this URI.
+ # This is the port number actually given in the URI. This does not
+ # infer port numbers from default values.
+ #
+ # @return [Integer] The port component.
+ def port
+ return defined?(@port) ? @port : nil
+ end
+
+ ##
+ # The port component for this URI, normalized.
+ #
+ # @return [Integer] The port component, normalized.
+ def normalized_port
+ return nil unless self.port
+ return @normalized_port if defined?(@normalized_port)
+ @normalized_port ||= begin
+ if URI.port_mapping[self.normalized_scheme] == self.port
+ nil
+ else
+ self.port
+ end
+ end
+ end
+
+ ##
+ # Sets the port component for this URI.
+ #
+ # @param [String, Integer, #to_s] new_port The new port component.
+ def port=(new_port)
+ if new_port != nil && new_port.respond_to?(:to_str)
+ new_port = Addressable::URI.unencode_component(new_port.to_str)
+ end
+
+ if new_port.respond_to?(:valid_encoding?) && !new_port.valid_encoding?
+ raise InvalidURIError, "Invalid encoding in port"
+ end
+
+ if new_port != nil && !(new_port.to_s =~ /^\d+$/)
+ raise InvalidURIError,
+ "Invalid port number: #{new_port.inspect}"
+ end
+
+ @port = new_port.to_s.to_i
+ @port = nil if @port == 0
+
+ # Reset dependent values
+ remove_instance_variable(:@authority) if defined?(@authority)
+ remove_instance_variable(:@normalized_port) if defined?(@normalized_port)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The inferred port component for this URI.
+ # This method will normalize to the default port for the URI's scheme if
+ # the port isn't explicitly specified in the URI.
+ #
+ # @return [Integer] The inferred port component.
+ def inferred_port
+ if self.port.to_i == 0
+ self.default_port
+ else
+ self.port.to_i
+ end
+ end
+
+ ##
+ # The default port for this URI's scheme.
+ # This method will always returns the default port for the URI's scheme
+ # regardless of the presence of an explicit port in the URI.
+ #
+ # @return [Integer] The default port.
+ def default_port
+ URI.port_mapping[self.scheme.strip.downcase] if self.scheme
+ end
+
+ ##
+ # The combination of components that represent a site.
+ # Combines the scheme, user, password, host, and port components.
+ # Primarily useful for HTTP and HTTPS.
+ #
+ # For example, "http://example.com/path?query" would have a
+ # site value of "http://example.com".
+ #
+ # @return [String] The components that identify a site.
+ def site
+ (self.scheme || self.authority) && @site ||= begin
+ site_string = "".dup
+ site_string << "#{self.scheme}:" if self.scheme != nil
+ site_string << "//#{self.authority}" if self.authority != nil
+ site_string
+ end
+ end
+
+ ##
+ # The normalized combination of components that represent a site.
+ # Combines the scheme, user, password, host, and port components.
+ # Primarily useful for HTTP and HTTPS.
+ #
+ # For example, "http://example.com/path?query" would have a
+ # site value of "http://example.com".
+ #
+ # @return [String] The normalized components that identify a site.
+ def normalized_site
+ return nil unless self.site
+ @normalized_site ||= begin
+ site_string = "".dup
+ if self.normalized_scheme != nil
+ site_string << "#{self.normalized_scheme}:"
+ end
+ if self.normalized_authority != nil
+ site_string << "//#{self.normalized_authority}"
+ end
+ site_string
+ end
+ # All normalized values should be UTF-8
+ @normalized_site.force_encoding(Encoding::UTF_8) if @normalized_site
+ @normalized_site
+ end
+
+ ##
+ # Sets the site value for this URI.
+ #
+ # @param [String, #to_str] new_site The new site value.
+ def site=(new_site)
+ if new_site
+ if !new_site.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_site.class} into String."
+ end
+ new_site = new_site.to_str
+ # These two regular expressions derived from the primary parsing
+ # expression
+ self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1]
+ self.authority = new_site[
+ /^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1
+ ]
+ else
+ self.scheme = nil
+ self.authority = nil
+ end
+ end
+
+ ##
+ # The path component for this URI.
+ #
+ # @return [String] The path component.
+ def path
+ return defined?(@path) ? @path : EMPTY_STR
+ end
+
+ NORMPATH = /^(?!\/)[^\/:]*:.*$/
+ ##
+ # The path component for this URI, normalized.
+ #
+ # @return [String] The path component, normalized.
+ def normalized_path
+ @normalized_path ||= begin
+ path = self.path.to_s
+ if self.scheme == nil && path =~ NORMPATH
+ # Relative paths with colons in the first segment are ambiguous.
+ path = path.sub(":", "%2F")
+ end
+ # String#split(delimeter, -1) uses the more strict splitting behavior
+ # found by default in Python.
+ result = path.strip.split(SLASH, -1).map do |segment|
+ Addressable::URI.normalize_component(
+ segment,
+ Addressable::URI::NormalizeCharacterClasses::PCHAR
+ )
+ end.join(SLASH)
+
+ result = URI.normalize_path(result)
+ if result.empty? &&
+ ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
+ result = SLASH.dup
+ end
+ result
+ end
+ # All normalized values should be UTF-8
+ @normalized_path.force_encoding(Encoding::UTF_8) if @normalized_path
+ @normalized_path
+ end
+
+ ##
+ # Sets the path component for this URI.
+ #
+ # @param [String, #to_str] new_path The new path component.
+ def path=(new_path)
+ if new_path && !new_path.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_path.class} into String."
+ end
+ @path = (new_path || EMPTY_STR).to_str
+ if !@path.empty? && @path[0..0] != SLASH && host != nil
+ @path = "/#{@path}"
+ end
+
+ # Reset dependent values
+ remove_instance_variable(:@normalized_path) if defined?(@normalized_path)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # The basename, if any, of the file in the path component.
+ #
+ # @return [String] The path's basename.
+ def basename
+ # Path cannot be nil
+ return File.basename(self.path).sub(/;[^\/]*$/, EMPTY_STR)
+ end
+
+ ##
+ # The extname, if any, of the file in the path component.
+ # Empty string if there is no extension.
+ #
+ # @return [String] The path's extname.
+ def extname
+ return nil unless self.path
+ return File.extname(self.basename)
+ end
+
+ ##
+ # The query component for this URI.
+ #
+ # @return [String] The query component.
+ def query
+ return defined?(@query) ? @query : nil
+ end
+
+ ##
+ # The query component for this URI, normalized.
+ #
+ # @return [String] The query component, normalized.
+ def normalized_query(*flags)
+ return nil unless self.query
+ return @normalized_query if defined?(@normalized_query)
+ @normalized_query ||= begin
+ modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
+ # Make sure possible key-value pair delimiters are escaped.
+ modified_query_class.sub!("\\&", "").sub!("\\;", "")
+ pairs = (query || "").split("&", -1)
+ pairs.delete_if(&:empty?).uniq! if flags.include?(:compacted)
+ pairs.sort! if flags.include?(:sorted)
+ component = pairs.map do |pair|
+ Addressable::URI.normalize_component(
+ pair,
+ Addressable::URI::NormalizeCharacterClasses::QUERY,
+ "+"
+ )
+ end.join("&")
+ component == "" ? nil : component
+ end
+ # All normalized values should be UTF-8
+ @normalized_query.force_encoding(Encoding::UTF_8) if @normalized_query
+ @normalized_query
+ end
+
+ ##
+ # Sets the query component for this URI.
+ #
+ # @param [String, #to_str] new_query The new query component.
+ def query=(new_query)
+ if new_query && !new_query.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_query.class} into String."
+ end
+ @query = new_query ? new_query.to_str : nil
+
+ # Reset dependent values
+ remove_instance_variable(:@normalized_query) if defined?(@normalized_query)
+ remove_composite_values
+ end
+
+ ##
+ # Converts the query component to a Hash value.
+ #
+ # @param [Class] return_type The return type desired. Value must be either
+ # `Hash` or `Array`.
+ #
+ # @return [Hash, Array, nil] The query string parsed as a Hash or Array
+ # or nil if the query string is blank.
+ #
+ # @example
+ # Addressable::URI.parse("?one=1&two=2&three=3").query_values
+ # #=> {"one" => "1", "two" => "2", "three" => "3"}
+ # Addressable::URI.parse("?one=two&one=three").query_values(Array)
+ # #=> [["one", "two"], ["one", "three"]]
+ # Addressable::URI.parse("?one=two&one=three").query_values(Hash)
+ # #=> {"one" => "three"}
+ # Addressable::URI.parse("?").query_values
+ # #=> {}
+ # Addressable::URI.parse("").query_values
+ # #=> nil
+ def query_values(return_type=Hash)
+ empty_accumulator = Array == return_type ? [] : {}
+ if return_type != Hash && return_type != Array
+ raise ArgumentError, "Invalid return type. Must be Hash or Array."
+ end
+ return nil if self.query == nil
+ split_query = self.query.split("&").map do |pair|
+ pair.split("=", 2) if pair && !pair.empty?
+ end.compact
+ return split_query.inject(empty_accumulator.dup) do |accu, pair|
+ # I'd rather use key/value identifiers instead of array lookups,
+ # but in this case I really want to maintain the exact pair structure,
+ # so it's best to make all changes in-place.
+ pair[0] = URI.unencode_component(pair[0])
+ if pair[1].respond_to?(:to_str)
+ value = pair[1].to_str
+ # I loathe the fact that I have to do this. Stupid HTML 4.01.
+ # Treating '+' as a space was just an unbelievably bad idea.
+ # There was nothing wrong with '%20'!
+ # If it ain't broke, don't fix it!
+ value = value.tr("+", " ") if ["http", "https", nil].include?(scheme)
+ pair[1] = URI.unencode_component(value)
+ end
+ if return_type == Hash
+ accu[pair[0]] = pair[1]
+ else
+ accu << pair
+ end
+ accu
+ end
+ end
+
+ ##
+ # Sets the query component for this URI from a Hash object.
+ # An empty Hash or Array will result in an empty query string.
+ #
+ # @param [Hash, #to_hash, Array] new_query_values The new query values.
+ #
+ # @example
+ # uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
+ # uri.query
+ # # => "a=a&b=c&b=d&b=e"
+ # uri.query_values = [['a', 'a'], ['b', 'c'], ['b', 'd'], ['b', 'e']]
+ # uri.query
+ # # => "a=a&b=c&b=d&b=e"
+ # uri.query_values = [['a', 'a'], ['b', ['c', 'd', 'e']]]
+ # uri.query
+ # # => "a=a&b=c&b=d&b=e"
+ # uri.query_values = [['flag'], ['key', 'value']]
+ # uri.query
+ # # => "flag&key=value"
+ def query_values=(new_query_values)
+ if new_query_values == nil
+ self.query = nil
+ return nil
+ end
+
+ if !new_query_values.is_a?(Array)
+ if !new_query_values.respond_to?(:to_hash)
+ raise TypeError,
+ "Can't convert #{new_query_values.class} into Hash."
+ end
+ new_query_values = new_query_values.to_hash
+ new_query_values = new_query_values.map do |key, value|
+ key = key.to_s if key.kind_of?(Symbol)
+ [key, value]
+ end
+ # Useful default for OAuth and caching.
+ # Only to be used for non-Array inputs. Arrays should preserve order.
+ new_query_values.sort!
+ end
+
+ # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
+ buffer = "".dup
+ new_query_values.each do |key, value|
+ encoded_key = URI.encode_component(
+ key, CharacterClasses::UNRESERVED
+ )
+ if value == nil
+ buffer << "#{encoded_key}&"
+ elsif value.kind_of?(Array)
+ value.each do |sub_value|
+ encoded_value = URI.encode_component(
+ sub_value, CharacterClasses::UNRESERVED
+ )
+ buffer << "#{encoded_key}=#{encoded_value}&"
+ end
+ else
+ encoded_value = URI.encode_component(
+ value, CharacterClasses::UNRESERVED
+ )
+ buffer << "#{encoded_key}=#{encoded_value}&"
+ end
+ end
+ self.query = buffer.chop
+ end
+
+ ##
+ # The HTTP request URI for this URI. This is the path and the
+ # query string.
+ #
+ # @return [String] The request URI required for an HTTP request.
+ def request_uri
+ return nil if self.absolute? && self.scheme !~ /^https?$/i
+ return (
+ (!self.path.empty? ? self.path : SLASH) +
+ (self.query ? "?#{self.query}" : EMPTY_STR)
+ )
+ end
+
+ ##
+ # Sets the HTTP request URI for this URI.
+ #
+ # @param [String, #to_str] new_request_uri The new HTTP request URI.
+ def request_uri=(new_request_uri)
+ if !new_request_uri.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_request_uri.class} into String."
+ end
+ if self.absolute? && self.scheme !~ /^https?$/i
+ raise InvalidURIError,
+ "Cannot set an HTTP request URI for a non-HTTP URI."
+ end
+ new_request_uri = new_request_uri.to_str
+ path_component = new_request_uri[/^([^\?]*)\??(?:.*)$/, 1]
+ query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
+ path_component = path_component.to_s
+ path_component = (!path_component.empty? ? path_component : SLASH)
+ self.path = path_component
+ self.query = query_component
+
+ # Reset dependent values
+ remove_composite_values
+ end
+
+ ##
+ # The fragment component for this URI.
+ #
+ # @return [String] The fragment component.
+ def fragment
+ return defined?(@fragment) ? @fragment : nil
+ end
+
+ ##
+ # The fragment component for this URI, normalized.
+ #
+ # @return [String] The fragment component, normalized.
+ def normalized_fragment
+ return nil unless self.fragment
+ return @normalized_fragment if defined?(@normalized_fragment)
+ @normalized_fragment ||= begin
+ component = Addressable::URI.normalize_component(
+ self.fragment,
+ Addressable::URI::NormalizeCharacterClasses::FRAGMENT
+ )
+ component == "" ? nil : component
+ end
+ # All normalized values should be UTF-8
+ if @normalized_fragment
+ @normalized_fragment.force_encoding(Encoding::UTF_8)
+ end
+ @normalized_fragment
+ end
+
+ ##
+ # Sets the fragment component for this URI.
+ #
+ # @param [String, #to_str] new_fragment The new fragment component.
+ def fragment=(new_fragment)
+ if new_fragment && !new_fragment.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{new_fragment.class} into String."
+ end
+ @fragment = new_fragment ? new_fragment.to_str : nil
+
+ # Reset dependent values
+ remove_instance_variable(:@normalized_fragment) if defined?(@normalized_fragment)
+ remove_composite_values
+
+ # Ensure we haven't created an invalid URI
+ validate()
+ end
+
+ ##
+ # Determines if the scheme indicates an IP-based protocol.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the scheme indicates an IP-based protocol.
+ # false otherwise.
+ def ip_based?
+ if self.scheme
+ return URI.ip_based_schemes.include?(
+ self.scheme.strip.downcase)
+ end
+ return false
+ end
+
+ ##
+ # Determines if the URI is relative.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URI is relative. false
+ # otherwise.
+ def relative?
+ return self.scheme.nil?
+ end
+
+ ##
+ # Determines if the URI is absolute.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URI is absolute. false
+ # otherwise.
+ def absolute?
+ return !relative?
+ end
+
+ ##
+ # Joins two URIs together.
+ #
+ # @param [String, Addressable::URI, #to_str] The URI to join with.
+ #
+ # @return [Addressable::URI] The joined URI.
+ def join(uri)
+ if !uri.respond_to?(:to_str)
+ raise TypeError, "Can't convert #{uri.class} into String."
+ end
+ if !uri.kind_of?(URI)
+ # Otherwise, convert to a String, then parse.
+ uri = URI.parse(uri.to_str)
+ end
+ if uri.to_s.empty?
+ return self.dup
+ end
+
+ joined_scheme = nil
+ joined_user = nil
+ joined_password = nil
+ joined_host = nil
+ joined_port = nil
+ joined_path = nil
+ joined_query = nil
+ joined_fragment = nil
+
+ # Section 5.2.2 of RFC 3986
+ if uri.scheme != nil
+ joined_scheme = uri.scheme
+ joined_user = uri.user
+ joined_password = uri.password
+ joined_host = uri.host
+ joined_port = uri.port
+ joined_path = URI.normalize_path(uri.path)
+ joined_query = uri.query
+ else
+ if uri.authority != nil
+ joined_user = uri.user
+ joined_password = uri.password
+ joined_host = uri.host
+ joined_port = uri.port
+ joined_path = URI.normalize_path(uri.path)
+ joined_query = uri.query
+ else
+ if uri.path == nil || uri.path.empty?
+ joined_path = self.path
+ if uri.query != nil
+ joined_query = uri.query
+ else
+ joined_query = self.query
+ end
+ else
+ if uri.path[0..0] == SLASH
+ joined_path = URI.normalize_path(uri.path)
+ else
+ base_path = self.path.dup
+ base_path = EMPTY_STR if base_path == nil
+ base_path = URI.normalize_path(base_path)
+
+ # Section 5.2.3 of RFC 3986
+ #
+ # Removes the right-most path segment from the base path.
+ if base_path.include?(SLASH)
+ base_path.sub!(/\/[^\/]+$/, SLASH)
+ else
+ base_path = EMPTY_STR
+ end
+
+ # If the base path is empty and an authority segment has been
+ # defined, use a base path of SLASH
+ if base_path.empty? && self.authority != nil
+ base_path = SLASH
+ end
+
+ joined_path = URI.normalize_path(base_path + uri.path)
+ end
+ joined_query = uri.query
+ end
+ joined_user = self.user
+ joined_password = self.password
+ joined_host = self.host
+ joined_port = self.port
+ end
+ joined_scheme = self.scheme
+ end
+ joined_fragment = uri.fragment
+
+ return self.class.new(
+ :scheme => joined_scheme,
+ :user => joined_user,
+ :password => joined_password,
+ :host => joined_host,
+ :port => joined_port,
+ :path => joined_path,
+ :query => joined_query,
+ :fragment => joined_fragment
+ )
+ end
+ alias_method :+, :join
+
+ ##
+ # Destructive form of join.
+ #
+ # @param [String, Addressable::URI, #to_str] The URI to join with.
+ #
+ # @return [Addressable::URI] The joined URI.
+ #
+ # @see Addressable::URI#join
+ def join!(uri)
+ replace_self(self.join(uri))
+ end
+
+ ##
+ # Merges a URI with a Hash of components.
+ # This method has different behavior from join. Any
+ # components present in the hash parameter will override the
+ # original components. The path component is not treated specially.
+ #
+ # @param [Hash, Addressable::URI, #to_hash] The components to merge with.
+ #
+ # @return [Addressable::URI] The merged URI.
+ #
+ # @see Hash#merge
+ def merge(hash)
+ if !hash.respond_to?(:to_hash)
+ raise TypeError, "Can't convert #{hash.class} into Hash."
+ end
+ hash = hash.to_hash
+
+ if hash.has_key?(:authority)
+ if (hash.keys & [:userinfo, :user, :password, :host, :port]).any?
+ raise ArgumentError,
+ "Cannot specify both an authority and any of the components " +
+ "within the authority."
+ end
+ end
+ if hash.has_key?(:userinfo)
+ if (hash.keys & [:user, :password]).any?
+ raise ArgumentError,
+ "Cannot specify both a userinfo and either the user or password."
+ end
+ end
+
+ uri = self.class.new
+ uri.defer_validation do
+ # Bunch of crazy logic required because of the composite components
+ # like userinfo and authority.
+ uri.scheme =
+ hash.has_key?(:scheme) ? hash[:scheme] : self.scheme
+ if hash.has_key?(:authority)
+ uri.authority =
+ hash.has_key?(:authority) ? hash[:authority] : self.authority
+ end
+ if hash.has_key?(:userinfo)
+ uri.userinfo =
+ hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo
+ end
+ if !hash.has_key?(:userinfo) && !hash.has_key?(:authority)
+ uri.user =
+ hash.has_key?(:user) ? hash[:user] : self.user
+ uri.password =
+ hash.has_key?(:password) ? hash[:password] : self.password
+ end
+ if !hash.has_key?(:authority)
+ uri.host =
+ hash.has_key?(:host) ? hash[:host] : self.host
+ uri.port =
+ hash.has_key?(:port) ? hash[:port] : self.port
+ end
+ uri.path =
+ hash.has_key?(:path) ? hash[:path] : self.path
+ uri.query =
+ hash.has_key?(:query) ? hash[:query] : self.query
+ uri.fragment =
+ hash.has_key?(:fragment) ? hash[:fragment] : self.fragment
+ end
+
+ return uri
+ end
+
+ ##
+ # Destructive form of merge.
+ #
+ # @param [Hash, Addressable::URI, #to_hash] The components to merge with.
+ #
+ # @return [Addressable::URI] The merged URI.
+ #
+ # @see Addressable::URI#merge
+ def merge!(uri)
+ replace_self(self.merge(uri))
+ end
+
+ ##
+ # Returns the shortest normalized relative form of this URI that uses the
+ # supplied URI as a base for resolution. Returns an absolute URI if
+ # necessary. This is effectively the opposite of route_to.
+ #
+ # @param [String, Addressable::URI, #to_str] uri The URI to route from.
+ #
+ # @return [Addressable::URI]
+ # The normalized relative URI that is equivalent to the original URI.
+ def route_from(uri)
+ uri = URI.parse(uri).normalize
+ normalized_self = self.normalize
+ if normalized_self.relative?
+ raise ArgumentError, "Expected absolute URI, got: #{self.to_s}"
+ end
+ if uri.relative?
+ raise ArgumentError, "Expected absolute URI, got: #{uri.to_s}"
+ end
+ if normalized_self == uri
+ return Addressable::URI.parse("##{normalized_self.fragment}")
+ end
+ components = normalized_self.to_hash
+ if normalized_self.scheme == uri.scheme
+ components[:scheme] = nil
+ if normalized_self.authority == uri.authority
+ components[:user] = nil
+ components[:password] = nil
+ components[:host] = nil
+ components[:port] = nil
+ if normalized_self.path == uri.path
+ components[:path] = nil
+ if normalized_self.query == uri.query
+ components[:query] = nil
+ end
+ else
+ if uri.path != SLASH and components[:path]
+ self_splitted_path = split_path(components[:path])
+ uri_splitted_path = split_path(uri.path)
+ self_dir = self_splitted_path.shift
+ uri_dir = uri_splitted_path.shift
+ while !self_splitted_path.empty? && !uri_splitted_path.empty? and self_dir == uri_dir
+ self_dir = self_splitted_path.shift
+ uri_dir = uri_splitted_path.shift
+ end
+ components[:path] = (uri_splitted_path.fill('..') + [self_dir] + self_splitted_path).join(SLASH)
+ end
+ end
+ end
+ end
+ # Avoid network-path references.
+ if components[:host] != nil
+ components[:scheme] = normalized_self.scheme
+ end
+ return Addressable::URI.new(
+ :scheme => components[:scheme],
+ :user => components[:user],
+ :password => components[:password],
+ :host => components[:host],
+ :port => components[:port],
+ :path => components[:path],
+ :query => components[:query],
+ :fragment => components[:fragment]
+ )
+ end
+
+ ##
+ # Returns the shortest normalized relative form of the supplied URI that
+ # uses this URI as a base for resolution. Returns an absolute URI if
+ # necessary. This is effectively the opposite of route_from.
+ #
+ # @param [String, Addressable::URI, #to_str] uri The URI to route to.
+ #
+ # @return [Addressable::URI]
+ # The normalized relative URI that is equivalent to the supplied URI.
+ def route_to(uri)
+ return URI.parse(uri).route_from(self)
+ end
+
+ ##
+ # Returns a normalized URI object.
+ #
+ # NOTE: This method does not attempt to fully conform to specifications.
+ # It exists largely to correct other people's failures to read the
+ # specifications, and also to deal with caching issues since several
+ # different URIs may represent the same resource and should not be
+ # cached multiple times.
+ #
+ # @return [Addressable::URI] The normalized URI.
+ def normalize
+ # This is a special exception for the frequently misused feed
+ # URI scheme.
+ if normalized_scheme == "feed"
+ if self.to_s =~ /^feed:\/*http:\/*/
+ return URI.parse(
+ self.to_s[/^feed:\/*(http:\/*.*)/, 1]
+ ).normalize
+ end
+ end
+
+ return self.class.new(
+ :scheme => normalized_scheme,
+ :authority => normalized_authority,
+ :path => normalized_path,
+ :query => normalized_query,
+ :fragment => normalized_fragment
+ )
+ end
+
+ ##
+ # Destructively normalizes this URI object.
+ #
+ # @return [Addressable::URI] The normalized URI.
+ #
+ # @see Addressable::URI#normalize
+ def normalize!
+ replace_self(self.normalize)
+ end
+
+ ##
+ # Creates a URI suitable for display to users. If semantic attacks are
+ # likely, the application should try to detect these and warn the user.
+ # See RFC 3986,
+ # section 7.6 for more information.
+ #
+ # @return [Addressable::URI] A URI suitable for display purposes.
+ def display_uri
+ display_uri = self.normalize
+ display_uri.host = ::Addressable::IDNA.to_unicode(display_uri.host)
+ return display_uri
+ end
+
+ ##
+ # Returns true if the URI objects are equal. This method
+ # normalizes both URIs before doing the comparison, and allows comparison
+ # against Strings.
+ #
+ # @param [Object] uri The URI to compare.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URIs are equivalent, false
+ # otherwise.
+ def ===(uri)
+ if uri.respond_to?(:normalize)
+ uri_string = uri.normalize.to_s
+ else
+ begin
+ uri_string = ::Addressable::URI.parse(uri).normalize.to_s
+ rescue InvalidURIError, TypeError
+ return false
+ end
+ end
+ return self.normalize.to_s == uri_string
+ end
+
+ ##
+ # Returns true if the URI objects are equal. This method
+ # normalizes both URIs before doing the comparison.
+ #
+ # @param [Object] uri The URI to compare.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URIs are equivalent, false
+ # otherwise.
+ def ==(uri)
+ return false unless uri.kind_of?(URI)
+ return self.normalize.to_s == uri.normalize.to_s
+ end
+
+ ##
+ # Returns true if the URI objects are equal. This method
+ # does NOT normalize either URI before doing the comparison.
+ #
+ # @param [Object] uri The URI to compare.
+ #
+ # @return [TrueClass, FalseClass]
+ # true if the URIs are equivalent, false
+ # otherwise.
+ def eql?(uri)
+ return false unless uri.kind_of?(URI)
+ return self.to_s == uri.to_s
+ end
+
+ ##
+ # A hash value that will make a URI equivalent to its normalized
+ # form.
+ #
+ # @return [Integer] A hash of the URI.
+ def hash
+ @hash ||= self.to_s.hash * -1
+ end
+
+ ##
+ # Clones the URI object.
+ #
+ # @return [Addressable::URI] The cloned URI.
+ def dup
+ duplicated_uri = self.class.new(
+ :scheme => self.scheme ? self.scheme.dup : nil,
+ :user => self.user ? self.user.dup : nil,
+ :password => self.password ? self.password.dup : nil,
+ :host => self.host ? self.host.dup : nil,
+ :port => self.port,
+ :path => self.path ? self.path.dup : nil,
+ :query => self.query ? self.query.dup : nil,
+ :fragment => self.fragment ? self.fragment.dup : nil
+ )
+ return duplicated_uri
+ end
+
+ ##
+ # Omits components from a URI.
+ #
+ # @param [Symbol] *components The components to be omitted.
+ #
+ # @return [Addressable::URI] The URI with components omitted.
+ #
+ # @example
+ # uri = Addressable::URI.parse("http://example.com/path?query")
+ # #=> #
+ # uri.omit(:scheme, :authority)
+ # #=> #
+ def omit(*components)
+ invalid_components = components - [
+ :scheme, :user, :password, :userinfo, :host, :port, :authority,
+ :path, :query, :fragment
+ ]
+ unless invalid_components.empty?
+ raise ArgumentError,
+ "Invalid component names: #{invalid_components.inspect}."
+ end
+ duplicated_uri = self.dup
+ duplicated_uri.defer_validation do
+ components.each do |component|
+ duplicated_uri.send((component.to_s + "=").to_sym, nil)
+ end
+ duplicated_uri.user = duplicated_uri.normalized_user
+ end
+ duplicated_uri
+ end
+
+ ##
+ # Destructive form of omit.
+ #
+ # @param [Symbol] *components The components to be omitted.
+ #
+ # @return [Addressable::URI] The URI with components omitted.
+ #
+ # @see Addressable::URI#omit
+ def omit!(*components)
+ replace_self(self.omit(*components))
+ end
+
+ ##
+ # Determines if the URI is an empty string.
+ #
+ # @return [TrueClass, FalseClass]
+ # Returns true if empty, false otherwise.
+ def empty?
+ return self.to_s.empty?
+ end
+
+ ##
+ # Converts the URI to a String.
+ #
+ # @return [String] The URI's String representation.
+ def to_s
+ if self.scheme == nil && self.path != nil && !self.path.empty? &&
+ self.path =~ NORMPATH
+ raise InvalidURIError,
+ "Cannot assemble URI string with ambiguous path: '#{self.path}'"
+ end
+ @uri_string ||= begin
+ uri_string = String.new
+ uri_string << "#{self.scheme}:" if self.scheme != nil
+ uri_string << "//#{self.authority}" if self.authority != nil
+ uri_string << self.path.to_s
+ uri_string << "?#{self.query}" if self.query != nil
+ uri_string << "##{self.fragment}" if self.fragment != nil
+ uri_string.force_encoding(Encoding::UTF_8)
+ uri_string
+ end
+ end
+
+ ##
+ # URI's are glorified Strings. Allow implicit conversion.
+ alias_method :to_str, :to_s
+
+ ##
+ # Returns a Hash of the URI components.
+ #
+ # @return [Hash] The URI as a Hash of components.
+ def to_hash
+ return {
+ :scheme => self.scheme,
+ :user => self.user,
+ :password => self.password,
+ :host => self.host,
+ :port => self.port,
+ :path => self.path,
+ :query => self.query,
+ :fragment => self.fragment
+ }
+ end
+
+ ##
+ # Returns a String representation of the URI object's state.
+ #
+ # @return [String] The URI object's state, as a String.
+ def inspect
+ sprintf("#<%s:%#0x URI:%s>", URI.to_s, self.object_id, self.to_s)
+ end
+
+ ##
+ # This method allows you to make several changes to a URI simultaneously,
+ # which separately would cause validation errors, but in conjunction,
+ # are valid. The URI will be revalidated as soon as the entire block has
+ # been executed.
+ #
+ # @param [Proc] block
+ # A set of operations to perform on a given URI.
+ def defer_validation
+ raise LocalJumpError, "No block given." unless block_given?
+ @validation_deferred = true
+ yield
+ @validation_deferred = false
+ validate
+ return nil
+ end
+
+ protected
+ SELF_REF = '.'
+ PARENT = '..'
+
+ RULE_2A = /\/\.\/|\/\.$/
+ RULE_2B_2C = /\/([^\/]*)\/\.\.\/|\/([^\/]*)\/\.\.$/
+ RULE_2D = /^\.\.?\/?/
+ RULE_PREFIXED_PARENT = /^\/\.\.?\/|^(\/\.\.?)+\/?$/
+
+ ##
+ # Resolves paths to their simplest form.
+ #
+ # @param [String] path The path to normalize.
+ #
+ # @return [String] The normalized path.
+ def self.normalize_path(path)
+ # Section 5.2.4 of RFC 3986
+
+ return nil if path.nil?
+ normalized_path = path.dup
+ begin
+ mod = nil
+ mod ||= normalized_path.gsub!(RULE_2A, SLASH)
+
+ pair = normalized_path.match(RULE_2B_2C)
+ parent, current = pair[1], pair[2] if pair
+ if pair && ((parent != SELF_REF && parent != PARENT) ||
+ (current != SELF_REF && current != PARENT))
+ mod ||= normalized_path.gsub!(
+ Regexp.new(
+ "/#{Regexp.escape(parent.to_s)}/\\.\\./|" +
+ "(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
+ ), SLASH
+ )
+ end
+
+ mod ||= normalized_path.gsub!(RULE_2D, EMPTY_STR)
+ # Non-standard, removes prefixed dotted segments from path.
+ mod ||= normalized_path.gsub!(RULE_PREFIXED_PARENT, SLASH)
+ end until mod.nil?
+
+ return normalized_path
+ end
+
+ ##
+ # Ensures that the URI is valid.
+ def validate
+ return if !!@validation_deferred
+ if self.scheme != nil && self.ip_based? &&
+ (self.host == nil || self.host.empty?) &&
+ (self.path == nil || self.path.empty?)
+ raise InvalidURIError,
+ "Absolute URI missing hierarchical segment: '#{self.to_s}'"
+ end
+ if self.host == nil
+ if self.port != nil ||
+ self.user != nil ||
+ self.password != nil
+ raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'"
+ end
+ end
+ if self.path != nil && !self.path.empty? && self.path[0..0] != SLASH &&
+ self.authority != nil
+ raise InvalidURIError,
+ "Cannot have a relative path with an authority set: '#{self.to_s}'"
+ end
+ if self.path != nil && !self.path.empty? &&
+ self.path[0..1] == SLASH + SLASH && self.authority == nil
+ raise InvalidURIError,
+ "Cannot have a path with two leading slashes " +
+ "without an authority set: '#{self.to_s}'"
+ end
+ unreserved = CharacterClasses::UNRESERVED
+ sub_delims = CharacterClasses::SUB_DELIMS
+ if !self.host.nil? && (self.host =~ /[<>{}\/\\\?\#\@"[[:space:]]]/ ||
+ (self.host[/^\[(.*)\]$/, 1] != nil && self.host[/^\[(.*)\]$/, 1] !~
+ Regexp.new("^[#{unreserved}#{sub_delims}:]*$")))
+ raise InvalidURIError, "Invalid character in host: '#{self.host.to_s}'"
+ end
+ return nil
+ end
+
+ ##
+ # Replaces the internal state of self with the specified URI's state.
+ # Used in destructive operations to avoid massive code repetition.
+ #
+ # @param [Addressable::URI] uri The URI to replace self with.
+ #
+ # @return [Addressable::URI] self.
+ def replace_self(uri)
+ # Reset dependent values
+ instance_variables.each do |var|
+ if instance_variable_defined?(var) && var != :@validation_deferred
+ remove_instance_variable(var)
+ end
+ end
+
+ @scheme = uri.scheme
+ @user = uri.user
+ @password = uri.password
+ @host = uri.host
+ @port = uri.port
+ @path = uri.path
+ @query = uri.query
+ @fragment = uri.fragment
+ return self
+ end
+
+ ##
+ # Splits path string with "/" (slash).
+ # It is considered that there is empty string after last slash when
+ # path ends with slash.
+ #
+ # @param [String] path The path to split.
+ #
+ # @return [Array] An array of parts of path.
+ def split_path(path)
+ splitted = path.split(SLASH)
+ splitted << EMPTY_STR if path.end_with? SLASH
+ splitted
+ end
+
+ ##
+ # Resets composite values for the entire URI
+ #
+ # @api private
+ def remove_composite_values
+ remove_instance_variable(:@uri_string) if defined?(@uri_string)
+ remove_instance_variable(:@hash) if defined?(@hash)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/version.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/version.rb
new file mode 100644
index 0000000..2efe434
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/lib/addressable/version.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+# encoding:utf-8
+#--
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#++
+
+
+# Used to prevent the class/module from being loaded more than once
+if !defined?(Addressable::VERSION)
+ module Addressable
+ module VERSION
+ MAJOR = 2
+ MINOR = 8
+ TINY = 0
+
+ STRING = [MAJOR, MINOR, TINY].join('.')
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/idna_spec.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/idna_spec.rb
new file mode 100644
index 0000000..4104b37
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/idna_spec.rb
@@ -0,0 +1,302 @@
+# frozen_string_literal: true
+
+# coding: utf-8
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+require "spec_helper"
+
+# Have to use RubyGems to load the idn gem.
+require "rubygems"
+
+require "addressable/idna"
+
+shared_examples_for "converting from unicode to ASCII" do
+ it "should convert 'www.google.com' correctly" do
+ expect(Addressable::IDNA.to_ascii("www.google.com")).to eq("www.google.com")
+ end
+
+ long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com'
+ it "should convert '#{long}' correctly" do
+ expect(Addressable::IDNA.to_ascii(long)).to eq(long)
+ end
+
+ it "should convert 'www.詹姆斯.com' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "www.詹姆斯.com"
+ )).to eq("www.xn--8ws00zhy3a.com")
+ end
+
+ it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do
+ "www.Iñtërnâtiônàlizætiøn.com"
+ expect(Addressable::IDNA.to_ascii(
+ "www.I\xC3\xB1t\xC3\xABrn\xC3\xA2ti\xC3\xB4" +
+ "n\xC3\xA0liz\xC3\xA6ti\xC3\xB8n.com"
+ )).to eq("www.xn--itrntinliztin-vdb0a5exd8ewcye.com")
+ end
+
+ it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "www.In\xCC\x83te\xCC\x88rna\xCC\x82tio\xCC\x82n" +
+ "a\xCC\x80liz\xC3\xA6ti\xC3\xB8n.com"
+ )).to eq("www.xn--itrntinliztin-vdb0a5exd8ewcye.com")
+ end
+
+ it "should convert " +
+ "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " +
+ "correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" +
+ "\201\252\343\201\214\343\201\204\343\202\217\343\201\221\343\201\256" +
+ "\343\202\217\343\201\213\343\202\211\343\201\252\343\201\204\343\201" +
+ "\251\343\202\201\343\201\204\343\202\223\343\202\201\343\201\204\343" +
+ "\201\256\343\202\211\343\201\271\343\202\213\343\201\276\343\201\240" +
+ "\343\201\252\343\201\214\343\201\217\343\201\227\343\201\252\343\201" +
+ "\204\343\201\250\343\201\237\343\202\212\343\201\252\343\201\204." +
+ "w3.mag.keio.ac.jp"
+ )).to eq(
+ "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" +
+ "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
+ )
+ end
+
+ it "should convert " +
+ "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " +
+ "correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" +
+ "\201\252\343\201\213\343\202\231\343\201\204\343\202\217\343\201\221" +
+ "\343\201\256\343\202\217\343\201\213\343\202\211\343\201\252\343\201" +
+ "\204\343\201\250\343\202\231\343\202\201\343\201\204\343\202\223\343" +
+ "\202\201\343\201\204\343\201\256\343\202\211\343\201\270\343\202\231" +
+ "\343\202\213\343\201\276\343\201\237\343\202\231\343\201\252\343\201" +
+ "\213\343\202\231\343\201\217\343\201\227\343\201\252\343\201\204\343" +
+ "\201\250\343\201\237\343\202\212\343\201\252\343\201\204." +
+ "w3.mag.keio.ac.jp"
+ )).to eq(
+ "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" +
+ "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
+ )
+ end
+
+ it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "点心和烤鸭.w3.mag.keio.ac.jp"
+ )).to eq("xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp")
+ end
+
+ it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "가각갂갃간갅갆갇갈갉힢힣.com"
+ )).to eq("xn--o39acdefghijk5883jma.com")
+ end
+
+ it "should convert " +
+ "'\347\242\274\346\250\231\346\272\226\350" +
+ "\220\254\345\234\213\347\242\274.com' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "\347\242\274\346\250\231\346\272\226\350" +
+ "\220\254\345\234\213\347\242\274.com"
+ )).to eq("xn--9cs565brid46mda086o.com")
+ end
+
+ it "should convert 'リ宠퐱〹.com' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "\357\276\230\345\256\240\355\220\261\343\200\271.com"
+ )).to eq("xn--eek174hoxfpr4k.com")
+ end
+
+ it "should convert 'リ宠퐱卄.com' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "\343\203\252\345\256\240\355\220\261\345\215\204.com"
+ )).to eq("xn--eek174hoxfpr4k.com")
+ end
+
+ it "should convert 'ᆵ' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "\341\206\265"
+ )).to eq("xn--4ud")
+ end
+
+ it "should convert 'ᆵ' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "\357\276\257"
+ )).to eq("xn--4ud")
+ end
+
+ it "should convert '🌹🌹🌹.ws' correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "\360\237\214\271\360\237\214\271\360\237\214\271.ws"
+ )).to eq("xn--2h8haa.ws")
+ end
+
+ it "should handle two adjacent '.'s correctly" do
+ expect(Addressable::IDNA.to_ascii(
+ "example..host"
+ )).to eq("example..host")
+ end
+end
+
+shared_examples_for "converting from ASCII to unicode" do
+ long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com'
+ it "should convert '#{long}' correctly" do
+ expect(Addressable::IDNA.to_unicode(long)).to eq(long)
+ end
+
+ it "should return the identity conversion when punycode decode fails" do
+ expect(Addressable::IDNA.to_unicode("xn--zckp1cyg1.sblo.jp")).to eq(
+ "xn--zckp1cyg1.sblo.jp")
+ end
+
+ it "should return the identity conversion when the ACE prefix has no suffix" do
+ expect(Addressable::IDNA.to_unicode("xn--...-")).to eq("xn--...-")
+ end
+
+ it "should convert 'www.google.com' correctly" do
+ expect(Addressable::IDNA.to_unicode("www.google.com")).to eq(
+ "www.google.com")
+ end
+
+ it "should convert 'www.詹姆斯.com' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "www.xn--8ws00zhy3a.com"
+ )).to eq("www.詹姆斯.com")
+ end
+
+ it "should convert '詹姆斯.com' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "xn--8ws00zhy3a.com"
+ )).to eq("詹姆斯.com")
+ end
+
+ it "should convert 'www.iñtërnâtiônàlizætiøn.com' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "www.xn--itrntinliztin-vdb0a5exd8ewcye.com"
+ )).to eq("www.iñtërnâtiônàlizætiøn.com")
+ end
+
+ it "should convert 'iñtërnâtiônàlizætiøn.com' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "xn--itrntinliztin-vdb0a5exd8ewcye.com"
+ )).to eq("iñtërnâtiônàlizætiøn.com")
+ end
+
+ it "should convert " +
+ "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " +
+ "correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" +
+ "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
+ )).to eq(
+ "www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp"
+ )
+ end
+
+ it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp"
+ )).to eq("点心和烤鸭.w3.mag.keio.ac.jp")
+ end
+
+ it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "xn--o39acdefghijk5883jma.com"
+ )).to eq("가각갂갃간갅갆갇갈갉힢힣.com")
+ end
+
+ it "should convert " +
+ "'\347\242\274\346\250\231\346\272\226\350" +
+ "\220\254\345\234\213\347\242\274.com' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "xn--9cs565brid46mda086o.com"
+ )).to eq(
+ "\347\242\274\346\250\231\346\272\226\350" +
+ "\220\254\345\234\213\347\242\274.com"
+ )
+ end
+
+ it "should convert 'リ宠퐱卄.com' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "xn--eek174hoxfpr4k.com"
+ )).to eq("\343\203\252\345\256\240\355\220\261\345\215\204.com")
+ end
+
+ it "should convert 'ᆵ' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "xn--4ud"
+ )).to eq("\341\206\265")
+ end
+
+ it "should convert '🌹🌹🌹.ws' correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "xn--2h8haa.ws"
+ )).to eq("\360\237\214\271\360\237\214\271\360\237\214\271.ws")
+ end
+
+ it "should handle two adjacent '.'s correctly" do
+ expect(Addressable::IDNA.to_unicode(
+ "example..host"
+ )).to eq("example..host")
+ end
+
+ it "should normalize 'string' correctly" do
+ expect(Addressable::IDNA.unicode_normalize_kc(:'string')).to eq("string")
+ expect(Addressable::IDNA.unicode_normalize_kc("string")).to eq("string")
+ end
+end
+
+describe Addressable::IDNA, "when using the pure-Ruby implementation" do
+ before do
+ Addressable.send(:remove_const, :IDNA)
+ load "addressable/idna/pure.rb"
+ end
+
+ it_should_behave_like "converting from unicode to ASCII"
+ it_should_behave_like "converting from ASCII to unicode"
+
+ begin
+ require "fiber"
+
+ it "should not blow up inside fibers" do
+ f = Fiber.new do
+ Addressable.send(:remove_const, :IDNA)
+ load "addressable/idna/pure.rb"
+ end
+ f.resume
+ end
+ rescue LoadError
+ # Fibers aren't supported in this version of Ruby, skip this test.
+ warn('Fibers unsupported.')
+ end
+end
+
+begin
+ require "idn"
+
+ describe Addressable::IDNA, "when using the native-code implementation" do
+ before do
+ Addressable.send(:remove_const, :IDNA)
+ load "addressable/idna/native.rb"
+ end
+
+ it_should_behave_like "converting from unicode to ASCII"
+ it_should_behave_like "converting from ASCII to unicode"
+ end
+rescue LoadError => error
+ raise error if ENV["CI"] && TestHelper.native_supported?
+
+ # Cannot test the native implementation without libidn support.
+ warn('Could not load native IDN implementation.')
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/net_http_compat_spec.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/net_http_compat_spec.rb
new file mode 100644
index 0000000..8663a86
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/net_http_compat_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+# coding: utf-8
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+require "spec_helper"
+
+require "addressable/uri"
+require "net/http"
+
+describe Net::HTTP do
+ it "should be compatible with Addressable" do
+ response_body =
+ Net::HTTP.get(Addressable::URI.parse('http://www.google.com/'))
+ expect(response_body).not_to be_nil
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/security_spec.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/security_spec.rb
new file mode 100644
index 0000000..601e808
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/security_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+# coding: utf-8
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+require "spec_helper"
+
+require "addressable/uri"
+
+describe Addressable::URI, "when created with a URI known to cause crashes " +
+ "in certain browsers" do
+ it "should parse correctly" do
+ uri = Addressable::URI.parse('%%30%30')
+ expect(uri.path).to eq('%%30%30')
+ expect(uri.normalize.path).to eq('%2500')
+ end
+
+ it "should parse correctly as a full URI" do
+ uri = Addressable::URI.parse('http://www.example.com/%%30%30')
+ expect(uri.path).to eq('/%%30%30')
+ expect(uri.normalize.path).to eq('/%2500')
+ end
+end
+
+describe Addressable::URI, "when created with a URI known to cause crashes " +
+ "in certain browsers" do
+ it "should parse correctly" do
+ uri = Addressable::URI.parse('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
+ expect(uri.path).to eq('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
+ expect(uri.normalize.path).to eq(
+ '%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' +
+ '%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' +
+ '%A3%20%E0%A5%A3%20%E5%86%97'
+ )
+ end
+
+ it "should parse correctly as a full URI" do
+ uri = Addressable::URI.parse('http://www.example.com/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
+ expect(uri.path).to eq('/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
+ expect(uri.normalize.path).to eq(
+ '/%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' +
+ '%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' +
+ '%A3%20%E0%A5%A3%20%E5%86%97'
+ )
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/template_spec.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/template_spec.rb
new file mode 100644
index 0000000..d47589a
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/template_spec.rb
@@ -0,0 +1,1460 @@
+# frozen_string_literal: true
+
+# coding: utf-8
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+require "spec_helper"
+
+require "bigdecimal"
+require "timeout"
+require "addressable/template"
+
+shared_examples_for 'expands' do |tests|
+ tests.each do |template, expansion|
+ exp = expansion.is_a?(Array) ? expansion.first : expansion
+ it "#{template} to #{exp}" do
+ tmpl = Addressable::Template.new(template).expand(subject)
+ if expansion.is_a?(Array)
+ expect(expansion.any?{|i| i == tmpl.to_str}).to be true
+ else
+ expect(tmpl.to_str).to eq(expansion)
+ end
+ end
+ end
+end
+
+describe "eql?" do
+ let(:template) { Addressable::Template.new('https://www.example.com/{foo}') }
+ it 'is equal when the pattern matches' do
+ other_template = Addressable::Template.new('https://www.example.com/{foo}')
+ expect(template).to be_eql(other_template)
+ expect(other_template).to be_eql(template)
+ end
+ it 'is not equal when the pattern differs' do
+ other_template = Addressable::Template.new('https://www.example.com/{bar}')
+ expect(template).to_not be_eql(other_template)
+ expect(other_template).to_not be_eql(template)
+ end
+ it 'is not equal to non-templates' do
+ uri = 'https://www.example.com/foo/bar'
+ addressable_template = Addressable::Template.new uri
+ addressable_uri = Addressable::URI.parse uri
+ expect(addressable_template).to_not be_eql(addressable_uri)
+ expect(addressable_uri).to_not be_eql(addressable_template)
+ end
+end
+
+describe "==" do
+ let(:template) { Addressable::Template.new('https://www.example.com/{foo}') }
+ it 'is equal when the pattern matches' do
+ other_template = Addressable::Template.new('https://www.example.com/{foo}')
+ expect(template).to eq other_template
+ expect(other_template).to eq template
+ end
+ it 'is not equal when the pattern differs' do
+ other_template = Addressable::Template.new('https://www.example.com/{bar}')
+ expect(template).not_to eq other_template
+ expect(other_template).not_to eq template
+ end
+ it 'is not equal to non-templates' do
+ uri = 'https://www.example.com/foo/bar'
+ addressable_template = Addressable::Template.new uri
+ addressable_uri = Addressable::URI.parse uri
+ expect(addressable_template).not_to eq addressable_uri
+ expect(addressable_uri).not_to eq addressable_template
+ end
+end
+
+describe "Type conversion" do
+ subject {
+ {
+ :var => true,
+ :hello => 1234,
+ :nothing => nil,
+ :sym => :symbolic,
+ :decimal => BigDecimal('1')
+ }
+ }
+
+ it_behaves_like 'expands', {
+ '{var}' => 'true',
+ '{hello}' => '1234',
+ '{nothing}' => '',
+ '{sym}' => 'symbolic',
+ '{decimal}' => RUBY_VERSION < '2.4.0' ? '0.1E1' : '0.1e1'
+ }
+end
+
+describe "Level 1:" do
+ subject {
+ {:var => "value", :hello => "Hello World!"}
+ }
+ it_behaves_like 'expands', {
+ '{var}' => 'value',
+ '{hello}' => 'Hello%20World%21'
+ }
+end
+
+describe "Level 2" do
+ subject {
+ {
+ :var => "value",
+ :hello => "Hello World!",
+ :path => "/foo/bar"
+ }
+ }
+ context "Operator +:" do
+ it_behaves_like 'expands', {
+ '{+var}' => 'value',
+ '{+hello}' => 'Hello%20World!',
+ '{+path}/here' => '/foo/bar/here',
+ 'here?ref={+path}' => 'here?ref=/foo/bar'
+ }
+ end
+ context "Operator #:" do
+ it_behaves_like 'expands', {
+ 'X{#var}' => 'X#value',
+ 'X{#hello}' => 'X#Hello%20World!'
+ }
+ end
+end
+
+describe "Level 3" do
+ subject {
+ {
+ :var => "value",
+ :hello => "Hello World!",
+ :empty => "",
+ :path => "/foo/bar",
+ :x => "1024",
+ :y => "768"
+ }
+ }
+ context "Operator nil (multiple vars):" do
+ it_behaves_like 'expands', {
+ 'map?{x,y}' => 'map?1024,768',
+ '{x,hello,y}' => '1024,Hello%20World%21,768'
+ }
+ end
+ context "Operator + (multiple vars):" do
+ it_behaves_like 'expands', {
+ '{+x,hello,y}' => '1024,Hello%20World!,768',
+ '{+path,x}/here' => '/foo/bar,1024/here'
+ }
+ end
+ context "Operator # (multiple vars):" do
+ it_behaves_like 'expands', {
+ '{#x,hello,y}' => '#1024,Hello%20World!,768',
+ '{#path,x}/here' => '#/foo/bar,1024/here'
+ }
+ end
+ context "Operator ." do
+ it_behaves_like 'expands', {
+ 'X{.var}' => 'X.value',
+ 'X{.x,y}' => 'X.1024.768'
+ }
+ end
+ context "Operator /" do
+ it_behaves_like 'expands', {
+ '{/var}' => '/value',
+ '{/var,x}/here' => '/value/1024/here'
+ }
+ end
+ context "Operator ;" do
+ it_behaves_like 'expands', {
+ '{;x,y}' => ';x=1024;y=768',
+ '{;x,y,empty}' => ';x=1024;y=768;empty'
+ }
+ end
+ context "Operator ?" do
+ it_behaves_like 'expands', {
+ '{?x,y}' => '?x=1024&y=768',
+ '{?x,y,empty}' => '?x=1024&y=768&empty='
+ }
+ end
+ context "Operator &" do
+ it_behaves_like 'expands', {
+ '?fixed=yes{&x}' => '?fixed=yes&x=1024',
+ '{&x,y,empty}' => '&x=1024&y=768&empty='
+ }
+ end
+end
+
+describe "Level 4" do
+ subject {
+ {
+ :var => "value",
+ :hello => "Hello World!",
+ :path => "/foo/bar",
+ :semi => ";",
+ :list => %w(red green blue),
+ :keys => {"semi" => ';', "dot" => '.', "comma" => ','}
+ }
+ }
+ context "Expansion with value modifiers" do
+ it_behaves_like 'expands', {
+ '{var:3}' => 'val',
+ '{var:30}' => 'value',
+ '{list}' => 'red,green,blue',
+ '{list*}' => 'red,green,blue',
+ '{keys}' => [
+ 'semi,%3B,dot,.,comma,%2C',
+ 'dot,.,semi,%3B,comma,%2C',
+ 'comma,%2C,semi,%3B,dot,.',
+ 'semi,%3B,comma,%2C,dot,.',
+ 'dot,.,comma,%2C,semi,%3B',
+ 'comma,%2C,dot,.,semi,%3B'
+ ],
+ '{keys*}' => [
+ 'semi=%3B,dot=.,comma=%2C',
+ 'dot=.,semi=%3B,comma=%2C',
+ 'comma=%2C,semi=%3B,dot=.',
+ 'semi=%3B,comma=%2C,dot=.',
+ 'dot=.,comma=%2C,semi=%3B',
+ 'comma=%2C,dot=.,semi=%3B'
+ ]
+ }
+ end
+ context "Operator + with value modifiers" do
+ it_behaves_like 'expands', {
+ '{+path:6}/here' => '/foo/b/here',
+ '{+list}' => 'red,green,blue',
+ '{+list*}' => 'red,green,blue',
+ '{+keys}' => [
+ 'semi,;,dot,.,comma,,',
+ 'dot,.,semi,;,comma,,',
+ 'comma,,,semi,;,dot,.',
+ 'semi,;,comma,,,dot,.',
+ 'dot,.,comma,,,semi,;',
+ 'comma,,,dot,.,semi,;'
+ ],
+ '{+keys*}' => [
+ 'semi=;,dot=.,comma=,',
+ 'dot=.,semi=;,comma=,',
+ 'comma=,,semi=;,dot=.',
+ 'semi=;,comma=,,dot=.',
+ 'dot=.,comma=,,semi=;',
+ 'comma=,,dot=.,semi=;'
+ ]
+ }
+ end
+ context "Operator # with value modifiers" do
+ it_behaves_like 'expands', {
+ '{#path:6}/here' => '#/foo/b/here',
+ '{#list}' => '#red,green,blue',
+ '{#list*}' => '#red,green,blue',
+ '{#keys}' => [
+ '#semi,;,dot,.,comma,,',
+ '#dot,.,semi,;,comma,,',
+ '#comma,,,semi,;,dot,.',
+ '#semi,;,comma,,,dot,.',
+ '#dot,.,comma,,,semi,;',
+ '#comma,,,dot,.,semi,;'
+ ],
+ '{#keys*}' => [
+ '#semi=;,dot=.,comma=,',
+ '#dot=.,semi=;,comma=,',
+ '#comma=,,semi=;,dot=.',
+ '#semi=;,comma=,,dot=.',
+ '#dot=.,comma=,,semi=;',
+ '#comma=,,dot=.,semi=;'
+ ]
+ }
+ end
+ context "Operator . with value modifiers" do
+ it_behaves_like 'expands', {
+ 'X{.var:3}' => 'X.val',
+ 'X{.list}' => 'X.red,green,blue',
+ 'X{.list*}' => 'X.red.green.blue',
+ 'X{.keys}' => [
+ 'X.semi,%3B,dot,.,comma,%2C',
+ 'X.dot,.,semi,%3B,comma,%2C',
+ 'X.comma,%2C,semi,%3B,dot,.',
+ 'X.semi,%3B,comma,%2C,dot,.',
+ 'X.dot,.,comma,%2C,semi,%3B',
+ 'X.comma,%2C,dot,.,semi,%3B'
+ ],
+ 'X{.keys*}' => [
+ 'X.semi=%3B.dot=..comma=%2C',
+ 'X.dot=..semi=%3B.comma=%2C',
+ 'X.comma=%2C.semi=%3B.dot=.',
+ 'X.semi=%3B.comma=%2C.dot=.',
+ 'X.dot=..comma=%2C.semi=%3B',
+ 'X.comma=%2C.dot=..semi=%3B'
+ ]
+ }
+ end
+ context "Operator / with value modifiers" do
+ it_behaves_like 'expands', {
+ '{/var:1,var}' => '/v/value',
+ '{/list}' => '/red,green,blue',
+ '{/list*}' => '/red/green/blue',
+ '{/list*,path:4}' => '/red/green/blue/%2Ffoo',
+ '{/keys}' => [
+ '/semi,%3B,dot,.,comma,%2C',
+ '/dot,.,semi,%3B,comma,%2C',
+ '/comma,%2C,semi,%3B,dot,.',
+ '/semi,%3B,comma,%2C,dot,.',
+ '/dot,.,comma,%2C,semi,%3B',
+ '/comma,%2C,dot,.,semi,%3B'
+ ],
+ '{/keys*}' => [
+ '/semi=%3B/dot=./comma=%2C',
+ '/dot=./semi=%3B/comma=%2C',
+ '/comma=%2C/semi=%3B/dot=.',
+ '/semi=%3B/comma=%2C/dot=.',
+ '/dot=./comma=%2C/semi=%3B',
+ '/comma=%2C/dot=./semi=%3B'
+ ]
+ }
+ end
+ context "Operator ; with value modifiers" do
+ it_behaves_like 'expands', {
+ '{;hello:5}' => ';hello=Hello',
+ '{;list}' => ';list=red,green,blue',
+ '{;list*}' => ';list=red;list=green;list=blue',
+ '{;keys}' => [
+ ';keys=semi,%3B,dot,.,comma,%2C',
+ ';keys=dot,.,semi,%3B,comma,%2C',
+ ';keys=comma,%2C,semi,%3B,dot,.',
+ ';keys=semi,%3B,comma,%2C,dot,.',
+ ';keys=dot,.,comma,%2C,semi,%3B',
+ ';keys=comma,%2C,dot,.,semi,%3B'
+ ],
+ '{;keys*}' => [
+ ';semi=%3B;dot=.;comma=%2C',
+ ';dot=.;semi=%3B;comma=%2C',
+ ';comma=%2C;semi=%3B;dot=.',
+ ';semi=%3B;comma=%2C;dot=.',
+ ';dot=.;comma=%2C;semi=%3B',
+ ';comma=%2C;dot=.;semi=%3B'
+ ]
+ }
+ end
+ context "Operator ? with value modifiers" do
+ it_behaves_like 'expands', {
+ '{?var:3}' => '?var=val',
+ '{?list}' => '?list=red,green,blue',
+ '{?list*}' => '?list=red&list=green&list=blue',
+ '{?keys}' => [
+ '?keys=semi,%3B,dot,.,comma,%2C',
+ '?keys=dot,.,semi,%3B,comma,%2C',
+ '?keys=comma,%2C,semi,%3B,dot,.',
+ '?keys=semi,%3B,comma,%2C,dot,.',
+ '?keys=dot,.,comma,%2C,semi,%3B',
+ '?keys=comma,%2C,dot,.,semi,%3B'
+ ],
+ '{?keys*}' => [
+ '?semi=%3B&dot=.&comma=%2C',
+ '?dot=.&semi=%3B&comma=%2C',
+ '?comma=%2C&semi=%3B&dot=.',
+ '?semi=%3B&comma=%2C&dot=.',
+ '?dot=.&comma=%2C&semi=%3B',
+ '?comma=%2C&dot=.&semi=%3B'
+ ]
+ }
+ end
+ context "Operator & with value modifiers" do
+ it_behaves_like 'expands', {
+ '{&var:3}' => '&var=val',
+ '{&list}' => '&list=red,green,blue',
+ '{&list*}' => '&list=red&list=green&list=blue',
+ '{&keys}' => [
+ '&keys=semi,%3B,dot,.,comma,%2C',
+ '&keys=dot,.,semi,%3B,comma,%2C',
+ '&keys=comma,%2C,semi,%3B,dot,.',
+ '&keys=semi,%3B,comma,%2C,dot,.',
+ '&keys=dot,.,comma,%2C,semi,%3B',
+ '&keys=comma,%2C,dot,.,semi,%3B'
+ ],
+ '{&keys*}' => [
+ '&semi=%3B&dot=.&comma=%2C',
+ '&dot=.&semi=%3B&comma=%2C',
+ '&comma=%2C&semi=%3B&dot=.',
+ '&semi=%3B&comma=%2C&dot=.',
+ '&dot=.&comma=%2C&semi=%3B',
+ '&comma=%2C&dot=.&semi=%3B'
+ ]
+ }
+ end
+end
+describe "Modifiers" do
+ subject {
+ {
+ :var => "value",
+ :semi => ";",
+ :year => %w(1965 2000 2012),
+ :dom => %w(example com)
+ }
+ }
+ context "length" do
+ it_behaves_like 'expands', {
+ '{var:3}' => 'val',
+ '{var:30}' => 'value',
+ '{var}' => 'value',
+ '{semi}' => '%3B',
+ '{semi:2}' => '%3B'
+ }
+ end
+ context "explode" do
+ it_behaves_like 'expands', {
+ 'find{?year*}' => 'find?year=1965&year=2000&year=2012',
+ 'www{.dom*}' => 'www.example.com',
+ }
+ end
+end
+describe "Expansion" do
+ subject {
+ {
+ :count => ["one", "two", "three"],
+ :dom => ["example", "com"],
+ :dub => "me/too",
+ :hello => "Hello World!",
+ :half => "50%",
+ :var => "value",
+ :who => "fred",
+ :base => "http://example.com/home/",
+ :path => "/foo/bar",
+ :list => ["red", "green", "blue"],
+ :keys => {"semi" => ";","dot" => ".","comma" => ","},
+ :v => "6",
+ :x => "1024",
+ :y => "768",
+ :empty => "",
+ :empty_keys => {},
+ :undef => nil
+ }
+ }
+ context "concatenation" do
+ it_behaves_like 'expands', {
+ '{count}' => 'one,two,three',
+ '{count*}' => 'one,two,three',
+ '{/count}' => '/one,two,three',
+ '{/count*}' => '/one/two/three',
+ '{;count}' => ';count=one,two,three',
+ '{;count*}' => ';count=one;count=two;count=three',
+ '{?count}' => '?count=one,two,three',
+ '{?count*}' => '?count=one&count=two&count=three',
+ '{&count*}' => '&count=one&count=two&count=three'
+ }
+ end
+ context "simple expansion" do
+ it_behaves_like 'expands', {
+ '{var}' => 'value',
+ '{hello}' => 'Hello%20World%21',
+ '{half}' => '50%25',
+ 'O{empty}X' => 'OX',
+ 'O{undef}X' => 'OX',
+ '{x,y}' => '1024,768',
+ '{x,hello,y}' => '1024,Hello%20World%21,768',
+ '?{x,empty}' => '?1024,',
+ '?{x,undef}' => '?1024',
+ '?{undef,y}' => '?768',
+ '{var:3}' => 'val',
+ '{var:30}' => 'value',
+ '{list}' => 'red,green,blue',
+ '{list*}' => 'red,green,blue',
+ '{keys}' => [
+ 'semi,%3B,dot,.,comma,%2C',
+ 'dot,.,semi,%3B,comma,%2C',
+ 'comma,%2C,semi,%3B,dot,.',
+ 'semi,%3B,comma,%2C,dot,.',
+ 'dot,.,comma,%2C,semi,%3B',
+ 'comma,%2C,dot,.,semi,%3B'
+ ],
+ '{keys*}' => [
+ 'semi=%3B,dot=.,comma=%2C',
+ 'dot=.,semi=%3B,comma=%2C',
+ 'comma=%2C,semi=%3B,dot=.',
+ 'semi=%3B,comma=%2C,dot=.',
+ 'dot=.,comma=%2C,semi=%3B',
+ 'comma=%2C,dot=.,semi=%3B'
+ ]
+ }
+ end
+ context "reserved expansion (+)" do
+ it_behaves_like 'expands', {
+ '{+var}' => 'value',
+ '{+hello}' => 'Hello%20World!',
+ '{+half}' => '50%25',
+ '{base}index' => 'http%3A%2F%2Fexample.com%2Fhome%2Findex',
+ '{+base}index' => 'http://example.com/home/index',
+ 'O{+empty}X' => 'OX',
+ 'O{+undef}X' => 'OX',
+ '{+path}/here' => '/foo/bar/here',
+ 'here?ref={+path}' => 'here?ref=/foo/bar',
+ 'up{+path}{var}/here' => 'up/foo/barvalue/here',
+ '{+x,hello,y}' => '1024,Hello%20World!,768',
+ '{+path,x}/here' => '/foo/bar,1024/here',
+ '{+path:6}/here' => '/foo/b/here',
+ '{+list}' => 'red,green,blue',
+ '{+list*}' => 'red,green,blue',
+ '{+keys}' => [
+ 'semi,;,dot,.,comma,,',
+ 'dot,.,semi,;,comma,,',
+ 'comma,,,semi,;,dot,.',
+ 'semi,;,comma,,,dot,.',
+ 'dot,.,comma,,,semi,;',
+ 'comma,,,dot,.,semi,;'
+ ],
+ '{+keys*}' => [
+ 'semi=;,dot=.,comma=,',
+ 'dot=.,semi=;,comma=,',
+ 'comma=,,semi=;,dot=.',
+ 'semi=;,comma=,,dot=.',
+ 'dot=.,comma=,,semi=;',
+ 'comma=,,dot=.,semi=;'
+ ]
+ }
+ end
+ context "fragment expansion (#)" do
+ it_behaves_like 'expands', {
+ '{#var}' => '#value',
+ '{#hello}' => '#Hello%20World!',
+ '{#half}' => '#50%25',
+ 'foo{#empty}' => 'foo#',
+ 'foo{#undef}' => 'foo',
+ '{#x,hello,y}' => '#1024,Hello%20World!,768',
+ '{#path,x}/here' => '#/foo/bar,1024/here',
+ '{#path:6}/here' => '#/foo/b/here',
+ '{#list}' => '#red,green,blue',
+ '{#list*}' => '#red,green,blue',
+ '{#keys}' => [
+ '#semi,;,dot,.,comma,,',
+ '#dot,.,semi,;,comma,,',
+ '#comma,,,semi,;,dot,.',
+ '#semi,;,comma,,,dot,.',
+ '#dot,.,comma,,,semi,;',
+ '#comma,,,dot,.,semi,;'
+ ],
+ '{#keys*}' => [
+ '#semi=;,dot=.,comma=,',
+ '#dot=.,semi=;,comma=,',
+ '#comma=,,semi=;,dot=.',
+ '#semi=;,comma=,,dot=.',
+ '#dot=.,comma=,,semi=;',
+ '#comma=,,dot=.,semi=;'
+ ]
+ }
+ end
+ context "label expansion (.)" do
+ it_behaves_like 'expands', {
+ '{.who}' => '.fred',
+ '{.who,who}' => '.fred.fred',
+ '{.half,who}' => '.50%25.fred',
+ 'www{.dom*}' => 'www.example.com',
+ 'X{.var}' => 'X.value',
+ 'X{.empty}' => 'X.',
+ 'X{.undef}' => 'X',
+ 'X{.var:3}' => 'X.val',
+ 'X{.list}' => 'X.red,green,blue',
+ 'X{.list*}' => 'X.red.green.blue',
+ 'X{.keys}' => [
+ 'X.semi,%3B,dot,.,comma,%2C',
+ 'X.dot,.,semi,%3B,comma,%2C',
+ 'X.comma,%2C,semi,%3B,dot,.',
+ 'X.semi,%3B,comma,%2C,dot,.',
+ 'X.dot,.,comma,%2C,semi,%3B',
+ 'X.comma,%2C,dot,.,semi,%3B'
+ ],
+ 'X{.keys*}' => [
+ 'X.semi=%3B.dot=..comma=%2C',
+ 'X.dot=..semi=%3B.comma=%2C',
+ 'X.comma=%2C.semi=%3B.dot=.',
+ 'X.semi=%3B.comma=%2C.dot=.',
+ 'X.dot=..comma=%2C.semi=%3B',
+ 'X.comma=%2C.dot=..semi=%3B'
+ ],
+ 'X{.empty_keys}' => 'X',
+ 'X{.empty_keys*}' => 'X'
+ }
+ end
+ context "path expansion (/)" do
+ it_behaves_like 'expands', {
+ '{/who}' => '/fred',
+ '{/who,who}' => '/fred/fred',
+ '{/half,who}' => '/50%25/fred',
+ '{/who,dub}' => '/fred/me%2Ftoo',
+ '{/var}' => '/value',
+ '{/var,empty}' => '/value/',
+ '{/var,undef}' => '/value',
+ '{/var,x}/here' => '/value/1024/here',
+ '{/var:1,var}' => '/v/value',
+ '{/list}' => '/red,green,blue',
+ '{/list*}' => '/red/green/blue',
+ '{/list*,path:4}' => '/red/green/blue/%2Ffoo',
+ '{/keys}' => [
+ '/semi,%3B,dot,.,comma,%2C',
+ '/dot,.,semi,%3B,comma,%2C',
+ '/comma,%2C,semi,%3B,dot,.',
+ '/semi,%3B,comma,%2C,dot,.',
+ '/dot,.,comma,%2C,semi,%3B',
+ '/comma,%2C,dot,.,semi,%3B'
+ ],
+ '{/keys*}' => [
+ '/semi=%3B/dot=./comma=%2C',
+ '/dot=./semi=%3B/comma=%2C',
+ '/comma=%2C/semi=%3B/dot=.',
+ '/semi=%3B/comma=%2C/dot=.',
+ '/dot=./comma=%2C/semi=%3B',
+ '/comma=%2C/dot=./semi=%3B'
+ ]
+ }
+ end
+ context "path-style expansion (;)" do
+ it_behaves_like 'expands', {
+ '{;who}' => ';who=fred',
+ '{;half}' => ';half=50%25',
+ '{;empty}' => ';empty',
+ '{;v,empty,who}' => ';v=6;empty;who=fred',
+ '{;v,bar,who}' => ';v=6;who=fred',
+ '{;x,y}' => ';x=1024;y=768',
+ '{;x,y,empty}' => ';x=1024;y=768;empty',
+ '{;x,y,undef}' => ';x=1024;y=768',
+ '{;hello:5}' => ';hello=Hello',
+ '{;list}' => ';list=red,green,blue',
+ '{;list*}' => ';list=red;list=green;list=blue',
+ '{;keys}' => [
+ ';keys=semi,%3B,dot,.,comma,%2C',
+ ';keys=dot,.,semi,%3B,comma,%2C',
+ ';keys=comma,%2C,semi,%3B,dot,.',
+ ';keys=semi,%3B,comma,%2C,dot,.',
+ ';keys=dot,.,comma,%2C,semi,%3B',
+ ';keys=comma,%2C,dot,.,semi,%3B'
+ ],
+ '{;keys*}' => [
+ ';semi=%3B;dot=.;comma=%2C',
+ ';dot=.;semi=%3B;comma=%2C',
+ ';comma=%2C;semi=%3B;dot=.',
+ ';semi=%3B;comma=%2C;dot=.',
+ ';dot=.;comma=%2C;semi=%3B',
+ ';comma=%2C;dot=.;semi=%3B'
+ ]
+ }
+ end
+ context "form query expansion (?)" do
+ it_behaves_like 'expands', {
+ '{?who}' => '?who=fred',
+ '{?half}' => '?half=50%25',
+ '{?x,y}' => '?x=1024&y=768',
+ '{?x,y,empty}' => '?x=1024&y=768&empty=',
+ '{?x,y,undef}' => '?x=1024&y=768',
+ '{?var:3}' => '?var=val',
+ '{?list}' => '?list=red,green,blue',
+ '{?list*}' => '?list=red&list=green&list=blue',
+ '{?keys}' => [
+ '?keys=semi,%3B,dot,.,comma,%2C',
+ '?keys=dot,.,semi,%3B,comma,%2C',
+ '?keys=comma,%2C,semi,%3B,dot,.',
+ '?keys=semi,%3B,comma,%2C,dot,.',
+ '?keys=dot,.,comma,%2C,semi,%3B',
+ '?keys=comma,%2C,dot,.,semi,%3B'
+ ],
+ '{?keys*}' => [
+ '?semi=%3B&dot=.&comma=%2C',
+ '?dot=.&semi=%3B&comma=%2C',
+ '?comma=%2C&semi=%3B&dot=.',
+ '?semi=%3B&comma=%2C&dot=.',
+ '?dot=.&comma=%2C&semi=%3B',
+ '?comma=%2C&dot=.&semi=%3B'
+ ]
+ }
+ end
+ context "form query expansion (&)" do
+ it_behaves_like 'expands', {
+ '{&who}' => '&who=fred',
+ '{&half}' => '&half=50%25',
+ '?fixed=yes{&x}' => '?fixed=yes&x=1024',
+ '{&x,y,empty}' => '&x=1024&y=768&empty=',
+ '{&x,y,undef}' => '&x=1024&y=768',
+ '{&var:3}' => '&var=val',
+ '{&list}' => '&list=red,green,blue',
+ '{&list*}' => '&list=red&list=green&list=blue',
+ '{&keys}' => [
+ '&keys=semi,%3B,dot,.,comma,%2C',
+ '&keys=dot,.,semi,%3B,comma,%2C',
+ '&keys=comma,%2C,semi,%3B,dot,.',
+ '&keys=semi,%3B,comma,%2C,dot,.',
+ '&keys=dot,.,comma,%2C,semi,%3B',
+ '&keys=comma,%2C,dot,.,semi,%3B'
+ ],
+ '{&keys*}' => [
+ '&semi=%3B&dot=.&comma=%2C',
+ '&dot=.&semi=%3B&comma=%2C',
+ '&comma=%2C&semi=%3B&dot=.',
+ '&semi=%3B&comma=%2C&dot=.',
+ '&dot=.&comma=%2C&semi=%3B',
+ '&comma=%2C&dot=.&semi=%3B'
+ ]
+ }
+ end
+ context "non-string key in match data" do
+ subject {Addressable::Template.new("http://example.com/{one}")}
+
+ it "raises TypeError" do
+ expect { subject.expand(Object.new => "1") }.to raise_error TypeError
+ end
+ end
+end
+
+class ExampleTwoProcessor
+ def self.restore(name, value)
+ return value.gsub(/-/, " ") if name == "query"
+ return value
+ end
+
+ def self.match(name)
+ return ".*?" if name == "first"
+ return ".*"
+ end
+ def self.validate(name, value)
+ return !!(value =~ /^[\w ]+$/) if name == "query"
+ return true
+ end
+
+ def self.transform(name, value)
+ return value.gsub(/ /, "+") if name == "query"
+ return value
+ end
+end
+
+class DumbProcessor
+ def self.match(name)
+ return ".*?" if name == "first"
+ end
+end
+
+describe Addressable::Template do
+ describe 'initialize' do
+ context 'with a non-string' do
+ it 'raises a TypeError' do
+ expect { Addressable::Template.new(nil) }.to raise_error(TypeError)
+ end
+ end
+ end
+
+ describe 'freeze' do
+ subject { Addressable::Template.new("http://example.com/{first}/{+second}/") }
+ it 'freezes the template' do
+ expect(subject.freeze).to be_frozen
+ end
+ end
+
+ describe "Matching" do
+ let(:uri){
+ Addressable::URI.parse(
+ "http://example.com/search/an-example-search-query/"
+ )
+ }
+ let(:uri2){
+ Addressable::URI.parse("http://example.com/a/b/c/")
+ }
+ let(:uri3){
+ Addressable::URI.parse("http://example.com/;a=1;b=2;c=3;first=foo")
+ }
+ let(:uri4){
+ Addressable::URI.parse("http://example.com/?a=1&b=2&c=3&first=foo")
+ }
+ let(:uri5){
+ "http://example.com/foo"
+ }
+ context "first uri with ExampleTwoProcessor" do
+ subject {
+ Addressable::Template.new(
+ "http://example.com/search/{query}/"
+ ).match(uri, ExampleTwoProcessor)
+ }
+ its(:variables){ should == ["query"] }
+ its(:captures){ should == ["an example search query"] }
+ end
+
+ context "second uri with ExampleTwoProcessor" do
+ subject {
+ Addressable::Template.new(
+ "http://example.com/{first}/{+second}/"
+ ).match(uri2, ExampleTwoProcessor)
+ }
+ its(:variables){ should == ["first", "second"] }
+ its(:captures){ should == ["a", "b/c"] }
+ end
+
+ context "second uri with DumbProcessor" do
+ subject {
+ Addressable::Template.new(
+ "http://example.com/{first}/{+second}/"
+ ).match(uri2, DumbProcessor)
+ }
+ its(:variables){ should == ["first", "second"] }
+ its(:captures){ should == ["a", "b/c"] }
+ end
+
+ context "second uri" do
+ subject {
+ Addressable::Template.new(
+ "http://example.com/{first}{/second*}/"
+ ).match(uri2)
+ }
+ its(:variables){ should == ["first", "second"] }
+ its(:captures){ should == ["a", ["b","c"]] }
+ end
+ context "third uri" do
+ subject {
+ Addressable::Template.new(
+ "http://example.com/{;hash*,first}"
+ ).match(uri3)
+ }
+ its(:variables){ should == ["hash", "first"] }
+ its(:captures){ should == [
+ {"a" => "1", "b" => "2", "c" => "3", "first" => "foo"}, nil] }
+ end
+ # Note that this expansion is impossible to revert deterministically - the
+ # * operator means first could have been a key of hash or a separate key.
+ # Semantically, a separate key is more likely, but both are possible.
+ context "fourth uri" do
+ subject {
+ Addressable::Template.new(
+ "http://example.com/{?hash*,first}"
+ ).match(uri4)
+ }
+ its(:variables){ should == ["hash", "first"] }
+ its(:captures){ should == [
+ {"a" => "1", "b" => "2", "c" => "3", "first"=> "foo"}, nil] }
+ end
+ context "fifth uri" do
+ subject {
+ Addressable::Template.new(
+ "http://example.com/{path}{?hash*,first}"
+ ).match(uri5)
+ }
+ its(:variables){ should == ["path", "hash", "first"] }
+ its(:captures){ should == ["foo", nil, nil] }
+ end
+ end
+
+ describe 'match' do
+ subject { Addressable::Template.new('http://example.com/first/second/') }
+ context 'when the URI is the same as the template' do
+ it 'returns the match data itself with an empty mapping' do
+ uri = Addressable::URI.parse('http://example.com/first/second/')
+ match_data = subject.match(uri)
+ expect(match_data).to be_an Addressable::Template::MatchData
+ expect(match_data.uri).to eq(uri)
+ expect(match_data.template).to eq(subject)
+ expect(match_data.mapping).to be_empty
+ expect(match_data.inspect).to be_an String
+ end
+ end
+ end
+
+ describe "extract" do
+ let(:template) {
+ Addressable::Template.new(
+ "http://{host}{/segments*}/{?one,two,bogus}{#fragment}"
+ )
+ }
+ let(:uri){ "http://example.com/a/b/c/?one=1&two=2#foo" }
+ let(:uri2){ "http://example.com/a/b/c/#foo" }
+ it "should be able to extract with queries" do
+ expect(template.extract(uri)).to eq({
+ "host" => "example.com",
+ "segments" => %w(a b c),
+ "one" => "1",
+ "bogus" => nil,
+ "two" => "2",
+ "fragment" => "foo"
+ })
+ end
+ it "should be able to extract without queries" do
+ expect(template.extract(uri2)).to eq({
+ "host" => "example.com",
+ "segments" => %w(a b c),
+ "one" => nil,
+ "bogus" => nil,
+ "two" => nil,
+ "fragment" => "foo"
+ })
+ end
+
+ context "issue #137" do
+ subject { Addressable::Template.new('/path{?page,per_page}') }
+
+ it "can match empty" do
+ data = subject.extract("/path")
+ expect(data["page"]).to eq(nil)
+ expect(data["per_page"]).to eq(nil)
+ expect(data.keys.sort).to eq(['page', 'per_page'])
+ end
+
+ it "can match first var" do
+ data = subject.extract("/path?page=1")
+ expect(data["page"]).to eq("1")
+ expect(data["per_page"]).to eq(nil)
+ expect(data.keys.sort).to eq(['page', 'per_page'])
+ end
+
+ it "can match second var" do
+ data = subject.extract("/path?per_page=1")
+ expect(data["page"]).to eq(nil)
+ expect(data["per_page"]).to eq("1")
+ expect(data.keys.sort).to eq(['page', 'per_page'])
+ end
+
+ it "can match both vars" do
+ data = subject.extract("/path?page=2&per_page=1")
+ expect(data["page"]).to eq("2")
+ expect(data["per_page"]).to eq("1")
+ expect(data.keys.sort).to eq(['page', 'per_page'])
+ end
+ end
+ end
+
+ describe "Partial expand with symbols" do
+ context "partial_expand with two simple values" do
+ subject {
+ Addressable::Template.new("http://example.com/{one}/{two}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand(:one => "1").pattern).to eq(
+ "http://example.com/1/{two}/"
+ )
+ end
+ end
+ context "partial_expand query with missing param in middle" do
+ subject {
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand(:one => "1", :three => "3").pattern).to eq(
+ "http://example.com/?one=1{&two}&three=3/"
+ )
+ end
+ end
+ context "partial_expand form style query with missing param at beginning" do
+ subject {
+ Addressable::Template.new("http://example.com/{?one,two}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand(:two => "2").pattern).to eq(
+ "http://example.com/?two=2{&one}/"
+ )
+ end
+ end
+ context "issue #307 - partial_expand form query with nil params" do
+ subject do
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
+ end
+ it "builds a new pattern with two=nil" do
+ expect(subject.partial_expand(two: nil).pattern).to eq(
+ "http://example.com/{?one}{&three}/"
+ )
+ end
+ it "builds a new pattern with one=nil and two=nil" do
+ expect(subject.partial_expand(one: nil, two: nil).pattern).to eq(
+ "http://example.com/{?three}/"
+ )
+ end
+ it "builds a new pattern with one=1 and two=nil" do
+ expect(subject.partial_expand(one: 1, two: nil).pattern).to eq(
+ "http://example.com/?one=1{&three}/"
+ )
+ end
+ it "builds a new pattern with one=nil and two=2" do
+ expect(subject.partial_expand(one: nil, two: 2).pattern).to eq(
+ "http://example.com/?two=2{&three}/"
+ )
+ end
+ it "builds a new pattern with one=nil" do
+ expect(subject.partial_expand(one: nil).pattern).to eq(
+ "http://example.com/{?two}{&three}/"
+ )
+ end
+ end
+ context "partial_expand with query string" do
+ subject {
+ Addressable::Template.new("http://example.com/{?two,one}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand(:one => "1").pattern).to eq(
+ "http://example.com/?one=1{&two}/"
+ )
+ end
+ end
+ context "partial_expand with path operator" do
+ subject {
+ Addressable::Template.new("http://example.com{/one,two}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand(:one => "1").pattern).to eq(
+ "http://example.com/1{/two}/"
+ )
+ end
+ end
+ context "partial expand with unicode values" do
+ subject do
+ Addressable::Template.new("http://example.com/{resource}/{query}/")
+ end
+ it "normalizes unicode by default" do
+ template = subject.partial_expand("query" => "Cafe\u0301")
+ expect(template.pattern).to eq(
+ "http://example.com/{resource}/Caf%C3%A9/"
+ )
+ end
+
+ it "does not normalize unicode when byte semantics requested" do
+ template = subject.partial_expand({"query" => "Cafe\u0301"}, nil, false)
+ expect(template.pattern).to eq(
+ "http://example.com/{resource}/Cafe%CC%81/"
+ )
+ end
+ end
+ end
+ describe "Partial expand with strings" do
+ context "partial_expand with two simple values" do
+ subject {
+ Addressable::Template.new("http://example.com/{one}/{two}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
+ "http://example.com/1/{two}/"
+ )
+ end
+ end
+ context "partial_expand query with missing param in middle" do
+ subject {
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq(
+ "http://example.com/?one=1{&two}&three=3/"
+ )
+ end
+ end
+ context "partial_expand with query string" do
+ subject {
+ Addressable::Template.new("http://example.com/{?two,one}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
+ "http://example.com/?one=1{&two}/"
+ )
+ end
+ end
+ context "partial_expand with path operator" do
+ subject {
+ Addressable::Template.new("http://example.com{/one,two}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
+ "http://example.com/1{/two}/"
+ )
+ end
+ end
+ end
+ describe "Expand" do
+ context "expand with unicode values" do
+ subject do
+ Addressable::Template.new("http://example.com/search/{query}/")
+ end
+ it "normalizes unicode by default" do
+ uri = subject.expand("query" => "Cafe\u0301").to_str
+ expect(uri).to eq("http://example.com/search/Caf%C3%A9/")
+ end
+
+ it "does not normalize unicode when byte semantics requested" do
+ uri = subject.expand({ "query" => "Cafe\u0301" }, nil, false).to_str
+ expect(uri).to eq("http://example.com/search/Cafe%CC%81/")
+ end
+ end
+ context "expand with a processor" do
+ subject {
+ Addressable::Template.new("http://example.com/search/{query}/")
+ }
+ it "processes spaces" do
+ expect(subject.expand({"query" => "an example search query"},
+ ExampleTwoProcessor).to_str).to eq(
+ "http://example.com/search/an+example+search+query/"
+ )
+ end
+ it "validates" do
+ expect{
+ subject.expand({"query" => "Bogus!"},
+ ExampleTwoProcessor).to_str
+ }.to raise_error(Addressable::Template::InvalidTemplateValueError)
+ end
+ end
+ context "partial_expand query with missing param in middle" do
+ subject {
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq(
+ "http://example.com/?one=1{&two}&three=3/"
+ )
+ end
+ end
+ context "partial_expand with query string" do
+ subject {
+ Addressable::Template.new("http://example.com/{?two,one}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
+ "http://example.com/?one=1{&two}/"
+ )
+ end
+ end
+ context "partial_expand with path operator" do
+ subject {
+ Addressable::Template.new("http://example.com{/one,two}/")
+ }
+ it "builds a new pattern" do
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
+ "http://example.com/1{/two}/"
+ )
+ end
+ end
+ end
+ context "Matching with operators" do
+ describe "Level 1:" do
+ subject { Addressable::Template.new("foo{foo}/{bar}baz") }
+ it "can match" do
+ data = subject.match("foofoo/bananabaz")
+ expect(data.mapping["foo"]).to eq("foo")
+ expect(data.mapping["bar"]).to eq("banana")
+ end
+ it "can fail" do
+ expect(subject.match("bar/foo")).to be_nil
+ expect(subject.match("foobaz")).to be_nil
+ end
+ it "can match empty" do
+ data = subject.match("foo/baz")
+ expect(data.mapping["foo"]).to eq(nil)
+ expect(data.mapping["bar"]).to eq(nil)
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(["foo", "bar"])
+ end
+ end
+
+ describe "Level 2:" do
+ subject { Addressable::Template.new("foo{+foo}{#bar}baz") }
+ it "can match" do
+ data = subject.match("foo/test/banana#bazbaz")
+ expect(data.mapping["foo"]).to eq("/test/banana")
+ expect(data.mapping["bar"]).to eq("baz")
+ end
+ it "can match empty level 2 #" do
+ data = subject.match("foo/test/bananabaz")
+ expect(data.mapping["foo"]).to eq("/test/banana")
+ expect(data.mapping["bar"]).to eq(nil)
+ data = subject.match("foo/test/banana#baz")
+ expect(data.mapping["foo"]).to eq("/test/banana")
+ expect(data.mapping["bar"]).to eq("")
+ end
+ it "can match empty level 2 +" do
+ data = subject.match("foobaz")
+ expect(data.mapping["foo"]).to eq(nil)
+ expect(data.mapping["bar"]).to eq(nil)
+ data = subject.match("foo#barbaz")
+ expect(data.mapping["foo"]).to eq(nil)
+ expect(data.mapping["bar"]).to eq("bar")
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(["foo", "bar"])
+ end
+ end
+
+ describe "Level 3:" do
+ context "no operator" do
+ subject { Addressable::Template.new("foo{foo,bar}baz") }
+ it "can match" do
+ data = subject.match("foofoo,barbaz")
+ expect(data.mapping["foo"]).to eq("foo")
+ expect(data.mapping["bar"]).to eq("bar")
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(["foo", "bar"])
+ end
+ end
+ context "+ operator" do
+ subject { Addressable::Template.new("foo{+foo,bar}baz") }
+ it "can match" do
+ data = subject.match("foofoo/bar,barbaz")
+ expect(data.mapping["bar"]).to eq("foo/bar,bar")
+ expect(data.mapping["foo"]).to eq("")
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(["foo", "bar"])
+ end
+ end
+ context ". operator" do
+ subject { Addressable::Template.new("foo{.foo,bar}baz") }
+ it "can match" do
+ data = subject.match("foo.foo.barbaz")
+ expect(data.mapping["foo"]).to eq("foo")
+ expect(data.mapping["bar"]).to eq("bar")
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(["foo", "bar"])
+ end
+ end
+ context "/ operator" do
+ subject { Addressable::Template.new("foo{/foo,bar}baz") }
+ it "can match" do
+ data = subject.match("foo/foo/barbaz")
+ expect(data.mapping["foo"]).to eq("foo")
+ expect(data.mapping["bar"]).to eq("bar")
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(["foo", "bar"])
+ end
+ end
+ context "; operator" do
+ subject { Addressable::Template.new("foo{;foo,bar,baz}baz") }
+ it "can match" do
+ data = subject.match("foo;foo=bar%20baz;bar=foo;bazbaz")
+ expect(data.mapping["foo"]).to eq("bar baz")
+ expect(data.mapping["bar"]).to eq("foo")
+ expect(data.mapping["baz"]).to eq("")
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(%w(foo bar baz))
+ end
+ end
+ context "? operator" do
+ context "test" do
+ subject { Addressable::Template.new("foo{?foo,bar}baz") }
+ it "can match" do
+ data = subject.match("foo?foo=bar%20baz&bar=foobaz")
+ expect(data.mapping["foo"]).to eq("bar baz")
+ expect(data.mapping["bar"]).to eq("foo")
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(%w(foo bar))
+ end
+ end
+
+ context "issue #137" do
+ subject { Addressable::Template.new('/path{?page,per_page}') }
+
+ it "can match empty" do
+ data = subject.match("/path")
+ expect(data.mapping["page"]).to eq(nil)
+ expect(data.mapping["per_page"]).to eq(nil)
+ expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
+ end
+
+ it "can match first var" do
+ data = subject.match("/path?page=1")
+ expect(data.mapping["page"]).to eq("1")
+ expect(data.mapping["per_page"]).to eq(nil)
+ expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
+ end
+
+ it "can match second var" do
+ data = subject.match("/path?per_page=1")
+ expect(data.mapping["page"]).to eq(nil)
+ expect(data.mapping["per_page"]).to eq("1")
+ expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
+ end
+
+ it "can match both vars" do
+ data = subject.match("/path?page=2&per_page=1")
+ expect(data.mapping["page"]).to eq("2")
+ expect(data.mapping["per_page"]).to eq("1")
+ expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
+ end
+ end
+
+ context "issue #71" do
+ subject { Addressable::Template.new("http://cyberscore.dev/api/users{?username}") }
+ it "can match" do
+ data = subject.match("http://cyberscore.dev/api/users?username=foobaz")
+ expect(data.mapping["username"]).to eq("foobaz")
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(%w(username))
+ expect(subject.keys).to eq(%w(username))
+ end
+ end
+ end
+ context "& operator" do
+ subject { Addressable::Template.new("foo{&foo,bar}baz") }
+ it "can match" do
+ data = subject.match("foo&foo=bar%20baz&bar=foobaz")
+ expect(data.mapping["foo"]).to eq("bar baz")
+ expect(data.mapping["bar"]).to eq("foo")
+ end
+ it "lists vars" do
+ expect(subject.variables).to eq(%w(foo bar))
+ end
+ end
+ end
+ end
+
+ context "support regexes:" do
+ context "EXPRESSION" do
+ subject { Addressable::Template::EXPRESSION }
+ it "should be able to match an expression" do
+ expect(subject).to match("{foo}")
+ expect(subject).to match("{foo,9}")
+ expect(subject).to match("{foo.bar,baz}")
+ expect(subject).to match("{+foo.bar,baz}")
+ expect(subject).to match("{foo,foo%20bar}")
+ expect(subject).to match("{#foo:20,baz*}")
+ expect(subject).to match("stuff{#foo:20,baz*}things")
+ end
+ it "should fail on non vars" do
+ expect(subject).not_to match("!{foo")
+ expect(subject).not_to match("{foo.bar.}")
+ expect(subject).not_to match("!{}")
+ end
+ end
+ context "VARNAME" do
+ subject { Addressable::Template::VARNAME }
+ it "should be able to match a variable" do
+ expect(subject).to match("foo")
+ expect(subject).to match("9")
+ expect(subject).to match("foo.bar")
+ expect(subject).to match("foo_bar")
+ expect(subject).to match("foo_bar.baz")
+ expect(subject).to match("foo%20bar")
+ expect(subject).to match("foo%20bar.baz")
+ end
+ it "should fail on non vars" do
+ expect(subject).not_to match("!foo")
+ expect(subject).not_to match("foo.bar.")
+ expect(subject).not_to match("foo%2%00bar")
+ expect(subject).not_to match("foo_ba%r")
+ expect(subject).not_to match("foo_bar*")
+ expect(subject).not_to match("foo_bar:20")
+ end
+
+ it 'should parse in a reasonable time' do
+ expect do
+ Timeout.timeout(0.1) do
+ expect(subject).not_to match("0"*25 + "!")
+ end
+ end.not_to raise_error
+ end
+ end
+ context "VARIABLE_LIST" do
+ subject { Addressable::Template::VARIABLE_LIST }
+ it "should be able to match a variable list" do
+ expect(subject).to match("foo,bar")
+ expect(subject).to match("foo")
+ expect(subject).to match("foo,bar*,baz")
+ expect(subject).to match("foo.bar,bar_baz*,baz:12")
+ end
+ it "should fail on non vars" do
+ expect(subject).not_to match(",foo,bar*,baz")
+ expect(subject).not_to match("foo,*bar,baz")
+ expect(subject).not_to match("foo,,bar*,baz")
+ end
+ end
+ context "VARSPEC" do
+ subject { Addressable::Template::VARSPEC }
+ it "should be able to match a variable with modifier" do
+ expect(subject).to match("9:8")
+ expect(subject).to match("foo.bar*")
+ expect(subject).to match("foo_bar:12")
+ expect(subject).to match("foo_bar.baz*")
+ expect(subject).to match("foo%20bar:12")
+ expect(subject).to match("foo%20bar.baz*")
+ end
+ it "should fail on non vars" do
+ expect(subject).not_to match("!foo")
+ expect(subject).not_to match("*foo")
+ expect(subject).not_to match("fo*o")
+ expect(subject).not_to match("fo:o")
+ expect(subject).not_to match("foo:")
+ end
+ end
+ end
+end
+
+describe Addressable::Template::MatchData do
+ let(:template) { Addressable::Template.new('{foo}/{bar}') }
+ subject(:its) { template.match('ab/cd') }
+ its(:uri) { should == Addressable::URI.parse('ab/cd') }
+ its(:template) { should == template }
+ its(:mapping) { should == { 'foo' => 'ab', 'bar' => 'cd' } }
+ its(:variables) { should == ['foo', 'bar'] }
+ its(:keys) { should == ['foo', 'bar'] }
+ its(:names) { should == ['foo', 'bar'] }
+ its(:values) { should == ['ab', 'cd'] }
+ its(:captures) { should == ['ab', 'cd'] }
+ its(:to_a) { should == ['ab/cd', 'ab', 'cd'] }
+ its(:to_s) { should == 'ab/cd' }
+ its(:string) { should == its.to_s }
+ its(:pre_match) { should == "" }
+ its(:post_match) { should == "" }
+
+ describe 'values_at' do
+ it 'returns an array with the values' do
+ expect(its.values_at(0, 2)).to eq(['ab/cd', 'cd'])
+ end
+ it 'allows mixing integer an string keys' do
+ expect(its.values_at('foo', 1)).to eq(['ab', 'ab'])
+ end
+ it 'accepts unknown keys' do
+ expect(its.values_at('baz', 'foo')).to eq([nil, 'ab'])
+ end
+ end
+
+ describe '[]' do
+ context 'string key' do
+ it 'returns the corresponding capture' do
+ expect(its['foo']).to eq('ab')
+ expect(its['bar']).to eq('cd')
+ end
+ it 'returns nil for unknown keys' do
+ expect(its['baz']).to be_nil
+ end
+ end
+ context 'symbol key' do
+ it 'returns the corresponding capture' do
+ expect(its[:foo]).to eq('ab')
+ expect(its[:bar]).to eq('cd')
+ end
+ it 'returns nil for unknown keys' do
+ expect(its[:baz]).to be_nil
+ end
+ end
+ context 'integer key' do
+ it 'returns the full URI for index 0' do
+ expect(its[0]).to eq('ab/cd')
+ end
+ it 'returns the corresponding capture' do
+ expect(its[1]).to eq('ab')
+ expect(its[2]).to eq('cd')
+ end
+ it 'returns nil for unknown keys' do
+ expect(its[3]).to be_nil
+ end
+ end
+ context 'other key' do
+ it 'raises an exception' do
+ expect { its[Object.new] }.to raise_error(TypeError)
+ end
+ end
+ context 'with length' do
+ it 'returns an array starting at index with given length' do
+ expect(its[0, 2]).to eq(['ab/cd', 'ab'])
+ expect(its[2, 1]).to eq(['cd'])
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/uri_spec.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/uri_spec.rb
new file mode 100644
index 0000000..00baaac
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/addressable/uri_spec.rb
@@ -0,0 +1,6665 @@
+# frozen_string_literal: true
+
+# coding: utf-8
+# Copyright (C) Bob Aman
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+require "spec_helper"
+
+require "addressable/uri"
+require "uri"
+require "ipaddr"
+
+if !"".respond_to?("force_encoding")
+ class String
+ def force_encoding(encoding)
+ @encoding = encoding
+ end
+
+ def encoding
+ @encoding ||= Encoding::ASCII_8BIT
+ end
+ end
+
+ class Encoding
+ def initialize(name)
+ @name = name
+ end
+
+ def to_s
+ return @name
+ end
+
+ UTF_8 = Encoding.new("UTF-8")
+ ASCII_8BIT = Encoding.new("US-ASCII")
+ end
+end
+
+module Fake
+ module URI
+ class HTTP
+ def initialize(uri)
+ @uri = uri
+ end
+
+ def to_s
+ return @uri.to_s
+ end
+
+ alias :to_str :to_s
+ end
+ end
+end
+
+describe Addressable::URI, "when created with a non-numeric port number" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:port => "bogus")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when created with a invalid encoded port number" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:port => "%eb")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when created with a non-string scheme" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:scheme => :bogus)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when created with a non-string user" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:user => :bogus)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when created with a non-string password" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:password => :bogus)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when created with a non-string userinfo" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:userinfo => :bogus)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when created with a non-string host" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:host => :bogus)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when created with a non-string authority" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:authority => :bogus)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when created with a non-string path" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:path => :bogus)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when created with a non-string query" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:query => :bogus)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when created with a non-string fragment" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:fragment => :bogus)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when created with a scheme but no hierarchical " +
+ "segment" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.parse("http:")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "quote handling" do
+ describe 'in host name' do
+ it "should raise an error for single quote" do
+ expect do
+ Addressable::URI.parse("http://local\"host/")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+ end
+end
+
+describe Addressable::URI, "newline normalization" do
+ it "should not accept newlines in scheme" do
+ expect do
+ Addressable::URI.parse("ht%0atp://localhost/")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should not unescape newline in path" do
+ uri = Addressable::URI.parse("http://localhost/%0a").normalize
+ expect(uri.to_s).to eq("http://localhost/%0A")
+ end
+
+ it "should not unescape newline in hostname" do
+ uri = Addressable::URI.parse("http://local%0ahost/").normalize
+ expect(uri.to_s).to eq("http://local%0Ahost/")
+ end
+
+ it "should not unescape newline in username" do
+ uri = Addressable::URI.parse("http://foo%0abar@localhost/").normalize
+ expect(uri.to_s).to eq("http://foo%0Abar@localhost/")
+ end
+
+ it "should not unescape newline in username" do
+ uri = Addressable::URI.parse("http://example:foo%0abar@example/").normalize
+ expect(uri.to_s).to eq("http://example:foo%0Abar@example/")
+ end
+
+ it "should not accept newline in hostname" do
+ uri = Addressable::URI.parse("http://localhost/")
+ expect do
+ uri.host = "local\nhost"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when created with ambiguous path" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.parse("::http")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when created with an invalid host" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:host => "")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when created with a host consisting of " +
+ "sub-delims characters" do
+ it "should not raise an error" do
+ expect do
+ Addressable::URI.new(
+ :host => Addressable::URI::CharacterClasses::SUB_DELIMS.gsub(/\\/, '')
+ )
+ end.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when created with a host consisting of " +
+ "unreserved characters" do
+ it "should not raise an error" do
+ expect do
+ Addressable::URI.new(
+ :host => Addressable::URI::CharacterClasses::UNRESERVED.gsub(/\\/, '')
+ )
+ end.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when created from nil components" do
+ before do
+ @uri = Addressable::URI.new
+ end
+
+ it "should have a nil site value" do
+ expect(@uri.site).to eq(nil)
+ end
+
+ it "should have an empty path" do
+ expect(@uri.path).to eq("")
+ end
+
+ it "should be an empty uri" do
+ expect(@uri.to_s).to eq("")
+ end
+
+ it "should have a nil default port" do
+ expect(@uri.default_port).to eq(nil)
+ end
+
+ it "should be empty" do
+ expect(@uri).to be_empty
+ end
+
+ it "should raise an error if the scheme is set to whitespace" do
+ expect do
+ @uri.scheme = "\t \n"
+ end.to raise_error(Addressable::URI::InvalidURIError, /'\t \n'/)
+ end
+
+ it "should raise an error if the scheme is set to all digits" do
+ expect do
+ @uri.scheme = "123"
+ end.to raise_error(Addressable::URI::InvalidURIError, /'123'/)
+ end
+
+ it "should raise an error if the scheme begins with a digit" do
+ expect do
+ @uri.scheme = "1scheme"
+ end.to raise_error(Addressable::URI::InvalidURIError, /'1scheme'/)
+ end
+
+ it "should raise an error if the scheme begins with a plus" do
+ expect do
+ @uri.scheme = "+scheme"
+ end.to raise_error(Addressable::URI::InvalidURIError, /'\+scheme'/)
+ end
+
+ it "should raise an error if the scheme begins with a dot" do
+ expect do
+ @uri.scheme = ".scheme"
+ end.to raise_error(Addressable::URI::InvalidURIError, /'\.scheme'/)
+ end
+
+ it "should raise an error if the scheme begins with a dash" do
+ expect do
+ @uri.scheme = "-scheme"
+ end.to raise_error(Addressable::URI::InvalidURIError, /'-scheme'/)
+ end
+
+ it "should raise an error if the scheme contains an illegal character" do
+ expect do
+ @uri.scheme = "scheme!"
+ end.to raise_error(Addressable::URI::InvalidURIError, /'scheme!'/)
+ end
+
+ it "should raise an error if the scheme contains whitespace" do
+ expect do
+ @uri.scheme = "sch eme"
+ end.to raise_error(Addressable::URI::InvalidURIError, /'sch eme'/)
+ end
+
+ it "should raise an error if the scheme contains a newline" do
+ expect do
+ @uri.scheme = "sch\neme"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should raise an error if set into an invalid state" do
+ expect do
+ @uri.user = "user"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should raise an error if set into an invalid state" do
+ expect do
+ @uri.password = "pass"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should raise an error if set into an invalid state" do
+ expect do
+ @uri.scheme = "http"
+ @uri.fragment = "fragment"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should raise an error if set into an invalid state" do
+ expect do
+ @uri.fragment = "fragment"
+ @uri.scheme = "http"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when initialized from individual components" do
+ before do
+ @uri = Addressable::URI.new(
+ :scheme => "http",
+ :user => "user",
+ :password => "password",
+ :host => "example.com",
+ :port => 8080,
+ :path => "/path",
+ :query => "query=value",
+ :fragment => "fragment"
+ )
+ end
+
+ it "returns 'http' for #scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "returns 'http' for #normalized_scheme" do
+ expect(@uri.normalized_scheme).to eq("http")
+ end
+
+ it "returns 'user' for #user" do
+ expect(@uri.user).to eq("user")
+ end
+
+ it "returns 'user' for #normalized_user" do
+ expect(@uri.normalized_user).to eq("user")
+ end
+
+ it "returns 'password' for #password" do
+ expect(@uri.password).to eq("password")
+ end
+
+ it "returns 'password' for #normalized_password" do
+ expect(@uri.normalized_password).to eq("password")
+ end
+
+ it "returns 'user:password' for #userinfo" do
+ expect(@uri.userinfo).to eq("user:password")
+ end
+
+ it "returns 'user:password' for #normalized_userinfo" do
+ expect(@uri.normalized_userinfo).to eq("user:password")
+ end
+
+ it "returns 'example.com' for #host" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "returns 'example.com' for #normalized_host" do
+ expect(@uri.normalized_host).to eq("example.com")
+ end
+
+ it "returns 'com' for #tld" do
+ expect(@uri.tld).to eq("com")
+ end
+
+ it "returns 'user:password@example.com:8080' for #authority" do
+ expect(@uri.authority).to eq("user:password@example.com:8080")
+ end
+
+ it "returns 'user:password@example.com:8080' for #normalized_authority" do
+ expect(@uri.normalized_authority).to eq("user:password@example.com:8080")
+ end
+
+ it "returns 8080 for #port" do
+ expect(@uri.port).to eq(8080)
+ end
+
+ it "returns 8080 for #normalized_port" do
+ expect(@uri.normalized_port).to eq(8080)
+ end
+
+ it "returns 80 for #default_port" do
+ expect(@uri.default_port).to eq(80)
+ end
+
+ it "returns 'http://user:password@example.com:8080' for #site" do
+ expect(@uri.site).to eq("http://user:password@example.com:8080")
+ end
+
+ it "returns 'http://user:password@example.com:8080' for #normalized_site" do
+ expect(@uri.normalized_site).to eq("http://user:password@example.com:8080")
+ end
+
+ it "returns '/path' for #path" do
+ expect(@uri.path).to eq("/path")
+ end
+
+ it "returns '/path' for #normalized_path" do
+ expect(@uri.normalized_path).to eq("/path")
+ end
+
+ it "returns 'query=value' for #query" do
+ expect(@uri.query).to eq("query=value")
+ end
+
+ it "returns 'query=value' for #normalized_query" do
+ expect(@uri.normalized_query).to eq("query=value")
+ end
+
+ it "returns 'fragment' for #fragment" do
+ expect(@uri.fragment).to eq("fragment")
+ end
+
+ it "returns 'fragment' for #normalized_fragment" do
+ expect(@uri.normalized_fragment).to eq("fragment")
+ end
+
+ it "returns #hash" do
+ expect(@uri.hash).not_to be nil
+ end
+
+ it "returns #to_s" do
+ expect(@uri.to_s).to eq(
+ "http://user:password@example.com:8080/path?query=value#fragment"
+ )
+ end
+
+ it "should not be empty" do
+ expect(@uri).not_to be_empty
+ end
+
+ it "should not be frozen" do
+ expect(@uri).not_to be_frozen
+ end
+
+ it "should allow destructive operations" do
+ expect { @uri.normalize! }.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when initialized from " +
+ "frozen individual components" do
+ before do
+ @uri = Addressable::URI.new(
+ :scheme => "http".freeze,
+ :user => "user".freeze,
+ :password => "password".freeze,
+ :host => "example.com".freeze,
+ :port => "8080".freeze,
+ :path => "/path".freeze,
+ :query => "query=value".freeze,
+ :fragment => "fragment".freeze
+ )
+ end
+
+ it "returns 'http' for #scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "returns 'http' for #normalized_scheme" do
+ expect(@uri.normalized_scheme).to eq("http")
+ end
+
+ it "returns 'user' for #user" do
+ expect(@uri.user).to eq("user")
+ end
+
+ it "returns 'user' for #normalized_user" do
+ expect(@uri.normalized_user).to eq("user")
+ end
+
+ it "returns 'password' for #password" do
+ expect(@uri.password).to eq("password")
+ end
+
+ it "returns 'password' for #normalized_password" do
+ expect(@uri.normalized_password).to eq("password")
+ end
+
+ it "returns 'user:password' for #userinfo" do
+ expect(@uri.userinfo).to eq("user:password")
+ end
+
+ it "returns 'user:password' for #normalized_userinfo" do
+ expect(@uri.normalized_userinfo).to eq("user:password")
+ end
+
+ it "returns 'example.com' for #host" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "returns 'example.com' for #normalized_host" do
+ expect(@uri.normalized_host).to eq("example.com")
+ end
+
+ it "returns 'user:password@example.com:8080' for #authority" do
+ expect(@uri.authority).to eq("user:password@example.com:8080")
+ end
+
+ it "returns 'user:password@example.com:8080' for #normalized_authority" do
+ expect(@uri.normalized_authority).to eq("user:password@example.com:8080")
+ end
+
+ it "returns 8080 for #port" do
+ expect(@uri.port).to eq(8080)
+ end
+
+ it "returns 8080 for #normalized_port" do
+ expect(@uri.normalized_port).to eq(8080)
+ end
+
+ it "returns 80 for #default_port" do
+ expect(@uri.default_port).to eq(80)
+ end
+
+ it "returns 'http://user:password@example.com:8080' for #site" do
+ expect(@uri.site).to eq("http://user:password@example.com:8080")
+ end
+
+ it "returns 'http://user:password@example.com:8080' for #normalized_site" do
+ expect(@uri.normalized_site).to eq("http://user:password@example.com:8080")
+ end
+
+ it "returns '/path' for #path" do
+ expect(@uri.path).to eq("/path")
+ end
+
+ it "returns '/path' for #normalized_path" do
+ expect(@uri.normalized_path).to eq("/path")
+ end
+
+ it "returns 'query=value' for #query" do
+ expect(@uri.query).to eq("query=value")
+ end
+
+ it "returns 'query=value' for #normalized_query" do
+ expect(@uri.normalized_query).to eq("query=value")
+ end
+
+ it "returns 'fragment' for #fragment" do
+ expect(@uri.fragment).to eq("fragment")
+ end
+
+ it "returns 'fragment' for #normalized_fragment" do
+ expect(@uri.normalized_fragment).to eq("fragment")
+ end
+
+ it "returns #hash" do
+ expect(@uri.hash).not_to be nil
+ end
+
+ it "returns #to_s" do
+ expect(@uri.to_s).to eq(
+ "http://user:password@example.com:8080/path?query=value#fragment"
+ )
+ end
+
+ it "should not be empty" do
+ expect(@uri).not_to be_empty
+ end
+
+ it "should not be frozen" do
+ expect(@uri).not_to be_frozen
+ end
+
+ it "should allow destructive operations" do
+ expect { @uri.normalize! }.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when parsed from a frozen string" do
+ before do
+ @uri = Addressable::URI.parse(
+ "http://user:password@example.com:8080/path?query=value#fragment".freeze
+ )
+ end
+
+ it "returns 'http' for #scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "returns 'http' for #normalized_scheme" do
+ expect(@uri.normalized_scheme).to eq("http")
+ end
+
+ it "returns 'user' for #user" do
+ expect(@uri.user).to eq("user")
+ end
+
+ it "returns 'user' for #normalized_user" do
+ expect(@uri.normalized_user).to eq("user")
+ end
+
+ it "returns 'password' for #password" do
+ expect(@uri.password).to eq("password")
+ end
+
+ it "returns 'password' for #normalized_password" do
+ expect(@uri.normalized_password).to eq("password")
+ end
+
+ it "returns 'user:password' for #userinfo" do
+ expect(@uri.userinfo).to eq("user:password")
+ end
+
+ it "returns 'user:password' for #normalized_userinfo" do
+ expect(@uri.normalized_userinfo).to eq("user:password")
+ end
+
+ it "returns 'example.com' for #host" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "returns 'example.com' for #normalized_host" do
+ expect(@uri.normalized_host).to eq("example.com")
+ end
+
+ it "returns 'user:password@example.com:8080' for #authority" do
+ expect(@uri.authority).to eq("user:password@example.com:8080")
+ end
+
+ it "returns 'user:password@example.com:8080' for #normalized_authority" do
+ expect(@uri.normalized_authority).to eq("user:password@example.com:8080")
+ end
+
+ it "returns 8080 for #port" do
+ expect(@uri.port).to eq(8080)
+ end
+
+ it "returns 8080 for #normalized_port" do
+ expect(@uri.normalized_port).to eq(8080)
+ end
+
+ it "returns 80 for #default_port" do
+ expect(@uri.default_port).to eq(80)
+ end
+
+ it "returns 'http://user:password@example.com:8080' for #site" do
+ expect(@uri.site).to eq("http://user:password@example.com:8080")
+ end
+
+ it "returns 'http://user:password@example.com:8080' for #normalized_site" do
+ expect(@uri.normalized_site).to eq("http://user:password@example.com:8080")
+ end
+
+ it "returns '/path' for #path" do
+ expect(@uri.path).to eq("/path")
+ end
+
+ it "returns '/path' for #normalized_path" do
+ expect(@uri.normalized_path).to eq("/path")
+ end
+
+ it "returns 'query=value' for #query" do
+ expect(@uri.query).to eq("query=value")
+ end
+
+ it "returns 'query=value' for #normalized_query" do
+ expect(@uri.normalized_query).to eq("query=value")
+ end
+
+ it "returns 'fragment' for #fragment" do
+ expect(@uri.fragment).to eq("fragment")
+ end
+
+ it "returns 'fragment' for #normalized_fragment" do
+ expect(@uri.normalized_fragment).to eq("fragment")
+ end
+
+ it "returns #hash" do
+ expect(@uri.hash).not_to be nil
+ end
+
+ it "returns #to_s" do
+ expect(@uri.to_s).to eq(
+ "http://user:password@example.com:8080/path?query=value#fragment"
+ )
+ end
+
+ it "should not be empty" do
+ expect(@uri).not_to be_empty
+ end
+
+ it "should not be frozen" do
+ expect(@uri).not_to be_frozen
+ end
+
+ it "should allow destructive operations" do
+ expect { @uri.normalize! }.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when frozen" do
+ before do
+ @uri = Addressable::URI.new.freeze
+ end
+
+ it "returns nil for #scheme" do
+ expect(@uri.scheme).to eq(nil)
+ end
+
+ it "returns nil for #normalized_scheme" do
+ expect(@uri.normalized_scheme).to eq(nil)
+ end
+
+ it "returns nil for #user" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "returns nil for #normalized_user" do
+ expect(@uri.normalized_user).to eq(nil)
+ end
+
+ it "returns nil for #password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "returns nil for #normalized_password" do
+ expect(@uri.normalized_password).to eq(nil)
+ end
+
+ it "returns nil for #userinfo" do
+ expect(@uri.userinfo).to eq(nil)
+ end
+
+ it "returns nil for #normalized_userinfo" do
+ expect(@uri.normalized_userinfo).to eq(nil)
+ end
+
+ it "returns nil for #host" do
+ expect(@uri.host).to eq(nil)
+ end
+
+ it "returns nil for #normalized_host" do
+ expect(@uri.normalized_host).to eq(nil)
+ end
+
+ it "returns nil for #authority" do
+ expect(@uri.authority).to eq(nil)
+ end
+
+ it "returns nil for #normalized_authority" do
+ expect(@uri.normalized_authority).to eq(nil)
+ end
+
+ it "returns nil for #port" do
+ expect(@uri.port).to eq(nil)
+ end
+
+ it "returns nil for #normalized_port" do
+ expect(@uri.normalized_port).to eq(nil)
+ end
+
+ it "returns nil for #default_port" do
+ expect(@uri.default_port).to eq(nil)
+ end
+
+ it "returns nil for #site" do
+ expect(@uri.site).to eq(nil)
+ end
+
+ it "returns nil for #normalized_site" do
+ expect(@uri.normalized_site).to eq(nil)
+ end
+
+ it "returns '' for #path" do
+ expect(@uri.path).to eq('')
+ end
+
+ it "returns '' for #normalized_path" do
+ expect(@uri.normalized_path).to eq('')
+ end
+
+ it "returns nil for #query" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "returns nil for #normalized_query" do
+ expect(@uri.normalized_query).to eq(nil)
+ end
+
+ it "returns nil for #fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+
+ it "returns nil for #normalized_fragment" do
+ expect(@uri.normalized_fragment).to eq(nil)
+ end
+
+ it "returns #hash" do
+ expect(@uri.hash).not_to be nil
+ end
+
+ it "returns #to_s" do
+ expect(@uri.to_s).to eq('')
+ end
+
+ it "should be empty" do
+ expect(@uri).to be_empty
+ end
+
+ it "should be frozen" do
+ expect(@uri).to be_frozen
+ end
+
+ it "should not be frozen after duping" do
+ expect(@uri.dup).not_to be_frozen
+ end
+
+ it "should not allow destructive operations" do
+ expect { @uri.normalize! }.to raise_error { |error|
+ expect(error.message).to match(/can't modify frozen/)
+ expect(error).to satisfy { |e| RuntimeError === e || TypeError === e }
+ }
+ end
+end
+
+describe Addressable::URI, "when frozen" do
+ before do
+ @uri = Addressable::URI.parse(
+ "HTTP://example.com.:%38%30/%70a%74%68?a=%31#1%323"
+ ).freeze
+ end
+
+ it "returns 'HTTP' for #scheme" do
+ expect(@uri.scheme).to eq("HTTP")
+ end
+
+ it "returns 'http' for #normalized_scheme" do
+ expect(@uri.normalized_scheme).to eq("http")
+ expect(@uri.normalize.scheme).to eq("http")
+ end
+
+ it "returns nil for #user" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "returns nil for #normalized_user" do
+ expect(@uri.normalized_user).to eq(nil)
+ end
+
+ it "returns nil for #password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "returns nil for #normalized_password" do
+ expect(@uri.normalized_password).to eq(nil)
+ end
+
+ it "returns nil for #userinfo" do
+ expect(@uri.userinfo).to eq(nil)
+ end
+
+ it "returns nil for #normalized_userinfo" do
+ expect(@uri.normalized_userinfo).to eq(nil)
+ end
+
+ it "returns 'example.com.' for #host" do
+ expect(@uri.host).to eq("example.com.")
+ end
+
+ it "returns nil for #normalized_host" do
+ expect(@uri.normalized_host).to eq("example.com")
+ expect(@uri.normalize.host).to eq("example.com")
+ end
+
+ it "returns 'example.com.:80' for #authority" do
+ expect(@uri.authority).to eq("example.com.:80")
+ end
+
+ it "returns 'example.com:80' for #normalized_authority" do
+ expect(@uri.normalized_authority).to eq("example.com")
+ expect(@uri.normalize.authority).to eq("example.com")
+ end
+
+ it "returns 80 for #port" do
+ expect(@uri.port).to eq(80)
+ end
+
+ it "returns nil for #normalized_port" do
+ expect(@uri.normalized_port).to eq(nil)
+ expect(@uri.normalize.port).to eq(nil)
+ end
+
+ it "returns 80 for #default_port" do
+ expect(@uri.default_port).to eq(80)
+ end
+
+ it "returns 'HTTP://example.com.:80' for #site" do
+ expect(@uri.site).to eq("HTTP://example.com.:80")
+ end
+
+ it "returns 'http://example.com' for #normalized_site" do
+ expect(@uri.normalized_site).to eq("http://example.com")
+ expect(@uri.normalize.site).to eq("http://example.com")
+ end
+
+ it "returns '/%70a%74%68' for #path" do
+ expect(@uri.path).to eq("/%70a%74%68")
+ end
+
+ it "returns '/path' for #normalized_path" do
+ expect(@uri.normalized_path).to eq("/path")
+ expect(@uri.normalize.path).to eq("/path")
+ end
+
+ it "returns 'a=%31' for #query" do
+ expect(@uri.query).to eq("a=%31")
+ end
+
+ it "returns 'a=1' for #normalized_query" do
+ expect(@uri.normalized_query).to eq("a=1")
+ expect(@uri.normalize.query).to eq("a=1")
+ end
+
+ it "returns '/%70a%74%68?a=%31' for #request_uri" do
+ expect(@uri.request_uri).to eq("/%70a%74%68?a=%31")
+ end
+
+ it "returns '1%323' for #fragment" do
+ expect(@uri.fragment).to eq("1%323")
+ end
+
+ it "returns '123' for #normalized_fragment" do
+ expect(@uri.normalized_fragment).to eq("123")
+ expect(@uri.normalize.fragment).to eq("123")
+ end
+
+ it "returns #hash" do
+ expect(@uri.hash).not_to be nil
+ end
+
+ it "returns #to_s" do
+ expect(@uri.to_s).to eq('HTTP://example.com.:80/%70a%74%68?a=%31#1%323')
+ expect(@uri.normalize.to_s).to eq('http://example.com/path?a=1#123')
+ end
+
+ it "should not be empty" do
+ expect(@uri).not_to be_empty
+ end
+
+ it "should be frozen" do
+ expect(@uri).to be_frozen
+ end
+
+ it "should not be frozen after duping" do
+ expect(@uri.dup).not_to be_frozen
+ end
+
+ it "should not allow destructive operations" do
+ expect { @uri.normalize! }.to raise_error { |error|
+ expect(error.message).to match(/can't modify frozen/)
+ expect(error).to satisfy { |e| RuntimeError === e || TypeError === e }
+ }
+ end
+end
+
+describe Addressable::URI, "when created from string components" do
+ before do
+ @uri = Addressable::URI.new(
+ :scheme => "http", :host => "example.com"
+ )
+ end
+
+ it "should have a site value of 'http://example.com'" do
+ expect(@uri.site).to eq("http://example.com")
+ end
+
+ it "should be equal to the equivalent parsed URI" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com"))
+ end
+
+ it "should raise an error if invalid components omitted" do
+ expect do
+ @uri.omit(:bogus)
+ end.to raise_error(ArgumentError)
+ expect do
+ @uri.omit(:scheme, :bogus, :path)
+ end.to raise_error(ArgumentError)
+ end
+end
+
+describe Addressable::URI, "when created with a nil host but " +
+ "non-nil authority components" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:user => "user", :password => "pass", :port => 80)
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when created with both an authority and a user" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(
+ :user => "user", :authority => "user@example.com:80"
+ )
+ end.to raise_error(ArgumentError)
+ end
+end
+
+describe Addressable::URI, "when created with an authority and no port" do
+ before do
+ @uri = Addressable::URI.new(:authority => "user@example.com")
+ end
+
+ it "should not infer a port" do
+ expect(@uri.port).to eq(nil)
+ expect(@uri.default_port).to eq(nil)
+ expect(@uri.inferred_port).to eq(nil)
+ end
+
+ it "should have a site value of '//user@example.com'" do
+ expect(@uri.site).to eq("//user@example.com")
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when created with a host with trailing dots" do
+ before do
+ @uri = Addressable::URI.new(:authority => "example...")
+ end
+
+ it "should have a stable normalized form" do
+ expect(@uri.normalize.normalize.normalize.host).to eq(
+ @uri.normalize.host
+ )
+ end
+end
+
+describe Addressable::URI, "when created with a host with a backslash" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:authority => "example\\example")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when created with a host with a slash" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:authority => "example/example")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when created with a host with a space" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:authority => "example example")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when created with both a userinfo and a user" do
+ it "should raise an error" do
+ expect do
+ Addressable::URI.new(:user => "user", :userinfo => "user:pass")
+ end.to raise_error(ArgumentError)
+ end
+end
+
+describe Addressable::URI, "when created with a path that hasn't been " +
+ "prefixed with a '/' but a host specified" do
+ before do
+ @uri = Addressable::URI.new(
+ :scheme => "http", :host => "example.com", :path => "path"
+ )
+ end
+
+ it "should prefix a '/' to the path" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com/path"))
+ end
+
+ it "should have a site value of 'http://example.com'" do
+ expect(@uri.site).to eq("http://example.com")
+ end
+
+ it "should have an origin of 'http://example.com" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+end
+
+describe Addressable::URI, "when created with a path that hasn't been " +
+ "prefixed with a '/' but no host specified" do
+ before do
+ @uri = Addressable::URI.new(
+ :scheme => "http", :path => "path"
+ )
+ end
+
+ it "should not prefix a '/' to the path" do
+ expect(@uri).to eq(Addressable::URI.parse("http:path"))
+ end
+
+ it "should have a site value of 'http:'" do
+ expect(@uri.site).to eq("http:")
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when parsed from an Addressable::URI object" do
+ it "should not have unexpected side-effects" do
+ original_uri = Addressable::URI.parse("http://example.com/")
+ new_uri = Addressable::URI.parse(original_uri)
+ new_uri.host = 'www.example.com'
+ expect(new_uri.host).to eq('www.example.com')
+ expect(new_uri.to_s).to eq('http://www.example.com/')
+ expect(original_uri.host).to eq('example.com')
+ expect(original_uri.to_s).to eq('http://example.com/')
+ end
+
+ it "should not have unexpected side-effects" do
+ original_uri = Addressable::URI.parse("http://example.com/")
+ new_uri = Addressable::URI.heuristic_parse(original_uri)
+ new_uri.host = 'www.example.com'
+ expect(new_uri.host).to eq('www.example.com')
+ expect(new_uri.to_s).to eq('http://www.example.com/')
+ expect(original_uri.host).to eq('example.com')
+ expect(original_uri.to_s).to eq('http://example.com/')
+ end
+
+ it "should not have unexpected side-effects" do
+ original_uri = Addressable::URI.parse("http://example.com/")
+ new_uri = Addressable::URI.parse(original_uri)
+ new_uri.origin = 'https://www.example.com:8080'
+ expect(new_uri.host).to eq('www.example.com')
+ expect(new_uri.to_s).to eq('https://www.example.com:8080/')
+ expect(original_uri.host).to eq('example.com')
+ expect(original_uri.to_s).to eq('http://example.com/')
+ end
+
+ it "should not have unexpected side-effects" do
+ original_uri = Addressable::URI.parse("http://example.com/")
+ new_uri = Addressable::URI.heuristic_parse(original_uri)
+ new_uri.origin = 'https://www.example.com:8080'
+ expect(new_uri.host).to eq('www.example.com')
+ expect(new_uri.to_s).to eq('https://www.example.com:8080/')
+ expect(original_uri.host).to eq('example.com')
+ expect(original_uri.to_s).to eq('http://example.com/')
+ end
+end
+
+describe Addressable::URI, "when parsed from something that looks " +
+ "like a URI object" do
+ it "should parse without error" do
+ uri = Addressable::URI.parse(Fake::URI::HTTP.new("http://example.com/"))
+ expect do
+ Addressable::URI.parse(uri)
+ end.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when parsed from a standard library URI object" do
+ it "should parse without error" do
+ uri = Addressable::URI.parse(URI.parse("http://example.com/"))
+ expect do
+ Addressable::URI.parse(uri)
+ end.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when parsed from ''" do
+ before do
+ @uri = Addressable::URI.parse("")
+ end
+
+ it "should have no scheme" do
+ expect(@uri.scheme).to eq(nil)
+ end
+
+ it "should not be considered to be ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should have a path of ''" do
+ expect(@uri.path).to eq("")
+ end
+
+ it "should have a request URI of '/'" do
+ expect(@uri.request_uri).to eq("/")
+ end
+
+ it "should be considered relative" do
+ expect(@uri).to be_relative
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+# Section 1.1.2 of RFC 3986
+describe Addressable::URI, "when parsed from " +
+ "'ftp://ftp.is.co.za/rfc/rfc1808.txt'" do
+ before do
+ @uri = Addressable::URI.parse("ftp://ftp.is.co.za/rfc/rfc1808.txt")
+ end
+
+ it "should use the 'ftp' scheme" do
+ expect(@uri.scheme).to eq("ftp")
+ end
+
+ it "should be considered to be ip-based" do
+ expect(@uri).to be_ip_based
+ end
+
+ it "should have a host of 'ftp.is.co.za'" do
+ expect(@uri.host).to eq("ftp.is.co.za")
+ end
+
+ it "should have inferred_port of 21" do
+ expect(@uri.inferred_port).to eq(21)
+ end
+
+ it "should have a path of '/rfc/rfc1808.txt'" do
+ expect(@uri.path).to eq("/rfc/rfc1808.txt")
+ end
+
+ it "should not have a request URI" do
+ expect(@uri.request_uri).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have an origin of 'ftp://ftp.is.co.za'" do
+ expect(@uri.origin).to eq('ftp://ftp.is.co.za')
+ end
+end
+
+# Section 1.1.2 of RFC 3986
+describe Addressable::URI, "when parsed from " +
+ "'http://www.ietf.org/rfc/rfc2396.txt'" do
+ before do
+ @uri = Addressable::URI.parse("http://www.ietf.org/rfc/rfc2396.txt")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should be considered to be ip-based" do
+ expect(@uri).to be_ip_based
+ end
+
+ it "should have a host of 'www.ietf.org'" do
+ expect(@uri.host).to eq("www.ietf.org")
+ end
+
+ it "should have inferred_port of 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have a path of '/rfc/rfc2396.txt'" do
+ expect(@uri.path).to eq("/rfc/rfc2396.txt")
+ end
+
+ it "should have a request URI of '/rfc/rfc2396.txt'" do
+ expect(@uri.request_uri).to eq("/rfc/rfc2396.txt")
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should correctly omit components" do
+ expect(@uri.omit(:scheme).to_s).to eq("//www.ietf.org/rfc/rfc2396.txt")
+ expect(@uri.omit(:path).to_s).to eq("http://www.ietf.org")
+ end
+
+ it "should correctly omit components destructively" do
+ @uri.omit!(:scheme)
+ expect(@uri.to_s).to eq("//www.ietf.org/rfc/rfc2396.txt")
+ end
+
+ it "should have an origin of 'http://www.ietf.org'" do
+ expect(@uri.origin).to eq('http://www.ietf.org')
+ end
+end
+
+# Section 1.1.2 of RFC 3986
+describe Addressable::URI, "when parsed from " +
+ "'ldap://[2001:db8::7]/c=GB?objectClass?one'" do
+ before do
+ @uri = Addressable::URI.parse("ldap://[2001:db8::7]/c=GB?objectClass?one")
+ end
+
+ it "should use the 'ldap' scheme" do
+ expect(@uri.scheme).to eq("ldap")
+ end
+
+ it "should be considered to be ip-based" do
+ expect(@uri).to be_ip_based
+ end
+
+ it "should have a host of '[2001:db8::7]'" do
+ expect(@uri.host).to eq("[2001:db8::7]")
+ end
+
+ it "should have inferred_port of 389" do
+ expect(@uri.inferred_port).to eq(389)
+ end
+
+ it "should have a path of '/c=GB'" do
+ expect(@uri.path).to eq("/c=GB")
+ end
+
+ it "should not have a request URI" do
+ expect(@uri.request_uri).to eq(nil)
+ end
+
+ it "should not allow request URI assignment" do
+ expect do
+ @uri.request_uri = "/"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should have a query of 'objectClass?one'" do
+ expect(@uri.query).to eq("objectClass?one")
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should correctly omit components" do
+ expect(@uri.omit(:scheme, :authority).to_s).to eq("/c=GB?objectClass?one")
+ expect(@uri.omit(:path).to_s).to eq("ldap://[2001:db8::7]?objectClass?one")
+ end
+
+ it "should correctly omit components destructively" do
+ @uri.omit!(:scheme, :authority)
+ expect(@uri.to_s).to eq("/c=GB?objectClass?one")
+ end
+
+ it "should raise an error if omission would create an invalid URI" do
+ expect do
+ @uri.omit(:authority, :path)
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should have an origin of 'ldap://[2001:db8::7]'" do
+ expect(@uri.origin).to eq('ldap://[2001:db8::7]')
+ end
+end
+
+# Section 1.1.2 of RFC 3986
+describe Addressable::URI, "when parsed from " +
+ "'mailto:John.Doe@example.com'" do
+ before do
+ @uri = Addressable::URI.parse("mailto:John.Doe@example.com")
+ end
+
+ it "should use the 'mailto' scheme" do
+ expect(@uri.scheme).to eq("mailto")
+ end
+
+ it "should not be considered to be ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should not have an inferred_port" do
+ expect(@uri.inferred_port).to eq(nil)
+ end
+
+ it "should have a path of 'John.Doe@example.com'" do
+ expect(@uri.path).to eq("John.Doe@example.com")
+ end
+
+ it "should not have a request URI" do
+ expect(@uri.request_uri).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+# Section 2 of RFC 6068
+describe Addressable::URI, "when parsed from " +
+ "'mailto:?to=addr1@an.example,addr2@an.example'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "mailto:?to=addr1@an.example,addr2@an.example"
+ )
+ end
+
+ it "should use the 'mailto' scheme" do
+ expect(@uri.scheme).to eq("mailto")
+ end
+
+ it "should not be considered to be ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should not have an inferred_port" do
+ expect(@uri.inferred_port).to eq(nil)
+ end
+
+ it "should have a path of ''" do
+ expect(@uri.path).to eq("")
+ end
+
+ it "should not have a request URI" do
+ expect(@uri.request_uri).to eq(nil)
+ end
+
+ it "should have the To: field value parameterized" do
+ expect(@uri.query_values(Hash)["to"]).to eq(
+ "addr1@an.example,addr2@an.example"
+ )
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+# Section 1.1.2 of RFC 3986
+describe Addressable::URI, "when parsed from " +
+ "'news:comp.infosystems.www.servers.unix'" do
+ before do
+ @uri = Addressable::URI.parse("news:comp.infosystems.www.servers.unix")
+ end
+
+ it "should use the 'news' scheme" do
+ expect(@uri.scheme).to eq("news")
+ end
+
+ it "should not have an inferred_port" do
+ expect(@uri.inferred_port).to eq(nil)
+ end
+
+ it "should not be considered to be ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should have a path of 'comp.infosystems.www.servers.unix'" do
+ expect(@uri.path).to eq("comp.infosystems.www.servers.unix")
+ end
+
+ it "should not have a request URI" do
+ expect(@uri.request_uri).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+# Section 1.1.2 of RFC 3986
+describe Addressable::URI, "when parsed from " +
+ "'tel:+1-816-555-1212'" do
+ before do
+ @uri = Addressable::URI.parse("tel:+1-816-555-1212")
+ end
+
+ it "should use the 'tel' scheme" do
+ expect(@uri.scheme).to eq("tel")
+ end
+
+ it "should not be considered to be ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should not have an inferred_port" do
+ expect(@uri.inferred_port).to eq(nil)
+ end
+
+ it "should have a path of '+1-816-555-1212'" do
+ expect(@uri.path).to eq("+1-816-555-1212")
+ end
+
+ it "should not have a request URI" do
+ expect(@uri.request_uri).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+# Section 1.1.2 of RFC 3986
+describe Addressable::URI, "when parsed from " +
+ "'telnet://192.0.2.16:80/'" do
+ before do
+ @uri = Addressable::URI.parse("telnet://192.0.2.16:80/")
+ end
+
+ it "should use the 'telnet' scheme" do
+ expect(@uri.scheme).to eq("telnet")
+ end
+
+ it "should have a host of '192.0.2.16'" do
+ expect(@uri.host).to eq("192.0.2.16")
+ end
+
+ it "should have a port of 80" do
+ expect(@uri.port).to eq(80)
+ end
+
+ it "should have a inferred_port of 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have a default_port of 23" do
+ expect(@uri.default_port).to eq(23)
+ end
+
+ it "should be considered to be ip-based" do
+ expect(@uri).to be_ip_based
+ end
+
+ it "should have a path of '/'" do
+ expect(@uri.path).to eq("/")
+ end
+
+ it "should not have a request URI" do
+ expect(@uri.request_uri).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have an origin of 'telnet://192.0.2.16:80'" do
+ expect(@uri.origin).to eq('telnet://192.0.2.16:80')
+ end
+end
+
+# Section 1.1.2 of RFC 3986
+describe Addressable::URI, "when parsed from " +
+ "'urn:oasis:names:specification:docbook:dtd:xml:4.1.2'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "urn:oasis:names:specification:docbook:dtd:xml:4.1.2")
+ end
+
+ it "should use the 'urn' scheme" do
+ expect(@uri.scheme).to eq("urn")
+ end
+
+ it "should not have an inferred_port" do
+ expect(@uri.inferred_port).to eq(nil)
+ end
+
+ it "should not be considered to be ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should have a path of " +
+ "'oasis:names:specification:docbook:dtd:xml:4.1.2'" do
+ expect(@uri.path).to eq("oasis:names:specification:docbook:dtd:xml:4.1.2")
+ end
+
+ it "should not have a request URI" do
+ expect(@uri.request_uri).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when heuristically parsed from " +
+ "'192.0.2.16:8000/path'" do
+ before do
+ @uri = Addressable::URI.heuristic_parse("192.0.2.16:8000/path")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have a host of '192.0.2.16'" do
+ expect(@uri.host).to eq("192.0.2.16")
+ end
+
+ it "should have a port of '8000'" do
+ expect(@uri.port).to eq(8000)
+ end
+
+ it "should be considered to be ip-based" do
+ expect(@uri).to be_ip_based
+ end
+
+ it "should have a path of '/path'" do
+ expect(@uri.path).to eq("/path")
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have an origin of 'http://192.0.2.16:8000'" do
+ expect(@uri.origin).to eq('http://192.0.2.16:8000')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com")
+ end
+
+ it "when inspected, should have the correct URI" do
+ expect(@uri.inspect).to include("http://example.com")
+ end
+
+ it "when inspected, should have the correct class name" do
+ expect(@uri.inspect).to include("Addressable::URI")
+ end
+
+ it "when inspected, should have the correct object id" do
+ expect(@uri.inspect).to include("%#0x" % @uri.object_id)
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should be considered to be ip-based" do
+ expect(@uri).to be_ip_based
+ end
+
+ it "should have an authority segment of 'example.com'" do
+ expect(@uri.authority).to eq("example.com")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should be considered ip-based" do
+ expect(@uri).to be_ip_based
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should not have a specified port" do
+ expect(@uri.port).to eq(nil)
+ end
+
+ it "should have an empty path" do
+ expect(@uri.path).to eq("")
+ end
+
+ it "should have no query string" do
+ expect(@uri.query).to eq(nil)
+ expect(@uri.query_values).to eq(nil)
+ end
+
+ it "should have a request URI of '/'" do
+ expect(@uri.request_uri).to eq("/")
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+
+ it "should be considered absolute" do
+ expect(@uri).to be_absolute
+ end
+
+ it "should not be considered relative" do
+ expect(@uri).not_to be_relative
+ end
+
+ it "should not be exactly equal to 42" do
+ expect(@uri.eql?(42)).to eq(false)
+ end
+
+ it "should not be equal to 42" do
+ expect(@uri == 42).to eq(false)
+ end
+
+ it "should not be roughly equal to 42" do
+ expect(@uri === 42).to eq(false)
+ end
+
+ it "should be exactly equal to http://example.com" do
+ expect(@uri.eql?(Addressable::URI.parse("http://example.com"))).to eq(true)
+ end
+
+ it "should be roughly equal to http://example.com/" do
+ expect(@uri === Addressable::URI.parse("http://example.com/")).to eq(true)
+ end
+
+ it "should be roughly equal to the string 'http://example.com/'" do
+ expect(@uri === "http://example.com/").to eq(true)
+ end
+
+ it "should not be roughly equal to the string " +
+ "'http://example.com:bogus/'" do
+ expect do
+ expect(@uri === "http://example.com:bogus/").to eq(false)
+ end.not_to raise_error
+ end
+
+ it "should result in itself when joined with itself" do
+ expect(@uri.join(@uri).to_s).to eq("http://example.com")
+ expect(@uri.join!(@uri).to_s).to eq("http://example.com")
+ end
+
+ it "should be equivalent to http://EXAMPLE.com" do
+ expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com"))
+ end
+
+ it "should be equivalent to http://EXAMPLE.com:80/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com:80/"))
+ end
+
+ it "should have the same hash as http://example.com" do
+ expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com").hash)
+ end
+
+ it "should have the same hash as http://EXAMPLE.com after assignment" do
+ @uri.origin = "http://EXAMPLE.com"
+ expect(@uri.hash).to eq(Addressable::URI.parse("http://EXAMPLE.com").hash)
+ end
+
+ it "should have a different hash from http://EXAMPLE.com" do
+ expect(@uri.hash).not_to eq(Addressable::URI.parse("http://EXAMPLE.com").hash)
+ end
+
+ it "should not allow origin assignment without scheme" do
+ expect do
+ @uri.origin = "example.com"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should not allow origin assignment without host" do
+ expect do
+ @uri.origin = "http://"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should not allow origin assignment with bogus type" do
+ expect do
+ @uri.origin = :bogus
+ end.to raise_error(TypeError)
+ end
+
+ # Section 6.2.3 of RFC 3986
+ it "should be equivalent to http://example.com/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com/"))
+ end
+
+ # Section 6.2.3 of RFC 3986
+ it "should be equivalent to http://example.com:/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com:/"))
+ end
+
+ # Section 6.2.3 of RFC 3986
+ it "should be equivalent to http://example.com:80/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com:80/"))
+ end
+
+ # Section 6.2.2.1 of RFC 3986
+ it "should be equivalent to http://EXAMPLE.COM/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.COM/"))
+ end
+
+ it "should have a route of '/path/' to 'http://example.com/path/'" do
+ expect(@uri.route_to("http://example.com/path/")).to eq(
+ Addressable::URI.parse("/path/")
+ )
+ end
+
+ it "should have a route of '..' from 'http://example.com/path/'" do
+ expect(@uri.route_from("http://example.com/path/")).to eq(
+ Addressable::URI.parse("..")
+ )
+ end
+
+ it "should have a route of '#' to 'http://example.com/'" do
+ expect(@uri.route_to("http://example.com/")).to eq(
+ Addressable::URI.parse("#")
+ )
+ end
+
+ it "should have a route of 'http://elsewhere.com/' to " +
+ "'http://elsewhere.com/'" do
+ expect(@uri.route_to("http://elsewhere.com/")).to eq(
+ Addressable::URI.parse("http://elsewhere.com/")
+ )
+ end
+
+ it "when joined with 'relative/path' should be " +
+ "'http://example.com/relative/path'" do
+ expect(@uri.join('relative/path')).to eq(
+ Addressable::URI.parse("http://example.com/relative/path")
+ )
+ end
+
+ it "when joined with a bogus object a TypeError should be raised" do
+ expect do
+ @uri.join(42)
+ end.to raise_error(TypeError)
+ end
+
+ it "should have the correct username after assignment" do
+ @uri.user = "newuser"
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq(nil)
+ expect(@uri.to_s).to eq("http://newuser@example.com")
+ end
+
+ it "should have the correct username after assignment" do
+ @uri.user = "user@123!"
+ expect(@uri.user).to eq("user@123!")
+ expect(@uri.normalized_user).to eq("user%40123%21")
+ expect(@uri.password).to eq(nil)
+ expect(@uri.normalize.to_s).to eq("http://user%40123%21@example.com/")
+ end
+
+ it "should have the correct password after assignment" do
+ @uri.password = "newpass"
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.user).to eq("")
+ expect(@uri.to_s).to eq("http://:newpass@example.com")
+ end
+
+ it "should have the correct password after assignment" do
+ @uri.password = "#secret@123!"
+ expect(@uri.password).to eq("#secret@123!")
+ expect(@uri.normalized_password).to eq("%23secret%40123%21")
+ expect(@uri.user).to eq("")
+ expect(@uri.normalize.to_s).to eq("http://:%23secret%40123%21@example.com/")
+ expect(@uri.omit(:password).to_s).to eq("http://example.com")
+ end
+
+ it "should have the correct user/pass after repeated assignment" do
+ @uri.user = nil
+ expect(@uri.user).to eq(nil)
+ @uri.password = "newpass"
+ expect(@uri.password).to eq("newpass")
+ # Username cannot be nil if the password is set
+ expect(@uri.user).to eq("")
+ expect(@uri.to_s).to eq("http://:newpass@example.com")
+ @uri.user = "newuser"
+ expect(@uri.user).to eq("newuser")
+ @uri.password = nil
+ expect(@uri.password).to eq(nil)
+ expect(@uri.to_s).to eq("http://newuser@example.com")
+ @uri.user = "newuser"
+ expect(@uri.user).to eq("newuser")
+ @uri.password = ""
+ expect(@uri.password).to eq("")
+ expect(@uri.to_s).to eq("http://newuser:@example.com")
+ @uri.password = "newpass"
+ expect(@uri.password).to eq("newpass")
+ @uri.user = nil
+ # Username cannot be nil if the password is set
+ expect(@uri.user).to eq("")
+ expect(@uri.to_s).to eq("http://:newpass@example.com")
+ end
+
+ it "should have the correct user/pass after userinfo assignment" do
+ @uri.user = "newuser"
+ expect(@uri.user).to eq("newuser")
+ @uri.password = "newpass"
+ expect(@uri.password).to eq("newpass")
+ @uri.userinfo = nil
+ expect(@uri.userinfo).to eq(nil)
+ expect(@uri.user).to eq(nil)
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "http",
+ :user => nil,
+ :password => nil,
+ :host => "example.com",
+ :port => nil,
+ :path => "",
+ :query => nil,
+ :fragment => nil
+ })
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+end
+
+# Section 5.1.2 of RFC 2616
+describe Addressable::URI, "when parsed from " +
+ "'HTTP://www.w3.org/pub/WWW/TheProject.html'" do
+ before do
+ @uri = Addressable::URI.parse("HTTP://www.w3.org/pub/WWW/TheProject.html")
+ end
+
+ it "should have the correct request URI" do
+ expect(@uri.request_uri).to eq("/pub/WWW/TheProject.html")
+ end
+
+ it "should have the correct request URI after assignment" do
+ @uri.request_uri = "/pub/WWW/TheProject.html?"
+ expect(@uri.request_uri).to eq("/pub/WWW/TheProject.html?")
+ expect(@uri.path).to eq("/pub/WWW/TheProject.html")
+ expect(@uri.query).to eq("")
+ end
+
+ it "should have the correct request URI after assignment" do
+ @uri.request_uri = "/some/where/else.html"
+ expect(@uri.request_uri).to eq("/some/where/else.html")
+ expect(@uri.path).to eq("/some/where/else.html")
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have the correct request URI after assignment" do
+ @uri.request_uri = "/some/where/else.html?query?string"
+ expect(@uri.request_uri).to eq("/some/where/else.html?query?string")
+ expect(@uri.path).to eq("/some/where/else.html")
+ expect(@uri.query).to eq("query?string")
+ end
+
+ it "should have the correct request URI after assignment" do
+ @uri.request_uri = "?x=y"
+ expect(@uri.request_uri).to eq("/?x=y")
+ expect(@uri.path).to eq("/")
+ expect(@uri.query).to eq("x=y")
+ end
+
+ it "should raise an error if the site value is set to something bogus" do
+ expect do
+ @uri.site = 42
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise an error if the request URI is set to something bogus" do
+ expect do
+ @uri.request_uri = 42
+ end.to raise_error(TypeError)
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "HTTP",
+ :user => nil,
+ :password => nil,
+ :host => "www.w3.org",
+ :port => nil,
+ :path => "/pub/WWW/TheProject.html",
+ :query => nil,
+ :fragment => nil
+ })
+ end
+
+ it "should have an origin of 'http://www.w3.org'" do
+ expect(@uri.origin).to eq('http://www.w3.org')
+ end
+end
+
+describe Addressable::URI, "when parsing IPv6 addresses" do
+ it "should not raise an error for " +
+ "'http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/'" do
+ Addressable::URI.parse("http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/")
+ end
+
+ it "should not raise an error for " +
+ "'http://[fe80:0:0:0:200:f8ff:fe21:67cf]/'" do
+ Addressable::URI.parse("http://[fe80:0:0:0:200:f8ff:fe21:67cf]/")
+ end
+
+ it "should not raise an error for " +
+ "'http://[fe80::200:f8ff:fe21:67cf]/'" do
+ Addressable::URI.parse("http://[fe80::200:f8ff:fe21:67cf]/")
+ end
+
+ it "should not raise an error for " +
+ "'http://[::1]/'" do
+ Addressable::URI.parse("http://[::1]/")
+ end
+
+ it "should not raise an error for " +
+ "'http://[fe80::1]/'" do
+ Addressable::URI.parse("http://[fe80::1]/")
+ end
+
+ it "should raise an error for " +
+ "'http://[]/'" do
+ expect do
+ Addressable::URI.parse("http://[]/")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when parsing IPv6 address" do
+ subject { Addressable::URI.parse("http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/") }
+ its(:host) { should == '[3ffe:1900:4545:3:200:f8ff:fe21:67cf]' }
+ its(:hostname) { should == '3ffe:1900:4545:3:200:f8ff:fe21:67cf' }
+end
+
+describe Addressable::URI, "when assigning IPv6 address" do
+ it "should allow to set bare IPv6 address as hostname" do
+ uri = Addressable::URI.parse("http://[::1]/")
+ uri.hostname = '3ffe:1900:4545:3:200:f8ff:fe21:67cf'
+ expect(uri.to_s).to eq('http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/')
+ end
+
+ it "should allow to set bare IPv6 address as hostname with IPAddr object" do
+ uri = Addressable::URI.parse("http://[::1]/")
+ uri.hostname = IPAddr.new('3ffe:1900:4545:3:200:f8ff:fe21:67cf')
+ expect(uri.to_s).to eq('http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/')
+ end
+
+ it "should not allow to set bare IPv6 address as host" do
+ uri = Addressable::URI.parse("http://[::1]/")
+ skip "not checked"
+ expect do
+ uri.host = '3ffe:1900:4545:3:200:f8ff:fe21:67cf'
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when parsing IPvFuture addresses" do
+ it "should not raise an error for " +
+ "'http://[v9.3ffe:1900:4545:3:200:f8ff:fe21:67cf]/'" do
+ Addressable::URI.parse("http://[v9.3ffe:1900:4545:3:200:f8ff:fe21:67cf]/")
+ end
+
+ it "should not raise an error for " +
+ "'http://[vff.fe80:0:0:0:200:f8ff:fe21:67cf]/'" do
+ Addressable::URI.parse("http://[vff.fe80:0:0:0:200:f8ff:fe21:67cf]/")
+ end
+
+ it "should not raise an error for " +
+ "'http://[v12.fe80::200:f8ff:fe21:67cf]/'" do
+ Addressable::URI.parse("http://[v12.fe80::200:f8ff:fe21:67cf]/")
+ end
+
+ it "should not raise an error for " +
+ "'http://[va0.::1]/'" do
+ Addressable::URI.parse("http://[va0.::1]/")
+ end
+
+ it "should not raise an error for " +
+ "'http://[v255.fe80::1]/'" do
+ Addressable::URI.parse("http://[v255.fe80::1]/")
+ end
+
+ it "should raise an error for " +
+ "'http://[v0.]/'" do
+ expect do
+ Addressable::URI.parse("http://[v0.]/")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/")
+ end
+
+ # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence
+ it "should be equivalent to http://example.com" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com"))
+ end
+
+ # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence
+ it "should be equivalent to HTTP://example.com/" do
+ expect(@uri).to eq(Addressable::URI.parse("HTTP://example.com/"))
+ end
+
+ # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence
+ it "should be equivalent to http://example.com:/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com:/"))
+ end
+
+ # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence
+ it "should be equivalent to http://example.com:80/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com:80/"))
+ end
+
+ # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence
+ it "should be equivalent to http://Example.com/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://Example.com/"))
+ end
+
+ it "should have the correct username after assignment" do
+ @uri.user = nil
+ expect(@uri.user).to eq(nil)
+ expect(@uri.password).to eq(nil)
+ expect(@uri.to_s).to eq("http://example.com/")
+ end
+
+ it "should have the correct password after assignment" do
+ @uri.password = nil
+ expect(@uri.password).to eq(nil)
+ expect(@uri.user).to eq(nil)
+ expect(@uri.to_s).to eq("http://example.com/")
+ end
+
+ it "should have a request URI of '/'" do
+ expect(@uri.request_uri).to eq("/")
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "http",
+ :user => nil,
+ :password => nil,
+ :host => "example.com",
+ :port => nil,
+ :path => "/",
+ :query => nil,
+ :fragment => nil
+ })
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+
+ it "should have the same hash as its duplicate" do
+ expect(@uri.hash).to eq(@uri.dup.hash)
+ end
+
+ it "should have a different hash from its equivalent String value" do
+ expect(@uri.hash).not_to eq(@uri.to_s.hash)
+ end
+
+ it "should have the same hash as an equal URI" do
+ expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/").hash)
+ end
+
+ it "should be equivalent to http://EXAMPLE.com" do
+ expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com"))
+ end
+
+ it "should be equivalent to http://EXAMPLE.com:80/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com:80/"))
+ end
+
+ it "should have the same hash as http://example.com/" do
+ expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/").hash)
+ end
+
+ it "should have the same hash as http://example.com after assignment" do
+ @uri.path = ""
+ expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com").hash)
+ end
+
+ it "should have the same hash as http://example.com/? after assignment" do
+ @uri.query = ""
+ expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/?").hash)
+ end
+
+ it "should have the same hash as http://example.com/? after assignment" do
+ @uri.query_values = {}
+ expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/?").hash)
+ end
+
+ it "should have the same hash as http://example.com/# after assignment" do
+ @uri.fragment = ""
+ expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/#").hash)
+ end
+
+ it "should have a different hash from http://example.com" do
+ expect(@uri.hash).not_to eq(Addressable::URI.parse("http://example.com").hash)
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com?#'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com?#")
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "http",
+ :user => nil,
+ :password => nil,
+ :host => "example.com",
+ :port => nil,
+ :path => "",
+ :query => "",
+ :fragment => ""
+ })
+ end
+
+ it "should have a request URI of '/?'" do
+ expect(@uri.request_uri).to eq("/?")
+ end
+
+ it "should normalize to 'http://example.com/'" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/")
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq("http://example.com")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://@example.com/'" do
+ before do
+ @uri = Addressable::URI.parse("http://@example.com/")
+ end
+
+ it "should be equivalent to http://example.com" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com"))
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "http",
+ :user => "",
+ :password => nil,
+ :host => "example.com",
+ :port => nil,
+ :path => "/",
+ :query => nil,
+ :fragment => nil
+ })
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com./'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com./")
+ end
+
+ it "should be equivalent to http://example.com" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com"))
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://:@example.com/'" do
+ before do
+ @uri = Addressable::URI.parse("http://:@example.com/")
+ end
+
+ it "should be equivalent to http://example.com" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com"))
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "http",
+ :user => "",
+ :password => "",
+ :host => "example.com",
+ :port => nil,
+ :path => "/",
+ :query => nil,
+ :fragment => nil
+ })
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'HTTP://EXAMPLE.COM/'" do
+ before do
+ @uri = Addressable::URI.parse("HTTP://EXAMPLE.COM/")
+ end
+
+ it "should be equivalent to http://example.com" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com"))
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "HTTP",
+ :user => nil,
+ :password => nil,
+ :host => "EXAMPLE.COM",
+ :port => nil,
+ :path => "/",
+ :query => nil,
+ :fragment => nil
+ })
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+
+ it "should have a tld of 'com'" do
+ expect(@uri.tld).to eq('com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://www.example.co.uk/'" do
+ before do
+ @uri = Addressable::URI.parse("http://www.example.co.uk/")
+ end
+
+ it "should have an origin of 'http://www.example.co.uk'" do
+ expect(@uri.origin).to eq('http://www.example.co.uk')
+ end
+
+ it "should have a tld of 'co.uk'" do
+ expect(@uri.tld).to eq('co.uk')
+ end
+
+ it "should have a domain of 'example.co.uk'" do
+ expect(@uri.domain).to eq('example.co.uk')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://sub_domain.blogspot.com/'" do
+ before do
+ @uri = Addressable::URI.parse("http://sub_domain.blogspot.com/")
+ end
+
+ it "should have an origin of 'http://sub_domain.blogspot.com'" do
+ expect(@uri.origin).to eq('http://sub_domain.blogspot.com')
+ end
+
+ it "should have a tld of 'com'" do
+ expect(@uri.tld).to eq('com')
+ end
+
+ it "should have a domain of 'blogspot.com'" do
+ expect(@uri.domain).to eq('blogspot.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/~smith/'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/~smith/")
+ end
+
+ # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence
+ it "should be equivalent to http://example.com/%7Esmith/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com/%7Esmith/"))
+ end
+
+ # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence
+ it "should be equivalent to http://example.com/%7esmith/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com/%7esmith/"))
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/%E8'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/%E8")
+ end
+
+ it "should not raise an exception when normalized" do
+ expect do
+ @uri.normalize
+ end.not_to raise_error
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should not change if encoded with the normalizing algorithm" do
+ expect(Addressable::URI.normalized_encode(@uri).to_s).to eq(
+ "http://example.com/%E8"
+ )
+ expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be ===
+ "http://example.com/%E8"
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/path%2Fsegment/'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/path%2Fsegment/")
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should be equal to 'http://example.com/path%2Fsegment/'" do
+ expect(@uri.normalize).to be_eql(
+ Addressable::URI.parse("http://example.com/path%2Fsegment/")
+ )
+ end
+
+ it "should not be equal to 'http://example.com/path/segment/'" do
+ expect(@uri).not_to eq(
+ Addressable::URI.parse("http://example.com/path/segment/")
+ )
+ end
+
+ it "should not be equal to 'http://example.com/path/segment/'" do
+ expect(@uri.normalize).not_to be_eql(
+ Addressable::URI.parse("http://example.com/path/segment/")
+ )
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?%F6'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?%F6")
+ end
+
+ it "should not raise an exception when normalized" do
+ expect do
+ @uri.normalize
+ end.not_to raise_error
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should not change if encoded with the normalizing algorithm" do
+ expect(Addressable::URI.normalized_encode(@uri).to_s).to eq(
+ "http://example.com/?%F6"
+ )
+ expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be ===
+ "http://example.com/?%F6"
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/#%F6'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/#%F6")
+ end
+
+ it "should not raise an exception when normalized" do
+ expect do
+ @uri.normalize
+ end.not_to raise_error
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should not change if encoded with the normalizing algorithm" do
+ expect(Addressable::URI.normalized_encode(@uri).to_s).to eq(
+ "http://example.com/#%F6"
+ )
+ expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be ===
+ "http://example.com/#%F6"
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/%C3%87'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/%C3%87")
+ end
+
+ # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence
+ it "should be equivalent to 'http://example.com/C%CC%A7'" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com/C%CC%A7"))
+ end
+
+ it "should not change if encoded with the normalizing algorithm" do
+ expect(Addressable::URI.normalized_encode(@uri).to_s).to eq(
+ "http://example.com/%C3%87"
+ )
+ expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be ===
+ "http://example.com/%C3%87"
+ end
+
+ it "should raise an error if encoding with an unexpected return type" do
+ expect do
+ Addressable::URI.normalized_encode(@uri, Integer)
+ end.to raise_error(TypeError)
+ end
+
+ it "if percent encoded should be 'http://example.com/C%25CC%25A7'" do
+ expect(Addressable::URI.encode(@uri).to_s).to eq(
+ "http://example.com/%25C3%2587"
+ )
+ end
+
+ it "if percent encoded should be 'http://example.com/C%25CC%25A7'" do
+ expect(Addressable::URI.encode(@uri, Addressable::URI)).to eq(
+ Addressable::URI.parse("http://example.com/%25C3%2587")
+ )
+ end
+
+ it "should raise an error if encoding with an unexpected return type" do
+ expect do
+ Addressable::URI.encode(@uri, Integer)
+ end.to raise_error(TypeError)
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?q=string'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?q=string")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have an authority segment of 'example.com'" do
+ expect(@uri.authority).to eq("example.com")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have a path of '/'" do
+ expect(@uri.path).to eq("/")
+ end
+
+ it "should have a query string of 'q=string'" do
+ expect(@uri.query).to eq("q=string")
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+
+ it "should be considered absolute" do
+ expect(@uri).to be_absolute
+ end
+
+ it "should not be considered relative" do
+ expect(@uri).not_to be_relative
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com:80/'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com:80/")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have an authority segment of 'example.com:80'" do
+ expect(@uri.authority).to eq("example.com:80")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have explicit port 80" do
+ expect(@uri.port).to eq(80)
+ end
+
+ it "should have a path of '/'" do
+ expect(@uri.path).to eq("/")
+ end
+
+ it "should have no query string" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+
+ it "should be considered absolute" do
+ expect(@uri).to be_absolute
+ end
+
+ it "should not be considered relative" do
+ expect(@uri).not_to be_relative
+ end
+
+ it "should be exactly equal to http://example.com:80/" do
+ expect(@uri.eql?(Addressable::URI.parse("http://example.com:80/"))).to eq(true)
+ end
+
+ it "should be roughly equal to http://example.com/" do
+ expect(@uri === Addressable::URI.parse("http://example.com/")).to eq(true)
+ end
+
+ it "should be roughly equal to the string 'http://example.com/'" do
+ expect(@uri === "http://example.com/").to eq(true)
+ end
+
+ it "should not be roughly equal to the string " +
+ "'http://example.com:bogus/'" do
+ expect do
+ expect(@uri === "http://example.com:bogus/").to eq(false)
+ end.not_to raise_error
+ end
+
+ it "should result in itself when joined with itself" do
+ expect(@uri.join(@uri).to_s).to eq("http://example.com:80/")
+ expect(@uri.join!(@uri).to_s).to eq("http://example.com:80/")
+ end
+
+ # Section 6.2.3 of RFC 3986
+ it "should be equal to http://example.com/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com/"))
+ end
+
+ # Section 6.2.3 of RFC 3986
+ it "should be equal to http://example.com:/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com:/"))
+ end
+
+ # Section 6.2.3 of RFC 3986
+ it "should be equal to http://example.com:80/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://example.com:80/"))
+ end
+
+ # Section 6.2.2.1 of RFC 3986
+ it "should be equal to http://EXAMPLE.COM/" do
+ expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.COM/"))
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "http",
+ :user => nil,
+ :password => nil,
+ :host => "example.com",
+ :port => 80,
+ :path => "/",
+ :query => nil,
+ :fragment => nil
+ })
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+
+ it "should not change if encoded with the normalizing algorithm" do
+ expect(Addressable::URI.normalized_encode(@uri).to_s).to eq(
+ "http://example.com:80/"
+ )
+ expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be ===
+ "http://example.com:80/"
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com:8080/'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com:8080/")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have an authority segment of 'example.com:8080'" do
+ expect(@uri.authority).to eq("example.com:8080")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should use port 8080" do
+ expect(@uri.inferred_port).to eq(8080)
+ end
+
+ it "should have explicit port 8080" do
+ expect(@uri.port).to eq(8080)
+ end
+
+ it "should have default port 80" do
+ expect(@uri.default_port).to eq(80)
+ end
+
+ it "should have a path of '/'" do
+ expect(@uri.path).to eq("/")
+ end
+
+ it "should have no query string" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+
+ it "should be considered absolute" do
+ expect(@uri).to be_absolute
+ end
+
+ it "should not be considered relative" do
+ expect(@uri).not_to be_relative
+ end
+
+ it "should be exactly equal to http://example.com:8080/" do
+ expect(@uri.eql?(Addressable::URI.parse(
+ "http://example.com:8080/"))).to eq(true)
+ end
+
+ it "should have a route of 'http://example.com:8080/' from " +
+ "'http://example.com/path/to/'" do
+ expect(@uri.route_from("http://example.com/path/to/")).to eq(
+ Addressable::URI.parse("http://example.com:8080/")
+ )
+ end
+
+ it "should have a route of 'http://example.com:8080/' from " +
+ "'http://example.com:80/path/to/'" do
+ expect(@uri.route_from("http://example.com:80/path/to/")).to eq(
+ Addressable::URI.parse("http://example.com:8080/")
+ )
+ end
+
+ it "should have a route of '../../' from " +
+ "'http://example.com:8080/path/to/'" do
+ expect(@uri.route_from("http://example.com:8080/path/to/")).to eq(
+ Addressable::URI.parse("../../")
+ )
+ end
+
+ it "should have a route of 'http://example.com:8080/' from " +
+ "'http://user:pass@example.com/path/to/'" do
+ expect(@uri.route_from("http://user:pass@example.com/path/to/")).to eq(
+ Addressable::URI.parse("http://example.com:8080/")
+ )
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "http",
+ :user => nil,
+ :password => nil,
+ :host => "example.com",
+ :port => 8080,
+ :path => "/",
+ :query => nil,
+ :fragment => nil
+ })
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+
+ it "should have an origin of 'http://example.com:8080'" do
+ expect(@uri.origin).to eq('http://example.com:8080')
+ end
+
+ it "should not change if encoded with the normalizing algorithm" do
+ expect(Addressable::URI.normalized_encode(@uri).to_s).to eq(
+ "http://example.com:8080/"
+ )
+ expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be ===
+ "http://example.com:8080/"
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com:%38%30/'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com:%38%30/")
+ end
+
+ it "should have the correct port" do
+ expect(@uri.port).to eq(80)
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ it "should normalize to 'http://example.com/'" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/")
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/%2E/'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/%2E/")
+ end
+
+ it "should be considered to be in normal form" do
+ skip(
+ 'path segment normalization should happen before ' +
+ 'percent escaping normalization'
+ )
+ @uri.normalize.should be_eql(@uri)
+ end
+
+ it "should normalize to 'http://example.com/%2E/'" do
+ skip(
+ 'path segment normalization should happen before ' +
+ 'percent escaping normalization'
+ )
+ expect(@uri.normalize).to eq("http://example.com/%2E/")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/..'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/..")
+ end
+
+ it "should have the correct port" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ it "should normalize to 'http://example.com/'" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/../..'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/../..")
+ end
+
+ it "should have the correct port" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ it "should normalize to 'http://example.com/'" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/path(/..'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/path(/..")
+ end
+
+ it "should have the correct port" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ it "should normalize to 'http://example.com/'" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/(path)/..'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/(path)/..")
+ end
+
+ it "should have the correct port" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ it "should normalize to 'http://example.com/'" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/path(/../'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/path(/../")
+ end
+
+ it "should have the correct port" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ it "should normalize to 'http://example.com/'" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/(path)/../'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/(path)/../")
+ end
+
+ it "should have the correct port" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ it "should normalize to 'http://example.com/'" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'/..//example.com'" do
+ before do
+ @uri = Addressable::URI.parse("/..//example.com")
+ end
+
+ it "should become invalid when normalized" do
+ expect do
+ @uri.normalize
+ end.to raise_error(Addressable::URI::InvalidURIError, /authority/)
+ end
+
+ it "should have a path of '/..//example.com'" do
+ expect(@uri.path).to eq("/..//example.com")
+ end
+end
+
+describe Addressable::URI, "when parsed from '/a/b/c/./../../g'" do
+ before do
+ @uri = Addressable::URI.parse("/a/b/c/./../../g")
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ # Section 5.2.4 of RFC 3986
+ it "should normalize to '/a/g'" do
+ expect(@uri.normalize.to_s).to eq("/a/g")
+ end
+end
+
+describe Addressable::URI, "when parsed from 'mid/content=5/../6'" do
+ before do
+ @uri = Addressable::URI.parse("mid/content=5/../6")
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ # Section 5.2.4 of RFC 3986
+ it "should normalize to 'mid/6'" do
+ expect(@uri.normalize.to_s).to eq("mid/6")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://www.example.com///../'" do
+ before do
+ @uri = Addressable::URI.parse('http://www.example.com///../')
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+
+ it "should normalize to 'http://www.example.com//'" do
+ expect(@uri.normalize.to_s).to eq("http://www.example.com//")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/path/to/resource/'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/path/to/resource/")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have an authority segment of 'example.com'" do
+ expect(@uri.authority).to eq("example.com")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have a path of '/path/to/resource/'" do
+ expect(@uri.path).to eq("/path/to/resource/")
+ end
+
+ it "should have no query string" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+
+ it "should be considered absolute" do
+ expect(@uri).to be_absolute
+ end
+
+ it "should not be considered relative" do
+ expect(@uri).not_to be_relative
+ end
+
+ it "should be exactly equal to http://example.com:8080/" do
+ expect(@uri.eql?(Addressable::URI.parse(
+ "http://example.com/path/to/resource/"))).to eq(true)
+ end
+
+ it "should have a route of 'resource/' from " +
+ "'http://example.com/path/to/'" do
+ expect(@uri.route_from("http://example.com/path/to/")).to eq(
+ Addressable::URI.parse("resource/")
+ )
+ end
+
+ it "should have a route of '../' from " +
+ "'http://example.com/path/to/resource/sub'" do
+ expect(@uri.route_from("http://example.com/path/to/resource/sub")).to eq(
+ Addressable::URI.parse("../")
+ )
+ end
+
+
+ it "should have a route of 'resource/' from " +
+ "'http://example.com/path/to/another'" do
+ expect(@uri.route_from("http://example.com/path/to/another")).to eq(
+ Addressable::URI.parse("resource/")
+ )
+ end
+
+ it "should have a route of 'resource/' from " +
+ "'http://example.com/path/to/res'" do
+ expect(@uri.route_from("http://example.com/path/to/res")).to eq(
+ Addressable::URI.parse("resource/")
+ )
+ end
+
+ it "should have a route of 'resource/' from " +
+ "'http://example.com:80/path/to/'" do
+ expect(@uri.route_from("http://example.com:80/path/to/")).to eq(
+ Addressable::URI.parse("resource/")
+ )
+ end
+
+ it "should have a route of 'http://example.com/path/to/' from " +
+ "'http://example.com:8080/path/to/'" do
+ expect(@uri.route_from("http://example.com:8080/path/to/")).to eq(
+ Addressable::URI.parse("http://example.com/path/to/resource/")
+ )
+ end
+
+ it "should have a route of 'http://example.com/path/to/' from " +
+ "'http://user:pass@example.com/path/to/'" do
+ expect(@uri.route_from("http://user:pass@example.com/path/to/")).to eq(
+ Addressable::URI.parse("http://example.com/path/to/resource/")
+ )
+ end
+
+ it "should have a route of '../../path/to/resource/' from " +
+ "'http://example.com/to/resource/'" do
+ expect(@uri.route_from("http://example.com/to/resource/")).to eq(
+ Addressable::URI.parse("../../path/to/resource/")
+ )
+ end
+
+ it "should correctly convert to a hash" do
+ expect(@uri.to_hash).to eq({
+ :scheme => "http",
+ :user => nil,
+ :password => nil,
+ :host => "example.com",
+ :port => nil,
+ :path => "/path/to/resource/",
+ :query => nil,
+ :fragment => nil
+ })
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'relative/path/to/resource'" do
+ before do
+ @uri = Addressable::URI.parse("relative/path/to/resource")
+ end
+
+ it "should not have a scheme" do
+ expect(@uri.scheme).to eq(nil)
+ end
+
+ it "should not be considered ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should not have an authority segment" do
+ expect(@uri.authority).to eq(nil)
+ end
+
+ it "should not have a host" do
+ expect(@uri.host).to eq(nil)
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should not have a port" do
+ expect(@uri.port).to eq(nil)
+ end
+
+ it "should have a path of 'relative/path/to/resource'" do
+ expect(@uri.path).to eq("relative/path/to/resource")
+ end
+
+ it "should have no query string" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+
+ it "should not be considered absolute" do
+ expect(@uri).not_to be_absolute
+ end
+
+ it "should be considered relative" do
+ expect(@uri).to be_relative
+ end
+
+ it "should raise an error if routing is attempted" do
+ expect do
+ @uri.route_to("http://example.com/")
+ end.to raise_error(ArgumentError, /relative\/path\/to\/resource/)
+ expect do
+ @uri.route_from("http://example.com/")
+ end.to raise_error(ArgumentError, /relative\/path\/to\/resource/)
+ end
+
+ it "when joined with 'another/relative/path' should be " +
+ "'relative/path/to/another/relative/path'" do
+ expect(@uri.join('another/relative/path')).to eq(
+ Addressable::URI.parse("relative/path/to/another/relative/path")
+ )
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'relative_path_with_no_slashes'" do
+ before do
+ @uri = Addressable::URI.parse("relative_path_with_no_slashes")
+ end
+
+ it "should not have a scheme" do
+ expect(@uri.scheme).to eq(nil)
+ end
+
+ it "should not be considered ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should not have an authority segment" do
+ expect(@uri.authority).to eq(nil)
+ end
+
+ it "should not have a host" do
+ expect(@uri.host).to eq(nil)
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should not have a port" do
+ expect(@uri.port).to eq(nil)
+ end
+
+ it "should have a path of 'relative_path_with_no_slashes'" do
+ expect(@uri.path).to eq("relative_path_with_no_slashes")
+ end
+
+ it "should have no query string" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+
+ it "should not be considered absolute" do
+ expect(@uri).not_to be_absolute
+ end
+
+ it "should be considered relative" do
+ expect(@uri).to be_relative
+ end
+
+ it "when joined with 'another_relative_path' should be " +
+ "'another_relative_path'" do
+ expect(@uri.join('another_relative_path')).to eq(
+ Addressable::URI.parse("another_relative_path")
+ )
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/file.txt'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/file.txt")
+ end
+
+ it "should have a scheme of 'http'" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have an authority segment of 'example.com'" do
+ expect(@uri.authority).to eq("example.com")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have a path of '/file.txt'" do
+ expect(@uri.path).to eq("/file.txt")
+ end
+
+ it "should have a basename of 'file.txt'" do
+ expect(@uri.basename).to eq("file.txt")
+ end
+
+ it "should have an extname of '.txt'" do
+ expect(@uri.extname).to eq(".txt")
+ end
+
+ it "should have no query string" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/file.txt;parameter'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/file.txt;parameter")
+ end
+
+ it "should have a scheme of 'http'" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have an authority segment of 'example.com'" do
+ expect(@uri.authority).to eq("example.com")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have a path of '/file.txt;parameter'" do
+ expect(@uri.path).to eq("/file.txt;parameter")
+ end
+
+ it "should have a basename of 'file.txt'" do
+ expect(@uri.basename).to eq("file.txt")
+ end
+
+ it "should have an extname of '.txt'" do
+ expect(@uri.extname).to eq(".txt")
+ end
+
+ it "should have no query string" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/file.txt;x=y'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/file.txt;x=y")
+ end
+
+ it "should have a scheme of 'http'" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have a scheme of 'http'" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have an authority segment of 'example.com'" do
+ expect(@uri.authority).to eq("example.com")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should have no username" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have a path of '/file.txt;x=y'" do
+ expect(@uri.path).to eq("/file.txt;x=y")
+ end
+
+ it "should have an extname of '.txt'" do
+ expect(@uri.extname).to eq(".txt")
+ end
+
+ it "should have no query string" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have no fragment" do
+ expect(@uri.fragment).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'svn+ssh://developername@rubyforge.org/var/svn/project'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "svn+ssh://developername@rubyforge.org/var/svn/project"
+ )
+ end
+
+ it "should have a scheme of 'svn+ssh'" do
+ expect(@uri.scheme).to eq("svn+ssh")
+ end
+
+ it "should be considered to be ip-based" do
+ expect(@uri).to be_ip_based
+ end
+
+ it "should have a path of '/var/svn/project'" do
+ expect(@uri.path).to eq("/var/svn/project")
+ end
+
+ it "should have a username of 'developername'" do
+ expect(@uri.user).to eq("developername")
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'ssh+svn://developername@RUBYFORGE.ORG/var/svn/project'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "ssh+svn://developername@RUBYFORGE.ORG/var/svn/project"
+ )
+ end
+
+ it "should have a scheme of 'ssh+svn'" do
+ expect(@uri.scheme).to eq("ssh+svn")
+ end
+
+ it "should have a normalized scheme of 'svn+ssh'" do
+ expect(@uri.normalized_scheme).to eq("svn+ssh")
+ end
+
+ it "should have a normalized site of 'svn+ssh'" do
+ expect(@uri.normalized_site).to eq("svn+ssh://developername@rubyforge.org")
+ end
+
+ it "should not be considered to be ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should have a path of '/var/svn/project'" do
+ expect(@uri.path).to eq("/var/svn/project")
+ end
+
+ it "should have a username of 'developername'" do
+ expect(@uri.user).to eq("developername")
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should not be considered to be in normal form" do
+ expect(@uri.normalize).not_to be_eql(@uri)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'mailto:user@example.com'" do
+ before do
+ @uri = Addressable::URI.parse("mailto:user@example.com")
+ end
+
+ it "should have a scheme of 'mailto'" do
+ expect(@uri.scheme).to eq("mailto")
+ end
+
+ it "should not be considered to be ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should have a path of 'user@example.com'" do
+ expect(@uri.path).to eq("user@example.com")
+ end
+
+ it "should have no user" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'tag:example.com,2006-08-18:/path/to/something'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "tag:example.com,2006-08-18:/path/to/something")
+ end
+
+ it "should have a scheme of 'tag'" do
+ expect(@uri.scheme).to eq("tag")
+ end
+
+ it "should be considered to be ip-based" do
+ expect(@uri).not_to be_ip_based
+ end
+
+ it "should have a path of " +
+ "'example.com,2006-08-18:/path/to/something'" do
+ expect(@uri.path).to eq("example.com,2006-08-18:/path/to/something")
+ end
+
+ it "should have no user" do
+ expect(@uri.user).to eq(nil)
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/x;y/'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/x;y/")
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?x=1&y=2'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?x=1&y=2")
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'view-source:http://example.com/'" do
+ before do
+ @uri = Addressable::URI.parse("view-source:http://example.com/")
+ end
+
+ it "should have a scheme of 'view-source'" do
+ expect(@uri.scheme).to eq("view-source")
+ end
+
+ it "should have a path of 'http://example.com/'" do
+ expect(@uri.path).to eq("http://example.com/")
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://user:pass@example.com/path/to/resource?query=x#fragment'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "http://user:pass@example.com/path/to/resource?query=x#fragment")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have an authority segment of 'user:pass@example.com'" do
+ expect(@uri.authority).to eq("user:pass@example.com")
+ end
+
+ it "should have a username of 'user'" do
+ expect(@uri.user).to eq("user")
+ end
+
+ it "should have a password of 'pass'" do
+ expect(@uri.password).to eq("pass")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have a path of '/path/to/resource'" do
+ expect(@uri.path).to eq("/path/to/resource")
+ end
+
+ it "should have a query string of 'query=x'" do
+ expect(@uri.query).to eq("query=x")
+ end
+
+ it "should have a fragment of 'fragment'" do
+ expect(@uri.fragment).to eq("fragment")
+ end
+
+ it "should be considered to be in normal form" do
+ expect(@uri.normalize).to be_eql(@uri)
+ end
+
+ it "should have a route of '../../' to " +
+ "'http://user:pass@example.com/path/'" do
+ expect(@uri.route_to("http://user:pass@example.com/path/")).to eq(
+ Addressable::URI.parse("../../")
+ )
+ end
+
+ it "should have a route of 'to/resource?query=x#fragment' " +
+ "from 'http://user:pass@example.com/path/'" do
+ expect(@uri.route_from("http://user:pass@example.com/path/")).to eq(
+ Addressable::URI.parse("to/resource?query=x#fragment")
+ )
+ end
+
+ it "should have a route of '?query=x#fragment' " +
+ "from 'http://user:pass@example.com/path/to/resource'" do
+ expect(@uri.route_from("http://user:pass@example.com/path/to/resource")).to eq(
+ Addressable::URI.parse("?query=x#fragment")
+ )
+ end
+
+ it "should have a route of '#fragment' " +
+ "from 'http://user:pass@example.com/path/to/resource?query=x'" do
+ expect(@uri.route_from(
+ "http://user:pass@example.com/path/to/resource?query=x")).to eq(
+ Addressable::URI.parse("#fragment")
+ )
+ end
+
+ it "should have a route of '#fragment' from " +
+ "'http://user:pass@example.com/path/to/resource?query=x#fragment'" do
+ expect(@uri.route_from(
+ "http://user:pass@example.com/path/to/resource?query=x#fragment"
+ )).to eq(Addressable::URI.parse("#fragment"))
+ end
+
+ it "should have a route of 'http://elsewhere.com/' to " +
+ "'http://elsewhere.com/'" do
+ expect(@uri.route_to("http://elsewhere.com/")).to eq(
+ Addressable::URI.parse("http://elsewhere.com/")
+ )
+ end
+
+ it "should have a route of " +
+ "'http://user:pass@example.com/path/to/resource?query=x#fragment' " +
+ "from 'http://example.com/path/to/'" do
+ expect(@uri.route_from("http://elsewhere.com/path/to/")).to eq(
+ Addressable::URI.parse(
+ "http://user:pass@example.com/path/to/resource?query=x#fragment")
+ )
+ end
+
+ it "should have the correct scheme after assignment" do
+ @uri.scheme = "ftp"
+ expect(@uri.scheme).to eq("ftp")
+ expect(@uri.to_s).to eq(
+ "ftp://user:pass@example.com/path/to/resource?query=x#fragment"
+ )
+ expect(@uri.to_str).to eq(
+ "ftp://user:pass@example.com/path/to/resource?query=x#fragment"
+ )
+ end
+
+ it "should have the correct site segment after assignment" do
+ @uri.site = "https://newuser:newpass@example.com:443"
+ expect(@uri.scheme).to eq("https")
+ expect(@uri.authority).to eq("newuser:newpass@example.com:443")
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.userinfo).to eq("newuser:newpass")
+ expect(@uri.normalized_userinfo).to eq("newuser:newpass")
+ expect(@uri.host).to eq("example.com")
+ expect(@uri.port).to eq(443)
+ expect(@uri.inferred_port).to eq(443)
+ expect(@uri.to_s).to eq(
+ "https://newuser:newpass@example.com:443" +
+ "/path/to/resource?query=x#fragment"
+ )
+ end
+
+ it "should have the correct authority segment after assignment" do
+ @uri.authority = "newuser:newpass@example.com:80"
+ expect(@uri.authority).to eq("newuser:newpass@example.com:80")
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.userinfo).to eq("newuser:newpass")
+ expect(@uri.normalized_userinfo).to eq("newuser:newpass")
+ expect(@uri.host).to eq("example.com")
+ expect(@uri.port).to eq(80)
+ expect(@uri.inferred_port).to eq(80)
+ expect(@uri.to_s).to eq(
+ "http://newuser:newpass@example.com:80" +
+ "/path/to/resource?query=x#fragment"
+ )
+ end
+
+ it "should have the correct userinfo segment after assignment" do
+ @uri.userinfo = "newuser:newpass"
+ expect(@uri.userinfo).to eq("newuser:newpass")
+ expect(@uri.authority).to eq("newuser:newpass@example.com")
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.host).to eq("example.com")
+ expect(@uri.port).to eq(nil)
+ expect(@uri.inferred_port).to eq(80)
+ expect(@uri.to_s).to eq(
+ "http://newuser:newpass@example.com" +
+ "/path/to/resource?query=x#fragment"
+ )
+ end
+
+ it "should have the correct username after assignment" do
+ @uri.user = "newuser"
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.authority).to eq("newuser:pass@example.com")
+ end
+
+ it "should have the correct password after assignment" do
+ @uri.password = "newpass"
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.authority).to eq("user:newpass@example.com")
+ end
+
+ it "should have the correct host after assignment" do
+ @uri.host = "newexample.com"
+ expect(@uri.host).to eq("newexample.com")
+ expect(@uri.authority).to eq("user:pass@newexample.com")
+ end
+
+ it "should have the correct host after assignment" do
+ @uri.hostname = "newexample.com"
+ expect(@uri.host).to eq("newexample.com")
+ expect(@uri.hostname).to eq("newexample.com")
+ expect(@uri.authority).to eq("user:pass@newexample.com")
+ end
+
+ it "should raise an error if assigning a bogus object to the hostname" do
+ expect do
+ @uri.hostname = Object.new
+ end.to raise_error(TypeError)
+ end
+
+ it "should have the correct port after assignment" do
+ @uri.port = 8080
+ expect(@uri.port).to eq(8080)
+ expect(@uri.authority).to eq("user:pass@example.com:8080")
+ end
+
+ it "should have the correct origin after assignment" do
+ @uri.origin = "http://newexample.com"
+ expect(@uri.host).to eq("newexample.com")
+ expect(@uri.authority).to eq("newexample.com")
+ end
+
+ it "should have the correct path after assignment" do
+ @uri.path = "/newpath/to/resource"
+ expect(@uri.path).to eq("/newpath/to/resource")
+ expect(@uri.to_s).to eq(
+ "http://user:pass@example.com/newpath/to/resource?query=x#fragment"
+ )
+ end
+
+ it "should have the correct scheme and authority after nil assignment" do
+ @uri.site = nil
+ expect(@uri.scheme).to eq(nil)
+ expect(@uri.authority).to eq(nil)
+ expect(@uri.to_s).to eq("/path/to/resource?query=x#fragment")
+ end
+
+ it "should have the correct scheme and authority after assignment" do
+ @uri.site = "file://"
+ expect(@uri.scheme).to eq("file")
+ expect(@uri.authority).to eq("")
+ expect(@uri.to_s).to eq("file:///path/to/resource?query=x#fragment")
+ end
+
+ it "should have the correct path after nil assignment" do
+ @uri.path = nil
+ expect(@uri.path).to eq("")
+ expect(@uri.to_s).to eq(
+ "http://user:pass@example.com?query=x#fragment"
+ )
+ end
+
+ it "should have the correct query string after assignment" do
+ @uri.query = "newquery=x"
+ expect(@uri.query).to eq("newquery=x")
+ expect(@uri.to_s).to eq(
+ "http://user:pass@example.com/path/to/resource?newquery=x#fragment"
+ )
+ @uri.query = nil
+ expect(@uri.query).to eq(nil)
+ expect(@uri.to_s).to eq(
+ "http://user:pass@example.com/path/to/resource#fragment"
+ )
+ end
+
+ it "should have the correct query string after hash assignment" do
+ @uri.query_values = {"?uestion mark" => "=sign", "hello" => "g\xC3\xBCnther"}
+ expect(@uri.query.split("&")).to include("%3Fuestion%20mark=%3Dsign")
+ expect(@uri.query.split("&")).to include("hello=g%C3%BCnther")
+ expect(@uri.query_values).to eq({
+ "?uestion mark" => "=sign", "hello" => "g\xC3\xBCnther"
+ })
+ end
+
+ it "should have the correct query string after flag hash assignment" do
+ @uri.query_values = {'flag?1' => nil, 'fl=ag2' => nil, 'flag3' => nil}
+ expect(@uri.query.split("&")).to include("flag%3F1")
+ expect(@uri.query.split("&")).to include("fl%3Dag2")
+ expect(@uri.query.split("&")).to include("flag3")
+ expect(@uri.query_values(Array).sort).to eq([["fl=ag2"], ["flag3"], ["flag?1"]])
+ expect(@uri.query_values(Hash)).to eq({
+ 'flag?1' => nil, 'fl=ag2' => nil, 'flag3' => nil
+ })
+ end
+
+ it "should raise an error if query values are set to a bogus type" do
+ expect do
+ @uri.query_values = "bogus"
+ end.to raise_error(TypeError)
+ end
+
+ it "should have the correct fragment after assignment" do
+ @uri.fragment = "newfragment"
+ expect(@uri.fragment).to eq("newfragment")
+ expect(@uri.to_s).to eq(
+ "http://user:pass@example.com/path/to/resource?query=x#newfragment"
+ )
+
+ @uri.fragment = nil
+ expect(@uri.fragment).to eq(nil)
+ expect(@uri.to_s).to eq(
+ "http://user:pass@example.com/path/to/resource?query=x"
+ )
+ end
+
+ it "should have the correct values after a merge" do
+ expect(@uri.merge(:fragment => "newfragment").to_s).to eq(
+ "http://user:pass@example.com/path/to/resource?query=x#newfragment"
+ )
+ end
+
+ it "should have the correct values after a merge" do
+ expect(@uri.merge(:fragment => nil).to_s).to eq(
+ "http://user:pass@example.com/path/to/resource?query=x"
+ )
+ end
+
+ it "should have the correct values after a merge" do
+ expect(@uri.merge(:userinfo => "newuser:newpass").to_s).to eq(
+ "http://newuser:newpass@example.com/path/to/resource?query=x#fragment"
+ )
+ end
+
+ it "should have the correct values after a merge" do
+ expect(@uri.merge(:userinfo => nil).to_s).to eq(
+ "http://example.com/path/to/resource?query=x#fragment"
+ )
+ end
+
+ it "should have the correct values after a merge" do
+ expect(@uri.merge(:path => "newpath").to_s).to eq(
+ "http://user:pass@example.com/newpath?query=x#fragment"
+ )
+ end
+
+ it "should have the correct values after a merge" do
+ expect(@uri.merge(:port => "42", :path => "newpath", :query => "").to_s).to eq(
+ "http://user:pass@example.com:42/newpath?#fragment"
+ )
+ end
+
+ it "should have the correct values after a merge" do
+ expect(@uri.merge(:authority => "foo:bar@baz:42").to_s).to eq(
+ "http://foo:bar@baz:42/path/to/resource?query=x#fragment"
+ )
+ # Ensure the operation was not destructive
+ expect(@uri.to_s).to eq(
+ "http://user:pass@example.com/path/to/resource?query=x#fragment"
+ )
+ end
+
+ it "should have the correct values after a destructive merge" do
+ @uri.merge!(:authority => "foo:bar@baz:42")
+ # Ensure the operation was destructive
+ expect(@uri.to_s).to eq(
+ "http://foo:bar@baz:42/path/to/resource?query=x#fragment"
+ )
+ end
+
+ it "should fail to merge with bogus values" do
+ expect do
+ @uri.merge(:port => "bogus")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should fail to merge with bogus values" do
+ expect do
+ @uri.merge(:authority => "bar@baz:bogus")
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should fail to merge with bogus parameters" do
+ expect do
+ @uri.merge(42)
+ end.to raise_error(TypeError)
+ end
+
+ it "should fail to merge with bogus parameters" do
+ expect do
+ @uri.merge("http://example.com/")
+ end.to raise_error(TypeError)
+ end
+
+ it "should fail to merge with both authority and subcomponents" do
+ expect do
+ @uri.merge(:authority => "foo:bar@baz:42", :port => "42")
+ end.to raise_error(ArgumentError)
+ end
+
+ it "should fail to merge with both userinfo and subcomponents" do
+ expect do
+ @uri.merge(:userinfo => "foo:bar", :user => "foo")
+ end.to raise_error(ArgumentError)
+ end
+
+ it "should be identical to its duplicate" do
+ expect(@uri).to eq(@uri.dup)
+ end
+
+ it "should have an origin of 'http://example.com'" do
+ expect(@uri.origin).to eq('http://example.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/search?q=Q%26A'" do
+
+ before do
+ @uri = Addressable::URI.parse("http://example.com/search?q=Q%26A")
+ end
+
+ it "should have a query of 'q=Q%26A'" do
+ expect(@uri.query).to eq("q=Q%26A")
+ end
+
+ it "should have query_values of {'q' => 'Q&A'}" do
+ expect(@uri.query_values).to eq({ 'q' => 'Q&A' })
+ end
+
+ it "should normalize to the original uri " +
+ "(with the ampersand properly percent-encoded)" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/search?q=Q%26A")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?&x=b'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?&x=b")
+ end
+
+ it "should have a query of '&x=b'" do
+ expect(@uri.query).to eq("&x=b")
+ end
+
+ it "should have query_values of {'x' => 'b'}" do
+ expect(@uri.query_values).to eq({'x' => 'b'})
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?q='one;two'&x=1'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?q='one;two'&x=1")
+ end
+
+ it "should have a query of 'q='one;two'&x=1'" do
+ expect(@uri.query).to eq("q='one;two'&x=1")
+ end
+
+ it "should have query_values of {\"q\" => \"'one;two'\", \"x\" => \"1\"}" do
+ expect(@uri.query_values).to eq({"q" => "'one;two'", "x" => "1"})
+ end
+
+ it "should escape the ';' character when normalizing to avoid ambiguity " +
+ "with the W3C HTML 4.01 specification" do
+ # HTML 4.01 Section B.2.2
+ expect(@uri.normalize.query).to eq("q='one%3Btwo'&x=1")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?&&x=b'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?&&x=b")
+ end
+
+ it "should have a query of '&&x=b'" do
+ expect(@uri.query).to eq("&&x=b")
+ end
+
+ it "should have query_values of {'x' => 'b'}" do
+ expect(@uri.query_values).to eq({'x' => 'b'})
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?q=a&&x=b'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?q=a&&x=b")
+ end
+
+ it "should have a query of 'q=a&&x=b'" do
+ expect(@uri.query).to eq("q=a&&x=b")
+ end
+
+ it "should have query_values of {'q' => 'a, 'x' => 'b'}" do
+ expect(@uri.query_values).to eq({'q' => 'a', 'x' => 'b'})
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?q&&x=b'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?q&&x=b")
+ end
+
+ it "should have a query of 'q&&x=b'" do
+ expect(@uri.query).to eq("q&&x=b")
+ end
+
+ it "should have query_values of {'q' => true, 'x' => 'b'}" do
+ expect(@uri.query_values).to eq({'q' => nil, 'x' => 'b'})
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?q=a+b'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?q=a+b")
+ end
+
+ it "should have a query of 'q=a+b'" do
+ expect(@uri.query).to eq("q=a+b")
+ end
+
+ it "should have query_values of {'q' => 'a b'}" do
+ expect(@uri.query_values).to eq({'q' => 'a b'})
+ end
+
+ it "should have a normalized query of 'q=a+b'" do
+ expect(@uri.normalized_query).to eq("q=a+b")
+ end
+end
+
+describe Addressable::URI, "when parsed from 'https://example.com/?q=a+b'" do
+ before do
+ @uri = Addressable::URI.parse("https://example.com/?q=a+b")
+ end
+
+ it "should have query_values of {'q' => 'a b'}" do
+ expect(@uri.query_values).to eq("q" => "a b")
+ end
+end
+
+describe Addressable::URI, "when parsed from 'example.com?q=a+b'" do
+ before do
+ @uri = Addressable::URI.parse("example.com?q=a+b")
+ end
+
+ it "should have query_values of {'q' => 'a b'}" do
+ expect(@uri.query_values).to eq("q" => "a b")
+ end
+end
+
+describe Addressable::URI, "when parsed from 'mailto:?q=a+b'" do
+ before do
+ @uri = Addressable::URI.parse("mailto:?q=a+b")
+ end
+
+ it "should have query_values of {'q' => 'a+b'}" do
+ expect(@uri.query_values).to eq("q" => "a+b")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?q=a%2bb'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?q=a%2bb")
+ end
+
+ it "should have a query of 'q=a+b'" do
+ expect(@uri.query).to eq("q=a%2bb")
+ end
+
+ it "should have query_values of {'q' => 'a+b'}" do
+ expect(@uri.query_values).to eq({'q' => 'a+b'})
+ end
+
+ it "should have a normalized query of 'q=a%2Bb'" do
+ expect(@uri.normalized_query).to eq("q=a%2Bb")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?v=%7E&w=%&x=%25&y=%2B&z=C%CC%A7'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?v=%7E&w=%&x=%25&y=%2B&z=C%CC%A7")
+ end
+
+ it "should have a normalized query of 'v=~&w=%25&x=%25&y=%2B&z=%C3%87'" do
+ expect(@uri.normalized_query).to eq("v=~&w=%25&x=%25&y=%2B&z=%C3%87")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?v=%7E&w=%&x=%25&y=+&z=C%CC%A7'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?v=%7E&w=%&x=%25&y=+&z=C%CC%A7")
+ end
+
+ it "should have a normalized query of 'v=~&w=%25&x=%25&y=+&z=%C3%87'" do
+ expect(@uri.normalized_query).to eq("v=~&w=%25&x=%25&y=+&z=%C3%87")
+ end
+end
+
+describe Addressable::URI, "when parsed from 'http://example/?b=1&a=2&c=3'" do
+ before do
+ @uri = Addressable::URI.parse("http://example/?b=1&a=2&c=3")
+ end
+
+ it "should have a sorted normalized query of 'a=2&b=1&c=3'" do
+ expect(@uri.normalized_query(:sorted)).to eq("a=2&b=1&c=3")
+ end
+end
+
+describe Addressable::URI, "when parsed from 'http://example/?&a&&c&'" do
+ before do
+ @uri = Addressable::URI.parse("http://example/?&a&&c&")
+ end
+
+ it "should have a compacted normalized query of 'a&c'" do
+ expect(@uri.normalized_query(:compacted)).to eq("a&c")
+ end
+end
+
+describe Addressable::URI, "when parsed from 'http://example.com/?a=1&a=1'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?a=1&a=1")
+ end
+
+ it "should have a compacted normalized query of 'a=1'" do
+ expect(@uri.normalized_query(:compacted)).to eq("a=1")
+ end
+end
+
+describe Addressable::URI, "when parsed from 'http://example.com/?a=1&a=2'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?a=1&a=2")
+ end
+
+ it "should have a compacted normalized query of 'a=1&a=2'" do
+ expect(@uri.normalized_query(:compacted)).to eq("a=1&a=2")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/sound%2bvision'" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/sound%2bvision")
+ end
+
+ it "should have a normalized path of '/sound+vision'" do
+ expect(@uri.normalized_path).to eq('/sound+vision')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/?q='" do
+ before do
+ @uri = Addressable::URI.parse("http://example.com/?q=")
+ end
+
+ it "should have a query of 'q='" do
+ expect(@uri.query).to eq("q=")
+ end
+
+ it "should have query_values of {'q' => ''}" do
+ expect(@uri.query_values).to eq({'q' => ''})
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://user@example.com'" do
+ before do
+ @uri = Addressable::URI.parse("http://user@example.com")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have a username of 'user'" do
+ expect(@uri.user).to eq("user")
+ end
+
+ it "should have no password" do
+ expect(@uri.password).to eq(nil)
+ end
+
+ it "should have a userinfo of 'user'" do
+ expect(@uri.userinfo).to eq("user")
+ end
+
+ it "should have a normalized userinfo of 'user'" do
+ expect(@uri.normalized_userinfo).to eq("user")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should have default_port 80" do
+ expect(@uri.default_port).to eq(80)
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have the correct username after assignment" do
+ @uri.user = "newuser"
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq(nil)
+ expect(@uri.to_s).to eq("http://newuser@example.com")
+ end
+
+ it "should have the correct password after assignment" do
+ @uri.password = "newpass"
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.to_s).to eq("http://user:newpass@example.com")
+ end
+
+ it "should have the correct userinfo segment after assignment" do
+ @uri.userinfo = "newuser:newpass"
+ expect(@uri.userinfo).to eq("newuser:newpass")
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.host).to eq("example.com")
+ expect(@uri.port).to eq(nil)
+ expect(@uri.inferred_port).to eq(80)
+ expect(@uri.to_s).to eq("http://newuser:newpass@example.com")
+ end
+
+ it "should have the correct userinfo segment after nil assignment" do
+ @uri.userinfo = nil
+ expect(@uri.userinfo).to eq(nil)
+ expect(@uri.user).to eq(nil)
+ expect(@uri.password).to eq(nil)
+ expect(@uri.host).to eq("example.com")
+ expect(@uri.port).to eq(nil)
+ expect(@uri.inferred_port).to eq(80)
+ expect(@uri.to_s).to eq("http://example.com")
+ end
+
+ it "should have the correct authority segment after assignment" do
+ @uri.authority = "newuser@example.com"
+ expect(@uri.authority).to eq("newuser@example.com")
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq(nil)
+ expect(@uri.host).to eq("example.com")
+ expect(@uri.port).to eq(nil)
+ expect(@uri.inferred_port).to eq(80)
+ expect(@uri.to_s).to eq("http://newuser@example.com")
+ end
+
+ it "should raise an error after nil assignment of authority segment" do
+ expect do
+ # This would create an invalid URI
+ @uri.authority = nil
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://user:@example.com'" do
+ before do
+ @uri = Addressable::URI.parse("http://user:@example.com")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have a username of 'user'" do
+ expect(@uri.user).to eq("user")
+ end
+
+ it "should have a password of ''" do
+ expect(@uri.password).to eq("")
+ end
+
+ it "should have a normalized userinfo of 'user:'" do
+ expect(@uri.normalized_userinfo).to eq("user:")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have the correct username after assignment" do
+ @uri.user = "newuser"
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq("")
+ expect(@uri.to_s).to eq("http://newuser:@example.com")
+ end
+
+ it "should have the correct password after assignment" do
+ @uri.password = "newpass"
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.to_s).to eq("http://user:newpass@example.com")
+ end
+
+ it "should have the correct authority segment after assignment" do
+ @uri.authority = "newuser:@example.com"
+ expect(@uri.authority).to eq("newuser:@example.com")
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq("")
+ expect(@uri.host).to eq("example.com")
+ expect(@uri.port).to eq(nil)
+ expect(@uri.inferred_port).to eq(80)
+ expect(@uri.to_s).to eq("http://newuser:@example.com")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://:pass@example.com'" do
+ before do
+ @uri = Addressable::URI.parse("http://:pass@example.com")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have a username of ''" do
+ expect(@uri.user).to eq("")
+ end
+
+ it "should have a password of 'pass'" do
+ expect(@uri.password).to eq("pass")
+ end
+
+ it "should have a userinfo of ':pass'" do
+ expect(@uri.userinfo).to eq(":pass")
+ end
+
+ it "should have a normalized userinfo of ':pass'" do
+ expect(@uri.normalized_userinfo).to eq(":pass")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have the correct username after assignment" do
+ @uri.user = "newuser"
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq("pass")
+ expect(@uri.to_s).to eq("http://newuser:pass@example.com")
+ end
+
+ it "should have the correct password after assignment" do
+ @uri.password = "newpass"
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.user).to eq("")
+ expect(@uri.to_s).to eq("http://:newpass@example.com")
+ end
+
+ it "should have the correct authority segment after assignment" do
+ @uri.authority = ":newpass@example.com"
+ expect(@uri.authority).to eq(":newpass@example.com")
+ expect(@uri.user).to eq("")
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.host).to eq("example.com")
+ expect(@uri.port).to eq(nil)
+ expect(@uri.inferred_port).to eq(80)
+ expect(@uri.to_s).to eq("http://:newpass@example.com")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://:@example.com'" do
+ before do
+ @uri = Addressable::URI.parse("http://:@example.com")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have a username of ''" do
+ expect(@uri.user).to eq("")
+ end
+
+ it "should have a password of ''" do
+ expect(@uri.password).to eq("")
+ end
+
+ it "should have a normalized userinfo of nil" do
+ expect(@uri.normalized_userinfo).to eq(nil)
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have the correct username after assignment" do
+ @uri.user = "newuser"
+ expect(@uri.user).to eq("newuser")
+ expect(@uri.password).to eq("")
+ expect(@uri.to_s).to eq("http://newuser:@example.com")
+ end
+
+ it "should have the correct password after assignment" do
+ @uri.password = "newpass"
+ expect(@uri.password).to eq("newpass")
+ expect(@uri.user).to eq("")
+ expect(@uri.to_s).to eq("http://:newpass@example.com")
+ end
+
+ it "should have the correct authority segment after assignment" do
+ @uri.authority = ":@newexample.com"
+ expect(@uri.authority).to eq(":@newexample.com")
+ expect(@uri.user).to eq("")
+ expect(@uri.password).to eq("")
+ expect(@uri.host).to eq("newexample.com")
+ expect(@uri.port).to eq(nil)
+ expect(@uri.inferred_port).to eq(80)
+ expect(@uri.to_s).to eq("http://:@newexample.com")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'#example'" do
+ before do
+ @uri = Addressable::URI.parse("#example")
+ end
+
+ it "should be considered relative" do
+ expect(@uri).to be_relative
+ end
+
+ it "should have a host of nil" do
+ expect(@uri.host).to eq(nil)
+ end
+
+ it "should have a site of nil" do
+ expect(@uri.site).to eq(nil)
+ end
+
+ it "should have a normalized_site of nil" do
+ expect(@uri.normalized_site).to eq(nil)
+ end
+
+ it "should have a path of ''" do
+ expect(@uri.path).to eq("")
+ end
+
+ it "should have a query string of nil" do
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should have a fragment of 'example'" do
+ expect(@uri.fragment).to eq("example")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "the network-path reference '//example.com/'" do
+ before do
+ @uri = Addressable::URI.parse("//example.com/")
+ end
+
+ it "should be considered relative" do
+ expect(@uri).to be_relative
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should have a path of '/'" do
+ expect(@uri.path).to eq("/")
+ end
+
+ it "should raise an error if routing is attempted" do
+ expect do
+ @uri.route_to("http://example.com/")
+ end.to raise_error(ArgumentError, /\/\/example.com\//)
+ expect do
+ @uri.route_from("http://example.com/")
+ end.to raise_error(ArgumentError, /\/\/example.com\//)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'feed://http://example.com/'" do
+ before do
+ @uri = Addressable::URI.parse("feed://http://example.com/")
+ end
+
+ it "should have a host of 'http'" do
+ expect(@uri.host).to eq("http")
+ end
+
+ it "should have a path of '//example.com/'" do
+ expect(@uri.path).to eq("//example.com/")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'feed:http://example.com/'" do
+ before do
+ @uri = Addressable::URI.parse("feed:http://example.com/")
+ end
+
+ it "should have a path of 'http://example.com/'" do
+ expect(@uri.path).to eq("http://example.com/")
+ end
+
+ it "should normalize to 'http://example.com/'" do
+ expect(@uri.normalize.to_s).to eq("http://example.com/")
+ expect(@uri.normalize!.to_s).to eq("http://example.com/")
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'example://a/b/c/%7Bfoo%7D'" do
+ before do
+ @uri = Addressable::URI.parse("example://a/b/c/%7Bfoo%7D")
+ end
+
+ # Section 6.2.2 of RFC 3986
+ it "should be equivalent to eXAMPLE://a/./b/../b/%63/%7bfoo%7d" do
+ expect(@uri).to eq(
+ Addressable::URI.parse("eXAMPLE://a/./b/../b/%63/%7bfoo%7d")
+ )
+ end
+
+ it "should have an origin of 'example://a'" do
+ expect(@uri.origin).to eq('example://a')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://example.com/indirect/path/./to/../resource/'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "http://example.com/indirect/path/./to/../resource/")
+ end
+
+ it "should use the 'http' scheme" do
+ expect(@uri.scheme).to eq("http")
+ end
+
+ it "should have a host of 'example.com'" do
+ expect(@uri.host).to eq("example.com")
+ end
+
+ it "should use port 80" do
+ expect(@uri.inferred_port).to eq(80)
+ end
+
+ it "should have a path of '/indirect/path/./to/../resource/'" do
+ expect(@uri.path).to eq("/indirect/path/./to/../resource/")
+ end
+
+ # Section 6.2.2.3 of RFC 3986
+ it "should have a normalized path of '/indirect/path/resource/'" do
+ expect(@uri.normalize.path).to eq("/indirect/path/resource/")
+ expect(@uri.normalize!.path).to eq("/indirect/path/resource/")
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://under_score.example.com/'" do
+ it "should not cause an error" do
+ expect do
+ Addressable::URI.parse("http://under_score.example.com/")
+ end.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'./this:that'" do
+ before do
+ @uri = Addressable::URI.parse("./this:that")
+ end
+
+ it "should be considered relative" do
+ expect(@uri).to be_relative
+ end
+
+ it "should have no scheme" do
+ expect(@uri.scheme).to eq(nil)
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'this:that'" do
+ before do
+ @uri = Addressable::URI.parse("this:that")
+ end
+
+ it "should be considered absolute" do
+ expect(@uri).to be_absolute
+ end
+
+ it "should have a scheme of 'this'" do
+ expect(@uri.scheme).to eq("this")
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when parsed from '?'" do
+ before do
+ @uri = Addressable::URI.parse("?")
+ end
+
+ it "should normalize to ''" do
+ expect(@uri.normalize.to_s).to eq("")
+ end
+
+ it "should have the correct return type" do
+ expect(@uri.query_values).to eq({})
+ expect(@uri.query_values(Hash)).to eq({})
+ expect(@uri.query_values(Array)).to eq([])
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when parsed from '?one=1&two=2&three=3'" do
+ before do
+ @uri = Addressable::URI.parse("?one=1&two=2&three=3")
+ end
+
+ it "should have the correct query values" do
+ expect(@uri.query_values).to eq({"one" => "1", "two" => "2", "three" => "3"})
+ end
+
+ it "should raise an error for invalid return type values" do
+ expect do
+ @uri.query_values(Integer)
+ end.to raise_error(ArgumentError)
+ end
+
+ it "should have the correct array query values" do
+ expect(@uri.query_values(Array)).to eq([
+ ["one", "1"], ["two", "2"], ["three", "3"]
+ ])
+ end
+
+ it "should have a 'null' origin" do
+ expect(@uri.origin).to eq('null')
+ end
+end
+
+describe Addressable::URI, "when parsed from '?one=1=uno&two=2=dos'" do
+ before do
+ @uri = Addressable::URI.parse("?one=1=uno&two=2=dos")
+ end
+
+ it "should have the correct query values" do
+ expect(@uri.query_values).to eq({"one" => "1=uno", "two" => "2=dos"})
+ end
+
+ it "should have the correct array query values" do
+ expect(@uri.query_values(Array)).to eq([
+ ["one", "1=uno"], ["two", "2=dos"]
+ ])
+ end
+end
+
+describe Addressable::URI, "when parsed from '?one[two][three]=four'" do
+ before do
+ @uri = Addressable::URI.parse("?one[two][three]=four")
+ end
+
+ it "should have the correct query values" do
+ expect(@uri.query_values).to eq({"one[two][three]" => "four"})
+ end
+
+ it "should have the correct array query values" do
+ expect(@uri.query_values(Array)).to eq([
+ ["one[two][three]", "four"]
+ ])
+ end
+end
+
+describe Addressable::URI, "when parsed from '?one.two.three=four'" do
+ before do
+ @uri = Addressable::URI.parse("?one.two.three=four")
+ end
+
+ it "should have the correct query values" do
+ expect(@uri.query_values).to eq({
+ "one.two.three" => "four"
+ })
+ end
+
+ it "should have the correct array query values" do
+ expect(@uri.query_values(Array)).to eq([
+ ["one.two.three", "four"]
+ ])
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'?one[two][three]=four&one[two][five]=six'" do
+ before do
+ @uri = Addressable::URI.parse("?one[two][three]=four&one[two][five]=six")
+ end
+
+ it "should have the correct query values" do
+ expect(@uri.query_values).to eq({
+ "one[two][three]" => "four", "one[two][five]" => "six"
+ })
+ end
+
+ it "should have the correct array query values" do
+ expect(@uri.query_values(Array)).to eq([
+ ["one[two][three]", "four"], ["one[two][five]", "six"]
+ ])
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'?one.two.three=four&one.two.five=six'" do
+ before do
+ @uri = Addressable::URI.parse("?one.two.three=four&one.two.five=six")
+ end
+
+ it "should have the correct query values" do
+ expect(@uri.query_values).to eq({
+ "one.two.three" => "four", "one.two.five" => "six"
+ })
+ end
+
+ it "should have the correct array query values" do
+ expect(@uri.query_values(Array)).to eq([
+ ["one.two.three", "four"], ["one.two.five", "six"]
+ ])
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'?one=two&one=three'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "?one=two&one=three&one=four"
+ )
+ end
+
+ it "should have correct array query values" do
+ expect(@uri.query_values(Array)).to eq(
+ [['one', 'two'], ['one', 'three'], ['one', 'four']]
+ )
+ end
+
+ it "should have correct hash query values" do
+ skip("This is probably more desirable behavior.")
+ expect(@uri.query_values(Hash)).to eq(
+ {'one' => ['two', 'three', 'four']}
+ )
+ end
+
+ it "should handle assignment with keys of mixed type" do
+ @uri.query_values = @uri.query_values(Hash).merge({:one => 'three'})
+ expect(@uri.query_values(Hash)).to eq({'one' => 'three'})
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'?one[two][three][]=four&one[two][three][]=five'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "?one[two][three][]=four&one[two][three][]=five"
+ )
+ end
+
+ it "should have correct query values" do
+ expect(@uri.query_values(Hash)).to eq({"one[two][three][]" => "five"})
+ end
+
+ it "should have correct array query values" do
+ expect(@uri.query_values(Array)).to eq([
+ ["one[two][three][]", "four"], ["one[two][three][]", "five"]
+ ])
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'?one[two][three][0]=four&one[two][three][1]=five'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "?one[two][three][0]=four&one[two][three][1]=five"
+ )
+ end
+
+ it "should have the correct query values" do
+ expect(@uri.query_values).to eq({
+ "one[two][three][0]" => "four", "one[two][three][1]" => "five"
+ })
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'?one[two][three][1]=four&one[two][three][0]=five'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "?one[two][three][1]=four&one[two][three][0]=five"
+ )
+ end
+
+ it "should have the correct query values" do
+ expect(@uri.query_values).to eq({
+ "one[two][three][1]" => "four", "one[two][three][0]" => "five"
+ })
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'?one[two][three][2]=four&one[two][three][1]=five'" do
+ before do
+ @uri = Addressable::URI.parse(
+ "?one[two][three][2]=four&one[two][three][1]=five"
+ )
+ end
+
+ it "should have the correct query values" do
+ expect(@uri.query_values).to eq({
+ "one[two][three][2]" => "four", "one[two][three][1]" => "five"
+ })
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://www.詹姆斯.com/'" do
+ before do
+ @uri = Addressable::URI.parse("http://www.詹姆斯.com/")
+ end
+
+ it "should be equivalent to 'http://www.xn--8ws00zhy3a.com/'" do
+ expect(@uri).to eq(
+ Addressable::URI.parse("http://www.xn--8ws00zhy3a.com/")
+ )
+ end
+
+ it "should not have domain name encoded during normalization" do
+ expect(Addressable::URI.normalized_encode(@uri.to_s)).to eq(
+ "http://www.詹姆斯.com/"
+ )
+ end
+
+ it "should have an origin of 'http://www.xn--8ws00zhy3a.com'" do
+ expect(@uri.origin).to eq('http://www.xn--8ws00zhy3a.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://www.詹姆斯.com/ some spaces /'" do
+ before do
+ @uri = Addressable::URI.parse("http://www.詹姆斯.com/ some spaces /")
+ end
+
+ it "should be equivalent to " +
+ "'http://www.xn--8ws00zhy3a.com/%20some%20spaces%20/'" do
+ expect(@uri).to eq(
+ Addressable::URI.parse(
+ "http://www.xn--8ws00zhy3a.com/%20some%20spaces%20/")
+ )
+ end
+
+ it "should not have domain name encoded during normalization" do
+ expect(Addressable::URI.normalized_encode(@uri.to_s)).to eq(
+ "http://www.詹姆斯.com/%20some%20spaces%20/"
+ )
+ end
+
+ it "should have an origin of 'http://www.xn--8ws00zhy3a.com'" do
+ expect(@uri.origin).to eq('http://www.xn--8ws00zhy3a.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://www.xn--8ws00zhy3a.com/'" do
+ before do
+ @uri = Addressable::URI.parse("http://www.xn--8ws00zhy3a.com/")
+ end
+
+ it "should be displayed as http://www.詹姆斯.com/" do
+ expect(@uri.display_uri.to_s).to eq("http://www.詹姆斯.com/")
+ end
+
+ it "should properly force the encoding" do
+ display_string = @uri.display_uri.to_str
+ expect(display_string).to eq("http://www.詹姆斯.com/")
+ if display_string.respond_to?(:encoding)
+ expect(display_string.encoding.to_s).to eq(Encoding::UTF_8.to_s)
+ end
+ end
+
+ it "should have an origin of 'http://www.xn--8ws00zhy3a.com'" do
+ expect(@uri.origin).to eq('http://www.xn--8ws00zhy3a.com')
+ end
+end
+
+describe Addressable::URI, "when parsed from " +
+ "'http://www.詹姆斯.com/atomtests/iri/詹.html'" do
+ before do
+ @uri = Addressable::URI.parse("http://www.詹姆斯.com/atomtests/iri/詹.html")
+ end
+
+ it "should normalize to " +
+ "http://www.xn--8ws00zhy3a.com/atomtests/iri/%E8%A9%B9.html" do
+ expect(@uri.normalize.to_s).to eq(
+ "http://www.xn--8ws00zhy3a.com/atomtests/iri/%E8%A9%B9.html"
+ )
+ expect(@uri.normalize!.to_s).to eq(
+ "http://www.xn--8ws00zhy3a.com/atomtests/iri/%E8%A9%B9.html"
+ )
+ end
+end
+
+describe Addressable::URI, "when parsed from a percent-encoded IRI" do
+ before do
+ @uri = Addressable::URI.parse(
+ "http://www.%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA" +
+ "%E3%81%8C%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3" +
+ "%82%89%E3%81%AA%E3%81%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82" +
+ "%81%E3%81%84%E3%81%AE%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0" +
+ "%E3%81%AA%E3%81%8C%E3%81%8F%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3" +
+ "%81%9F%E3%82%8A%E3%81%AA%E3%81%84.w3.mag.keio.ac.jp"
+ )
+ end
+
+ it "should normalize to something sane" do
+ expect(@uri.normalize.to_s).to eq(
+ "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3f" +
+ "g11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp/"
+ )
+ expect(@uri.normalize!.to_s).to eq(
+ "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3f" +
+ "g11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp/"
+ )
+ end
+
+ it "should have the correct origin" do
+ expect(@uri.origin).to eq(
+ "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3f" +
+ "g11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
+ )
+ end
+end
+
+describe Addressable::URI, "with a base uri of 'http://a/b/c/d;p?q'" do
+ before do
+ @uri = Addressable::URI.parse("http://a/b/c/d;p?q")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with 'g:h' should resolve to g:h" do
+ expect((@uri + "g:h").to_s).to eq("g:h")
+ expect(Addressable::URI.join(@uri, "g:h").to_s).to eq("g:h")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with 'g' should resolve to http://a/b/c/g" do
+ expect((@uri + "g").to_s).to eq("http://a/b/c/g")
+ expect(Addressable::URI.join(@uri.to_s, "g").to_s).to eq("http://a/b/c/g")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with './g' should resolve to http://a/b/c/g" do
+ expect((@uri + "./g").to_s).to eq("http://a/b/c/g")
+ expect(Addressable::URI.join(@uri.to_s, "./g").to_s).to eq("http://a/b/c/g")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with 'g/' should resolve to http://a/b/c/g/" do
+ expect((@uri + "g/").to_s).to eq("http://a/b/c/g/")
+ expect(Addressable::URI.join(@uri.to_s, "g/").to_s).to eq("http://a/b/c/g/")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '/g' should resolve to http://a/g" do
+ expect((@uri + "/g").to_s).to eq("http://a/g")
+ expect(Addressable::URI.join(@uri.to_s, "/g").to_s).to eq("http://a/g")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '//g' should resolve to http://g" do
+ expect((@uri + "//g").to_s).to eq("http://g")
+ expect(Addressable::URI.join(@uri.to_s, "//g").to_s).to eq("http://g")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '?y' should resolve to http://a/b/c/d;p?y" do
+ expect((@uri + "?y").to_s).to eq("http://a/b/c/d;p?y")
+ expect(Addressable::URI.join(@uri.to_s, "?y").to_s).to eq("http://a/b/c/d;p?y")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with 'g?y' should resolve to http://a/b/c/g?y" do
+ expect((@uri + "g?y").to_s).to eq("http://a/b/c/g?y")
+ expect(Addressable::URI.join(@uri.to_s, "g?y").to_s).to eq("http://a/b/c/g?y")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '#s' should resolve to http://a/b/c/d;p?q#s" do
+ expect((@uri + "#s").to_s).to eq("http://a/b/c/d;p?q#s")
+ expect(Addressable::URI.join(@uri.to_s, "#s").to_s).to eq(
+ "http://a/b/c/d;p?q#s"
+ )
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with 'g#s' should resolve to http://a/b/c/g#s" do
+ expect((@uri + "g#s").to_s).to eq("http://a/b/c/g#s")
+ expect(Addressable::URI.join(@uri.to_s, "g#s").to_s).to eq("http://a/b/c/g#s")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with 'g?y#s' should resolve to http://a/b/c/g?y#s" do
+ expect((@uri + "g?y#s").to_s).to eq("http://a/b/c/g?y#s")
+ expect(Addressable::URI.join(
+ @uri.to_s, "g?y#s").to_s).to eq("http://a/b/c/g?y#s")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with ';x' should resolve to http://a/b/c/;x" do
+ expect((@uri + ";x").to_s).to eq("http://a/b/c/;x")
+ expect(Addressable::URI.join(@uri.to_s, ";x").to_s).to eq("http://a/b/c/;x")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with 'g;x' should resolve to http://a/b/c/g;x" do
+ expect((@uri + "g;x").to_s).to eq("http://a/b/c/g;x")
+ expect(Addressable::URI.join(@uri.to_s, "g;x").to_s).to eq("http://a/b/c/g;x")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with 'g;x?y#s' should resolve to http://a/b/c/g;x?y#s" do
+ expect((@uri + "g;x?y#s").to_s).to eq("http://a/b/c/g;x?y#s")
+ expect(Addressable::URI.join(
+ @uri.to_s, "g;x?y#s").to_s).to eq("http://a/b/c/g;x?y#s")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '' should resolve to http://a/b/c/d;p?q" do
+ expect((@uri + "").to_s).to eq("http://a/b/c/d;p?q")
+ expect(Addressable::URI.join(@uri.to_s, "").to_s).to eq("http://a/b/c/d;p?q")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '.' should resolve to http://a/b/c/" do
+ expect((@uri + ".").to_s).to eq("http://a/b/c/")
+ expect(Addressable::URI.join(@uri.to_s, ".").to_s).to eq("http://a/b/c/")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with './' should resolve to http://a/b/c/" do
+ expect((@uri + "./").to_s).to eq("http://a/b/c/")
+ expect(Addressable::URI.join(@uri.to_s, "./").to_s).to eq("http://a/b/c/")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '..' should resolve to http://a/b/" do
+ expect((@uri + "..").to_s).to eq("http://a/b/")
+ expect(Addressable::URI.join(@uri.to_s, "..").to_s).to eq("http://a/b/")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '../' should resolve to http://a/b/" do
+ expect((@uri + "../").to_s).to eq("http://a/b/")
+ expect(Addressable::URI.join(@uri.to_s, "../").to_s).to eq("http://a/b/")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '../g' should resolve to http://a/b/g" do
+ expect((@uri + "../g").to_s).to eq("http://a/b/g")
+ expect(Addressable::URI.join(@uri.to_s, "../g").to_s).to eq("http://a/b/g")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '../..' should resolve to http://a/" do
+ expect((@uri + "../..").to_s).to eq("http://a/")
+ expect(Addressable::URI.join(@uri.to_s, "../..").to_s).to eq("http://a/")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '../../' should resolve to http://a/" do
+ expect((@uri + "../../").to_s).to eq("http://a/")
+ expect(Addressable::URI.join(@uri.to_s, "../../").to_s).to eq("http://a/")
+ end
+
+ # Section 5.4.1 of RFC 3986
+ it "when joined with '../../g' should resolve to http://a/g" do
+ expect((@uri + "../../g").to_s).to eq("http://a/g")
+ expect(Addressable::URI.join(@uri.to_s, "../../g").to_s).to eq("http://a/g")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with '../../../g' should resolve to http://a/g" do
+ expect((@uri + "../../../g").to_s).to eq("http://a/g")
+ expect(Addressable::URI.join(@uri.to_s, "../../../g").to_s).to eq("http://a/g")
+ end
+
+ it "when joined with '../.././../g' should resolve to http://a/g" do
+ expect((@uri + "../.././../g").to_s).to eq("http://a/g")
+ expect(Addressable::URI.join(@uri.to_s, "../.././../g").to_s).to eq(
+ "http://a/g"
+ )
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with '../../../../g' should resolve to http://a/g" do
+ expect((@uri + "../../../../g").to_s).to eq("http://a/g")
+ expect(Addressable::URI.join(
+ @uri.to_s, "../../../../g").to_s).to eq("http://a/g")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with '/./g' should resolve to http://a/g" do
+ expect((@uri + "/./g").to_s).to eq("http://a/g")
+ expect(Addressable::URI.join(@uri.to_s, "/./g").to_s).to eq("http://a/g")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with '/../g' should resolve to http://a/g" do
+ expect((@uri + "/../g").to_s).to eq("http://a/g")
+ expect(Addressable::URI.join(@uri.to_s, "/../g").to_s).to eq("http://a/g")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g.' should resolve to http://a/b/c/g." do
+ expect((@uri + "g.").to_s).to eq("http://a/b/c/g.")
+ expect(Addressable::URI.join(@uri.to_s, "g.").to_s).to eq("http://a/b/c/g.")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with '.g' should resolve to http://a/b/c/.g" do
+ expect((@uri + ".g").to_s).to eq("http://a/b/c/.g")
+ expect(Addressable::URI.join(@uri.to_s, ".g").to_s).to eq("http://a/b/c/.g")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g..' should resolve to http://a/b/c/g.." do
+ expect((@uri + "g..").to_s).to eq("http://a/b/c/g..")
+ expect(Addressable::URI.join(@uri.to_s, "g..").to_s).to eq("http://a/b/c/g..")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with '..g' should resolve to http://a/b/c/..g" do
+ expect((@uri + "..g").to_s).to eq("http://a/b/c/..g")
+ expect(Addressable::URI.join(@uri.to_s, "..g").to_s).to eq("http://a/b/c/..g")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with './../g' should resolve to http://a/b/g" do
+ expect((@uri + "./../g").to_s).to eq("http://a/b/g")
+ expect(Addressable::URI.join(@uri.to_s, "./../g").to_s).to eq("http://a/b/g")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with './g/.' should resolve to http://a/b/c/g/" do
+ expect((@uri + "./g/.").to_s).to eq("http://a/b/c/g/")
+ expect(Addressable::URI.join(@uri.to_s, "./g/.").to_s).to eq("http://a/b/c/g/")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g/./h' should resolve to http://a/b/c/g/h" do
+ expect((@uri + "g/./h").to_s).to eq("http://a/b/c/g/h")
+ expect(Addressable::URI.join(@uri.to_s, "g/./h").to_s).to eq("http://a/b/c/g/h")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g/../h' should resolve to http://a/b/c/h" do
+ expect((@uri + "g/../h").to_s).to eq("http://a/b/c/h")
+ expect(Addressable::URI.join(@uri.to_s, "g/../h").to_s).to eq("http://a/b/c/h")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g;x=1/./y' " +
+ "should resolve to http://a/b/c/g;x=1/y" do
+ expect((@uri + "g;x=1/./y").to_s).to eq("http://a/b/c/g;x=1/y")
+ expect(Addressable::URI.join(
+ @uri.to_s, "g;x=1/./y").to_s).to eq("http://a/b/c/g;x=1/y")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g;x=1/../y' should resolve to http://a/b/c/y" do
+ expect((@uri + "g;x=1/../y").to_s).to eq("http://a/b/c/y")
+ expect(Addressable::URI.join(
+ @uri.to_s, "g;x=1/../y").to_s).to eq("http://a/b/c/y")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g?y/./x' " +
+ "should resolve to http://a/b/c/g?y/./x" do
+ expect((@uri + "g?y/./x").to_s).to eq("http://a/b/c/g?y/./x")
+ expect(Addressable::URI.join(
+ @uri.to_s, "g?y/./x").to_s).to eq("http://a/b/c/g?y/./x")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g?y/../x' " +
+ "should resolve to http://a/b/c/g?y/../x" do
+ expect((@uri + "g?y/../x").to_s).to eq("http://a/b/c/g?y/../x")
+ expect(Addressable::URI.join(
+ @uri.to_s, "g?y/../x").to_s).to eq("http://a/b/c/g?y/../x")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g#s/./x' " +
+ "should resolve to http://a/b/c/g#s/./x" do
+ expect((@uri + "g#s/./x").to_s).to eq("http://a/b/c/g#s/./x")
+ expect(Addressable::URI.join(
+ @uri.to_s, "g#s/./x").to_s).to eq("http://a/b/c/g#s/./x")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'g#s/../x' " +
+ "should resolve to http://a/b/c/g#s/../x" do
+ expect((@uri + "g#s/../x").to_s).to eq("http://a/b/c/g#s/../x")
+ expect(Addressable::URI.join(
+ @uri.to_s, "g#s/../x").to_s).to eq("http://a/b/c/g#s/../x")
+ end
+
+ # Section 5.4.2 of RFC 3986
+ it "when joined with 'http:g' should resolve to http:g" do
+ expect((@uri + "http:g").to_s).to eq("http:g")
+ expect(Addressable::URI.join(@uri.to_s, "http:g").to_s).to eq("http:g")
+ end
+
+ # Edge case to be sure
+ it "when joined with '//example.com/' should " +
+ "resolve to http://example.com/" do
+ expect((@uri + "//example.com/").to_s).to eq("http://example.com/")
+ expect(Addressable::URI.join(
+ @uri.to_s, "//example.com/").to_s).to eq("http://example.com/")
+ end
+
+ it "when joined with a bogus object a TypeError should be raised" do
+ expect do
+ Addressable::URI.join(@uri, 42)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when converting the path " +
+ "'relative/path/to/something'" do
+ before do
+ @path = 'relative/path/to/something'
+ end
+
+ it "should convert to " +
+ "\'relative/path/to/something\'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("relative/path/to/something")
+ end
+
+ it "should join with an absolute file path correctly" do
+ @base = Addressable::URI.convert_path("/absolute/path/")
+ @uri = Addressable::URI.convert_path(@path)
+ expect((@base + @uri).to_str).to eq(
+ "file:///absolute/path/relative/path/to/something"
+ )
+ end
+end
+
+describe Addressable::URI, "when converting a bogus path" do
+ it "should raise a TypeError" do
+ expect do
+ Addressable::URI.convert_path(42)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when given a UNIX root directory" do
+ before do
+ @path = "/"
+ end
+
+ it "should convert to \'file:///\'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("file:///")
+ end
+
+ it "should have an origin of 'file://'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.origin).to eq('file://')
+ end
+end
+
+describe Addressable::URI, "when given a Windows root directory" do
+ before do
+ @path = "C:\\"
+ end
+
+ it "should convert to \'file:///c:/\'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("file:///c:/")
+ end
+
+ it "should have an origin of 'file://'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.origin).to eq('file://')
+ end
+end
+
+describe Addressable::URI, "when given the path '/one/two/'" do
+ before do
+ @path = '/one/two/'
+ end
+
+ it "should convert to " +
+ "\'file:///one/two/\'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("file:///one/two/")
+ end
+
+ it "should have an origin of 'file://'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.origin).to eq('file://')
+ end
+end
+
+describe Addressable::URI, "when given the tld " do
+ it "'uk' should have a tld of 'uk'" do
+ uri = Addressable::URI.parse("http://example.com")
+ uri.tld = "uk"
+
+ expect(uri.tld).to eq("uk")
+ end
+
+ context "which " do
+ let (:uri) { Addressable::URI.parse("http://www.comrade.net/path/to/source/") }
+
+ it "contains a subdomain" do
+ uri.tld = "co.uk"
+
+ expect(uri.to_s).to eq("http://www.comrade.co.uk/path/to/source/")
+ end
+
+ it "is part of the domain" do
+ uri.tld = "com"
+
+ expect(uri.to_s).to eq("http://www.comrade.com/path/to/source/")
+ end
+ end
+end
+
+describe Addressable::URI, "when given the path " +
+ "'c:\\windows\\My Documents 100%20\\foo.txt'" do
+ before do
+ @path = "c:\\windows\\My Documents 100%20\\foo.txt"
+ end
+
+ it "should convert to " +
+ "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt")
+ end
+
+ it "should have an origin of 'file://'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.origin).to eq('file://')
+ end
+end
+
+describe Addressable::URI, "when given the path " +
+ "'file://c:\\windows\\My Documents 100%20\\foo.txt'" do
+ before do
+ @path = "file://c:\\windows\\My Documents 100%20\\foo.txt"
+ end
+
+ it "should convert to " +
+ "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt")
+ end
+
+ it "should have an origin of 'file://'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.origin).to eq('file://')
+ end
+end
+
+describe Addressable::URI, "when given the path " +
+ "'file:c:\\windows\\My Documents 100%20\\foo.txt'" do
+ before do
+ @path = "file:c:\\windows\\My Documents 100%20\\foo.txt"
+ end
+
+ it "should convert to " +
+ "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt")
+ end
+
+ it "should have an origin of 'file://'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.origin).to eq('file://')
+ end
+end
+
+describe Addressable::URI, "when given the path " +
+ "'file:/c:\\windows\\My Documents 100%20\\foo.txt'" do
+ before do
+ @path = "file:/c:\\windows\\My Documents 100%20\\foo.txt"
+ end
+
+ it "should convert to " +
+ "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt")
+ end
+
+ it "should have an origin of 'file://'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.origin).to eq('file://')
+ end
+end
+
+describe Addressable::URI, "when given the path " +
+ "'file:///c|/windows/My%20Documents%20100%20/foo.txt'" do
+ before do
+ @path = "file:///c|/windows/My%20Documents%20100%20/foo.txt"
+ end
+
+ it "should convert to " +
+ "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt")
+ end
+
+ it "should have an origin of 'file://'" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.origin).to eq('file://')
+ end
+end
+
+describe Addressable::URI, "when given an http protocol URI" do
+ before do
+ @path = "http://example.com/"
+ end
+
+ it "should not do any conversion at all" do
+ @uri = Addressable::URI.convert_path(@path)
+ expect(@uri.to_str).to eq("http://example.com/")
+ end
+end
+
+class SuperString
+ def initialize(string)
+ @string = string.to_s
+ end
+
+ def to_str
+ return @string
+ end
+end
+
+describe Addressable::URI, "when parsing a non-String object" do
+ it "should correctly parse anything with a 'to_str' method" do
+ Addressable::URI.parse(SuperString.new(42))
+ end
+
+ it "should raise a TypeError for objects than cannot be converted" do
+ expect do
+ Addressable::URI.parse(42)
+ end.to raise_error(TypeError)
+ end
+
+ it "should correctly parse heuristically anything with a 'to_str' method" do
+ Addressable::URI.heuristic_parse(SuperString.new(42))
+ end
+
+ it "should raise a TypeError for objects than cannot be converted" do
+ expect do
+ Addressable::URI.heuristic_parse(42)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when form encoding a hash" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.form_encode(
+ [["&one", "/1"], ["=two", "?2"], [":three", "#3"]]
+ )).to eq("%26one=%2F1&%3Dtwo=%3F2&%3Athree=%233")
+ end
+
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.form_encode(
+ {"q" => "one two three"}
+ )).to eq("q=one+two+three")
+ end
+
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.form_encode(
+ {"key" => nil}
+ )).to eq("key=")
+ end
+
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.form_encode(
+ {"q" => ["one", "two", "three"]}
+ )).to eq("q=one&q=two&q=three")
+ end
+
+ it "should result in correctly encoded newlines" do
+ expect(Addressable::URI.form_encode(
+ {"text" => "one\ntwo\rthree\r\nfour\n\r"}
+ )).to eq("text=one%0D%0Atwo%0D%0Athree%0D%0Afour%0D%0A%0D%0A")
+ end
+
+ it "should result in a sorted percent encoded sequence" do
+ expect(Addressable::URI.form_encode(
+ [["a", "1"], ["dup", "3"], ["dup", "2"]], true
+ )).to eq("a=1&dup=2&dup=3")
+ end
+end
+
+describe Addressable::URI, "when form encoding a non-Array object" do
+ it "should raise a TypeError for objects than cannot be converted" do
+ expect do
+ Addressable::URI.form_encode(42)
+ end.to raise_error(TypeError)
+ end
+end
+
+# See https://tools.ietf.org/html/rfc6749#appendix-B
+describe Addressable::URI, "when form encoding the example value from OAuth 2" do
+ it "should result in correct values" do
+ expect(Addressable::URI.form_encode(
+ {"value" => " %&+£€"}
+ )).to eq("value=+%25%26%2B%C2%A3%E2%82%AC")
+ end
+end
+
+# See https://tools.ietf.org/html/rfc6749#appendix-B
+describe Addressable::URI, "when form unencoding the example value from OAuth 2" do
+ it "should result in correct values" do
+ expect(Addressable::URI.form_unencode(
+ "value=+%25%26%2B%C2%A3%E2%82%AC"
+ )).to eq([["value", " %&+£€"]])
+ end
+end
+
+describe Addressable::URI, "when form unencoding a string" do
+ it "should result in correct values" do
+ expect(Addressable::URI.form_unencode(
+ "%26one=%2F1&%3Dtwo=%3F2&%3Athree=%233"
+ )).to eq([["&one", "/1"], ["=two", "?2"], [":three", "#3"]])
+ end
+
+ it "should result in correct values" do
+ expect(Addressable::URI.form_unencode(
+ "q=one+two+three"
+ )).to eq([["q", "one two three"]])
+ end
+
+ it "should result in correct values" do
+ expect(Addressable::URI.form_unencode(
+ "text=one%0D%0Atwo%0D%0Athree%0D%0Afour%0D%0A%0D%0A"
+ )).to eq([["text", "one\ntwo\nthree\nfour\n\n"]])
+ end
+
+ it "should result in correct values" do
+ expect(Addressable::URI.form_unencode(
+ "a=1&dup=2&dup=3"
+ )).to eq([["a", "1"], ["dup", "2"], ["dup", "3"]])
+ end
+
+ it "should result in correct values" do
+ expect(Addressable::URI.form_unencode(
+ "key"
+ )).to eq([["key", nil]])
+ end
+
+ it "should result in correct values" do
+ expect(Addressable::URI.form_unencode("GivenName=Ren%C3%A9")).to eq(
+ [["GivenName", "René"]]
+ )
+ end
+end
+
+describe Addressable::URI, "when form unencoding a non-String object" do
+ it "should correctly parse anything with a 'to_str' method" do
+ Addressable::URI.form_unencode(SuperString.new(42))
+ end
+
+ it "should raise a TypeError for objects than cannot be converted" do
+ expect do
+ Addressable::URI.form_unencode(42)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when normalizing a non-String object" do
+ it "should correctly parse anything with a 'to_str' method" do
+ Addressable::URI.normalize_component(SuperString.new(42))
+ end
+
+ it "should raise a TypeError for objects than cannot be converted" do
+ expect do
+ Addressable::URI.normalize_component(42)
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise a TypeError for objects than cannot be converted" do
+ expect do
+ Addressable::URI.normalize_component("component", 42)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when normalizing a path with an encoded slash" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.parse("/path%2Fsegment/").normalize.path).to eq(
+ "/path%2Fsegment/"
+ )
+ end
+end
+
+describe Addressable::URI, "when normalizing a partially encoded string" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.normalize_component(
+ "partially % encoded%21"
+ )).to eq("partially%20%25%20encoded!")
+ end
+
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.normalize_component(
+ "partially %25 encoded!"
+ )).to eq("partially%20%25%20encoded!")
+ end
+end
+
+describe Addressable::URI, "when normalizing a unicode sequence" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.normalize_component(
+ "/C%CC%A7"
+ )).to eq("/%C3%87")
+ end
+
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.normalize_component(
+ "/%C3%87"
+ )).to eq("/%C3%87")
+ end
+end
+
+describe Addressable::URI, "when normalizing a multibyte string" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.normalize_component("günther")).to eq(
+ "g%C3%BCnther"
+ )
+ end
+
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.normalize_component("g%C3%BCnther")).to eq(
+ "g%C3%BCnther"
+ )
+ end
+end
+
+describe Addressable::URI, "when normalizing a string but leaving some characters encoded" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.normalize_component("%58X%59Y%5AZ", "0-9a-zXY", "Y")).to eq(
+ "XX%59Y%5A%5A"
+ )
+ end
+
+ it "should not modify the character class" do
+ character_class = "0-9a-zXY"
+
+ character_class_copy = character_class.dup
+
+ Addressable::URI.normalize_component("%58X%59Y%5AZ", character_class, "Y")
+
+ expect(character_class).to eq(character_class_copy)
+ end
+end
+
+describe Addressable::URI, "when encoding IP literals" do
+ it "should work for IPv4" do
+ input = "http://127.0.0.1/"
+ expect(Addressable::URI.encode(input)).to eq(input)
+ end
+
+ it "should work for IPv6" do
+ input = "http://[fe80::200:f8ff:fe21:67cf]/"
+ expect(Addressable::URI.encode(input)).to eq(input)
+ end
+end
+
+describe Addressable::URI, "when encoding a string with existing encodings to upcase" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.encode_component("JK%4c", "0-9A-IKM-Za-z%", "L")).to eq("%4AK%4C")
+ end
+end
+
+describe Addressable::URI, "when encoding a multibyte string" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.encode_component("günther")).to eq("g%C3%BCnther")
+ end
+
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.encode_component(
+ "günther", /[^a-zA-Z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\-\.\_\~]/
+ )).to eq("g%C3%BCnther")
+ end
+end
+
+describe Addressable::URI, "when form encoding a multibyte string" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.form_encode({"GivenName" => "René"})).to eq(
+ "GivenName=Ren%C3%A9"
+ )
+ end
+end
+
+describe Addressable::URI, "when encoding a string with ASCII chars 0-15" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.encode_component("one\ntwo")).to eq("one%0Atwo")
+ end
+
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.encode_component(
+ "one\ntwo", /[^a-zA-Z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\-\.\_\~]/
+ )).to eq("one%0Atwo")
+ end
+end
+
+describe Addressable::URI, "when unencoding a multibyte string" do
+ it "should result in correct percent encoded sequence" do
+ expect(Addressable::URI.unencode_component("g%C3%BCnther")).to eq("günther")
+ end
+
+ it "should consistently use UTF-8 internally" do
+ expect(Addressable::URI.unencode_component("ski=%BA%DAɫ")).to eq("ski=\xBA\xDAɫ")
+ end
+
+ it "should result in correct percent encoded sequence as a URI" do
+ expect(Addressable::URI.unencode(
+ "/path?g%C3%BCnther", ::Addressable::URI
+ )).to eq(Addressable::URI.new(
+ :path => "/path", :query => "günther"
+ ))
+ end
+end
+
+describe Addressable::URI, "when partially unencoding a string" do
+ it "should unencode all characters by default" do
+ expect(Addressable::URI.unencode('%%25~%7e+%2b', String)).to eq('%%~~++')
+ end
+
+ it "should unencode characters not in leave_encoded" do
+ expect(Addressable::URI.unencode('%%25~%7e+%2b', String, '~')).to eq('%%~%7e++')
+ end
+
+ it "should leave characters in leave_encoded alone" do
+ expect(Addressable::URI.unencode('%%25~%7e+%2b', String, '%~+')).to eq('%%25~%7e+%2b')
+ end
+end
+
+describe Addressable::URI, "when unencoding a bogus object" do
+ it "should raise a TypeError" do
+ expect do
+ Addressable::URI.unencode_component(42)
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise a TypeError" do
+ expect do
+ Addressable::URI.unencode("/path?g%C3%BCnther", Integer)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when encoding a bogus object" do
+ it "should raise a TypeError" do
+ expect do
+ Addressable::URI.encode(Object.new)
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise a TypeError" do
+ expect do
+ Addressable::URI.normalized_encode(Object.new)
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise a TypeError" do
+ expect do
+ Addressable::URI.encode_component("günther", Object.new)
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise a TypeError" do
+ expect do
+ Addressable::URI.encode_component(Object.new)
+ end.to raise_error(TypeError)
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http://example.com/'" do
+ before do
+ @input = "http://example.com/"
+ end
+
+ it "should heuristically parse to 'http://example.com/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("http://example.com/")
+ end
+
+ it "should not raise error when frozen" do
+ expect do
+ Addressable::URI.heuristic_parse(@input).freeze.to_s
+ end.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'https://example.com/'" do
+ before do
+ @input = "https://example.com/"
+ end
+
+ it "should heuristically parse to 'https://example.com/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("https://example.com/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http:example.com/'" do
+ before do
+ @input = "http:example.com/"
+ end
+
+ it "should heuristically parse to 'http://example.com/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("http://example.com/")
+ end
+
+ it "should heuristically parse to 'http://example.com/' " +
+ "even with a scheme hint of 'ftp'" do
+ @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'})
+ expect(@uri.to_s).to eq("http://example.com/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'https:example.com/'" do
+ before do
+ @input = "https:example.com/"
+ end
+
+ it "should heuristically parse to 'https://example.com/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("https://example.com/")
+ end
+
+ it "should heuristically parse to 'https://example.com/' " +
+ "even with a scheme hint of 'ftp'" do
+ @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'})
+ expect(@uri.to_s).to eq("https://example.com/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http://example.com/example.com/'" do
+ before do
+ @input = "http://example.com/example.com/"
+ end
+
+ it "should heuristically parse to 'http://example.com/example.com/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("http://example.com/example.com/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http://prefix\\.example.com/'" do
+ before do
+ @input = "http://prefix\\.example.com/"
+ end
+
+ it "should heuristically parse to 'http://prefix/.example.com/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.authority).to eq("prefix")
+ expect(@uri.to_s).to eq("http://prefix/.example.com/")
+ end
+
+ it "should heuristically parse to 'http://prefix/.example.com/' " +
+ "even with a scheme hint of 'ftp'" do
+ @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'})
+ expect(@uri.to_s).to eq("http://prefix/.example.com/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http://p:\\/'" do
+ before do
+ @input = "http://p:\\/"
+ end
+
+ it "should heuristically parse to 'http://p//'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.authority).to eq("p")
+ expect(@uri.to_s).to eq("http://p//")
+ end
+
+ it "should heuristically parse to 'http://p//' " +
+ "even with a scheme hint of 'ftp'" do
+ @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'})
+ expect(@uri.to_s).to eq("http://p//")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http://p://'" do
+ before do
+ @input = "http://p://"
+ end
+
+ it "should heuristically parse to 'http://p//'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.authority).to eq("p")
+ expect(@uri.to_s).to eq("http://p//")
+ end
+
+ it "should heuristically parse to 'http://p//' " +
+ "even with a scheme hint of 'ftp'" do
+ @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'})
+ expect(@uri.to_s).to eq("http://p//")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http://p://p'" do
+ before do
+ @input = "http://p://p"
+ end
+
+ it "should heuristically parse to 'http://p//p'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.authority).to eq("p")
+ expect(@uri.to_s).to eq("http://p//p")
+ end
+
+ it "should heuristically parse to 'http://p//p' " +
+ "even with a scheme hint of 'ftp'" do
+ @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'})
+ expect(@uri.to_s).to eq("http://p//p")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http://prefix .example.com/'" do
+ before do
+ @input = "http://prefix .example.com/"
+ end
+
+ # Justification here being that no browser actually tries to resolve this.
+ # They all treat this as a web search.
+ it "should heuristically parse to 'http://prefix%20.example.com/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.authority).to eq("prefix%20.example.com")
+ expect(@uri.to_s).to eq("http://prefix%20.example.com/")
+ end
+
+ it "should heuristically parse to 'http://prefix%20.example.com/' " +
+ "even with a scheme hint of 'ftp'" do
+ @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'})
+ expect(@uri.to_s).to eq("http://prefix%20.example.com/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "' http://www.example.com/ '" do
+ before do
+ @input = " http://www.example.com/ "
+ end
+
+ it "should heuristically parse to 'http://prefix%20.example.com/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.scheme).to eq("http")
+ expect(@uri.path).to eq("/")
+ expect(@uri.to_s).to eq("http://www.example.com/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http://prefix%2F.example.com/'" do
+ before do
+ @input = "http://prefix%2F.example.com/"
+ end
+
+ it "should heuristically parse to 'http://prefix%2F.example.com/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.authority).to eq("prefix%2F.example.com")
+ expect(@uri.to_s).to eq("http://prefix%2F.example.com/")
+ end
+
+ it "should heuristically parse to 'http://prefix%2F.example.com/' " +
+ "even with a scheme hint of 'ftp'" do
+ @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'})
+ expect(@uri.to_s).to eq("http://prefix%2F.example.com/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'/path/to/resource'" do
+ before do
+ @input = "/path/to/resource"
+ end
+
+ it "should heuristically parse to '/path/to/resource'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("/path/to/resource")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'relative/path/to/resource'" do
+ before do
+ @input = "relative/path/to/resource"
+ end
+
+ it "should heuristically parse to 'relative/path/to/resource'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("relative/path/to/resource")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'example.com'" do
+ before do
+ @input = "example.com"
+ end
+
+ it "should heuristically parse to 'http://example.com'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("http://example.com")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'example.com' and a scheme hint of 'ftp'" do
+ before do
+ @input = "example.com"
+ @hints = {:scheme => 'ftp'}
+ end
+
+ it "should heuristically parse to 'http://example.com'" do
+ @uri = Addressable::URI.heuristic_parse(@input, @hints)
+ expect(@uri.to_s).to eq("ftp://example.com")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'example.com:21' and a scheme hint of 'ftp'" do
+ before do
+ @input = "example.com:21"
+ @hints = {:scheme => 'ftp'}
+ end
+
+ it "should heuristically parse to 'http://example.com:21'" do
+ @uri = Addressable::URI.heuristic_parse(@input, @hints)
+ expect(@uri.to_s).to eq("ftp://example.com:21")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'example.com/path/to/resource'" do
+ before do
+ @input = "example.com/path/to/resource"
+ end
+
+ it "should heuristically parse to 'http://example.com/path/to/resource'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("http://example.com/path/to/resource")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'http:///example.com'" do
+ before do
+ @input = "http:///example.com"
+ end
+
+ it "should heuristically parse to 'http://example.com'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("http://example.com")
+ end
+end
+
+describe Addressable::URI, "when given the input which "\
+ "start with digits and has specified port" do
+ before do
+ @input = "7777.example.org:8089"
+ end
+
+ it "should heuristically parse to 'http://7777.example.org:8089'" do
+ uri = Addressable::URI.heuristic_parse(@input)
+ expect(uri.to_s).to eq("http://7777.example.org:8089")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'feed:///example.com'" do
+ before do
+ @input = "feed:///example.com"
+ end
+
+ it "should heuristically parse to 'feed://example.com'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("feed://example.com")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'file://localhost/path/to/resource/'" do
+ before do
+ @input = "file://localhost/path/to/resource/"
+ end
+
+ it "should heuristically parse to 'file:///path/to/resource/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("file:///path/to/resource/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'file://path/to/resource/'" do
+ before do
+ @input = "file://path/to/resource/"
+ end
+
+ it "should heuristically parse to 'file:///path/to/resource/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("file:///path/to/resource/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'file://///path/to/resource/'" do
+ before do
+ @input = "file:///////path/to/resource/"
+ end
+
+ it "should heuristically parse to 'file:////path/to/resource/'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("file:////path/to/resource/")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "'feed://http://example.com'" do
+ before do
+ @input = "feed://http://example.com"
+ end
+
+ it "should heuristically parse to 'feed:http://example.com'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("feed:http://example.com")
+ end
+end
+
+describe Addressable::URI, "when given the input " +
+ "::URI.parse('http://example.com')" do
+ before do
+ @input = ::URI.parse('http://example.com')
+ end
+
+ it "should heuristically parse to 'http://example.com'" do
+ @uri = Addressable::URI.heuristic_parse(@input)
+ expect(@uri.to_s).to eq("http://example.com")
+ end
+end
+
+describe Addressable::URI, "when given the input: 'user@domain.com'" do
+ before do
+ @input = "user@domain.com"
+ end
+
+ context "for heuristic parse" do
+ it "should remain 'mailto:user@domain.com'" do
+ uri = Addressable::URI.heuristic_parse("mailto:#{@input}")
+ expect(uri.to_s).to eq("mailto:user@domain.com")
+ end
+
+ it "should have a scheme of 'mailto'" do
+ uri = Addressable::URI.heuristic_parse(@input)
+ expect(uri.to_s).to eq("mailto:user@domain.com")
+ expect(uri.scheme).to eq("mailto")
+ end
+
+ it "should remain 'acct:user@domain.com'" do
+ uri = Addressable::URI.heuristic_parse("acct:#{@input}")
+ expect(uri.to_s).to eq("acct:user@domain.com")
+ end
+
+ context "HTTP" do
+ before do
+ @uri = Addressable::URI.heuristic_parse("http://#{@input}/")
+ end
+
+ it "should remain 'http://user@domain.com/'" do
+ expect(@uri.to_s).to eq("http://user@domain.com/")
+ end
+
+ it "should have the username 'user' for HTTP basic authentication" do
+ expect(@uri.user).to eq("user")
+ end
+ end
+ end
+end
+
+describe Addressable::URI, "when assigning query values" do
+ before do
+ @uri = Addressable::URI.new
+ end
+
+ it "should correctly assign {:a => 'a', :b => ['c', 'd', 'e']}" do
+ @uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
+ expect(@uri.query).to eq("a=a&b=c&b=d&b=e")
+ end
+
+ it "should raise an error attempting to assign {'a' => {'b' => ['c']}}" do
+ expect do
+ @uri.query_values = { 'a' => {'b' => ['c'] } }
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise an error attempting to assign " +
+ "{:b => '2', :a => {:c => '1'}}" do
+ expect do
+ @uri.query_values = {:b => '2', :a => {:c => '1'}}
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise an error attempting to assign " +
+ "{:a => 'a', :b => [{:c => 'c', :d => 'd'}, " +
+ "{:e => 'e', :f => 'f'}]}" do
+ expect do
+ @uri.query_values = {
+ :a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]
+ }
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise an error attempting to assign " +
+ "{:a => 'a', :b => [{:c => true, :d => 'd'}, " +
+ "{:e => 'e', :f => 'f'}]}" do
+ expect do
+ @uri.query_values = {
+ :a => 'a', :b => [{:c => true, :d => 'd'}, {:e => 'e', :f => 'f'}]
+ }
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise an error attempting to assign " +
+ "{:a => 'a', :b => {:c => true, :d => 'd'}}" do
+ expect do
+ @uri.query_values = {
+ :a => 'a', :b => {:c => true, :d => 'd'}
+ }
+ end.to raise_error(TypeError)
+ end
+
+ it "should raise an error attempting to assign " +
+ "{:a => 'a', :b => {:c => true, :d => 'd'}}" do
+ expect do
+ @uri.query_values = {
+ :a => 'a', :b => {:c => true, :d => 'd'}
+ }
+ end.to raise_error(TypeError)
+ end
+
+ it "should correctly assign {:a => 1, :b => 1.5}" do
+ @uri.query_values = { :a => 1, :b => 1.5 }
+ expect(@uri.query).to eq("a=1&b=1.5")
+ end
+
+ it "should raise an error attempting to assign " +
+ "{:z => 1, :f => [2, {999.1 => [3,'4']}, ['h', 'i']], " +
+ ":a => {:b => ['c', 'd'], :e => true, :y => 0.5}}" do
+ expect do
+ @uri.query_values = {
+ :z => 1,
+ :f => [ 2, {999.1 => [3,'4']}, ['h', 'i'] ],
+ :a => { :b => ['c', 'd'], :e => true, :y => 0.5 }
+ }
+ end.to raise_error(TypeError)
+ end
+
+ it "should correctly assign {}" do
+ @uri.query_values = {}
+ expect(@uri.query).to eq('')
+ end
+
+ it "should correctly assign nil" do
+ @uri.query_values = nil
+ expect(@uri.query).to eq(nil)
+ end
+
+ it "should correctly sort {'ab' => 'c', :ab => 'a', :a => 'x'}" do
+ @uri.query_values = {'ab' => 'c', :ab => 'a', :a => 'x'}
+ expect(@uri.query).to eq("a=x&ab=a&ab=c")
+ end
+
+ it "should correctly assign " +
+ "[['b', 'c'], ['b', 'a'], ['a', 'a']]" do
+ # Order can be guaranteed in this format, so preserve it.
+ @uri.query_values = [['b', 'c'], ['b', 'a'], ['a', 'a']]
+ expect(@uri.query).to eq("b=c&b=a&a=a")
+ end
+
+ it "should preserve query string order" do
+ query_string = (('a'..'z').to_a.reverse.map { |e| "#{e}=#{e}" }).join("&")
+ @uri.query = query_string
+ original_uri = @uri.to_s
+ @uri.query_values = @uri.query_values(Array)
+ expect(@uri.to_s).to eq(original_uri)
+ end
+
+ describe 'when a hash with mixed types is assigned to query_values' do
+ it 'should not raise an error' do
+ skip 'Issue #94'
+ expect { subject.query_values = { "page" => "1", :page => 2 } }.to_not raise_error
+ end
+ end
+end
+
+describe Addressable::URI, "when assigning path values" do
+ before do
+ @uri = Addressable::URI.new
+ end
+
+ it "should correctly assign paths containing colons" do
+ @uri.path = "acct:bob@sporkmonger.com"
+ expect(@uri.path).to eq("acct:bob@sporkmonger.com")
+ expect(@uri.normalize.to_str).to eq("acct%2Fbob@sporkmonger.com")
+ expect { @uri.to_s }.to raise_error(
+ Addressable::URI::InvalidURIError
+ )
+ end
+
+ it "should correctly assign paths containing colons" do
+ @uri.path = "/acct:bob@sporkmonger.com"
+ @uri.authority = "example.com"
+ expect(@uri.normalize.to_str).to eq("//example.com/acct:bob@sporkmonger.com")
+ end
+
+ it "should correctly assign paths containing colons" do
+ @uri.path = "acct:bob@sporkmonger.com"
+ @uri.scheme = "something"
+ expect(@uri.normalize.to_str).to eq("something:acct:bob@sporkmonger.com")
+ end
+
+ it "should not allow relative paths to be assigned on absolute URIs" do
+ expect do
+ @uri.scheme = "http"
+ @uri.host = "example.com"
+ @uri.path = "acct:bob@sporkmonger.com"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should not allow relative paths to be assigned on absolute URIs" do
+ expect do
+ @uri.path = "acct:bob@sporkmonger.com"
+ @uri.scheme = "http"
+ @uri.host = "example.com"
+ end.to raise_error(Addressable::URI::InvalidURIError)
+ end
+
+ it "should not allow relative paths to be assigned on absolute URIs" do
+ expect do
+ @uri.path = "uuid:0b3ecf60-3f93-11df-a9c3-001f5bfffe12"
+ @uri.scheme = "urn"
+ end.not_to raise_error
+ end
+end
+
+describe Addressable::URI, "when initializing a subclass of Addressable::URI" do
+ before do
+ @uri = Class.new(Addressable::URI).new
+ end
+
+ it "should have the same class after being parsed" do
+ expect(@uri.class).to eq(Addressable::URI.parse(@uri).class)
+ end
+
+ it "should have the same class as its duplicate" do
+ expect(@uri.class).to eq(@uri.dup.class)
+ end
+
+ it "should have the same class after being normalized" do
+ expect(@uri.class).to eq(@uri.normalize.class)
+ end
+
+ it "should have the same class after being merged" do
+ expect(@uri.class).to eq(@uri.merge(:path => 'path').class)
+ end
+
+ it "should have the same class after being joined" do
+ expect(@uri.class).to eq(@uri.join('path').class)
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/spec_helper.rb b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/spec_helper.rb
new file mode 100644
index 0000000..bd8e395
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/spec/spec_helper.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'bundler/setup'
+require 'rspec/its'
+
+begin
+ require 'coveralls'
+ Coveralls.wear! do
+ add_filter "spec/"
+ add_filter "vendor/"
+ end
+rescue LoadError
+ warn "warning: coveralls gem not found; skipping Coveralls"
+ require 'simplecov'
+ SimpleCov.start do
+ add_filter "spec/"
+ add_filter "vendor/"
+ end
+end if Gem.loaded_specs.key?("simplecov")
+
+class TestHelper
+ def self.native_supported?
+ mri = RUBY_ENGINE == "ruby"
+ windows = RUBY_PLATFORM.include?("mingw")
+
+ mri && !windows
+ end
+end
+
+RSpec.configure do |config|
+ config.warnings = true
+ config.filter_run_when_matching :focus
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/clobber.rake b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/clobber.rake
new file mode 100644
index 0000000..a9e32b3
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/clobber.rake
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+desc "Remove all build products"
+task "clobber"
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/gem.rake b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/gem.rake
new file mode 100644
index 0000000..1f793ba
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/gem.rake
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require "rubygems/package_task"
+
+namespace :gem do
+ GEM_SPEC = Gem::Specification.new do |s|
+ s.name = PKG_NAME
+ s.version = PKG_VERSION
+ s.summary = PKG_SUMMARY
+ s.description = PKG_DESCRIPTION
+
+ s.files = PKG_FILES.to_a
+
+ s.extra_rdoc_files = %w( README.md )
+ s.rdoc_options.concat ["--main", "README.md"]
+
+ if !s.respond_to?(:add_development_dependency)
+ puts "Cannot build Gem with this version of RubyGems."
+ exit(1)
+ end
+
+ s.required_ruby_version = ">= 2.0"
+
+ s.add_runtime_dependency "public_suffix", ">= 2.0.2", "< 5.0"
+ s.add_development_dependency "bundler", ">= 1.0", "< 3.0"
+
+ s.require_path = "lib"
+
+ s.author = "Bob Aman"
+ s.email = "bob@sporkmonger.com"
+ s.homepage = "https://github.com/sporkmonger/addressable"
+ s.license = "Apache-2.0"
+ end
+
+ Gem::PackageTask.new(GEM_SPEC) do |p|
+ p.gem_spec = GEM_SPEC
+ p.need_tar = true
+ p.need_zip = true
+ end
+
+ desc "Generates .gemspec file"
+ task :gemspec do
+ spec_string = GEM_SPEC.to_ruby
+ File.open("#{GEM_SPEC.name}.gemspec", "w") do |file|
+ file.write spec_string
+ end
+ end
+
+ desc "Show information about the gem"
+ task :debug do
+ puts GEM_SPEC.to_ruby
+ end
+
+ desc "Install the gem"
+ task :install => ["clobber", "gem:package"] do
+ sh "#{SUDO} gem install --local pkg/#{GEM_SPEC.full_name}"
+ end
+
+ desc "Uninstall the gem"
+ task :uninstall do
+ installed_list = Gem.source_index.find_name(PKG_NAME)
+ if installed_list &&
+ (installed_list.collect { |s| s.version.to_s}.include?(PKG_VERSION))
+ sh(
+ "#{SUDO} gem uninstall --version '#{PKG_VERSION}' " +
+ "--ignore-dependencies --executables #{PKG_NAME}"
+ )
+ end
+ end
+
+ desc "Reinstall the gem"
+ task :reinstall => [:uninstall, :install]
+
+ desc "Package for release"
+ task :release => ["gem:package", "gem:gemspec"] do |t|
+ v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
+ abort "Versions don't match #{v} vs #{PROJ.version}" if v != PKG_VERSION
+ pkg = "pkg/#{GEM_SPEC.full_name}"
+
+ changelog = File.open("CHANGELOG.md") { |file| file.read }
+
+ puts "Releasing #{PKG_NAME} v. #{PKG_VERSION}"
+ Rake::Task["git:tag:create"].invoke
+ end
+end
+
+desc "Alias to gem:package"
+task "gem" => "gem:package"
+
+task "gem:release" => "gem:gemspec"
+
+task "clobber" => ["gem:clobber_package"]
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/git.rake b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/git.rake
new file mode 100644
index 0000000..1238c8d
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/git.rake
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+namespace :git do
+ namespace :tag do
+ desc "List tags from the Git repository"
+ task :list do
+ tags = `git tag -l`
+ tags.gsub!("\r", "")
+ tags = tags.split("\n").sort {|a, b| b <=> a }
+ puts tags.join("\n")
+ end
+
+ desc "Create a new tag in the Git repository"
+ task :create do
+ changelog = File.open("CHANGELOG.md", "r") { |file| file.read }
+ puts "-" * 80
+ puts changelog
+ puts "-" * 80
+ puts
+
+ v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
+ abort "Versions don't match #{v} vs #{PKG_VERSION}" if v != PKG_VERSION
+
+ git_status = `git status`
+ if git_status !~ /^nothing to commit/
+ abort "Working directory isn't clean."
+ end
+
+ tag = "#{PKG_NAME}-#{PKG_VERSION}"
+ msg = "Release #{PKG_NAME}-#{PKG_VERSION}"
+
+ existing_tags = `git tag -l #{PKG_NAME}-*`.split('\n')
+ if existing_tags.include?(tag)
+ warn("Tag already exists, deleting...")
+ unless system "git tag -d #{tag}"
+ abort "Tag deletion failed."
+ end
+ end
+ puts "Creating git tag '#{tag}'..."
+ unless system "git tag -a -m \"#{msg}\" #{tag}"
+ abort "Tag creation failed."
+ end
+ end
+ end
+end
+
+task "gem:release" => "git:tag:create"
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/metrics.rake b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/metrics.rake
new file mode 100644
index 0000000..107cc24
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/metrics.rake
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+namespace :metrics do
+ task :lines do
+ lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
+ for file_name in FileList["lib/**/*.rb"]
+ f = File.open(file_name)
+ while line = f.gets
+ lines += 1
+ next if line =~ /^\s*$/
+ next if line =~ /^\s*#/
+ codelines += 1
+ end
+ puts "L: #{sprintf("%4d", lines)}, " +
+ "LOC #{sprintf("%4d", codelines)} | #{file_name}"
+ total_lines += lines
+ total_codelines += codelines
+
+ lines, codelines = 0, 0
+ end
+
+ puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/profile.rake b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/profile.rake
new file mode 100644
index 0000000..b697d48
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/profile.rake
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+namespace :profile do
+ desc "Profile Template match memory allocations"
+ task :template_match_memory do
+ require "memory_profiler"
+ require "addressable/template"
+
+ start_at = Time.now.to_f
+ template = Addressable::Template.new("http://example.com/{?one,two,three}")
+ report = MemoryProfiler.report do
+ 30_000.times do
+ template.match(
+ "http://example.com/?one=one&two=floo&three=me"
+ )
+ end
+ end
+ end_at = Time.now.to_f
+ print_options = { scale_bytes: true, normalize_paths: true }
+ puts "\n\n"
+
+ if ENV["CI"]
+ report.pretty_print(print_options)
+ else
+ t_allocated = report.scale_bytes(report.total_allocated_memsize)
+ t_retained = report.scale_bytes(report.total_retained_memsize)
+
+ puts "Total allocated: #{t_allocated} (#{report.total_allocated} objects)"
+ puts "Total retained: #{t_retained} (#{report.total_retained} objects)"
+ puts "Took #{end_at - start_at} seconds"
+
+ FileUtils.mkdir_p("tmp")
+ report.pretty_print(to_file: "tmp/memprof.txt", **print_options)
+ end
+ end
+
+ desc "Profile URI parse memory allocations"
+ task :memory do
+ require "memory_profiler"
+ require "addressable/uri"
+ if ENV["IDNA_MODE"] == "pure"
+ Addressable.send(:remove_const, :IDNA)
+ load "addressable/idna/pure.rb"
+ end
+
+ start_at = Time.now.to_f
+ report = MemoryProfiler.report do
+ 30_000.times do
+ Addressable::URI.parse(
+ "http://google.com/stuff/../?with_lots=of¶ms=asdff#!stuff"
+ ).normalize
+ end
+ end
+ end_at = Time.now.to_f
+ print_options = { scale_bytes: true, normalize_paths: true }
+ puts "\n\n"
+
+ if ENV["CI"]
+ report.pretty_print(**print_options)
+ else
+ t_allocated = report.scale_bytes(report.total_allocated_memsize)
+ t_retained = report.scale_bytes(report.total_retained_memsize)
+
+ puts "Total allocated: #{t_allocated} (#{report.total_allocated} objects)"
+ puts "Total retained: #{t_retained} (#{report.total_retained} objects)"
+ puts "Took #{end_at - start_at} seconds"
+
+ FileUtils.mkdir_p("tmp")
+ report.pretty_print(to_file: "tmp/memprof.txt", **print_options)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/rspec.rake b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/rspec.rake
new file mode 100644
index 0000000..e3d9f01
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/rspec.rake
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require "rspec/core/rake_task"
+
+namespace :spec do
+ RSpec::Core::RakeTask.new(:simplecov) do |t|
+ t.pattern = FileList['spec/**/*_spec.rb']
+ t.rspec_opts = %w[--color --format documentation] unless ENV["CI"]
+ end
+
+ namespace :simplecov do
+ desc "Browse the code coverage report."
+ task :browse => "spec:simplecov" do
+ require "launchy"
+ Launchy.open("coverage/index.html")
+ end
+ end
+end
+
+desc "Alias to spec:simplecov"
+task "spec" => "spec:simplecov"
+
+task "clobber" => ["spec:clobber_simplecov"]
diff --git a/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/yard.rake b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/yard.rake
new file mode 100644
index 0000000..515f960
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/addressable-2.8.0/tasks/yard.rake
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require "rake"
+
+begin
+ require "yard"
+ require "yard/rake/yardoc_task"
+
+ namespace :doc do
+ desc "Generate Yardoc documentation"
+ YARD::Rake::YardocTask.new do |yardoc|
+ yardoc.name = "yard"
+ yardoc.options = ["--verbose", "--markup", "markdown"]
+ yardoc.files = FileList[
+ "lib/**/*.rb", "ext/**/*.c",
+ "README.md", "CHANGELOG.md", "LICENSE.txt"
+ ].exclude(/idna/)
+ end
+ end
+
+ task "clobber" => ["doc:clobber_yard"]
+
+ desc "Alias to doc:yard"
+ task "doc" => "doc:yard"
+rescue LoadError
+ # If yard isn't available, it's not the end of the world
+ desc "Alias to doc:rdoc"
+ task "doc" => "doc:rdoc"
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/.rspec b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/.rspec
new file mode 100644
index 0000000..397921f
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/.rspec
@@ -0,0 +1,2 @@
+--color
+--format d
diff --git a/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/.travis.yml b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/.travis.yml
new file mode 100644
index 0000000..4c090ba
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/.travis.yml
@@ -0,0 +1,33 @@
+language: ruby
+
+branches:
+ only:
+ - master
+
+rvm:
+- 1.9.3
+- 2.0
+- 2.1
+- 2.2
+- 2.3
+- 2.4
+- 2.5
+- jruby
+- rbx-3
+
+matrix:
+ allow_failures:
+ - rvm: rbx-3
+ - rvm: jruby
+ include:
+ - rvm: 1.8.7
+ dist: precise
+
+
+cache: bundler
+
+before_script:
+ - wget https://alg.li/algolia-keys && chmod +x algolia-keys
+
+script:
+ - if [ "$TRAVIS_PULL_REQUEST" != "false" ] && [[ ! "$TRAVIS_PULL_REQUEST_SLUG" =~ ^algolia\/ ]]; then eval $(./algolia-keys export) && bundle exec rspec --tag ~maintainers_only; else bundle exec rspec; fi
diff --git a/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/CHANGELOG.md b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/CHANGELOG.md
new file mode 100755
index 0000000..26969f8
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/CHANGELOG.md
@@ -0,0 +1,454 @@
+# ChangeLog
+
+## Unreleased
+
+## [1.27.4](https://github.com/algolia/algoliasearch-client-ruby/compare/1.27.3...1.27.4) (2020-09-16)
+
+**Fixed**
+
+* Retrieve all objects when using `copy_index` from `AccountClient` class ([399](https://github.com/algolia/algoliasearch-client-ruby/pull/399))
+
+## [1.27.3](https://github.com/algolia/algoliasearch-client-ruby/compare/1.27.2...1.27.3) (2020-06-03)
+
+**Fixed**
+
+* Replace expired certificate within embedded certificate chain ([9087dd1](https://github.com/algolia/algoliasearch-client-ruby/commit/9087dd14a97bf77c9391a3360c4803edf686086d))
+
+## [1.27.2](https://github.com/algolia/algoliasearch-client-ruby/compare/1.27.1...1.27.2) (2020-04-28)
+
+**Fixed**
+
+* In `search_user_id`, retrieve param `cluster` instead of `clusterName`. [368](https://github.com/algolia/algoliasearch-client-ruby/issues/368)
+
+## [1.27.1](https://github.com/algolia/algoliasearch-client-ruby/compare/1.27.0...1.27.1) (2019-09-26)
+
+**Fixed**
+
+* Update `Algolia::Index.exists` method to `Algolia::Index.exists?`. [364](https://github.com/algolia/algoliasearch-client-ruby/issues/364)
+
+## [1.27.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.27.0) (2019-09-16)
+
+**Added**
+
+* Introduce `Algolia::Index.exists` method. [358](https://github.com/algolia/algoliasearch-client-ruby/issues/358)
+
+ Check whether an index exists or not.
+
+* Introduce `Algolia::Index.find_object` method. [359](https://github.com/algolia/algoliasearch-client-ruby/issues/359)
+
+ Find object by the given condition.
+
+* Introduce `Algolia::Index.get_object_position` method. [359](https://github.com/algolia/algoliasearch-client-ruby/issues/359)
+
+ Retrieve the given object position in a set of results.
+
+* Introduce `Algolia.get_secured_api_key_remaining_validity` method. [361](https://github.com/algolia/algoliasearch-client-ruby/issues/361)
+
+ Returns the remaining validity time for the given API key in seconds.
+
+
+## [1.26.1](https://github.com/algolia/algoliasearch-client-ruby/compare/1.26.0...1.26.1) (2019-07-31)
+
+### Chore
+
+- stop using coveralls because of a GPL-licensed transitive dep ([d2fbe8c](https://github.com/algolia/algoliasearch-client-ruby/commit/d2fbe8c))
+
+
+[1.26.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.26.0) (2019-02-12)
+
+**Added**
+
+* Introduce `Algolia.restore_api_key` method.
+
+ If you delete your API key by mistake, you can now restore it via
+ this new method. This especially useful if this key is used in a
+ mobile app or somewhere you can't update easily.
+
+
+## [1.25.2](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.25.2) (2018-12-19)
+
+## [1.25.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.25.1) (2018-12-19)
+
+**Fixed**
+
+* Missing `insights.rb` in gemspec - [7d2f3ab](https://github.com/algolia/algoliasearch-client-ruby/commit/7d2f3abe6e4338f0f7364f6f52ac1d371f066464)
+
+## [1.25.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.25.0) (2018-12-19)
+
+**Added**
+
+* Introduce Insights client to send events to Algolia Insights API - [326](https://github.com/algolia/algoliasearch-client-ruby/issues/326)
+
+* Add `multiple_get_objects` to retrieve objects by objectID across multiple indices - [329](https://github.com/algolia/algoliasearch-client-ruby/issues/329)
+
+**Modified**
+
+* Use the correct `hitsPerPage` key when exporting index resources - [319](https://github.com/algolia/algoliasearch-client-ruby/issues/319)
+
+## [1.24.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.24.0) (2018-11-28)
+
+* Feat(adds-account-client-copy-index): adds `copy_index` to account client ([#324](https://github.com/algolia/algoliasearch-client-ruby/pull/324))
+* Feat(replace-all-methods): adds `replace_all_rules`, `replace_all_objects` and `replace_all_synonyms` to search index ([#323](https://github.com/algolia/algoliasearch-client-ruby/pull/323))
+* Feat(scoped-copy-methods): adds `copy_settings`, `copy_synonyms` and `copy_rules` to search client ([#322](https://github.com/algolia/algoliasearch-client-ruby/pull/322))
+
+## [1.23.2](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.23.2) (2018-06-19)
+
+* Fix(analytics): gem without new analytics class (#306)
+
+## [1.23.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.23.0) (2018-06-19)
+
+* Feat(analytics): introduce new analytics class
+* Chore(rake): use unshift to keep compat with older ruby versions
+* Ruby version must be after client version in ua
+* Fix ua tests with new format
+* Rewrite ua
+* Feat(ua): add ruby version
+* Fix(syntax): this isn't php
+* Tests(mcm): use unique userid everytime
+* Tests(mcm): introduce auto_retry for more stable tests
+* Feat(client): expose waittask in the client (#302)
+* Fix(travis): always test on the latest patches (#295)
+
+## [1.22.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.22.0) (2018-05-30)
+
+* Rename license file (#297)
+* Fix release task (#294)
+* Introduce multi cluster management (#285)
+* Fix(browse): ensure cursor is passed in the body (#288)
+* Chore(md): update contribution-related files (#293)
+
+## [1.21.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.21.0) (2018-05-24)
+
+* Fix(tests): fix warning for unspecified exception (#287)
+* Fix release task missing github link (#291)
+* Api review (#292)
+
+## [1.20.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.20.1) (2018-05-15)
+
+* Fix changelog link in gemspec (#290)
+* Utils: move to changelog.md and add rake task for release (#289)
+
+## [1.20.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.20.0) (2018-05-07)
+
+* Feat: deprecate api keys methods on index in favor of client ones (#286)
+* Chore(gemfile): remove useless dependencies (#280)
+* Fix(env): adding default env var (#279)
+* Chore(travis): test against Rubinius 3 (#281)
+* Fix: prevent saving a rule with an empty `objectID` (#283)
+
+## [1.19.2](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.19.2) (2018-04-03)
+
+* Fix `Algolia.delete_index` wrong number of arguments (#277)
+
+## [1.19.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.19.1) (2017-12-18)
+
+* Fix hard dependency on `hashdiff` (#262)
+
+## [1.19.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.19.0) (2017-12-15)
+
+* Add request options to any method using API calls (#213)
+* Add `export_synonyms` index method (#260)
+* Add `export_rules` index method (#261)
+
+## [1.18.5](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.18.5) (2017-12-07)
+
+* Fix missing requirement
+
+## [1.18.4](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.18.4) (2017-12-06)
+
+* Remove remaining unnecessary requirements (#256)
+* Remove Gemfile.lock (#257)
+
+## [1.18.3](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.18.3) (2017-12-04)
+
+* Remove Bundler and RubyGems requirements (#253)
+
+## [1.18.2](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.18.2) (2017-11-28)
+
+* Add (undocumented) gzip option to disable gzip (#240)
+
+## [1.18.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.18.1) (2017-11-15)
+
+* Fix `get_logs` always returning type `all` (#244)
+* New scopes to `copy_index` method (#243)
+
+## [1.18.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.18.0) (2017-11-02)
+
+* Allow to reuse the webmocks using `Algolia::WebMock.mock!` (#256)
+
+## [1.17.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.17.0) (2017-10-10)
+
+* New `delete_by` method
+
+## [1.16.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.16.0) (2017-09-14)
+
+* New Query Rules API
+
+## [1.15.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.15.1) (2017-08-17)
+
+* Fixed regression introduced in 1.15.0
+
+## [1.15.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.15.0) (2017-08-17)
+
+* Make `delete_by_query` not `wait_task` by default (also, make it mockable)
+* Add a new `delete_by_query!` doing the last `wait_task`
+
+## [1.14.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.14.0) (2017-07-31)
+
+* Ability to override the underlying user-agent
+
+## [1.13.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.13.0) (2017-03-17)
+
+* Add a `index.get_task_status(taskID)` method (#199)
+
+## [1.12.7](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.12.7) (2017-03-01)
+
+* Renamed all `*_user_key` methods to `*_api_key`
+
+## [1.12.6](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.12.6) (2017-01-25)
+
+* Upgraded `httpclient` to 2.8.3
+
+## [1.12.5](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.12.5) (2016-12-07)
+
+* Fixed retry strategy not keeping the `current_host` first (#163)
+
+## [1.12.4](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.12.4) (2016-12-07)
+
+* Fix DNS tests
+
+## [1.12.3](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.12.3) (2016-12-06)
+
+* Allow for multiple clients on different app ids on the same thread
+
+## [1.12.2](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.12.2) (2016-12-05)
+
+* Fix client scoped methods
+
+## [1.12.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.12.1) (2016-11-25)
+
+* Rename `search_facet` to `search_for_facet_values`
+
+## [1.12.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.12.0) (2016-10-31)
+
+* Add `search_facet`
+
+## [1.11.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.11.0) (2016-08-21)
+
+* Upgraded to httpclient 2.8.1 to avoid reseting the keep-alive while changing timeouts
+
+## [1.10.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.10.0) (2016-07-11)
+
+* `{get,set}_settings` now take optional custom query parameters
+
+## [1.9.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.9.0) (2016-06-17)
+
+* New synonyms API
+
+## [1.8.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.8.1) (2016-04-14)
+
+* Ensure we're using an absolute path for the `ca-bundle.crt` file (could fix some `OpenSSL::X509::StoreError: system lib` errors)
+
+## [1.8.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.8.0) (2016-04-06)
+
+* Upgraded to `httpclient` 2.7.1 (includes ruby 2.3.0 deprecation fixes)
+* Upgraded WebMock URLs
+
+## [1.7.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.7.0) (2016-01-09)
+
+* New `generate_secured_api_key` embedding the filters in the resulting key
+
+## [1.6.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.6.1) (2015-08-01)
+
+* Search queries are now using POST requests
+
+## [1.6.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.6.0) (2015-07-19)
+
+* Ability to instantiate multiple API clients in the same process (was using a class variable before).
+
+## [1.5.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.5.1) (2015-07-14)
+
+* Ability to retrieve a single page from a cursor with `browse_from`
+
+## [1.5.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.5.0) (2015-06-05)
+
+* New cursor-based `browse()` implementation taking query parameters
+
+## [1.4.3](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.4.3) (2015-05-27)
+
+* Do not call `WebMock.disable!` in the helper
+
+## [1.4.2](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.4,2) (2015-05-04)
+
+* Add new methods to add/update api key
+* Add batch method to target multiple indices
+* Add strategy parameter for the multipleQueries
+* Add new method to generate secured api key from query parameters
+
+## [1.4.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.4.1) (2015-04-10)
+
+* Force the default connect/read/write/search/batch timeouts to Algolia-specific values
+
+## [1.4.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.4.0) (2015-03-17)
+
+* High-available DNS: search queries are now targeting `APPID-DSN.algolia.net` first, then the main cluster using NSOne, then the main cluster using Route53.
+* High-available DNS: Indexing queries are targeting `APPID.algolia.net` first, then the main cluster using NSOne, then the main cluster using Route53.
+
+## [1.3.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.3.1) (2014-11-29)
+
+* Fixed wrong deployed version (1.3.0 was based on 1.2.13 instead of 1.2.14)
+
+## [1.3.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.3.0) (2014-11-29)
+
+* Use `algolia.net` domain instead of `algolia.io`
+
+## [1.2.14](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.14) (2014-11-10)
+
+* Force the underlying `httpclient` dependency to be >= 2.4 in the gemspec as well
+* Ability to force the SSL version
+
+## [1.2.13](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.13) (2014-10-22)
+
+* Fix the loop on hosts to retry when the http code is different than 200, 201, 400, 403, 404
+
+## [1.2.12](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.12) (2014-10-08)
+
+* Upgrade to `httpclient` 2.4
+* Do not reset the timeout on each requests
+
+## [1.2.11](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.11) (2014-09-14)
+
+* Ability to update API keys
+
+## [1.2.10](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.10) (2014-08-22)
+
+* Using Digest to remove "Digest::Digest is deprecated; Use Digest" warning (author: @dglancy)
+
+## [1.2.9](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.9) (2014-07-10)
+
+* Expose `connect_timeout`, `receive_timeout` and `send_timeout`
+* Add new `delete_by_query` method to delete all objects matching a specific query
+* Add new `get_objects` method to retrieve a list of objects from a single API call
+* Add a helper to perform disjunctive faceting
+
+## [1.2.8](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.8) (2014-03-27)
+
+* Catch all exceptions before retrying with another host
+
+## [1.2.7](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.7) (2014-03-24)
+
+* Ruby 1.8 compatibility
+
+## [1.2.6](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.6) (2014-03-19)
+
+* Raise an exception if no `APPLICATION_ID` is provided
+* Ability to get last API call errors
+* Ability to send multiple queries using a single API call
+* Secured API keys generation is now based on secured HMAC-SHA256
+
+## [1.2.5](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.5) (2014-02-24)
+
+* Ability to generate secured API key from a list of tags + optional `user_token`
+* Ability to specify a list of indexes targeted by the user key
+
+## [1.2.4](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.4) (2014-02-21)
+
+* Add `delete_objects`
+
+## [1.2.3](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.3) (2014-02-10)
+
+* `add_object`: POST request if `objectID` is `nil` OR `empty`
+
+## [1.2.2](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.2) (2014-01-11)
+
+* Expose `batch` requests
+
+## [1.2.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.1) (2014-01-07)
+
+* Removed `jeweler` since it doesn't support platform specific deps (see https://github.com/technicalpickles/jeweler/issues/170)
+
+## [1.2.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.2.0) (2014-01-07)
+
+* Removed `curb` dependency and switched on `httpclient` to avoid fork-safety issue (see issue #5)
+
+## [1.1.18](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.18) (2014-01-06)
+
+* Fixed batch request builder (broken since last refactoring)
+
+## [1.1.17](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.17) (2014-01-02)
+
+* Ability to use IP rate limit behind a proxy forwarding the end-user's IP
+* Add documentation for the new `attributeForDistinct` setting and `distinct` search parameter
+
+## [1.1.16](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.16) (2013-12-16)
+
+* Add arguments type-checking
+* Normalize save_object/partial_update/add_object signatures
+* Force dependencies versions
+
+## [1.1.15](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.15) (2013-12-16)
+
+* Embed ca-bundle.crt
+
+## [1.1.14](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.14) (2013-12-11)
+
+* Added `index.add_user_key(acls, validity, rate_limit, maxApiCalls)``
+
+## [1.1.13](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.13) (2013-12-10)
+
+* WebMock integration
+
+## [1.1.12](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.12) (2013-12-05)
+
+* Add `browse` command
+
+## [1.1.11](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.11) (2013-11-29)
+
+* Remove rubysl (rbx required dependencies)
+
+## [1.1.10](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.10) (2013-11-29)
+
+* Fixed gzip handling bug
+
+## [1.1.9](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.9) (2013-11-28)
+
+* Added gzip support
+
+## [1.1.8](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.8) (2013-11-26)
+
+* Add `partial_update_objects` method
+
+## [1.1.7](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.7) (2013-11-08)
+
+* Search params: encode array-based parameters (`tagFilters`, `facetFilters`, ...)
+
+## [1.1.6](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.6) (2013-11-07)
+
+* Index: `clear` and `clear!` methods can now be used the delete the whole content of an index
+* User keys: plug new `maxQueriesPerIPPerHour` and `maxHitsPerQuery` parameters
+
+## [1.1.5](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.5) (2013-10-17)
+
+* Version is now part of the user-agent
+
+## [1.1.4](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.4) (2013-10-17)
+
+* Fixed `wait_task` not sleeping at all
+
+## [1.1.3](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.3) (2013-10-15)
+
+* Fixed thread-safety issues
+* Curl sessions are now thread-local
+
+## [1.1.2](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.2) (2013-10-02)
+
+* Fixed instance/class method conflict
+
+## [1.1.1](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.1) (2013-10-01)
+
+* Updated documentation
+* Plug copy/move index
+
+## [1.1.0](https://github.com/algolia/algoliasearch-client-ruby/releases/tag/1.1.0) (2013-09-17)
+
+* Initial import
diff --git a/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/Gemfile b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/Gemfile
new file mode 100644
index 0000000..e05662e
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/Gemfile
@@ -0,0 +1,28 @@
+source 'https://rubygems.org'
+
+git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
+
+# Load algoliasearch.gemspec dependencies
+gemspec
+
+# See https://github.com/algolia/algoliasearch-client-ruby/pull/257/files/36bcd0b1c4d05776dcbdb362c15a609c81f41cde
+if Gem::Version.new(RUBY_VERSION) <= Gem::Version.new('1.9.3')
+ gem 'hashdiff', '< 0.3.6' # Hashdiff 0.3.6 no longer supports Ruby 1.8
+ gem 'highline', '< 1.7.0'
+ gem 'mime-types', '< 2.0'
+ gem 'rubysl', '~> 2.0', :platform => :rbx
+else
+ gem 'rubysl', '~> 2.2', :platform => :rbx
+end
+
+group :development do
+ gem 'rake'
+ gem 'rdoc'
+ gem 'travis'
+end
+
+group :test do
+ gem 'rspec', '>= 2.5.0'
+ gem 'webmock'
+ gem 'simplecov'
+end
diff --git a/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/Gemfile.lock b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/Gemfile.lock
new file mode 100644
index 0000000..998dc7c
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/Gemfile.lock
@@ -0,0 +1,99 @@
+PATH
+ remote: .
+ specs:
+ algoliasearch (1.27.4)
+ httpclient (~> 2.8, >= 2.8.3)
+ json (>= 1.5.1)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ addressable (2.7.0)
+ public_suffix (>= 2.0.2, < 5.0)
+ backports (3.18.2)
+ connection_pool (2.2.3)
+ crack (0.4.3)
+ safe_yaml (~> 1.0.0)
+ diff-lcs (1.4.4)
+ docile (1.3.2)
+ ethon (0.12.0)
+ ffi (>= 1.3.0)
+ faraday (0.17.3)
+ multipart-post (>= 1.2, < 3)
+ faraday_middleware (0.14.0)
+ faraday (>= 0.7.4, < 1.0)
+ ffi (1.13.1)
+ gh (0.14.0)
+ addressable
+ backports
+ faraday (~> 0.8)
+ multi_json (~> 1.0)
+ net-http-persistent (>= 2.7)
+ net-http-pipeline
+ hashdiff (1.0.1)
+ highline (1.7.10)
+ httpclient (2.8.3)
+ json (2.3.1)
+ launchy (2.5.0)
+ addressable (~> 2.7)
+ multi_json (1.15.0)
+ multipart-post (2.1.1)
+ net-http-persistent (4.0.0)
+ connection_pool (~> 2.2)
+ net-http-pipeline (1.0.1)
+ public_suffix (4.0.6)
+ pusher-client (0.6.2)
+ json
+ websocket (~> 1.0)
+ rake (13.0.1)
+ rdoc (6.2.1)
+ rspec (3.9.0)
+ rspec-core (~> 3.9.0)
+ rspec-expectations (~> 3.9.0)
+ rspec-mocks (~> 3.9.0)
+ rspec-core (3.9.2)
+ rspec-support (~> 3.9.3)
+ rspec-expectations (3.9.2)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.9.0)
+ rspec-mocks (3.9.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.9.0)
+ rspec-support (3.9.3)
+ safe_yaml (1.0.5)
+ simplecov (0.19.0)
+ docile (~> 1.1)
+ simplecov-html (~> 0.11)
+ simplecov-html (0.12.2)
+ travis (1.8.13)
+ backports
+ faraday (~> 0.9)
+ faraday_middleware (~> 0.9, >= 0.9.1)
+ gh (~> 0.13)
+ highline (~> 1.6)
+ launchy (~> 2.1)
+ pusher-client (~> 0.4)
+ typhoeus (~> 0.6, >= 0.6.8)
+ typhoeus (0.8.0)
+ ethon (>= 0.8.0)
+ webmock (3.8.3)
+ addressable (>= 2.3.6)
+ crack (>= 0.3.2)
+ hashdiff (>= 0.4.0, < 2.0.0)
+ websocket (1.2.8)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ algoliasearch!
+ rake
+ rdoc
+ rspec (>= 2.5.0)
+ rubysl (~> 2.2)
+ simplecov
+ travis
+ webmock
+
+BUNDLED WITH
+ 1.17.2
diff --git a/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/LICENSE b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/LICENSE
new file mode 100644
index 0000000..fddf416
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-Present Algolia
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/README.md b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/README.md
new file mode 100644
index 0000000..a19b944
--- /dev/null
+++ b/vendor/bundle/ruby/2.6.0/gems/algoliasearch-1.27.5/README.md
@@ -0,0 +1,61 @@
+
This class is a member of the
+ *
+ * Java Collections Framework.
+ *
+ * @since 1.5
+ * @author Doug Lea
+ * @param the type of keys maintained by this map
+ * @param the type of mapped values
+ */
+public class ConcurrentHashMapV8
+ implements ConcurrentMap, Serializable, ConcurrentHashMap {
+ private static final long serialVersionUID = 7249069246763182397L;
+
+ /**
+ * A partitionable iterator. A Spliterator can be traversed
+ * directly, but can also be partitioned (before traversal) by
+ * creating another Spliterator that covers a non-overlapping
+ * portion of the elements, and so may be amenable to parallel
+ * execution.
+ *
+ *
This interface exports a subset of expected JDK8
+ * functionality.
+ *
+ *
Sample usage: Here is one (of the several) ways to compute
+ * the sum of the values held in a map using the ForkJoin
+ * framework. As illustrated here, Spliterators are well suited to
+ * designs in which a task repeatedly splits off half its work
+ * into forked subtasks until small enough to process directly,
+ * and then joins these subtasks. Variants of this style can also
+ * be used in completion-based designs.
+ *
+ *
+ * {@code ConcurrentHashMapV8 m = ...
+ * // split as if have 8 * parallelism, for load balance
+ * int n = m.size();
+ * int p = aForkJoinPool.getParallelism() * 8;
+ * int split = (n < p)? n : p;
+ * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
+ * // ...
+ * static class SumValues extends RecursiveTask {
+ * final Spliterator s;
+ * final int split; // split while > 1
+ * final SumValues nextJoin; // records forked subtasks to join
+ * SumValues(Spliterator s, int depth, SumValues nextJoin) {
+ * this.s = s; this.depth = depth; this.nextJoin = nextJoin;
+ * }
+ * public Long compute() {
+ * long sum = 0;
+ * SumValues subtasks = null; // fork subtasks
+ * for (int s = split >>> 1; s > 0; s >>>= 1)
+ * (subtasks = new SumValues(s.split(), s, subtasks)).fork();
+ * while (s.hasNext()) // directly process remaining elements
+ * sum += s.next();
+ * for (SumValues t = subtasks; t != null; t = t.nextJoin)
+ * sum += t.join(); // collect subtask results
+ * return sum;
+ * }
+ * }
+ * }
+ */
+ public static interface Spliterator extends Iterator {
+ /**
+ * Returns a Spliterator covering approximately half of the
+ * elements, guaranteed not to overlap with those subsequently
+ * returned by this Spliterator. After invoking this method,
+ * the current Spliterator will not produce any of
+ * the elements of the returned Spliterator, but the two
+ * Spliterators together will produce all of the elements that
+ * would have been produced by this Spliterator had this
+ * method not been called. The exact number of elements
+ * produced by the returned Spliterator is not guaranteed, and
+ * may be zero (i.e., with {@code hasNext()} reporting {@code
+ * false}) if this Spliterator cannot be further split.
+ *
+ * @return a Spliterator covering approximately half of the
+ * elements
+ * @throws IllegalStateException if this Spliterator has
+ * already commenced traversing elements
+ */
+ Spliterator split();
+ }
+
+
+ /*
+ * Overview:
+ *
+ * The primary design goal of this hash table is to maintain
+ * concurrent readability (typically method get(), but also
+ * iterators and related methods) while minimizing update
+ * contention. Secondary goals are to keep space consumption about
+ * the same or better than java.util.HashMap, and to support high
+ * initial insertion rates on an empty table by many threads.
+ *
+ * Each key-value mapping is held in a Node. Because Node fields
+ * can contain special values, they are defined using plain Object
+ * types. Similarly in turn, all internal methods that use them
+ * work off Object types. And similarly, so do the internal
+ * methods of auxiliary iterator and view classes. All public
+ * generic typed methods relay in/out of these internal methods,
+ * supplying null-checks and casts as needed. This also allows
+ * many of the public methods to be factored into a smaller number
+ * of internal methods (although sadly not so for the five
+ * variants of put-related operations). The validation-based
+ * approach explained below leads to a lot of code sprawl because
+ * retry-control precludes factoring into smaller methods.
+ *
+ * The table is lazily initialized to a power-of-two size upon the
+ * first insertion. Each bin in the table normally contains a
+ * list of Nodes (most often, the list has only zero or one Node).
+ * Table accesses require volatile/atomic reads, writes, and
+ * CASes. Because there is no other way to arrange this without
+ * adding further indirections, we use intrinsics
+ * (sun.misc.Unsafe) operations. The lists of nodes within bins
+ * are always accurately traversable under volatile reads, so long
+ * as lookups check hash code and non-nullness of value before
+ * checking key equality.
+ *
+ * We use the top two bits of Node hash fields for control
+ * purposes -- they are available anyway because of addressing
+ * constraints. As explained further below, these top bits are
+ * used as follows:
+ * 00 - Normal
+ * 01 - Locked
+ * 11 - Locked and may have a thread waiting for lock
+ * 10 - Node is a forwarding node
+ *
+ * The lower 30 bits of each Node's hash field contain a
+ * transformation of the key's hash code, except for forwarding
+ * nodes, for which the lower bits are zero (and so always have
+ * hash field == MOVED).
+ *
+ * Insertion (via put or its variants) of the first node in an
+ * empty bin is performed by just CASing it to the bin. This is
+ * by far the most common case for put operations under most
+ * key/hash distributions. Other update operations (insert,
+ * delete, and replace) require locks. We do not want to waste
+ * the space required to associate a distinct lock object with
+ * each bin, so instead use the first node of a bin list itself as
+ * a lock. Blocking support for these locks relies on the builtin
+ * "synchronized" monitors. However, we also need a tryLock
+ * construction, so we overlay these by using bits of the Node
+ * hash field for lock control (see above), and so normally use
+ * builtin monitors only for blocking and signalling using
+ * wait/notifyAll constructions. See Node.tryAwaitLock.
+ *
+ * Using the first node of a list as a lock does not by itself
+ * suffice though: When a node is locked, any update must first
+ * validate that it is still the first node after locking it, and
+ * retry if not. Because new nodes are always appended to lists,
+ * once a node is first in a bin, it remains first until deleted
+ * or the bin becomes invalidated (upon resizing). However,
+ * operations that only conditionally update may inspect nodes
+ * until the point of update. This is a converse of sorts to the
+ * lazy locking technique described by Herlihy & Shavit.
+ *
+ * The main disadvantage of per-bin locks is that other update
+ * operations on other nodes in a bin list protected by the same
+ * lock can stall, for example when user equals() or mapping
+ * functions take a long time. However, statistically, under
+ * random hash codes, this is not a common problem. Ideally, the
+ * frequency of nodes in bins follows a Poisson distribution
+ * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
+ * parameter of about 0.5 on average, given the resizing threshold
+ * of 0.75, although with a large variance because of resizing
+ * granularity. Ignoring variance, the expected occurrences of
+ * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The
+ * first values are:
+ *
+ * 0: 0.60653066
+ * 1: 0.30326533
+ * 2: 0.07581633
+ * 3: 0.01263606
+ * 4: 0.00157952
+ * 5: 0.00015795
+ * 6: 0.00001316
+ * 7: 0.00000094
+ * 8: 0.00000006
+ * more: less than 1 in ten million
+ *
+ * Lock contention probability for two threads accessing distinct
+ * elements is roughly 1 / (8 * #elements) under random hashes.
+ *
+ * Actual hash code distributions encountered in practice
+ * sometimes deviate significantly from uniform randomness. This
+ * includes the case when N > (1<<30), so some keys MUST collide.
+ * Similarly for dumb or hostile usages in which multiple keys are
+ * designed to have identical hash codes. Also, although we guard
+ * against the worst effects of this (see method spread), sets of
+ * hashes may differ only in bits that do not impact their bin
+ * index for a given power-of-two mask. So we use a secondary
+ * strategy that applies when the number of nodes in a bin exceeds
+ * a threshold, and at least one of the keys implements
+ * Comparable. These TreeBins use a balanced tree to hold nodes
+ * (a specialized form of red-black trees), bounding search time
+ * to O(log N). Each search step in a TreeBin is around twice as
+ * slow as in a regular list, but given that N cannot exceed
+ * (1<<64) (before running out of addresses) this bounds search
+ * steps, lock hold times, etc, to reasonable constants (roughly
+ * 100 nodes inspected per operation worst case) so long as keys
+ * are Comparable (which is very common -- String, Long, etc).
+ * TreeBin nodes (TreeNodes) also maintain the same "next"
+ * traversal pointers as regular nodes, so can be traversed in
+ * iterators in the same way.
+ *
+ * The table is resized when occupancy exceeds a percentage
+ * threshold (nominally, 0.75, but see below). Only a single
+ * thread performs the resize (using field "sizeCtl", to arrange
+ * exclusion), but the table otherwise remains usable for reads
+ * and updates. Resizing proceeds by transferring bins, one by
+ * one, from the table to the next table. Because we are using
+ * power-of-two expansion, the elements from each bin must either
+ * stay at same index, or move with a power of two offset. We
+ * eliminate unnecessary node creation by catching cases where old
+ * nodes can be reused because their next fields won't change. On
+ * average, only about one-sixth of them need cloning when a table
+ * doubles. The nodes they replace will be garbage collectable as
+ * soon as they are no longer referenced by any reader thread that
+ * may be in the midst of concurrently traversing table. Upon
+ * transfer, the old table bin contains only a special forwarding
+ * node (with hash field "MOVED") that contains the next table as
+ * its key. On encountering a forwarding node, access and update
+ * operations restart, using the new table.
+ *
+ * Each bin transfer requires its bin lock. However, unlike other
+ * cases, a transfer can skip a bin if it fails to acquire its
+ * lock, and revisit it later (unless it is a TreeBin). Method
+ * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that
+ * have been skipped because of failure to acquire a lock, and
+ * blocks only if none are available (i.e., only very rarely).
+ * The transfer operation must also ensure that all accessible
+ * bins in both the old and new table are usable by any traversal.
+ * When there are no lock acquisition failures, this is arranged
+ * simply by proceeding from the last bin (table.length - 1) up
+ * towards the first. Upon seeing a forwarding node, traversals
+ * (see class Iter) arrange to move to the new table
+ * without revisiting nodes. However, when any node is skipped
+ * during a transfer, all earlier table bins may have become
+ * visible, so are initialized with a reverse-forwarding node back
+ * to the old table until the new ones are established. (This
+ * sometimes requires transiently locking a forwarding node, which
+ * is possible under the above encoding.) These more expensive
+ * mechanics trigger only when necessary.
+ *
+ * The traversal scheme also applies to partial traversals of
+ * ranges of bins (via an alternate Traverser constructor)
+ * to support partitioned aggregate operations. Also, read-only
+ * operations give up if ever forwarded to a null table, which
+ * provides support for shutdown-style clearing, which is also not
+ * currently implemented.
+ *
+ * Lazy table initialization minimizes footprint until first use,
+ * and also avoids resizings when the first operation is from a
+ * putAll, constructor with map argument, or deserialization.
+ * These cases attempt to override the initial capacity settings,
+ * but harmlessly fail to take effect in cases of races.
+ *
+ * The element count is maintained using a LongAdder, which avoids
+ * contention on updates but can encounter cache thrashing if read
+ * too frequently during concurrent access. To avoid reading so
+ * often, resizing is attempted either when a bin lock is
+ * contended, or upon adding to a bin already holding two or more
+ * nodes (checked before adding in the xIfAbsent methods, after
+ * adding in others). Under uniform hash distributions, the
+ * probability of this occurring at threshold is around 13%,
+ * meaning that only about 1 in 8 puts check threshold (and after
+ * resizing, many fewer do so). But this approximation has high
+ * variance for small table sizes, so we check on any collision
+ * for sizes <= 64. The bulk putAll operation further reduces
+ * contention by only committing count updates upon these size
+ * checks.
+ *
+ * Maintaining API and serialization compatibility with previous
+ * versions of this class introduces several oddities. Mainly: We
+ * leave untouched but unused constructor arguments refering to
+ * concurrencyLevel. We accept a loadFactor constructor argument,
+ * but apply it only to initial table capacity (which is the only
+ * time that we can guarantee to honor it.) We also declare an
+ * unused "Segment" class that is instantiated in minimal form
+ * only when serializing.
+ */
+
+ /* ---------------- Constants -------------- */
+
+ /**
+ * The largest possible table capacity. This value must be
+ * exactly 1<<30 to stay within Java array allocation and indexing
+ * bounds for power of two table sizes, and is further required
+ * because the top two bits of 32bit hash fields are used for
+ * control purposes.
+ */
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+
+ /**
+ * The default initial table capacity. Must be a power of 2
+ * (i.e., at least 1) and at most MAXIMUM_CAPACITY.
+ */
+ private static final int DEFAULT_CAPACITY = 16;
+
+ /**
+ * The largest possible (non-power of two) array size.
+ * Needed by toArray and related methods.
+ */
+ static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+ /**
+ * The default concurrency level for this table. Unused but
+ * defined for compatibility with previous versions of this class.
+ */
+ private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
+
+ /**
+ * The load factor for this table. Overrides of this value in
+ * constructors affect only the initial table capacity. The
+ * actual floating point value isn't normally used -- it is
+ * simpler to use expressions such as {@code n - (n >>> 2)} for
+ * the associated resizing threshold.
+ */
+ private static final float LOAD_FACTOR = 0.75f;
+
+ /**
+ * The buffer size for skipped bins during transfers. The
+ * value is arbitrary but should be large enough to avoid
+ * most locking stalls during resizes.
+ */
+ private static final int TRANSFER_BUFFER_SIZE = 32;
+
+ /**
+ * The bin count threshold for using a tree rather than list for a
+ * bin. The value reflects the approximate break-even point for
+ * using tree-based operations.
+ * Note that Doug's version defaults to 8, but when dealing with
+ * Ruby objects it is actually beneficial to avoid TreeNodes
+ * as long as possible as it usually means going into Ruby land.
+ */
+ private static final int TREE_THRESHOLD = 16;
+
+ /*
+ * Encodings for special uses of Node hash fields. See above for
+ * explanation.
+ */
+ static final int MOVED = 0x80000000; // hash field for forwarding nodes
+ static final int LOCKED = 0x40000000; // set/tested only as a bit
+ static final int WAITING = 0xc0000000; // both bits set/tested together
+ static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash
+
+ /* ---------------- Fields -------------- */
+
+ /**
+ * The array of bins. Lazily initialized upon first insertion.
+ * Size is always a power of two. Accessed directly by iterators.
+ */
+ transient volatile Node[] table;
+
+ /**
+ * The counter maintaining number of elements.
+ */
+ private transient final LongAdder counter;
+
+ /**
+ * Table initialization and resizing control. When negative, the
+ * table is being initialized or resized. Otherwise, when table is
+ * null, holds the initial table size to use upon creation, or 0
+ * for default. After initialization, holds the next element count
+ * value upon which to resize the table.
+ */
+ private transient volatile int sizeCtl;
+
+ // views
+ private transient KeySetView keySet;
+ private transient ValuesView values;
+ private transient EntrySetView entrySet;
+
+ /** For serialization compatibility. Null unless serialized; see below */
+ private Segment[] segments;
+
+ /* ---------------- Table element access -------------- */
+
+ /*
+ * Volatile access methods are used for table elements as well as
+ * elements of in-progress next table while resizing. Uses are
+ * null checked by callers, and implicitly bounds-checked, relying
+ * on the invariants that tab arrays have non-zero size, and all
+ * indices are masked with (tab.length - 1) which is never
+ * negative and always less than length. Note that, to be correct
+ * wrt arbitrary concurrency errors by users, bounds checks must
+ * operate on local variables, which accounts for some odd-looking
+ * inline assignments below.
+ */
+
+ static final Node tabAt(Node[] tab, int i) { // used by Iter
+ return (Node)UNSAFE.getObjectVolatile(tab, ((long)i< 1 ? 64 : 1;
+
+ /**
+ * Spins a while if LOCKED bit set and this node is the first
+ * of its bin, and then sets WAITING bits on hash field and
+ * blocks (once) if they are still set. It is OK for this
+ * method to return even if lock is not available upon exit,
+ * which enables these simple single-wait mechanics.
+ *
+ * The corresponding signalling operation is performed within
+ * callers: Upon detecting that WAITING has been set when
+ * unlocking lock (via a failed CAS from non-waiting LOCKED
+ * state), unlockers acquire the sync lock and perform a
+ * notifyAll.
+ *
+ * The initial sanity check on tab and bounds is not currently
+ * necessary in the only usages of this method, but enables
+ * use in other future contexts.
+ */
+ final void tryAwaitLock(Node[] tab, int i) {
+ if (tab != null && i >= 0 && i < tab.length) { // sanity check
+ int r = ThreadLocalRandom.current().nextInt(); // randomize spins
+ int spins = MAX_SPINS, h;
+ while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) {
+ if (spins >= 0) {
+ r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift
+ if (r >= 0 && --spins == 0)
+ Thread.yield(); // yield before block
+ }
+ else if (casHash(h, h | WAITING)) {
+ synchronized (this) {
+ if (tabAt(tab, i) == this &&
+ (hash & WAITING) == WAITING) {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ else
+ notifyAll(); // possibly won race vs signaller
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Unsafe mechanics for casHash
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long hashOffset;
+
+ static {
+ try {
+ UNSAFE = getUnsafe();
+ Class> k = Node.class;
+ hashOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("hash"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+ }
+
+ /* ---------------- TreeBins -------------- */
+
+ /**
+ * Nodes for use in TreeBins
+ */
+ static final class TreeNode extends Node {
+ TreeNode parent; // red-black tree links
+ TreeNode left;
+ TreeNode right;
+ TreeNode prev; // needed to unlink next upon deletion
+ boolean red;
+
+ TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) {
+ super(hash, key, val, next);
+ this.parent = parent;
+ }
+ }
+
+ /**
+ * A specialized form of red-black tree for use in bins
+ * whose size exceeds a threshold.
+ *
+ * TreeBins use a special form of comparison for search and
+ * related operations (which is the main reason we cannot use
+ * existing collections such as TreeMaps). TreeBins contain
+ * Comparable elements, but may contain others, as well as
+ * elements that are Comparable but not necessarily Comparable
+ * for the same T, so we cannot invoke compareTo among them. To
+ * handle this, the tree is ordered primarily by hash value, then
+ * by getClass().getName() order, and then by Comparator order
+ * among elements of the same class. On lookup at a node, if
+ * elements are not comparable or compare as 0, both left and
+ * right children may need to be searched in the case of tied hash
+ * values. (This corresponds to the full list search that would be
+ * necessary if all elements were non-Comparable and had tied
+ * hashes.) The red-black balancing code is updated from
+ * pre-jdk-collections
+ * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
+ * based in turn on Cormen, Leiserson, and Rivest "Introduction to
+ * Algorithms" (CLR).
+ *
+ * TreeBins also maintain a separate locking discipline than
+ * regular bins. Because they are forwarded via special MOVED
+ * nodes at bin heads (which can never change once established),
+ * we cannot use those nodes as locks. Instead, TreeBin
+ * extends AbstractQueuedSynchronizer to support a simple form of
+ * read-write lock. For update operations and table validation,
+ * the exclusive form of lock behaves in the same way as bin-head
+ * locks. However, lookups use shared read-lock mechanics to allow
+ * multiple readers in the absence of writers. Additionally,
+ * these lookups do not ever block: While the lock is not
+ * available, they proceed along the slow traversal path (via
+ * next-pointers) until the lock becomes available or the list is
+ * exhausted, whichever comes first. (These cases are not fast,
+ * but maximize aggregate expected throughput.) The AQS mechanics
+ * for doing this are straightforward. The lock state is held as
+ * AQS getState(). Read counts are negative; the write count (1)
+ * is positive. There are no signalling preferences among readers
+ * and writers. Since we don't need to export full Lock API, we
+ * just override the minimal AQS methods and use them directly.
+ */
+ static final class TreeBin extends AbstractQueuedSynchronizer {
+ private static final long serialVersionUID = 2249069246763182397L;
+ transient TreeNode root; // root of tree
+ transient TreeNode first; // head of next-pointer list
+
+ /* AQS overrides */
+ public final boolean isHeldExclusively() { return getState() > 0; }
+ public final boolean tryAcquire(int ignore) {
+ if (compareAndSetState(0, 1)) {
+ setExclusiveOwnerThread(Thread.currentThread());
+ return true;
+ }
+ return false;
+ }
+ public final boolean tryRelease(int ignore) {
+ setExclusiveOwnerThread(null);
+ setState(0);
+ return true;
+ }
+ public final int tryAcquireShared(int ignore) {
+ for (int c;;) {
+ if ((c = getState()) > 0)
+ return -1;
+ if (compareAndSetState(c, c -1))
+ return 1;
+ }
+ }
+ public final boolean tryReleaseShared(int ignore) {
+ int c;
+ do {} while (!compareAndSetState(c = getState(), c + 1));
+ return c == -1;
+ }
+
+ /** From CLR */
+ private void rotateLeft(TreeNode p) {
+ if (p != null) {
+ TreeNode r = p.right, pp, rl;
+ if ((rl = p.right = r.left) != null)
+ rl.parent = p;
+ if ((pp = r.parent = p.parent) == null)
+ root = r;
+ else if (pp.left == p)
+ pp.left = r;
+ else
+ pp.right = r;
+ r.left = p;
+ p.parent = r;
+ }
+ }
+
+ /** From CLR */
+ private void rotateRight(TreeNode p) {
+ if (p != null) {
+ TreeNode l = p.left, pp, lr;
+ if ((lr = p.left = l.right) != null)
+ lr.parent = p;
+ if ((pp = l.parent = p.parent) == null)
+ root = l;
+ else if (pp.right == p)
+ pp.right = l;
+ else
+ pp.left = l;
+ l.right = p;
+ p.parent = l;
+ }
+ }
+
+ @SuppressWarnings("unchecked") final TreeNode getTreeNode
+ (int h, Object k, TreeNode p) {
+ return getTreeNode(h, (RubyObject)k, p);
+ }
+
+ /**
+ * Returns the TreeNode (or null if not found) for the given key
+ * starting at given root.
+ */
+ @SuppressWarnings("unchecked") final TreeNode getTreeNode
+ (int h, RubyObject k, TreeNode p) {
+ RubyClass c = k.getMetaClass(); boolean kNotComparable = !k.respondsTo("<=>");
+ while (p != null) {
+ int dir, ph; RubyObject pk; RubyClass pc;
+ if ((ph = p.hash) == h) {
+ if ((pk = (RubyObject)p.key) == k || k.equals(pk))
+ return p;
+ if (c != (pc = (RubyClass)pk.getMetaClass()) ||
+ kNotComparable ||
+ (dir = rubyCompare(k, pk)) == 0) {
+ dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName());
+ if (dir == 0) { // if still stuck, need to check both sides
+ TreeNode r = null, pl, pr;
+ // try to recurse on the right
+ if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null)
+ return r;
+ // try to continue iterating on the left side
+ else if ((pl = p.left) != null && h <= pl.hash)
+ dir = -1;
+ else // no matching node found
+ return null;
+ }
+ }
+ }
+ else
+ dir = (h < ph) ? -1 : 1;
+ p = (dir > 0) ? p.right : p.left;
+ }
+ return null;
+ }
+
+ int rubyCompare(RubyObject l, RubyObject r) {
+ ThreadContext context = l.getMetaClass().getRuntime().getCurrentContext();
+ IRubyObject result;
+ try {
+ result = l.callMethod(context, "<=>", r);
+ } catch (RaiseException e) {
+ // handle objects "lying" about responding to <=>, ie: an Array containing non-comparable keys
+ if (context.runtime.getNoMethodError().isInstance(e.getException())) {
+ return 0;
+ }
+ throw e;
+ }
+
+ return result.isNil() ? 0 : RubyNumeric.num2int(result.convertToInteger());
+ }
+
+ /**
+ * Wrapper for getTreeNode used by CHM.get. Tries to obtain
+ * read-lock to call getTreeNode, but during failure to get
+ * lock, searches along next links.
+ */
+ final Object getValue(int h, Object k) {
+ Node r = null;
+ int c = getState(); // Must read lock state first
+ for (Node e = first; e != null; e = e.next) {
+ if (c <= 0 && compareAndSetState(c, c - 1)) {
+ try {
+ r = getTreeNode(h, k, root);
+ } finally {
+ releaseShared(0);
+ }
+ break;
+ }
+ else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) {
+ r = e;
+ break;
+ }
+ else
+ c = getState();
+ }
+ return r == null ? null : r.val;
+ }
+
+ @SuppressWarnings("unchecked") final TreeNode putTreeNode
+ (int h, Object k, Object v) {
+ return putTreeNode(h, (RubyObject)k, v);
+ }
+
+ /**
+ * Finds or adds a node.
+ * @return null if added
+ */
+ @SuppressWarnings("unchecked") final TreeNode putTreeNode
+ (int h, RubyObject k, Object v) {
+ RubyClass c = k.getMetaClass();
+ boolean kNotComparable = !k.respondsTo("<=>");
+ TreeNode pp = root, p = null;
+ int dir = 0;
+ while (pp != null) { // find existing node or leaf to insert at
+ int ph; RubyObject pk; RubyClass pc;
+ p = pp;
+ if ((ph = p.hash) == h) {
+ if ((pk = (RubyObject)p.key) == k || k.equals(pk))
+ return p;
+ if (c != (pc = pk.getMetaClass()) ||
+ kNotComparable ||
+ (dir = rubyCompare(k, pk)) == 0) {
+ dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName());
+ if (dir == 0) { // if still stuck, need to check both sides
+ TreeNode r = null, pr;
+ // try to recurse on the right
+ if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null)
+ return r;
+ else // continue descending down the left subtree
+ dir = -1;
+ }
+ }
+ }
+ else
+ dir = (h < ph) ? -1 : 1;
+ pp = (dir > 0) ? p.right : p.left;
+ }
+
+ TreeNode f = first;
+ TreeNode x = first = new TreeNode(h, (Object)k, v, f, p);
+ if (p == null)
+ root = x;
+ else { // attach and rebalance; adapted from CLR
+ TreeNode xp, xpp;
+ if (f != null)
+ f.prev = x;
+ if (dir <= 0)
+ p.left = x;
+ else
+ p.right = x;
+ x.red = true;
+ while (x != null && (xp = x.parent) != null && xp.red &&
+ (xpp = xp.parent) != null) {
+ TreeNode xppl = xpp.left;
+ if (xp == xppl) {
+ TreeNode y = xpp.right;
+ if (y != null && y.red) {
+ y.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.right) {
+ rotateLeft(x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ rotateRight(xpp);
+ }
+ }
+ }
+ }
+ else {
+ TreeNode y = xppl;
+ if (y != null && y.red) {
+ y.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.left) {
+ rotateRight(x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ rotateLeft(xpp);
+ }
+ }
+ }
+ }
+ }
+ TreeNode r = root;
+ if (r != null && r.red)
+ r.red = false;
+ }
+ return null;
+ }
+
+ /**
+ * Removes the given node, that must be present before this
+ * call. This is messier than typical red-black deletion code
+ * because we cannot swap the contents of an interior node
+ * with a leaf successor that is pinned by "next" pointers
+ * that are accessible independently of lock. So instead we
+ * swap the tree linkages.
+ */
+ final void deleteTreeNode(TreeNode p) {
+ TreeNode next = (TreeNode)p.next; // unlink traversal pointers
+ TreeNode pred = p.prev;
+ if (pred == null)
+ first = next;
+ else
+ pred.next = next;
+ if (next != null)
+ next.prev = pred;
+ TreeNode replacement;
+ TreeNode pl = p.left;
+ TreeNode pr = p.right;
+ if (pl != null && pr != null) {
+ TreeNode s = pr, sl;
+ while ((sl = s.left) != null) // find successor
+ s = sl;
+ boolean c = s.red; s.red = p.red; p.red = c; // swap colors
+ TreeNode sr = s.right;
+ TreeNode pp = p.parent;
+ if (s == pr) { // p was s's direct parent
+ p.parent = s;
+ s.right = p;
+ }
+ else {
+ TreeNode sp = s.parent;
+ if ((p.parent = sp) != null) {
+ if (s == sp.left)
+ sp.left = p;
+ else
+ sp.right = p;
+ }
+ if ((s.right = pr) != null)
+ pr.parent = s;
+ }
+ p.left = null;
+ if ((p.right = sr) != null)
+ sr.parent = p;
+ if ((s.left = pl) != null)
+ pl.parent = s;
+ if ((s.parent = pp) == null)
+ root = s;
+ else if (p == pp.left)
+ pp.left = s;
+ else
+ pp.right = s;
+ replacement = sr;
+ }
+ else
+ replacement = (pl != null) ? pl : pr;
+ TreeNode pp = p.parent;
+ if (replacement == null) {
+ if (pp == null) {
+ root = null;
+ return;
+ }
+ replacement = p;
+ }
+ else {
+ replacement.parent = pp;
+ if (pp == null)
+ root = replacement;
+ else if (p == pp.left)
+ pp.left = replacement;
+ else
+ pp.right = replacement;
+ p.left = p.right = p.parent = null;
+ }
+ if (!p.red) { // rebalance, from CLR
+ TreeNode x = replacement;
+ while (x != null) {
+ TreeNode xp, xpl;
+ if (x.red || (xp = x.parent) == null) {
+ x.red = false;
+ break;
+ }
+ if (x == (xpl = xp.left)) {
+ TreeNode sib = xp.right;
+ if (sib != null && sib.red) {
+ sib.red = false;
+ xp.red = true;
+ rotateLeft(xp);
+ sib = (xp = x.parent) == null ? null : xp.right;
+ }
+ if (sib == null)
+ x = xp;
+ else {
+ TreeNode sl = sib.left, sr = sib.right;
+ if ((sr == null || !sr.red) &&
+ (sl == null || !sl.red)) {
+ sib.red = true;
+ x = xp;
+ }
+ else {
+ if (sr == null || !sr.red) {
+ if (sl != null)
+ sl.red = false;
+ sib.red = true;
+ rotateRight(sib);
+ sib = (xp = x.parent) == null ? null : xp.right;
+ }
+ if (sib != null) {
+ sib.red = (xp == null) ? false : xp.red;
+ if ((sr = sib.right) != null)
+ sr.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ rotateLeft(xp);
+ }
+ x = root;
+ }
+ }
+ }
+ else { // symmetric
+ TreeNode sib = xpl;
+ if (sib != null && sib.red) {
+ sib.red = false;
+ xp.red = true;
+ rotateRight(xp);
+ sib = (xp = x.parent) == null ? null : xp.left;
+ }
+ if (sib == null)
+ x = xp;
+ else {
+ TreeNode sl = sib.left, sr = sib.right;
+ if ((sl == null || !sl.red) &&
+ (sr == null || !sr.red)) {
+ sib.red = true;
+ x = xp;
+ }
+ else {
+ if (sl == null || !sl.red) {
+ if (sr != null)
+ sr.red = false;
+ sib.red = true;
+ rotateLeft(sib);
+ sib = (xp = x.parent) == null ? null : xp.left;
+ }
+ if (sib != null) {
+ sib.red = (xp == null) ? false : xp.red;
+ if ((sl = sib.left) != null)
+ sl.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ rotateRight(xp);
+ }
+ x = root;
+ }
+ }
+ }
+ }
+ }
+ if (p == replacement && (pp = p.parent) != null) {
+ if (p == pp.left) // detach pointers
+ pp.left = null;
+ else if (p == pp.right)
+ pp.right = null;
+ p.parent = null;
+ }
+ }
+ }
+
+ /* ---------------- Collision reduction methods -------------- */
+
+ /**
+ * Spreads higher bits to lower, and also forces top 2 bits to 0.
+ * Because the table uses power-of-two masking, sets of hashes
+ * that vary only in bits above the current mask will always
+ * collide. (Among known examples are sets of Float keys holding
+ * consecutive whole numbers in small tables.) To counter this,
+ * we apply a transform that spreads the impact of higher bits
+ * downward. There is a tradeoff between speed, utility, and
+ * quality of bit-spreading. Because many common sets of hashes
+ * are already reasonably distributed across bits (so don't benefit
+ * from spreading), and because we use trees to handle large sets
+ * of collisions in bins, we don't need excessively high quality.
+ */
+ private static final int spread(int h) {
+ h ^= (h >>> 18) ^ (h >>> 12);
+ return (h ^ (h >>> 10)) & HASH_BITS;
+ }
+
+ /**
+ * Replaces a list bin with a tree bin. Call only when locked.
+ * Fails to replace if the given key is non-comparable or table
+ * is, or needs, resizing.
+ */
+ private final void replaceWithTreeBin(Node[] tab, int index, Object key) {
+ if ((key instanceof Comparable) &&
+ (tab.length >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) {
+ TreeBin t = new TreeBin();
+ for (Node e = tabAt(tab, index); e != null; e = e.next)
+ t.putTreeNode(e.hash & HASH_BITS, e.key, e.val);
+ setTabAt(tab, index, new Node(MOVED, t, null, null));
+ }
+ }
+
+ /* ---------------- Internal access and update methods -------------- */
+
+ /** Implementation for get and containsKey */
+ private final Object internalGet(Object k) {
+ int h = spread(k.hashCode());
+ retry: for (Node[] tab = table; tab != null;) {
+ Node e, p; Object ek, ev; int eh; // locals to read fields once
+ for (e = tabAt(tab, (tab.length - 1) & h); e != null; e = e.next) {
+ if ((eh = e.hash) == MOVED) {
+ if ((ek = e.key) instanceof TreeBin) // search TreeBin
+ return ((TreeBin)ek).getValue(h, k);
+ else { // restart with new table
+ tab = (Node[])ek;
+ continue retry;
+ }
+ }
+ else if ((eh & HASH_BITS) == h && (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek)))
+ return ev;
+ }
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Implementation for the four public remove/replace methods:
+ * Replaces node value with v, conditional upon match of cv if
+ * non-null. If resulting value is null, delete.
+ */
+ private final Object internalReplace(Object k, Object v, Object cv) {
+ int h = spread(k.hashCode());
+ Object oldVal = null;
+ for (Node[] tab = table;;) {
+ Node f; int i, fh; Object fk;
+ if (tab == null ||
+ (f = tabAt(tab, i = (tab.length - 1) & h)) == null)
+ break;
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ boolean validated = false;
+ boolean deleted = false;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ if (p != null) {
+ Object pv = p.val;
+ if (cv == null || cv == pv || cv.equals(pv)) {
+ oldVal = pv;
+ if ((p.val = v) == null) {
+ deleted = true;
+ t.deleteTreeNode(p);
+ }
+ }
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (validated) {
+ if (deleted)
+ counter.add(-1L);
+ break;
+ }
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & HASH_BITS) != h && f.next == null) // precheck
+ break; // rules out possible existence
+ else if ((fh & LOCKED) != 0) {
+ checkForResize(); // try resizing if can't get lock
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ boolean validated = false;
+ boolean deleted = false;
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ for (Node e = f, pred = null;;) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ ((ev = e.val) != null) &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ if (cv == null || cv == ev || cv.equals(ev)) {
+ oldVal = ev;
+ if ((e.val = v) == null) {
+ deleted = true;
+ Node en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null)
+ break;
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (validated) {
+ if (deleted)
+ counter.add(-1L);
+ break;
+ }
+ }
+ }
+ return oldVal;
+ }
+
+ /*
+ * Internal versions of the six insertion methods, each a
+ * little more complicated than the last. All have
+ * the same basic structure as the first (internalPut):
+ * 1. If table uninitialized, create
+ * 2. If bin empty, try to CAS new node
+ * 3. If bin stale, use new table
+ * 4. if bin converted to TreeBin, validate and relay to TreeBin methods
+ * 5. Lock and validate; if valid, scan and add or update
+ *
+ * The others interweave other checks and/or alternative actions:
+ * * Plain put checks for and performs resize after insertion.
+ * * putIfAbsent prescans for mapping without lock (and fails to add
+ * if present), which also makes pre-emptive resize checks worthwhile.
+ * * computeIfAbsent extends form used in putIfAbsent with additional
+ * mechanics to deal with, calls, potential exceptions and null
+ * returns from function call.
+ * * compute uses the same function-call mechanics, but without
+ * the prescans
+ * * merge acts as putIfAbsent in the absent case, but invokes the
+ * update function if present
+ * * putAll attempts to pre-allocate enough table space
+ * and more lazily performs count updates and checks.
+ *
+ * Someday when details settle down a bit more, it might be worth
+ * some factoring to reduce sprawl.
+ */
+
+ /** Implementation for put */
+ private final Object internalPut(Object k, Object v) {
+ int h = spread(k.hashCode());
+ int count = 0;
+ for (Node[] tab = table;;) {
+ int i; Node f; int fh; Object fk;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
+ if (casTabAt(tab, i, null, new Node(h, k, v, null)))
+ break; // no lock when adding to empty bin
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ Object oldVal = null;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 2;
+ TreeNode p = t.putTreeNode(h, k, v);
+ if (p != null) {
+ oldVal = p.val;
+ p.val = v;
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0) {
+ if (oldVal != null)
+ return oldVal;
+ break;
+ }
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ Object oldVal = null;
+ try { // needed in case equals() throws
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ oldVal = ev;
+ e.val = v;
+ break;
+ }
+ Node last = e;
+ if ((e = e.next) == null) {
+ last.next = new Node(h, k, v, null);
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ break;
+ }
+ }
+ }
+ } finally { // unlock and signal if needed
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (oldVal != null)
+ return oldVal;
+ if (tab.length <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ counter.add(1L);
+ if (count > 1)
+ checkForResize();
+ return null;
+ }
+
+ /** Implementation for putIfAbsent */
+ private final Object internalPutIfAbsent(Object k, Object v) {
+ int h = spread(k.hashCode());
+ int count = 0;
+ for (Node[] tab = table;;) {
+ int i; Node f; int fh; Object fk, fv;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
+ if (casTabAt(tab, i, null, new Node(h, k, v, null)))
+ break;
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ Object oldVal = null;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 2;
+ TreeNode p = t.putTreeNode(h, k, v);
+ if (p != null)
+ oldVal = p.val;
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0) {
+ if (oldVal != null)
+ return oldVal;
+ break;
+ }
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & HASH_BITS) == h && (fv = f.val) != null &&
+ ((fk = f.key) == k || k.equals(fk)))
+ return fv;
+ else {
+ Node g = f.next;
+ if (g != null) { // at least 2 nodes -- search and maybe resize
+ for (Node e = g;;) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h && (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek)))
+ return ev;
+ if ((e = e.next) == null) {
+ checkForResize();
+ break;
+ }
+ }
+ }
+ if (((fh = f.hash) & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) {
+ Object oldVal = null;
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ oldVal = ev;
+ break;
+ }
+ Node last = e;
+ if ((e = e.next) == null) {
+ last.next = new Node(h, k, v, null);
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (oldVal != null)
+ return oldVal;
+ if (tab.length <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ }
+ counter.add(1L);
+ if (count > 1)
+ checkForResize();
+ return null;
+ }
+
+ /** Implementation for computeIfAbsent */
+ private final Object internalComputeIfAbsent(K k,
+ Fun super K, ?> mf) {
+ int h = spread(k.hashCode());
+ Object val = null;
+ int count = 0;
+ for (Node[] tab = table;;) {
+ Node f; int i, fh; Object fk, fv;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
+ Node node = new Node(fh = h | LOCKED, k, null, null);
+ if (casTabAt(tab, i, null, node)) {
+ count = 1;
+ try {
+ if ((val = mf.apply(k)) != null)
+ node.val = val;
+ } finally {
+ if (val == null)
+ setTabAt(tab, i, null);
+ if (!node.casHash(fh, h)) {
+ node.hash = h;
+ synchronized (node) { node.notifyAll(); };
+ }
+ }
+ }
+ if (count != 0)
+ break;
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ boolean added = false;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ if (p != null)
+ val = p.val;
+ else if ((val = mf.apply(k)) != null) {
+ added = true;
+ count = 2;
+ t.putTreeNode(h, k, val);
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0) {
+ if (!added)
+ return val;
+ break;
+ }
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & HASH_BITS) == h && (fv = f.val) != null &&
+ ((fk = f.key) == k || k.equals(fk)))
+ return fv;
+ else {
+ Node g = f.next;
+ if (g != null) {
+ for (Node e = g;;) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h && (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek)))
+ return ev;
+ if ((e = e.next) == null) {
+ checkForResize();
+ break;
+ }
+ }
+ }
+ if (((fh = f.hash) & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) {
+ boolean added = false;
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ val = ev;
+ break;
+ }
+ Node last = e;
+ if ((e = e.next) == null) {
+ if ((val = mf.apply(k)) != null) {
+ added = true;
+ last.next = new Node(h, k, val, null);
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ }
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (!added)
+ return val;
+ if (tab.length <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ }
+ if (val != null) {
+ counter.add(1L);
+ if (count > 1)
+ checkForResize();
+ }
+ return val;
+ }
+
+ /** Implementation for compute */
+ @SuppressWarnings("unchecked") private final Object internalCompute
+ (K k, boolean onlyIfPresent, BiFun super K, ? super V, ? extends V> mf) {
+ int h = spread(k.hashCode());
+ Object val = null;
+ int delta = 0;
+ int count = 0;
+ for (Node[] tab = table;;) {
+ Node f; int i, fh; Object fk;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
+ if (onlyIfPresent)
+ break;
+ Node node = new Node(fh = h | LOCKED, k, null, null);
+ if (casTabAt(tab, i, null, node)) {
+ try {
+ count = 1;
+ if ((val = mf.apply(k, null)) != null) {
+ node.val = val;
+ delta = 1;
+ }
+ } finally {
+ if (delta == 0)
+ setTabAt(tab, i, null);
+ if (!node.casHash(fh, h)) {
+ node.hash = h;
+ synchronized (node) { node.notifyAll(); };
+ }
+ }
+ }
+ if (count != 0)
+ break;
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ Object pv;
+ if (p == null) {
+ if (onlyIfPresent)
+ break;
+ pv = null;
+ } else
+ pv = p.val;
+ if ((val = mf.apply(k, (V)pv)) != null) {
+ if (p != null)
+ p.val = val;
+ else {
+ count = 2;
+ delta = 1;
+ t.putTreeNode(h, k, val);
+ }
+ }
+ else if (p != null) {
+ delta = -1;
+ t.deleteTreeNode(p);
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0)
+ break;
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f, pred = null;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ val = mf.apply(k, (V)ev);
+ if (val != null)
+ e.val = val;
+ else {
+ delta = -1;
+ Node en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null) {
+ if (!onlyIfPresent && (val = mf.apply(k, null)) != null) {
+ pred.next = new Node(h, k, val, null);
+ delta = 1;
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ }
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (tab.length <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ if (delta != 0) {
+ counter.add((long)delta);
+ if (count > 1)
+ checkForResize();
+ }
+ return val;
+ }
+
+ /** Implementation for merge */
+ @SuppressWarnings("unchecked") private final Object internalMerge
+ (K k, V v, BiFun super V, ? super V, ? extends V> mf) {
+ int h = spread(k.hashCode());
+ Object val = null;
+ int delta = 0;
+ int count = 0;
+ for (Node[] tab = table;;) {
+ int i; Node f; int fh; Object fk, fv;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
+ if (casTabAt(tab, i, null, new Node(h, k, v, null))) {
+ delta = 1;
+ val = v;
+ break;
+ }
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ val = (p == null) ? v : mf.apply((V)p.val, v);
+ if (val != null) {
+ if (p != null)
+ p.val = val;
+ else {
+ count = 2;
+ delta = 1;
+ t.putTreeNode(h, k, val);
+ }
+ }
+ else if (p != null) {
+ delta = -1;
+ t.deleteTreeNode(p);
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0)
+ break;
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f, pred = null;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ val = mf.apply((V)ev, v);
+ if (val != null)
+ e.val = val;
+ else {
+ delta = -1;
+ Node en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null) {
+ val = v;
+ pred.next = new Node(h, k, val, null);
+ delta = 1;
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (tab.length <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ if (delta != 0) {
+ counter.add((long)delta);
+ if (count > 1)
+ checkForResize();
+ }
+ return val;
+ }
+
+ /** Implementation for putAll */
+ private final void internalPutAll(Map, ?> m) {
+ tryPresize(m.size());
+ long delta = 0L; // number of uncommitted additions
+ boolean npe = false; // to throw exception on exit for nulls
+ try { // to clean up counts on other exceptions
+ for (Map.Entry, ?> entry : m.entrySet()) {
+ Object k, v;
+ if (entry == null || (k = entry.getKey()) == null ||
+ (v = entry.getValue()) == null) {
+ npe = true;
+ break;
+ }
+ int h = spread(k.hashCode());
+ for (Node[] tab = table;;) {
+ int i; Node f; int fh; Object fk;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null){
+ if (casTabAt(tab, i, null, new Node(h, k, v, null))) {
+ ++delta;
+ break;
+ }
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ boolean validated = false;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ if (p != null)
+ p.val = v;
+ else {
+ t.putTreeNode(h, k, v);
+ ++delta;
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (validated)
+ break;
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ counter.add(delta);
+ delta = 0L;
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ int count = 0;
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ e.val = v;
+ break;
+ }
+ Node last = e;
+ if ((e = e.next) == null) {
+ ++delta;
+ last.next = new Node(h, k, v, null);
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (count > 1) {
+ counter.add(delta);
+ delta = 0L;
+ checkForResize();
+ }
+ break;
+ }
+ }
+ }
+ }
+ } finally {
+ if (delta != 0)
+ counter.add(delta);
+ }
+ if (npe)
+ throw new NullPointerException();
+ }
+
+ /* ---------------- Table Initialization and Resizing -------------- */
+
+ /**
+ * Returns a power of two table size for the given desired capacity.
+ * See Hackers Delight, sec 3.2
+ */
+ private static final int tableSizeFor(int c) {
+ int n = c - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+
+ /**
+ * Initializes table, using the size recorded in sizeCtl.
+ */
+ private final Node[] initTable() {
+ Node[] tab; int sc;
+ while ((tab = table) == null) {
+ if ((sc = sizeCtl) < 0)
+ Thread.yield(); // lost initialization race; just spin
+ else if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) {
+ try {
+ if ((tab = table) == null) {
+ int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
+ tab = table = new Node[n];
+ sc = n - (n >>> 2);
+ }
+ } finally {
+ sizeCtl = sc;
+ }
+ break;
+ }
+ }
+ return tab;
+ }
+
+ /**
+ * If table is too small and not already resizing, creates next
+ * table and transfers bins. Rechecks occupancy after a transfer
+ * to see if another resize is already needed because resizings
+ * are lagging additions.
+ */
+ private final void checkForResize() {
+ Node[] tab; int n, sc;
+ while ((tab = table) != null &&
+ (n = tab.length) < MAXIMUM_CAPACITY &&
+ (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc &&
+ UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) {
+ try {
+ if (tab == table) {
+ table = rebuild(tab);
+ sc = (n << 1) - (n >>> 1);
+ }
+ } finally {
+ sizeCtl = sc;
+ }
+ }
+ }
+
+ /**
+ * Tries to presize table to accommodate the given number of elements.
+ *
+ * @param size number of elements (doesn't need to be perfectly accurate)
+ */
+ private final void tryPresize(int size) {
+ int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
+ tableSizeFor(size + (size >>> 1) + 1);
+ int sc;
+ while ((sc = sizeCtl) >= 0) {
+ Node[] tab = table; int n;
+ if (tab == null || (n = tab.length) == 0) {
+ n = (sc > c) ? sc : c;
+ if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) {
+ try {
+ if (table == tab) {
+ table = new Node[n];
+ sc = n - (n >>> 2);
+ }
+ } finally {
+ sizeCtl = sc;
+ }
+ }
+ }
+ else if (c <= sc || n >= MAXIMUM_CAPACITY)
+ break;
+ else if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) {
+ try {
+ if (table == tab) {
+ table = rebuild(tab);
+ sc = (n << 1) - (n >>> 1);
+ }
+ } finally {
+ sizeCtl = sc;
+ }
+ }
+ }
+ }
+
+ /*
+ * Moves and/or copies the nodes in each bin to new table. See
+ * above for explanation.
+ *
+ * @return the new table
+ */
+ private static final Node[] rebuild(Node[] tab) {
+ int n = tab.length;
+ Node[] nextTab = new Node[n << 1];
+ Node fwd = new Node(MOVED, nextTab, null, null);
+ int[] buffer = null; // holds bins to revisit; null until needed
+ Node rev = null; // reverse forwarder; null until needed
+ int nbuffered = 0; // the number of bins in buffer list
+ int bufferIndex = 0; // buffer index of current buffered bin
+ int bin = n - 1; // current non-buffered bin or -1 if none
+
+ for (int i = bin;;) { // start upwards sweep
+ int fh; Node f;
+ if ((f = tabAt(tab, i)) == null) {
+ if (bin >= 0) { // Unbuffered; no lock needed (or available)
+ if (!casTabAt(tab, i, f, fwd))
+ continue;
+ }
+ else { // transiently use a locked forwarding node
+ Node g = new Node(MOVED|LOCKED, nextTab, null, null);
+ if (!casTabAt(tab, i, f, g))
+ continue;
+ setTabAt(nextTab, i, null);
+ setTabAt(nextTab, i + n, null);
+ setTabAt(tab, i, fwd);
+ if (!g.casHash(MOVED|LOCKED, MOVED)) {
+ g.hash = MOVED;
+ synchronized (g) { g.notifyAll(); }
+ }
+ }
+ }
+ else if ((fh = f.hash) == MOVED) {
+ Object fk = f.key;
+ if (fk instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ boolean validated = false;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ splitTreeBin(nextTab, i, t);
+ setTabAt(tab, i, fwd);
+ }
+ } finally {
+ t.release(0);
+ }
+ if (!validated)
+ continue;
+ }
+ }
+ else if ((fh & LOCKED) == 0 && f.casHash(fh, fh|LOCKED)) {
+ boolean validated = false;
+ try { // split to lo and hi lists; copying as needed
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ splitBin(nextTab, i, f);
+ setTabAt(tab, i, fwd);
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (!validated)
+ continue;
+ }
+ else {
+ if (buffer == null) // initialize buffer for revisits
+ buffer = new int[TRANSFER_BUFFER_SIZE];
+ if (bin < 0 && bufferIndex > 0) {
+ int j = buffer[--bufferIndex];
+ buffer[bufferIndex] = i;
+ i = j; // swap with another bin
+ continue;
+ }
+ if (bin < 0 || nbuffered >= TRANSFER_BUFFER_SIZE) {
+ f.tryAwaitLock(tab, i);
+ continue; // no other options -- block
+ }
+ if (rev == null) // initialize reverse-forwarder
+ rev = new Node(MOVED, tab, null, null);
+ if (tabAt(tab, i) != f || (f.hash & LOCKED) == 0)
+ continue; // recheck before adding to list
+ buffer[nbuffered++] = i;
+ setTabAt(nextTab, i, rev); // install place-holders
+ setTabAt(nextTab, i + n, rev);
+ }
+
+ if (bin > 0)
+ i = --bin;
+ else if (buffer != null && nbuffered > 0) {
+ bin = -1;
+ i = buffer[bufferIndex = --nbuffered];
+ }
+ else
+ return nextTab;
+ }
+ }
+
+ /**
+ * Splits a normal bin with list headed by e into lo and hi parts;
+ * installs in given table.
+ */
+ private static void splitBin(Node[] nextTab, int i, Node e) {
+ int bit = nextTab.length >>> 1; // bit to split on
+ int runBit = e.hash & bit;
+ Node lastRun = e, lo = null, hi = null;
+ for (Node p = e.next; p != null; p = p.next) {
+ int b = p.hash & bit;
+ if (b != runBit) {
+ runBit = b;
+ lastRun = p;
+ }
+ }
+ if (runBit == 0)
+ lo = lastRun;
+ else
+ hi = lastRun;
+ for (Node p = e; p != lastRun; p = p.next) {
+ int ph = p.hash & HASH_BITS;
+ Object pk = p.key, pv = p.val;
+ if ((ph & bit) == 0)
+ lo = new Node(ph, pk, pv, lo);
+ else
+ hi = new Node(ph, pk, pv, hi);
+ }
+ setTabAt(nextTab, i, lo);
+ setTabAt(nextTab, i + bit, hi);
+ }
+
+ /**
+ * Splits a tree bin into lo and hi parts; installs in given table.
+ */
+ private static void splitTreeBin(Node[] nextTab, int i, TreeBin t) {
+ int bit = nextTab.length >>> 1;
+ TreeBin lt = new TreeBin();
+ TreeBin ht = new TreeBin();
+ int lc = 0, hc = 0;
+ for (Node e = t.first; e != null; e = e.next) {
+ int h = e.hash & HASH_BITS;
+ Object k = e.key, v = e.val;
+ if ((h & bit) == 0) {
+ ++lc;
+ lt.putTreeNode(h, k, v);
+ }
+ else {
+ ++hc;
+ ht.putTreeNode(h, k, v);
+ }
+ }
+ Node ln, hn; // throw away trees if too small
+ if (lc <= (TREE_THRESHOLD >>> 1)) {
+ ln = null;
+ for (Node p = lt.first; p != null; p = p.next)
+ ln = new Node(p.hash, p.key, p.val, ln);
+ }
+ else
+ ln = new Node(MOVED, lt, null, null);
+ setTabAt(nextTab, i, ln);
+ if (hc <= (TREE_THRESHOLD >>> 1)) {
+ hn = null;
+ for (Node p = ht.first; p != null; p = p.next)
+ hn = new Node(p.hash, p.key, p.val, hn);
+ }
+ else
+ hn = new Node(MOVED, ht, null, null);
+ setTabAt(nextTab, i + bit, hn);
+ }
+
+ /**
+ * Implementation for clear. Steps through each bin, removing all
+ * nodes.
+ */
+ private final void internalClear() {
+ long delta = 0L; // negative number of deletions
+ int i = 0;
+ Node[] tab = table;
+ while (tab != null && i < tab.length) {
+ int fh; Object fk;
+ Node f = tabAt(tab, i);
+ if (f == null)
+ ++i;
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ for (Node p = t.first; p != null; p = p.next) {
+ if (p.val != null) { // (currently always true)
+ p.val = null;
+ --delta;
+ }
+ }
+ t.first = null;
+ t.root = null;
+ ++i;
+ }
+ } finally {
+ t.release(0);
+ }
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ counter.add(delta); // opportunistically update count
+ delta = 0L;
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ try {
+ if (tabAt(tab, i) == f) {
+ for (Node e = f; e != null; e = e.next) {
+ if (e.val != null) { // (currently always true)
+ e.val = null;
+ --delta;
+ }
+ }
+ setTabAt(tab, i, null);
+ ++i;
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ }
+ }
+ if (delta != 0)
+ counter.add(delta);
+ }
+
+ /* ----------------Table Traversal -------------- */
+
+ /**
+ * Encapsulates traversal for methods such as containsValue; also
+ * serves as a base class for other iterators and bulk tasks.
+ *
+ * At each step, the iterator snapshots the key ("nextKey") and
+ * value ("nextVal") of a valid node (i.e., one that, at point of
+ * snapshot, has a non-null user value). Because val fields can
+ * change (including to null, indicating deletion), field nextVal
+ * might not be accurate at point of use, but still maintains the
+ * weak consistency property of holding a value that was once
+ * valid. To support iterator.remove, the nextKey field is not
+ * updated (nulled out) when the iterator cannot advance.
+ *
+ * Internal traversals directly access these fields, as in:
+ * {@code while (it.advance() != null) { process(it.nextKey); }}
+ *
+ * Exported iterators must track whether the iterator has advanced
+ * (in hasNext vs next) (by setting/checking/nulling field
+ * nextVal), and then extract key, value, or key-value pairs as
+ * return values of next().
+ *
+ * The iterator visits once each still-valid node that was
+ * reachable upon iterator construction. It might miss some that
+ * were added to a bin after the bin was visited, which is OK wrt
+ * consistency guarantees. Maintaining this property in the face
+ * of possible ongoing resizes requires a fair amount of
+ * bookkeeping state that is difficult to optimize away amidst
+ * volatile accesses. Even so, traversal maintains reasonable
+ * throughput.
+ *
+ * Normally, iteration proceeds bin-by-bin traversing lists.
+ * However, if the table has been resized, then all future steps
+ * must traverse both the bin at the current index as well as at
+ * (index + baseSize); and so on for further resizings. To
+ * paranoically cope with potential sharing by users of iterators
+ * across threads, iteration terminates if a bounds checks fails
+ * for a table read.
+ *
+ * This class extends ForkJoinTask to streamline parallel
+ * iteration in bulk operations (see BulkTask). This adds only an
+ * int of space overhead, which is close enough to negligible in
+ * cases where it is not needed to not worry about it. Because
+ * ForkJoinTask is Serializable, but iterators need not be, we
+ * need to add warning suppressions.
+ */
+ @SuppressWarnings("serial") static class Traverser {
+ final ConcurrentHashMapV8 map;
+ Node next; // the next entry to use
+ K nextKey; // cached key field of next
+ V nextVal; // cached val field of next
+ Node[] tab; // current table; updated if resized
+ int index; // index of bin to use next
+ int baseIndex; // current index of initial table
+ int baseLimit; // index bound for initial table
+ int baseSize; // initial table size
+
+ /** Creates iterator for all entries in the table. */
+ Traverser(ConcurrentHashMapV8 map) {
+ this.map = map;
+ }
+
+ /** Creates iterator for split() methods */
+ Traverser(Traverser it) {
+ ConcurrentHashMapV8 m; Node[] t;
+ if ((m = this.map = it.map) == null)
+ t = null;
+ else if ((t = it.tab) == null && // force parent tab initialization
+ (t = it.tab = m.table) != null)
+ it.baseLimit = it.baseSize = t.length;
+ this.tab = t;
+ this.baseSize = it.baseSize;
+ it.baseLimit = this.index = this.baseIndex =
+ ((this.baseLimit = it.baseLimit) + it.baseIndex + 1) >>> 1;
+ }
+
+ /**
+ * Advances next; returns nextVal or null if terminated.
+ * See above for explanation.
+ */
+ final V advance() {
+ Node e = next;
+ V ev = null;
+ outer: do {
+ if (e != null) // advance past used/skipped node
+ e = e.next;
+ while (e == null) { // get to next non-null bin
+ ConcurrentHashMapV8 m;
+ Node[] t; int b, i, n; Object ek; // checks must use locals
+ if ((t = tab) != null)
+ n = t.length;
+ else if ((m = map) != null && (t = tab = m.table) != null)
+ n = baseLimit = baseSize = t.length;
+ else
+ break outer;
+ if ((b = baseIndex) >= baseLimit ||
+ (i = index) < 0 || i >= n)
+ break outer;
+ if ((e = tabAt(t, i)) != null && e.hash == MOVED) {
+ if ((ek = e.key) instanceof TreeBin)
+ e = ((TreeBin)ek).first;
+ else {
+ tab = (Node[])ek;
+ continue; // restarts due to null val
+ }
+ } // visit upper slots if present
+ index = (i += baseSize) < n ? i : (baseIndex = b + 1);
+ }
+ nextKey = (K) e.key;
+ } while ((ev = (V) e.val) == null); // skip deleted or special nodes
+ next = e;
+ return nextVal = ev;
+ }
+
+ public final void remove() {
+ Object k = nextKey;
+ if (k == null && (advance() == null || (k = nextKey) == null))
+ throw new IllegalStateException();
+ map.internalReplace(k, null, null);
+ }
+
+ public final boolean hasNext() {
+ return nextVal != null || advance() != null;
+ }
+
+ public final boolean hasMoreElements() { return hasNext(); }
+ public final void setRawResult(Object x) { }
+ public R getRawResult() { return null; }
+ public boolean exec() { return true; }
+ }
+
+ /* ---------------- Public operations -------------- */
+
+ /**
+ * Creates a new, empty map with the default initial table size (16).
+ */
+ public ConcurrentHashMapV8() {
+ this.counter = new LongAdder();
+ }
+
+ /**
+ * Creates a new, empty map with an initial table size
+ * accommodating the specified number of elements without the need
+ * to dynamically resize.
+ *
+ * @param initialCapacity The implementation performs internal
+ * sizing to accommodate this many elements.
+ * @throws IllegalArgumentException if the initial capacity of
+ * elements is negative
+ */
+ public ConcurrentHashMapV8(int initialCapacity) {
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException();
+ int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
+ MAXIMUM_CAPACITY :
+ tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
+ this.counter = new LongAdder();
+ this.sizeCtl = cap;
+ }
+
+ /**
+ * Creates a new map with the same mappings as the given map.
+ *
+ * @param m the map
+ */
+ public ConcurrentHashMapV8(Map extends K, ? extends V> m) {
+ this.counter = new LongAdder();
+ this.sizeCtl = DEFAULT_CAPACITY;
+ internalPutAll(m);
+ }
+
+ /**
+ * Creates a new, empty map with an initial table size based on
+ * the given number of elements ({@code initialCapacity}) and
+ * initial table density ({@code loadFactor}).
+ *
+ * @param initialCapacity the initial capacity. The implementation
+ * performs internal sizing to accommodate this many elements,
+ * given the specified load factor.
+ * @param loadFactor the load factor (table density) for
+ * establishing the initial table size
+ * @throws IllegalArgumentException if the initial capacity of
+ * elements is negative or the load factor is nonpositive
+ *
+ * @since 1.6
+ */
+ public ConcurrentHashMapV8(int initialCapacity, float loadFactor) {
+ this(initialCapacity, loadFactor, 1);
+ }
+
+ /**
+ * Creates a new, empty map with an initial table size based on
+ * the given number of elements ({@code initialCapacity}), table
+ * density ({@code loadFactor}), and number of concurrently
+ * updating threads ({@code concurrencyLevel}).
+ *
+ * @param initialCapacity the initial capacity. The implementation
+ * performs internal sizing to accommodate this many elements,
+ * given the specified load factor.
+ * @param loadFactor the load factor (table density) for
+ * establishing the initial table size
+ * @param concurrencyLevel the estimated number of concurrently
+ * updating threads. The implementation may use this value as
+ * a sizing hint.
+ * @throws IllegalArgumentException if the initial capacity is
+ * negative or the load factor or concurrencyLevel are
+ * nonpositive
+ */
+ public ConcurrentHashMapV8(int initialCapacity,
+ float loadFactor, int concurrencyLevel) {
+ if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
+ throw new IllegalArgumentException();
+ if (initialCapacity < concurrencyLevel) // Use at least as many bins
+ initialCapacity = concurrencyLevel; // as estimated threads
+ long size = (long)(1.0 + (long)initialCapacity / loadFactor);
+ int cap = (size >= (long)MAXIMUM_CAPACITY) ?
+ MAXIMUM_CAPACITY : tableSizeFor((int)size);
+ this.counter = new LongAdder();
+ this.sizeCtl = cap;
+ }
+
+ /**
+ * Creates a new {@link Set} backed by a ConcurrentHashMapV8
+ * from the given type to {@code Boolean.TRUE}.
+ *
+ * @return the new set
+ */
+ public static KeySetView newKeySet() {
+ return new KeySetView(new ConcurrentHashMapV8(),
+ Boolean.TRUE);
+ }
+
+ /**
+ * Creates a new {@link Set} backed by a ConcurrentHashMapV8
+ * from the given type to {@code Boolean.TRUE}.
+ *
+ * @param initialCapacity The implementation performs internal
+ * sizing to accommodate this many elements.
+ * @throws IllegalArgumentException if the initial capacity of
+ * elements is negative
+ * @return the new set
+ */
+ public static KeySetView newKeySet(int initialCapacity) {
+ return new KeySetView(new ConcurrentHashMapV8(initialCapacity),
+ Boolean.TRUE);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEmpty() {
+ return counter.sum() <= 0L; // ignore transient negative values
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int size() {
+ long n = counter.sum();
+ return ((n < 0L) ? 0 :
+ (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
+ (int)n);
+ }
+
+ /**
+ * Returns the number of mappings. This method should be used
+ * instead of {@link #size} because a ConcurrentHashMapV8 may
+ * contain more mappings than can be represented as an int. The
+ * value returned is a snapshot; the actual count may differ if
+ * there are ongoing concurrent insertions or removals.
+ *
+ * @return the number of mappings
+ */
+ public long mappingCount() {
+ long n = counter.sum();
+ return (n < 0L) ? 0L : n; // ignore transient negative values
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped,
+ * or {@code null} if this map contains no mapping for the key.
+ *
+ *
More formally, if this map contains a mapping from a key
+ * {@code k} to a value {@code v} such that {@code key.equals(k)},
+ * then this method returns {@code v}; otherwise it returns
+ * {@code null}. (There can be at most one such mapping.)
+ *
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V get(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return (V)internalGet(key);
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped,
+ * or the given defaultValue if this map contains no mapping for the key.
+ *
+ * @param key the key
+ * @param defaultValue the value to return if this map contains
+ * no mapping for the given key
+ * @return the mapping for the key, if present; else the defaultValue
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) {
+ if (key == null)
+ throw new NullPointerException();
+ V v = (V) internalGet(key);
+ return v == null ? defaultValue : v;
+ }
+
+ /**
+ * Tests if the specified object is a key in this table.
+ *
+ * @param key possible key
+ * @return {@code true} if and only if the specified object
+ * is a key in this table, as determined by the
+ * {@code equals} method; {@code false} otherwise
+ * @throws NullPointerException if the specified key is null
+ */
+ public boolean containsKey(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return internalGet(key) != null;
+ }
+
+ /**
+ * Returns {@code true} if this map maps one or more keys to the
+ * specified value. Note: This method may require a full traversal
+ * of the map, and is much slower than method {@code containsKey}.
+ *
+ * @param value value whose presence in this map is to be tested
+ * @return {@code true} if this map maps one or more keys to the
+ * specified value
+ * @throws NullPointerException if the specified value is null
+ */
+ public boolean containsValue(Object value) {
+ if (value == null)
+ throw new NullPointerException();
+ Object v;
+ Traverser it = new Traverser(this);
+ while ((v = it.advance()) != null) {
+ if (v == value || value.equals(v))
+ return true;
+ }
+ return false;
+ }
+
+ public K findKey(Object value) {
+ if (value == null)
+ throw new NullPointerException();
+ Object v;
+ Traverser it = new Traverser(this);
+ while ((v = it.advance()) != null) {
+ if (v == value || value.equals(v))
+ return it.nextKey;
+ }
+ return null;
+ }
+
+ /**
+ * Legacy method testing if some key maps into the specified value
+ * in this table. This method is identical in functionality to
+ * {@link #containsValue}, and exists solely to ensure
+ * full compatibility with class {@link java.util.Hashtable},
+ * which supported this method prior to introduction of the
+ * Java Collections framework.
+ *
+ * @param value a value to search for
+ * @return {@code true} if and only if some key maps to the
+ * {@code value} argument in this table as
+ * determined by the {@code equals} method;
+ * {@code false} otherwise
+ * @throws NullPointerException if the specified value is null
+ */
+ public boolean contains(Object value) {
+ return containsValue(value);
+ }
+
+ /**
+ * Maps the specified key to the specified value in this table.
+ * Neither the key nor the value can be null.
+ *
+ *
The value can be retrieved by calling the {@code get} method
+ * with a key that is equal to the original key.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with {@code key}, or
+ * {@code null} if there was no mapping for {@code key}
+ * @throws NullPointerException if the specified key or value is null
+ */
+ @SuppressWarnings("unchecked") public V put(K key, V value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ return (V)internalPut(key, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the previous value associated with the specified key,
+ * or {@code null} if there was no mapping for the key
+ * @throws NullPointerException if the specified key or value is null
+ */
+ @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ return (V)internalPutIfAbsent(key, value);
+ }
+
+ /**
+ * Copies all of the mappings from the specified map to this one.
+ * These mappings replace any mappings that this map had for any of the
+ * keys currently in the specified map.
+ *
+ * @param m mappings to be stored in this map
+ */
+ public void putAll(Map extends K, ? extends V> m) {
+ internalPutAll(m);
+ }
+
+ /**
+ * If the specified key is not already associated with a value,
+ * computes its value using the given mappingFunction and enters
+ * it into the map unless null. This is equivalent to
+ *
{@code
+ * if (map.containsKey(key))
+ * return map.get(key);
+ * value = mappingFunction.apply(key);
+ * if (value != null)
+ * map.put(key, value);
+ * return value;}
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null} no mapping is recorded. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and no mapping is recorded. Some
+ * attempted update operations on this map by other threads may be
+ * blocked while computation is in progress, so the computation
+ * should be short and simple, and must not attempt to update any
+ * other mappings of this Map. The most appropriate usage is to
+ * construct a new object serving as an initial mapped value, or
+ * memoized result, as in:
+ *
+ *
{@code
+ * map.computeIfAbsent(key, new Fun() {
+ * public V map(K k) { return new Value(f(k)); }});}
+ *
+ * @param key key with which the specified value is to be associated
+ * @param mappingFunction the function to compute a value
+ * @return the current (existing or computed) value associated with
+ * the specified key, or null if the computed value is null
+ * @throws NullPointerException if the specified key or mappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the mappingFunction does so,
+ * in which case the mapping is left unestablished
+ */
+ @SuppressWarnings("unchecked") public V computeIfAbsent
+ (K key, Fun super K, ? extends V> mappingFunction) {
+ if (key == null || mappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalComputeIfAbsent(key, mappingFunction);
+ }
+
+ /**
+ * If the given key is present, computes a new mapping value given a key and
+ * its current mapped value. This is equivalent to
+ *
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null}, the mapping is removed. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and the current mapping is left
+ * unchanged. Some attempted update operations on this map by
+ * other threads may be blocked while computation is in progress,
+ * so the computation should be short and simple, and must not
+ * attempt to update any other mappings of this Map. For example,
+ * to either create or append new messages to a value mapping:
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or remappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ @SuppressWarnings("unchecked") public V computeIfPresent
+ (K key, BiFun super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalCompute(key, true, remappingFunction);
+ }
+
+ /**
+ * Computes a new mapping value given a key and
+ * its current mapped value (or {@code null} if there is no current
+ * mapping). This is equivalent to
+ *
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null}, the mapping is removed. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and the current mapping is left
+ * unchanged. Some attempted update operations on this map by
+ * other threads may be blocked while computation is in progress,
+ * so the computation should be short and simple, and must not
+ * attempt to update any other mappings of this Map. For example,
+ * to either create or append new messages to a value mapping:
+ *
+ *
{@code
+ * Map map = ...;
+ * final String msg = ...;
+ * map.compute(key, new BiFun() {
+ * public String apply(Key k, String v) {
+ * return (v == null) ? msg : v + msg;});}}
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or remappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ @SuppressWarnings("unchecked") public V compute
+ (K key, BiFun super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalCompute(key, false, remappingFunction);
+ }
+
+ /**
+ * If the specified key is not already associated
+ * with a value, associate it with the given value.
+ * Otherwise, replace the value with the results of
+ * the given remapping function. This is equivalent to:
+ *
+ * except that the action is performed atomically. If the
+ * function returns {@code null}, the mapping is removed. If the
+ * function itself throws an (unchecked) exception, the exception
+ * is rethrown to its caller, and the current mapping is left
+ * unchanged. Some attempted update operations on this map by
+ * other threads may be blocked while computation is in progress,
+ * so the computation should be short and simple, and must not
+ * attempt to update any other mappings of this Map.
+ */
+ @SuppressWarnings("unchecked") public V merge
+ (K key, V value, BiFun super V, ? super V, ? extends V> remappingFunction) {
+ if (key == null || value == null || remappingFunction == null)
+ throw new NullPointerException();
+ return (V)internalMerge(key, value, remappingFunction);
+ }
+
+ /**
+ * Removes the key (and its corresponding value) from this map.
+ * This method does nothing if the key is not in the map.
+ *
+ * @param key the key that needs to be removed
+ * @return the previous value associated with {@code key}, or
+ * {@code null} if there was no mapping for {@code key}
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V remove(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return (V)internalReplace(key, null, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NullPointerException if the specified key is null
+ */
+ public boolean remove(Object key, Object value) {
+ if (key == null)
+ throw new NullPointerException();
+ if (value == null)
+ return false;
+ return internalReplace(key, null, value) != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NullPointerException if any of the arguments are null
+ */
+ public boolean replace(K key, V oldValue, V newValue) {
+ if (key == null || oldValue == null || newValue == null)
+ throw new NullPointerException();
+ return internalReplace(key, newValue, oldValue) != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the previous value associated with the specified key,
+ * or {@code null} if there was no mapping for the key
+ * @throws NullPointerException if the specified key or value is null
+ */
+ @SuppressWarnings("unchecked") public V replace(K key, V value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ return (V)internalReplace(key, value, null);
+ }
+
+ /**
+ * Removes all of the mappings from this map.
+ */
+ public void clear() {
+ internalClear();
+ }
+
+ /**
+ * Returns a {@link Set} view of the keys contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa.
+ *
+ * @return the set view
+ */
+ public KeySetView keySet() {
+ KeySetView ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySetView(this, null));
+ }
+
+ /**
+ * Returns a {@link Set} view of the keys in this map, using the
+ * given common mapped value for any additions (i.e., {@link
+ * Collection#add} and {@link Collection#addAll}). This is of
+ * course only appropriate if it is acceptable to use the same
+ * value for all additions from this view.
+ *
+ * @param mappedValue the mapped value to use for any
+ * additions.
+ * @return the set view
+ * @throws NullPointerException if the mappedValue is null
+ */
+ public KeySetView keySet(V mappedValue) {
+ if (mappedValue == null)
+ throw new NullPointerException();
+ return new KeySetView(this, mappedValue);
+ }
+
+ /**
+ * Returns a {@link Collection} view of the values contained in this map.
+ * The collection is backed by the map, so changes to the map are
+ * reflected in the collection, and vice-versa.
+ */
+ public ValuesView values() {
+ ValuesView vs = values;
+ return (vs != null) ? vs : (values = new ValuesView(this));
+ }
+
+ /**
+ * Returns a {@link Set} view of the mappings contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa. The set supports element
+ * removal, which removes the corresponding mapping from the map,
+ * via the {@code Iterator.remove}, {@code Set.remove},
+ * {@code removeAll}, {@code retainAll}, and {@code clear}
+ * operations. It does not support the {@code add} or
+ * {@code addAll} operations.
+ *
+ *
This class is a member of the
+ *
+ * Java Collections Framework.
+ *
+ * @since 1.5
+ * @author Doug Lea
+ * @param the type of keys maintained by this map
+ * @param the type of mapped values
+ */
+public class ConcurrentHashMapV8
+ implements ConcurrentMap, Serializable, ConcurrentHashMap {
+ private static final long serialVersionUID = 7249069246763182397L;
+
+ /**
+ * A partitionable iterator. A Spliterator can be traversed
+ * directly, but can also be partitioned (before traversal) by
+ * creating another Spliterator that covers a non-overlapping
+ * portion of the elements, and so may be amenable to parallel
+ * execution.
+ *
+ *
This interface exports a subset of expected JDK8
+ * functionality.
+ *
+ *
Sample usage: Here is one (of the several) ways to compute
+ * the sum of the values held in a map using the ForkJoin
+ * framework. As illustrated here, Spliterators are well suited to
+ * designs in which a task repeatedly splits off half its work
+ * into forked subtasks until small enough to process directly,
+ * and then joins these subtasks. Variants of this style can also
+ * be used in completion-based designs.
+ *
+ *
+ * {@code ConcurrentHashMapV8 m = ...
+ * // split as if have 8 * parallelism, for load balance
+ * int n = m.size();
+ * int p = aForkJoinPool.getParallelism() * 8;
+ * int split = (n < p)? n : p;
+ * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
+ * // ...
+ * static class SumValues extends RecursiveTask {
+ * final Spliterator s;
+ * final int split; // split while > 1
+ * final SumValues nextJoin; // records forked subtasks to join
+ * SumValues(Spliterator s, int depth, SumValues nextJoin) {
+ * this.s = s; this.depth = depth; this.nextJoin = nextJoin;
+ * }
+ * public Long compute() {
+ * long sum = 0;
+ * SumValues subtasks = null; // fork subtasks
+ * for (int s = split >>> 1; s > 0; s >>>= 1)
+ * (subtasks = new SumValues(s.split(), s, subtasks)).fork();
+ * while (s.hasNext()) // directly process remaining elements
+ * sum += s.next();
+ * for (SumValues t = subtasks; t != null; t = t.nextJoin)
+ * sum += t.join(); // collect subtask results
+ * return sum;
+ * }
+ * }
+ * }
+ */
+ public static interface Spliterator extends Iterator {
+ /**
+ * Returns a Spliterator covering approximately half of the
+ * elements, guaranteed not to overlap with those subsequently
+ * returned by this Spliterator. After invoking this method,
+ * the current Spliterator will not produce any of
+ * the elements of the returned Spliterator, but the two
+ * Spliterators together will produce all of the elements that
+ * would have been produced by this Spliterator had this
+ * method not been called. The exact number of elements
+ * produced by the returned Spliterator is not guaranteed, and
+ * may be zero (i.e., with {@code hasNext()} reporting {@code
+ * false}) if this Spliterator cannot be further split.
+ *
+ * @return a Spliterator covering approximately half of the
+ * elements
+ * @throws IllegalStateException if this Spliterator has
+ * already commenced traversing elements
+ */
+ Spliterator split();
+ }
+
+
+ /*
+ * Overview:
+ *
+ * The primary design goal of this hash table is to maintain
+ * concurrent readability (typically method get(), but also
+ * iterators and related methods) while minimizing update
+ * contention. Secondary goals are to keep space consumption about
+ * the same or better than java.util.HashMap, and to support high
+ * initial insertion rates on an empty table by many threads.
+ *
+ * Each key-value mapping is held in a Node. Because Node fields
+ * can contain special values, they are defined using plain Object
+ * types. Similarly in turn, all internal methods that use them
+ * work off Object types. And similarly, so do the internal
+ * methods of auxiliary iterator and view classes. All public
+ * generic typed methods relay in/out of these internal methods,
+ * supplying null-checks and casts as needed. This also allows
+ * many of the public methods to be factored into a smaller number
+ * of internal methods (although sadly not so for the five
+ * variants of put-related operations). The validation-based
+ * approach explained below leads to a lot of code sprawl because
+ * retry-control precludes factoring into smaller methods.
+ *
+ * The table is lazily initialized to a power-of-two size upon the
+ * first insertion. Each bin in the table normally contains a
+ * list of Nodes (most often, the list has only zero or one Node).
+ * Table accesses require volatile/atomic reads, writes, and
+ * CASes. Because there is no other way to arrange this without
+ * adding further indirections, we use intrinsics
+ * (sun.misc.Unsafe) operations. The lists of nodes within bins
+ * are always accurately traversable under volatile reads, so long
+ * as lookups check hash code and non-nullness of value before
+ * checking key equality.
+ *
+ * We use the top two bits of Node hash fields for control
+ * purposes -- they are available anyway because of addressing
+ * constraints. As explained further below, these top bits are
+ * used as follows:
+ * 00 - Normal
+ * 01 - Locked
+ * 11 - Locked and may have a thread waiting for lock
+ * 10 - Node is a forwarding node
+ *
+ * The lower 30 bits of each Node's hash field contain a
+ * transformation of the key's hash code, except for forwarding
+ * nodes, for which the lower bits are zero (and so always have
+ * hash field == MOVED).
+ *
+ * Insertion (via put or its variants) of the first node in an
+ * empty bin is performed by just CASing it to the bin. This is
+ * by far the most common case for put operations under most
+ * key/hash distributions. Other update operations (insert,
+ * delete, and replace) require locks. We do not want to waste
+ * the space required to associate a distinct lock object with
+ * each bin, so instead use the first node of a bin list itself as
+ * a lock. Blocking support for these locks relies on the builtin
+ * "synchronized" monitors. However, we also need a tryLock
+ * construction, so we overlay these by using bits of the Node
+ * hash field for lock control (see above), and so normally use
+ * builtin monitors only for blocking and signalling using
+ * wait/notifyAll constructions. See Node.tryAwaitLock.
+ *
+ * Using the first node of a list as a lock does not by itself
+ * suffice though: When a node is locked, any update must first
+ * validate that it is still the first node after locking it, and
+ * retry if not. Because new nodes are always appended to lists,
+ * once a node is first in a bin, it remains first until deleted
+ * or the bin becomes invalidated (upon resizing). However,
+ * operations that only conditionally update may inspect nodes
+ * until the point of update. This is a converse of sorts to the
+ * lazy locking technique described by Herlihy & Shavit.
+ *
+ * The main disadvantage of per-bin locks is that other update
+ * operations on other nodes in a bin list protected by the same
+ * lock can stall, for example when user equals() or mapping
+ * functions take a long time. However, statistically, under
+ * random hash codes, this is not a common problem. Ideally, the
+ * frequency of nodes in bins follows a Poisson distribution
+ * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
+ * parameter of about 0.5 on average, given the resizing threshold
+ * of 0.75, although with a large variance because of resizing
+ * granularity. Ignoring variance, the expected occurrences of
+ * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The
+ * first values are:
+ *
+ * 0: 0.60653066
+ * 1: 0.30326533
+ * 2: 0.07581633
+ * 3: 0.01263606
+ * 4: 0.00157952
+ * 5: 0.00015795
+ * 6: 0.00001316
+ * 7: 0.00000094
+ * 8: 0.00000006
+ * more: less than 1 in ten million
+ *
+ * Lock contention probability for two threads accessing distinct
+ * elements is roughly 1 / (8 * #elements) under random hashes.
+ *
+ * Actual hash code distributions encountered in practice
+ * sometimes deviate significantly from uniform randomness. This
+ * includes the case when N > (1<<30), so some keys MUST collide.
+ * Similarly for dumb or hostile usages in which multiple keys are
+ * designed to have identical hash codes. Also, although we guard
+ * against the worst effects of this (see method spread), sets of
+ * hashes may differ only in bits that do not impact their bin
+ * index for a given power-of-two mask. So we use a secondary
+ * strategy that applies when the number of nodes in a bin exceeds
+ * a threshold, and at least one of the keys implements
+ * Comparable. These TreeBins use a balanced tree to hold nodes
+ * (a specialized form of red-black trees), bounding search time
+ * to O(log N). Each search step in a TreeBin is around twice as
+ * slow as in a regular list, but given that N cannot exceed
+ * (1<<64) (before running out of addresses) this bounds search
+ * steps, lock hold times, etc, to reasonable constants (roughly
+ * 100 nodes inspected per operation worst case) so long as keys
+ * are Comparable (which is very common -- String, Long, etc).
+ * TreeBin nodes (TreeNodes) also maintain the same "next"
+ * traversal pointers as regular nodes, so can be traversed in
+ * iterators in the same way.
+ *
+ * The table is resized when occupancy exceeds a percentage
+ * threshold (nominally, 0.75, but see below). Only a single
+ * thread performs the resize (using field "sizeCtl", to arrange
+ * exclusion), but the table otherwise remains usable for reads
+ * and updates. Resizing proceeds by transferring bins, one by
+ * one, from the table to the next table. Because we are using
+ * power-of-two expansion, the elements from each bin must either
+ * stay at same index, or move with a power of two offset. We
+ * eliminate unnecessary node creation by catching cases where old
+ * nodes can be reused because their next fields won't change. On
+ * average, only about one-sixth of them need cloning when a table
+ * doubles. The nodes they replace will be garbage collectable as
+ * soon as they are no longer referenced by any reader thread that
+ * may be in the midst of concurrently traversing table. Upon
+ * transfer, the old table bin contains only a special forwarding
+ * node (with hash field "MOVED") that contains the next table as
+ * its key. On encountering a forwarding node, access and update
+ * operations restart, using the new table.
+ *
+ * Each bin transfer requires its bin lock. However, unlike other
+ * cases, a transfer can skip a bin if it fails to acquire its
+ * lock, and revisit it later (unless it is a TreeBin). Method
+ * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that
+ * have been skipped because of failure to acquire a lock, and
+ * blocks only if none are available (i.e., only very rarely).
+ * The transfer operation must also ensure that all accessible
+ * bins in both the old and new table are usable by any traversal.
+ * When there are no lock acquisition failures, this is arranged
+ * simply by proceeding from the last bin (table.length - 1) up
+ * towards the first. Upon seeing a forwarding node, traversals
+ * (see class Iter) arrange to move to the new table
+ * without revisiting nodes. However, when any node is skipped
+ * during a transfer, all earlier table bins may have become
+ * visible, so are initialized with a reverse-forwarding node back
+ * to the old table until the new ones are established. (This
+ * sometimes requires transiently locking a forwarding node, which
+ * is possible under the above encoding.) These more expensive
+ * mechanics trigger only when necessary.
+ *
+ * The traversal scheme also applies to partial traversals of
+ * ranges of bins (via an alternate Traverser constructor)
+ * to support partitioned aggregate operations. Also, read-only
+ * operations give up if ever forwarded to a null table, which
+ * provides support for shutdown-style clearing, which is also not
+ * currently implemented.
+ *
+ * Lazy table initialization minimizes footprint until first use,
+ * and also avoids resizings when the first operation is from a
+ * putAll, constructor with map argument, or deserialization.
+ * These cases attempt to override the initial capacity settings,
+ * but harmlessly fail to take effect in cases of races.
+ *
+ * The element count is maintained using a LongAdder, which avoids
+ * contention on updates but can encounter cache thrashing if read
+ * too frequently during concurrent access. To avoid reading so
+ * often, resizing is attempted either when a bin lock is
+ * contended, or upon adding to a bin already holding two or more
+ * nodes (checked before adding in the xIfAbsent methods, after
+ * adding in others). Under uniform hash distributions, the
+ * probability of this occurring at threshold is around 13%,
+ * meaning that only about 1 in 8 puts check threshold (and after
+ * resizing, many fewer do so). But this approximation has high
+ * variance for small table sizes, so we check on any collision
+ * for sizes <= 64. The bulk putAll operation further reduces
+ * contention by only committing count updates upon these size
+ * checks.
+ *
+ * Maintaining API and serialization compatibility with previous
+ * versions of this class introduces several oddities. Mainly: We
+ * leave untouched but unused constructor arguments refering to
+ * concurrencyLevel. We accept a loadFactor constructor argument,
+ * but apply it only to initial table capacity (which is the only
+ * time that we can guarantee to honor it.) We also declare an
+ * unused "Segment" class that is instantiated in minimal form
+ * only when serializing.
+ */
+
+ /* ---------------- Constants -------------- */
+
+ /**
+ * The largest possible table capacity. This value must be
+ * exactly 1<<30 to stay within Java array allocation and indexing
+ * bounds for power of two table sizes, and is further required
+ * because the top two bits of 32bit hash fields are used for
+ * control purposes.
+ */
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+
+ /**
+ * The default initial table capacity. Must be a power of 2
+ * (i.e., at least 1) and at most MAXIMUM_CAPACITY.
+ */
+ private static final int DEFAULT_CAPACITY = 16;
+
+ /**
+ * The largest possible (non-power of two) array size.
+ * Needed by toArray and related methods.
+ */
+ static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+ /**
+ * The default concurrency level for this table. Unused but
+ * defined for compatibility with previous versions of this class.
+ */
+ private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
+
+ /**
+ * The load factor for this table. Overrides of this value in
+ * constructors affect only the initial table capacity. The
+ * actual floating point value isn't normally used -- it is
+ * simpler to use expressions such as {@code n - (n >>> 2)} for
+ * the associated resizing threshold.
+ */
+ private static final float LOAD_FACTOR = 0.75f;
+
+ /**
+ * The buffer size for skipped bins during transfers. The
+ * value is arbitrary but should be large enough to avoid
+ * most locking stalls during resizes.
+ */
+ private static final int TRANSFER_BUFFER_SIZE = 32;
+
+ /**
+ * The bin count threshold for using a tree rather than list for a
+ * bin. The value reflects the approximate break-even point for
+ * using tree-based operations.
+ * Note that Doug's version defaults to 8, but when dealing with
+ * Ruby objects it is actually beneficial to avoid TreeNodes
+ * as long as possible as it usually means going into Ruby land.
+ */
+ private static final int TREE_THRESHOLD = 16;
+
+ /*
+ * Encodings for special uses of Node hash fields. See above for
+ * explanation.
+ */
+ static final int MOVED = 0x80000000; // hash field for forwarding nodes
+ static final int LOCKED = 0x40000000; // set/tested only as a bit
+ static final int WAITING = 0xc0000000; // both bits set/tested together
+ static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash
+
+ /* ---------------- Fields -------------- */
+
+ /**
+ * The array of bins. Lazily initialized upon first insertion.
+ * Size is always a power of two. Accessed directly by iterators.
+ */
+ transient volatile AtomicReferenceArray table;
+
+ /**
+ * The counter maintaining number of elements.
+ */
+ private transient LongAdder counter;
+
+ /**
+ * Table initialization and resizing control. When negative, the
+ * table is being initialized or resized. Otherwise, when table is
+ * null, holds the initial table size to use upon creation, or 0
+ * for default. After initialization, holds the next element count
+ * value upon which to resize the table.
+ */
+ private transient volatile int sizeCtl;
+
+ // views
+ private transient KeySetView keySet;
+ private transient ValuesView values;
+ private transient EntrySetView entrySet;
+
+ /** For serialization compatibility. Null unless serialized; see below */
+ private Segment[] segments;
+
+ static AtomicIntegerFieldUpdater SIZE_CTRL_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ConcurrentHashMapV8.class, "sizeCtl");
+
+ /* ---------------- Table element access -------------- */
+
+ /*
+ * Volatile access methods are used for table elements as well as
+ * elements of in-progress next table while resizing. Uses are
+ * null checked by callers, and implicitly bounds-checked, relying
+ * on the invariants that tab arrays have non-zero size, and all
+ * indices are masked with (tab.length - 1) which is never
+ * negative and always less than length. Note that, to be correct
+ * wrt arbitrary concurrency errors by users, bounds checks must
+ * operate on local variables, which accounts for some odd-looking
+ * inline assignments below.
+ */
+
+ static final Node tabAt(AtomicReferenceArray tab, int i) { // used by Iter
+ return tab.get(i);
+ }
+
+ private static final boolean casTabAt(AtomicReferenceArray tab, int i, Node c, Node v) {
+ return tab.compareAndSet(i, c, v);
+ }
+
+ private static final void setTabAt(AtomicReferenceArray tab, int i, Node v) {
+ tab.set(i, v);
+ }
+
+ /* ---------------- Nodes -------------- */
+
+ /**
+ * Key-value entry. Note that this is never exported out as a
+ * user-visible Map.Entry (see MapEntry below). Nodes with a hash
+ * field of MOVED are special, and do not contain user keys or
+ * values. Otherwise, keys are never null, and null val fields
+ * indicate that a node is in the process of being deleted or
+ * created. For purposes of read-only access, a key may be read
+ * before a val, but can only be used after checking val to be
+ * non-null.
+ */
+ static class Node {
+ volatile int hash;
+ final Object key;
+ volatile Object val;
+ volatile Node next;
+
+ static AtomicIntegerFieldUpdater HASH_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Node.class, "hash");
+
+ Node(int hash, Object key, Object val, Node next) {
+ this.hash = hash;
+ this.key = key;
+ this.val = val;
+ this.next = next;
+ }
+
+ /** CompareAndSet the hash field */
+ final boolean casHash(int cmp, int val) {
+ return HASH_UPDATER.compareAndSet(this, cmp, val);
+ }
+
+ /** The number of spins before blocking for a lock */
+ static final int MAX_SPINS =
+ Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
+
+ /**
+ * Spins a while if LOCKED bit set and this node is the first
+ * of its bin, and then sets WAITING bits on hash field and
+ * blocks (once) if they are still set. It is OK for this
+ * method to return even if lock is not available upon exit,
+ * which enables these simple single-wait mechanics.
+ *
+ * The corresponding signalling operation is performed within
+ * callers: Upon detecting that WAITING has been set when
+ * unlocking lock (via a failed CAS from non-waiting LOCKED
+ * state), unlockers acquire the sync lock and perform a
+ * notifyAll.
+ *
+ * The initial sanity check on tab and bounds is not currently
+ * necessary in the only usages of this method, but enables
+ * use in other future contexts.
+ */
+ final void tryAwaitLock(AtomicReferenceArray tab, int i) {
+ if (tab != null && i >= 0 && i < tab.length()) { // sanity check
+ int r = ThreadLocalRandom.current().nextInt(); // randomize spins
+ int spins = MAX_SPINS, h;
+ while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) {
+ if (spins >= 0) {
+ r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift
+ if (r >= 0 && --spins == 0)
+ Thread.yield(); // yield before block
+ }
+ else if (casHash(h, h | WAITING)) {
+ synchronized (this) {
+ if (tabAt(tab, i) == this &&
+ (hash & WAITING) == WAITING) {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ else
+ notifyAll(); // possibly won race vs signaller
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* ---------------- TreeBins -------------- */
+
+ /**
+ * Nodes for use in TreeBins
+ */
+ static final class TreeNode extends Node {
+ TreeNode parent; // red-black tree links
+ TreeNode left;
+ TreeNode right;
+ TreeNode prev; // needed to unlink next upon deletion
+ boolean red;
+
+ TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) {
+ super(hash, key, val, next);
+ this.parent = parent;
+ }
+ }
+
+ /**
+ * A specialized form of red-black tree for use in bins
+ * whose size exceeds a threshold.
+ *
+ * TreeBins use a special form of comparison for search and
+ * related operations (which is the main reason we cannot use
+ * existing collections such as TreeMaps). TreeBins contain
+ * Comparable elements, but may contain others, as well as
+ * elements that are Comparable but not necessarily Comparable
+ * for the same T, so we cannot invoke compareTo among them. To
+ * handle this, the tree is ordered primarily by hash value, then
+ * by getClass().getName() order, and then by Comparator order
+ * among elements of the same class. On lookup at a node, if
+ * elements are not comparable or compare as 0, both left and
+ * right children may need to be searched in the case of tied hash
+ * values. (This corresponds to the full list search that would be
+ * necessary if all elements were non-Comparable and had tied
+ * hashes.) The red-black balancing code is updated from
+ * pre-jdk-collections
+ * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
+ * based in turn on Cormen, Leiserson, and Rivest "Introduction to
+ * Algorithms" (CLR).
+ *
+ * TreeBins also maintain a separate locking discipline than
+ * regular bins. Because they are forwarded via special MOVED
+ * nodes at bin heads (which can never change once established),
+ * we cannot use those nodes as locks. Instead, TreeBin
+ * extends AbstractQueuedSynchronizer to support a simple form of
+ * read-write lock. For update operations and table validation,
+ * the exclusive form of lock behaves in the same way as bin-head
+ * locks. However, lookups use shared read-lock mechanics to allow
+ * multiple readers in the absence of writers. Additionally,
+ * these lookups do not ever block: While the lock is not
+ * available, they proceed along the slow traversal path (via
+ * next-pointers) until the lock becomes available or the list is
+ * exhausted, whichever comes first. (These cases are not fast,
+ * but maximize aggregate expected throughput.) The AQS mechanics
+ * for doing this are straightforward. The lock state is held as
+ * AQS getState(). Read counts are negative; the write count (1)
+ * is positive. There are no signalling preferences among readers
+ * and writers. Since we don't need to export full Lock API, we
+ * just override the minimal AQS methods and use them directly.
+ */
+ static final class TreeBin extends AbstractQueuedSynchronizer {
+ private static final long serialVersionUID = 2249069246763182397L;
+ transient TreeNode root; // root of tree
+ transient TreeNode first; // head of next-pointer list
+
+ /* AQS overrides */
+ public final boolean isHeldExclusively() { return getState() > 0; }
+ public final boolean tryAcquire(int ignore) {
+ if (compareAndSetState(0, 1)) {
+ setExclusiveOwnerThread(Thread.currentThread());
+ return true;
+ }
+ return false;
+ }
+ public final boolean tryRelease(int ignore) {
+ setExclusiveOwnerThread(null);
+ setState(0);
+ return true;
+ }
+ public final int tryAcquireShared(int ignore) {
+ for (int c;;) {
+ if ((c = getState()) > 0)
+ return -1;
+ if (compareAndSetState(c, c -1))
+ return 1;
+ }
+ }
+ public final boolean tryReleaseShared(int ignore) {
+ int c;
+ do {} while (!compareAndSetState(c = getState(), c + 1));
+ return c == -1;
+ }
+
+ /** From CLR */
+ private void rotateLeft(TreeNode p) {
+ if (p != null) {
+ TreeNode r = p.right, pp, rl;
+ if ((rl = p.right = r.left) != null)
+ rl.parent = p;
+ if ((pp = r.parent = p.parent) == null)
+ root = r;
+ else if (pp.left == p)
+ pp.left = r;
+ else
+ pp.right = r;
+ r.left = p;
+ p.parent = r;
+ }
+ }
+
+ /** From CLR */
+ private void rotateRight(TreeNode p) {
+ if (p != null) {
+ TreeNode l = p.left, pp, lr;
+ if ((lr = p.left = l.right) != null)
+ lr.parent = p;
+ if ((pp = l.parent = p.parent) == null)
+ root = l;
+ else if (pp.right == p)
+ pp.right = l;
+ else
+ pp.left = l;
+ l.right = p;
+ p.parent = l;
+ }
+ }
+
+ @SuppressWarnings("unchecked") final TreeNode getTreeNode
+ (int h, Object k, TreeNode p) {
+ return getTreeNode(h, (RubyObject)k, p);
+ }
+
+ /**
+ * Returns the TreeNode (or null if not found) for the given key
+ * starting at given root.
+ */
+ @SuppressWarnings("unchecked") final TreeNode getTreeNode
+ (int h, RubyObject k, TreeNode p) {
+ RubyClass c = k.getMetaClass(); boolean kNotComparable = !k.respondsTo("<=>");
+ while (p != null) {
+ int dir, ph; RubyObject pk; RubyClass pc;
+ if ((ph = p.hash) == h) {
+ if ((pk = (RubyObject)p.key) == k || k.equals(pk))
+ return p;
+ if (c != (pc = (RubyClass)pk.getMetaClass()) ||
+ kNotComparable ||
+ (dir = rubyCompare(k, pk)) == 0) {
+ dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName());
+ if (dir == 0) { // if still stuck, need to check both sides
+ TreeNode r = null, pl, pr;
+ // try to recurse on the right
+ if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null)
+ return r;
+ // try to continue iterating on the left side
+ else if ((pl = p.left) != null && h <= pl.hash)
+ dir = -1;
+ else // no matching node found
+ return null;
+ }
+ }
+ }
+ else
+ dir = (h < ph) ? -1 : 1;
+ p = (dir > 0) ? p.right : p.left;
+ }
+ return null;
+ }
+
+ int rubyCompare(RubyObject l, RubyObject r) {
+ ThreadContext context = l.getMetaClass().getRuntime().getCurrentContext();
+ IRubyObject result;
+ try {
+ result = l.callMethod(context, "<=>", r);
+ } catch (RaiseException e) {
+ // handle objects "lying" about responding to <=>, ie: an Array containing non-comparable keys
+ if (context.runtime.getNoMethodError().isInstance(e.getException())) {
+ return 0;
+ }
+ throw e;
+ }
+
+ return result.isNil() ? 0 : RubyNumeric.num2int(result.convertToInteger());
+ }
+
+ /**
+ * Wrapper for getTreeNode used by CHM.get. Tries to obtain
+ * read-lock to call getTreeNode, but during failure to get
+ * lock, searches along next links.
+ */
+ final Object getValue(int h, Object k) {
+ Node r = null;
+ int c = getState(); // Must read lock state first
+ for (Node e = first; e != null; e = e.next) {
+ if (c <= 0 && compareAndSetState(c, c - 1)) {
+ try {
+ r = getTreeNode(h, k, root);
+ } finally {
+ releaseShared(0);
+ }
+ break;
+ }
+ else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) {
+ r = e;
+ break;
+ }
+ else
+ c = getState();
+ }
+ return r == null ? null : r.val;
+ }
+
+ @SuppressWarnings("unchecked") final TreeNode putTreeNode
+ (int h, Object k, Object v) {
+ return putTreeNode(h, (RubyObject)k, v);
+ }
+
+ /**
+ * Finds or adds a node.
+ * @return null if added
+ */
+ @SuppressWarnings("unchecked") final TreeNode putTreeNode
+ (int h, RubyObject k, Object v) {
+ RubyClass c = k.getMetaClass();
+ boolean kNotComparable = !k.respondsTo("<=>");
+ TreeNode pp = root, p = null;
+ int dir = 0;
+ while (pp != null) { // find existing node or leaf to insert at
+ int ph; RubyObject pk; RubyClass pc;
+ p = pp;
+ if ((ph = p.hash) == h) {
+ if ((pk = (RubyObject)p.key) == k || k.equals(pk))
+ return p;
+ if (c != (pc = pk.getMetaClass()) ||
+ kNotComparable ||
+ (dir = rubyCompare(k, pk)) == 0) {
+ dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName());
+ if (dir == 0) { // if still stuck, need to check both sides
+ TreeNode r = null, pr;
+ // try to recurse on the right
+ if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null)
+ return r;
+ else // continue descending down the left subtree
+ dir = -1;
+ }
+ }
+ }
+ else
+ dir = (h < ph) ? -1 : 1;
+ pp = (dir > 0) ? p.right : p.left;
+ }
+
+ TreeNode f = first;
+ TreeNode x = first = new TreeNode(h, (Object)k, v, f, p);
+ if (p == null)
+ root = x;
+ else { // attach and rebalance; adapted from CLR
+ TreeNode xp, xpp;
+ if (f != null)
+ f.prev = x;
+ if (dir <= 0)
+ p.left = x;
+ else
+ p.right = x;
+ x.red = true;
+ while (x != null && (xp = x.parent) != null && xp.red &&
+ (xpp = xp.parent) != null) {
+ TreeNode xppl = xpp.left;
+ if (xp == xppl) {
+ TreeNode y = xpp.right;
+ if (y != null && y.red) {
+ y.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.right) {
+ rotateLeft(x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ rotateRight(xpp);
+ }
+ }
+ }
+ }
+ else {
+ TreeNode y = xppl;
+ if (y != null && y.red) {
+ y.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.left) {
+ rotateRight(x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ rotateLeft(xpp);
+ }
+ }
+ }
+ }
+ }
+ TreeNode r = root;
+ if (r != null && r.red)
+ r.red = false;
+ }
+ return null;
+ }
+
+ /**
+ * Removes the given node, that must be present before this
+ * call. This is messier than typical red-black deletion code
+ * because we cannot swap the contents of an interior node
+ * with a leaf successor that is pinned by "next" pointers
+ * that are accessible independently of lock. So instead we
+ * swap the tree linkages.
+ */
+ final void deleteTreeNode(TreeNode p) {
+ TreeNode next = (TreeNode)p.next; // unlink traversal pointers
+ TreeNode pred = p.prev;
+ if (pred == null)
+ first = next;
+ else
+ pred.next = next;
+ if (next != null)
+ next.prev = pred;
+ TreeNode replacement;
+ TreeNode pl = p.left;
+ TreeNode pr = p.right;
+ if (pl != null && pr != null) {
+ TreeNode s = pr, sl;
+ while ((sl = s.left) != null) // find successor
+ s = sl;
+ boolean c = s.red; s.red = p.red; p.red = c; // swap colors
+ TreeNode sr = s.right;
+ TreeNode pp = p.parent;
+ if (s == pr) { // p was s's direct parent
+ p.parent = s;
+ s.right = p;
+ }
+ else {
+ TreeNode sp = s.parent;
+ if ((p.parent = sp) != null) {
+ if (s == sp.left)
+ sp.left = p;
+ else
+ sp.right = p;
+ }
+ if ((s.right = pr) != null)
+ pr.parent = s;
+ }
+ p.left = null;
+ if ((p.right = sr) != null)
+ sr.parent = p;
+ if ((s.left = pl) != null)
+ pl.parent = s;
+ if ((s.parent = pp) == null)
+ root = s;
+ else if (p == pp.left)
+ pp.left = s;
+ else
+ pp.right = s;
+ replacement = sr;
+ }
+ else
+ replacement = (pl != null) ? pl : pr;
+ TreeNode pp = p.parent;
+ if (replacement == null) {
+ if (pp == null) {
+ root = null;
+ return;
+ }
+ replacement = p;
+ }
+ else {
+ replacement.parent = pp;
+ if (pp == null)
+ root = replacement;
+ else if (p == pp.left)
+ pp.left = replacement;
+ else
+ pp.right = replacement;
+ p.left = p.right = p.parent = null;
+ }
+ if (!p.red) { // rebalance, from CLR
+ TreeNode x = replacement;
+ while (x != null) {
+ TreeNode xp, xpl;
+ if (x.red || (xp = x.parent) == null) {
+ x.red = false;
+ break;
+ }
+ if (x == (xpl = xp.left)) {
+ TreeNode sib = xp.right;
+ if (sib != null && sib.red) {
+ sib.red = false;
+ xp.red = true;
+ rotateLeft(xp);
+ sib = (xp = x.parent) == null ? null : xp.right;
+ }
+ if (sib == null)
+ x = xp;
+ else {
+ TreeNode sl = sib.left, sr = sib.right;
+ if ((sr == null || !sr.red) &&
+ (sl == null || !sl.red)) {
+ sib.red = true;
+ x = xp;
+ }
+ else {
+ if (sr == null || !sr.red) {
+ if (sl != null)
+ sl.red = false;
+ sib.red = true;
+ rotateRight(sib);
+ sib = (xp = x.parent) == null ? null : xp.right;
+ }
+ if (sib != null) {
+ sib.red = (xp == null) ? false : xp.red;
+ if ((sr = sib.right) != null)
+ sr.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ rotateLeft(xp);
+ }
+ x = root;
+ }
+ }
+ }
+ else { // symmetric
+ TreeNode sib = xpl;
+ if (sib != null && sib.red) {
+ sib.red = false;
+ xp.red = true;
+ rotateRight(xp);
+ sib = (xp = x.parent) == null ? null : xp.left;
+ }
+ if (sib == null)
+ x = xp;
+ else {
+ TreeNode sl = sib.left, sr = sib.right;
+ if ((sl == null || !sl.red) &&
+ (sr == null || !sr.red)) {
+ sib.red = true;
+ x = xp;
+ }
+ else {
+ if (sl == null || !sl.red) {
+ if (sr != null)
+ sr.red = false;
+ sib.red = true;
+ rotateLeft(sib);
+ sib = (xp = x.parent) == null ? null : xp.left;
+ }
+ if (sib != null) {
+ sib.red = (xp == null) ? false : xp.red;
+ if ((sl = sib.left) != null)
+ sl.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ rotateRight(xp);
+ }
+ x = root;
+ }
+ }
+ }
+ }
+ }
+ if (p == replacement && (pp = p.parent) != null) {
+ if (p == pp.left) // detach pointers
+ pp.left = null;
+ else if (p == pp.right)
+ pp.right = null;
+ p.parent = null;
+ }
+ }
+ }
+
+ /* ---------------- Collision reduction methods -------------- */
+
+ /**
+ * Spreads higher bits to lower, and also forces top 2 bits to 0.
+ * Because the table uses power-of-two masking, sets of hashes
+ * that vary only in bits above the current mask will always
+ * collide. (Among known examples are sets of Float keys holding
+ * consecutive whole numbers in small tables.) To counter this,
+ * we apply a transform that spreads the impact of higher bits
+ * downward. There is a tradeoff between speed, utility, and
+ * quality of bit-spreading. Because many common sets of hashes
+ * are already reasonably distributed across bits (so don't benefit
+ * from spreading), and because we use trees to handle large sets
+ * of collisions in bins, we don't need excessively high quality.
+ */
+ private static final int spread(int h) {
+ h ^= (h >>> 18) ^ (h >>> 12);
+ return (h ^ (h >>> 10)) & HASH_BITS;
+ }
+
+ /**
+ * Replaces a list bin with a tree bin. Call only when locked.
+ * Fails to replace if the given key is non-comparable or table
+ * is, or needs, resizing.
+ */
+ private final void replaceWithTreeBin(AtomicReferenceArray tab, int index, Object key) {
+ if ((key instanceof Comparable) &&
+ (tab.length() >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) {
+ TreeBin t = new TreeBin();
+ for (Node e = tabAt(tab, index); e != null; e = e.next)
+ t.putTreeNode(e.hash & HASH_BITS, e.key, e.val);
+ setTabAt(tab, index, new Node(MOVED, t, null, null));
+ }
+ }
+
+ /* ---------------- Internal access and update methods -------------- */
+
+ /** Implementation for get and containsKey */
+ private final Object internalGet(Object k) {
+ int h = spread(k.hashCode());
+ retry: for (AtomicReferenceArray tab = table; tab != null;) {
+ Node e, p; Object ek, ev; int eh; // locals to read fields once
+ for (e = tabAt(tab, (tab.length() - 1) & h); e != null; e = e.next) {
+ if ((eh = e.hash) == MOVED) {
+ if ((ek = e.key) instanceof TreeBin) // search TreeBin
+ return ((TreeBin)ek).getValue(h, k);
+ else { // restart with new table
+ tab = (AtomicReferenceArray)ek;
+ continue retry;
+ }
+ }
+ else if ((eh & HASH_BITS) == h && (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek)))
+ return ev;
+ }
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Implementation for the four public remove/replace methods:
+ * Replaces node value with v, conditional upon match of cv if
+ * non-null. If resulting value is null, delete.
+ */
+ private final Object internalReplace(Object k, Object v, Object cv) {
+ int h = spread(k.hashCode());
+ Object oldVal = null;
+ for (AtomicReferenceArray tab = table;;) {
+ Node f; int i, fh; Object fk;
+ if (tab == null ||
+ (f = tabAt(tab, i = (tab.length() - 1) & h)) == null)
+ break;
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ boolean validated = false;
+ boolean deleted = false;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ if (p != null) {
+ Object pv = p.val;
+ if (cv == null || cv == pv || cv.equals(pv)) {
+ oldVal = pv;
+ if ((p.val = v) == null) {
+ deleted = true;
+ t.deleteTreeNode(p);
+ }
+ }
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (validated) {
+ if (deleted)
+ counter.add(-1L);
+ break;
+ }
+ }
+ else
+ tab = (AtomicReferenceArray)fk;
+ }
+ else if ((fh & HASH_BITS) != h && f.next == null) // precheck
+ break; // rules out possible existence
+ else if ((fh & LOCKED) != 0) {
+ checkForResize(); // try resizing if can't get lock
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ boolean validated = false;
+ boolean deleted = false;
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ for (Node e = f, pred = null;;) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ ((ev = e.val) != null) &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ if (cv == null || cv == ev || cv.equals(ev)) {
+ oldVal = ev;
+ if ((e.val = v) == null) {
+ deleted = true;
+ Node en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null)
+ break;
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (validated) {
+ if (deleted)
+ counter.add(-1L);
+ break;
+ }
+ }
+ }
+ return oldVal;
+ }
+
+ /*
+ * Internal versions of the six insertion methods, each a
+ * little more complicated than the last. All have
+ * the same basic structure as the first (internalPut):
+ * 1. If table uninitialized, create
+ * 2. If bin empty, try to CAS new node
+ * 3. If bin stale, use new table
+ * 4. if bin converted to TreeBin, validate and relay to TreeBin methods
+ * 5. Lock and validate; if valid, scan and add or update
+ *
+ * The others interweave other checks and/or alternative actions:
+ * * Plain put checks for and performs resize after insertion.
+ * * putIfAbsent prescans for mapping without lock (and fails to add
+ * if present), which also makes pre-emptive resize checks worthwhile.
+ * * computeIfAbsent extends form used in putIfAbsent with additional
+ * mechanics to deal with, calls, potential exceptions and null
+ * returns from function call.
+ * * compute uses the same function-call mechanics, but without
+ * the prescans
+ * * merge acts as putIfAbsent in the absent case, but invokes the
+ * update function if present
+ * * putAll attempts to pre-allocate enough table space
+ * and more lazily performs count updates and checks.
+ *
+ * Someday when details settle down a bit more, it might be worth
+ * some factoring to reduce sprawl.
+ */
+
+ /** Implementation for put */
+ private final Object internalPut(Object k, Object v) {
+ int h = spread(k.hashCode());
+ int count = 0;
+ for (AtomicReferenceArray tab = table;;) {
+ int i; Node f; int fh; Object fk;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) {
+ if (casTabAt(tab, i, null, new Node(h, k, v, null)))
+ break; // no lock when adding to empty bin
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ Object oldVal = null;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 2;
+ TreeNode p = t.putTreeNode(h, k, v);
+ if (p != null) {
+ oldVal = p.val;
+ p.val = v;
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0) {
+ if (oldVal != null)
+ return oldVal;
+ break;
+ }
+ }
+ else
+ tab = (AtomicReferenceArray)fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ Object oldVal = null;
+ try { // needed in case equals() throws
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ oldVal = ev;
+ e.val = v;
+ break;
+ }
+ Node last = e;
+ if ((e = e.next) == null) {
+ last.next = new Node(h, k, v, null);
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ break;
+ }
+ }
+ }
+ } finally { // unlock and signal if needed
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (oldVal != null)
+ return oldVal;
+ if (tab.length() <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ counter.add(1L);
+ if (count > 1)
+ checkForResize();
+ return null;
+ }
+
+ /** Implementation for putIfAbsent */
+ private final Object internalPutIfAbsent(Object k, Object v) {
+ int h = spread(k.hashCode());
+ int count = 0;
+ for (AtomicReferenceArray tab = table;;) {
+ int i; Node f; int fh; Object fk, fv;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) {
+ if (casTabAt(tab, i, null, new Node(h, k, v, null)))
+ break;
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ Object oldVal = null;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 2;
+ TreeNode p = t.putTreeNode(h, k, v);
+ if (p != null)
+ oldVal = p.val;
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0) {
+ if (oldVal != null)
+ return oldVal;
+ break;
+ }
+ }
+ else
+ tab = (AtomicReferenceArray)fk;
+ }
+ else if ((fh & HASH_BITS) == h && (fv = f.val) != null &&
+ ((fk = f.key) == k || k.equals(fk)))
+ return fv;
+ else {
+ Node g = f.next;
+ if (g != null) { // at least 2 nodes -- search and maybe resize
+ for (Node e = g;;) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h && (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek)))
+ return ev;
+ if ((e = e.next) == null) {
+ checkForResize();
+ break;
+ }
+ }
+ }
+ if (((fh = f.hash) & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) {
+ Object oldVal = null;
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ oldVal = ev;
+ break;
+ }
+ Node last = e;
+ if ((e = e.next) == null) {
+ last.next = new Node(h, k, v, null);
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (oldVal != null)
+ return oldVal;
+ if (tab.length() <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ }
+ counter.add(1L);
+ if (count > 1)
+ checkForResize();
+ return null;
+ }
+
+ /** Implementation for computeIfAbsent */
+ private final Object internalComputeIfAbsent(K k,
+ Fun super K, ?> mf) {
+ int h = spread(k.hashCode());
+ Object val = null;
+ int count = 0;
+ for (AtomicReferenceArray tab = table;;) {
+ Node f; int i, fh; Object fk, fv;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) {
+ Node node = new Node(fh = h | LOCKED, k, null, null);
+ if (casTabAt(tab, i, null, node)) {
+ count = 1;
+ try {
+ if ((val = mf.apply(k)) != null)
+ node.val = val;
+ } finally {
+ if (val == null)
+ setTabAt(tab, i, null);
+ if (!node.casHash(fh, h)) {
+ node.hash = h;
+ synchronized (node) { node.notifyAll(); };
+ }
+ }
+ }
+ if (count != 0)
+ break;
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ boolean added = false;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ if (p != null)
+ val = p.val;
+ else if ((val = mf.apply(k)) != null) {
+ added = true;
+ count = 2;
+ t.putTreeNode(h, k, val);
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0) {
+ if (!added)
+ return val;
+ break;
+ }
+ }
+ else
+ tab = (AtomicReferenceArray)fk;
+ }
+ else if ((fh & HASH_BITS) == h && (fv = f.val) != null &&
+ ((fk = f.key) == k || k.equals(fk)))
+ return fv;
+ else {
+ Node g = f.next;
+ if (g != null) {
+ for (Node e = g;;) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h && (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek)))
+ return ev;
+ if ((e = e.next) == null) {
+ checkForResize();
+ break;
+ }
+ }
+ }
+ if (((fh = f.hash) & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) {
+ boolean added = false;
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ val = ev;
+ break;
+ }
+ Node last = e;
+ if ((e = e.next) == null) {
+ if ((val = mf.apply(k)) != null) {
+ added = true;
+ last.next = new Node(h, k, val, null);
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ }
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (!added)
+ return val;
+ if (tab.length() <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ }
+ if (val != null) {
+ counter.add(1L);
+ if (count > 1)
+ checkForResize();
+ }
+ return val;
+ }
+
+ /** Implementation for compute */
+ @SuppressWarnings("unchecked") private final Object internalCompute
+ (K k, boolean onlyIfPresent, BiFun super K, ? super V, ? extends V> mf) {
+ int h = spread(k.hashCode());
+ Object val = null;
+ int delta = 0;
+ int count = 0;
+ for (AtomicReferenceArray tab = table;;) {
+ Node f; int i, fh; Object fk;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) {
+ if (onlyIfPresent)
+ break;
+ Node node = new Node(fh = h | LOCKED, k, null, null);
+ if (casTabAt(tab, i, null, node)) {
+ try {
+ count = 1;
+ if ((val = mf.apply(k, null)) != null) {
+ node.val = val;
+ delta = 1;
+ }
+ } finally {
+ if (delta == 0)
+ setTabAt(tab, i, null);
+ if (!node.casHash(fh, h)) {
+ node.hash = h;
+ synchronized (node) { node.notifyAll(); };
+ }
+ }
+ }
+ if (count != 0)
+ break;
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ Object pv;
+ if (p == null) {
+ if (onlyIfPresent)
+ break;
+ pv = null;
+ } else
+ pv = p.val;
+ if ((val = mf.apply(k, (V)pv)) != null) {
+ if (p != null)
+ p.val = val;
+ else {
+ count = 2;
+ delta = 1;
+ t.putTreeNode(h, k, val);
+ }
+ }
+ else if (p != null) {
+ delta = -1;
+ t.deleteTreeNode(p);
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0)
+ break;
+ }
+ else
+ tab = (AtomicReferenceArray)fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f, pred = null;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ val = mf.apply(k, (V)ev);
+ if (val != null)
+ e.val = val;
+ else {
+ delta = -1;
+ Node en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null) {
+ if (!onlyIfPresent && (val = mf.apply(k, null)) != null) {
+ pred.next = new Node(h, k, val, null);
+ delta = 1;
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ }
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (tab.length() <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ if (delta != 0) {
+ counter.add((long)delta);
+ if (count > 1)
+ checkForResize();
+ }
+ return val;
+ }
+
+ /** Implementation for merge */
+ @SuppressWarnings("unchecked") private final Object internalMerge
+ (K k, V v, BiFun super V, ? super V, ? extends V> mf) {
+ int h = spread(k.hashCode());
+ Object val = null;
+ int delta = 0;
+ int count = 0;
+ for (AtomicReferenceArray tab = table;;) {
+ int i; Node f; int fh; Object fk, fv;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) {
+ if (casTabAt(tab, i, null, new Node(h, k, v, null))) {
+ delta = 1;
+ val = v;
+ break;
+ }
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ val = (p == null) ? v : mf.apply((V)p.val, v);
+ if (val != null) {
+ if (p != null)
+ p.val = val;
+ else {
+ count = 2;
+ delta = 1;
+ t.putTreeNode(h, k, val);
+ }
+ }
+ else if (p != null) {
+ delta = -1;
+ t.deleteTreeNode(p);
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0)
+ break;
+ }
+ else
+ tab = (AtomicReferenceArray)fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f, pred = null;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ val = mf.apply((V)ev, v);
+ if (val != null)
+ e.val = val;
+ else {
+ delta = -1;
+ Node en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null) {
+ val = v;
+ pred.next = new Node(h, k, val, null);
+ delta = 1;
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (tab.length() <= 64)
+ count = 2;
+ break;
+ }
+ }
+ }
+ if (delta != 0) {
+ counter.add((long)delta);
+ if (count > 1)
+ checkForResize();
+ }
+ return val;
+ }
+
+ /** Implementation for putAll */
+ private final void internalPutAll(Map, ?> m) {
+ tryPresize(m.size());
+ long delta = 0L; // number of uncommitted additions
+ boolean npe = false; // to throw exception on exit for nulls
+ try { // to clean up counts on other exceptions
+ for (Map.Entry, ?> entry : m.entrySet()) {
+ Object k, v;
+ if (entry == null || (k = entry.getKey()) == null ||
+ (v = entry.getValue()) == null) {
+ npe = true;
+ break;
+ }
+ int h = spread(k.hashCode());
+ for (AtomicReferenceArray tab = table;;) {
+ int i; Node f; int fh; Object fk;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null){
+ if (casTabAt(tab, i, null, new Node(h, k, v, null))) {
+ ++delta;
+ break;
+ }
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ boolean validated = false;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ if (p != null)
+ p.val = v;
+ else {
+ t.putTreeNode(h, k, v);
+ ++delta;
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (validated)
+ break;
+ }
+ else
+ tab = (AtomicReferenceArray)fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ counter.add(delta);
+ delta = 0L;
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ int count = 0;
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ e.val = v;
+ break;
+ }
+ Node last = e;
+ if ((e = e.next) == null) {
+ ++delta;
+ last.next = new Node(h, k, v, null);
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ break;
+ }
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (count != 0) {
+ if (count > 1) {
+ counter.add(delta);
+ delta = 0L;
+ checkForResize();
+ }
+ break;
+ }
+ }
+ }
+ }
+ } finally {
+ if (delta != 0)
+ counter.add(delta);
+ }
+ if (npe)
+ throw new NullPointerException();
+ }
+
+ /* ---------------- Table Initialization and Resizing -------------- */
+
+ /**
+ * Returns a power of two table size for the given desired capacity.
+ * See Hackers Delight, sec 3.2
+ */
+ private static final int tableSizeFor(int c) {
+ int n = c - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+
+ /**
+ * Initializes table, using the size recorded in sizeCtl.
+ */
+ private final AtomicReferenceArray initTable() {
+ AtomicReferenceArray tab; int sc;
+ while ((tab = table) == null) {
+ if ((sc = sizeCtl) < 0)
+ Thread.yield(); // lost initialization race; just spin
+ else if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) {
+ try {
+ if ((tab = table) == null) {
+ int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
+ tab = table = new AtomicReferenceArray(n);
+ sc = n - (n >>> 2);
+ }
+ } finally {
+ sizeCtl = sc;
+ }
+ break;
+ }
+ }
+ return tab;
+ }
+
+ /**
+ * If table is too small and not already resizing, creates next
+ * table and transfers bins. Rechecks occupancy after a transfer
+ * to see if another resize is already needed because resizings
+ * are lagging additions.
+ */
+ private final void checkForResize() {
+ AtomicReferenceArray tab; int n, sc;
+ while ((tab = table) != null &&
+ (n = tab.length()) < MAXIMUM_CAPACITY &&
+ (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc &&
+ SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) {
+ try {
+ if (tab == table) {
+ table = rebuild(tab);
+ sc = (n << 1) - (n >>> 1);
+ }
+ } finally {
+ sizeCtl = sc;
+ }
+ }
+ }
+
+ /**
+ * Tries to presize table to accommodate the given number of elements.
+ *
+ * @param size number of elements (doesn't need to be perfectly accurate)
+ */
+ private final void tryPresize(int size) {
+ int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
+ tableSizeFor(size + (size >>> 1) + 1);
+ int sc;
+ while ((sc = sizeCtl) >= 0) {
+ AtomicReferenceArray tab = table; int n;
+ if (tab == null || (n = tab.length()) == 0) {
+ n = (sc > c) ? sc : c;
+ if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) {
+ try {
+ if (table == tab) {
+ table = new AtomicReferenceArray(n);
+ sc = n - (n >>> 2);
+ }
+ } finally {
+ sizeCtl = sc;
+ }
+ }
+ }
+ else if (c <= sc || n >= MAXIMUM_CAPACITY)
+ break;
+ else if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) {
+ try {
+ if (table == tab) {
+ table = rebuild(tab);
+ sc = (n << 1) - (n >>> 1);
+ }
+ } finally {
+ sizeCtl = sc;
+ }
+ }
+ }
+ }
+
+ /*
+ * Moves and/or copies the nodes in each bin to new table. See
+ * above for explanation.
+ *
+ * @return the new table
+ */
+ private static final AtomicReferenceArray rebuild(AtomicReferenceArray tab) {
+ int n = tab.length();
+ AtomicReferenceArray nextTab = new AtomicReferenceArray(n << 1);
+ Node fwd = new Node(MOVED, nextTab, null, null);
+ int[] buffer = null; // holds bins to revisit; null until needed
+ Node rev = null; // reverse forwarder; null until needed
+ int nbuffered = 0; // the number of bins in buffer list
+ int bufferIndex = 0; // buffer index of current buffered bin
+ int bin = n - 1; // current non-buffered bin or -1 if none
+
+ for (int i = bin;;) { // start upwards sweep
+ int fh; Node f;
+ if ((f = tabAt(tab, i)) == null) {
+ if (bin >= 0) { // Unbuffered; no lock needed (or available)
+ if (!casTabAt(tab, i, f, fwd))
+ continue;
+ }
+ else { // transiently use a locked forwarding node
+ Node g = new Node(MOVED|LOCKED, nextTab, null, null);
+ if (!casTabAt(tab, i, f, g))
+ continue;
+ setTabAt(nextTab, i, null);
+ setTabAt(nextTab, i + n, null);
+ setTabAt(tab, i, fwd);
+ if (!g.casHash(MOVED|LOCKED, MOVED)) {
+ g.hash = MOVED;
+ synchronized (g) { g.notifyAll(); }
+ }
+ }
+ }
+ else if ((fh = f.hash) == MOVED) {
+ Object fk = f.key;
+ if (fk instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ boolean validated = false;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ splitTreeBin(nextTab, i, t);
+ setTabAt(tab, i, fwd);
+ }
+ } finally {
+ t.release(0);
+ }
+ if (!validated)
+ continue;
+ }
+ }
+ else if ((fh & LOCKED) == 0 && f.casHash(fh, fh|LOCKED)) {
+ boolean validated = false;
+ try { // split to lo and hi lists; copying as needed
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ splitBin(nextTab, i, f);
+ setTabAt(tab, i, fwd);
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (!validated)
+ continue;
+ }
+ else {
+ if (buffer == null) // initialize buffer for revisits
+ buffer = new int[TRANSFER_BUFFER_SIZE];
+ if (bin < 0 && bufferIndex > 0) {
+ int j = buffer[--bufferIndex];
+ buffer[bufferIndex] = i;
+ i = j; // swap with another bin
+ continue;
+ }
+ if (bin < 0 || nbuffered >= TRANSFER_BUFFER_SIZE) {
+ f.tryAwaitLock(tab, i);
+ continue; // no other options -- block
+ }
+ if (rev == null) // initialize reverse-forwarder
+ rev = new Node(MOVED, tab, null, null);
+ if (tabAt(tab, i) != f || (f.hash & LOCKED) == 0)
+ continue; // recheck before adding to list
+ buffer[nbuffered++] = i;
+ setTabAt(nextTab, i, rev); // install place-holders
+ setTabAt(nextTab, i + n, rev);
+ }
+
+ if (bin > 0)
+ i = --bin;
+ else if (buffer != null && nbuffered > 0) {
+ bin = -1;
+ i = buffer[bufferIndex = --nbuffered];
+ }
+ else
+ return nextTab;
+ }
+ }
+
+ /**
+ * Splits a normal bin with list headed by e into lo and hi parts;
+ * installs in given table.
+ */
+ private static void splitBin(AtomicReferenceArray nextTab, int i, Node e) {
+ int bit = nextTab.length() >>> 1; // bit to split on
+ int runBit = e.hash & bit;
+ Node lastRun = e, lo = null, hi = null;
+ for (Node p = e.next; p != null; p = p.next) {
+ int b = p.hash & bit;
+ if (b != runBit) {
+ runBit = b;
+ lastRun = p;
+ }
+ }
+ if (runBit == 0)
+ lo = lastRun;
+ else
+ hi = lastRun;
+ for (Node p = e; p != lastRun; p = p.next) {
+ int ph = p.hash & HASH_BITS;
+ Object pk = p.key, pv = p.val;
+ if ((ph & bit) == 0)
+ lo = new Node(ph, pk, pv, lo);
+ else
+ hi = new Node(ph, pk, pv, hi);
+ }
+ setTabAt(nextTab, i, lo);
+ setTabAt(nextTab, i + bit, hi);
+ }
+
+ /**
+ * Splits a tree bin into lo and hi parts; installs in given table.
+ */
+ private static void splitTreeBin(AtomicReferenceArray nextTab, int i, TreeBin t) {
+ int bit = nextTab.length() >>> 1;
+ TreeBin lt = new TreeBin();
+ TreeBin ht = new TreeBin();
+ int lc = 0, hc = 0;
+ for (Node e = t.first; e != null; e = e.next) {
+ int h = e.hash & HASH_BITS;
+ Object k = e.key, v = e.val;
+ if ((h & bit) == 0) {
+ ++lc;
+ lt.putTreeNode(h, k, v);
+ }
+ else {
+ ++hc;
+ ht.putTreeNode(h, k, v);
+ }
+ }
+ Node ln, hn; // throw away trees if too small
+ if (lc <= (TREE_THRESHOLD >>> 1)) {
+ ln = null;
+ for (Node p = lt.first; p != null; p = p.next)
+ ln = new Node(p.hash, p.key, p.val, ln);
+ }
+ else
+ ln = new Node(MOVED, lt, null, null);
+ setTabAt(nextTab, i, ln);
+ if (hc <= (TREE_THRESHOLD >>> 1)) {
+ hn = null;
+ for (Node p = ht.first; p != null; p = p.next)
+ hn = new Node(p.hash, p.key, p.val, hn);
+ }
+ else
+ hn = new Node(MOVED, ht, null, null);
+ setTabAt(nextTab, i + bit, hn);
+ }
+
+ /**
+ * Implementation for clear. Steps through each bin, removing all
+ * nodes.
+ */
+ private final void internalClear() {
+ long delta = 0L; // negative number of deletions
+ int i = 0;
+ AtomicReferenceArray tab = table;
+ while (tab != null && i < tab.length()) {
+ int fh; Object fk;
+ Node f = tabAt(tab, i);
+ if (f == null)
+ ++i;
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ for (Node p = t.first; p != null; p = p.next) {
+ if (p.val != null) { // (currently always true)
+ p.val = null;
+ --delta;
+ }
+ }
+ t.first = null;
+ t.root = null;
+ ++i;
+ }
+ } finally {
+ t.release(0);
+ }
+ }
+ else
+ tab = (AtomicReferenceArray)fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ counter.add(delta); // opportunistically update count
+ delta = 0L;
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ try {
+ if (tabAt(tab, i) == f) {
+ for (Node e = f; e != null; e = e.next) {
+ if (e.val != null) { // (currently always true)
+ e.val = null;
+ --delta;
+ }
+ }
+ setTabAt(tab, i, null);
+ ++i;
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ }
+ }
+ if (delta != 0)
+ counter.add(delta);
+ }
+
+ /* ----------------Table Traversal -------------- */
+
+ /**
+ * Encapsulates traversal for methods such as containsValue; also
+ * serves as a base class for other iterators and bulk tasks.
+ *
+ * At each step, the iterator snapshots the key ("nextKey") and
+ * value ("nextVal") of a valid node (i.e., one that, at point of
+ * snapshot, has a non-null user value). Because val fields can
+ * change (including to null, indicating deletion), field nextVal
+ * might not be accurate at point of use, but still maintains the
+ * weak consistency property of holding a value that was once
+ * valid. To support iterator.remove, the nextKey field is not
+ * updated (nulled out) when the iterator cannot advance.
+ *
+ * Internal traversals directly access these fields, as in:
+ * {@code while (it.advance() != null) { process(it.nextKey); }}
+ *
+ * Exported iterators must track whether the iterator has advanced
+ * (in hasNext vs next) (by setting/checking/nulling field
+ * nextVal), and then extract key, value, or key-value pairs as
+ * return values of next().
+ *
+ * The iterator visits once each still-valid node that was
+ * reachable upon iterator construction. It might miss some that
+ * were added to a bin after the bin was visited, which is OK wrt
+ * consistency guarantees. Maintaining this property in the face
+ * of possible ongoing resizes requires a fair amount of
+ * bookkeeping state that is difficult to optimize away amidst
+ * volatile accesses. Even so, traversal maintains reasonable
+ * throughput.
+ *
+ * Normally, iteration proceeds bin-by-bin traversing lists.
+ * However, if the table has been resized, then all future steps
+ * must traverse both the bin at the current index as well as at
+ * (index + baseSize); and so on for further resizings. To
+ * paranoically cope with potential sharing by users of iterators
+ * across threads, iteration terminates if a bounds checks fails
+ * for a table read.
+ *
+ * This class extends ForkJoinTask to streamline parallel
+ * iteration in bulk operations (see BulkTask). This adds only an
+ * int of space overhead, which is close enough to negligible in
+ * cases where it is not needed to not worry about it. Because
+ * ForkJoinTask is Serializable, but iterators need not be, we
+ * need to add warning suppressions.
+ */
+ @SuppressWarnings("serial") static class Traverser {
+ final ConcurrentHashMapV8 map;
+ Node next; // the next entry to use
+ K nextKey; // cached key field of next
+ V nextVal; // cached val field of next
+ AtomicReferenceArray tab; // current table; updated if resized
+ int index; // index of bin to use next
+ int baseIndex; // current index of initial table
+ int baseLimit; // index bound for initial table
+ int baseSize; // initial table size
+
+ /** Creates iterator for all entries in the table. */
+ Traverser(ConcurrentHashMapV8 map) {
+ this.map = map;
+ }
+
+ /** Creates iterator for split() methods */
+ Traverser(Traverser it) {
+ ConcurrentHashMapV8 m; AtomicReferenceArray t;
+ if ((m = this.map = it.map) == null)
+ t = null;
+ else if ((t = it.tab) == null && // force parent tab initialization
+ (t = it.tab = m.table) != null)
+ it.baseLimit = it.baseSize = t.length();
+ this.tab = t;
+ this.baseSize = it.baseSize;
+ it.baseLimit = this.index = this.baseIndex =
+ ((this.baseLimit = it.baseLimit) + it.baseIndex + 1) >>> 1;
+ }
+
+ /**
+ * Advances next; returns nextVal or null if terminated.
+ * See above for explanation.
+ */
+ final V advance() {
+ Node e = next;
+ V ev = null;
+ outer: do {
+ if (e != null) // advance past used/skipped node
+ e = e.next;
+ while (e == null) { // get to next non-null bin
+ ConcurrentHashMapV8 m;
+ AtomicReferenceArray t; int b, i, n; Object ek; // checks must use locals
+ if ((t = tab) != null)
+ n = t.length();
+ else if ((m = map) != null && (t = tab = m.table) != null)
+ n = baseLimit = baseSize = t.length();
+ else
+ break outer;
+ if ((b = baseIndex) >= baseLimit ||
+ (i = index) < 0 || i >= n)
+ break outer;
+ if ((e = tabAt(t, i)) != null && e.hash == MOVED) {
+ if ((ek = e.key) instanceof TreeBin)
+ e = ((TreeBin)ek).first;
+ else {
+ tab = (AtomicReferenceArray)ek;
+ continue; // restarts due to null val
+ }
+ } // visit upper slots if present
+ index = (i += baseSize) < n ? i : (baseIndex = b + 1);
+ }
+ nextKey = (K) e.key;
+ } while ((ev = (V) e.val) == null); // skip deleted or special nodes
+ next = e;
+ return nextVal = ev;
+ }
+
+ public final void remove() {
+ Object k = nextKey;
+ if (k == null && (advance() == null || (k = nextKey) == null))
+ throw new IllegalStateException();
+ map.internalReplace(k, null, null);
+ }
+
+ public final boolean hasNext() {
+ return nextVal != null || advance() != null;
+ }
+
+ public final boolean hasMoreElements() { return hasNext(); }
+ public final void setRawResult(Object x) { }
+ public R getRawResult() { return null; }
+ public boolean exec() { return true; }
+ }
+
+ /* ---------------- Public operations -------------- */
+
+ /**
+ * Creates a new, empty map with the default initial table size (16).
+ */
+ public ConcurrentHashMapV8() {
+ this.counter = new LongAdder();
+ }
+
+ /**
+ * Creates a new, empty map with an initial table size
+ * accommodating the specified number of elements without the need
+ * to dynamically resize.
+ *
+ * @param initialCapacity The implementation performs internal
+ * sizing to accommodate this many elements.
+ * @throws IllegalArgumentException if the initial capacity of
+ * elements is negative
+ */
+ public ConcurrentHashMapV8(int initialCapacity) {
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException();
+ int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
+ MAXIMUM_CAPACITY :
+ tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
+ this.counter = new LongAdder();
+ this.sizeCtl = cap;
+ }
+
+ /**
+ * Creates a new map with the same mappings as the given map.
+ *
+ * @param m the map
+ */
+ public ConcurrentHashMapV8(Map extends K, ? extends V> m) {
+ this.counter = new LongAdder();
+ this.sizeCtl = DEFAULT_CAPACITY;
+ internalPutAll(m);
+ }
+
+ /**
+ * Creates a new, empty map with an initial table size based on
+ * the given number of elements ({@code initialCapacity}) and
+ * initial table density ({@code loadFactor}).
+ *
+ * @param initialCapacity the initial capacity. The implementation
+ * performs internal sizing to accommodate this many elements,
+ * given the specified load factor.
+ * @param loadFactor the load factor (table density) for
+ * establishing the initial table size
+ * @throws IllegalArgumentException if the initial capacity of
+ * elements is negative or the load factor is nonpositive
+ *
+ * @since 1.6
+ */
+ public ConcurrentHashMapV8(int initialCapacity, float loadFactor) {
+ this(initialCapacity, loadFactor, 1);
+ }
+
+ /**
+ * Creates a new, empty map with an initial table size based on
+ * the given number of elements ({@code initialCapacity}), table
+ * density ({@code loadFactor}), and number of concurrently
+ * updating threads ({@code concurrencyLevel}).
+ *
+ * @param initialCapacity the initial capacity. The implementation
+ * performs internal sizing to accommodate this many elements,
+ * given the specified load factor.
+ * @param loadFactor the load factor (table density) for
+ * establishing the initial table size
+ * @param concurrencyLevel the estimated number of concurrently
+ * updating threads. The implementation may use this value as
+ * a sizing hint.
+ * @throws IllegalArgumentException if the initial capacity is
+ * negative or the load factor or concurrencyLevel are
+ * nonpositive
+ */
+ public ConcurrentHashMapV8(int initialCapacity,
+ float loadFactor, int concurrencyLevel) {
+ if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
+ throw new IllegalArgumentException();
+ if (initialCapacity < concurrencyLevel) // Use at least as many bins
+ initialCapacity = concurrencyLevel; // as estimated threads
+ long size = (long)(1.0 + (long)initialCapacity / loadFactor);
+ int cap = (size >= (long)MAXIMUM_CAPACITY) ?
+ MAXIMUM_CAPACITY : tableSizeFor((int)size);
+ this.counter = new LongAdder();
+ this.sizeCtl = cap;
+ }
+
+ /**
+ * Creates a new {@link Set} backed by a ConcurrentHashMapV8
+ * from the given type to {@code Boolean.TRUE}.
+ *
+ * @return the new set
+ */
+ public static KeySetView newKeySet() {
+ return new KeySetView(new ConcurrentHashMapV8(),
+ Boolean.TRUE);
+ }
+
+ /**
+ * Creates a new {@link Set} backed by a ConcurrentHashMapV8
+ * from the given type to {@code Boolean.TRUE}.
+ *
+ * @param initialCapacity The implementation performs internal
+ * sizing to accommodate this many elements.
+ * @throws IllegalArgumentException if the initial capacity of
+ * elements is negative
+ * @return the new set
+ */
+ public static KeySetView newKeySet(int initialCapacity) {
+ return new KeySetView(new ConcurrentHashMapV8(initialCapacity),
+ Boolean.TRUE);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEmpty() {
+ return counter.sum() <= 0L; // ignore transient negative values
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int size() {
+ long n = counter.sum();
+ return ((n < 0L) ? 0 :
+ (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
+ (int)n);
+ }
+
+ /**
+ * Returns the number of mappings. This method should be used
+ * instead of {@link #size} because a ConcurrentHashMapV8 may
+ * contain more mappings than can be represented as an int. The
+ * value returned is a snapshot; the actual count may differ if
+ * there are ongoing concurrent insertions or removals.
+ *
+ * @return the number of mappings
+ */
+ public long mappingCount() {
+ long n = counter.sum();
+ return (n < 0L) ? 0L : n; // ignore transient negative values
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped,
+ * or {@code null} if this map contains no mapping for the key.
+ *
+ *
More formally, if this map contains a mapping from a key
+ * {@code k} to a value {@code v} such that {@code key.equals(k)},
+ * then this method returns {@code v}; otherwise it returns
+ * {@code null}. (There can be at most one such mapping.)
+ *
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V get(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return (V)internalGet(key);
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped,
+ * or the given defaultValue if this map contains no mapping for the key.
+ *
+ * @param key the key
+ * @param defaultValue the value to return if this map contains
+ * no mapping for the given key
+ * @return the mapping for the key, if present; else the defaultValue
+ * @throws NullPointerException if the specified key is null
+ */
+ @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) {
+ if (key == null)
+ throw new NullPointerException();
+ V v = (V) internalGet(key);
+ return v == null ? defaultValue : v;
+ }
+
+ /**
+ * Tests if the specified object is a key in this table.
+ *
+ * @param key possible key
+ * @return {@code true} if and only if the specified object
+ * is a key in this table, as determined by the
+ * {@code equals} method; {@code false} otherwise
+ * @throws NullPointerException if the specified key is null
+ */
+ public boolean containsKey(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ return internalGet(key) != null;
+ }
+
+ /**
+ * Returns {@code true} if this map maps one or more keys to the
+ * specified value. Note: This method may require a full traversal
+ * of the map, and is much slower than method {@code containsKey}.
+ *
+ * @param value value whose presence in this map is to be tested
+ * @return {@code true} if this map maps one or more keys to the
+ * specified value
+ * @throws NullPointerException if the specified value is null
+ */
+ public boolean containsValue(Object value) {
+ if (value == null)
+ throw new NullPointerException();
+ Object v;
+ Traverser it = new Traverser(this);
+ while ((v = it.advance()) != null) {
+ if (v == value || value.equals(v))
+ return true;
+ }
+ return false;
+ }
+
+ public K findKey(Object value) {
+ if (value == null)
+ throw new NullPointerException();
+ Object v;
+ Traverser