aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGenadi Samokovarov <gsamokovarov@gmail.com>2016-05-30 14:53:03 +0300
committerGenadi Samokovarov <gsamokovarov@gmail.com>2016-05-30 14:53:03 +0300
commita11a3861b474ef642ac48796572d6276436c7eb0 (patch)
tree5e7da0567079981991e9a23696c7d20faf4c9f2b
parente6ed3aaf437887bc25a1f715f21c5ca3ebbc966f (diff)
parent3f2e83d964fcb4cd7f7f2ed8fb2b2592ffc57647 (diff)
downloadrails-a11a3861b474ef642ac48796572d6276436c7eb0.tar.gz
rails-a11a3861b474ef642ac48796572d6276436c7eb0.tar.bz2
rails-a11a3861b474ef642ac48796572d6276436c7eb0.zip
Merge branch 'master' into always-inherit-from-application-record
-rw-r--r--.travis.yml25
-rw-r--r--CONTRIBUTING.md4
-rw-r--r--Gemfile12
-rw-r--r--Gemfile.lock276
-rw-r--r--RAILS_VERSION2
-rw-r--r--RELEASING_RAILS.md24
-rw-r--r--actioncable/CHANGELOG.md81
-rw-r--r--actioncable/README.md21
-rw-r--r--actioncable/Rakefile46
-rw-r--r--actioncable/app/assets/javascripts/action_cable.coffee.erb1
-rw-r--r--actioncable/blade.yml34
-rw-r--r--actioncable/lib/action_cable/channel/naming.rb1
-rw-r--r--actioncable/lib/action_cable/channel/streams.rb4
-rw-r--r--actioncable/lib/action_cable/connection/base.rb2
-rw-r--r--actioncable/lib/action_cable/engine.rb2
-rw-r--r--actioncable/lib/action_cable/gem_version.rb4
-rw-r--r--actioncable/lib/action_cable/server/base.rb6
-rw-r--r--actioncable/lib/rails/generators/channel/channel_generator.rb3
-rw-r--r--actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb3
-rw-r--r--actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb3
-rw-r--r--actioncable/lib/rails/generators/channel/templates/assets/channel.js18
-rw-r--r--actioncable/package.json24
-rw-r--r--actioncable/test/javascript/src/test.coffee3
-rw-r--r--actioncable/test/javascript/src/test_helpers/index.coffee5
-rw-r--r--actioncable/test/javascript/src/test_helpers/mock_websocket.coffee21
-rw-r--r--actioncable/test/javascript/src/unit/action_cable_test.coffee24
-rw-r--r--actioncable/test/javascript/src/unit/consumer_test.coffee31
-rw-r--r--actioncable/test/javascript/vendor/mock-socket.js4533
-rw-r--r--actionmailer/CHANGELOG.md134
-rw-r--r--actionmailer/Rakefile1
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionmailer/lib/action_mailer/base.rb3
-rw-r--r--actionmailer/lib/action_mailer/delivery_job.rb21
-rw-r--r--actionmailer/lib/action_mailer/gem_version.rb4
-rw-r--r--actionmailer/lib/action_mailer/message_delivery.rb59
-rw-r--r--actionmailer/lib/action_mailer/rescuable.rb27
-rw-r--r--actionmailer/test/caching_test.rb48
-rw-r--r--actionmailer/test/fixtures/caching_mailer/fragment_caching_options.html.erb3
-rw-r--r--actionmailer/test/fixtures/caching_mailer/multipart_cache.html.erb3
-rw-r--r--actionmailer/test/fixtures/caching_mailer/multipart_cache.text.erb3
-rw-r--r--actionmailer/test/mailers/caching_mailer.rb8
-rw-r--r--actionmailer/test/mailers/delayed_mailer.rb20
-rw-r--r--actionmailer/test/message_delivery_test.rb51
-rw-r--r--actionpack/CHANGELOG.md813
-rw-r--r--actionpack/Rakefile1
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb4
-rw-r--r--actionpack/lib/action_controller/base.rb9
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb9
-rw-r--r--actionpack/lib/action_controller/metal/live.rb16
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb4
-rw-r--r--actionpack/lib/action_controller/metal/rescue.rb17
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb18
-rw-r--r--actionpack/lib/action_controller/renderer.rb4
-rw-r--r--actionpack/lib/action_controller/test_case.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb33
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb20
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb8
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb7
-rw-r--r--actionpack/lib/action_dispatch/testing/assertion_response.rb16
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb5
-rw-r--r--actionpack/lib/action_dispatch/testing/test_request.rb22
-rw-r--r--actionpack/lib/action_pack/gem_version.rb4
-rw-r--r--actionpack/test/assertions/response_assertions_test.rb2
-rw-r--r--actionpack/test/controller/flash_hash_test.rb4
-rw-r--r--actionpack/test/controller/helper_test.rb16
-rw-r--r--actionpack/test/controller/live_stream_test.rb3
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb31
-rw-r--r--actionpack/test/controller/rescue_test.rb31
-rw-r--r--actionpack/test/controller/routing_test.rb2
-rw-r--r--actionpack/test/controller/test_case_test.rb4
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb89
-rw-r--r--actionpack/test/dispatch/live_response_test.rb2
-rw-r--r--actionpack/test/dispatch/request/session_test.rb26
-rw-r--r--actionpack/test/dispatch/request_test.rb31
-rw-r--r--actionpack/test/dispatch/response_test.rb33
-rw-r--r--actionpack/test/dispatch/test_request_test.rb27
-rw-r--r--actionview/CHANGELOG.md318
-rw-r--r--actionview/Rakefile1
-rw-r--r--actionview/actionview.gemspec2
-rw-r--r--actionview/lib/action_view/digestor.rb9
-rw-r--r--actionview/lib/action_view/gem_version.rb4
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb6
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb4
-rw-r--r--actionview/lib/action_view/lookup_context.rb12
-rw-r--r--actionview/lib/action_view/railtie.rb12
-rw-r--r--actionview/lib/action_view/renderer/template_renderer.rb6
-rw-r--r--actionview/lib/action_view/template/resolver.rb17
-rw-r--r--actionview/test/actionpack/abstract/layouts_test.rb191
-rw-r--r--actionview/test/template/digestor_test.rb46
-rw-r--r--actionview/test/template/form_options_helper_test.rb2
-rw-r--r--actionview/test/template/form_tag_helper_test.rb10
-rw-r--r--activejob/CHANGELOG.md160
-rw-r--r--activejob/Rakefile4
-rw-r--r--activejob/lib/active_job/arguments.rb5
-rw-r--r--activejob/lib/active_job/callbacks.rb4
-rw-r--r--activejob/lib/active_job/execution.rb2
-rw-r--r--activejob/lib/active_job/gem_version.rb4
-rw-r--r--activejob/test/support/integration/test_case_helpers.rb4
-rw-r--r--activemodel/CHANGELOG.md147
-rw-r--r--activemodel/Rakefile1
-rw-r--r--activemodel/lib/active_model.rb1
-rw-r--r--activemodel/lib/active_model/errors.rb17
-rw-r--r--activemodel/lib/active_model/gem_version.rb4
-rw-r--r--activemodel/lib/active_model/type/integer.rb2
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb6
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb6
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb2
-rw-r--r--activemodel/lib/active_model/validator.rb2
-rw-r--r--activemodel/test/cases/errors_test.rb2
-rw-r--r--activemodel/test/cases/type/integer_test.rb12
-rw-r--r--activemodel/test/cases/type/unsigned_integer_test.rb2
-rw-r--r--activemodel/test/cases/validations/length_validation_test.rb2
-rw-r--r--activerecord/CHANGELOG.md2113
-rw-r--r--activerecord/Rakefile1
-rw-r--r--activerecord/lib/active_record/associations.rb8
-rw-r--r--activerecord/lib/active_record/associations/association.rb3
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb3
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb1
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb15
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb16
-rw-r--r--activerecord/lib/active_record/attribute.rb2
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb2
-rw-r--r--activerecord/lib/active_record/attributes.rb7
-rw-r--r--activerecord/lib/active_record/callbacks.rb6
-rw-r--r--activerecord/lib/active_record/coders/json.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb98
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb30
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_handling.rb47
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/errors.rb10
-rw-r--r--activerecord/lib/active_record/gem_version.rb4
-rw-r--r--activerecord/lib/active_record/internal_metadata.rb2
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb7
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb6
-rw-r--r--activerecord/lib/active_record/migration.rb18
-rw-r--r--activerecord/lib/active_record/migration/compatibility.rb15
-rw-r--r--activerecord/lib/active_record/model_schema.rb2
-rw-r--r--activerecord/lib/active_record/querying.rb2
-rw-r--r--activerecord/lib/active_record/relation/batches/batch_enumerator.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb8
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb20
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb4
-rw-r--r--activerecord/lib/active_record/suppressor.rb3
-rw-r--r--activerecord/lib/active_record/table_metadata.rb3
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb4
-rw-r--r--activerecord/lib/active_record/type/serialized.rb8
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/transaction_test.rb62
-rw-r--r--activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb7
-rw-r--r--activerecord/test/cases/adapters/postgresql/transaction_test.rb72
-rw-r--r--activerecord/test/cases/adapters/postgresql/type_lookup_test.rb4
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb13
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb4
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb18
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb2
-rw-r--r--activerecord/test/cases/attributes_test.rb11
-rw-r--r--activerecord/test/cases/base_test.rb4
-rw-r--r--activerecord/test/cases/calculations_test.rb4
-rw-r--r--activerecord/test/cases/coders/json_test.rb15
-rw-r--r--activerecord/test/cases/comment_test.rb11
-rw-r--r--activerecord/test/cases/connection_adapters/adapter_leasing_test.rb2
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb71
-rw-r--r--activerecord/test/cases/connection_adapters/connection_specification_test.rb2
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb10
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb2
-rw-r--r--activerecord/test/cases/connection_pool_test.rb7
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb21
-rw-r--r--activerecord/test/cases/finder_test.rb2
-rw-r--r--activerecord/test/cases/fixtures_test.rb2
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb6
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb2
-rw-r--r--activerecord/test/cases/migration_test.rb25
-rw-r--r--activerecord/test/cases/multiple_db_test.rb11
-rw-r--r--activerecord/test/cases/query_cache_test.rb5
-rw-r--r--activerecord/test/cases/quoting_test.rb6
-rw-r--r--activerecord/test/cases/relations_test.rb23
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb4
-rw-r--r--activerecord/test/cases/schema_loading_test.rb52
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb8
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb33
-rw-r--r--activerecord/test/cases/suppressor_test.rb12
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb2
-rw-r--r--activesupport/CHANGELOG.md772
-rw-r--r--activesupport/Rakefile1
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support/array_inquirer.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/array/grouping.rb17
-rw-r--r--activesupport/lib/active_support/core_ext/big_decimal/conversions.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/zones.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/hash/compact.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb65
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/conversions.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/object/blank.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb5
-rw-r--r--activesupport/lib/active_support/deprecation.rb2
-rw-r--r--activesupport/lib/active_support/duration.rb6
-rw-r--r--activesupport/lib/active_support/gem_version.rb4
-rw-r--r--activesupport/lib/active_support/rescuable.rb142
-rw-r--r--activesupport/lib/active_support/xml_mini.rb33
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb4
-rw-r--r--activesupport/test/abstract_unit.rb3
-rw-r--r--activesupport/test/core_ext/array/conversions_test.rb6
-rw-r--r--activesupport/test/core_ext/array/grouping_test.rb5
-rw-r--r--activesupport/test/core_ext/bigdecimal_test.rb2
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb17
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb10
-rw-r--r--activesupport/test/core_ext/duration_test.rb11
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb78
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb34
-rw-r--r--activesupport/test/core_ext/module_test.rb59
-rw-r--r--activesupport/test/core_ext/numeric_ext_test.rb7
-rw-r--r--activesupport/test/core_ext/object/deep_dup_test.rb2
-rw-r--r--activesupport/test/core_ext/object/json_gem_encoding_test.rb2
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb38
-rw-r--r--activesupport/test/deprecation_test.rb2
-rw-r--r--activesupport/test/inflector_test_cases.rb3
-rw-r--r--activesupport/test/rescuable_test.rb21
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb26
-rwxr-xr-xci/travis.rb7
-rw-r--r--guides/CHANGELOG.md46
-rw-r--r--guides/bug_report_templates/action_controller_gem.rb2
-rw-r--r--guides/bug_report_templates/active_record_gem.rb2
-rw-r--r--guides/bug_report_templates/active_record_master.rb4
-rw-r--r--guides/bug_report_templates/generic_gem.rb2
-rw-r--r--guides/source/2_2_release_notes.md22
-rw-r--r--guides/source/2_3_release_notes.md38
-rw-r--r--guides/source/3_0_release_notes.md16
-rw-r--r--guides/source/5_0_release_notes.md113
-rw-r--r--guides/source/action_cable_overview.md350
-rw-r--r--guides/source/action_controller_overview.md2
-rw-r--r--guides/source/active_record_postgresql.md2
-rw-r--r--guides/source/active_record_querying.md2
-rw-r--r--guides/source/active_record_validations.md25
-rw-r--r--guides/source/active_support_core_extensions.md80
-rw-r--r--guides/source/api_app.md15
-rw-r--r--guides/source/api_documentation_guidelines.md2
-rw-r--r--guides/source/asset_pipeline.md28
-rw-r--r--guides/source/association_basics.md8
-rw-r--r--guides/source/autoloading_and_reloading_constants.md7
-rw-r--r--guides/source/caching_with_rails.md6
-rw-r--r--guides/source/command_line.md2
-rw-r--r--guides/source/configuring.md193
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md8
-rw-r--r--guides/source/debugging_rails_applications.md325
-rw-r--r--guides/source/documents.yaml4
-rw-r--r--guides/source/getting_started.md17
-rw-r--r--guides/source/rails_on_rack.md4
-rw-r--r--guides/source/security.md38
-rw-r--r--guides/source/testing.md2
-rw-r--r--guides/source/upgrading_ruby_on_rails.md12
-rw-r--r--guides/source/working_with_javascript_in_rails.md2
-rw-r--r--railties/CHANGELOG.md523
-rw-r--r--railties/RDOC_MAIN.rdoc6
-rw-r--r--railties/Rakefile1
-rw-r--r--railties/lib/rails/application.rb11
-rw-r--r--railties/lib/rails/engine.rb2
-rw-r--r--railties/lib/rails/gem_version.rb4
-rw-r--r--railties/lib/rails/generators/app_base.rb4
-rw-r--r--railties/lib/rails/generators/named_base.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb33
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/cable.yml9
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/active_record_belongs_to_required_by_default.rb)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/callback_terminator.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/callback_terminator.rb)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/per_form_csrf_tokens.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/per_form_csrf_tokens.rb)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/request_forgery_protection.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/request_forgery_protection.rb)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/ssl_options.rb6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/to_time_preserves_timezone.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/to_time_preserves_timezone.rb)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/ssl_options.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/secrets.yml8
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb3
-rw-r--r--railties/lib/rails/generators/rails/scaffold/templates/scaffold.css12
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb6
-rw-r--r--railties/test/application/assets_test.rb2
-rw-r--r--railties/test/application/configuration_test.rb27
-rw-r--r--railties/test/application/middleware_test.rb2
-rw-r--r--railties/test/application/rake_test.rb4
-rw-r--r--railties/test/generators/api_app_generator_test.rb12
-rw-r--r--railties/test/generators/app_generator_test.rb34
-rw-r--r--railties/test/generators/channel_generator_test.rb24
-rw-r--r--railties/test/generators/scaffold_generator_test.rb4
-rw-r--r--railties/test/isolation/abstract_unit.rb2
-rw-r--r--tasks/release.rb36
-rw-r--r--version.rb4
323 files changed, 8063 insertions, 6690 deletions
diff --git a/.travis.yml b/.travis.yml
index 06b7fa87b2..461bd172c1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,9 +16,11 @@ addons:
postgresql: "9.4"
bundler_args: --without test --jobs 3 --retry 3
-
+#FIXME: Remove bundler uninstall on Travis when https://github.com/bundler/bundler/issues/4493 is fixed.
before_install:
- - gem install bundler
+ - rvm @global do gem uninstall bundler --all --ignore-dependencies --executables
+ - rvm @global do gem install bundler -v '1.11.2'
+ - bundle --version
- "rm ${BUNDLE_GEMFILE}.lock"
- "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/kr/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)"
- "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd"
@@ -26,6 +28,11 @@ before_install:
before_script:
- bundle update
+ # Set Sauce Labs username and access key. Obfuscated, purposefully not encrypted.
+ # Decodes to e.g. `export VARIABLE=VALUE`
+ - $(base64 --decode <<< "ZXhwb3J0IFNBVUNFX0FDQ0VTU19LRVk9YTAzNTM0M2YtZTkyMi00MGIzLWFhM2MtMDZiM2VhNjM1YzQ4")
+ - $(base64 --decode <<< "ZXhwb3J0IFNBVUNFX1VTRVJOQU1FPXJ1YnlvbnJhaWxz")
+
script: 'ci/travis.rb'
env:
@@ -34,7 +41,10 @@ env:
- "GEM=ap"
- "GEM=ac"
- "GEM=ac FAYE=1"
+ - "GEM=ac:integration"
+ - "GEM=ac:integration FAYE=1"
- "GEM=am,amo,as,av,aj"
+ - "GEM=as PRESERVE_TIMEZONES=1"
- "GEM=ar:mysql2"
- "GEM=ar:sqlite3"
- "GEM=ar:postgresql"
@@ -42,14 +52,14 @@ env:
- "GEM=guides"
rvm:
- - 2.2.4
- - 2.3.0
+ - 2.2.5
+ - 2.3.1
- ruby-head
matrix:
include:
# Latest compiled version in http://rubies.travis-ci.org
- - rvm: 2.3.0
+ - rvm: 2.3.1
env:
- "GEM=ar:mysql2"
addons:
@@ -62,6 +72,8 @@ matrix:
allow_failures:
- rvm: ruby-head
- rvm: jruby-9.0.5.0
+ - env: "GEM=ac:integration"
+ - env: "GEM=ac:integration FAYE=1"
fast_finish: true
notifications:
@@ -76,3 +88,6 @@ notifications:
on_failure: always
rooms:
- secure: "YA1alef1ESHWGFNVwvmVGCkMe4cUy4j+UcNvMUESraceiAfVyRMAovlQBGs6\n9kBRm7DHYBUXYC2ABQoJbQRLDr/1B5JPf/M8+Qd7BKu8tcDC03U01SMHFLpO\naOs/HLXcDxtnnpL07tGVsm0zhMc5N8tq4/L3SHxK7Vi+TacwQzI="
+
+git:
+ depth: 1
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 961b48733c..f6ebef7e89 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -21,6 +21,10 @@
* Before submitting, please read the [Contributing to Ruby on Rails](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html) guide to know more about coding conventions and benchmarks.
+#### **Did you fix whitespace, format code, or make a purely cosmetic patch?**
+
+Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of Rails will generally not be accepted (read more about [our rationales behind this decision](https://github.com/rails/rails/pull/13771#issuecomment-32746700)).
+
#### **Do you intend to add a new feature or change an existing one?**
* Suggest your change in the [rubyonrails-core mailing list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core) and start writing code.
diff --git a/Gemfile b/Gemfile
index 64e467604b..858aa55916 100644
--- a/Gemfile
+++ b/Gemfile
@@ -11,7 +11,7 @@ gem 'mocha', '~> 0.14', require: false
gem 'rack-cache', '~> 1.2'
gem 'jquery-rails'
-gem 'coffee-rails', '~> 4.1.0'
+gem 'coffee-rails', github: 'rails/coffee-rails'
gem 'turbolinks', github: 'turbolinks/turbolinks-rails'
# require: false so bcrypt is loaded only when has_secure_password is used.
@@ -43,14 +43,15 @@ group :job do
gem 'resque-scheduler', require: false
gem 'sidekiq', require: false
gem 'sucker_punch', require: false
- gem 'delayed_job', require: false
+ gem 'delayed_job', require: false, github: 'collectiveidea/delayed_job'
gem 'queue_classic', github: "QueueClassic/queue_classic", branch: 'master', require: false, platforms: :ruby
gem 'sneakers', require: false
gem 'que', require: false
gem 'backburner', require: false
- gem 'qu-rails', github: "bkeepers/qu", branch: "master", require: false
+ #TODO: add qu after it support Rails 5.1
+ # gem 'qu-rails', github: "bkeepers/qu", branch: "master", require: false
gem 'qu-redis', require: false
- gem 'delayed_job_active_record', require: false
+ gem 'delayed_job_active_record', require: false, github: 'collectiveidea/delayed_job_active_record'
gem 'sequel', require: false
end
@@ -63,6 +64,9 @@ group :cable do
gem 'redis', require: false
gem 'faye-websocket', require: false
+
+ gem 'blade', '~> 0.5.5', require: false
+ gem 'blade-sauce_labs_plugin', github: 'javan/blade-sauce_labs_plugin', require: false
end
# Add your own local bundler stuff.
diff --git a/Gemfile.lock b/Gemfile.lock
index 3bbaee414f..7cddaa7e3b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,30 +1,49 @@
GIT
remote: git://github.com/QueueClassic/queue_classic.git
- revision: 2e3b624f3043849751b24a758d8c156337ead862
+ revision: c26f2c9f6f6133b946fbcdd7b7ec905a4aca9f94
branch: master
specs:
queue_classic (3.2.0.RC1)
pg (>= 0.17, < 0.19)
GIT
- remote: git://github.com/bkeepers/qu.git
- revision: d098e2657c92e89a6413bebd9c033930759c061f
- branch: master
+ remote: git://github.com/collectiveidea/delayed_job.git
+ revision: 71f1d5faf934d3057abca942f0d410327bc69087
specs:
- qu (0.2.0)
- qu-rails (0.2.0)
- qu (= 0.2.0)
- railties (>= 3.2, < 5)
- qu-redis (0.2.0)
- qu (= 0.2.0)
- redis-namespace
+ delayed_job (4.1.1)
+ activesupport (>= 3.0, < 5.1)
+
+GIT
+ remote: git://github.com/collectiveidea/delayed_job_active_record.git
+ revision: 61e688e03b2ef4004b08de6d1e0a123fda8fffad
+ specs:
+ delayed_job_active_record (4.1.0)
+ activerecord (>= 3.0, < 5.1)
+ delayed_job (>= 3.0, < 5)
+
+GIT
+ remote: git://github.com/javan/blade-sauce_labs_plugin.git
+ revision: dd4230c556aaa19b62cca757d6faeb2bb6bf95e7
+ specs:
+ blade-sauce_labs_plugin (0.5.1)
+ childprocess
+ faraday
+ selenium-webdriver
+
+GIT
+ remote: git://github.com/rails/coffee-rails.git
+ revision: aa2e623cbda4f3c789a0a15d1f707239e68f5736
+ specs:
+ coffee-rails (4.1.1)
+ coffee-script (>= 2.2.0)
+ railties (>= 4.0.0, < 5.2.x)
GIT
remote: git://github.com/sass/sass.git
- revision: ac8e7aa8e99c425237c708f347f76495a2e18519
+ revision: 3fda1cbe70d615e7ef96e28db4fd1f8a3ebb5505
branch: stable
specs:
- sass (3.4.21)
+ sass (3.4.22)
GIT
remote: git://github.com/turbolinks/turbolinks-rails.git
@@ -36,58 +55,58 @@ GIT
PATH
remote: .
specs:
- actioncable (5.0.0.beta3)
- actionpack (= 5.0.0.beta3)
+ actioncable (5.1.0.alpha)
+ actionpack (= 5.1.0.alpha)
nio4r (~> 1.2)
websocket-driver (~> 0.6.1)
- actionmailer (5.0.0.beta3)
- actionpack (= 5.0.0.beta3)
- actionview (= 5.0.0.beta3)
- activejob (= 5.0.0.beta3)
+ actionmailer (5.1.0.alpha)
+ actionpack (= 5.1.0.alpha)
+ actionview (= 5.1.0.alpha)
+ activejob (= 5.1.0.alpha)
mail (~> 2.5, >= 2.5.4)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- actionpack (5.0.0.beta3)
- actionview (= 5.0.0.beta3)
- activesupport (= 5.0.0.beta3)
+ rails-dom-testing (~> 2.0)
+ actionpack (5.1.0.alpha)
+ actionview (= 5.1.0.alpha)
+ activesupport (= 5.1.0.alpha)
rack (~> 2.x)
rack-test (~> 0.6.3)
- rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (5.0.0.beta3)
- activesupport (= 5.0.0.beta3)
+ actionview (5.1.0.alpha)
+ activesupport (= 5.1.0.alpha)
builder (~> 3.1)
erubis (~> 2.7.0)
- rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- activejob (5.0.0.beta3)
- activesupport (= 5.0.0.beta3)
+ activejob (5.1.0.alpha)
+ activesupport (= 5.1.0.alpha)
globalid (>= 0.3.6)
- activemodel (5.0.0.beta3)
- activesupport (= 5.0.0.beta3)
- activerecord (5.0.0.beta3)
- activemodel (= 5.0.0.beta3)
- activesupport (= 5.0.0.beta3)
+ activemodel (5.1.0.alpha)
+ activesupport (= 5.1.0.alpha)
+ activerecord (5.1.0.alpha)
+ activemodel (= 5.1.0.alpha)
+ activesupport (= 5.1.0.alpha)
arel (~> 7.0)
- activesupport (5.0.0.beta3)
- concurrent-ruby (~> 1.0)
+ activesupport (5.1.0.alpha)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
- rails (5.0.0.beta3)
- actioncable (= 5.0.0.beta3)
- actionmailer (= 5.0.0.beta3)
- actionpack (= 5.0.0.beta3)
- actionview (= 5.0.0.beta3)
- activejob (= 5.0.0.beta3)
- activemodel (= 5.0.0.beta3)
- activerecord (= 5.0.0.beta3)
- activesupport (= 5.0.0.beta3)
+ rails (5.1.0.alpha)
+ actioncable (= 5.1.0.alpha)
+ actionmailer (= 5.1.0.alpha)
+ actionpack (= 5.1.0.alpha)
+ actionview (= 5.1.0.alpha)
+ activejob (= 5.1.0.alpha)
+ activemodel (= 5.1.0.alpha)
+ activerecord (= 5.1.0.alpha)
+ activesupport (= 5.1.0.alpha)
bundler (>= 1.3.0, < 2.0)
- railties (= 5.0.0.beta3)
+ railties (= 5.1.0.alpha)
sprockets-rails (>= 2.0.0)
- railties (5.0.0.beta3)
- actionpack (= 5.0.0.beta3)
- activesupport (= 5.0.0.beta3)
+ railties (5.1.0.alpha)
+ actionpack (= 5.1.0.alpha)
+ activesupport (= 5.1.0.alpha)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
@@ -95,42 +114,74 @@ PATH
GEM
remote: https://rubygems.org/
specs:
+ addressable (2.4.0)
amq-protocol (2.0.1)
arel (7.0.0)
- backburner (1.2.0)
+ backburner (1.3.0)
beaneater (~> 1.0)
dante (> 0.1.5)
bcrypt (3.1.11)
bcrypt (3.1.11-x64-mingw32)
bcrypt (3.1.11-x86-mingw32)
beaneater (1.0.0)
- benchmark-ips (2.3.0)
+ benchmark-ips (2.6.1)
+ blade (0.5.5)
+ activesupport (>= 3.0.0)
+ blade-qunit_adapter (~> 1.20.0)
+ coffee-script
+ coffee-script-source
+ curses (~> 1.0.0)
+ eventmachine
+ faye
+ sprockets (>= 3.0)
+ sprockets-export (~> 0.9.1)
+ thin (~> 1.6.0)
+ thor (~> 0.19.1)
+ useragent (~> 0.16.7)
+ blade-qunit_adapter (1.20.0)
builder (3.2.2)
bunny (2.2.2)
amq-protocol (>= 2.0.1)
- byebug (8.2.1)
- coffee-rails (4.1.1)
- coffee-script (>= 2.2.0)
- railties (>= 4.0.0, < 5.1.x)
+ byebug (8.2.5)
+ childprocess (0.5.9)
+ ffi (~> 1.0, >= 1.0.11)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.10.0)
- concurrent-ruby (1.0.1)
+ concurrent-ruby (1.0.2)
connection_pool (2.2.0)
+ cookiejar (0.3.0)
+ curses (1.0.2)
+ daemons (1.2.3)
dalli (2.7.6)
dante (0.2.0)
- delayed_job (4.1.1)
- activesupport (>= 3.0, < 5.0)
- delayed_job_active_record (4.1.0)
- activerecord (>= 3.0, < 5)
- delayed_job (>= 3.0, < 5)
em-hiredis (0.3.1)
eventmachine (~> 1.0)
hiredis (~> 0.6.0)
+ em-http-request (1.1.3)
+ addressable (>= 2.3.4)
+ cookiejar (<= 0.3.0)
+ em-socksify (>= 0.3)
+ eventmachine (>= 1.0.3)
+ http_parser.rb (>= 0.6.0)
+ em-socksify (0.3.1)
+ eventmachine (>= 1.0.0.beta.4)
erubis (2.7.0)
- eventmachine (1.0.9.1)
+ eventmachine (1.2.0.1)
+ eventmachine (1.2.0.1-x64-mingw32)
+ eventmachine (1.2.0.1-x86-mingw32)
execjs (2.6.0)
+ faraday (0.9.2)
+ multipart-post (>= 1.2, < 3)
+ faye (1.1.2)
+ cookiejar (>= 0.3.0)
+ em-http-request (>= 0.3.0)
+ eventmachine (>= 0.12.0)
+ faye-websocket (>= 0.9.1)
+ multi_json (>= 1.0.0)
+ rack (>= 1.0.0)
+ websocket-driver (>= 0.5.1)
faye-websocket (0.10.3)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
@@ -140,32 +191,36 @@ GEM
globalid (0.3.6)
activesupport (>= 4.1.0)
hiredis (0.6.1)
+ http_parser.rb (0.6.0)
i18n (0.7.0)
- jquery-rails (4.1.0)
- rails-dom-testing (~> 1.0)
+ jquery-rails (4.1.1)
+ rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (1.8.3)
kindlerb (0.1.1)
mustache
nokogiri
- listen (3.0.5)
+ listen (3.0.7)
rb-fsevent (>= 0.9.3)
- rb-inotify (>= 0.9)
+ rb-inotify (>= 0.9.7)
loofah (2.0.3)
nokogiri (>= 1.5.9)
- mail (2.6.3)
- mime-types (>= 1.16, < 3)
+ mail (2.6.4)
+ mime-types (>= 1.16, < 4)
metaclass (0.0.4)
method_source (0.8.2)
- mime-types (2.99.1)
+ mime-types (3.0)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2016.0221)
mini_portile2 (2.0.0)
minitest (5.3.3)
mocha (0.14.0)
metaclass (~> 0.0.1)
mono_logger (1.1.0)
- multi_json (1.11.2)
- mustache (1.0.2)
+ multi_json (1.12.0)
+ multipart-post (2.0.0)
+ mustache (1.0.3)
mysql2 (0.4.4)
mysql2 (0.4.4-x64-mingw32)
mysql2 (0.4.4-x86-mingw32)
@@ -180,30 +235,34 @@ GEM
pg (0.18.4-x64-mingw32)
pg (0.18.4-x86-mingw32)
psych (2.0.17)
- puma (2.16.0)
- que (0.11.2)
+ puma (3.4.0)
+ qu (0.2.0)
+ multi_json
+ qu-redis (0.2.0)
+ qu (= 0.2.0)
+ redis-namespace
+ simple_uuid
+ que (0.11.4)
racc (1.4.14)
- rack (2.0.0.alpha)
+ rack (2.0.0.rc1)
json
- rack-cache (1.5.1)
+ rack-cache (1.6.1)
rack (>= 0.4)
rack-test (0.6.3)
rack (>= 1.0)
- rails-deprecated_sanitizer (1.0.3)
- activesupport (>= 4.2.0.alpha)
- rails-dom-testing (1.0.7)
- activesupport (>= 4.2.0.beta, < 5.0)
+ rails-dom-testing (2.0.0)
+ activesupport (>= 4.2.0, < 6.0)
nokogiri (~> 1.6.0)
- rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
- rake (11.1.1)
+ rake (11.1.2)
rb-fsevent (0.9.7)
- rb-inotify (0.9.5)
+ rb-inotify (0.9.7)
ffi (>= 0.5.0)
- rdoc (4.2.1)
+ rdoc (4.2.2)
+ json (~> 1.4)
redcarpet (3.2.3)
- redis (3.2.2)
+ redis (3.3.0)
redis-namespace (1.5.2)
redis (~> 3.0, >= 3.0.4)
resque (1.25.2)
@@ -212,23 +271,29 @@ GEM
redis-namespace (~> 1.3)
sinatra (>= 0.9.2)
vegas (~> 0.1.2)
- resque-scheduler (4.0.0)
+ resque-scheduler (4.2.0)
mono_logger (~> 1.0)
redis (~> 3.0)
resque (~> 1.25)
- rufus-scheduler (~> 3.0)
- rufus-scheduler (3.2.0)
+ rufus-scheduler (~> 3.2)
+ rubyzip (1.2.0)
+ rufus-scheduler (3.2.1)
sdoc (0.4.1)
json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0)
- sequel (4.30.0)
+ selenium-webdriver (2.53.0)
+ childprocess (~> 0.5)
+ rubyzip (~> 1.0)
+ websocket (~> 1.0)
+ sequel (4.34.0)
serverengine (1.5.11)
sigdump (~> 0.2.2)
- sidekiq (4.0.2)
+ sidekiq (4.1.2)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
redis (~> 3.2, >= 3.2.1)
- sigdump (0.2.3)
+ sigdump (0.2.4)
+ simple_uuid (0.4.0)
sinatra (1.0)
rack (>= 1.0)
sneakers (2.3.5)
@@ -236,9 +301,10 @@ GEM
serverengine (~> 1.5.11)
thor
thread (~> 0.1.7)
- sprockets (3.5.2)
+ sprockets (3.6.0)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
+ sprockets-export (0.9.1)
sprockets-rails (3.0.4)
actionpack (>= 4.0)
activesupport (>= 4.0)
@@ -246,26 +312,31 @@ GEM
sqlite3 (1.3.11)
sqlite3 (1.3.11-x64-mingw32)
sqlite3 (1.3.11-x86-mingw32)
- stackprof (0.2.8)
- sucker_punch (2.0.0)
+ stackprof (0.2.9)
+ sucker_punch (2.0.2)
concurrent-ruby (~> 1.0.0)
+ thin (1.6.2)
+ daemons (>= 1.0.9)
+ eventmachine (>= 1.0.0)
+ rack (>= 1.0.0)
thor (0.19.1)
thread (0.1.7)
thread_safe (0.3.5)
- turbolinks-source (5.0.0.beta2)
+ turbolinks-source (5.0.0.beta4)
tzinfo (1.2.2)
thread_safe (~> 0.1)
- tzinfo-data (1.2015.7)
+ tzinfo-data (1.2016.4)
tzinfo (>= 1.0.0)
- uglifier (2.7.2)
- execjs (>= 0.3.0)
- json (>= 1.8.0)
+ uglifier (3.0.0)
+ execjs (>= 0.3.0, < 3)
+ useragent (0.16.7)
vegas (0.1.11)
rack (>= 1.0.0)
w3c_validators (1.2)
json
nokogiri
wdm (0.1.1)
+ websocket (1.2.3)
websocket-driver (0.6.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
@@ -282,11 +353,13 @@ DEPENDENCIES
backburner
bcrypt (~> 3.1.11)
benchmark-ips
+ blade (~> 0.5.5)
+ blade-sauce_labs_plugin!
byebug
- coffee-rails (~> 4.1.0)
+ coffee-rails!
dalli (>= 2.2.1)
- delayed_job
- delayed_job_active_record
+ delayed_job!
+ delayed_job_active_record!
em-hiredis
faye-websocket
hiredis
@@ -300,7 +373,6 @@ DEPENDENCIES
pg (>= 0.18.0)
psych (~> 2.0)
puma
- qu-rails!
qu-redis
que
queue_classic!
diff --git a/RAILS_VERSION b/RAILS_VERSION
index d727b28ee9..8ea1016081 100644
--- a/RAILS_VERSION
+++ b/RAILS_VERSION
@@ -1 +1 @@
-5.0.0.beta3
+5.1.0.alpha
diff --git a/RELEASING_RAILS.md b/RELEASING_RAILS.md
index 037aa8c119..5ed5a8b029 100644
--- a/RELEASING_RAILS.md
+++ b/RELEASING_RAILS.md
@@ -21,7 +21,7 @@ http://travis-ci.org/rails/rails
Sam Ruby keeps a [test suite](https://github.com/rubys/awdwr) that makes
sure the code samples in his book
-([Agile Web Development with Rails](https://pragprog.com/titles/rails4/agile-web-development-with-rails-4th-edition))
+([Agile Web Development with Rails](https://pragprog.com/titles/rails5/agile-web-development-with-rails-5th-edition))
all work. These are valuable system tests
for Rails. You can check the status of these tests here:
@@ -103,18 +103,24 @@ branch.
Run `rake install` to generate the gems and install them locally. Then try
generating a new app and ensure that nothing explodes.
+Verify that Action Cable's package.json is updated with the RC version.
+
This will stop you from looking silly when you push an RC to rubygems.org and
then realize it is broken.
-### Release the gem.
+### Release to RubyGems and NPM.
+
+IMPORTANT: The Action Cable client is released as an NPM package, so you must
+have Node.js installed, have an NPM account (npmjs.com), and be an actioncable
+package owner (`npm owner ls actioncable`) to do a full release. Do not release
+until you're set up with NPM!
-IMPORTANT: Due to YAML parse problems on the rubygems.org server, it is safest
-to use Ruby 1.8 when releasing.
+Run `rake release`. This will populate the gemspecs and NPM package.json with
+the current RAILS_VERSION, commit the changes, tag it, and push the gems to
+rubygems.org.
-Run `rake release`. This will populate the gemspecs with data from
-RAILS_VERSION, commit the changes, tag it, and push the gems to rubygems.org.
-Here are the commands that `rake release` should use, so you can understand
-what to do in case anything goes wrong:
+Here are the commands that `rake release` uses so you can understand what to do
+in case anything goes wrong:
```
$ rake all:build
@@ -122,7 +128,7 @@ $ git commit -am'updating RAILS_VERSION'
$ git tag -m 'v3.0.10.rc1 release' v3.0.10.rc1
$ git push
$ git push --tags
-$ for i in $(ls pkg); do gem push $i; done
+$ for i in $(ls pkg); do gem push $i; npm publish; done
```
### Send Rails release announcements
diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md
index 5162a31cf8..a767857607 100644
--- a/actioncable/CHANGELOG.md
+++ b/actioncable/CHANGELOG.md
@@ -1,81 +1,2 @@
-* WebSocket protocol negotiation.
- Introduces an Action Cable protocol version that moves independently
- of and, hopefully, more slowly than Action Cable itself. Client sockets
- negotiate a protocol with the Cable server using WebSockets' native
- subprotocol support:
- * https://tools.ietf.org/html/rfc6455#section-1.9
- * https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Subprotocols
-
- If they can't negotiate a compatible protocol (usually due to upgrading
- the Cable server with a browser still running old JavaScript) then the
- client knows to disconnect, cease retrying, and tell the app that it hit
- a protocol mismatch.
-
- This allows us to evolve the Action Cable message format, handshaking,
- pings, acknowledgements, and more without breaking older clients'
- expectations of server behavior.
-
- *Daniel Rhodes*
-
-* Pubsub: automatic stream decoding.
-
- stream_for @room, coder: ActiveSupport::JSON do |message|
- # `message` is a Ruby hash here instead of a JSON string
-
- The `coder` must respond to `#decode`. Defaults to `coder: nil`
- which skips decoding entirely.
-
- *Jeremy Daer*
-
-* Add ActiveSupport::Notifications to ActionCable::Channel.
-
- *Matthew Wear*
-
-* Safely support autoloading and class unloading, by preventing concurrent
- loads, and disconnecting all cables during reload.
-
- *Matthew Draper*
-
-* Ensure ActionCable behaves correctly for non-string queue names.
-
- *Jay Hayes*
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* Added `em_redis_connector` and `redis_connector` to
- `ActionCable::SubscriptionAdapter::EventedRedis` and added `redis_connector`
- to `ActionCable::SubscriptionAdapter::Redis`, so you can overwrite with your
- own initializers. This is used when you want to use different-than-standard
- Redis adapters, like for Makara distributed Redis.
-
- *DHH*
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* Support PostgreSQL pubsub adapter.
-
- *Jon Moss*
-
-* Remove EventMachine dependency.
-
- *Matthew Draper*
-
-* Remove Celluloid dependency.
-
- *Mike Perham*
-
-* Create notion of an `ActionCable::SubscriptionAdapter`.
- Separate out Redis functionality into
- `ActionCable::SubscriptionAdapter::Redis`, and add a
- PostgreSQL adapter as well. Configuration file for
- ActionCable was changed from`config/redis/cable.yml` to
- `config/cable.yml`.
-
- *Jon Moss*
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* Added to Rails!
-
- *DHH*
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actioncable/CHANGELOG.md) for previous changes.
diff --git a/actioncable/README.md b/actioncable/README.md
index fe4d213485..71fec714f5 100644
--- a/actioncable/README.md
+++ b/actioncable/README.md
@@ -86,12 +86,17 @@ end
The client-side needs to setup a consumer instance of this connection. That's done like so:
-```coffeescript
-# app/assets/javascripts/cable.coffee
-#= require action_cable
+```js
+// app/assets/javascripts/cable.js
+//= require action_cable
+//= require_self
+//= require_tree ./channels
+
+(function() {
+ this.App || (this.App = {});
-@App = {}
-App.cable = ActionCable.createConsumer("ws://cable.example.com")
+ App.cable = ActionCable.createConsumer("ws://cable.example.com");
+}).call(this);
```
The `ws://cable.example.com` address must point to your Action Cable server(s), and it
@@ -293,7 +298,7 @@ The rebroadcast will be received by all connected clients, _including_ the clien
### More complete examples
-See the [rails/actioncable-examples](http://github.com/rails/actioncable-examples) repository for a full example of how to setup Action Cable in a Rails app, and how to add channels.
+See the [rails/actioncable-examples](https://github.com/rails/actioncable-examples) repository for a full example of how to setup Action Cable in a Rails app, and how to add channels.
## Configuration
@@ -385,7 +390,7 @@ Rails.application.config.action_cable.log_tags = [
For a full list of all configuration options, see the `ActionCable::Server::Configuration` class.
-Also note that your server must provide at least the same number of database connections as you have workers. The default worker pool is set to 100, so that means you have to make at least that available. You can change that in `config/database.yml` through the `pool` attribute.
+Also note that your server must provide at least the same number of database connections as you have workers. The default worker pool is set to 4, so that means you have to make at least that available. You can change that in `config/database.yml` through the `pool` attribute.
## Running the cable server
@@ -403,7 +408,7 @@ run ActionCable.server
```
Then you start the server using a binstub in bin/cable ala:
-```
+```sh
#!/bin/bash
bundle exec puma -p 28080 cable/config.ru
```
diff --git a/actioncable/Rakefile b/actioncable/Rakefile
index 5ba7b7f7f6..a72142deb5 100644
--- a/actioncable/Rakefile
+++ b/actioncable/Rakefile
@@ -1,15 +1,13 @@
require 'rake/testtask'
require 'pathname'
-require 'sprockets'
-require 'coffee-script'
require 'action_cable'
+require 'blade'
dir = File.dirname(__FILE__)
task :default => :test
task :package => "assets:compile"
-task "package:clean" => "assets:clean"
Rake::TestTask.new do |t|
t.libs << "test"
@@ -25,41 +23,19 @@ namespace :test do
sh(Gem.ruby, '-w', '-Ilib:test', file)
end or raise "Failures"
end
-end
-
-namespace :assets do
- root_path = Pathname.new(dir)
- destination_path = root_path.join("lib/assets/compiled")
-
- desc "Compile dist/action_cable.js"
- task :compile do
- puts 'Compiling Action Cable assets...'
-
- precompile_list = %w(action_cable.js)
-
- environment = Sprockets::Environment.new
- environment.gzip = false
- Pathname.glob(root_path.join("app/assets/*/")) do |subdir|
- environment.append_path subdir
- end
-
- compile_path = root_path.join("tmp/sprockets")
- compile_path.rmtree if compile_path.exist?
- compile_path.mkpath
- manifest = Sprockets::Manifest.new(environment.index, compile_path)
- manifest.compile(precompile_list)
-
- destination_path.rmtree if destination_path.exist?
- manifest.assets.each do |path, fingerprint_path|
- destination_path.join(path).dirname.mkpath
- FileUtils.cp(compile_path.join(fingerprint_path), destination_path.join(path))
+ task :integration do
+ if ENV['CI']
+ Blade.start(interface: :ci)
+ else
+ Blade.start(interface: :runner)
end
-
- puts 'Done'
end
+end
- task :clean do
- destination_path.rmtree if destination_path.exist?
+namespace :assets do
+ desc "Compile Action Cable assets"
+ task :compile do
+ Blade.build
end
end
diff --git a/actioncable/app/assets/javascripts/action_cable.coffee.erb b/actioncable/app/assets/javascripts/action_cable.coffee.erb
index f0422d9d9c..210a3ae17e 100644
--- a/actioncable/app/assets/javascripts/action_cable.coffee.erb
+++ b/actioncable/app/assets/javascripts/action_cable.coffee.erb
@@ -1,3 +1,4 @@
+#= export ActionCable
#= require_self
#= require ./action_cable/consumer
diff --git a/actioncable/blade.yml b/actioncable/blade.yml
new file mode 100644
index 0000000000..e21151099a
--- /dev/null
+++ b/actioncable/blade.yml
@@ -0,0 +1,34 @@
+load_paths:
+ - app/assets/javascripts
+ - test/javascript/src
+ - test/javascript/vendor
+
+logical_paths:
+ - test.js
+
+build:
+ logical_paths:
+ - action_cable.js
+ path: lib/assets/compiled
+ clean: true
+
+plugins:
+ sauce_labs:
+ browsers:
+ Google Chrome:
+ os: Mac, Windows
+ version: -2
+ Firefox:
+ os: Mac, Windows
+ version: -2
+ Safari:
+ platform: Mac
+ version: -3
+ Microsoft Edge:
+ version: -2
+ Internet Explorer:
+ version: 11
+ iPhone:
+ version: [9.2, 8.4]
+ Motorola Droid 4 Emulator:
+ version: [5.1, 4.4]
diff --git a/actioncable/lib/action_cable/channel/naming.rb b/actioncable/lib/action_cable/channel/naming.rb
index 4c9d53b15a..8e1b2a4af0 100644
--- a/actioncable/lib/action_cable/channel/naming.rb
+++ b/actioncable/lib/action_cable/channel/naming.rb
@@ -10,6 +10,7 @@ module ActionCable
#
# ChatChannel.channel_name # => 'chat'
# Chats::AppearancesChannel.channel_name # => 'chats:appearances'
+ # FooChats::BarAppearancesChannel.channel_name # => 'foo_chats:bar_appearances'
def channel_name
@channel_name ||= name.sub(/Channel$/, '').gsub('::',':').underscore
end
diff --git a/actioncable/lib/action_cable/channel/streams.rb b/actioncable/lib/action_cable/channel/streams.rb
index 200c9d053c..0a0a95f453 100644
--- a/actioncable/lib/action_cable/channel/streams.rb
+++ b/actioncable/lib/action_cable/channel/streams.rb
@@ -19,14 +19,14 @@ module ActionCable
# end
#
# Based on the above example, the subscribers of this channel will get whatever data is put into the,
- # let's say, `comments_for_45` broadcasting as soon as it's put there.
+ # let's say, <tt>comments_for_45</tt> broadcasting as soon as it's put there.
#
# An example broadcasting for this channel looks like so:
#
# ActionCable.server.broadcast "comments_for_45", author: 'DHH', content: 'Rails is just swell'
#
# If you have a stream that is related to a model, then the broadcasting used can be generated from the model and channel.
- # The following example would subscribe to a broadcasting like `comments:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE`
+ # The following example would subscribe to a broadcasting like <tt>comments:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE</tt>.
#
# class CommentsChannel < ApplicationCable::Channel
# def subscribed
diff --git a/actioncable/lib/action_cable/connection/base.rb b/actioncable/lib/action_cable/connection/base.rb
index cc4e0f8c8b..75c1299e36 100644
--- a/actioncable/lib/action_cable/connection/base.rb
+++ b/actioncable/lib/action_cable/connection/base.rb
@@ -2,7 +2,7 @@ require 'action_dispatch'
module ActionCable
module Connection
- # For every WebSocket the Action Cable server accepts, a Connection object will be instantiated. This instance becomes the parent
+ # For every WebSocket connection the Action Cable server accepts, a Connection object will be instantiated. This instance becomes the parent
# of all of the channel subscriptions that are created from there on. Incoming messages are then routed to these channel subscriptions
# based on an identifier sent by the Action Cable consumer. The Connection itself does not deal with any specific application logic beyond
# authentication and authorization.
diff --git a/actioncable/lib/action_cable/engine.rb b/actioncable/lib/action_cable/engine.rb
index 7dc541d00c..8ce1b24962 100644
--- a/actioncable/lib/action_cable/engine.rb
+++ b/actioncable/lib/action_cable/engine.rb
@@ -4,7 +4,7 @@ require "action_cable/helpers/action_cable_helper"
require "active_support/core_ext/hash/indifferent_access"
module ActionCable
- class Railtie < Rails::Engine # :nodoc:
+ class Engine < Rails::Engine # :nodoc:
config.action_cable = ActiveSupport::OrderedOptions.new
config.action_cable.mount_path = ActionCable::INTERNAL[:default_mount_path]
diff --git a/actioncable/lib/action_cable/gem_version.rb b/actioncable/lib/action_cable/gem_version.rb
index 67adeefaff..8ba0230d47 100644
--- a/actioncable/lib/action_cable/gem_version.rb
+++ b/actioncable/lib/action_cable/gem_version.rb
@@ -6,9 +6,9 @@ module ActionCable
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actioncable/lib/action_cable/server/base.rb b/actioncable/lib/action_cable/server/base.rb
index b1a0e11631..717a60fe4f 100644
--- a/actioncable/lib/action_cable/server/base.rb
+++ b/actioncable/lib/action_cable/server/base.rb
@@ -54,15 +54,15 @@ module ActionCable
# The worker pool is where we run connection callbacks and channel actions. We do as little as possible on the server's main thread.
# The worker pool is an executor service that's backed by a pool of threads working from a task queue. The thread pool size maxes out
- # at 4 worker threads by default. Tune the size yourself with config.action_cable.worker_pool_size.
+ # at 4 worker threads by default. Tune the size yourself with `config.action_cable.worker_pool_size`.
#
# Using Active Record, Redis, etc within your channel actions means you'll get a separate connection from each thread in the worker pool.
# Plan your deployment accordingly: 5 servers each running 5 Puma workers each running an 8-thread worker pool means at least 200 database
# connections.
#
# Also, ensure that your database connection pool size is as least as large as your worker pool size. Otherwise, workers may oversubscribe
- # the db connection pool and block while they wait for other workers to release their connections. Use a smaller worker pool or a larger
- # db connection pool instead.
+ # the database connection pool and block while they wait for other workers to release their connections. Use a smaller worker pool or a larger
+ # database connection pool instead.
def worker_pool
@worker_pool || @mutex.synchronize { @worker_pool ||= ActionCable::Server::Worker.new(max_size: config.worker_pool_size) }
end
diff --git a/actioncable/lib/rails/generators/channel/channel_generator.rb b/actioncable/lib/rails/generators/channel/channel_generator.rb
index 05fd21a954..47232252e3 100644
--- a/actioncable/lib/rails/generators/channel/channel_generator.rb
+++ b/actioncable/lib/rails/generators/channel/channel_generator.rb
@@ -16,7 +16,8 @@ module Rails
if self.behavior == :invoke
template "assets/cable.js", "app/assets/javascripts/cable.js"
end
- template "assets/channel.coffee", File.join('app/assets/javascripts/channels', class_path, "#{file_name}.coffee")
+
+ js_template "assets/channel", File.join('app/assets/javascripts/channels', class_path, "#{file_name}")
end
generate_application_cable_files
diff --git a/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb b/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb
index d56fa30f4d..17a85f60f9 100644
--- a/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb
+++ b/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb
@@ -1,4 +1,5 @@
-# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
+# Be sure to restart your server when you modify this file. Action Cable runs in
+# a loop that does not support auto reloading.
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
diff --git a/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb b/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb
index b4f41389ad..93f28c4306 100644
--- a/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb
+++ b/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb
@@ -1,4 +1,5 @@
-# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
+# Be sure to restart your server when you modify this file. Action Cable runs in
+# a loop that does not support auto reloading.
module ApplicationCable
class Connection < ActionCable::Connection::Base
end
diff --git a/actioncable/lib/rails/generators/channel/templates/assets/channel.js b/actioncable/lib/rails/generators/channel/templates/assets/channel.js
new file mode 100644
index 0000000000..ab0e68b11a
--- /dev/null
+++ b/actioncable/lib/rails/generators/channel/templates/assets/channel.js
@@ -0,0 +1,18 @@
+App.<%= class_name.underscore %> = App.cable.subscriptions.create("<%= class_name %>Channel", {
+ connected: function() {
+ // Called when the subscription is ready for use on the server
+ },
+
+ disconnected: function() {
+ // Called when the subscription has been terminated by the server
+ },
+
+ received: function(data) {
+ // Called when there's incoming data on the websocket for this channel
+ }<%= actions.any? ? ",\n" : '' %>
+<% actions.each do |action| -%>
+ <%=action %>: function() {
+ return this.perform('<%= action %>');
+ }<%= action == actions[-1] ? '' : ",\n" %>
+<% end -%>
+});
diff --git a/actioncable/package.json b/actioncable/package.json
new file mode 100644
index 0000000000..37f82fa1ea
--- /dev/null
+++ b/actioncable/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "actioncable",
+ "version": "5.0.0-rc1",
+ "description": "WebSocket framework for Ruby on Rails.",
+ "main": "lib/assets/compiled/action_cable.js",
+ "files": [
+ "lib/assets/compiled/*.js"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "rails/rails"
+ },
+ "keywords": [
+ "websockets",
+ "actioncable",
+ "rails"
+ ],
+ "author": "David Heinemeier Hansson <david@loudthinking.com>",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/rails/rails/issues"
+ },
+ "homepage": "http://rubyonrails.org/"
+}
diff --git a/actioncable/test/javascript/src/test.coffee b/actioncable/test/javascript/src/test.coffee
new file mode 100644
index 0000000000..3ce88c7789
--- /dev/null
+++ b/actioncable/test/javascript/src/test.coffee
@@ -0,0 +1,3 @@
+#= require action_cable
+#= require_tree ./test_helpers
+#= require_tree ./unit
diff --git a/actioncable/test/javascript/src/test_helpers/index.coffee b/actioncable/test/javascript/src/test_helpers/index.coffee
new file mode 100644
index 0000000000..e0d1e412cd
--- /dev/null
+++ b/actioncable/test/javascript/src/test_helpers/index.coffee
@@ -0,0 +1,5 @@
+#= require_self
+#= require_tree .
+
+ActionCable.TestHelpers =
+ testURL: "ws://cable.example.com/"
diff --git a/actioncable/test/javascript/src/test_helpers/mock_websocket.coffee b/actioncable/test/javascript/src/test_helpers/mock_websocket.coffee
new file mode 100644
index 0000000000..b7f86f18f6
--- /dev/null
+++ b/actioncable/test/javascript/src/test_helpers/mock_websocket.coffee
@@ -0,0 +1,21 @@
+#= require mock-socket
+
+NativeWebSocket = window.WebSocket
+
+server = null
+consumer = null
+
+ActionCable.TestHelpers.createConsumer = (url, callback) ->
+ window.WebSocket = MockWebSocket
+ server = new MockServer url
+ consumer = ActionCable.createConsumer(url)
+ callback(consumer, server)
+
+QUnit.testDone ->
+ if consumer?
+ consumer.disconnect()
+
+ if server?
+ server.clients().forEach (client) -> client.close()
+ server.close()
+ window.WebSocket = NativeWebSocket
diff --git a/actioncable/test/javascript/src/unit/action_cable_test.coffee b/actioncable/test/javascript/src/unit/action_cable_test.coffee
new file mode 100644
index 0000000000..f9eff64769
--- /dev/null
+++ b/actioncable/test/javascript/src/unit/action_cable_test.coffee
@@ -0,0 +1,24 @@
+{module, test} = QUnit
+{testURL} = ActionCable.TestHelpers
+
+module "ActionCable", ->
+ module "#createConsumer", ->
+ test "uses specified URL", (assert) ->
+ consumer = ActionCable.createConsumer(testURL)
+ assert.equal consumer.url, testURL
+
+ test "uses default URL", (assert) ->
+ pattern = ///#{ActionCable.INTERNAL.default_mount_path}$///
+ consumer = ActionCable.createConsumer()
+ assert.ok pattern.test(consumer.url), "Expected #{consumer.url} to match #{pattern}"
+
+ test "uses URL from meta tag", (assert) ->
+ element = document.createElement("meta")
+ element.setAttribute("name", "action-cable-url")
+ element.setAttribute("content", testURL)
+
+ document.head.appendChild(element)
+ consumer = ActionCable.createConsumer()
+ document.head.removeChild(element)
+
+ assert.equal consumer.url, testURL
diff --git a/actioncable/test/javascript/src/unit/consumer_test.coffee b/actioncable/test/javascript/src/unit/consumer_test.coffee
new file mode 100644
index 0000000000..d8b1450ad8
--- /dev/null
+++ b/actioncable/test/javascript/src/unit/consumer_test.coffee
@@ -0,0 +1,31 @@
+{module, test} = QUnit
+{testURL, createConsumer} = ActionCable.TestHelpers
+
+module "ActionCable.Consumer", ->
+ test "#connect", (assert) ->
+ done = assert.async()
+
+ createConsumer testURL, (consumer, server) ->
+ server.on "connection", ->
+ clients = server.clients()
+ assert.equal clients.length, 1
+ assert.equal clients[0].readyState, WebSocket.OPEN
+ done()
+
+ consumer.connect()
+
+ test "#disconnect", (assert) ->
+ done = assert.async()
+
+ createConsumer testURL, (consumer, server) ->
+ server.on "connection", ->
+ clients = server.clients()
+ assert.equal clients.length, 1
+
+ clients[0].addEventListener "close", (event) ->
+ assert.equal event.type, "close"
+ done()
+
+ consumer.disconnect()
+
+ consumer.connect()
diff --git a/actioncable/test/javascript/vendor/mock-socket.js b/actioncable/test/javascript/vendor/mock-socket.js
new file mode 100644
index 0000000000..b465c8b53f
--- /dev/null
+++ b/actioncable/test/javascript/vendor/mock-socket.js
@@ -0,0 +1,4533 @@
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+/*!
+ * URI.js - Mutating URLs
+ * IPv6 Support
+ *
+ * Version: 1.17.0
+ *
+ * Author: Rodney Rehm
+ * Web: http://medialize.github.io/URI.js/
+ *
+ * Licensed under
+ * MIT License http://www.opensource.org/licenses/mit-license
+ * GPL v3 http://opensource.org/licenses/GPL-3.0
+ *
+ */
+
+(function (root, factory) {
+ 'use strict';
+ // https://github.com/umdjs/umd/blob/master/returnExports.js
+ if (typeof exports === 'object') {
+ // Node
+ module.exports = factory();
+ } else if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(factory);
+ } else {
+ // Browser globals (root is window)
+ root.IPv6 = factory(root);
+ }
+}(this, function (root) {
+ 'use strict';
+
+ /*
+ var _in = "fe80:0000:0000:0000:0204:61ff:fe9d:f156";
+ var _out = IPv6.best(_in);
+ var _expected = "fe80::204:61ff:fe9d:f156";
+
+ console.log(_in, _out, _expected, _out === _expected);
+ */
+
+ // save current IPv6 variable, if any
+ var _IPv6 = root && root.IPv6;
+
+ function bestPresentation(address) {
+ // based on:
+ // Javascript to test an IPv6 address for proper format, and to
+ // present the "best text representation" according to IETF Draft RFC at
+ // http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04
+ // 8 Feb 2010 Rich Brown, Dartware, LLC
+ // Please feel free to use this code as long as you provide a link to
+ // http://www.intermapper.com
+ // http://intermapper.com/support/tools/IPV6-Validator.aspx
+ // http://download.dartware.com/thirdparty/ipv6validator.js
+
+ var _address = address.toLowerCase();
+ var segments = _address.split(':');
+ var length = segments.length;
+ var total = 8;
+
+ // trim colons (:: or ::a:b:c… or …a:b:c::)
+ if (segments[0] === '' && segments[1] === '' && segments[2] === '') {
+ // must have been ::
+ // remove first two items
+ segments.shift();
+ segments.shift();
+ } else if (segments[0] === '' && segments[1] === '') {
+ // must have been ::xxxx
+ // remove the first item
+ segments.shift();
+ } else if (segments[length - 1] === '' && segments[length - 2] === '') {
+ // must have been xxxx::
+ segments.pop();
+ }
+
+ length = segments.length;
+
+ // adjust total segments for IPv4 trailer
+ if (segments[length - 1].indexOf('.') !== -1) {
+ // found a "." which means IPv4
+ total = 7;
+ }
+
+ // fill empty segments them with "0000"
+ var pos;
+ for (pos = 0; pos < length; pos++) {
+ if (segments[pos] === '') {
+ break;
+ }
+ }
+
+ if (pos < total) {
+ segments.splice(pos, 1, '0000');
+ while (segments.length < total) {
+ segments.splice(pos, 0, '0000');
+ }
+
+ length = segments.length;
+ }
+
+ // strip leading zeros
+ var _segments;
+ for (var i = 0; i < total; i++) {
+ _segments = segments[i].split('');
+ for (var j = 0; j < 3 ; j++) {
+ if (_segments[0] === '0' && _segments.length > 1) {
+ _segments.splice(0,1);
+ } else {
+ break;
+ }
+ }
+
+ segments[i] = _segments.join('');
+ }
+
+ // find longest sequence of zeroes and coalesce them into one segment
+ var best = -1;
+ var _best = 0;
+ var _current = 0;
+ var current = -1;
+ var inzeroes = false;
+ // i; already declared
+
+ for (i = 0; i < total; i++) {
+ if (inzeroes) {
+ if (segments[i] === '0') {
+ _current += 1;
+ } else {
+ inzeroes = false;
+ if (_current > _best) {
+ best = current;
+ _best = _current;
+ }
+ }
+ } else {
+ if (segments[i] === '0') {
+ inzeroes = true;
+ current = i;
+ _current = 1;
+ }
+ }
+ }
+
+ if (_current > _best) {
+ best = current;
+ _best = _current;
+ }
+
+ if (_best > 1) {
+ segments.splice(best, _best, '');
+ }
+
+ length = segments.length;
+
+ // assemble remaining segments
+ var result = '';
+ if (segments[0] === '') {
+ result = ':';
+ }
+
+ for (i = 0; i < length; i++) {
+ result += segments[i];
+ if (i === length - 1) {
+ break;
+ }
+
+ result += ':';
+ }
+
+ if (segments[length - 1] === '') {
+ result += ':';
+ }
+
+ return result;
+ }
+
+ function noConflict() {
+ /*jshint validthis: true */
+ if (root.IPv6 === this) {
+ root.IPv6 = _IPv6;
+ }
+
+ return this;
+ }
+
+ return {
+ best: bestPresentation,
+ noConflict: noConflict
+ };
+}));
+
+},{}],2:[function(require,module,exports){
+/*!
+ * URI.js - Mutating URLs
+ * Second Level Domain (SLD) Support
+ *
+ * Version: 1.17.0
+ *
+ * Author: Rodney Rehm
+ * Web: http://medialize.github.io/URI.js/
+ *
+ * Licensed under
+ * MIT License http://www.opensource.org/licenses/mit-license
+ * GPL v3 http://opensource.org/licenses/GPL-3.0
+ *
+ */
+
+(function (root, factory) {
+ 'use strict';
+ // https://github.com/umdjs/umd/blob/master/returnExports.js
+ if (typeof exports === 'object') {
+ // Node
+ module.exports = factory();
+ } else if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(factory);
+ } else {
+ // Browser globals (root is window)
+ root.SecondLevelDomains = factory(root);
+ }
+}(this, function (root) {
+ 'use strict';
+
+ // save current SecondLevelDomains variable, if any
+ var _SecondLevelDomains = root && root.SecondLevelDomains;
+
+ var SLD = {
+ // list of known Second Level Domains
+ // converted list of SLDs from https://github.com/gavingmiller/second-level-domains
+ // ----
+ // publicsuffix.org is more current and actually used by a couple of browsers internally.
+ // downside is it also contains domains like "dyndns.org" - which is fine for the security
+ // issues browser have to deal with (SOP for cookies, etc) - but is way overboard for URI.js
+ // ----
+ list: {
+ 'ac':' com gov mil net org ',
+ 'ae':' ac co gov mil name net org pro sch ',
+ 'af':' com edu gov net org ',
+ 'al':' com edu gov mil net org ',
+ 'ao':' co ed gv it og pb ',
+ 'ar':' com edu gob gov int mil net org tur ',
+ 'at':' ac co gv or ',
+ 'au':' asn com csiro edu gov id net org ',
+ 'ba':' co com edu gov mil net org rs unbi unmo unsa untz unze ',
+ 'bb':' biz co com edu gov info net org store tv ',
+ 'bh':' biz cc com edu gov info net org ',
+ 'bn':' com edu gov net org ',
+ 'bo':' com edu gob gov int mil net org tv ',
+ 'br':' adm adv agr am arq art ato b bio blog bmd cim cng cnt com coop ecn edu eng esp etc eti far flog fm fnd fot fst g12 ggf gov imb ind inf jor jus lel mat med mil mus net nom not ntr odo org ppg pro psc psi qsl rec slg srv tmp trd tur tv vet vlog wiki zlg ',
+ 'bs':' com edu gov net org ',
+ 'bz':' du et om ov rg ',
+ 'ca':' ab bc mb nb nf nl ns nt nu on pe qc sk yk ',
+ 'ck':' biz co edu gen gov info net org ',
+ 'cn':' ac ah bj com cq edu fj gd gov gs gx gz ha hb he hi hl hn jl js jx ln mil net nm nx org qh sc sd sh sn sx tj tw xj xz yn zj ',
+ 'co':' com edu gov mil net nom org ',
+ 'cr':' ac c co ed fi go or sa ',
+ 'cy':' ac biz com ekloges gov ltd name net org parliament press pro tm ',
+ 'do':' art com edu gob gov mil net org sld web ',
+ 'dz':' art asso com edu gov net org pol ',
+ 'ec':' com edu fin gov info med mil net org pro ',
+ 'eg':' com edu eun gov mil name net org sci ',
+ 'er':' com edu gov ind mil net org rochest w ',
+ 'es':' com edu gob nom org ',
+ 'et':' biz com edu gov info name net org ',
+ 'fj':' ac biz com info mil name net org pro ',
+ 'fk':' ac co gov net nom org ',
+ 'fr':' asso com f gouv nom prd presse tm ',
+ 'gg':' co net org ',
+ 'gh':' com edu gov mil org ',
+ 'gn':' ac com gov net org ',
+ 'gr':' com edu gov mil net org ',
+ 'gt':' com edu gob ind mil net org ',
+ 'gu':' com edu gov net org ',
+ 'hk':' com edu gov idv net org ',
+ 'hu':' 2000 agrar bolt casino city co erotica erotika film forum games hotel info ingatlan jogasz konyvelo lakas media news org priv reklam sex shop sport suli szex tm tozsde utazas video ',
+ 'id':' ac co go mil net or sch web ',
+ 'il':' ac co gov idf k12 muni net org ',
+ 'in':' ac co edu ernet firm gen gov i ind mil net nic org res ',
+ 'iq':' com edu gov i mil net org ',
+ 'ir':' ac co dnssec gov i id net org sch ',
+ 'it':' edu gov ',
+ 'je':' co net org ',
+ 'jo':' com edu gov mil name net org sch ',
+ 'jp':' ac ad co ed go gr lg ne or ',
+ 'ke':' ac co go info me mobi ne or sc ',
+ 'kh':' com edu gov mil net org per ',
+ 'ki':' biz com de edu gov info mob net org tel ',
+ 'km':' asso com coop edu gouv k medecin mil nom notaires pharmaciens presse tm veterinaire ',
+ 'kn':' edu gov net org ',
+ 'kr':' ac busan chungbuk chungnam co daegu daejeon es gangwon go gwangju gyeongbuk gyeonggi gyeongnam hs incheon jeju jeonbuk jeonnam k kg mil ms ne or pe re sc seoul ulsan ',
+ 'kw':' com edu gov net org ',
+ 'ky':' com edu gov net org ',
+ 'kz':' com edu gov mil net org ',
+ 'lb':' com edu gov net org ',
+ 'lk':' assn com edu gov grp hotel int ltd net ngo org sch soc web ',
+ 'lr':' com edu gov net org ',
+ 'lv':' asn com conf edu gov id mil net org ',
+ 'ly':' com edu gov id med net org plc sch ',
+ 'ma':' ac co gov m net org press ',
+ 'mc':' asso tm ',
+ 'me':' ac co edu gov its net org priv ',
+ 'mg':' com edu gov mil nom org prd tm ',
+ 'mk':' com edu gov inf name net org pro ',
+ 'ml':' com edu gov net org presse ',
+ 'mn':' edu gov org ',
+ 'mo':' com edu gov net org ',
+ 'mt':' com edu gov net org ',
+ 'mv':' aero biz com coop edu gov info int mil museum name net org pro ',
+ 'mw':' ac co com coop edu gov int museum net org ',
+ 'mx':' com edu gob net org ',
+ 'my':' com edu gov mil name net org sch ',
+ 'nf':' arts com firm info net other per rec store web ',
+ 'ng':' biz com edu gov mil mobi name net org sch ',
+ 'ni':' ac co com edu gob mil net nom org ',
+ 'np':' com edu gov mil net org ',
+ 'nr':' biz com edu gov info net org ',
+ 'om':' ac biz co com edu gov med mil museum net org pro sch ',
+ 'pe':' com edu gob mil net nom org sld ',
+ 'ph':' com edu gov i mil net ngo org ',
+ 'pk':' biz com edu fam gob gok gon gop gos gov net org web ',
+ 'pl':' art bialystok biz com edu gda gdansk gorzow gov info katowice krakow lodz lublin mil net ngo olsztyn org poznan pwr radom slupsk szczecin torun warszawa waw wroc wroclaw zgora ',
+ 'pr':' ac biz com edu est gov info isla name net org pro prof ',
+ 'ps':' com edu gov net org plo sec ',
+ 'pw':' belau co ed go ne or ',
+ 'ro':' arts com firm info nom nt org rec store tm www ',
+ 'rs':' ac co edu gov in org ',
+ 'sb':' com edu gov net org ',
+ 'sc':' com edu gov net org ',
+ 'sh':' co com edu gov net nom org ',
+ 'sl':' com edu gov net org ',
+ 'st':' co com consulado edu embaixada gov mil net org principe saotome store ',
+ 'sv':' com edu gob org red ',
+ 'sz':' ac co org ',
+ 'tr':' av bbs bel biz com dr edu gen gov info k12 name net org pol tel tsk tv web ',
+ 'tt':' aero biz cat co com coop edu gov info int jobs mil mobi museum name net org pro tel travel ',
+ 'tw':' club com ebiz edu game gov idv mil net org ',
+ 'mu':' ac co com gov net or org ',
+ 'mz':' ac co edu gov org ',
+ 'na':' co com ',
+ 'nz':' ac co cri geek gen govt health iwi maori mil net org parliament school ',
+ 'pa':' abo ac com edu gob ing med net nom org sld ',
+ 'pt':' com edu gov int net nome org publ ',
+ 'py':' com edu gov mil net org ',
+ 'qa':' com edu gov mil net org ',
+ 're':' asso com nom ',
+ 'ru':' ac adygeya altai amur arkhangelsk astrakhan bashkiria belgorod bir bryansk buryatia cbg chel chelyabinsk chita chukotka chuvashia com dagestan e-burg edu gov grozny int irkutsk ivanovo izhevsk jar joshkar-ola kalmykia kaluga kamchatka karelia kazan kchr kemerovo khabarovsk khakassia khv kirov koenig komi kostroma kranoyarsk kuban kurgan kursk lipetsk magadan mari mari-el marine mil mordovia mosreg msk murmansk nalchik net nnov nov novosibirsk nsk omsk orenburg org oryol penza perm pp pskov ptz rnd ryazan sakhalin samara saratov simbirsk smolensk spb stavropol stv surgut tambov tatarstan tom tomsk tsaritsyn tsk tula tuva tver tyumen udm udmurtia ulan-ude vladikavkaz vladimir vladivostok volgograd vologda voronezh vrn vyatka yakutia yamal yekaterinburg yuzhno-sakhalinsk ',
+ 'rw':' ac co com edu gouv gov int mil net ',
+ 'sa':' com edu gov med net org pub sch ',
+ 'sd':' com edu gov info med net org tv ',
+ 'se':' a ac b bd c d e f g h i k l m n o org p parti pp press r s t tm u w x y z ',
+ 'sg':' com edu gov idn net org per ',
+ 'sn':' art com edu gouv org perso univ ',
+ 'sy':' com edu gov mil net news org ',
+ 'th':' ac co go in mi net or ',
+ 'tj':' ac biz co com edu go gov info int mil name net nic org test web ',
+ 'tn':' agrinet com defense edunet ens fin gov ind info intl mincom nat net org perso rnrt rns rnu tourism ',
+ 'tz':' ac co go ne or ',
+ 'ua':' biz cherkassy chernigov chernovtsy ck cn co com crimea cv dn dnepropetrovsk donetsk dp edu gov if in ivano-frankivsk kh kharkov kherson khmelnitskiy kiev kirovograd km kr ks kv lg lugansk lutsk lviv me mk net nikolaev od odessa org pl poltava pp rovno rv sebastopol sumy te ternopil uzhgorod vinnica vn zaporizhzhe zhitomir zp zt ',
+ 'ug':' ac co go ne or org sc ',
+ 'uk':' ac bl british-library co cym gov govt icnet jet lea ltd me mil mod national-library-scotland nel net nhs nic nls org orgn parliament plc police sch scot soc ',
+ 'us':' dni fed isa kids nsn ',
+ 'uy':' com edu gub mil net org ',
+ 've':' co com edu gob info mil net org web ',
+ 'vi':' co com k12 net org ',
+ 'vn':' ac biz com edu gov health info int name net org pro ',
+ 'ye':' co com gov ltd me net org plc ',
+ 'yu':' ac co edu gov org ',
+ 'za':' ac agric alt bourse city co cybernet db edu gov grondar iaccess imt inca landesign law mil net ngo nis nom olivetti org pix school tm web ',
+ 'zm':' ac co com edu gov net org sch '
+ },
+ // gorhill 2013-10-25: Using indexOf() instead Regexp(). Significant boost
+ // in both performance and memory footprint. No initialization required.
+ // http://jsperf.com/uri-js-sld-regex-vs-binary-search/4
+ // Following methods use lastIndexOf() rather than array.split() in order
+ // to avoid any memory allocations.
+ has: function(domain) {
+ var tldOffset = domain.lastIndexOf('.');
+ if (tldOffset <= 0 || tldOffset >= (domain.length-1)) {
+ return false;
+ }
+ var sldOffset = domain.lastIndexOf('.', tldOffset-1);
+ if (sldOffset <= 0 || sldOffset >= (tldOffset-1)) {
+ return false;
+ }
+ var sldList = SLD.list[domain.slice(tldOffset+1)];
+ if (!sldList) {
+ return false;
+ }
+ return sldList.indexOf(' ' + domain.slice(sldOffset+1, tldOffset) + ' ') >= 0;
+ },
+ is: function(domain) {
+ var tldOffset = domain.lastIndexOf('.');
+ if (tldOffset <= 0 || tldOffset >= (domain.length-1)) {
+ return false;
+ }
+ var sldOffset = domain.lastIndexOf('.', tldOffset-1);
+ if (sldOffset >= 0) {
+ return false;
+ }
+ var sldList = SLD.list[domain.slice(tldOffset+1)];
+ if (!sldList) {
+ return false;
+ }
+ return sldList.indexOf(' ' + domain.slice(0, tldOffset) + ' ') >= 0;
+ },
+ get: function(domain) {
+ var tldOffset = domain.lastIndexOf('.');
+ if (tldOffset <= 0 || tldOffset >= (domain.length-1)) {
+ return null;
+ }
+ var sldOffset = domain.lastIndexOf('.', tldOffset-1);
+ if (sldOffset <= 0 || sldOffset >= (tldOffset-1)) {
+ return null;
+ }
+ var sldList = SLD.list[domain.slice(tldOffset+1)];
+ if (!sldList) {
+ return null;
+ }
+ if (sldList.indexOf(' ' + domain.slice(sldOffset+1, tldOffset) + ' ') < 0) {
+ return null;
+ }
+ return domain.slice(sldOffset+1);
+ },
+ noConflict: function(){
+ if (root.SecondLevelDomains === this) {
+ root.SecondLevelDomains = _SecondLevelDomains;
+ }
+ return this;
+ }
+ };
+
+ return SLD;
+}));
+
+},{}],3:[function(require,module,exports){
+/*!
+ * URI.js - Mutating URLs
+ *
+ * Version: 1.17.0
+ *
+ * Author: Rodney Rehm
+ * Web: http://medialize.github.io/URI.js/
+ *
+ * Licensed under
+ * MIT License http://www.opensource.org/licenses/mit-license
+ * GPL v3 http://opensource.org/licenses/GPL-3.0
+ *
+ */
+(function (root, factory) {
+ 'use strict';
+ // https://github.com/umdjs/umd/blob/master/returnExports.js
+ if (typeof exports === 'object') {
+ // Node
+ module.exports = factory(require('./punycode'), require('./IPv6'), require('./SecondLevelDomains'));
+ } else if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['./punycode', './IPv6', './SecondLevelDomains'], factory);
+ } else {
+ // Browser globals (root is window)
+ root.URI = factory(root.punycode, root.IPv6, root.SecondLevelDomains, root);
+ }
+}(this, function (punycode, IPv6, SLD, root) {
+ 'use strict';
+ /*global location, escape, unescape */
+ // FIXME: v2.0.0 renamce non-camelCase properties to uppercase
+ /*jshint camelcase: false */
+
+ // save current URI variable, if any
+ var _URI = root && root.URI;
+
+ function URI(url, base) {
+ var _urlSupplied = arguments.length >= 1;
+ var _baseSupplied = arguments.length >= 2;
+
+ // Allow instantiation without the 'new' keyword
+ if (!(this instanceof URI)) {
+ if (_urlSupplied) {
+ if (_baseSupplied) {
+ return new URI(url, base);
+ }
+
+ return new URI(url);
+ }
+
+ return new URI();
+ }
+
+ if (url === undefined) {
+ if (_urlSupplied) {
+ throw new TypeError('undefined is not a valid argument for URI');
+ }
+
+ if (typeof location !== 'undefined') {
+ url = location.href + '';
+ } else {
+ url = '';
+ }
+ }
+
+ this.href(url);
+
+ // resolve to base according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#constructor
+ if (base !== undefined) {
+ return this.absoluteTo(base);
+ }
+
+ return this;
+ }
+
+ URI.version = '1.17.0';
+
+ var p = URI.prototype;
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ function escapeRegEx(string) {
+ // https://github.com/medialize/URI.js/commit/85ac21783c11f8ccab06106dba9735a31a86924d#commitcomment-821963
+ return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+ }
+
+ function getType(value) {
+ // IE8 doesn't return [Object Undefined] but [Object Object] for undefined value
+ if (value === undefined) {
+ return 'Undefined';
+ }
+
+ return String(Object.prototype.toString.call(value)).slice(8, -1);
+ }
+
+ function isArray(obj) {
+ return getType(obj) === 'Array';
+ }
+
+ function filterArrayValues(data, value) {
+ var lookup = {};
+ var i, length;
+
+ if (getType(value) === 'RegExp') {
+ lookup = null;
+ } else if (isArray(value)) {
+ for (i = 0, length = value.length; i < length; i++) {
+ lookup[value[i]] = true;
+ }
+ } else {
+ lookup[value] = true;
+ }
+
+ for (i = 0, length = data.length; i < length; i++) {
+ /*jshint laxbreak: true */
+ var _match = lookup && lookup[data[i]] !== undefined
+ || !lookup && value.test(data[i]);
+ /*jshint laxbreak: false */
+ if (_match) {
+ data.splice(i, 1);
+ length--;
+ i--;
+ }
+ }
+
+ return data;
+ }
+
+ function arrayContains(list, value) {
+ var i, length;
+
+ // value may be string, number, array, regexp
+ if (isArray(value)) {
+ // Note: this can be optimized to O(n) (instead of current O(m * n))
+ for (i = 0, length = value.length; i < length; i++) {
+ if (!arrayContains(list, value[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ var _type = getType(value);
+ for (i = 0, length = list.length; i < length; i++) {
+ if (_type === 'RegExp') {
+ if (typeof list[i] === 'string' && list[i].match(value)) {
+ return true;
+ }
+ } else if (list[i] === value) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function arraysEqual(one, two) {
+ if (!isArray(one) || !isArray(two)) {
+ return false;
+ }
+
+ // arrays can't be equal if they have different amount of content
+ if (one.length !== two.length) {
+ return false;
+ }
+
+ one.sort();
+ two.sort();
+
+ for (var i = 0, l = one.length; i < l; i++) {
+ if (one[i] !== two[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function trimSlashes(text) {
+ var trim_expression = /^\/+|\/+$/g;
+ return text.replace(trim_expression, '');
+ }
+
+ URI._parts = function() {
+ return {
+ protocol: null,
+ username: null,
+ password: null,
+ hostname: null,
+ urn: null,
+ port: null,
+ path: null,
+ query: null,
+ fragment: null,
+ // state
+ duplicateQueryParameters: URI.duplicateQueryParameters,
+ escapeQuerySpace: URI.escapeQuerySpace
+ };
+ };
+ // state: allow duplicate query parameters (a=1&a=1)
+ URI.duplicateQueryParameters = false;
+ // state: replaces + with %20 (space in query strings)
+ URI.escapeQuerySpace = true;
+ // static properties
+ URI.protocol_expression = /^[a-z][a-z0-9.+-]*$/i;
+ URI.idn_expression = /[^a-z0-9\.-]/i;
+ URI.punycode_expression = /(xn--)/i;
+ // well, 333.444.555.666 matches, but it sure ain't no IPv4 - do we care?
+ URI.ip4_expression = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
+ // credits to Rich Brown
+ // source: http://forums.intermapper.com/viewtopic.php?p=1096#1096
+ // specification: http://www.ietf.org/rfc/rfc4291.txt
+ URI.ip6_expression = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
+ // expression used is "gruber revised" (@gruber v2) determined to be the
+ // best solution in a regex-golf we did a couple of ages ago at
+ // * http://mathiasbynens.be/demo/url-regex
+ // * http://rodneyrehm.de/t/url-regex.html
+ URI.find_uri_expression = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig;
+ URI.findUri = {
+ // valid "scheme://" or "www."
+ start: /\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.)/gi,
+ // everything up to the next whitespace
+ end: /[\s\r\n]|$/,
+ // trim trailing punctuation captured by end RegExp
+ trim: /[`!()\[\]{};:'".,<>?«»“”„‘’]+$/
+ };
+ // http://www.iana.org/assignments/uri-schemes.html
+ // http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports
+ URI.defaultPorts = {
+ http: '80',
+ https: '443',
+ ftp: '21',
+ gopher: '70',
+ ws: '80',
+ wss: '443'
+ };
+ // allowed hostname characters according to RFC 3986
+ // ALPHA DIGIT "-" "." "_" "~" "!" "$" "&" "'" "(" ")" "*" "+" "," ";" "=" %encoded
+ // I've never seen a (non-IDN) hostname other than: ALPHA DIGIT . -
+ URI.invalid_hostname_characters = /[^a-zA-Z0-9\.-]/;
+ // map DOM Elements to their URI attribute
+ URI.domAttributes = {
+ 'a': 'href',
+ 'blockquote': 'cite',
+ 'link': 'href',
+ 'base': 'href',
+ 'script': 'src',
+ 'form': 'action',
+ 'img': 'src',
+ 'area': 'href',
+ 'iframe': 'src',
+ 'embed': 'src',
+ 'source': 'src',
+ 'track': 'src',
+ 'input': 'src', // but only if type="image"
+ 'audio': 'src',
+ 'video': 'src'
+ };
+ URI.getDomAttribute = function(node) {
+ if (!node || !node.nodeName) {
+ return undefined;
+ }
+
+ var nodeName = node.nodeName.toLowerCase();
+ // <input> should only expose src for type="image"
+ if (nodeName === 'input' && node.type !== 'image') {
+ return undefined;
+ }
+
+ return URI.domAttributes[nodeName];
+ };
+
+ function escapeForDumbFirefox36(value) {
+ // https://github.com/medialize/URI.js/issues/91
+ return escape(value);
+ }
+
+ // encoding / decoding according to RFC3986
+ function strictEncodeURIComponent(string) {
+ // see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent
+ return encodeURIComponent(string)
+ .replace(/[!'()*]/g, escapeForDumbFirefox36)
+ .replace(/\*/g, '%2A');
+ }
+ URI.encode = strictEncodeURIComponent;
+ URI.decode = decodeURIComponent;
+ URI.iso8859 = function() {
+ URI.encode = escape;
+ URI.decode = unescape;
+ };
+ URI.unicode = function() {
+ URI.encode = strictEncodeURIComponent;
+ URI.decode = decodeURIComponent;
+ };
+ URI.characters = {
+ pathname: {
+ encode: {
+ // RFC3986 2.1: For consistency, URI producers and normalizers should
+ // use uppercase hexadecimal digits for all percent-encodings.
+ expression: /%(24|26|2B|2C|3B|3D|3A|40)/ig,
+ map: {
+ // -._~!'()*
+ '%24': '$',
+ '%26': '&',
+ '%2B': '+',
+ '%2C': ',',
+ '%3B': ';',
+ '%3D': '=',
+ '%3A': ':',
+ '%40': '@'
+ }
+ },
+ decode: {
+ expression: /[\/\?#]/g,
+ map: {
+ '/': '%2F',
+ '?': '%3F',
+ '#': '%23'
+ }
+ }
+ },
+ reserved: {
+ encode: {
+ // RFC3986 2.1: For consistency, URI producers and normalizers should
+ // use uppercase hexadecimal digits for all percent-encodings.
+ expression: /%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,
+ map: {
+ // gen-delims
+ '%3A': ':',
+ '%2F': '/',
+ '%3F': '?',
+ '%23': '#',
+ '%5B': '[',
+ '%5D': ']',
+ '%40': '@',
+ // sub-delims
+ '%21': '!',
+ '%24': '$',
+ '%26': '&',
+ '%27': '\'',
+ '%28': '(',
+ '%29': ')',
+ '%2A': '*',
+ '%2B': '+',
+ '%2C': ',',
+ '%3B': ';',
+ '%3D': '='
+ }
+ }
+ },
+ urnpath: {
+ // The characters under `encode` are the characters called out by RFC 2141 as being acceptable
+ // for usage in a URN. RFC2141 also calls out "-", ".", and "_" as acceptable characters, but
+ // these aren't encoded by encodeURIComponent, so we don't have to call them out here. Also
+ // note that the colon character is not featured in the encoding map; this is because URI.js
+ // gives the colons in URNs semantic meaning as the delimiters of path segements, and so it
+ // should not appear unencoded in a segment itself.
+ // See also the note above about RFC3986 and capitalalized hex digits.
+ encode: {
+ expression: /%(21|24|27|28|29|2A|2B|2C|3B|3D|40)/ig,
+ map: {
+ '%21': '!',
+ '%24': '$',
+ '%27': '\'',
+ '%28': '(',
+ '%29': ')',
+ '%2A': '*',
+ '%2B': '+',
+ '%2C': ',',
+ '%3B': ';',
+ '%3D': '=',
+ '%40': '@'
+ }
+ },
+ // These characters are the characters called out by RFC2141 as "reserved" characters that
+ // should never appear in a URN, plus the colon character (see note above).
+ decode: {
+ expression: /[\/\?#:]/g,
+ map: {
+ '/': '%2F',
+ '?': '%3F',
+ '#': '%23',
+ ':': '%3A'
+ }
+ }
+ }
+ };
+ URI.encodeQuery = function(string, escapeQuerySpace) {
+ var escaped = URI.encode(string + '');
+ if (escapeQuerySpace === undefined) {
+ escapeQuerySpace = URI.escapeQuerySpace;
+ }
+
+ return escapeQuerySpace ? escaped.replace(/%20/g, '+') : escaped;
+ };
+ URI.decodeQuery = function(string, escapeQuerySpace) {
+ string += '';
+ if (escapeQuerySpace === undefined) {
+ escapeQuerySpace = URI.escapeQuerySpace;
+ }
+
+ try {
+ return URI.decode(escapeQuerySpace ? string.replace(/\+/g, '%20') : string);
+ } catch(e) {
+ // we're not going to mess with weird encodings,
+ // give up and return the undecoded original string
+ // see https://github.com/medialize/URI.js/issues/87
+ // see https://github.com/medialize/URI.js/issues/92
+ return string;
+ }
+ };
+ // generate encode/decode path functions
+ var _parts = {'encode':'encode', 'decode':'decode'};
+ var _part;
+ var generateAccessor = function(_group, _part) {
+ return function(string) {
+ try {
+ return URI[_part](string + '').replace(URI.characters[_group][_part].expression, function(c) {
+ return URI.characters[_group][_part].map[c];
+ });
+ } catch (e) {
+ // we're not going to mess with weird encodings,
+ // give up and return the undecoded original string
+ // see https://github.com/medialize/URI.js/issues/87
+ // see https://github.com/medialize/URI.js/issues/92
+ return string;
+ }
+ };
+ };
+
+ for (_part in _parts) {
+ URI[_part + 'PathSegment'] = generateAccessor('pathname', _parts[_part]);
+ URI[_part + 'UrnPathSegment'] = generateAccessor('urnpath', _parts[_part]);
+ }
+
+ var generateSegmentedPathFunction = function(_sep, _codingFuncName, _innerCodingFuncName) {
+ return function(string) {
+ // Why pass in names of functions, rather than the function objects themselves? The
+ // definitions of some functions (but in particular, URI.decode) will occasionally change due
+ // to URI.js having ISO8859 and Unicode modes. Passing in the name and getting it will ensure
+ // that the functions we use here are "fresh".
+ var actualCodingFunc;
+ if (!_innerCodingFuncName) {
+ actualCodingFunc = URI[_codingFuncName];
+ } else {
+ actualCodingFunc = function(string) {
+ return URI[_codingFuncName](URI[_innerCodingFuncName](string));
+ };
+ }
+
+ var segments = (string + '').split(_sep);
+
+ for (var i = 0, length = segments.length; i < length; i++) {
+ segments[i] = actualCodingFunc(segments[i]);
+ }
+
+ return segments.join(_sep);
+ };
+ };
+
+ // This takes place outside the above loop because we don't want, e.g., encodeUrnPath functions.
+ URI.decodePath = generateSegmentedPathFunction('/', 'decodePathSegment');
+ URI.decodeUrnPath = generateSegmentedPathFunction(':', 'decodeUrnPathSegment');
+ URI.recodePath = generateSegmentedPathFunction('/', 'encodePathSegment', 'decode');
+ URI.recodeUrnPath = generateSegmentedPathFunction(':', 'encodeUrnPathSegment', 'decode');
+
+ URI.encodeReserved = generateAccessor('reserved', 'encode');
+
+ URI.parse = function(string, parts) {
+ var pos;
+ if (!parts) {
+ parts = {};
+ }
+ // [protocol"://"[username[":"password]"@"]hostname[":"port]"/"?][path]["?"querystring]["#"fragment]
+
+ // extract fragment
+ pos = string.indexOf('#');
+ if (pos > -1) {
+ // escaping?
+ parts.fragment = string.substring(pos + 1) || null;
+ string = string.substring(0, pos);
+ }
+
+ // extract query
+ pos = string.indexOf('?');
+ if (pos > -1) {
+ // escaping?
+ parts.query = string.substring(pos + 1) || null;
+ string = string.substring(0, pos);
+ }
+
+ // extract protocol
+ if (string.substring(0, 2) === '//') {
+ // relative-scheme
+ parts.protocol = null;
+ string = string.substring(2);
+ // extract "user:pass@host:port"
+ string = URI.parseAuthority(string, parts);
+ } else {
+ pos = string.indexOf(':');
+ if (pos > -1) {
+ parts.protocol = string.substring(0, pos) || null;
+ if (parts.protocol && !parts.protocol.match(URI.protocol_expression)) {
+ // : may be within the path
+ parts.protocol = undefined;
+ } else if (string.substring(pos + 1, pos + 3) === '//') {
+ string = string.substring(pos + 3);
+
+ // extract "user:pass@host:port"
+ string = URI.parseAuthority(string, parts);
+ } else {
+ string = string.substring(pos + 1);
+ parts.urn = true;
+ }
+ }
+ }
+
+ // what's left must be the path
+ parts.path = string;
+
+ // and we're done
+ return parts;
+ };
+ URI.parseHost = function(string, parts) {
+ // Copy chrome, IE, opera backslash-handling behavior.
+ // Back slashes before the query string get converted to forward slashes
+ // See: https://github.com/joyent/node/blob/386fd24f49b0e9d1a8a076592a404168faeecc34/lib/url.js#L115-L124
+ // See: https://code.google.com/p/chromium/issues/detail?id=25916
+ // https://github.com/medialize/URI.js/pull/233
+ string = string.replace(/\\/g, '/');
+
+ // extract host:port
+ var pos = string.indexOf('/');
+ var bracketPos;
+ var t;
+
+ if (pos === -1) {
+ pos = string.length;
+ }
+
+ if (string.charAt(0) === '[') {
+ // IPv6 host - http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04#section-6
+ // I claim most client software breaks on IPv6 anyways. To simplify things, URI only accepts
+ // IPv6+port in the format [2001:db8::1]:80 (for the time being)
+ bracketPos = string.indexOf(']');
+ parts.hostname = string.substring(1, bracketPos) || null;
+ parts.port = string.substring(bracketPos + 2, pos) || null;
+ if (parts.port === '/') {
+ parts.port = null;
+ }
+ } else {
+ var firstColon = string.indexOf(':');
+ var firstSlash = string.indexOf('/');
+ var nextColon = string.indexOf(':', firstColon + 1);
+ if (nextColon !== -1 && (firstSlash === -1 || nextColon < firstSlash)) {
+ // IPv6 host contains multiple colons - but no port
+ // this notation is actually not allowed by RFC 3986, but we're a liberal parser
+ parts.hostname = string.substring(0, pos) || null;
+ parts.port = null;
+ } else {
+ t = string.substring(0, pos).split(':');
+ parts.hostname = t[0] || null;
+ parts.port = t[1] || null;
+ }
+ }
+
+ if (parts.hostname && string.substring(pos).charAt(0) !== '/') {
+ pos++;
+ string = '/' + string;
+ }
+
+ return string.substring(pos) || '/';
+ };
+ URI.parseAuthority = function(string, parts) {
+ string = URI.parseUserinfo(string, parts);
+ return URI.parseHost(string, parts);
+ };
+ URI.parseUserinfo = function(string, parts) {
+ // extract username:password
+ var firstSlash = string.indexOf('/');
+ var pos = string.lastIndexOf('@', firstSlash > -1 ? firstSlash : string.length - 1);
+ var t;
+
+ // authority@ must come before /path
+ if (pos > -1 && (firstSlash === -1 || pos < firstSlash)) {
+ t = string.substring(0, pos).split(':');
+ parts.username = t[0] ? URI.decode(t[0]) : null;
+ t.shift();
+ parts.password = t[0] ? URI.decode(t.join(':')) : null;
+ string = string.substring(pos + 1);
+ } else {
+ parts.username = null;
+ parts.password = null;
+ }
+
+ return string;
+ };
+ URI.parseQuery = function(string, escapeQuerySpace) {
+ if (!string) {
+ return {};
+ }
+
+ // throw out the funky business - "?"[name"="value"&"]+
+ string = string.replace(/&+/g, '&').replace(/^\?*&*|&+$/g, '');
+
+ if (!string) {
+ return {};
+ }
+
+ var items = {};
+ var splits = string.split('&');
+ var length = splits.length;
+ var v, name, value;
+
+ for (var i = 0; i < length; i++) {
+ v = splits[i].split('=');
+ name = URI.decodeQuery(v.shift(), escapeQuerySpace);
+ // no "=" is null according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#collect-url-parameters
+ value = v.length ? URI.decodeQuery(v.join('='), escapeQuerySpace) : null;
+
+ if (hasOwn.call(items, name)) {
+ if (typeof items[name] === 'string' || items[name] === null) {
+ items[name] = [items[name]];
+ }
+
+ items[name].push(value);
+ } else {
+ items[name] = value;
+ }
+ }
+
+ return items;
+ };
+
+ URI.build = function(parts) {
+ var t = '';
+
+ if (parts.protocol) {
+ t += parts.protocol + ':';
+ }
+
+ if (!parts.urn && (t || parts.hostname)) {
+ t += '//';
+ }
+
+ t += (URI.buildAuthority(parts) || '');
+
+ if (typeof parts.path === 'string') {
+ if (parts.path.charAt(0) !== '/' && typeof parts.hostname === 'string') {
+ t += '/';
+ }
+
+ t += parts.path;
+ }
+
+ if (typeof parts.query === 'string' && parts.query) {
+ t += '?' + parts.query;
+ }
+
+ if (typeof parts.fragment === 'string' && parts.fragment) {
+ t += '#' + parts.fragment;
+ }
+ return t;
+ };
+ URI.buildHost = function(parts) {
+ var t = '';
+
+ if (!parts.hostname) {
+ return '';
+ } else if (URI.ip6_expression.test(parts.hostname)) {
+ t += '[' + parts.hostname + ']';
+ } else {
+ t += parts.hostname;
+ }
+
+ if (parts.port) {
+ t += ':' + parts.port;
+ }
+
+ return t;
+ };
+ URI.buildAuthority = function(parts) {
+ return URI.buildUserinfo(parts) + URI.buildHost(parts);
+ };
+ URI.buildUserinfo = function(parts) {
+ var t = '';
+
+ if (parts.username) {
+ t += URI.encode(parts.username);
+
+ if (parts.password) {
+ t += ':' + URI.encode(parts.password);
+ }
+
+ t += '@';
+ }
+
+ return t;
+ };
+ URI.buildQuery = function(data, duplicateQueryParameters, escapeQuerySpace) {
+ // according to http://tools.ietf.org/html/rfc3986 or http://labs.apache.org/webarch/uri/rfc/rfc3986.html
+ // being »-._~!$&'()*+,;=:@/?« %HEX and alnum are allowed
+ // the RFC explicitly states ?/foo being a valid use case, no mention of parameter syntax!
+ // URI.js treats the query string as being application/x-www-form-urlencoded
+ // see http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type
+
+ var t = '';
+ var unique, key, i, length;
+ for (key in data) {
+ if (hasOwn.call(data, key) && key) {
+ if (isArray(data[key])) {
+ unique = {};
+ for (i = 0, length = data[key].length; i < length; i++) {
+ if (data[key][i] !== undefined && unique[data[key][i] + ''] === undefined) {
+ t += '&' + URI.buildQueryParameter(key, data[key][i], escapeQuerySpace);
+ if (duplicateQueryParameters !== true) {
+ unique[data[key][i] + ''] = true;
+ }
+ }
+ }
+ } else if (data[key] !== undefined) {
+ t += '&' + URI.buildQueryParameter(key, data[key], escapeQuerySpace);
+ }
+ }
+ }
+
+ return t.substring(1);
+ };
+ URI.buildQueryParameter = function(name, value, escapeQuerySpace) {
+ // http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type -- application/x-www-form-urlencoded
+ // don't append "=" for null values, according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#url-parameter-serialization
+ return URI.encodeQuery(name, escapeQuerySpace) + (value !== null ? '=' + URI.encodeQuery(value, escapeQuerySpace) : '');
+ };
+
+ URI.addQuery = function(data, name, value) {
+ if (typeof name === 'object') {
+ for (var key in name) {
+ if (hasOwn.call(name, key)) {
+ URI.addQuery(data, key, name[key]);
+ }
+ }
+ } else if (typeof name === 'string') {
+ if (data[name] === undefined) {
+ data[name] = value;
+ return;
+ } else if (typeof data[name] === 'string') {
+ data[name] = [data[name]];
+ }
+
+ if (!isArray(value)) {
+ value = [value];
+ }
+
+ data[name] = (data[name] || []).concat(value);
+ } else {
+ throw new TypeError('URI.addQuery() accepts an object, string as the name parameter');
+ }
+ };
+ URI.removeQuery = function(data, name, value) {
+ var i, length, key;
+
+ if (isArray(name)) {
+ for (i = 0, length = name.length; i < length; i++) {
+ data[name[i]] = undefined;
+ }
+ } else if (getType(name) === 'RegExp') {
+ for (key in data) {
+ if (name.test(key)) {
+ data[key] = undefined;
+ }
+ }
+ } else if (typeof name === 'object') {
+ for (key in name) {
+ if (hasOwn.call(name, key)) {
+ URI.removeQuery(data, key, name[key]);
+ }
+ }
+ } else if (typeof name === 'string') {
+ if (value !== undefined) {
+ if (getType(value) === 'RegExp') {
+ if (!isArray(data[name]) && value.test(data[name])) {
+ data[name] = undefined;
+ } else {
+ data[name] = filterArrayValues(data[name], value);
+ }
+ } else if (data[name] === String(value) && (!isArray(value) || value.length === 1)) {
+ data[name] = undefined;
+ } else if (isArray(data[name])) {
+ data[name] = filterArrayValues(data[name], value);
+ }
+ } else {
+ data[name] = undefined;
+ }
+ } else {
+ throw new TypeError('URI.removeQuery() accepts an object, string, RegExp as the first parameter');
+ }
+ };
+ URI.hasQuery = function(data, name, value, withinArray) {
+ if (typeof name === 'object') {
+ for (var key in name) {
+ if (hasOwn.call(name, key)) {
+ if (!URI.hasQuery(data, key, name[key])) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ } else if (typeof name !== 'string') {
+ throw new TypeError('URI.hasQuery() accepts an object, string as the name parameter');
+ }
+
+ switch (getType(value)) {
+ case 'Undefined':
+ // true if exists (but may be empty)
+ return name in data; // data[name] !== undefined;
+
+ case 'Boolean':
+ // true if exists and non-empty
+ var _booly = Boolean(isArray(data[name]) ? data[name].length : data[name]);
+ return value === _booly;
+
+ case 'Function':
+ // allow complex comparison
+ return !!value(data[name], name, data);
+
+ case 'Array':
+ if (!isArray(data[name])) {
+ return false;
+ }
+
+ var op = withinArray ? arrayContains : arraysEqual;
+ return op(data[name], value);
+
+ case 'RegExp':
+ if (!isArray(data[name])) {
+ return Boolean(data[name] && data[name].match(value));
+ }
+
+ if (!withinArray) {
+ return false;
+ }
+
+ return arrayContains(data[name], value);
+
+ case 'Number':
+ value = String(value);
+ /* falls through */
+ case 'String':
+ if (!isArray(data[name])) {
+ return data[name] === value;
+ }
+
+ if (!withinArray) {
+ return false;
+ }
+
+ return arrayContains(data[name], value);
+
+ default:
+ throw new TypeError('URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter');
+ }
+ };
+
+
+ URI.commonPath = function(one, two) {
+ var length = Math.min(one.length, two.length);
+ var pos;
+
+ // find first non-matching character
+ for (pos = 0; pos < length; pos++) {
+ if (one.charAt(pos) !== two.charAt(pos)) {
+ pos--;
+ break;
+ }
+ }
+
+ if (pos < 1) {
+ return one.charAt(0) === two.charAt(0) && one.charAt(0) === '/' ? '/' : '';
+ }
+
+ // revert to last /
+ if (one.charAt(pos) !== '/' || two.charAt(pos) !== '/') {
+ pos = one.substring(0, pos).lastIndexOf('/');
+ }
+
+ return one.substring(0, pos + 1);
+ };
+
+ URI.withinString = function(string, callback, options) {
+ options || (options = {});
+ var _start = options.start || URI.findUri.start;
+ var _end = options.end || URI.findUri.end;
+ var _trim = options.trim || URI.findUri.trim;
+ var _attributeOpen = /[a-z0-9-]=["']?$/i;
+
+ _start.lastIndex = 0;
+ while (true) {
+ var match = _start.exec(string);
+ if (!match) {
+ break;
+ }
+
+ var start = match.index;
+ if (options.ignoreHtml) {
+ // attribut(e=["']?$)
+ var attributeOpen = string.slice(Math.max(start - 3, 0), start);
+ if (attributeOpen && _attributeOpen.test(attributeOpen)) {
+ continue;
+ }
+ }
+
+ var end = start + string.slice(start).search(_end);
+ var slice = string.slice(start, end).replace(_trim, '');
+ if (options.ignore && options.ignore.test(slice)) {
+ continue;
+ }
+
+ end = start + slice.length;
+ var result = callback(slice, start, end, string);
+ string = string.slice(0, start) + result + string.slice(end);
+ _start.lastIndex = start + result.length;
+ }
+
+ _start.lastIndex = 0;
+ return string;
+ };
+
+ URI.ensureValidHostname = function(v) {
+ // Theoretically URIs allow percent-encoding in Hostnames (according to RFC 3986)
+ // they are not part of DNS and therefore ignored by URI.js
+
+ if (v.match(URI.invalid_hostname_characters)) {
+ // test punycode
+ if (!punycode) {
+ throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-] and Punycode.js is not available');
+ }
+
+ if (punycode.toASCII(v).match(URI.invalid_hostname_characters)) {
+ throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]');
+ }
+ }
+ };
+
+ // noConflict
+ URI.noConflict = function(removeAll) {
+ if (removeAll) {
+ var unconflicted = {
+ URI: this.noConflict()
+ };
+
+ if (root.URITemplate && typeof root.URITemplate.noConflict === 'function') {
+ unconflicted.URITemplate = root.URITemplate.noConflict();
+ }
+
+ if (root.IPv6 && typeof root.IPv6.noConflict === 'function') {
+ unconflicted.IPv6 = root.IPv6.noConflict();
+ }
+
+ if (root.SecondLevelDomains && typeof root.SecondLevelDomains.noConflict === 'function') {
+ unconflicted.SecondLevelDomains = root.SecondLevelDomains.noConflict();
+ }
+
+ return unconflicted;
+ } else if (root.URI === this) {
+ root.URI = _URI;
+ }
+
+ return this;
+ };
+
+ p.build = function(deferBuild) {
+ if (deferBuild === true) {
+ this._deferred_build = true;
+ } else if (deferBuild === undefined || this._deferred_build) {
+ this._string = URI.build(this._parts);
+ this._deferred_build = false;
+ }
+
+ return this;
+ };
+
+ p.clone = function() {
+ return new URI(this);
+ };
+
+ p.valueOf = p.toString = function() {
+ return this.build(false)._string;
+ };
+
+
+ function generateSimpleAccessor(_part){
+ return function(v, build) {
+ if (v === undefined) {
+ return this._parts[_part] || '';
+ } else {
+ this._parts[_part] = v || null;
+ this.build(!build);
+ return this;
+ }
+ };
+ }
+
+ function generatePrefixAccessor(_part, _key){
+ return function(v, build) {
+ if (v === undefined) {
+ return this._parts[_part] || '';
+ } else {
+ if (v !== null) {
+ v = v + '';
+ if (v.charAt(0) === _key) {
+ v = v.substring(1);
+ }
+ }
+
+ this._parts[_part] = v;
+ this.build(!build);
+ return this;
+ }
+ };
+ }
+
+ p.protocol = generateSimpleAccessor('protocol');
+ p.username = generateSimpleAccessor('username');
+ p.password = generateSimpleAccessor('password');
+ p.hostname = generateSimpleAccessor('hostname');
+ p.port = generateSimpleAccessor('port');
+ p.query = generatePrefixAccessor('query', '?');
+ p.fragment = generatePrefixAccessor('fragment', '#');
+
+ p.search = function(v, build) {
+ var t = this.query(v, build);
+ return typeof t === 'string' && t.length ? ('?' + t) : t;
+ };
+ p.hash = function(v, build) {
+ var t = this.fragment(v, build);
+ return typeof t === 'string' && t.length ? ('#' + t) : t;
+ };
+
+ p.pathname = function(v, build) {
+ if (v === undefined || v === true) {
+ var res = this._parts.path || (this._parts.hostname ? '/' : '');
+ return v ? (this._parts.urn ? URI.decodeUrnPath : URI.decodePath)(res) : res;
+ } else {
+ if (this._parts.urn) {
+ this._parts.path = v ? URI.recodeUrnPath(v) : '';
+ } else {
+ this._parts.path = v ? URI.recodePath(v) : '/';
+ }
+ this.build(!build);
+ return this;
+ }
+ };
+ p.path = p.pathname;
+ p.href = function(href, build) {
+ var key;
+
+ if (href === undefined) {
+ return this.toString();
+ }
+
+ this._string = '';
+ this._parts = URI._parts();
+
+ var _URI = href instanceof URI;
+ var _object = typeof href === 'object' && (href.hostname || href.path || href.pathname);
+ if (href.nodeName) {
+ var attribute = URI.getDomAttribute(href);
+ href = href[attribute] || '';
+ _object = false;
+ }
+
+ // window.location is reported to be an object, but it's not the sort
+ // of object we're looking for:
+ // * location.protocol ends with a colon
+ // * location.query != object.search
+ // * location.hash != object.fragment
+ // simply serializing the unknown object should do the trick
+ // (for location, not for everything...)
+ if (!_URI && _object && href.pathname !== undefined) {
+ href = href.toString();
+ }
+
+ if (typeof href === 'string' || href instanceof String) {
+ this._parts = URI.parse(String(href), this._parts);
+ } else if (_URI || _object) {
+ var src = _URI ? href._parts : href;
+ for (key in src) {
+ if (hasOwn.call(this._parts, key)) {
+ this._parts[key] = src[key];
+ }
+ }
+ } else {
+ throw new TypeError('invalid input');
+ }
+
+ this.build(!build);
+ return this;
+ };
+
+ // identification accessors
+ p.is = function(what) {
+ var ip = false;
+ var ip4 = false;
+ var ip6 = false;
+ var name = false;
+ var sld = false;
+ var idn = false;
+ var punycode = false;
+ var relative = !this._parts.urn;
+
+ if (this._parts.hostname) {
+ relative = false;
+ ip4 = URI.ip4_expression.test(this._parts.hostname);
+ ip6 = URI.ip6_expression.test(this._parts.hostname);
+ ip = ip4 || ip6;
+ name = !ip;
+ sld = name && SLD && SLD.has(this._parts.hostname);
+ idn = name && URI.idn_expression.test(this._parts.hostname);
+ punycode = name && URI.punycode_expression.test(this._parts.hostname);
+ }
+
+ switch (what.toLowerCase()) {
+ case 'relative':
+ return relative;
+
+ case 'absolute':
+ return !relative;
+
+ // hostname identification
+ case 'domain':
+ case 'name':
+ return name;
+
+ case 'sld':
+ return sld;
+
+ case 'ip':
+ return ip;
+
+ case 'ip4':
+ case 'ipv4':
+ case 'inet4':
+ return ip4;
+
+ case 'ip6':
+ case 'ipv6':
+ case 'inet6':
+ return ip6;
+
+ case 'idn':
+ return idn;
+
+ case 'url':
+ return !this._parts.urn;
+
+ case 'urn':
+ return !!this._parts.urn;
+
+ case 'punycode':
+ return punycode;
+ }
+
+ return null;
+ };
+
+ // component specific input validation
+ var _protocol = p.protocol;
+ var _port = p.port;
+ var _hostname = p.hostname;
+
+ p.protocol = function(v, build) {
+ if (v !== undefined) {
+ if (v) {
+ // accept trailing ://
+ v = v.replace(/:(\/\/)?$/, '');
+
+ if (!v.match(URI.protocol_expression)) {
+ throw new TypeError('Protocol "' + v + '" contains characters other than [A-Z0-9.+-] or doesn\'t start with [A-Z]');
+ }
+ }
+ }
+ return _protocol.call(this, v, build);
+ };
+ p.scheme = p.protocol;
+ p.port = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (v !== undefined) {
+ if (v === 0) {
+ v = null;
+ }
+
+ if (v) {
+ v += '';
+ if (v.charAt(0) === ':') {
+ v = v.substring(1);
+ }
+
+ if (v.match(/[^0-9]/)) {
+ throw new TypeError('Port "' + v + '" contains characters other than [0-9]');
+ }
+ }
+ }
+ return _port.call(this, v, build);
+ };
+ p.hostname = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (v !== undefined) {
+ var x = {};
+ var res = URI.parseHost(v, x);
+ if (res !== '/') {
+ throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]');
+ }
+
+ v = x.hostname;
+ }
+ return _hostname.call(this, v, build);
+ };
+
+ // compound accessors
+ p.origin = function(v, build) {
+ var parts;
+
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (v === undefined) {
+ var protocol = this.protocol();
+ var authority = this.authority();
+ if (!authority) return '';
+ return (protocol ? protocol + '://' : '') + this.authority();
+ } else {
+ var origin = URI(v);
+ this
+ .protocol(origin.protocol())
+ .authority(origin.authority())
+ .build(!build);
+ return this;
+ }
+ };
+ p.host = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (v === undefined) {
+ return this._parts.hostname ? URI.buildHost(this._parts) : '';
+ } else {
+ var res = URI.parseHost(v, this._parts);
+ if (res !== '/') {
+ throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]');
+ }
+
+ this.build(!build);
+ return this;
+ }
+ };
+ p.authority = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (v === undefined) {
+ return this._parts.hostname ? URI.buildAuthority(this._parts) : '';
+ } else {
+ var res = URI.parseAuthority(v, this._parts);
+ if (res !== '/') {
+ throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]');
+ }
+
+ this.build(!build);
+ return this;
+ }
+ };
+ p.userinfo = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (v === undefined) {
+ if (!this._parts.username) {
+ return '';
+ }
+
+ var t = URI.buildUserinfo(this._parts);
+ return t.substring(0, t.length -1);
+ } else {
+ if (v[v.length-1] !== '@') {
+ v += '@';
+ }
+
+ URI.parseUserinfo(v, this._parts);
+ this.build(!build);
+ return this;
+ }
+ };
+ p.resource = function(v, build) {
+ var parts;
+
+ if (v === undefined) {
+ return this.path() + this.search() + this.hash();
+ }
+
+ parts = URI.parse(v);
+ this._parts.path = parts.path;
+ this._parts.query = parts.query;
+ this._parts.fragment = parts.fragment;
+ this.build(!build);
+ return this;
+ };
+
+ // fraction accessors
+ p.subdomain = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ // convenience, return "www" from "www.example.org"
+ if (v === undefined) {
+ if (!this._parts.hostname || this.is('IP')) {
+ return '';
+ }
+
+ // grab domain and add another segment
+ var end = this._parts.hostname.length - this.domain().length - 1;
+ return this._parts.hostname.substring(0, end) || '';
+ } else {
+ var e = this._parts.hostname.length - this.domain().length;
+ var sub = this._parts.hostname.substring(0, e);
+ var replace = new RegExp('^' + escapeRegEx(sub));
+
+ if (v && v.charAt(v.length - 1) !== '.') {
+ v += '.';
+ }
+
+ if (v) {
+ URI.ensureValidHostname(v);
+ }
+
+ this._parts.hostname = this._parts.hostname.replace(replace, v);
+ this.build(!build);
+ return this;
+ }
+ };
+ p.domain = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (typeof v === 'boolean') {
+ build = v;
+ v = undefined;
+ }
+
+ // convenience, return "example.org" from "www.example.org"
+ if (v === undefined) {
+ if (!this._parts.hostname || this.is('IP')) {
+ return '';
+ }
+
+ // if hostname consists of 1 or 2 segments, it must be the domain
+ var t = this._parts.hostname.match(/\./g);
+ if (t && t.length < 2) {
+ return this._parts.hostname;
+ }
+
+ // grab tld and add another segment
+ var end = this._parts.hostname.length - this.tld(build).length - 1;
+ end = this._parts.hostname.lastIndexOf('.', end -1) + 1;
+ return this._parts.hostname.substring(end) || '';
+ } else {
+ if (!v) {
+ throw new TypeError('cannot set domain empty');
+ }
+
+ URI.ensureValidHostname(v);
+
+ if (!this._parts.hostname || this.is('IP')) {
+ this._parts.hostname = v;
+ } else {
+ var replace = new RegExp(escapeRegEx(this.domain()) + '$');
+ this._parts.hostname = this._parts.hostname.replace(replace, v);
+ }
+
+ this.build(!build);
+ return this;
+ }
+ };
+ p.tld = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (typeof v === 'boolean') {
+ build = v;
+ v = undefined;
+ }
+
+ // return "org" from "www.example.org"
+ if (v === undefined) {
+ if (!this._parts.hostname || this.is('IP')) {
+ return '';
+ }
+
+ var pos = this._parts.hostname.lastIndexOf('.');
+ var tld = this._parts.hostname.substring(pos + 1);
+
+ if (build !== true && SLD && SLD.list[tld.toLowerCase()]) {
+ return SLD.get(this._parts.hostname) || tld;
+ }
+
+ return tld;
+ } else {
+ var replace;
+
+ if (!v) {
+ throw new TypeError('cannot set TLD empty');
+ } else if (v.match(/[^a-zA-Z0-9-]/)) {
+ if (SLD && SLD.is(v)) {
+ replace = new RegExp(escapeRegEx(this.tld()) + '$');
+ this._parts.hostname = this._parts.hostname.replace(replace, v);
+ } else {
+ throw new TypeError('TLD "' + v + '" contains characters other than [A-Z0-9]');
+ }
+ } else if (!this._parts.hostname || this.is('IP')) {
+ throw new ReferenceError('cannot set TLD on non-domain host');
+ } else {
+ replace = new RegExp(escapeRegEx(this.tld()) + '$');
+ this._parts.hostname = this._parts.hostname.replace(replace, v);
+ }
+
+ this.build(!build);
+ return this;
+ }
+ };
+ p.directory = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (v === undefined || v === true) {
+ if (!this._parts.path && !this._parts.hostname) {
+ return '';
+ }
+
+ if (this._parts.path === '/') {
+ return '/';
+ }
+
+ var end = this._parts.path.length - this.filename().length - 1;
+ var res = this._parts.path.substring(0, end) || (this._parts.hostname ? '/' : '');
+
+ return v ? URI.decodePath(res) : res;
+
+ } else {
+ var e = this._parts.path.length - this.filename().length;
+ var directory = this._parts.path.substring(0, e);
+ var replace = new RegExp('^' + escapeRegEx(directory));
+
+ // fully qualifier directories begin with a slash
+ if (!this.is('relative')) {
+ if (!v) {
+ v = '/';
+ }
+
+ if (v.charAt(0) !== '/') {
+ v = '/' + v;
+ }
+ }
+
+ // directories always end with a slash
+ if (v && v.charAt(v.length - 1) !== '/') {
+ v += '/';
+ }
+
+ v = URI.recodePath(v);
+ this._parts.path = this._parts.path.replace(replace, v);
+ this.build(!build);
+ return this;
+ }
+ };
+ p.filename = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (v === undefined || v === true) {
+ if (!this._parts.path || this._parts.path === '/') {
+ return '';
+ }
+
+ var pos = this._parts.path.lastIndexOf('/');
+ var res = this._parts.path.substring(pos+1);
+
+ return v ? URI.decodePathSegment(res) : res;
+ } else {
+ var mutatedDirectory = false;
+
+ if (v.charAt(0) === '/') {
+ v = v.substring(1);
+ }
+
+ if (v.match(/\.?\//)) {
+ mutatedDirectory = true;
+ }
+
+ var replace = new RegExp(escapeRegEx(this.filename()) + '$');
+ v = URI.recodePath(v);
+ this._parts.path = this._parts.path.replace(replace, v);
+
+ if (mutatedDirectory) {
+ this.normalizePath(build);
+ } else {
+ this.build(!build);
+ }
+
+ return this;
+ }
+ };
+ p.suffix = function(v, build) {
+ if (this._parts.urn) {
+ return v === undefined ? '' : this;
+ }
+
+ if (v === undefined || v === true) {
+ if (!this._parts.path || this._parts.path === '/') {
+ return '';
+ }
+
+ var filename = this.filename();
+ var pos = filename.lastIndexOf('.');
+ var s, res;
+
+ if (pos === -1) {
+ return '';
+ }
+
+ // suffix may only contain alnum characters (yup, I made this up.)
+ s = filename.substring(pos+1);
+ res = (/^[a-z0-9%]+$/i).test(s) ? s : '';
+ return v ? URI.decodePathSegment(res) : res;
+ } else {
+ if (v.charAt(0) === '.') {
+ v = v.substring(1);
+ }
+
+ var suffix = this.suffix();
+ var replace;
+
+ if (!suffix) {
+ if (!v) {
+ return this;
+ }
+
+ this._parts.path += '.' + URI.recodePath(v);
+ } else if (!v) {
+ replace = new RegExp(escapeRegEx('.' + suffix) + '$');
+ } else {
+ replace = new RegExp(escapeRegEx(suffix) + '$');
+ }
+
+ if (replace) {
+ v = URI.recodePath(v);
+ this._parts.path = this._parts.path.replace(replace, v);
+ }
+
+ this.build(!build);
+ return this;
+ }
+ };
+ p.segment = function(segment, v, build) {
+ var separator = this._parts.urn ? ':' : '/';
+ var path = this.path();
+ var absolute = path.substring(0, 1) === '/';
+ var segments = path.split(separator);
+
+ if (segment !== undefined && typeof segment !== 'number') {
+ build = v;
+ v = segment;
+ segment = undefined;
+ }
+
+ if (segment !== undefined && typeof segment !== 'number') {
+ throw new Error('Bad segment "' + segment + '", must be 0-based integer');
+ }
+
+ if (absolute) {
+ segments.shift();
+ }
+
+ if (segment < 0) {
+ // allow negative indexes to address from the end
+ segment = Math.max(segments.length + segment, 0);
+ }
+
+ if (v === undefined) {
+ /*jshint laxbreak: true */
+ return segment === undefined
+ ? segments
+ : segments[segment];
+ /*jshint laxbreak: false */
+ } else if (segment === null || segments[segment] === undefined) {
+ if (isArray(v)) {
+ segments = [];
+ // collapse empty elements within array
+ for (var i=0, l=v.length; i < l; i++) {
+ if (!v[i].length && (!segments.length || !segments[segments.length -1].length)) {
+ continue;
+ }
+
+ if (segments.length && !segments[segments.length -1].length) {
+ segments.pop();
+ }
+
+ segments.push(trimSlashes(v[i]));
+ }
+ } else if (v || typeof v === 'string') {
+ v = trimSlashes(v);
+ if (segments[segments.length -1] === '') {
+ // empty trailing elements have to be overwritten
+ // to prevent results such as /foo//bar
+ segments[segments.length -1] = v;
+ } else {
+ segments.push(v);
+ }
+ }
+ } else {
+ if (v) {
+ segments[segment] = trimSlashes(v);
+ } else {
+ segments.splice(segment, 1);
+ }
+ }
+
+ if (absolute) {
+ segments.unshift('');
+ }
+
+ return this.path(segments.join(separator), build);
+ };
+ p.segmentCoded = function(segment, v, build) {
+ var segments, i, l;
+
+ if (typeof segment !== 'number') {
+ build = v;
+ v = segment;
+ segment = undefined;
+ }
+
+ if (v === undefined) {
+ segments = this.segment(segment, v, build);
+ if (!isArray(segments)) {
+ segments = segments !== undefined ? URI.decode(segments) : undefined;
+ } else {
+ for (i = 0, l = segments.length; i < l; i++) {
+ segments[i] = URI.decode(segments[i]);
+ }
+ }
+
+ return segments;
+ }
+
+ if (!isArray(v)) {
+ v = (typeof v === 'string' || v instanceof String) ? URI.encode(v) : v;
+ } else {
+ for (i = 0, l = v.length; i < l; i++) {
+ v[i] = URI.encode(v[i]);
+ }
+ }
+
+ return this.segment(segment, v, build);
+ };
+
+ // mutating query string
+ var q = p.query;
+ p.query = function(v, build) {
+ if (v === true) {
+ return URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
+ } else if (typeof v === 'function') {
+ var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
+ var result = v.call(this, data);
+ this._parts.query = URI.buildQuery(result || data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
+ this.build(!build);
+ return this;
+ } else if (v !== undefined && typeof v !== 'string') {
+ this._parts.query = URI.buildQuery(v, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
+ this.build(!build);
+ return this;
+ } else {
+ return q.call(this, v, build);
+ }
+ };
+ p.setQuery = function(name, value, build) {
+ var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
+
+ if (typeof name === 'string' || name instanceof String) {
+ data[name] = value !== undefined ? value : null;
+ } else if (typeof name === 'object') {
+ for (var key in name) {
+ if (hasOwn.call(name, key)) {
+ data[key] = name[key];
+ }
+ }
+ } else {
+ throw new TypeError('URI.addQuery() accepts an object, string as the name parameter');
+ }
+
+ this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
+ if (typeof name !== 'string') {
+ build = value;
+ }
+
+ this.build(!build);
+ return this;
+ };
+ p.addQuery = function(name, value, build) {
+ var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
+ URI.addQuery(data, name, value === undefined ? null : value);
+ this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
+ if (typeof name !== 'string') {
+ build = value;
+ }
+
+ this.build(!build);
+ return this;
+ };
+ p.removeQuery = function(name, value, build) {
+ var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
+ URI.removeQuery(data, name, value);
+ this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
+ if (typeof name !== 'string') {
+ build = value;
+ }
+
+ this.build(!build);
+ return this;
+ };
+ p.hasQuery = function(name, value, withinArray) {
+ var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
+ return URI.hasQuery(data, name, value, withinArray);
+ };
+ p.setSearch = p.setQuery;
+ p.addSearch = p.addQuery;
+ p.removeSearch = p.removeQuery;
+ p.hasSearch = p.hasQuery;
+
+ // sanitizing URLs
+ p.normalize = function() {
+ if (this._parts.urn) {
+ return this
+ .normalizeProtocol(false)
+ .normalizePath(false)
+ .normalizeQuery(false)
+ .normalizeFragment(false)
+ .build();
+ }
+
+ return this
+ .normalizeProtocol(false)
+ .normalizeHostname(false)
+ .normalizePort(false)
+ .normalizePath(false)
+ .normalizeQuery(false)
+ .normalizeFragment(false)
+ .build();
+ };
+ p.normalizeProtocol = function(build) {
+ if (typeof this._parts.protocol === 'string') {
+ this._parts.protocol = this._parts.protocol.toLowerCase();
+ this.build(!build);
+ }
+
+ return this;
+ };
+ p.normalizeHostname = function(build) {
+ if (this._parts.hostname) {
+ if (this.is('IDN') && punycode) {
+ this._parts.hostname = punycode.toASCII(this._parts.hostname);
+ } else if (this.is('IPv6') && IPv6) {
+ this._parts.hostname = IPv6.best(this._parts.hostname);
+ }
+
+ this._parts.hostname = this._parts.hostname.toLowerCase();
+ this.build(!build);
+ }
+
+ return this;
+ };
+ p.normalizePort = function(build) {
+ // remove port of it's the protocol's default
+ if (typeof this._parts.protocol === 'string' && this._parts.port === URI.defaultPorts[this._parts.protocol]) {
+ this._parts.port = null;
+ this.build(!build);
+ }
+
+ return this;
+ };
+ p.normalizePath = function(build) {
+ var _path = this._parts.path;
+ if (!_path) {
+ return this;
+ }
+
+ if (this._parts.urn) {
+ this._parts.path = URI.recodeUrnPath(this._parts.path);
+ this.build(!build);
+ return this;
+ }
+
+ if (this._parts.path === '/') {
+ return this;
+ }
+
+ var _was_relative;
+ var _leadingParents = '';
+ var _parent, _pos;
+
+ // handle relative paths
+ if (_path.charAt(0) !== '/') {
+ _was_relative = true;
+ _path = '/' + _path;
+ }
+
+ // handle relative files (as opposed to directories)
+ if (_path.slice(-3) === '/..' || _path.slice(-2) === '/.') {
+ _path += '/';
+ }
+
+ // resolve simples
+ _path = _path
+ .replace(/(\/(\.\/)+)|(\/\.$)/g, '/')
+ .replace(/\/{2,}/g, '/');
+
+ // remember leading parents
+ if (_was_relative) {
+ _leadingParents = _path.substring(1).match(/^(\.\.\/)+/) || '';
+ if (_leadingParents) {
+ _leadingParents = _leadingParents[0];
+ }
+ }
+
+ // resolve parents
+ while (true) {
+ _parent = _path.indexOf('/..');
+ if (_parent === -1) {
+ // no more ../ to resolve
+ break;
+ } else if (_parent === 0) {
+ // top level cannot be relative, skip it
+ _path = _path.substring(3);
+ continue;
+ }
+
+ _pos = _path.substring(0, _parent).lastIndexOf('/');
+ if (_pos === -1) {
+ _pos = _parent;
+ }
+ _path = _path.substring(0, _pos) + _path.substring(_parent + 3);
+ }
+
+ // revert to relative
+ if (_was_relative && this.is('relative')) {
+ _path = _leadingParents + _path.substring(1);
+ }
+
+ _path = URI.recodePath(_path);
+ this._parts.path = _path;
+ this.build(!build);
+ return this;
+ };
+ p.normalizePathname = p.normalizePath;
+ p.normalizeQuery = function(build) {
+ if (typeof this._parts.query === 'string') {
+ if (!this._parts.query.length) {
+ this._parts.query = null;
+ } else {
+ this.query(URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace));
+ }
+
+ this.build(!build);
+ }
+
+ return this;
+ };
+ p.normalizeFragment = function(build) {
+ if (!this._parts.fragment) {
+ this._parts.fragment = null;
+ this.build(!build);
+ }
+
+ return this;
+ };
+ p.normalizeSearch = p.normalizeQuery;
+ p.normalizeHash = p.normalizeFragment;
+
+ p.iso8859 = function() {
+ // expect unicode input, iso8859 output
+ var e = URI.encode;
+ var d = URI.decode;
+
+ URI.encode = escape;
+ URI.decode = decodeURIComponent;
+ try {
+ this.normalize();
+ } finally {
+ URI.encode = e;
+ URI.decode = d;
+ }
+ return this;
+ };
+
+ p.unicode = function() {
+ // expect iso8859 input, unicode output
+ var e = URI.encode;
+ var d = URI.decode;
+
+ URI.encode = strictEncodeURIComponent;
+ URI.decode = unescape;
+ try {
+ this.normalize();
+ } finally {
+ URI.encode = e;
+ URI.decode = d;
+ }
+ return this;
+ };
+
+ p.readable = function() {
+ var uri = this.clone();
+ // removing username, password, because they shouldn't be displayed according to RFC 3986
+ uri.username('').password('').normalize();
+ var t = '';
+ if (uri._parts.protocol) {
+ t += uri._parts.protocol + '://';
+ }
+
+ if (uri._parts.hostname) {
+ if (uri.is('punycode') && punycode) {
+ t += punycode.toUnicode(uri._parts.hostname);
+ if (uri._parts.port) {
+ t += ':' + uri._parts.port;
+ }
+ } else {
+ t += uri.host();
+ }
+ }
+
+ if (uri._parts.hostname && uri._parts.path && uri._parts.path.charAt(0) !== '/') {
+ t += '/';
+ }
+
+ t += uri.path(true);
+ if (uri._parts.query) {
+ var q = '';
+ for (var i = 0, qp = uri._parts.query.split('&'), l = qp.length; i < l; i++) {
+ var kv = (qp[i] || '').split('=');
+ q += '&' + URI.decodeQuery(kv[0], this._parts.escapeQuerySpace)
+ .replace(/&/g, '%26');
+
+ if (kv[1] !== undefined) {
+ q += '=' + URI.decodeQuery(kv[1], this._parts.escapeQuerySpace)
+ .replace(/&/g, '%26');
+ }
+ }
+ t += '?' + q.substring(1);
+ }
+
+ t += URI.decodeQuery(uri.hash(), true);
+ return t;
+ };
+
+ // resolving relative and absolute URLs
+ p.absoluteTo = function(base) {
+ var resolved = this.clone();
+ var properties = ['protocol', 'username', 'password', 'hostname', 'port'];
+ var basedir, i, p;
+
+ if (this._parts.urn) {
+ throw new Error('URNs do not have any generally defined hierarchical components');
+ }
+
+ if (!(base instanceof URI)) {
+ base = new URI(base);
+ }
+
+ if (!resolved._parts.protocol) {
+ resolved._parts.protocol = base._parts.protocol;
+ }
+
+ if (this._parts.hostname) {
+ return resolved;
+ }
+
+ for (i = 0; (p = properties[i]); i++) {
+ resolved._parts[p] = base._parts[p];
+ }
+
+ if (!resolved._parts.path) {
+ resolved._parts.path = base._parts.path;
+ if (!resolved._parts.query) {
+ resolved._parts.query = base._parts.query;
+ }
+ } else if (resolved._parts.path.substring(-2) === '..') {
+ resolved._parts.path += '/';
+ }
+
+ if (resolved.path().charAt(0) !== '/') {
+ basedir = base.directory();
+ basedir = basedir ? basedir : base.path().indexOf('/') === 0 ? '/' : '';
+ resolved._parts.path = (basedir ? (basedir + '/') : '') + resolved._parts.path;
+ resolved.normalizePath();
+ }
+
+ resolved.build();
+ return resolved;
+ };
+ p.relativeTo = function(base) {
+ var relative = this.clone().normalize();
+ var relativeParts, baseParts, common, relativePath, basePath;
+
+ if (relative._parts.urn) {
+ throw new Error('URNs do not have any generally defined hierarchical components');
+ }
+
+ base = new URI(base).normalize();
+ relativeParts = relative._parts;
+ baseParts = base._parts;
+ relativePath = relative.path();
+ basePath = base.path();
+
+ if (relativePath.charAt(0) !== '/') {
+ throw new Error('URI is already relative');
+ }
+
+ if (basePath.charAt(0) !== '/') {
+ throw new Error('Cannot calculate a URI relative to another relative URI');
+ }
+
+ if (relativeParts.protocol === baseParts.protocol) {
+ relativeParts.protocol = null;
+ }
+
+ if (relativeParts.username !== baseParts.username || relativeParts.password !== baseParts.password) {
+ return relative.build();
+ }
+
+ if (relativeParts.protocol !== null || relativeParts.username !== null || relativeParts.password !== null) {
+ return relative.build();
+ }
+
+ if (relativeParts.hostname === baseParts.hostname && relativeParts.port === baseParts.port) {
+ relativeParts.hostname = null;
+ relativeParts.port = null;
+ } else {
+ return relative.build();
+ }
+
+ if (relativePath === basePath) {
+ relativeParts.path = '';
+ return relative.build();
+ }
+
+ // determine common sub path
+ common = URI.commonPath(relativePath, basePath);
+
+ // If the paths have nothing in common, return a relative URL with the absolute path.
+ if (!common) {
+ return relative.build();
+ }
+
+ var parents = baseParts.path
+ .substring(common.length)
+ .replace(/[^\/]*$/, '')
+ .replace(/.*?\//g, '../');
+
+ relativeParts.path = (parents + relativeParts.path.substring(common.length)) || './';
+
+ return relative.build();
+ };
+
+ // comparing URIs
+ p.equals = function(uri) {
+ var one = this.clone();
+ var two = new URI(uri);
+ var one_map = {};
+ var two_map = {};
+ var checked = {};
+ var one_query, two_query, key;
+
+ one.normalize();
+ two.normalize();
+
+ // exact match
+ if (one.toString() === two.toString()) {
+ return true;
+ }
+
+ // extract query string
+ one_query = one.query();
+ two_query = two.query();
+ one.query('');
+ two.query('');
+
+ // definitely not equal if not even non-query parts match
+ if (one.toString() !== two.toString()) {
+ return false;
+ }
+
+ // query parameters have the same length, even if they're permuted
+ if (one_query.length !== two_query.length) {
+ return false;
+ }
+
+ one_map = URI.parseQuery(one_query, this._parts.escapeQuerySpace);
+ two_map = URI.parseQuery(two_query, this._parts.escapeQuerySpace);
+
+ for (key in one_map) {
+ if (hasOwn.call(one_map, key)) {
+ if (!isArray(one_map[key])) {
+ if (one_map[key] !== two_map[key]) {
+ return false;
+ }
+ } else if (!arraysEqual(one_map[key], two_map[key])) {
+ return false;
+ }
+
+ checked[key] = true;
+ }
+ }
+
+ for (key in two_map) {
+ if (hasOwn.call(two_map, key)) {
+ if (!checked[key]) {
+ // two contains a parameter not present in one
+ return false;
+ }
+ }
+ }
+
+ return true;
+ };
+
+ // state
+ p.duplicateQueryParameters = function(v) {
+ this._parts.duplicateQueryParameters = !!v;
+ return this;
+ };
+
+ p.escapeQuerySpace = function(v) {
+ this._parts.escapeQuerySpace = !!v;
+ return this;
+ };
+
+ return URI;
+}));
+
+},{"./IPv6":1,"./SecondLevelDomains":2,"./punycode":4}],4:[function(require,module,exports){
+(function (global){
+/*! http://mths.be/punycode v1.2.3 by @mathias */
+;(function(root) {
+
+ /** Detect free variables */
+ var freeExports = typeof exports == 'object' && exports;
+ var freeModule = typeof module == 'object' && module &&
+ module.exports == freeExports && module;
+ var freeGlobal = typeof global == 'object' && global;
+ if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
+ root = freeGlobal;
+ }
+
+ /**
+ * The `punycode` object.
+ * @name punycode
+ * @type Object
+ */
+ var punycode,
+
+ /** Highest positive signed 32-bit float value */
+ maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+ /** Bootstring parameters */
+ base = 36,
+ tMin = 1,
+ tMax = 26,
+ skew = 38,
+ damp = 700,
+ initialBias = 72,
+ initialN = 128, // 0x80
+ delimiter = '-', // '\x2D'
+
+ /** Regular expressions */
+ regexPunycode = /^xn--/,
+ regexNonASCII = /[^ -~]/, // unprintable ASCII chars + non-ASCII chars
+ regexSeparators = /\x2E|\u3002|\uFF0E|\uFF61/g, // RFC 3490 separators
+
+ /** Error messages */
+ errors = {
+ 'overflow': 'Overflow: input needs wider integers to process',
+ 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+ 'invalid-input': 'Invalid input'
+ },
+
+ /** Convenience shortcuts */
+ baseMinusTMin = base - tMin,
+ floor = Math.floor,
+ stringFromCharCode = String.fromCharCode,
+
+ /** Temporary variable */
+ key;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A generic error utility function.
+ * @private
+ * @param {String} type The error type.
+ * @returns {Error} Throws a `RangeError` with the applicable error message.
+ */
+ function error(type) {
+ throw RangeError(errors[type]);
+ }
+
+ /**
+ * A generic `Array#map` utility function.
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function that gets called for every array
+ * item.
+ * @returns {Array} A new array of values returned by the callback function.
+ */
+ function map(array, fn) {
+ var length = array.length;
+ while (length--) {
+ array[length] = fn(array[length]);
+ }
+ return array;
+ }
+
+ /**
+ * A simple `Array#map`-like wrapper to work with domain name strings.
+ * @private
+ * @param {String} domain The domain name.
+ * @param {Function} callback The function that gets called for every
+ * character.
+ * @returns {Array} A new string of characters returned by the callback
+ * function.
+ */
+ function mapDomain(string, fn) {
+ return map(string.split(regexSeparators), fn).join('.');
+ }
+
+ /**
+ * Creates an array containing the numeric code points of each Unicode
+ * character in the string. While JavaScript uses UCS-2 internally,
+ * this function will convert a pair of surrogate halves (each of which
+ * UCS-2 exposes as separate characters) into a single code point,
+ * matching UTF-16.
+ * @see `punycode.ucs2.encode`
+ * @see <http://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode.ucs2
+ * @name decode
+ * @param {String} string The Unicode input string (UCS-2).
+ * @returns {Array} The new array of code points.
+ */
+ function ucs2decode(string) {
+ var output = [],
+ counter = 0,
+ length = string.length,
+ value,
+ extra;
+ while (counter < length) {
+ value = string.charCodeAt(counter++);
+ if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+ // high surrogate, and there is a next character
+ extra = string.charCodeAt(counter++);
+ if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+ output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+ } else {
+ // unmatched surrogate; only append this code unit, in case the next
+ // code unit is the high surrogate of a surrogate pair
+ output.push(value);
+ counter--;
+ }
+ } else {
+ output.push(value);
+ }
+ }
+ return output;
+ }
+
+ /**
+ * Creates a string based on an array of numeric code points.
+ * @see `punycode.ucs2.decode`
+ * @memberOf punycode.ucs2
+ * @name encode
+ * @param {Array} codePoints The array of numeric code points.
+ * @returns {String} The new Unicode string (UCS-2).
+ */
+ function ucs2encode(array) {
+ return map(array, function(value) {
+ var output = '';
+ if (value > 0xFFFF) {
+ value -= 0x10000;
+ output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+ value = 0xDC00 | value & 0x3FF;
+ }
+ output += stringFromCharCode(value);
+ return output;
+ }).join('');
+ }
+
+ /**
+ * Converts a basic code point into a digit/integer.
+ * @see `digitToBasic()`
+ * @private
+ * @param {Number} codePoint The basic numeric code point value.
+ * @returns {Number} The numeric value of a basic code point (for use in
+ * representing integers) in the range `0` to `base - 1`, or `base` if
+ * the code point does not represent a value.
+ */
+ function basicToDigit(codePoint) {
+ if (codePoint - 48 < 10) {
+ return codePoint - 22;
+ }
+ if (codePoint - 65 < 26) {
+ return codePoint - 65;
+ }
+ if (codePoint - 97 < 26) {
+ return codePoint - 97;
+ }
+ return base;
+ }
+
+ /**
+ * Converts a digit/integer into a basic code point.
+ * @see `basicToDigit()`
+ * @private
+ * @param {Number} digit The numeric value of a basic code point.
+ * @returns {Number} The basic code point whose value (when used for
+ * representing integers) is `digit`, which needs to be in the range
+ * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+ * used; else, the lowercase form is used. The behavior is undefined
+ * if `flag` is non-zero and `digit` has no uppercase form.
+ */
+ function digitToBasic(digit, flag) {
+ // 0..25 map to ASCII a..z or A..Z
+ // 26..35 map to ASCII 0..9
+ return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+ }
+
+ /**
+ * Bias adaptation function as per section 3.4 of RFC 3492.
+ * http://tools.ietf.org/html/rfc3492#section-3.4
+ * @private
+ */
+ function adapt(delta, numPoints, firstTime) {
+ var k = 0;
+ delta = firstTime ? floor(delta / damp) : delta >> 1;
+ delta += floor(delta / numPoints);
+ for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+ delta = floor(delta / baseMinusTMin);
+ }
+ return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+ }
+
+ /**
+ * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+ * symbols.
+ * @memberOf punycode
+ * @param {String} input The Punycode string of ASCII-only symbols.
+ * @returns {String} The resulting string of Unicode symbols.
+ */
+ function decode(input) {
+ // Don't use UCS-2
+ var output = [],
+ inputLength = input.length,
+ out,
+ i = 0,
+ n = initialN,
+ bias = initialBias,
+ basic,
+ j,
+ index,
+ oldi,
+ w,
+ k,
+ digit,
+ t,
+ length,
+ /** Cached calculation results */
+ baseMinusT;
+
+ // Handle the basic code points: let `basic` be the number of input code
+ // points before the last delimiter, or `0` if there is none, then copy
+ // the first basic code points to the output.
+
+ basic = input.lastIndexOf(delimiter);
+ if (basic < 0) {
+ basic = 0;
+ }
+
+ for (j = 0; j < basic; ++j) {
+ // if it's not a basic code point
+ if (input.charCodeAt(j) >= 0x80) {
+ error('not-basic');
+ }
+ output.push(input.charCodeAt(j));
+ }
+
+ // Main decoding loop: start just after the last delimiter if any basic code
+ // points were copied; start at the beginning otherwise.
+
+ for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+ // `index` is the index of the next character to be consumed.
+ // 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`.
+ for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+ if (index >= inputLength) {
+ error('invalid-input');
+ }
+
+ digit = basicToDigit(input.charCodeAt(index++));
+
+ if (digit >= base || digit > floor((maxInt - i) / w)) {
+ error('overflow');
+ }
+
+ i += digit * w;
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+ if (digit < t) {
+ break;
+ }
+
+ baseMinusT = base - t;
+ if (w > floor(maxInt / baseMinusT)) {
+ error('overflow');
+ }
+
+ w *= baseMinusT;
+
+ }
+
+ out = output.length + 1;
+ bias = adapt(i - oldi, out, oldi == 0);
+
+ // `i` was supposed to wrap around from `out` to `0`,
+ // incrementing `n` each time, so we'll fix that now:
+ if (floor(i / out) > maxInt - n) {
+ error('overflow');
+ }
+
+ n += floor(i / out);
+ i %= out;
+
+ // Insert `n` at position `i` of the output
+ output.splice(i++, 0, n);
+
+ }
+
+ return ucs2encode(output);
+ }
+
+ /**
+ * Converts a string of Unicode symbols to a Punycode string of ASCII-only
+ * symbols.
+ * @memberOf punycode
+ * @param {String} input The string of Unicode symbols.
+ * @returns {String} The resulting Punycode string of ASCII-only symbols.
+ */
+ function encode(input) {
+ var n,
+ delta,
+ handledCPCount,
+ basicLength,
+ bias,
+ j,
+ m,
+ q,
+ k,
+ t,
+ currentValue,
+ output = [],
+ /** `inputLength` will hold the number of code points in `input`. */
+ inputLength,
+ /** Cached calculation results */
+ handledCPCountPlusOne,
+ baseMinusT,
+ qMinusT;
+
+ // Convert the input in UCS-2 to Unicode
+ input = ucs2decode(input);
+
+ // Cache the length
+ inputLength = input.length;
+
+ // Initialize the state
+ n = initialN;
+ delta = 0;
+ bias = initialBias;
+
+ // Handle the basic code points
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue < 0x80) {
+ output.push(stringFromCharCode(currentValue));
+ }
+ }
+
+ handledCPCount = basicLength = output.length;
+
+ // `handledCPCount` is the number of code points that have been handled;
+ // `basicLength` is the number of basic code points.
+
+ // Finish the basic string - if it is not empty - with a delimiter
+ if (basicLength) {
+ output.push(delimiter);
+ }
+
+ // Main encoding loop:
+ while (handledCPCount < inputLength) {
+
+ // All non-basic code points < n have been handled already. Find the next
+ // larger one:
+ for (m = maxInt, j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue >= n && currentValue < m) {
+ m = currentValue;
+ }
+ }
+
+ // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+ // but guard against overflow
+ handledCPCountPlusOne = handledCPCount + 1;
+ if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+ error('overflow');
+ }
+
+ delta += (m - n) * handledCPCountPlusOne;
+ n = m;
+
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+
+ if (currentValue < n && ++delta > maxInt) {
+ error('overflow');
+ }
+
+ if (currentValue == n) {
+ // Represent delta as a generalized variable-length integer
+ for (q = delta, k = base; /* no condition */; k += base) {
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+ if (q < t) {
+ break;
+ }
+ qMinusT = q - t;
+ baseMinusT = base - t;
+ output.push(
+ stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+ );
+ q = floor(qMinusT / baseMinusT);
+ }
+
+ output.push(stringFromCharCode(digitToBasic(q, 0)));
+ bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+ delta = 0;
+ ++handledCPCount;
+ }
+ }
+
+ ++delta;
+ ++n;
+
+ }
+ return output.join('');
+ }
+
+ /**
+ * Converts a Punycode string representing a domain name to Unicode. Only the
+ * Punycoded parts of the domain name will be converted, i.e. it doesn't
+ * matter if you call it on a string that has already been converted to
+ * Unicode.
+ * @memberOf punycode
+ * @param {String} domain The Punycode domain name to convert to Unicode.
+ * @returns {String} The Unicode representation of the given Punycode
+ * string.
+ */
+ function toUnicode(domain) {
+ return mapDomain(domain, function(string) {
+ return regexPunycode.test(string)
+ ? decode(string.slice(4).toLowerCase())
+ : string;
+ });
+ }
+
+ /**
+ * Converts a Unicode string representing a domain name to Punycode. Only the
+ * non-ASCII parts of the domain name will be converted, i.e. it doesn't
+ * matter if you call it with a domain that's already in ASCII.
+ * @memberOf punycode
+ * @param {String} domain The domain name to convert, as a Unicode string.
+ * @returns {String} The Punycode representation of the given domain name.
+ */
+ function toASCII(domain) {
+ return mapDomain(domain, function(string) {
+ return regexNonASCII.test(string)
+ ? 'xn--' + encode(string)
+ : string;
+ });
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /** Define the public API */
+ punycode = {
+ /**
+ * A string representing the current Punycode.js version number.
+ * @memberOf punycode
+ * @type String
+ */
+ 'version': '1.2.3',
+ /**
+ * An object of methods to convert from JavaScript's internal character
+ * representation (UCS-2) to Unicode code points, and back.
+ * @see <http://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode
+ * @type Object
+ */
+ 'ucs2': {
+ 'decode': ucs2decode,
+ 'encode': ucs2encode
+ },
+ 'decode': decode,
+ 'encode': encode,
+ 'toASCII': toASCII,
+ 'toUnicode': toUnicode
+ };
+
+ /** Expose `punycode` */
+ // Some AMD build optimizers, like r.js, check for specific condition patterns
+ // like the following:
+ if (
+ typeof define == 'function' &&
+ typeof define.amd == 'object' &&
+ define.amd
+ ) {
+ define(function() {
+ return punycode;
+ });
+ } else if (freeExports && !freeExports.nodeType) {
+ if (freeModule) { // in Node.js or RingoJS v0.8.0+
+ freeModule.exports = punycode;
+ } else { // in Narwhal or RingoJS v0.7.0-
+ for (key in punycode) {
+ punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+ }
+ }
+ } else { // in Rhino or a web browser
+ root.punycode = punycode;
+ }
+
+}(this));
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{}],5:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+var _helpersEvent = require('./helpers/event');
+
+var _helpersEvent2 = _interopRequireDefault(_helpersEvent);
+
+var _helpersMessageEvent = require('./helpers/message-event');
+
+var _helpersMessageEvent2 = _interopRequireDefault(_helpersMessageEvent);
+
+var _helpersCloseEvent = require('./helpers/close-event');
+
+var _helpersCloseEvent2 = _interopRequireDefault(_helpersCloseEvent);
+
+/*
+* Creates an Event object and extends it to allow full modification of
+* its properties.
+*
+* @param {object} config - within config you will need to pass type and optionally target
+*/
+function createEvent(config) {
+ var type = config.type;
+ var target = config.target;
+
+ var eventObject = new _helpersEvent2['default'](type);
+
+ if (target) {
+ eventObject.target = target;
+ eventObject.srcElement = target;
+ eventObject.currentTarget = target;
+ }
+
+ return eventObject;
+}
+
+/*
+* Creates a MessageEvent object and extends it to allow full modification of
+* its properties.
+*
+* @param {object} config - within config you will need to pass type, origin, data and optionally target
+*/
+function createMessageEvent(config) {
+ var type = config.type;
+ var origin = config.origin;
+ var data = config.data;
+ var target = config.target;
+
+ var messageEvent = new _helpersMessageEvent2['default'](type, {
+ data: data,
+ origin: origin
+ });
+
+ if (target) {
+ messageEvent.target = target;
+ messageEvent.srcElement = target;
+ messageEvent.currentTarget = target;
+ }
+
+ return messageEvent;
+}
+
+/*
+* Creates a CloseEvent object and extends it to allow full modification of
+* its properties.
+*
+* @param {object} config - within config you will need to pass type and optionally target, code, and reason
+*/
+function createCloseEvent(config) {
+ var code = config.code;
+ var reason = config.reason;
+ var type = config.type;
+ var target = config.target;
+ var wasClean = config.wasClean;
+
+ if (!wasClean) {
+ wasClean = code === 1000;
+ }
+
+ var closeEvent = new _helpersCloseEvent2['default'](type, {
+ code: code,
+ reason: reason,
+ wasClean: wasClean
+ });
+
+ if (target) {
+ closeEvent.target = target;
+ closeEvent.srcElement = target;
+ closeEvent.currentTarget = target;
+ }
+
+ return closeEvent;
+}
+
+exports.createEvent = createEvent;
+exports.createMessageEvent = createMessageEvent;
+exports.createCloseEvent = createCloseEvent;
+},{"./helpers/close-event":9,"./helpers/event":12,"./helpers/message-event":13}],6:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+var _helpersArrayHelpers = require('./helpers/array-helpers');
+
+/*
+* EventTarget is an interface implemented by objects that can
+* receive events and may have listeners for them.
+*
+* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
+*/
+
+var EventTarget = (function () {
+ function EventTarget() {
+ _classCallCheck(this, EventTarget);
+
+ this.listeners = {};
+ }
+
+ /*
+ * Ties a listener function to a event type which can later be invoked via the
+ * dispatchEvent method.
+ *
+ * @param {string} type - the type of event (ie: 'open', 'message', etc.)
+ * @param {function} listener - the callback function to invoke whenever a event is dispatched matching the given type
+ * @param {boolean} useCapture - N/A TODO: implement useCapture functionality
+ */
+
+ _createClass(EventTarget, [{
+ key: 'addEventListener',
+ value: function addEventListener(type, listener /* , useCapture */) {
+ if (typeof listener === 'function') {
+ if (!Array.isArray(this.listeners[type])) {
+ this.listeners[type] = [];
+ }
+
+ // Only add the same function once
+ if ((0, _helpersArrayHelpers.filter)(this.listeners[type], function (item) {
+ return item === listener;
+ }).length === 0) {
+ this.listeners[type].push(listener);
+ }
+ }
+ }
+
+ /*
+ * Removes the listener so it will no longer be invoked via the dispatchEvent method.
+ *
+ * @param {string} type - the type of event (ie: 'open', 'message', etc.)
+ * @param {function} listener - the callback function to invoke whenever a event is dispatched matching the given type
+ * @param {boolean} useCapture - N/A TODO: implement useCapture functionality
+ */
+ }, {
+ key: 'removeEventListener',
+ value: function removeEventListener(type, removingListener /* , useCapture */) {
+ var arrayOfListeners = this.listeners[type];
+ this.listeners[type] = (0, _helpersArrayHelpers.reject)(arrayOfListeners, function (listener) {
+ return listener === removingListener;
+ });
+ }
+
+ /*
+ * Invokes all listener functions that are listening to the given event.type property. Each
+ * listener will be passed the event as the first argument.
+ *
+ * @param {object} event - event object which will be passed to all listeners of the event.type property
+ */
+ }, {
+ key: 'dispatchEvent',
+ value: function dispatchEvent(event) {
+ var _this = this;
+
+ for (var _len = arguments.length, customArguments = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ customArguments[_key - 1] = arguments[_key];
+ }
+
+ var eventName = event.type;
+ var listeners = this.listeners[eventName];
+
+ if (!Array.isArray(listeners)) {
+ return false;
+ }
+
+ listeners.forEach(function (listener) {
+ if (customArguments.length > 0) {
+ listener.apply(_this, customArguments);
+ } else {
+ listener.call(_this, event);
+ }
+ });
+ }
+ }]);
+
+ return EventTarget;
+})();
+
+exports['default'] = EventTarget;
+module.exports = exports['default'];
+},{"./helpers/array-helpers":7}],7:[function(require,module,exports){
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.reject = reject;
+exports.filter = filter;
+
+function reject(array, callback) {
+ var results = [];
+ array.forEach(function (itemInArray) {
+ if (!callback(itemInArray)) {
+ results.push(itemInArray);
+ }
+ });
+
+ return results;
+}
+
+function filter(array, callback) {
+ var results = [];
+ array.forEach(function (itemInArray) {
+ if (callback(itemInArray)) {
+ results.push(itemInArray);
+ }
+ });
+
+ return results;
+}
+},{}],8:[function(require,module,exports){
+/*
+* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
+*/
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+var codes = {
+ CLOSE_NORMAL: 1000,
+ CLOSE_GOING_AWAY: 1001,
+ CLOSE_PROTOCOL_ERROR: 1002,
+ CLOSE_UNSUPPORTED: 1003,
+ CLOSE_NO_STATUS: 1005,
+ CLOSE_ABNORMAL: 1006,
+ CLOSE_TOO_LARGE: 1009
+};
+
+exports["default"] = codes;
+module.exports = exports["default"];
+},{}],9:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var _eventPrototype = require('./event-prototype');
+
+var _eventPrototype2 = _interopRequireDefault(_eventPrototype);
+
+var CloseEvent = (function (_EventPrototype) {
+ _inherits(CloseEvent, _EventPrototype);
+
+ function CloseEvent(type) {
+ var eventInitConfig = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+ _classCallCheck(this, CloseEvent);
+
+ _get(Object.getPrototypeOf(CloseEvent.prototype), 'constructor', this).call(this);
+
+ if (!type) {
+ throw new TypeError('Failed to construct \'CloseEvent\': 1 argument required, but only 0 present.');
+ }
+
+ if (typeof eventInitConfig !== 'object') {
+ throw new TypeError('Failed to construct \'CloseEvent\': parameter 2 (\'eventInitDict\') is not an object');
+ }
+
+ var bubbles = eventInitConfig.bubbles;
+ var cancelable = eventInitConfig.cancelable;
+ var code = eventInitConfig.code;
+ var reason = eventInitConfig.reason;
+ var wasClean = eventInitConfig.wasClean;
+
+ this.type = String(type);
+ this.timeStamp = Date.now();
+ this.target = null;
+ this.srcElement = null;
+ this.returnValue = true;
+ this.isTrusted = false;
+ this.eventPhase = 0;
+ this.defaultPrevented = false;
+ this.currentTarget = null;
+ this.cancelable = cancelable ? Boolean(cancelable) : false;
+ this.canncelBubble = false;
+ this.bubbles = bubbles ? Boolean(bubbles) : false;
+ this.code = typeof code === 'number' ? Number(code) : 0;
+ this.reason = reason ? String(reason) : '';
+ this.wasClean = wasClean ? Boolean(wasClean) : false;
+ }
+
+ return CloseEvent;
+})(_eventPrototype2['default']);
+
+exports['default'] = CloseEvent;
+module.exports = exports['default'];
+},{"./event-prototype":11}],10:[function(require,module,exports){
+/*
+* This delay allows the thread to finish assigning its on* methods
+* before invoking the delay callback. This is purely a timing hack.
+* http://geekabyte.blogspot.com/2014/01/javascript-effect-of-setting-settimeout.html
+*
+* @param {callback: function} the callback which will be invoked after the timeout
+* @parma {context: object} the context in which to invoke the function
+*/
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+function delay(callback, context) {
+ setTimeout(function timeout(timeoutContext) {
+ callback.call(timeoutContext);
+ }, 4, context);
+}
+
+exports["default"] = delay;
+module.exports = exports["default"];
+},{}],11:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+var EventPrototype = (function () {
+ function EventPrototype() {
+ _classCallCheck(this, EventPrototype);
+ }
+
+ _createClass(EventPrototype, [{
+ key: 'stopPropagation',
+
+ // Noops
+ value: function stopPropagation() {}
+ }, {
+ key: 'stopImmediatePropagation',
+ value: function stopImmediatePropagation() {}
+
+ // if no arguments are passed then the type is set to "undefined" on
+ // chrome and safari.
+ }, {
+ key: 'initEvent',
+ value: function initEvent() {
+ var type = arguments.length <= 0 || arguments[0] === undefined ? 'undefined' : arguments[0];
+ var bubbles = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
+ var cancelable = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
+
+ Object.assign(this, {
+ type: String(type),
+ bubbles: Boolean(bubbles),
+ cancelable: Boolean(cancelable)
+ });
+ }
+ }]);
+
+ return EventPrototype;
+})();
+
+exports['default'] = EventPrototype;
+module.exports = exports['default'];
+},{}],12:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var _eventPrototype = require('./event-prototype');
+
+var _eventPrototype2 = _interopRequireDefault(_eventPrototype);
+
+var Event = (function (_EventPrototype) {
+ _inherits(Event, _EventPrototype);
+
+ function Event(type) {
+ var eventInitConfig = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+ _classCallCheck(this, Event);
+
+ _get(Object.getPrototypeOf(Event.prototype), 'constructor', this).call(this);
+
+ if (!type) {
+ throw new TypeError('Failed to construct \'Event\': 1 argument required, but only 0 present.');
+ }
+
+ if (typeof eventInitConfig !== 'object') {
+ throw new TypeError('Failed to construct \'Event\': parameter 2 (\'eventInitDict\') is not an object');
+ }
+
+ var bubbles = eventInitConfig.bubbles;
+ var cancelable = eventInitConfig.cancelable;
+
+ this.type = String(type);
+ this.timeStamp = Date.now();
+ this.target = null;
+ this.srcElement = null;
+ this.returnValue = true;
+ this.isTrusted = false;
+ this.eventPhase = 0;
+ this.defaultPrevented = false;
+ this.currentTarget = null;
+ this.cancelable = cancelable ? Boolean(cancelable) : false;
+ this.canncelBubble = false;
+ this.bubbles = bubbles ? Boolean(bubbles) : false;
+ }
+
+ return Event;
+})(_eventPrototype2['default']);
+
+exports['default'] = Event;
+module.exports = exports['default'];
+},{"./event-prototype":11}],13:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var _eventPrototype = require('./event-prototype');
+
+var _eventPrototype2 = _interopRequireDefault(_eventPrototype);
+
+var MessageEvent = (function (_EventPrototype) {
+ _inherits(MessageEvent, _EventPrototype);
+
+ function MessageEvent(type) {
+ var eventInitConfig = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+ _classCallCheck(this, MessageEvent);
+
+ _get(Object.getPrototypeOf(MessageEvent.prototype), 'constructor', this).call(this);
+
+ if (!type) {
+ throw new TypeError('Failed to construct \'MessageEvent\': 1 argument required, but only 0 present.');
+ }
+
+ if (typeof eventInitConfig !== 'object') {
+ throw new TypeError('Failed to construct \'MessageEvent\': parameter 2 (\'eventInitDict\') is not an object');
+ }
+
+ var bubbles = eventInitConfig.bubbles;
+ var cancelable = eventInitConfig.cancelable;
+ var data = eventInitConfig.data;
+ var origin = eventInitConfig.origin;
+ var lastEventId = eventInitConfig.lastEventId;
+ var ports = eventInitConfig.ports;
+
+ this.type = String(type);
+ this.timeStamp = Date.now();
+ this.target = null;
+ this.srcElement = null;
+ this.returnValue = true;
+ this.isTrusted = false;
+ this.eventPhase = 0;
+ this.defaultPrevented = false;
+ this.currentTarget = null;
+ this.cancelable = cancelable ? Boolean(cancelable) : false;
+ this.canncelBubble = false;
+ this.bubbles = bubbles ? Boolean(bubbles) : false;
+ this.origin = origin ? String(origin) : '';
+ this.ports = typeof ports === 'undefined' ? null : ports;
+ this.data = typeof data === 'undefined' ? null : data;
+ this.lastEventId = lastEventId ? String(lastEventId) : '';
+ }
+
+ return MessageEvent;
+})(_eventPrototype2['default']);
+
+exports['default'] = MessageEvent;
+module.exports = exports['default'];
+},{"./event-prototype":11}],14:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+var _server = require('./server');
+
+var _server2 = _interopRequireDefault(_server);
+
+var _socketIo = require('./socket-io');
+
+var _socketIo2 = _interopRequireDefault(_socketIo);
+
+var _websocket = require('./websocket');
+
+var _websocket2 = _interopRequireDefault(_websocket);
+
+if (typeof window !== 'undefined') {
+ window.MockServer = _server2['default'];
+ window.MockWebSocket = _websocket2['default'];
+ window.MockSocketIO = _socketIo2['default'];
+}
+
+var Server = _server2['default'];
+exports.Server = Server;
+var WebSocket = _websocket2['default'];
+exports.WebSocket = WebSocket;
+var SocketIO = _socketIo2['default'];
+exports.SocketIO = SocketIO;
+},{"./server":16,"./socket-io":17,"./websocket":18}],15:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+var _helpersArrayHelpers = require('./helpers/array-helpers');
+
+/*
+* The network bridge is a way for the mock websocket object to 'communicate' with
+* all avalible servers. This is a singleton object so it is important that you
+* clean up urlMap whenever you are finished.
+*/
+
+var NetworkBridge = (function () {
+ function NetworkBridge() {
+ _classCallCheck(this, NetworkBridge);
+
+ this.urlMap = {};
+ }
+
+ /*
+ * Attaches a websocket object to the urlMap hash so that it can find the server
+ * it is connected to and the server in turn can find it.
+ *
+ * @param {object} websocket - websocket object to add to the urlMap hash
+ * @param {string} url
+ */
+
+ _createClass(NetworkBridge, [{
+ key: 'attachWebSocket',
+ value: function attachWebSocket(websocket, url) {
+ var connectionLookup = this.urlMap[url];
+
+ if (connectionLookup && connectionLookup.server && connectionLookup.websockets.indexOf(websocket) === -1) {
+ connectionLookup.websockets.push(websocket);
+ return connectionLookup.server;
+ }
+ }
+
+ /*
+ * Attaches a websocket to a room
+ */
+ }, {
+ key: 'addMembershipToRoom',
+ value: function addMembershipToRoom(websocket, room) {
+ var connectionLookup = this.urlMap[websocket.url];
+
+ if (connectionLookup && connectionLookup.server && connectionLookup.websockets.indexOf(websocket) !== -1) {
+ if (!connectionLookup.roomMemberships[room]) {
+ connectionLookup.roomMemberships[room] = [];
+ }
+
+ connectionLookup.roomMemberships[room].push(websocket);
+ }
+ }
+
+ /*
+ * Attaches a server object to the urlMap hash so that it can find a websockets
+ * which are connected to it and so that websockets can in turn can find it.
+ *
+ * @param {object} server - server object to add to the urlMap hash
+ * @param {string} url
+ */
+ }, {
+ key: 'attachServer',
+ value: function attachServer(server, url) {
+ var connectionLookup = this.urlMap[url];
+
+ if (!connectionLookup) {
+ this.urlMap[url] = {
+ server: server,
+ websockets: [],
+ roomMemberships: {}
+ };
+
+ return server;
+ }
+ }
+
+ /*
+ * Finds the server which is 'running' on the given url.
+ *
+ * @param {string} url - the url to use to find which server is running on it
+ */
+ }, {
+ key: 'serverLookup',
+ value: function serverLookup(url) {
+ var connectionLookup = this.urlMap[url];
+
+ if (connectionLookup) {
+ return connectionLookup.server;
+ }
+ }
+
+ /*
+ * Finds all websockets which is 'listening' on the given url.
+ *
+ * @param {string} url - the url to use to find all websockets which are associated with it
+ * @param {string} room - if a room is provided, will only return sockets in this room
+ */
+ }, {
+ key: 'websocketsLookup',
+ value: function websocketsLookup(url, room) {
+ var connectionLookup = this.urlMap[url];
+
+ if (!connectionLookup) {
+ return [];
+ }
+
+ if (room) {
+ var members = connectionLookup.roomMemberships[room];
+ return members ? members : [];
+ }
+
+ return connectionLookup.websockets;
+ }
+
+ /*
+ * Removes the entry associated with the url.
+ *
+ * @param {string} url
+ */
+ }, {
+ key: 'removeServer',
+ value: function removeServer(url) {
+ delete this.urlMap[url];
+ }
+
+ /*
+ * Removes the individual websocket from the map of associated websockets.
+ *
+ * @param {object} websocket - websocket object to remove from the url map
+ * @param {string} url
+ */
+ }, {
+ key: 'removeWebSocket',
+ value: function removeWebSocket(websocket, url) {
+ var connectionLookup = this.urlMap[url];
+
+ if (connectionLookup) {
+ connectionLookup.websockets = (0, _helpersArrayHelpers.reject)(connectionLookup.websockets, function (socket) {
+ return socket === websocket;
+ });
+ }
+ }
+
+ /*
+ * Removes a websocket from a room
+ */
+ }, {
+ key: 'removeMembershipFromRoom',
+ value: function removeMembershipFromRoom(websocket, room) {
+ var connectionLookup = this.urlMap[websocket.url];
+ var memberships = connectionLookup.roomMemberships[room];
+
+ if (connectionLookup && memberships !== null) {
+ connectionLookup.roomMemberships[room] = (0, _helpersArrayHelpers.reject)(memberships, function (socket) {
+ return socket === websocket;
+ });
+ }
+ }
+ }]);
+
+ return NetworkBridge;
+})();
+
+exports['default'] = new NetworkBridge();
+// Note: this is a singleton
+module.exports = exports['default'];
+},{"./helpers/array-helpers":7}],16:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { var object = _x4, property = _x5, receiver = _x6; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var _urijs = require('urijs');
+
+var _urijs2 = _interopRequireDefault(_urijs);
+
+var _websocket = require('./websocket');
+
+var _websocket2 = _interopRequireDefault(_websocket);
+
+var _eventTarget = require('./event-target');
+
+var _eventTarget2 = _interopRequireDefault(_eventTarget);
+
+var _networkBridge = require('./network-bridge');
+
+var _networkBridge2 = _interopRequireDefault(_networkBridge);
+
+var _helpersCloseCodes = require('./helpers/close-codes');
+
+var _helpersCloseCodes2 = _interopRequireDefault(_helpersCloseCodes);
+
+var _eventFactory = require('./event-factory');
+
+/*
+* https://github.com/websockets/ws#server-example
+*/
+
+var Server = (function (_EventTarget) {
+ _inherits(Server, _EventTarget);
+
+ /*
+ * @param {string} url
+ */
+
+ function Server(url) {
+ _classCallCheck(this, Server);
+
+ _get(Object.getPrototypeOf(Server.prototype), 'constructor', this).call(this);
+ this.url = (0, _urijs2['default'])(url).toString();
+ var server = _networkBridge2['default'].attachServer(this, this.url);
+
+ if (!server) {
+ this.dispatchEvent((0, _eventFactory.createEvent)({ type: 'error' }));
+ throw new Error('A mock server is already listening on this url');
+ }
+ }
+
+ /*
+ * Alternative constructor to support namespaces in socket.io
+ *
+ * http://socket.io/docs/rooms-and-namespaces/#custom-namespaces
+ */
+
+ /*
+ * This is the main function for the mock server to subscribe to the on events.
+ *
+ * ie: mockServer.on('connection', function() { console.log('a mock client connected'); });
+ *
+ * @param {string} type - The event key to subscribe to. Valid keys are: connection, message, and close.
+ * @param {function} callback - The callback which should be called when a certain event is fired.
+ */
+
+ _createClass(Server, [{
+ key: 'on',
+ value: function on(type, callback) {
+ this.addEventListener(type, callback);
+ }
+
+ /*
+ * This send function will notify all mock clients via their onmessage callbacks that the server
+ * has a message for them.
+ *
+ * @param {*} data - Any javascript object which will be crafted into a MessageObject.
+ */
+ }, {
+ key: 'send',
+ value: function send(data) {
+ var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+ this.emit('message', data, options);
+ }
+
+ /*
+ * Sends a generic message event to all mock clients.
+ */
+ }, {
+ key: 'emit',
+ value: function emit(event, data) {
+ var _this2 = this;
+
+ var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
+ var websockets = options.websockets;
+
+ if (!websockets) {
+ websockets = _networkBridge2['default'].websocketsLookup(this.url);
+ }
+
+ websockets.forEach(function (socket) {
+ socket.dispatchEvent((0, _eventFactory.createMessageEvent)({
+ type: event,
+ data: data,
+ origin: _this2.url,
+ target: socket
+ }));
+ });
+ }
+
+ /*
+ * Closes the connection and triggers the onclose method of all listening
+ * websockets. After that it removes itself from the urlMap so another server
+ * could add itself to the url.
+ *
+ * @param {object} options
+ */
+ }, {
+ key: 'close',
+ value: function close() {
+ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+ var code = options.code;
+ var reason = options.reason;
+ var wasClean = options.wasClean;
+
+ var listeners = _networkBridge2['default'].websocketsLookup(this.url);
+
+ listeners.forEach(function (socket) {
+ socket.readyState = _websocket2['default'].CLOSE;
+ socket.dispatchEvent((0, _eventFactory.createCloseEvent)({
+ type: 'close',
+ target: socket,
+ code: code || _helpersCloseCodes2['default'].CLOSE_NORMAL,
+ reason: reason || '',
+ wasClean: wasClean
+ }));
+ });
+
+ this.dispatchEvent((0, _eventFactory.createCloseEvent)({ type: 'close' }), this);
+ _networkBridge2['default'].removeServer(this.url);
+ }
+
+ /*
+ * Returns an array of websockets which are listening to this server
+ */
+ }, {
+ key: 'clients',
+ value: function clients() {
+ return _networkBridge2['default'].websocketsLookup(this.url);
+ }
+
+ /*
+ * Prepares a method to submit an event to members of the room
+ *
+ * e.g. server.to('my-room').emit('hi!');
+ */
+ }, {
+ key: 'to',
+ value: function to(room) {
+ var _this = this;
+ var websockets = _networkBridge2['default'].websocketsLookup(this.url, room);
+ return {
+ emit: function emit(event, data) {
+ _this.emit(event, data, { websockets: websockets });
+ }
+ };
+ }
+ }]);
+
+ return Server;
+})(_eventTarget2['default']);
+
+Server.of = function of(url) {
+ return new Server(url);
+};
+
+exports['default'] = Server;
+module.exports = exports['default'];
+},{"./event-factory":5,"./event-target":6,"./helpers/close-codes":8,"./network-bridge":15,"./websocket":18,"urijs":3}],17:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+var _get = function get(_x3, _x4, _x5) { var _again = true; _function: while (_again) { var object = _x3, property = _x4, receiver = _x5; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x3 = parent; _x4 = property; _x5 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var _urijs = require('urijs');
+
+var _urijs2 = _interopRequireDefault(_urijs);
+
+var _helpersDelay = require('./helpers/delay');
+
+var _helpersDelay2 = _interopRequireDefault(_helpersDelay);
+
+var _eventTarget = require('./event-target');
+
+var _eventTarget2 = _interopRequireDefault(_eventTarget);
+
+var _networkBridge = require('./network-bridge');
+
+var _networkBridge2 = _interopRequireDefault(_networkBridge);
+
+var _helpersCloseCodes = require('./helpers/close-codes');
+
+var _helpersCloseCodes2 = _interopRequireDefault(_helpersCloseCodes);
+
+var _eventFactory = require('./event-factory');
+
+/*
+* The socket-io class is designed to mimick the real API as closely as possible.
+*
+* http://socket.io/docs/
+*/
+
+var SocketIO = (function (_EventTarget) {
+ _inherits(SocketIO, _EventTarget);
+
+ /*
+ * @param {string} url
+ */
+
+ function SocketIO() {
+ var _this = this;
+
+ var url = arguments.length <= 0 || arguments[0] === undefined ? 'socket.io' : arguments[0];
+ var protocol = arguments.length <= 1 || arguments[1] === undefined ? '' : arguments[1];
+
+ _classCallCheck(this, SocketIO);
+
+ _get(Object.getPrototypeOf(SocketIO.prototype), 'constructor', this).call(this);
+
+ this.binaryType = 'blob';
+ this.url = (0, _urijs2['default'])(url).toString();
+ this.readyState = SocketIO.CONNECTING;
+ this.protocol = '';
+
+ if (typeof protocol === 'string') {
+ this.protocol = protocol;
+ } else if (Array.isArray(protocol) && protocol.length > 0) {
+ this.protocol = protocol[0];
+ }
+
+ var server = _networkBridge2['default'].attachWebSocket(this, this.url);
+
+ /*
+ * Delay triggering the connection events so they can be defined in time.
+ */
+ (0, _helpersDelay2['default'])(function delayCallback() {
+ if (server) {
+ this.readyState = SocketIO.OPEN;
+ server.dispatchEvent((0, _eventFactory.createEvent)({ type: 'connection' }), server, this);
+ server.dispatchEvent((0, _eventFactory.createEvent)({ type: 'connect' }), server, this); // alias
+ this.dispatchEvent((0, _eventFactory.createEvent)({ type: 'connect', target: this }));
+ } else {
+ this.readyState = SocketIO.CLOSED;
+ this.dispatchEvent((0, _eventFactory.createEvent)({ type: 'error', target: this }));
+ this.dispatchEvent((0, _eventFactory.createCloseEvent)({
+ type: 'close',
+ target: this,
+ code: _helpersCloseCodes2['default'].CLOSE_NORMAL
+ }));
+
+ console.error('Socket.io connection to \'' + this.url + '\' failed');
+ }
+ }, this);
+
+ /**
+ Add an aliased event listener for close / disconnect
+ */
+ this.addEventListener('close', function (event) {
+ _this.dispatchEvent((0, _eventFactory.createCloseEvent)({
+ type: 'disconnect',
+ target: event.target,
+ code: event.code
+ }));
+ });
+ }
+
+ /*
+ * Closes the SocketIO connection or connection attempt, if any.
+ * If the connection is already CLOSED, this method does nothing.
+ */
+
+ _createClass(SocketIO, [{
+ key: 'close',
+ value: function close() {
+ if (this.readyState !== SocketIO.OPEN) {
+ return undefined;
+ }
+
+ var server = _networkBridge2['default'].serverLookup(this.url);
+ _networkBridge2['default'].removeWebSocket(this, this.url);
+
+ this.readyState = SocketIO.CLOSED;
+ this.dispatchEvent((0, _eventFactory.createCloseEvent)({
+ type: 'close',
+ target: this,
+ code: _helpersCloseCodes2['default'].CLOSE_NORMAL
+ }));
+
+ if (server) {
+ server.dispatchEvent((0, _eventFactory.createCloseEvent)({
+ type: 'disconnect',
+ target: this,
+ code: _helpersCloseCodes2['default'].CLOSE_NORMAL
+ }), server);
+ }
+ }
+
+ /*
+ * Alias for Socket#close
+ *
+ * https://github.com/socketio/socket.io-client/blob/master/lib/socket.js#L383
+ */
+ }, {
+ key: 'disconnect',
+ value: function disconnect() {
+ this.close();
+ }
+
+ /*
+ * Submits an event to the server with a payload
+ */
+ }, {
+ key: 'emit',
+ value: function emit(event, data) {
+ if (this.readyState !== SocketIO.OPEN) {
+ throw new Error('SocketIO is already in CLOSING or CLOSED state');
+ }
+
+ var messageEvent = (0, _eventFactory.createMessageEvent)({
+ type: event,
+ origin: this.url,
+ data: data
+ });
+
+ var server = _networkBridge2['default'].serverLookup(this.url);
+
+ if (server) {
+ server.dispatchEvent(messageEvent, data);
+ }
+ }
+
+ /*
+ * Submits a 'message' event to the server.
+ *
+ * Should behave exactly like WebSocket#send
+ *
+ * https://github.com/socketio/socket.io-client/blob/master/lib/socket.js#L113
+ */
+ }, {
+ key: 'send',
+ value: function send(data) {
+ this.emit('message', data);
+ }
+
+ /*
+ * For registering events to be received from the server
+ */
+ }, {
+ key: 'on',
+ value: function on(type, callback) {
+ this.addEventListener(type, callback);
+ }
+
+ /*
+ * Join a room on a server
+ *
+ * http://socket.io/docs/rooms-and-namespaces/#joining-and-leaving
+ */
+ }, {
+ key: 'join',
+ value: function join(room) {
+ _networkBridge2['default'].addMembershipToRoom(this, room);
+ }
+
+ /*
+ * Get the websocket to leave the room
+ *
+ * http://socket.io/docs/rooms-and-namespaces/#joining-and-leaving
+ */
+ }, {
+ key: 'leave',
+ value: function leave(room) {
+ _networkBridge2['default'].removeMembershipFromRoom(this, room);
+ }
+
+ /*
+ * Invokes all listener functions that are listening to the given event.type property. Each
+ * listener will be passed the event as the first argument.
+ *
+ * @param {object} event - event object which will be passed to all listeners of the event.type property
+ */
+ }, {
+ key: 'dispatchEvent',
+ value: function dispatchEvent(event) {
+ var _this2 = this;
+
+ for (var _len = arguments.length, customArguments = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ customArguments[_key - 1] = arguments[_key];
+ }
+
+ var eventName = event.type;
+ var listeners = this.listeners[eventName];
+
+ if (!Array.isArray(listeners)) {
+ return false;
+ }
+
+ listeners.forEach(function (listener) {
+ if (customArguments.length > 0) {
+ listener.apply(_this2, customArguments);
+ } else {
+ // Regular WebSockets expect a MessageEvent but Socketio.io just wants raw data
+ // payload instanceof MessageEvent works, but you can't isntance of NodeEvent
+ // for now we detect if the output has data defined on it
+ listener.call(_this2, event.data ? event.data : event);
+ }
+ });
+ }
+ }]);
+
+ return SocketIO;
+})(_eventTarget2['default']);
+
+SocketIO.CONNECTING = 0;
+SocketIO.OPEN = 1;
+SocketIO.CLOSING = 2;
+SocketIO.CLOSED = 3;
+
+/*
+* Static constructor methods for the IO Socket
+*/
+var IO = function ioConstructor(url) {
+ return new SocketIO(url);
+};
+
+/*
+* Alias the raw IO() constructor
+*/
+IO.connect = function ioConnect(url) {
+ /* eslint-disable new-cap */
+ return IO(url);
+ /* eslint-enable new-cap */
+};
+
+exports['default'] = IO;
+module.exports = exports['default'];
+},{"./event-factory":5,"./event-target":6,"./helpers/close-codes":8,"./helpers/delay":10,"./network-bridge":15,"urijs":3}],18:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, '__esModule', {
+ value: true
+});
+
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var _urijs = require('urijs');
+
+var _urijs2 = _interopRequireDefault(_urijs);
+
+var _helpersDelay = require('./helpers/delay');
+
+var _helpersDelay2 = _interopRequireDefault(_helpersDelay);
+
+var _eventTarget = require('./event-target');
+
+var _eventTarget2 = _interopRequireDefault(_eventTarget);
+
+var _networkBridge = require('./network-bridge');
+
+var _networkBridge2 = _interopRequireDefault(_networkBridge);
+
+var _helpersCloseCodes = require('./helpers/close-codes');
+
+var _helpersCloseCodes2 = _interopRequireDefault(_helpersCloseCodes);
+
+var _eventFactory = require('./event-factory');
+
+/*
+* The main websocket class which is designed to mimick the native WebSocket class as close
+* as possible.
+*
+* https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
+*/
+
+var WebSocket = (function (_EventTarget) {
+ _inherits(WebSocket, _EventTarget);
+
+ /*
+ * @param {string} url
+ */
+
+ function WebSocket(url) {
+ var protocol = arguments.length <= 1 || arguments[1] === undefined ? '' : arguments[1];
+
+ _classCallCheck(this, WebSocket);
+
+ _get(Object.getPrototypeOf(WebSocket.prototype), 'constructor', this).call(this);
+
+ if (!url) {
+ throw new TypeError('Failed to construct \'WebSocket\': 1 argument required, but only 0 present.');
+ }
+
+ this.binaryType = 'blob';
+ this.url = (0, _urijs2['default'])(url).toString();
+ this.readyState = WebSocket.CONNECTING;
+ this.protocol = '';
+
+ if (typeof protocol === 'string') {
+ this.protocol = protocol;
+ } else if (Array.isArray(protocol) && protocol.length > 0) {
+ this.protocol = protocol[0];
+ }
+
+ /*
+ * In order to capture the callback function we need to define custom setters.
+ * To illustrate:
+ * mySocket.onopen = function() { alert(true) };
+ *
+ * The only way to capture that function and hold onto it for later is with the
+ * below code:
+ */
+ Object.defineProperties(this, {
+ onopen: {
+ configurable: true,
+ enumerable: true,
+ get: function get() {
+ return this.listeners.open;
+ },
+ set: function set(listener) {
+ this.addEventListener('open', listener);
+ }
+ },
+ onmessage: {
+ configurable: true,
+ enumerable: true,
+ get: function get() {
+ return this.listeners.message;
+ },
+ set: function set(listener) {
+ this.addEventListener('message', listener);
+ }
+ },
+ onclose: {
+ configurable: true,
+ enumerable: true,
+ get: function get() {
+ return this.listeners.close;
+ },
+ set: function set(listener) {
+ this.addEventListener('close', listener);
+ }
+ },
+ onerror: {
+ configurable: true,
+ enumerable: true,
+ get: function get() {
+ return this.listeners.error;
+ },
+ set: function set(listener) {
+ this.addEventListener('error', listener);
+ }
+ }
+ });
+
+ var server = _networkBridge2['default'].attachWebSocket(this, this.url);
+
+ /*
+ * This delay is needed so that we dont trigger an event before the callbacks have been
+ * setup. For example:
+ *
+ * var socket = new WebSocket('ws://localhost');
+ *
+ * // If we dont have the delay then the event would be triggered right here and this is
+ * // before the onopen had a chance to register itself.
+ *
+ * socket.onopen = () => { // this would never be called };
+ *
+ * // and with the delay the event gets triggered here after all of the callbacks have been
+ * // registered :-)
+ */
+ (0, _helpersDelay2['default'])(function delayCallback() {
+ if (server) {
+ this.readyState = WebSocket.OPEN;
+ server.dispatchEvent((0, _eventFactory.createEvent)({ type: 'connection' }), server, this);
+ this.dispatchEvent((0, _eventFactory.createEvent)({ type: 'open', target: this }));
+ } else {
+ this.readyState = WebSocket.CLOSED;
+ this.dispatchEvent((0, _eventFactory.createEvent)({ type: 'error', target: this }));
+ this.dispatchEvent((0, _eventFactory.createCloseEvent)({ type: 'close', target: this, code: _helpersCloseCodes2['default'].CLOSE_NORMAL }));
+
+ console.error('WebSocket connection to \'' + this.url + '\' failed');
+ }
+ }, this);
+ }
+
+ /*
+ * Transmits data to the server over the WebSocket connection.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#send()
+ */
+
+ _createClass(WebSocket, [{
+ key: 'send',
+ value: function send(data) {
+ if (this.readyState === WebSocket.CLOSING || this.readyState === WebSocket.CLOSED) {
+ throw new Error('WebSocket is already in CLOSING or CLOSED state');
+ }
+
+ var messageEvent = (0, _eventFactory.createMessageEvent)({
+ type: 'message',
+ origin: this.url,
+ data: data
+ });
+
+ var server = _networkBridge2['default'].serverLookup(this.url);
+
+ if (server) {
+ server.dispatchEvent(messageEvent, data);
+ }
+ }
+
+ /*
+ * Closes the WebSocket connection or connection attempt, if any.
+ * If the connection is already CLOSED, this method does nothing.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#close()
+ */
+ }, {
+ key: 'close',
+ value: function close() {
+ if (this.readyState !== WebSocket.OPEN) {
+ return undefined;
+ }
+
+ var server = _networkBridge2['default'].serverLookup(this.url);
+ var closeEvent = (0, _eventFactory.createCloseEvent)({
+ type: 'close',
+ target: this,
+ code: _helpersCloseCodes2['default'].CLOSE_NORMAL
+ });
+
+ _networkBridge2['default'].removeWebSocket(this, this.url);
+
+ this.readyState = WebSocket.CLOSED;
+ this.dispatchEvent(closeEvent);
+
+ if (server) {
+ server.dispatchEvent(closeEvent, server);
+ }
+ }
+ }]);
+
+ return WebSocket;
+})(_eventTarget2['default']);
+
+WebSocket.CONNECTING = 0;
+WebSocket.OPEN = 1;
+WebSocket.CLOSING = 2;
+WebSocket.CLOSED = 3;
+
+exports['default'] = WebSocket;
+module.exports = exports['default'];
+},{"./event-factory":5,"./event-target":6,"./helpers/close-codes":8,"./helpers/delay":10,"./network-bridge":15,"urijs":3}]},{},[14])
+//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIuLi8uLi9ub2RlX21vZHVsZXMvdXJpanMvc3JjL0lQdjYuanMiLCIuLi8uLi9ub2RlX21vZHVsZXMvdXJpanMvc3JjL1NlY29uZExldmVsRG9tYWlucy5qcyIsIi4uLy4uL25vZGVfbW9kdWxlcy91cmlqcy9zcmMvVVJJLmpzIiwiLi4vLi4vbm9kZV9tb2R1bGVzL3VyaWpzL3NyYy9wdW55Y29kZS5qcyIsImV2ZW50LWZhY3RvcnkuanMiLCJldmVudC10YXJnZXQuanMiLCJoZWxwZXJzL2FycmF5LWhlbHBlcnMuanMiLCJoZWxwZXJzL2Nsb3NlLWNvZGVzLmpzIiwiaGVscGVycy9jbG9zZS1ldmVudC5qcyIsImhlbHBlcnMvZGVsYXkuanMiLCJoZWxwZXJzL2V2ZW50LXByb3RvdHlwZS5qcyIsImhlbHBlcnMvZXZlbnQuanMiLCJoZWxwZXJzL21lc3NhZ2UtZXZlbnQuanMiLCJtYWluLmpzIiwibmV0d29yay1icmlkZ2UuanMiLCJzZXJ2ZXIuanMiLCJzb2NrZXQtaW8uanMiLCJ3ZWJzb2NrZXQuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUxBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDalBBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQ2puRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7OztBQzVmQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4R0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDL0RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDL0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3S0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN0xBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDclJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qIVxuICogVVJJLmpzIC0gTXV0YXRpbmcgVVJMc1xuICogSVB2NiBTdXBwb3J0XG4gKlxuICogVmVyc2lvbjogMS4xNy4wXG4gKlxuICogQXV0aG9yOiBSb2RuZXkgUmVobVxuICogV2ViOiBodHRwOi8vbWVkaWFsaXplLmdpdGh1Yi5pby9VUkkuanMvXG4gKlxuICogTGljZW5zZWQgdW5kZXJcbiAqICAgTUlUIExpY2Vuc2UgaHR0cDovL3d3dy5vcGVuc291cmNlLm9yZy9saWNlbnNlcy9taXQtbGljZW5zZVxuICogICBHUEwgdjMgaHR0cDovL29wZW5zb3VyY2Uub3JnL2xpY2Vuc2VzL0dQTC0zLjBcbiAqXG4gKi9cblxuKGZ1bmN0aW9uIChyb290LCBmYWN0b3J5KSB7XG4gICd1c2Ugc3RyaWN0JztcbiAgLy8gaHR0cHM6Ly9naXRodWIuY29tL3VtZGpzL3VtZC9ibG9iL21hc3Rlci9yZXR1cm5FeHBvcnRzLmpzXG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcpIHtcbiAgICAvLyBOb2RlXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KCk7XG4gIH0gZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kKSB7XG4gICAgLy8gQU1ELiBSZWdpc3RlciBhcyBhbiBhbm9ueW1vdXMgbW9kdWxlLlxuICAgIGRlZmluZShmYWN0b3J5KTtcbiAgfSBlbHNlIHtcbiAgICAvLyBCcm93c2VyIGdsb2JhbHMgKHJvb3QgaXMgd2luZG93KVxuICAgIHJvb3QuSVB2NiA9IGZhY3Rvcnkocm9vdCk7XG4gIH1cbn0odGhpcywgZnVuY3Rpb24gKHJvb3QpIHtcbiAgJ3VzZSBzdHJpY3QnO1xuXG4gIC8qXG4gIHZhciBfaW4gPSBcImZlODA6MDAwMDowMDAwOjAwMDA6MDIwNDo2MWZmOmZlOWQ6ZjE1NlwiO1xuICB2YXIgX291dCA9IElQdjYuYmVzdChfaW4pO1xuICB2YXIgX2V4cGVjdGVkID0gXCJmZTgwOjoyMDQ6NjFmZjpmZTlkOmYxNTZcIjtcblxuICBjb25zb2xlLmxvZyhfaW4sIF9vdXQsIF9leHBlY3RlZCwgX291dCA9PT0gX2V4cGVjdGVkKTtcbiAgKi9cblxuICAvLyBzYXZlIGN1cnJlbnQgSVB2NiB2YXJpYWJsZSwgaWYgYW55XG4gIHZhciBfSVB2NiA9IHJvb3QgJiYgcm9vdC5JUHY2O1xuXG4gIGZ1bmN0aW9uIGJlc3RQcmVzZW50YXRpb24oYWRkcmVzcykge1xuICAgIC8vIGJhc2VkIG9uOlxuICAgIC8vIEphdmFzY3JpcHQgdG8gdGVzdCBhbiBJUHY2IGFkZHJlc3MgZm9yIHByb3BlciBmb3JtYXQsIGFuZCB0b1xuICAgIC8vIHByZXNlbnQgdGhlIFwiYmVzdCB0ZXh0IHJlcHJlc2VudGF0aW9uXCIgYWNjb3JkaW5nIHRvIElFVEYgRHJhZnQgUkZDIGF0XG4gICAgLy8gaHR0cDovL3Rvb2xzLmlldGYub3JnL2h0bWwvZHJhZnQtaWV0Zi02bWFuLXRleHQtYWRkci1yZXByZXNlbnRhdGlvbi0wNFxuICAgIC8vIDggRmViIDIwMTAgUmljaCBCcm93biwgRGFydHdhcmUsIExMQ1xuICAgIC8vIFBsZWFzZSBmZWVsIGZyZWUgdG8gdXNlIHRoaXMgY29kZSBhcyBsb25nIGFzIHlvdSBwcm92aWRlIGEgbGluayB0b1xuICAgIC8vIGh0dHA6Ly93d3cuaW50ZXJtYXBwZXIuY29tXG4gICAgLy8gaHR0cDovL2ludGVybWFwcGVyLmNvbS9zdXBwb3J0L3Rvb2xzL0lQVjYtVmFsaWRhdG9yLmFzcHhcbiAgICAvLyBodHRwOi8vZG93bmxvYWQuZGFydHdhcmUuY29tL3RoaXJkcGFydHkvaXB2NnZhbGlkYXRvci5qc1xuXG4gICAgdmFyIF9hZGRyZXNzID0gYWRkcmVzcy50b0xvd2VyQ2FzZSgpO1xuICAgIHZhciBzZWdtZW50cyA9IF9hZGRyZXNzLnNwbGl0KCc6Jyk7XG4gICAgdmFyIGxlbmd0aCA9IHNlZ21lbnRzLmxlbmd0aDtcbiAgICB2YXIgdG90YWwgPSA4O1xuXG4gICAgLy8gdHJpbSBjb2xvbnMgKDo6IG9yIDo6YTpiOmPigKYgb3Ig4oCmYTpiOmM6OilcbiAgICBpZiAoc2VnbWVudHNbMF0gPT09ICcnICYmIHNlZ21lbnRzWzFdID09PSAnJyAmJiBzZWdtZW50c1syXSA9PT0gJycpIHtcbiAgICAgIC8vIG11c3QgaGF2ZSBiZWVuIDo6XG4gICAgICAvLyByZW1vdmUgZmlyc3QgdHdvIGl0ZW1zXG4gICAgICBzZWdtZW50cy5zaGlmdCgpO1xuICAgICAgc2VnbWVudHMuc2hpZnQoKTtcbiAgICB9IGVsc2UgaWYgKHNlZ21lbnRzWzBdID09PSAnJyAmJiBzZWdtZW50c1sxXSA9PT0gJycpIHtcbiAgICAgIC8vIG11c3QgaGF2ZSBiZWVuIDo6eHh4eFxuICAgICAgLy8gcmVtb3ZlIHRoZSBmaXJzdCBpdGVtXG4gICAgICBzZWdtZW50cy5zaGlmdCgpO1xuICAgIH0gZWxzZSBpZiAoc2VnbWVudHNbbGVuZ3RoIC0gMV0gPT09ICcnICYmIHNlZ21lbnRzW2xlbmd0aCAtIDJdID09PSAnJykge1xuICAgICAgLy8gbXVzdCBoYXZlIGJlZW4geHh4eDo6XG4gICAgICBzZWdtZW50cy5wb3AoKTtcbiAgICB9XG5cbiAgICBsZW5ndGggPSBzZWdtZW50cy5sZW5ndGg7XG5cbiAgICAvLyBhZGp1c3QgdG90YWwgc2VnbWVudHMgZm9yIElQdjQgdHJhaWxlclxuICAgIGlmIChzZWdtZW50c1tsZW5ndGggLSAxXS5pbmRleE9mKCcuJykgIT09IC0xKSB7XG4gICAgICAvLyBmb3VuZCBhIFwiLlwiIHdoaWNoIG1lYW5zIElQdjRcbiAgICAgIHRvdGFsID0gNztcbiAgICB9XG5cbiAgICAvLyBmaWxsIGVtcHR5IHNlZ21lbnRzIHRoZW0gd2l0aCBcIjAwMDBcIlxuICAgIHZhciBwb3M7XG4gICAgZm9yIChwb3MgPSAwOyBwb3MgPCBsZW5ndGg7IHBvcysrKSB7XG4gICAgICBpZiAoc2VnbWVudHNbcG9zXSA9PT0gJycpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHBvcyA8IHRvdGFsKSB7XG4gICAgICBzZWdtZW50cy5zcGxpY2UocG9zLCAxLCAnMDAwMCcpO1xuICAgICAgd2hpbGUgKHNlZ21lbnRzLmxlbmd0aCA8IHRvdGFsKSB7XG4gICAgICAgIHNlZ21lbnRzLnNwbGljZShwb3MsIDAsICcwMDAwJyk7XG4gICAgICB9XG5cbiAgICAgIGxlbmd0aCA9IHNlZ21lbnRzLmxlbmd0aDtcbiAgICB9XG5cbiAgICAvLyBzdHJpcCBsZWFkaW5nIHplcm9zXG4gICAgdmFyIF9zZWdtZW50cztcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRvdGFsOyBpKyspIHtcbiAgICAgIF9zZWdtZW50cyA9IHNlZ21lbnRzW2ldLnNwbGl0KCcnKTtcbiAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgMyA7IGorKykge1xuICAgICAgICBpZiAoX3NlZ21lbnRzWzBdID09PSAnMCcgJiYgX3NlZ21lbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICBfc2VnbWVudHMuc3BsaWNlKDAsMSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgc2VnbWVudHNbaV0gPSBfc2VnbWVudHMuam9pbignJyk7XG4gICAgfVxuXG4gICAgLy8gZmluZCBsb25nZXN0IHNlcXVlbmNlIG9mIHplcm9lcyBhbmQgY29hbGVzY2UgdGhlbSBpbnRvIG9uZSBzZWdtZW50XG4gICAgdmFyIGJlc3QgPSAtMTtcbiAgICB2YXIgX2Jlc3QgPSAwO1xuICAgIHZhciBfY3VycmVudCA9IDA7XG4gICAgdmFyIGN1cnJlbnQgPSAtMTtcbiAgICB2YXIgaW56ZXJvZXMgPSBmYWxzZTtcbiAgICAvLyBpOyBhbHJlYWR5IGRlY2xhcmVkXG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgdG90YWw7IGkrKykge1xuICAgICAgaWYgKGluemVyb2VzKSB7XG4gICAgICAgIGlmIChzZWdtZW50c1tpXSA9PT0gJzAnKSB7XG4gICAgICAgICAgX2N1cnJlbnQgKz0gMTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpbnplcm9lcyA9IGZhbHNlO1xuICAgICAgICAgIGlmIChfY3VycmVudCA+IF9iZXN0KSB7XG4gICAgICAgICAgICBiZXN0ID0gY3VycmVudDtcbiAgICAgICAgICAgIF9iZXN0ID0gX2N1cnJlbnQ7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAoc2VnbWVudHNbaV0gPT09ICcwJykge1xuICAgICAgICAgIGluemVyb2VzID0gdHJ1ZTtcbiAgICAgICAgICBjdXJyZW50ID0gaTtcbiAgICAgICAgICBfY3VycmVudCA9IDE7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoX2N1cnJlbnQgPiBfYmVzdCkge1xuICAgICAgYmVzdCA9IGN1cnJlbnQ7XG4gICAgICBfYmVzdCA9IF9jdXJyZW50O1xuICAgIH1cblxuICAgIGlmIChfYmVzdCA+IDEpIHtcbiAgICAgIHNlZ21lbnRzLnNwbGljZShiZXN0LCBfYmVzdCwgJycpO1xuICAgIH1cblxuICAgIGxlbmd0aCA9IHNlZ21lbnRzLmxlbmd0aDtcblxuICAgIC8vIGFzc2VtYmxlIHJlbWFpbmluZyBzZWdtZW50c1xuICAgIHZhciByZXN1bHQgPSAnJztcbiAgICBpZiAoc2VnbWVudHNbMF0gPT09ICcnKSAge1xuICAgICAgcmVzdWx0ID0gJzonO1xuICAgIH1cblxuICAgIGZvciAoaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgICAgcmVzdWx0ICs9IHNlZ21lbnRzW2ldO1xuICAgICAgaWYgKGkgPT09IGxlbmd0aCAtIDEpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG5cbiAgICAgIHJlc3VsdCArPSAnOic7XG4gICAgfVxuXG4gICAgaWYgKHNlZ21lbnRzW2xlbmd0aCAtIDFdID09PSAnJykge1xuICAgICAgcmVzdWx0ICs9ICc6JztcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbm9Db25mbGljdCgpIHtcbiAgICAvKmpzaGludCB2YWxpZHRoaXM6IHRydWUgKi9cbiAgICBpZiAocm9vdC5JUHY2ID09PSB0aGlzKSB7XG4gICAgICByb290LklQdjYgPSBfSVB2NjtcbiAgICB9XG4gIFxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBiZXN0OiBiZXN0UHJlc2VudGF0aW9uLFxuICAgIG5vQ29uZmxpY3Q6IG5vQ29uZmxpY3RcbiAgfTtcbn0pKTtcbiIsIi8qIVxuICogVVJJLmpzIC0gTXV0YXRpbmcgVVJMc1xuICogU2Vjb25kIExldmVsIERvbWFpbiAoU0xEKSBTdXBwb3J0XG4gKlxuICogVmVyc2lvbjogMS4xNy4wXG4gKlxuICogQXV0aG9yOiBSb2RuZXkgUmVobVxuICogV2ViOiBodHRwOi8vbWVkaWFsaXplLmdpdGh1Yi5pby9VUkkuanMvXG4gKlxuICogTGljZW5zZWQgdW5kZXJcbiAqICAgTUlUIExpY2Vuc2UgaHR0cDovL3d3dy5vcGVuc291cmNlLm9yZy9saWNlbnNlcy9taXQtbGljZW5zZVxuICogICBHUEwgdjMgaHR0cDovL29wZW5zb3VyY2Uub3JnL2xpY2Vuc2VzL0dQTC0zLjBcbiAqXG4gKi9cblxuKGZ1bmN0aW9uIChyb290LCBmYWN0b3J5KSB7XG4gICd1c2Ugc3RyaWN0JztcbiAgLy8gaHR0cHM6Ly9naXRodWIuY29tL3VtZGpzL3VtZC9ibG9iL21hc3Rlci9yZXR1cm5FeHBvcnRzLmpzXG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcpIHtcbiAgICAvLyBOb2RlXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KCk7XG4gIH0gZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kKSB7XG4gICAgLy8gQU1ELiBSZWdpc3RlciBhcyBhbiBhbm9ueW1vdXMgbW9kdWxlLlxuICAgIGRlZmluZShmYWN0b3J5KTtcbiAgfSBlbHNlIHtcbiAgICAvLyBCcm93c2VyIGdsb2JhbHMgKHJvb3QgaXMgd2luZG93KVxuICAgIHJvb3QuU2Vjb25kTGV2ZWxEb21haW5zID0gZmFjdG9yeShyb290KTtcbiAgfVxufSh0aGlzLCBmdW5jdGlvbiAocm9vdCkge1xuICAndXNlIHN0cmljdCc7XG5cbiAgLy8gc2F2ZSBjdXJyZW50IFNlY29uZExldmVsRG9tYWlucyB2YXJpYWJsZSwgaWYgYW55XG4gIHZhciBfU2Vjb25kTGV2ZWxEb21haW5zID0gcm9vdCAmJiByb290LlNlY29uZExldmVsRG9tYWlucztcblxuICB2YXIgU0xEID0ge1xuICAgIC8vIGxpc3Qgb2Yga25vd24gU2Vjb25kIExldmVsIERvbWFpbnNcbiAgICAvLyBjb252ZXJ0ZWQgbGlzdCBvZiBTTERzIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL2dhdmluZ21pbGxlci9zZWNvbmQtbGV2ZWwtZG9tYWluc1xuICAgIC8vIC0tLS1cbiAgICAvLyBwdWJsaWNzdWZmaXgub3JnIGlzIG1vcmUgY3VycmVudCBhbmQgYWN0dWFsbHkgdXNlZCBieSBhIGNvdXBsZSBvZiBicm93c2VycyBpbnRlcm5hbGx5LlxuICAgIC8vIGRvd25zaWRlIGlzIGl0IGFsc28gY29udGFpbnMgZG9tYWlucyBsaWtlIFwiZHluZG5zLm9yZ1wiIC0gd2hpY2ggaXMgZmluZSBmb3IgdGhlIHNlY3VyaXR5XG4gICAgLy8gaXNzdWVzIGJyb3dzZXIgaGF2ZSB0byBkZWFsIHdpdGggKFNPUCBmb3IgY29va2llcywgZXRjKSAtIGJ1dCBpcyB3YXkgb3ZlcmJvYXJkIGZvciBVUkkuanNcbiAgICAvLyAtLS0tXG4gICAgbGlzdDoge1xuICAgICAgJ2FjJzonIGNvbSBnb3YgbWlsIG5ldCBvcmcgJyxcbiAgICAgICdhZSc6JyBhYyBjbyBnb3YgbWlsIG5hbWUgbmV0IG9yZyBwcm8gc2NoICcsXG4gICAgICAnYWYnOicgY29tIGVkdSBnb3YgbmV0IG9yZyAnLFxuICAgICAgJ2FsJzonIGNvbSBlZHUgZ292IG1pbCBuZXQgb3JnICcsXG4gICAgICAnYW8nOicgY28gZWQgZ3YgaXQgb2cgcGIgJyxcbiAgICAgICdhcic6JyBjb20gZWR1IGdvYiBnb3YgaW50IG1pbCBuZXQgb3JnIHR1ciAnLFxuICAgICAgJ2F0JzonIGFjIGNvIGd2IG9yICcsXG4gICAgICAnYXUnOicgYXNuIGNvbSBjc2lybyBlZHUgZ292IGlkIG5ldCBvcmcgJyxcbiAgICAgICdiYSc6JyBjbyBjb20gZWR1IGdvdiBtaWwgbmV0IG9yZyBycyB1bmJpIHVubW8gdW5zYSB1bnR6IHVuemUgJyxcbiAgICAgICdiYic6JyBiaXogY28gY29tIGVkdSBnb3YgaW5mbyBuZXQgb3JnIHN0b3JlIHR2ICcsXG4gICAgICAnYmgnOicgYml6IGNjIGNvbSBlZHUgZ292IGluZm8gbmV0IG9yZyAnLFxuICAgICAgJ2JuJzonIGNvbSBlZHUgZ292IG5ldCBvcmcgJyxcbiAgICAgICdibyc6JyBjb20gZWR1IGdvYiBnb3YgaW50IG1pbCBuZXQgb3JnIHR2ICcsXG4gICAgICAnYnInOicgYWRtIGFkdiBhZ3IgYW0gYXJxIGFydCBhdG8gYiBiaW8gYmxvZyBibWQgY2ltIGNuZyBjbnQgY29tIGNvb3AgZWNuIGVkdSBlbmcgZXNwIGV0YyBldGkgZmFyIGZsb2cgZm0gZm5kIGZvdCBmc3QgZzEyIGdnZiBnb3YgaW1iIGluZCBpbmYgam9yIGp1cyBsZWwgbWF0IG1lZCBtaWwgbXVzIG5ldCBub20gbm90IG50ciBvZG8gb3JnIHBwZyBwcm8gcHNjIHBzaSBxc2wgcmVjIHNsZyBzcnYgdG1wIHRyZCB0dXIgdHYgdmV0IHZsb2cgd2lraSB6bGcgJyxcbiAgICAgICdicyc6JyBjb20gZWR1IGdvdiBuZXQgb3JnICcsXG4gICAgICAnYnonOicgZHUgZXQgb20gb3YgcmcgJyxcbiAgICAgICdjYSc6JyBhYiBiYyBtYiBuYiBuZiBubCBucyBudCBudSBvbiBwZSBxYyBzayB5ayAnLFxuICAgICAgJ2NrJzonIGJpeiBjbyBlZHUgZ2VuIGdvdiBpbmZvIG5ldCBvcmcgJyxcbiAgICAgICdjbic6JyBhYyBhaCBiaiBjb20gY3EgZWR1IGZqIGdkIGdvdiBncyBneCBneiBoYSBoYiBoZSBoaSBobCBobiBqbCBqcyBqeCBsbiBtaWwgbmV0IG5tIG54IG9yZyBxaCBzYyBzZCBzaCBzbiBzeCB0aiB0dyB4aiB4eiB5biB6aiAnLFxuICAgICAgJ2NvJzonIGNvbSBlZHUgZ292IG1pbCBuZXQgbm9tIG9yZyAnLFxuICAgICAgJ2NyJzonIGFjIGMgY28gZWQgZmkgZ28gb3Igc2EgJyxcbiAgICAgICdjeSc6JyBhYyBiaXogY29tIGVrbG9nZXMgZ292IGx0ZCBuYW1lIG5ldCBvcmcgcGFybGlhbWVudCBwcmVzcyBwcm8gdG0gJyxcbiAgICAgICdkbyc6JyBhcnQgY29tIGVkdSBnb2IgZ292IG1pbCBuZXQgb3JnIHNsZCB3ZWIgJyxcbiAgICAgICdkeic6JyBhcnQgYXNzbyBjb20gZWR1IGdvdiBuZXQgb3JnIHBvbCAnLFxuICAgICAgJ2VjJzonIGNvbSBlZHUgZmluIGdvdiBpbmZvIG1lZCBtaWwgbmV0IG9yZyBwcm8gJyxcbiAgICAgICdlZyc6JyBjb20gZWR1IGV1biBnb3YgbWlsIG5hbWUgbmV0IG9yZyBzY2kgJyxcbiAgICAgICdlcic6JyBjb20gZWR1IGdvdiBpbmQgbWlsIG5ldCBvcmcgcm9jaGVzdCB3ICcsXG4gICAgICAnZXMnOicgY29tIGVkdSBnb2Igbm9tIG9yZyAnLFxuICAgICAgJ2V0JzonIGJpeiBjb20gZWR1IGdvdiBpbmZvIG5hbWUgbmV0IG9yZyAnLFxuICAgICAgJ2ZqJzonIGFjIGJpeiBjb20gaW5mbyBtaWwgbmFtZSBuZXQgb3JnIHBybyAnLFxuICAgICAgJ2ZrJzonIGFjIGNvIGdvdiBuZXQgbm9tIG9yZyAnLFxuICAgICAgJ2ZyJzonIGFzc28gY29tIGYgZ291diBub20gcHJkIHByZXNzZSB0bSAnLFxuICAgICAgJ2dnJzonIGNvIG5ldCBvcmcgJyxcbiAgICAgICdnaCc6JyBjb20gZWR1IGdvdiBtaWwgb3JnICcsXG4gICAgICAnZ24nOicgYWMgY29tIGdvdiBuZXQgb3JnICcsXG4gICAgICAnZ3InOicgY29tIGVkdSBnb3YgbWlsIG5ldCBvcmcgJyxcbiAgICAgICdndCc6JyBjb20gZWR1IGdvYiBpbmQgbWlsIG5ldCBvcmcgJyxcbiAgICAgICdndSc6JyBjb20gZWR1IGdvdiBuZXQgb3JnICcsXG4gICAgICAnaGsnOicgY29tIGVkdSBnb3YgaWR2IG5ldCBvcmcgJyxcbiAgICAgICdodSc6JyAyMDAwIGFncmFyIGJvbHQgY2FzaW5vIGNpdHkgY28gZXJvdGljYSBlcm90aWthIGZpbG0gZm9ydW0gZ2FtZXMgaG90ZWwgaW5mbyBpbmdhdGxhbiBqb2dhc3oga29ueXZlbG8gbGFrYXMgbWVkaWEgbmV3cyBvcmcgcHJpdiByZWtsYW0gc2V4IHNob3Agc3BvcnQgc3VsaSBzemV4IHRtIHRvenNkZSB1dGF6YXMgdmlkZW8gJyxcbiAgICAgICdpZCc6JyBhYyBjbyBnbyBtaWwgbmV0IG9yIHNjaCB3ZWIgJyxcbiAgICAgICdpbCc6JyBhYyBjbyBnb3YgaWRmIGsxMiBtdW5pIG5ldCBvcmcgJyxcbiAgICAgICdpbic6JyBhYyBjbyBlZHUgZXJuZXQgZmlybSBnZW4gZ292IGkgaW5kIG1pbCBuZXQgbmljIG9yZyByZXMgJyxcbiAgICAgICdpcSc6JyBjb20gZWR1IGdvdiBpIG1pbCBuZXQgb3JnICcsXG4gICAgICAnaXInOicgYWMgY28gZG5zc2VjIGdvdiBpIGlkIG5ldCBvcmcgc2NoICcsXG4gICAgICAnaXQnOicgZWR1IGdvdiAnLFxuICAgICAgJ2plJzonIGNvIG5ldCBvcmcgJyxcbiAgICAgICdqbyc6JyBjb20gZWR1IGdvdiBtaWwgbmFtZSBuZXQgb3JnIHNjaCAnLFxuICAgICAgJ2pwJzonIGFjIGFkIGNvIGVkIGdvIGdyIGxnIG5lIG9yICcsXG4gICAgICAna2UnOicgYWMgY28gZ28gaW5mbyBtZSBtb2JpIG5lIG9yIHNjICcsXG4gICAgICAna2gnOicgY29tIGVkdSBnb3YgbWlsIG5ldCBvcmcgcGVyICcsXG4gICAgICAna2knOicgYml6IGNvbSBkZSBlZHUgZ292IGluZm8gbW9iIG5ldCBvcmcgdGVsICcsXG4gICAgICAna20nOicgYXNzbyBjb20gY29vcCBlZHUgZ291diBrIG1lZGVjaW4gbWlsIG5vbSBub3RhaXJlcyBwaGFybWFjaWVucyBwcmVzc2UgdG0gdmV0ZXJpbmFpcmUgJyxcbiAgICAgICdrbic6JyBlZHUgZ292IG5ldCBvcmcgJyxcbiAgICAgICdrcic6JyBhYyBidXNhbiBjaHVuZ2J1ayBjaHVuZ25hbSBjbyBkYWVndSBkYWVqZW9uIGVzIGdhbmd3b24gZ28gZ3dhbmdqdSBneWVvbmdidWsgZ3llb25nZ2kgZ3llb25nbmFtIGhzIGluY2hlb24gamVqdSBqZW9uYnVrIGplb25uYW0gayBrZyBtaWwgbXMgbmUgb3IgcGUgcmUgc2Mgc2VvdWwgdWxzYW4gJyxcbiAgICAgICdrdyc6JyBjb20gZWR1IGdvdiBuZXQgb3JnICcsXG4gICAgICAna3knOicgY29tIGVkdSBnb3YgbmV0IG9yZyAnLFxuICAgICAgJ2t6JzonIGNvbSBlZHUgZ292IG1pbCBuZXQgb3JnICcsXG4gICAgICAnbGInOicgY29tIGVkdSBnb3YgbmV0IG9yZyAnLFxuICAgICAgJ2xrJzonIGFzc24gY29tIGVkdSBnb3YgZ3JwIGhvdGVsIGludCBsdGQgbmV0IG5nbyBvcmcgc2NoIHNvYyB3ZWIgJyxcbiAgICAgICdscic6JyBjb20gZWR1IGdvdiBuZXQgb3JnICcsXG4gICAgICAnbHYnOicgYXNuIGNvbSBjb25mIGVkdSBnb3YgaWQgbWlsIG5ldCBvcmcgJyxcbiAgICAgICdseSc6JyBjb20gZWR1IGdvdiBpZCBtZWQgbmV0IG9yZyBwbGMgc2NoICcsXG4gICAgICAnbWEnOicgYWMgY28gZ292IG0gbmV0IG9yZyBwcmVzcyAnLFxuICAgICAgJ21jJzonIGFzc28gdG0gJyxcbiAgICAgICdtZSc6JyBhYyBjbyBlZHUgZ292IGl0cyBuZXQgb3JnIHByaXYgJyxcbiAgICAgICdtZyc6JyBjb20gZWR1IGdvdiBtaWwgbm9tIG9yZyBwcmQgdG0gJyxcbiAgICAgICdtayc6JyBjb20gZWR1IGdvdiBpbmYgbmFtZSBuZXQgb3JnIHBybyAnLFxuICAgICAgJ21sJzonIGNvbSBlZHUgZ292IG5ldCBvcmcgcHJlc3NlICcsXG4gICAgICAnbW4nOicgZWR1IGdvdiBvcmcgJyxcbiAgICAgICdtbyc6JyBjb20gZWR1IGdvdiBuZXQgb3JnICcsXG4gICAgICAnbXQnOicgY29tIGVkdSBnb3YgbmV0IG9yZyAnLFxuICAgICAgJ212JzonIGFlcm8gYml6IGNvbSBjb29wIGVkdSBnb3YgaW5mbyBpbnQgbWlsIG11c2V1bSBuYW1lIG5ldCBvcmcgcHJvICcsXG4gICAgICAnbXcnOicgYWMgY28gY29tIGNvb3AgZWR1IGdvdiBpbnQgbXVzZXVtIG5ldCBvcmcgJyxcbiAgICAgICdteCc6JyBjb20gZWR1IGdvYiBuZXQgb3JnICcsXG4gICAgICAnbXknOicgY29tIGVkdSBnb3YgbWlsIG5hbWUgbmV0IG9yZyBzY2ggJyxcbiAgICAgICduZic6JyBhcnRzIGNvbSBmaXJtIGluZm8gbmV0IG90aGVyIHBlciByZWMgc3RvcmUgd2ViICcsXG4gICAgICAnbmcnOicgYml6IGNvbSBlZHUgZ292IG1pbCBtb2JpIG5hbWUgbmV0IG9yZyBzY2ggJyxcbiAgICAgICduaSc6JyBhYyBjbyBjb20gZWR1IGdvYiBtaWwgbmV0IG5vbSBvcmcgJyxcbiAgICAgICducCc6JyBjb20gZWR1IGdvdiBtaWwgbmV0IG9yZyAnLFxuICAgICAgJ25yJzonIGJpeiBjb20gZWR1IGdvdiBpbmZvIG5ldCBvcmcgJyxcbiAgICAgICdvbSc6JyBhYyBiaXogY28gY29tIGVkdSBnb3YgbWVkIG1pbCBtdXNldW0gbmV0IG9yZyBwcm8gc2NoICcsXG4gICAgICAncGUnOicgY29tIGVkdSBnb2IgbWlsIG5ldCBub20gb3JnIHNsZCAnLFxuICAgICAgJ3BoJzonIGNvbSBlZHUgZ292IGkgbWlsIG5ldCBuZ28gb3JnICcsXG4gICAgICAncGsnOicgYml6IGNvbSBlZHUgZmFtIGdvYiBnb2sgZ29uIGdvcCBnb3MgZ292IG5ldCBvcmcgd2ViICcsXG4gICAgICAncGwnOicgYXJ0IGJpYWx5c3RvayBiaXogY29tIGVkdSBnZGEgZ2RhbnNrIGdvcnpvdyBnb3YgaW5mbyBrYXRvd2ljZSBrcmFrb3cgbG9keiBsdWJsaW4gbWlsIG5ldCBuZ28gb2xzenR5biBvcmcgcG96bmFuIHB3ciByYWRvbSBzbHVwc2sgc3pjemVjaW4gdG9ydW4gd2Fyc3phd2Egd2F3IHdyb2Mgd3JvY2xhdyB6Z29yYSAnLFxuICAgICAgJ3ByJzonIGFjIGJpeiBjb20gZWR1IGVzdCBnb3YgaW5mbyBpc2xhIG5hbWUgbmV0IG9yZyBwcm8gcHJvZiAnLFxuICAgICAgJ3BzJzonIGNvbSBlZHUgZ292IG5ldCBvcmcgcGxvIHNlYyAnLFxuICAgICAgJ3B3JzonIGJlbGF1IGNvIGVkIGdvIG5lIG9yICcsXG4gICAgICAncm8nOicgYXJ0cyBjb20gZmlybSBpbmZvIG5vbSBudCBvcmcgcmVjIHN0b3JlIHRtIHd3dyAnLFxuICAgICAgJ3JzJzonIGFjIGNvIGVkdSBnb3YgaW4gb3JnICcsXG4gICAgICAnc2InOicgY29tIGVkdSBnb3YgbmV0IG9yZyAnLFxuICAgICAgJ3NjJzonIGNvbSBlZHUgZ292IG5ldCBvcmcgJyxcbiAgICAgICdzaCc6JyBjbyBjb20gZWR1IGdvdiBuZXQgbm9tIG9yZyAnLFxuICAgICAgJ3NsJzonIGNvbSBlZHUgZ292IG5ldCBvcmcgJyxcbiAgICAgICdzdCc6JyBjbyBjb20gY29uc3VsYWRvIGVkdSBlbWJhaXhhZGEgZ292IG1pbCBuZXQgb3JnIHByaW5jaXBlIHNhb3RvbWUgc3RvcmUgJyxcbiAgICAgICdzdic6JyBjb20gZWR1IGdvYiBvcmcgcmVkICcsXG4gICAgICAnc3onOicgYWMgY28gb3JnICcsXG4gICAgICAndHInOicgYXYgYmJzIGJlbCBiaXogY29tIGRyIGVkdSBnZW4gZ292IGluZm8gazEyIG5hbWUgbmV0IG9yZyBwb2wgdGVsIHRzayB0diB3ZWIgJyxcbiAgICAgICd0dCc6JyBhZXJvIGJpeiBjYXQgY28gY29tIGNvb3AgZWR1IGdvdiBpbmZvIGludCBqb2JzIG1pbCBtb2JpIG11c2V1bSBuYW1lIG5ldCBvcmcgcHJvIHRlbCB0cmF2ZWwgJyxcbiAgICAgICd0dyc6JyBjbHViIGNvbSBlYml6IGVkdSBnYW1lIGdvdiBpZHYgbWlsIG5ldCBvcmcgJyxcbiAgICAgICdtdSc6JyBhYyBjbyBjb20gZ292IG5ldCBvciBvcmcgJyxcbiAgICAgICdteic6JyBhYyBjbyBlZHUgZ292IG9yZyAnLFxuICAgICAgJ25hJzonIGNvIGNvbSAnLFxuICAgICAgJ256JzonIGFjIGNvIGNyaSBnZWVrIGdlbiBnb3Z0IGhlYWx0aCBpd2kgbWFvcmkgbWlsIG5ldCBvcmcgcGFybGlhbWVudCBzY2hvb2wgJyxcbiAgICAgICdwYSc6JyBhYm8gYWMgY29tIGVkdSBnb2IgaW5nIG1lZCBuZXQgbm9tIG9yZyBzbGQgJyxcbiAgICAgICdwdCc6JyBjb20gZWR1IGdvdiBpbnQgbmV0IG5vbWUgb3JnIHB1YmwgJyxcbiAgICAgICdweSc6JyBjb20gZWR1IGdvdiBtaWwgbmV0IG9yZyAnLFxuICAgICAgJ3FhJzonIGNvbSBlZHUgZ292IG1pbCBuZXQgb3JnICcsXG4gICAgICAncmUnOicgYXNzbyBjb20gbm9tICcsXG4gICAgICAncnUnOicgYWMgYWR5Z2V5YSBhbHRhaSBhbXVyIGFya2hhbmdlbHNrIGFzdHJha2hhbiBiYXNoa2lyaWEgYmVsZ29yb2QgYmlyIGJyeWFuc2sgYnVyeWF0aWEgY2JnIGNoZWwgY2hlbHlhYmluc2sgY2hpdGEgY2h1a290a2EgY2h1dmFzaGlhIGNvbSBkYWdlc3RhbiBlLWJ1cmcgZWR1IGdvdiBncm96bnkgaW50IGlya3V0c2sgaXZhbm92byBpemhldnNrIGphciBqb3Noa2FyLW9sYSBrYWxteWtpYSBrYWx1Z2Ega2FtY2hhdGthIGthcmVsaWEga2F6YW4ga2NociBrZW1lcm92byBraGFiYXJvdnNrIGtoYWthc3NpYSBraHYga2lyb3Yga29lbmlnIGtvbWkga29zdHJvbWEga3Jhbm95YXJzayBrdWJhbiBrdXJnYW4ga3Vyc2sgbGlwZXRzayBtYWdhZGFuIG1hcmkgbWFyaS1lbCBtYXJpbmUgbWlsIG1vcmRvdmlhIG1vc3JlZyBtc2sgbXVybWFuc2sgbmFsY2hpayBuZXQgbm5vdiBub3Ygbm92b3NpYmlyc2sgbnNrIG9tc2sgb3JlbmJ1cmcgb3JnIG9yeW9sIHBlbnphIHBlcm0gcHAgcHNrb3YgcHR6IHJuZCByeWF6YW4gc2FraGFsaW4gc2FtYXJhIHNhcmF0b3Ygc2ltYmlyc2sgc21vbGVuc2sgc3BiIHN0YXZyb3BvbCBzdHYgc3VyZ3V0IHRhbWJvdiB0YXRhcnN0YW4gdG9tIHRvbXNrIHRzYXJpdHN5biB0c2sgdHVsYSB0dXZhIHR2ZXIgdHl1bWVuIHVkbSB1ZG11cnRpYSB1bGFuLXVkZSB2bGFkaWthdmtheiB2bGFkaW1pciB2bGFkaXZvc3RvayB2b2xnb2dyYWQgdm9sb2dkYSB2b3JvbmV6aCB2cm4gdnlhdGthIHlha3V0aWEgeWFtYWwgeWVrYXRlcmluYnVyZyB5dXpobm8tc2FraGFsaW5zayAnLFxuICAgICAgJ3J3JzonIGFjIGNvIGNvbSBlZHUgZ291diBnb3YgaW50IG1pbCBuZXQgJyxcbiAgICAgICdzYSc6JyBjb20gZWR1IGdvdiBtZWQgbmV0IG9yZyBwdWIgc2NoICcsXG4gICAgICAnc2QnOicgY29tIGVkdSBnb3YgaW5mbyBtZWQgbmV0IG9yZyB0diAnLFxuICAgICAgJ3NlJzonIGEgYWMgYiBiZCBjIGQgZSBmIGcgaCBpIGsgbCBtIG4gbyBvcmcgcCBwYXJ0aSBwcCBwcmVzcyByIHMgdCB0bSB1IHcgeCB5IHogJyxcbiAgICAgICdzZyc6JyBjb20gZWR1IGdvdiBpZG4gbmV0IG9yZyBwZXIgJyxcbiAgICAgICdzbic6JyBhcnQgY29tIGVkdSBnb3V2IG9yZyBwZXJzbyB1bml2ICcsXG4gICAgICAnc3knOicgY29tIGVkdSBnb3YgbWlsIG5ldCBuZXdzIG9yZyAnLFxuICAgICAgJ3RoJzonIGFjIGNvIGdvIGluIG1pIG5ldCBvciAnLFxuICAgICAgJ3RqJzonIGFjIGJpeiBjbyBjb20gZWR1IGdvIGdvdiBpbmZvIGludCBtaWwgbmFtZSBuZXQgbmljIG9yZyB0ZXN0IHdlYiAnLFxuICAgICAgJ3RuJzonIGFncmluZXQgY29tIGRlZmVuc2UgZWR1bmV0IGVucyBmaW4gZ292IGluZCBpbmZvIGludGwgbWluY29tIG5hdCBuZXQgb3JnIHBlcnNvIHJucnQgcm5zIHJudSB0b3VyaXNtICcsXG4gICAgICAndHonOicgYWMgY28gZ28gbmUgb3IgJyxcbiAgICAgICd1YSc6JyBiaXogY2hlcmthc3N5IGNoZXJuaWdvdiBjaGVybm92dHN5IGNrIGNuIGNvIGNvbSBjcmltZWEgY3YgZG4gZG5lcHJvcGV0cm92c2sgZG9uZXRzayBkcCBlZHUgZ292IGlmIGluIGl2YW5vLWZyYW5raXZzayBraCBraGFya292IGtoZXJzb24ga2htZWxuaXRza2l5IGtpZXYga2lyb3ZvZ3JhZCBrbSBrciBrcyBrdiBsZyBsdWdhbnNrIGx1dHNrIGx2aXYgbWUgbWsgbmV0IG5pa29sYWV2IG9kIG9kZXNzYSBvcmcgcGwgcG9sdGF2YSBwcCByb3ZubyBydiBzZWJhc3RvcG9sIHN1bXkgdGUgdGVybm9waWwgdXpoZ29yb2QgdmlubmljYSB2biB6YXBvcml6aHpoZSB6aGl0b21pciB6cCB6dCAnLFxuICAgICAgJ3VnJzonIGFjIGNvIGdvIG5lIG9yIG9yZyBzYyAnLFxuICAgICAgJ3VrJzonIGFjIGJsIGJyaXRpc2gtbGlicmFyeSBjbyBjeW0gZ292IGdvdnQgaWNuZXQgamV0IGxlYSBsdGQgbWUgbWlsIG1vZCBuYXRpb25hbC1saWJyYXJ5LXNjb3RsYW5kIG5lbCBuZXQgbmhzIG5pYyBubHMgb3JnIG9yZ24gcGFybGlhbWVudCBwbGMgcG9saWNlIHNjaCBzY290IHNvYyAnLFxuICAgICAgJ3VzJzonIGRuaSBmZWQgaXNhIGtpZHMgbnNuICcsXG4gICAgICAndXknOicgY29tIGVkdSBndWIgbWlsIG5ldCBvcmcgJyxcbiAgICAgICd2ZSc6JyBjbyBjb20gZWR1IGdvYiBpbmZvIG1pbCBuZXQgb3JnIHdlYiAnLFxuICAgICAgJ3ZpJzonIGNvIGNvbSBrMTIgbmV0IG9yZyAnLFxuICAgICAgJ3ZuJzonIGFjIGJpeiBjb20gZWR1IGdvdiBoZWFsdGggaW5mbyBpbnQgbmFtZSBuZXQgb3JnIHBybyAnLFxuICAgICAgJ3llJzonIGNvIGNvbSBnb3YgbHRkIG1lIG5ldCBvcmcgcGxjICcsXG4gICAgICAneXUnOicgYWMgY28gZWR1IGdvdiBvcmcgJyxcbiAgICAgICd6YSc6JyBhYyBhZ3JpYyBhbHQgYm91cnNlIGNpdHkgY28gY3liZXJuZXQgZGIgZWR1IGdvdiBncm9uZGFyIGlhY2Nlc3MgaW10IGluY2EgbGFuZGVzaWduIGxhdyBtaWwgbmV0IG5nbyBuaXMgbm9tIG9saXZldHRpIG9yZyBwaXggc2Nob29sIHRtIHdlYiAnLFxuICAgICAgJ3ptJzonIGFjIGNvIGNvbSBlZHUgZ292IG5ldCBvcmcgc2NoICdcbiAgICB9LFxuICAgIC8vIGdvcmhpbGwgMjAxMy0xMC0yNTogVXNpbmcgaW5kZXhPZigpIGluc3RlYWQgUmVnZXhwKCkuIFNpZ25pZmljYW50IGJvb3N0XG4gICAgLy8gaW4gYm90aCBwZXJmb3JtYW5jZSBhbmQgbWVtb3J5IGZvb3RwcmludC4gTm8gaW5pdGlhbGl6YXRpb24gcmVxdWlyZWQuXG4gICAgLy8gaHR0cDovL2pzcGVyZi5jb20vdXJpLWpzLXNsZC1yZWdleC12cy1iaW5hcnktc2VhcmNoLzRcbiAgICAvLyBGb2xsb3dpbmcgbWV0aG9kcyB1c2UgbGFzdEluZGV4T2YoKSByYXRoZXIgdGhhbiBhcnJheS5zcGxpdCgpIGluIG9yZGVyXG4gICAgLy8gdG8gYXZvaWQgYW55IG1lbW9yeSBhbGxvY2F0aW9ucy5cbiAgICBoYXM6IGZ1bmN0aW9uKGRvbWFpbikge1xuICAgICAgdmFyIHRsZE9mZnNldCA9IGRvbWFpbi5sYXN0SW5kZXhPZignLicpO1xuICAgICAgaWYgKHRsZE9mZnNldCA8PSAwIHx8IHRsZE9mZnNldCA+PSAoZG9tYWluLmxlbmd0aC0xKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICB2YXIgc2xkT2Zmc2V0ID0gZG9tYWluLmxhc3RJbmRleE9mKCcuJywgdGxkT2Zmc2V0LTEpO1xuICAgICAgaWYgKHNsZE9mZnNldCA8PSAwIHx8IHNsZE9mZnNldCA+PSAodGxkT2Zmc2V0LTEpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIHZhciBzbGRMaXN0ID0gU0xELmxpc3RbZG9tYWluLnNsaWNlKHRsZE9mZnNldCsxKV07XG4gICAgICBpZiAoIXNsZExpc3QpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHNsZExpc3QuaW5kZXhPZignICcgKyBkb21haW4uc2xpY2Uoc2xkT2Zmc2V0KzEsIHRsZE9mZnNldCkgKyAnICcpID49IDA7XG4gICAgfSxcbiAgICBpczogZnVuY3Rpb24oZG9tYWluKSB7XG4gICAgICB2YXIgdGxkT2Zmc2V0ID0gZG9tYWluLmxhc3RJbmRleE9mKCcuJyk7XG4gICAgICBpZiAodGxkT2Zmc2V0IDw9IDAgfHwgdGxkT2Zmc2V0ID49IChkb21haW4ubGVuZ3RoLTEpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIHZhciBzbGRPZmZzZXQgPSBkb21haW4ubGFzdEluZGV4T2YoJy4nLCB0bGRPZmZzZXQtMSk7XG4gICAgICBpZiAoc2xkT2Zmc2V0ID49IDApIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgdmFyIHNsZExpc3QgPSBTTEQubGlzdFtkb21haW4uc2xpY2UodGxkT2Zmc2V0KzEpXTtcbiAgICAgIGlmICghc2xkTGlzdCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICByZXR1cm4gc2xkTGlzdC5pbmRleE9mKCcgJyArIGRvbWFpbi5zbGljZSgwLCB0bGRPZmZzZXQpICsgJyAnKSA+PSAwO1xuICAgIH0sXG4gICAgZ2V0OiBmdW5jdGlvbihkb21haW4pIHtcbiAgICAgIHZhciB0bGRPZmZzZXQgPSBkb21haW4ubGFzdEluZGV4T2YoJy4nKTtcbiAgICAgIGlmICh0bGRPZmZzZXQgPD0gMCB8fCB0bGRPZmZzZXQgPj0gKGRvbWFpbi5sZW5ndGgtMSkpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICB2YXIgc2xkT2Zmc2V0ID0gZG9tYWluLmxhc3RJbmRleE9mKCcuJywgdGxkT2Zmc2V0LTEpO1xuICAgICAgaWYgKHNsZE9mZnNldCA8PSAwIHx8IHNsZE9mZnNldCA+PSAodGxkT2Zmc2V0LTEpKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgdmFyIHNsZExpc3QgPSBTTEQubGlzdFtkb21haW4uc2xpY2UodGxkT2Zmc2V0KzEpXTtcbiAgICAgIGlmICghc2xkTGlzdCkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIGlmIChzbGRMaXN0LmluZGV4T2YoJyAnICsgZG9tYWluLnNsaWNlKHNsZE9mZnNldCsxLCB0bGRPZmZzZXQpICsgJyAnKSA8IDApIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICByZXR1cm4gZG9tYWluLnNsaWNlKHNsZE9mZnNldCsxKTtcbiAgICB9LFxuICAgIG5vQ29uZmxpY3Q6IGZ1bmN0aW9uKCl7XG4gICAgICBpZiAocm9vdC5TZWNvbmRMZXZlbERvbWFpbnMgPT09IHRoaXMpIHtcbiAgICAgICAgcm9vdC5TZWNvbmRMZXZlbERvbWFpbnMgPSBfU2Vjb25kTGV2ZWxEb21haW5zO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICB9O1xuXG4gIHJldHVybiBTTEQ7XG59KSk7XG4iLCIvKiFcbiAqIFVSSS5qcyAtIE11dGF0aW5nIFVSTHNcbiAqXG4gKiBWZXJzaW9uOiAxLjE3LjBcbiAqXG4gKiBBdXRob3I6IFJvZG5leSBSZWhtXG4gKiBXZWI6IGh0dHA6Ly9tZWRpYWxpemUuZ2l0aHViLmlvL1VSSS5qcy9cbiAqXG4gKiBMaWNlbnNlZCB1bmRlclxuICogICBNSVQgTGljZW5zZSBodHRwOi8vd3d3Lm9wZW5zb3VyY2Uub3JnL2xpY2Vuc2VzL21pdC1saWNlbnNlXG4gKiAgIEdQTCB2MyBodHRwOi8vb3BlbnNvdXJjZS5vcmcvbGljZW5zZXMvR1BMLTMuMFxuICpcbiAqL1xuKGZ1bmN0aW9uIChyb290LCBmYWN0b3J5KSB7XG4gICd1c2Ugc3RyaWN0JztcbiAgLy8gaHR0cHM6Ly9naXRodWIuY29tL3VtZGpzL3VtZC9ibG9iL21hc3Rlci9yZXR1cm5FeHBvcnRzLmpzXG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcpIHtcbiAgICAvLyBOb2RlXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KHJlcXVpcmUoJy4vcHVueWNvZGUnKSwgcmVxdWlyZSgnLi9JUHY2JyksIHJlcXVpcmUoJy4vU2Vjb25kTGV2ZWxEb21haW5zJykpO1xuICB9IGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCkge1xuICAgIC8vIEFNRC4gUmVnaXN0ZXIgYXMgYW4gYW5vbnltb3VzIG1vZHVsZS5cbiAgICBkZWZpbmUoWycuL3B1bnljb2RlJywgJy4vSVB2NicsICcuL1NlY29uZExldmVsRG9tYWlucyddLCBmYWN0b3J5KTtcbiAgfSBlbHNlIHtcbiAgICAvLyBCcm93c2VyIGdsb2JhbHMgKHJvb3QgaXMgd2luZG93KVxuICAgIHJvb3QuVVJJID0gZmFjdG9yeShyb290LnB1bnljb2RlLCByb290LklQdjYsIHJvb3QuU2Vjb25kTGV2ZWxEb21haW5zLCByb290KTtcbiAgfVxufSh0aGlzLCBmdW5jdGlvbiAocHVueWNvZGUsIElQdjYsIFNMRCwgcm9vdCkge1xuICAndXNlIHN0cmljdCc7XG4gIC8qZ2xvYmFsIGxvY2F0aW9uLCBlc2NhcGUsIHVuZXNjYXBlICovXG4gIC8vIEZJWE1FOiB2Mi4wLjAgcmVuYW1jZSBub24tY2FtZWxDYXNlIHByb3BlcnRpZXMgdG8gdXBwZXJjYXNlXG4gIC8qanNoaW50IGNhbWVsY2FzZTogZmFsc2UgKi9cblxuICAvLyBzYXZlIGN1cnJlbnQgVVJJIHZhcmlhYmxlLCBpZiBhbnlcbiAgdmFyIF9VUkkgPSByb290ICYmIHJvb3QuVVJJO1xuXG4gIGZ1bmN0aW9uIFVSSSh1cmwsIGJhc2UpIHtcbiAgICB2YXIgX3VybFN1cHBsaWVkID0gYXJndW1lbnRzLmxlbmd0aCA+PSAxO1xuICAgIHZhciBfYmFzZVN1cHBsaWVkID0gYXJndW1lbnRzLmxlbmd0aCA+PSAyO1xuXG4gICAgLy8gQWxsb3cgaW5zdGFudGlhdGlvbiB3aXRob3V0IHRoZSAnbmV3JyBrZXl3b3JkXG4gICAgaWYgKCEodGhpcyBpbnN0YW5jZW9mIFVSSSkpIHtcbiAgICAgIGlmIChfdXJsU3VwcGxpZWQpIHtcbiAgICAgICAgaWYgKF9iYXNlU3VwcGxpZWQpIHtcbiAgICAgICAgICByZXR1cm4gbmV3IFVSSSh1cmwsIGJhc2UpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG5ldyBVUkkodXJsKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIG5ldyBVUkkoKTtcbiAgICB9XG5cbiAgICBpZiAodXJsID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGlmIChfdXJsU3VwcGxpZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigndW5kZWZpbmVkIGlzIG5vdCBhIHZhbGlkIGFyZ3VtZW50IGZvciBVUkknKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHR5cGVvZiBsb2NhdGlvbiAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgdXJsID0gbG9jYXRpb24uaHJlZiArICcnO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdXJsID0gJyc7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5ocmVmKHVybCk7XG5cbiAgICAvLyByZXNvbHZlIHRvIGJhc2UgYWNjb3JkaW5nIHRvIGh0dHA6Ly9kdmNzLnczLm9yZy9oZy91cmwvcmF3LWZpbGUvdGlwL092ZXJ2aWV3Lmh0bWwjY29uc3RydWN0b3JcbiAgICBpZiAoYmFzZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gdGhpcy5hYnNvbHV0ZVRvKGJhc2UpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgVVJJLnZlcnNpb24gPSAnMS4xNy4wJztcblxuICB2YXIgcCA9IFVSSS5wcm90b3R5cGU7XG4gIHZhciBoYXNPd24gPSBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5O1xuXG4gIGZ1bmN0aW9uIGVzY2FwZVJlZ0V4KHN0cmluZykge1xuICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9tZWRpYWxpemUvVVJJLmpzL2NvbW1pdC84NWFjMjE3ODNjMTFmOGNjYWIwNjEwNmRiYTk3MzVhMzFhODY5MjRkI2NvbW1pdGNvbW1lbnQtODIxOTYzXG4gICAgcmV0dXJuIHN0cmluZy5yZXBsYWNlKC8oWy4qKz9ePSE6JHt9KCl8W1xcXVxcL1xcXFxdKS9nLCAnXFxcXCQxJyk7XG4gIH1cblxuICBmdW5jdGlvbiBnZXRUeXBlKHZhbHVlKSB7XG4gICAgLy8gSUU4IGRvZXNuJ3QgcmV0dXJuIFtPYmplY3QgVW5kZWZpbmVkXSBidXQgW09iamVjdCBPYmplY3RdIGZvciB1bmRlZmluZWQgdmFsdWVcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuICdVbmRlZmluZWQnO1xuICAgIH1cblxuICAgIHJldHVybiBTdHJpbmcoT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHZhbHVlKSkuc2xpY2UoOCwgLTEpO1xuICB9XG5cbiAgZnVuY3Rpb24gaXNBcnJheShvYmopIHtcbiAgICByZXR1cm4gZ2V0VHlwZShvYmopID09PSAnQXJyYXknO1xuICB9XG5cbiAgZnVuY3Rpb24gZmlsdGVyQXJyYXlWYWx1ZXMoZGF0YSwgdmFsdWUpIHtcbiAgICB2YXIgbG9va3VwID0ge307XG4gICAgdmFyIGksIGxlbmd0aDtcblxuICAgIGlmIChnZXRUeXBlKHZhbHVlKSA9PT0gJ1JlZ0V4cCcpIHtcbiAgICAgIGxvb2t1cCA9IG51bGw7XG4gICAgfSBlbHNlIGlmIChpc0FycmF5KHZhbHVlKSkge1xuICAgICAgZm9yIChpID0gMCwgbGVuZ3RoID0gdmFsdWUubGVuZ3RoOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgICAgbG9va3VwW3ZhbHVlW2ldXSA9IHRydWU7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvb2t1cFt2YWx1ZV0gPSB0cnVlO1xuICAgIH1cblxuICAgIGZvciAoaSA9IDAsIGxlbmd0aCA9IGRhdGEubGVuZ3RoOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgIC8qanNoaW50IGxheGJyZWFrOiB0cnVlICovXG4gICAgICB2YXIgX21hdGNoID0gbG9va3VwICYmIGxvb2t1cFtkYXRhW2ldXSAhPT0gdW5kZWZpbmVkXG4gICAgICAgIHx8ICFsb29rdXAgJiYgdmFsdWUudGVzdChkYXRhW2ldKTtcbiAgICAgIC8qanNoaW50IGxheGJyZWFrOiBmYWxzZSAqL1xuICAgICAgaWYgKF9tYXRjaCkge1xuICAgICAgICBkYXRhLnNwbGljZShpLCAxKTtcbiAgICAgICAgbGVuZ3RoLS07XG4gICAgICAgIGktLTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZGF0YTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGFycmF5Q29udGFpbnMobGlzdCwgdmFsdWUpIHtcbiAgICB2YXIgaSwgbGVuZ3RoO1xuXG4gICAgLy8gdmFsdWUgbWF5IGJlIHN0cmluZywgbnVtYmVyLCBhcnJheSwgcmVnZXhwXG4gICAgaWYgKGlzQXJyYXkodmFsdWUpKSB7XG4gICAgICAvLyBOb3RlOiB0aGlzIGNhbiBiZSBvcHRpbWl6ZWQgdG8gTyhuKSAoaW5zdGVhZCBvZiBjdXJyZW50IE8obSAqIG4pKVxuICAgICAgZm9yIChpID0gMCwgbGVuZ3RoID0gdmFsdWUubGVuZ3RoOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKCFhcnJheUNvbnRhaW5zKGxpc3QsIHZhbHVlW2ldKSkge1xuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICB2YXIgX3R5cGUgPSBnZXRUeXBlKHZhbHVlKTtcbiAgICBmb3IgKGkgPSAwLCBsZW5ndGggPSBsaXN0Lmxlbmd0aDsgaSA8IGxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAoX3R5cGUgPT09ICdSZWdFeHAnKSB7XG4gICAgICAgIGlmICh0eXBlb2YgbGlzdFtpXSA9PT0gJ3N0cmluZycgJiYgbGlzdFtpXS5tYXRjaCh2YWx1ZSkpIHtcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChsaXN0W2ldID09PSB2YWx1ZSkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBmdW5jdGlvbiBhcnJheXNFcXVhbChvbmUsIHR3bykge1xuICAgIGlmICghaXNBcnJheShvbmUpIHx8ICFpc0FycmF5KHR3bykpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBhcnJheXMgY2FuJ3QgYmUgZXF1YWwgaWYgdGhleSBoYXZlIGRpZmZlcmVudCBhbW91bnQgb2YgY29udGVudFxuICAgIGlmIChvbmUubGVuZ3RoICE9PSB0d28ubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgb25lLnNvcnQoKTtcbiAgICB0d28uc29ydCgpO1xuXG4gICAgZm9yICh2YXIgaSA9IDAsIGwgPSBvbmUubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICBpZiAob25lW2ldICE9PSB0d29baV0pIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJpbVNsYXNoZXModGV4dCkge1xuICAgIHZhciB0cmltX2V4cHJlc3Npb24gPSAvXlxcLyt8XFwvKyQvZztcbiAgICByZXR1cm4gdGV4dC5yZXBsYWNlKHRyaW1fZXhwcmVzc2lvbiwgJycpO1xuICB9XG5cbiAgVVJJLl9wYXJ0cyA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiB7XG4gICAgICBwcm90b2NvbDogbnVsbCxcbiAgICAgIHVzZXJuYW1lOiBudWxsLFxuICAgICAgcGFzc3dvcmQ6IG51bGwsXG4gICAgICBob3N0bmFtZTogbnVsbCxcbiAgICAgIHVybjogbnVsbCxcbiAgICAgIHBvcnQ6IG51bGwsXG4gICAgICBwYXRoOiBudWxsLFxuICAgICAgcXVlcnk6IG51bGwsXG4gICAgICBmcmFnbWVudDogbnVsbCxcbiAgICAgIC8vIHN0YXRlXG4gICAgICBkdXBsaWNhdGVRdWVyeVBhcmFtZXRlcnM6IFVSSS5kdXBsaWNhdGVRdWVyeVBhcmFtZXRlcnMsXG4gICAgICBlc2NhcGVRdWVyeVNwYWNlOiBVUkkuZXNjYXBlUXVlcnlTcGFjZVxuICAgIH07XG4gIH07XG4gIC8vIHN0YXRlOiBhbGxvdyBkdXBsaWNhdGUgcXVlcnkgcGFyYW1ldGVycyAoYT0xJmE9MSlcbiAgVVJJLmR1cGxpY2F0ZVF1ZXJ5UGFyYW1ldGVycyA9IGZhbHNlO1xuICAvLyBzdGF0ZTogcmVwbGFjZXMgKyB3aXRoICUyMCAoc3BhY2UgaW4gcXVlcnkgc3RyaW5ncylcbiAgVVJJLmVzY2FwZVF1ZXJ5U3BhY2UgPSB0cnVlO1xuICAvLyBzdGF0aWMgcHJvcGVydGllc1xuICBVUkkucHJvdG9jb2xfZXhwcmVzc2lvbiA9IC9eW2Etel1bYS16MC05ListXSokL2k7XG4gIFVSSS5pZG5fZXhwcmVzc2lvbiA9IC9bXmEtejAtOVxcLi1dL2k7XG4gIFVSSS5wdW55Y29kZV9leHByZXNzaW9uID0gLyh4bi0tKS9pO1xuICAvLyB3ZWxsLCAzMzMuNDQ0LjU1NS42NjYgbWF0Y2hlcywgYnV0IGl0IHN1cmUgYWluJ3Qgbm8gSVB2NCAtIGRvIHdlIGNhcmU/XG4gIFVSSS5pcDRfZXhwcmVzc2lvbiA9IC9eXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM30kLztcbiAgLy8gY3JlZGl0cyB0byBSaWNoIEJyb3duXG4gIC8vIHNvdXJjZTogaHR0cDovL2ZvcnVtcy5pbnRlcm1hcHBlci5jb20vdmlld3RvcGljLnBocD9wPTEwOTYjMTA5NlxuICAvLyBzcGVjaWZpY2F0aW9uOiBodHRwOi8vd3d3LmlldGYub3JnL3JmYy9yZmM0MjkxLnR4dFxuICBVUkkuaXA2X2V4cHJlc3Npb24gPSAvXlxccyooKChbMC05QS1GYS1mXXsxLDR9Oil7N30oWzAtOUEtRmEtZl17MSw0fXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7Nn0oOlswLTlBLUZhLWZdezEsNH18KCgyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkoXFwuKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKSl7M30pfDopKXwoKFswLTlBLUZhLWZdezEsNH06KXs1fSgoKDpbMC05QS1GYS1mXXsxLDR9KXsxLDJ9KXw6KCgyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkoXFwuKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKSl7M30pfDopKXwoKFswLTlBLUZhLWZdezEsNH06KXs0fSgoKDpbMC05QS1GYS1mXXsxLDR9KXsxLDN9KXwoKDpbMC05QS1GYS1mXXsxLDR9KT86KCgyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkoXFwuKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKSl7M30pKXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7M30oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSw0fSl8KCg6WzAtOUEtRmEtZl17MSw0fSl7MCwyfTooKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKShcXC4oMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKXszfSkpfDopKXwoKFswLTlBLUZhLWZdezEsNH06KXsyfSgoKDpbMC05QS1GYS1mXXsxLDR9KXsxLDV9KXwoKDpbMC05QS1GYS1mXXsxLDR9KXswLDN9OigoMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKFxcLigyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkpezN9KSl8OikpfCgoWzAtOUEtRmEtZl17MSw0fTopezF9KCgoOlswLTlBLUZhLWZdezEsNH0pezEsNn0pfCgoOlswLTlBLUZhLWZdezEsNH0pezAsNH06KCgyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkoXFwuKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKSl7M30pKXw6KSl8KDooKCg6WzAtOUEtRmEtZl17MSw0fSl7MSw3fSl8KCg6WzAtOUEtRmEtZl17MSw0fSl7MCw1fTooKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKShcXC4oMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKXszfSkpfDopKSkoJS4rKT9cXHMqJC87XG4gIC8vIGV4cHJlc3Npb24gdXNlZCBpcyBcImdydWJlciByZXZpc2VkXCIgKEBncnViZXIgdjIpIGRldGVybWluZWQgdG8gYmUgdGhlXG4gIC8vIGJlc3Qgc29sdXRpb24gaW4gYSByZWdleC1nb2xmIHdlIGRpZCBhIGNvdXBsZSBvZiBhZ2VzIGFnbyBhdFxuICAvLyAqIGh0dHA6Ly9tYXRoaWFzYnluZW5zLmJlL2RlbW8vdXJsLXJlZ2V4XG4gIC8vICogaHR0cDovL3JvZG5leXJlaG0uZGUvdC91cmwtcmVnZXguaHRtbFxuICBVUkkuZmluZF91cmlfZXhwcmVzc2lvbiA9IC9cXGIoKD86W2Etel1bXFx3LV0rOig/OlxcL3sxLDN9fFthLXowLTklXSl8d3d3XFxkezAsM31bLl18W2EtejAtOS5cXC1dK1suXVthLXpdezIsNH1cXC8pKD86W15cXHMoKTw+XSt8XFwoKFteXFxzKCk8Pl0rfChcXChbXlxccygpPD5dK1xcKSkpKlxcKSkrKD86XFwoKFteXFxzKCk8Pl0rfChcXChbXlxccygpPD5dK1xcKSkpKlxcKXxbXlxcc2AhKClcXFtcXF17fTs6J1wiLiw8Pj/Cq8K74oCc4oCd4oCY4oCZXSkpL2lnO1xuICBVUkkuZmluZFVyaSA9IHtcbiAgICAvLyB2YWxpZCBcInNjaGVtZTovL1wiIG9yIFwid3d3LlwiXG4gICAgc3RhcnQ6IC9cXGIoPzooW2Etel1bYS16MC05ListXSo6XFwvXFwvKXx3d3dcXC4pL2dpLFxuICAgIC8vIGV2ZXJ5dGhpbmcgdXAgdG8gdGhlIG5leHQgd2hpdGVzcGFjZVxuICAgIGVuZDogL1tcXHNcXHJcXG5dfCQvLFxuICAgIC8vIHRyaW0gdHJhaWxpbmcgcHVuY3R1YXRpb24gY2FwdHVyZWQgYnkgZW5kIFJlZ0V4cFxuICAgIHRyaW06IC9bYCEoKVxcW1xcXXt9OzonXCIuLDw+P8KrwrvigJzigJ3igJ7igJjigJldKyQvXG4gIH07XG4gIC8vIGh0dHA6Ly93d3cuaWFuYS5vcmcvYXNzaWdubWVudHMvdXJpLXNjaGVtZXMuaHRtbFxuICAvLyBodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfVENQX2FuZF9VRFBfcG9ydF9udW1iZXJzI1dlbGwta25vd25fcG9ydHNcbiAgVVJJLmRlZmF1bHRQb3J0cyA9IHtcbiAgICBodHRwOiAnODAnLFxuICAgIGh0dHBzOiAnNDQzJyxcbiAgICBmdHA6ICcyMScsXG4gICAgZ29waGVyOiAnNzAnLFxuICAgIHdzOiAnODAnLFxuICAgIHdzczogJzQ0MydcbiAgfTtcbiAgLy8gYWxsb3dlZCBob3N0bmFtZSBjaGFyYWN0ZXJzIGFjY29yZGluZyB0byBSRkMgMzk4NlxuICAvLyBBTFBIQSBESUdJVCBcIi1cIiBcIi5cIiBcIl9cIiBcIn5cIiBcIiFcIiBcIiRcIiBcIiZcIiBcIidcIiBcIihcIiBcIilcIiBcIipcIiBcIitcIiBcIixcIiBcIjtcIiBcIj1cIiAlZW5jb2RlZFxuICAvLyBJJ3ZlIG5ldmVyIHNlZW4gYSAobm9uLUlETikgaG9zdG5hbWUgb3RoZXIgdGhhbjogQUxQSEEgRElHSVQgLiAtXG4gIFVSSS5pbnZhbGlkX2hvc3RuYW1lX2NoYXJhY3RlcnMgPSAvW15hLXpBLVowLTlcXC4tXS87XG4gIC8vIG1hcCBET00gRWxlbWVudHMgdG8gdGhlaXIgVVJJIGF0dHJpYnV0ZVxuICBVUkkuZG9tQXR0cmlidXRlcyA9IHtcbiAgICAnYSc6ICdocmVmJyxcbiAgICAnYmxvY2txdW90ZSc6ICdjaXRlJyxcbiAgICAnbGluayc6ICdocmVmJyxcbiAgICAnYmFzZSc6ICdocmVmJyxcbiAgICAnc2NyaXB0JzogJ3NyYycsXG4gICAgJ2Zvcm0nOiAnYWN0aW9uJyxcbiAgICAnaW1nJzogJ3NyYycsXG4gICAgJ2FyZWEnOiAnaHJlZicsXG4gICAgJ2lmcmFtZSc6ICdzcmMnLFxuICAgICdlbWJlZCc6ICdzcmMnLFxuICAgICdzb3VyY2UnOiAnc3JjJyxcbiAgICAndHJhY2snOiAnc3JjJyxcbiAgICAnaW5wdXQnOiAnc3JjJywgLy8gYnV0IG9ubHkgaWYgdHlwZT1cImltYWdlXCJcbiAgICAnYXVkaW8nOiAnc3JjJyxcbiAgICAndmlkZW8nOiAnc3JjJ1xuICB9O1xuICBVUkkuZ2V0RG9tQXR0cmlidXRlID0gZnVuY3Rpb24obm9kZSkge1xuICAgIGlmICghbm9kZSB8fCAhbm9kZS5ub2RlTmFtZSkge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICB2YXIgbm9kZU5hbWUgPSBub2RlLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7XG4gICAgLy8gPGlucHV0PiBzaG91bGQgb25seSBleHBvc2Ugc3JjIGZvciB0eXBlPVwiaW1hZ2VcIlxuICAgIGlmIChub2RlTmFtZSA9PT0gJ2lucHV0JyAmJiBub2RlLnR5cGUgIT09ICdpbWFnZScpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIFVSSS5kb21BdHRyaWJ1dGVzW25vZGVOYW1lXTtcbiAgfTtcblxuICBmdW5jdGlvbiBlc2NhcGVGb3JEdW1iRmlyZWZveDM2KHZhbHVlKSB7XG4gICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL21lZGlhbGl6ZS9VUkkuanMvaXNzdWVzLzkxXG4gICAgcmV0dXJuIGVzY2FwZSh2YWx1ZSk7XG4gIH1cblxuICAvLyBlbmNvZGluZyAvIGRlY29kaW5nIGFjY29yZGluZyB0byBSRkMzOTg2XG4gIGZ1bmN0aW9uIHN0cmljdEVuY29kZVVSSUNvbXBvbmVudChzdHJpbmcpIHtcbiAgICAvLyBzZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9lbmNvZGVVUklDb21wb25lbnRcbiAgICByZXR1cm4gZW5jb2RlVVJJQ29tcG9uZW50KHN0cmluZylcbiAgICAgIC5yZXBsYWNlKC9bIScoKSpdL2csIGVzY2FwZUZvckR1bWJGaXJlZm94MzYpXG4gICAgICAucmVwbGFjZSgvXFwqL2csICclMkEnKTtcbiAgfVxuICBVUkkuZW5jb2RlID0gc3RyaWN0RW5jb2RlVVJJQ29tcG9uZW50O1xuICBVUkkuZGVjb2RlID0gZGVjb2RlVVJJQ29tcG9uZW50O1xuICBVUkkuaXNvODg1OSA9IGZ1bmN0aW9uKCkge1xuICAgIFVSSS5lbmNvZGUgPSBlc2NhcGU7XG4gICAgVVJJLmRlY29kZSA9IHVuZXNjYXBlO1xuICB9O1xuICBVUkkudW5pY29kZSA9IGZ1bmN0aW9uKCkge1xuICAgIFVSSS5lbmNvZGUgPSBzdHJpY3RFbmNvZGVVUklDb21wb25lbnQ7XG4gICAgVVJJLmRlY29kZSA9IGRlY29kZVVSSUNvbXBvbmVudDtcbiAgfTtcbiAgVVJJLmNoYXJhY3RlcnMgPSB7XG4gICAgcGF0aG5hbWU6IHtcbiAgICAgIGVuY29kZToge1xuICAgICAgICAvLyBSRkMzOTg2IDIuMTogRm9yIGNvbnNpc3RlbmN5LCBVUkkgcHJvZHVjZXJzIGFuZCBub3JtYWxpemVycyBzaG91bGRcbiAgICAgICAgLy8gdXNlIHVwcGVyY2FzZSBoZXhhZGVjaW1hbCBkaWdpdHMgZm9yIGFsbCBwZXJjZW50LWVuY29kaW5ncy5cbiAgICAgICAgZXhwcmVzc2lvbjogLyUoMjR8MjZ8MkJ8MkN8M0J8M0R8M0F8NDApL2lnLFxuICAgICAgICBtYXA6IHtcbiAgICAgICAgICAvLyAtLl9+IScoKSpcbiAgICAgICAgICAnJTI0JzogJyQnLFxuICAgICAgICAgICclMjYnOiAnJicsXG4gICAgICAgICAgJyUyQic6ICcrJyxcbiAgICAgICAgICAnJTJDJzogJywnLFxuICAgICAgICAgICclM0InOiAnOycsXG4gICAgICAgICAgJyUzRCc6ICc9JyxcbiAgICAgICAgICAnJTNBJzogJzonLFxuICAgICAgICAgICclNDAnOiAnQCdcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIGRlY29kZToge1xuICAgICAgICBleHByZXNzaW9uOiAvW1xcL1xcPyNdL2csXG4gICAgICAgIG1hcDoge1xuICAgICAgICAgICcvJzogJyUyRicsXG4gICAgICAgICAgJz8nOiAnJTNGJyxcbiAgICAgICAgICAnIyc6ICclMjMnXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuICAgIHJlc2VydmVkOiB7XG4gICAgICBlbmNvZGU6IHtcbiAgICAgICAgLy8gUkZDMzk4NiAyLjE6IEZvciBjb25zaXN0ZW5jeSwgVVJJIHByb2R1Y2VycyBhbmQgbm9ybWFsaXplcnMgc2hvdWxkXG4gICAgICAgIC8vIHVzZSB1cHBlcmNhc2UgaGV4YWRlY2ltYWwgZGlnaXRzIGZvciBhbGwgcGVyY2VudC1lbmNvZGluZ3MuXG4gICAgICAgIGV4cHJlc3Npb246IC8lKDIxfDIzfDI0fDI2fDI3fDI4fDI5fDJBfDJCfDJDfDJGfDNBfDNCfDNEfDNGfDQwfDVCfDVEKS9pZyxcbiAgICAgICAgbWFwOiB7XG4gICAgICAgICAgLy8gZ2VuLWRlbGltc1xuICAgICAgICAgICclM0EnOiAnOicsXG4gICAgICAgICAgJyUyRic6ICcvJyxcbiAgICAgICAgICAnJTNGJzogJz8nLFxuICAgICAgICAgICclMjMnOiAnIycsXG4gICAgICAgICAgJyU1Qic6ICdbJyxcbiAgICAgICAgICAnJTVEJzogJ10nLFxuICAgICAgICAgICclNDAnOiAnQCcsXG4gICAgICAgICAgLy8gc3ViLWRlbGltc1xuICAgICAgICAgICclMjEnOiAnIScsXG4gICAgICAgICAgJyUyNCc6ICckJyxcbiAgICAgICAgICAnJTI2JzogJyYnLFxuICAgICAgICAgICclMjcnOiAnXFwnJyxcbiAgICAgICAgICAnJTI4JzogJygnLFxuICAgICAgICAgICclMjknOiAnKScsXG4gICAgICAgICAgJyUyQSc6ICcqJyxcbiAgICAgICAgICAnJTJCJzogJysnLFxuICAgICAgICAgICclMkMnOiAnLCcsXG4gICAgICAgICAgJyUzQic6ICc7JyxcbiAgICAgICAgICAnJTNEJzogJz0nXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuICAgIHVybnBhdGg6IHtcbiAgICAgIC8vIFRoZSBjaGFyYWN0ZXJzIHVuZGVyIGBlbmNvZGVgIGFyZSB0aGUgY2hhcmFjdGVycyBjYWxsZWQgb3V0IGJ5IFJGQyAyMTQxIGFzIGJlaW5nIGFjY2VwdGFibGVcbiAgICAgIC8vIGZvciB1c2FnZSBpbiBhIFVSTi4gUkZDMjE0MSBhbHNvIGNhbGxzIG91dCBcIi1cIiwgXCIuXCIsIGFuZCBcIl9cIiBhcyBhY2NlcHRhYmxlIGNoYXJhY3RlcnMsIGJ1dFxuICAgICAgLy8gdGhlc2UgYXJlbid0IGVuY29kZWQgYnkgZW5jb2RlVVJJQ29tcG9uZW50LCBzbyB3ZSBkb24ndCBoYXZlIHRvIGNhbGwgdGhlbSBvdXQgaGVyZS4gQWxzb1xuICAgICAgLy8gbm90ZSB0aGF0IHRoZSBjb2xvbiBjaGFyYWN0ZXIgaXMgbm90IGZlYXR1cmVkIGluIHRoZSBlbmNvZGluZyBtYXA7IHRoaXMgaXMgYmVjYXVzZSBVUkkuanNcbiAgICAgIC8vIGdpdmVzIHRoZSBjb2xvbnMgaW4gVVJOcyBzZW1hbnRpYyBtZWFuaW5nIGFzIHRoZSBkZWxpbWl0ZXJzIG9mIHBhdGggc2VnZW1lbnRzLCBhbmQgc28gaXRcbiAgICAgIC8vIHNob3VsZCBub3QgYXBwZWFyIHVuZW5jb2RlZCBpbiBhIHNlZ21lbnQgaXRzZWxmLlxuICAgICAgLy8gU2VlIGFsc28gdGhlIG5vdGUgYWJvdmUgYWJvdXQgUkZDMzk4NiBhbmQgY2FwaXRhbGFsaXplZCBoZXggZGlnaXRzLlxuICAgICAgZW5jb2RlOiB7XG4gICAgICAgIGV4cHJlc3Npb246IC8lKDIxfDI0fDI3fDI4fDI5fDJBfDJCfDJDfDNCfDNEfDQwKS9pZyxcbiAgICAgICAgbWFwOiB7XG4gICAgICAgICAgJyUyMSc6ICchJyxcbiAgICAgICAgICAnJTI0JzogJyQnLFxuICAgICAgICAgICclMjcnOiAnXFwnJyxcbiAgICAgICAgICAnJTI4JzogJygnLFxuICAgICAgICAgICclMjknOiAnKScsXG4gICAgICAgICAgJyUyQSc6ICcqJyxcbiAgICAgICAgICAnJTJCJzogJysnLFxuICAgICAgICAgICclMkMnOiAnLCcsXG4gICAgICAgICAgJyUzQic6ICc7JyxcbiAgICAgICAgICAnJTNEJzogJz0nLFxuICAgICAgICAgICclNDAnOiAnQCdcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIC8vIFRoZXNlIGNoYXJhY3RlcnMgYXJlIHRoZSBjaGFyYWN0ZXJzIGNhbGxlZCBvdXQgYnkgUkZDMjE0MSBhcyBcInJlc2VydmVkXCIgY2hhcmFjdGVycyB0aGF0XG4gICAgICAvLyBzaG91bGQgbmV2ZXIgYXBwZWFyIGluIGEgVVJOLCBwbHVzIHRoZSBjb2xvbiBjaGFyYWN0ZXIgKHNlZSBub3RlIGFib3ZlKS5cbiAgICAgIGRlY29kZToge1xuICAgICAgICBleHByZXNzaW9uOiAvW1xcL1xcPyM6XS9nLFxuICAgICAgICBtYXA6IHtcbiAgICAgICAgICAnLyc6ICclMkYnLFxuICAgICAgICAgICc/JzogJyUzRicsXG4gICAgICAgICAgJyMnOiAnJTIzJyxcbiAgICAgICAgICAnOic6ICclM0EnXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH07XG4gIFVSSS5lbmNvZGVRdWVyeSA9IGZ1bmN0aW9uKHN0cmluZywgZXNjYXBlUXVlcnlTcGFjZSkge1xuICAgIHZhciBlc2NhcGVkID0gVVJJLmVuY29kZShzdHJpbmcgKyAnJyk7XG4gICAgaWYgKGVzY2FwZVF1ZXJ5U3BhY2UgPT09IHVuZGVmaW5lZCkge1xuICAgICAgZXNjYXBlUXVlcnlTcGFjZSA9IFVSSS5lc2NhcGVRdWVyeVNwYWNlO1xuICAgIH1cblxuICAgIHJldHVybiBlc2NhcGVRdWVyeVNwYWNlID8gZXNjYXBlZC5yZXBsYWNlKC8lMjAvZywgJysnKSA6IGVzY2FwZWQ7XG4gIH07XG4gIFVSSS5kZWNvZGVRdWVyeSA9IGZ1bmN0aW9uKHN0cmluZywgZXNjYXBlUXVlcnlTcGFjZSkge1xuICAgIHN0cmluZyArPSAnJztcbiAgICBpZiAoZXNjYXBlUXVlcnlTcGFjZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBlc2NhcGVRdWVyeVNwYWNlID0gVVJJLmVzY2FwZVF1ZXJ5U3BhY2U7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBVUkkuZGVjb2RlKGVzY2FwZVF1ZXJ5U3BhY2UgPyBzdHJpbmcucmVwbGFjZSgvXFwrL2csICclMjAnKSA6IHN0cmluZyk7XG4gICAgfSBjYXRjaChlKSB7XG4gICAgICAvLyB3ZSdyZSBub3QgZ29pbmcgdG8gbWVzcyB3aXRoIHdlaXJkIGVuY29kaW5ncyxcbiAgICAgIC8vIGdpdmUgdXAgYW5kIHJldHVybiB0aGUgdW5kZWNvZGVkIG9yaWdpbmFsIHN0cmluZ1xuICAgICAgLy8gc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9tZWRpYWxpemUvVVJJLmpzL2lzc3Vlcy84N1xuICAgICAgLy8gc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9tZWRpYWxpemUvVVJJLmpzL2lzc3Vlcy85MlxuICAgICAgcmV0dXJuIHN0cmluZztcbiAgICB9XG4gIH07XG4gIC8vIGdlbmVyYXRlIGVuY29kZS9kZWNvZGUgcGF0aCBmdW5jdGlvbnNcbiAgdmFyIF9wYXJ0cyA9IHsnZW5jb2RlJzonZW5jb2RlJywgJ2RlY29kZSc6J2RlY29kZSd9O1xuICB2YXIgX3BhcnQ7XG4gIHZhciBnZW5lcmF0ZUFjY2Vzc29yID0gZnVuY3Rpb24oX2dyb3VwLCBfcGFydCkge1xuICAgIHJldHVybiBmdW5jdGlvbihzdHJpbmcpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBVUklbX3BhcnRdKHN0cmluZyArICcnKS5yZXBsYWNlKFVSSS5jaGFyYWN0ZXJzW19ncm91cF1bX3BhcnRdLmV4cHJlc3Npb24sIGZ1bmN0aW9uKGMpIHtcbiAgICAgICAgICByZXR1cm4gVVJJLmNoYXJhY3RlcnNbX2dyb3VwXVtfcGFydF0ubWFwW2NdO1xuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgLy8gd2UncmUgbm90IGdvaW5nIHRvIG1lc3Mgd2l0aCB3ZWlyZCBlbmNvZGluZ3MsXG4gICAgICAgIC8vIGdpdmUgdXAgYW5kIHJldHVybiB0aGUgdW5kZWNvZGVkIG9yaWdpbmFsIHN0cmluZ1xuICAgICAgICAvLyBzZWUgaHR0cHM6Ly9naXRodWIuY29tL21lZGlhbGl6ZS9VUkkuanMvaXNzdWVzLzg3XG4gICAgICAgIC8vIHNlZSBodHRwczovL2dpdGh1Yi5jb20vbWVkaWFsaXplL1VSSS5qcy9pc3N1ZXMvOTJcbiAgICAgICAgcmV0dXJuIHN0cmluZztcbiAgICAgIH1cbiAgICB9O1xuICB9O1xuXG4gIGZvciAoX3BhcnQgaW4gX3BhcnRzKSB7XG4gICAgVVJJW19wYXJ0ICsgJ1BhdGhTZWdtZW50J10gPSBnZW5lcmF0ZUFjY2Vzc29yKCdwYXRobmFtZScsIF9wYXJ0c1tfcGFydF0pO1xuICAgIFVSSVtfcGFydCArICdVcm5QYXRoU2VnbWVudCddID0gZ2VuZXJhdGVBY2Nlc3NvcigndXJucGF0aCcsIF9wYXJ0c1tfcGFydF0pO1xuICB9XG5cbiAgdmFyIGdlbmVyYXRlU2VnbWVudGVkUGF0aEZ1bmN0aW9uID0gZnVuY3Rpb24oX3NlcCwgX2NvZGluZ0Z1bmNOYW1lLCBfaW5uZXJDb2RpbmdGdW5jTmFtZSkge1xuICAgIHJldHVybiBmdW5jdGlvbihzdHJpbmcpIHtcbiAgICAgIC8vIFdoeSBwYXNzIGluIG5hbWVzIG9mIGZ1bmN0aW9ucywgcmF0aGVyIHRoYW4gdGhlIGZ1bmN0aW9uIG9iamVjdHMgdGhlbXNlbHZlcz8gVGhlXG4gICAgICAvLyBkZWZpbml0aW9ucyBvZiBzb21lIGZ1bmN0aW9ucyAoYnV0IGluIHBhcnRpY3VsYXIsIFVSSS5kZWNvZGUpIHdpbGwgb2NjYXNpb25hbGx5IGNoYW5nZSBkdWVcbiAgICAgIC8vIHRvIFVSSS5qcyBoYXZpbmcgSVNPODg1OSBhbmQgVW5pY29kZSBtb2Rlcy4gUGFzc2luZyBpbiB0aGUgbmFtZSBhbmQgZ2V0dGluZyBpdCB3aWxsIGVuc3VyZVxuICAgICAgLy8gdGhhdCB0aGUgZnVuY3Rpb25zIHdlIHVzZSBoZXJlIGFyZSBcImZyZXNoXCIuXG4gICAgICB2YXIgYWN0dWFsQ29kaW5nRnVuYztcbiAgICAgIGlmICghX2lubmVyQ29kaW5nRnVuY05hbWUpIHtcbiAgICAgICAgYWN0dWFsQ29kaW5nRnVuYyA9IFVSSVtfY29kaW5nRnVuY05hbWVdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYWN0dWFsQ29kaW5nRnVuYyA9IGZ1bmN0aW9uKHN0cmluZykge1xuICAgICAgICAgIHJldHVybiBVUklbX2NvZGluZ0Z1bmNOYW1lXShVUklbX2lubmVyQ29kaW5nRnVuY05hbWVdKHN0cmluZykpO1xuICAgICAgICB9O1xuICAgICAgfVxuXG4gICAgICB2YXIgc2VnbWVudHMgPSAoc3RyaW5nICsgJycpLnNwbGl0KF9zZXApO1xuXG4gICAgICBmb3IgKHZhciBpID0gMCwgbGVuZ3RoID0gc2VnbWVudHMubGVuZ3RoOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgICAgc2VnbWVudHNbaV0gPSBhY3R1YWxDb2RpbmdGdW5jKHNlZ21lbnRzW2ldKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHNlZ21lbnRzLmpvaW4oX3NlcCk7XG4gICAgfTtcbiAgfTtcblxuICAvLyBUaGlzIHRha2VzIHBsYWNlIG91dHNpZGUgdGhlIGFib3ZlIGxvb3AgYmVjYXVzZSB3ZSBkb24ndCB3YW50LCBlLmcuLCBlbmNvZGVVcm5QYXRoIGZ1bmN0aW9ucy5cbiAgVVJJLmRlY29kZVBhdGggPSBnZW5lcmF0ZVNlZ21lbnRlZFBhdGhGdW5jdGlvbignLycsICdkZWNvZGVQYXRoU2VnbWVudCcpO1xuICBVUkkuZGVjb2RlVXJuUGF0aCA9IGdlbmVyYXRlU2VnbWVudGVkUGF0aEZ1bmN0aW9uKCc6JywgJ2RlY29kZVVyblBhdGhTZWdtZW50Jyk7XG4gIFVSSS5yZWNvZGVQYXRoID0gZ2VuZXJhdGVTZWdtZW50ZWRQYXRoRnVuY3Rpb24oJy8nLCAnZW5jb2RlUGF0aFNlZ21lbnQnLCAnZGVjb2RlJyk7XG4gIFVSSS5yZWNvZGVVcm5QYXRoID0gZ2VuZXJhdGVTZWdtZW50ZWRQYXRoRnVuY3Rpb24oJzonLCAnZW5jb2RlVXJuUGF0aFNlZ21lbnQnLCAnZGVjb2RlJyk7XG5cbiAgVVJJLmVuY29kZVJlc2VydmVkID0gZ2VuZXJhdGVBY2Nlc3NvcigncmVzZXJ2ZWQnLCAnZW5jb2RlJyk7XG5cbiAgVVJJLnBhcnNlID0gZnVuY3Rpb24oc3RyaW5nLCBwYXJ0cykge1xuICAgIHZhciBwb3M7XG4gICAgaWYgKCFwYXJ0cykge1xuICAgICAgcGFydHMgPSB7fTtcbiAgICB9XG4gICAgLy8gW3Byb3RvY29sXCI6Ly9cIlt1c2VybmFtZVtcIjpcInBhc3N3b3JkXVwiQFwiXWhvc3RuYW1lW1wiOlwicG9ydF1cIi9cIj9dW3BhdGhdW1wiP1wicXVlcnlzdHJpbmddW1wiI1wiZnJhZ21lbnRdXG5cbiAgICAvLyBleHRyYWN0IGZyYWdtZW50XG4gICAgcG9zID0gc3RyaW5nLmluZGV4T2YoJyMnKTtcbiAgICBpZiAocG9zID4gLTEpIHtcbiAgICAgIC8vIGVzY2FwaW5nP1xuICAgICAgcGFydHMuZnJhZ21lbnQgPSBzdHJpbmcuc3Vic3RyaW5nKHBvcyArIDEpIHx8IG51bGw7XG4gICAgICBzdHJpbmcgPSBzdHJpbmcuc3Vic3RyaW5nKDAsIHBvcyk7XG4gICAgfVxuXG4gICAgLy8gZXh0cmFjdCBxdWVyeVxuICAgIHBvcyA9IHN0cmluZy5pbmRleE9mKCc/Jyk7XG4gICAgaWYgKHBvcyA+IC0xKSB7XG4gICAgICAvLyBlc2NhcGluZz9cbiAgICAgIHBhcnRzLnF1ZXJ5ID0gc3RyaW5nLnN1YnN0cmluZyhwb3MgKyAxKSB8fCBudWxsO1xuICAgICAgc3RyaW5nID0gc3RyaW5nLnN1YnN0cmluZygwLCBwb3MpO1xuICAgIH1cblxuICAgIC8vIGV4dHJhY3QgcHJvdG9jb2xcbiAgICBpZiAoc3RyaW5nLnN1YnN0cmluZygwLCAyKSA9PT0gJy8vJykge1xuICAgICAgLy8gcmVsYXRpdmUtc2NoZW1lXG4gICAgICBwYXJ0cy5wcm90b2NvbCA9IG51bGw7XG4gICAgICBzdHJpbmcgPSBzdHJpbmcuc3Vic3RyaW5nKDIpO1xuICAgICAgLy8gZXh0cmFjdCBcInVzZXI6cGFzc0Bob3N0OnBvcnRcIlxuICAgICAgc3RyaW5nID0gVVJJLnBhcnNlQXV0aG9yaXR5KHN0cmluZywgcGFydHMpO1xuICAgIH0gZWxzZSB7XG4gICAgICBwb3MgPSBzdHJpbmcuaW5kZXhPZignOicpO1xuICAgICAgaWYgKHBvcyA+IC0xKSB7XG4gICAgICAgIHBhcnRzLnByb3RvY29sID0gc3RyaW5nLnN1YnN0cmluZygwLCBwb3MpIHx8IG51bGw7XG4gICAgICAgIGlmIChwYXJ0cy5wcm90b2NvbCAmJiAhcGFydHMucHJvdG9jb2wubWF0Y2goVVJJLnByb3RvY29sX2V4cHJlc3Npb24pKSB7XG4gICAgICAgICAgLy8gOiBtYXkgYmUgd2l0aGluIHRoZSBwYXRoXG4gICAgICAgICAgcGFydHMucHJvdG9jb2wgPSB1bmRlZmluZWQ7XG4gICAgICAgIH0gZWxzZSBpZiAoc3RyaW5nLnN1YnN0cmluZyhwb3MgKyAxLCBwb3MgKyAzKSA9PT0gJy8vJykge1xuICAgICAgICAgIHN0cmluZyA9IHN0cmluZy5zdWJzdHJpbmcocG9zICsgMyk7XG5cbiAgICAgICAgICAvLyBleHRyYWN0IFwidXNlcjpwYXNzQGhvc3Q6cG9ydFwiXG4gICAgICAgICAgc3RyaW5nID0gVVJJLnBhcnNlQXV0aG9yaXR5KHN0cmluZywgcGFydHMpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHN0cmluZyA9IHN0cmluZy5zdWJzdHJpbmcocG9zICsgMSk7XG4gICAgICAgICAgcGFydHMudXJuID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHdoYXQncyBsZWZ0IG11c3QgYmUgdGhlIHBhdGhcbiAgICBwYXJ0cy5wYXRoID0gc3RyaW5nO1xuXG4gICAgLy8gYW5kIHdlJ3JlIGRvbmVcbiAgICByZXR1cm4gcGFydHM7XG4gIH07XG4gIFVSSS5wYXJzZUhvc3QgPSBmdW5jdGlvbihzdHJpbmcsIHBhcnRzKSB7XG4gICAgLy8gQ29weSBjaHJvbWUsIElFLCBvcGVyYSBiYWNrc2xhc2gtaGFuZGxpbmcgYmVoYXZpb3IuXG4gICAgLy8gQmFjayBzbGFzaGVzIGJlZm9yZSB0aGUgcXVlcnkgc3RyaW5nIGdldCBjb252ZXJ0ZWQgdG8gZm9yd2FyZCBzbGFzaGVzXG4gICAgLy8gU2VlOiBodHRwczovL2dpdGh1Yi5jb20vam95ZW50L25vZGUvYmxvYi8zODZmZDI0ZjQ5YjBlOWQxYThhMDc2NTkyYTQwNDE2OGZhZWVjYzM0L2xpYi91cmwuanMjTDExNS1MMTI0XG4gICAgLy8gU2VlOiBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL2Nocm9taXVtL2lzc3Vlcy9kZXRhaWw/aWQ9MjU5MTZcbiAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vbWVkaWFsaXplL1VSSS5qcy9wdWxsLzIzM1xuICAgIHN0cmluZyA9IHN0cmluZy5yZXBsYWNlKC9cXFxcL2csICcvJyk7XG5cbiAgICAvLyBleHRyYWN0IGhvc3Q6cG9ydFxuICAgIHZhciBwb3MgPSBzdHJpbmcuaW5kZXhPZignLycpO1xuICAgIHZhciBicmFja2V0UG9zO1xuICAgIHZhciB0O1xuXG4gICAgaWYgKHBvcyA9PT0gLTEpIHtcbiAgICAgIHBvcyA9IHN0cmluZy5sZW5ndGg7XG4gICAgfVxuXG4gICAgaWYgKHN0cmluZy5jaGFyQXQoMCkgPT09ICdbJykge1xuICAgICAgLy8gSVB2NiBob3N0IC0gaHR0cDovL3Rvb2xzLmlldGYub3JnL2h0bWwvZHJhZnQtaWV0Zi02bWFuLXRleHQtYWRkci1yZXByZXNlbnRhdGlvbi0wNCNzZWN0aW9uLTZcbiAgICAgIC8vIEkgY2xhaW0gbW9zdCBjbGllbnQgc29mdHdhcmUgYnJlYWtzIG9uIElQdjYgYW55d2F5cy4gVG8gc2ltcGxpZnkgdGhpbmdzLCBVUkkgb25seSBhY2NlcHRzXG4gICAgICAvLyBJUHY2K3BvcnQgaW4gdGhlIGZvcm1hdCBbMjAwMTpkYjg6OjFdOjgwIChmb3IgdGhlIHRpbWUgYmVpbmcpXG4gICAgICBicmFja2V0UG9zID0gc3RyaW5nLmluZGV4T2YoJ10nKTtcbiAgICAgIHBhcnRzLmhvc3RuYW1lID0gc3RyaW5nLnN1YnN0cmluZygxLCBicmFja2V0UG9zKSB8fCBudWxsO1xuICAgICAgcGFydHMucG9ydCA9IHN0cmluZy5zdWJzdHJpbmcoYnJhY2tldFBvcyArIDIsIHBvcykgfHwgbnVsbDtcbiAgICAgIGlmIChwYXJ0cy5wb3J0ID09PSAnLycpIHtcbiAgICAgICAgcGFydHMucG9ydCA9IG51bGw7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciBmaXJzdENvbG9uID0gc3RyaW5nLmluZGV4T2YoJzonKTtcbiAgICAgIHZhciBmaXJzdFNsYXNoID0gc3RyaW5nLmluZGV4T2YoJy8nKTtcbiAgICAgIHZhciBuZXh0Q29sb24gPSBzdHJpbmcuaW5kZXhPZignOicsIGZpcnN0Q29sb24gKyAxKTtcbiAgICAgIGlmIChuZXh0Q29sb24gIT09IC0xICYmIChmaXJzdFNsYXNoID09PSAtMSB8fCBuZXh0Q29sb24gPCBmaXJzdFNsYXNoKSkge1xuICAgICAgICAvLyBJUHY2IGhvc3QgY29udGFpbnMgbXVsdGlwbGUgY29sb25zIC0gYnV0IG5vIHBvcnRcbiAgICAgICAgLy8gdGhpcyBub3RhdGlvbiBpcyBhY3R1YWxseSBub3QgYWxsb3dlZCBieSBSRkMgMzk4NiwgYnV0IHdlJ3JlIGEgbGliZXJhbCBwYXJzZXJcbiAgICAgICAgcGFydHMuaG9zdG5hbWUgPSBzdHJpbmcuc3Vic3RyaW5nKDAsIHBvcykgfHwgbnVsbDtcbiAgICAgICAgcGFydHMucG9ydCA9IG51bGw7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0ID0gc3RyaW5nLnN1YnN0cmluZygwLCBwb3MpLnNwbGl0KCc6Jyk7XG4gICAgICAgIHBhcnRzLmhvc3RuYW1lID0gdFswXSB8fCBudWxsO1xuICAgICAgICBwYXJ0cy5wb3J0ID0gdFsxXSB8fCBudWxsO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChwYXJ0cy5ob3N0bmFtZSAmJiBzdHJpbmcuc3Vic3RyaW5nKHBvcykuY2hhckF0KDApICE9PSAnLycpIHtcbiAgICAgIHBvcysrO1xuICAgICAgc3RyaW5nID0gJy8nICsgc3RyaW5nO1xuICAgIH1cblxuICAgIHJldHVybiBzdHJpbmcuc3Vic3RyaW5nKHBvcykgfHwgJy8nO1xuICB9O1xuICBVUkkucGFyc2VBdXRob3JpdHkgPSBmdW5jdGlvbihzdHJpbmcsIHBhcnRzKSB7XG4gICAgc3RyaW5nID0gVVJJLnBhcnNlVXNlcmluZm8oc3RyaW5nLCBwYXJ0cyk7XG4gICAgcmV0dXJuIFVSSS5wYXJzZUhvc3Qoc3RyaW5nLCBwYXJ0cyk7XG4gIH07XG4gIFVSSS5wYXJzZVVzZXJpbmZvID0gZnVuY3Rpb24oc3RyaW5nLCBwYXJ0cykge1xuICAgIC8vIGV4dHJhY3QgdXNlcm5hbWU6cGFzc3dvcmRcbiAgICB2YXIgZmlyc3RTbGFzaCA9IHN0cmluZy5pbmRleE9mKCcvJyk7XG4gICAgdmFyIHBvcyA9IHN0cmluZy5sYXN0SW5kZXhPZignQCcsIGZpcnN0U2xhc2ggPiAtMSA/IGZpcnN0U2xhc2ggOiBzdHJpbmcubGVuZ3RoIC0gMSk7XG4gICAgdmFyIHQ7XG5cbiAgICAvLyBhdXRob3JpdHlAIG11c3QgY29tZSBiZWZvcmUgL3BhdGhcbiAgICBpZiAocG9zID4gLTEgJiYgKGZpcnN0U2xhc2ggPT09IC0xIHx8IHBvcyA8IGZpcnN0U2xhc2gpKSB7XG4gICAgICB0ID0gc3RyaW5nLnN1YnN0cmluZygwLCBwb3MpLnNwbGl0KCc6Jyk7XG4gICAgICBwYXJ0cy51c2VybmFtZSA9IHRbMF0gPyBVUkkuZGVjb2RlKHRbMF0pIDogbnVsbDtcbiAgICAgIHQuc2hpZnQoKTtcbiAgICAgIHBhcnRzLnBhc3N3b3JkID0gdFswXSA/IFVSSS5kZWNvZGUodC5qb2luKCc6JykpIDogbnVsbDtcbiAgICAgIHN0cmluZyA9IHN0cmluZy5zdWJzdHJpbmcocG9zICsgMSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhcnRzLnVzZXJuYW1lID0gbnVsbDtcbiAgICAgIHBhcnRzLnBhc3N3b3JkID0gbnVsbDtcbiAgICB9XG5cbiAgICByZXR1cm4gc3RyaW5nO1xuICB9O1xuICBVUkkucGFyc2VRdWVyeSA9IGZ1bmN0aW9uKHN0cmluZywgZXNjYXBlUXVlcnlTcGFjZSkge1xuICAgIGlmICghc3RyaW5nKSB7XG4gICAgICByZXR1cm4ge307XG4gICAgfVxuXG4gICAgLy8gdGhyb3cgb3V0IHRoZSBmdW5reSBidXNpbmVzcyAtIFwiP1wiW25hbWVcIj1cInZhbHVlXCImXCJdK1xuICAgIHN0cmluZyA9IHN0cmluZy5yZXBsYWNlKC8mKy9nLCAnJicpLnJlcGxhY2UoL15cXD8qJip8JiskL2csICcnKTtcblxuICAgIGlmICghc3RyaW5nKSB7XG4gICAgICByZXR1cm4ge307XG4gICAgfVxuXG4gICAgdmFyIGl0ZW1zID0ge307XG4gICAgdmFyIHNwbGl0cyA9IHN0cmluZy5zcGxpdCgnJicpO1xuICAgIHZhciBsZW5ndGggPSBzcGxpdHMubGVuZ3RoO1xuICAgIHZhciB2LCBuYW1lLCB2YWx1ZTtcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgIHYgPSBzcGxpdHNbaV0uc3BsaXQoJz0nKTtcbiAgICAgIG5hbWUgPSBVUkkuZGVjb2RlUXVlcnkodi5zaGlmdCgpLCBlc2NhcGVRdWVyeVNwYWNlKTtcbiAgICAgIC8vIG5vIFwiPVwiIGlzIG51bGwgYWNjb3JkaW5nIHRvIGh0dHA6Ly9kdmNzLnczLm9yZy9oZy91cmwvcmF3LWZpbGUvdGlwL092ZXJ2aWV3Lmh0bWwjY29sbGVjdC11cmwtcGFyYW1ldGVyc1xuICAgICAgdmFsdWUgPSB2Lmxlbmd0aCA/IFVSSS5kZWNvZGVRdWVyeSh2LmpvaW4oJz0nKSwgZXNjYXBlUXVlcnlTcGFjZSkgOiBudWxsO1xuXG4gICAgICBpZiAoaGFzT3duLmNhbGwoaXRlbXMsIG5hbWUpKSB7XG4gICAgICAgIGlmICh0eXBlb2YgaXRlbXNbbmFtZV0gPT09ICdzdHJpbmcnIHx8IGl0ZW1zW25hbWVdID09PSBudWxsKSB7XG4gICAgICAgICAgaXRlbXNbbmFtZV0gPSBbaXRlbXNbbmFtZV1dO1xuICAgICAgICB9XG5cbiAgICAgICAgaXRlbXNbbmFtZV0ucHVzaCh2YWx1ZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpdGVtc1tuYW1lXSA9IHZhbHVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBpdGVtcztcbiAgfTtcblxuICBVUkkuYnVpbGQgPSBmdW5jdGlvbihwYXJ0cykge1xuICAgIHZhciB0ID0gJyc7XG5cbiAgICBpZiAocGFydHMucHJvdG9jb2wpIHtcbiAgICAgIHQgKz0gcGFydHMucHJvdG9jb2wgKyAnOic7XG4gICAgfVxuXG4gICAgaWYgKCFwYXJ0cy51cm4gJiYgKHQgfHwgcGFydHMuaG9zdG5hbWUpKSB7XG4gICAgICB0ICs9ICcvLyc7XG4gICAgfVxuXG4gICAgdCArPSAoVVJJLmJ1aWxkQXV0aG9yaXR5KHBhcnRzKSB8fCAnJyk7XG5cbiAgICBpZiAodHlwZW9mIHBhcnRzLnBhdGggPT09ICdzdHJpbmcnKSB7XG4gICAgICBpZiAocGFydHMucGF0aC5jaGFyQXQoMCkgIT09ICcvJyAmJiB0eXBlb2YgcGFydHMuaG9zdG5hbWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIHQgKz0gJy8nO1xuICAgICAgfVxuXG4gICAgICB0ICs9IHBhcnRzLnBhdGg7XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBwYXJ0cy5xdWVyeSA9PT0gJ3N0cmluZycgJiYgcGFydHMucXVlcnkpIHtcbiAgICAgIHQgKz0gJz8nICsgcGFydHMucXVlcnk7XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBwYXJ0cy5mcmFnbWVudCA9PT0gJ3N0cmluZycgJiYgcGFydHMuZnJhZ21lbnQpIHtcbiAgICAgIHQgKz0gJyMnICsgcGFydHMuZnJhZ21lbnQ7XG4gICAgfVxuICAgIHJldHVybiB0O1xuICB9O1xuICBVUkkuYnVpbGRIb3N0ID0gZnVuY3Rpb24ocGFydHMpIHtcbiAgICB2YXIgdCA9ICcnO1xuXG4gICAgaWYgKCFwYXJ0cy5ob3N0bmFtZSkge1xuICAgICAgcmV0dXJuICcnO1xuICAgIH0gZWxzZSBpZiAoVVJJLmlwNl9leHByZXNzaW9uLnRlc3QocGFydHMuaG9zdG5hbWUpKSB7XG4gICAgICB0ICs9ICdbJyArIHBhcnRzLmhvc3RuYW1lICsgJ10nO1xuICAgIH0gZWxzZSB7XG4gICAgICB0ICs9IHBhcnRzLmhvc3RuYW1lO1xuICAgIH1cblxuICAgIGlmIChwYXJ0cy5wb3J0KSB7XG4gICAgICB0ICs9ICc6JyArIHBhcnRzLnBvcnQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIHQ7XG4gIH07XG4gIFVSSS5idWlsZEF1dGhvcml0eSA9IGZ1bmN0aW9uKHBhcnRzKSB7XG4gICAgcmV0dXJuIFVSSS5idWlsZFVzZXJpbmZvKHBhcnRzKSArIFVSSS5idWlsZEhvc3QocGFydHMpO1xuICB9O1xuICBVUkkuYnVpbGRVc2VyaW5mbyA9IGZ1bmN0aW9uKHBhcnRzKSB7XG4gICAgdmFyIHQgPSAnJztcblxuICAgIGlmIChwYXJ0cy51c2VybmFtZSkge1xuICAgICAgdCArPSBVUkkuZW5jb2RlKHBhcnRzLnVzZXJuYW1lKTtcblxuICAgICAgaWYgKHBhcnRzLnBhc3N3b3JkKSB7XG4gICAgICAgIHQgKz0gJzonICsgVVJJLmVuY29kZShwYXJ0cy5wYXNzd29yZCk7XG4gICAgICB9XG5cbiAgICAgIHQgKz0gJ0AnO1xuICAgIH1cblxuICAgIHJldHVybiB0O1xuICB9O1xuICBVUkkuYnVpbGRRdWVyeSA9IGZ1bmN0aW9uKGRhdGEsIGR1cGxpY2F0ZVF1ZXJ5UGFyYW1ldGVycywgZXNjYXBlUXVlcnlTcGFjZSkge1xuICAgIC8vIGFjY29yZGluZyB0byBodHRwOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9yZmMzOTg2IG9yIGh0dHA6Ly9sYWJzLmFwYWNoZS5vcmcvd2ViYXJjaC91cmkvcmZjL3JmYzM5ODYuaHRtbFxuICAgIC8vIGJlaW5nIMK7LS5ffiEkJicoKSorLDs9OkAvP8KrICVIRVggYW5kIGFsbnVtIGFyZSBhbGxvd2VkXG4gICAgLy8gdGhlIFJGQyBleHBsaWNpdGx5IHN0YXRlcyA/L2ZvbyBiZWluZyBhIHZhbGlkIHVzZSBjYXNlLCBubyBtZW50aW9uIG9mIHBhcmFtZXRlciBzeW50YXghXG4gICAgLy8gVVJJLmpzIHRyZWF0cyB0aGUgcXVlcnkgc3RyaW5nIGFzIGJlaW5nIGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZFxuICAgIC8vIHNlZSBodHRwOi8vd3d3LnczLm9yZy9UUi9SRUMtaHRtbDQwL2ludGVyYWN0L2Zvcm1zLmh0bWwjZm9ybS1jb250ZW50LXR5cGVcblxuICAgIHZhciB0ID0gJyc7XG4gICAgdmFyIHVuaXF1ZSwga2V5LCBpLCBsZW5ndGg7XG4gICAgZm9yIChrZXkgaW4gZGF0YSkge1xuICAgICAgaWYgKGhhc093bi5jYWxsKGRhdGEsIGtleSkgJiYga2V5KSB7XG4gICAgICAgIGlmIChpc0FycmF5KGRhdGFba2V5XSkpIHtcbiAgICAgICAgICB1bmlxdWUgPSB7fTtcbiAgICAgICAgICBmb3IgKGkgPSAwLCBsZW5ndGggPSBkYXRhW2tleV0ubGVuZ3RoOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmIChkYXRhW2tleV1baV0gIT09IHVuZGVmaW5lZCAmJiB1bmlxdWVbZGF0YVtrZXldW2ldICsgJyddID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgdCArPSAnJicgKyBVUkkuYnVpbGRRdWVyeVBhcmFtZXRlcihrZXksIGRhdGFba2V5XVtpXSwgZXNjYXBlUXVlcnlTcGFjZSk7XG4gICAgICAgICAgICAgIGlmIChkdXBsaWNhdGVRdWVyeVBhcmFtZXRlcnMgIT09IHRydWUpIHtcbiAgICAgICAgICAgICAgICB1bmlxdWVbZGF0YVtrZXldW2ldICsgJyddID0gdHJ1ZTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChkYXRhW2tleV0gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHQgKz0gJyYnICsgVVJJLmJ1aWxkUXVlcnlQYXJhbWV0ZXIoa2V5LCBkYXRhW2tleV0sIGVzY2FwZVF1ZXJ5U3BhY2UpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHQuc3Vic3RyaW5nKDEpO1xuICB9O1xuICBVUkkuYnVpbGRRdWVyeVBhcmFtZXRlciA9IGZ1bmN0aW9uKG5hbWUsIHZhbHVlLCBlc2NhcGVRdWVyeVNwYWNlKSB7XG4gICAgLy8gaHR0cDovL3d3dy53My5vcmcvVFIvUkVDLWh0bWw0MC9pbnRlcmFjdC9mb3Jtcy5odG1sI2Zvcm0tY29udGVudC10eXBlIC0tIGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZFxuICAgIC8vIGRvbid0IGFwcGVuZCBcIj1cIiBmb3IgbnVsbCB2YWx1ZXMsIGFjY29yZGluZyB0byBodHRwOi8vZHZjcy53My5vcmcvaGcvdXJsL3Jhdy1maWxlL3RpcC9PdmVydmlldy5odG1sI3VybC1wYXJhbWV0ZXItc2VyaWFsaXphdGlvblxuICAgIHJldHVybiBVUkkuZW5jb2RlUXVlcnkobmFtZSwgZXNjYXBlUXVlcnlTcGFjZSkgKyAodmFsdWUgIT09IG51bGwgPyAnPScgKyBVUkkuZW5jb2RlUXVlcnkodmFsdWUsIGVzY2FwZVF1ZXJ5U3BhY2UpIDogJycpO1xuICB9O1xuXG4gIFVSSS5hZGRRdWVyeSA9IGZ1bmN0aW9uKGRhdGEsIG5hbWUsIHZhbHVlKSB7XG4gICAgaWYgKHR5cGVvZiBuYW1lID09PSAnb2JqZWN0Jykge1xuICAgICAgZm9yICh2YXIga2V5IGluIG5hbWUpIHtcbiAgICAgICAgaWYgKGhhc093bi5jYWxsKG5hbWUsIGtleSkpIHtcbiAgICAgICAgICBVUkkuYWRkUXVlcnkoZGF0YSwga2V5LCBuYW1lW2tleV0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0eXBlb2YgbmFtZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIGlmIChkYXRhW25hbWVdID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgZGF0YVtuYW1lXSA9IHZhbHVlO1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGVsc2UgaWYgKHR5cGVvZiBkYXRhW25hbWVdID09PSAnc3RyaW5nJykge1xuICAgICAgICBkYXRhW25hbWVdID0gW2RhdGFbbmFtZV1dO1xuICAgICAgfVxuXG4gICAgICBpZiAoIWlzQXJyYXkodmFsdWUpKSB7XG4gICAgICAgIHZhbHVlID0gW3ZhbHVlXTtcbiAgICAgIH1cblxuICAgICAgZGF0YVtuYW1lXSA9IChkYXRhW25hbWVdIHx8IFtdKS5jb25jYXQodmFsdWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdVUkkuYWRkUXVlcnkoKSBhY2NlcHRzIGFuIG9iamVjdCwgc3RyaW5nIGFzIHRoZSBuYW1lIHBhcmFtZXRlcicpO1xuICAgIH1cbiAgfTtcbiAgVVJJLnJlbW92ZVF1ZXJ5ID0gZnVuY3Rpb24oZGF0YSwgbmFtZSwgdmFsdWUpIHtcbiAgICB2YXIgaSwgbGVuZ3RoLCBrZXk7XG5cbiAgICBpZiAoaXNBcnJheShuYW1lKSkge1xuICAgICAgZm9yIChpID0gMCwgbGVuZ3RoID0gbmFtZS5sZW5ndGg7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgICAgICBkYXRhW25hbWVbaV1dID0gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoZ2V0VHlwZShuYW1lKSA9PT0gJ1JlZ0V4cCcpIHtcbiAgICAgIGZvciAoa2V5IGluIGRhdGEpIHtcbiAgICAgICAgaWYgKG5hbWUudGVzdChrZXkpKSB7XG4gICAgICAgICAgZGF0YVtrZXldID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0eXBlb2YgbmFtZSA9PT0gJ29iamVjdCcpIHtcbiAgICAgIGZvciAoa2V5IGluIG5hbWUpIHtcbiAgICAgICAgaWYgKGhhc093bi5jYWxsKG5hbWUsIGtleSkpIHtcbiAgICAgICAgICBVUkkucmVtb3ZlUXVlcnkoZGF0YSwga2V5LCBuYW1lW2tleV0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0eXBlb2YgbmFtZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGlmIChnZXRUeXBlKHZhbHVlKSA9PT0gJ1JlZ0V4cCcpIHtcbiAgICAgICAgICBpZiAoIWlzQXJyYXkoZGF0YVtuYW1lXSkgJiYgdmFsdWUudGVzdChkYXRhW25hbWVdKSkge1xuICAgICAgICAgICAgZGF0YVtuYW1lXSA9IHVuZGVmaW5lZDtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZGF0YVtuYW1lXSA9IGZpbHRlckFycmF5VmFsdWVzKGRhdGFbbmFtZV0sIHZhbHVlKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZGF0YVtuYW1lXSA9PT0gU3RyaW5nKHZhbHVlKSAmJiAoIWlzQXJyYXkodmFsdWUpIHx8IHZhbHVlLmxlbmd0aCA9PT0gMSkpIHtcbiAgICAgICAgICBkYXRhW25hbWVdID0gdW5kZWZpbmVkO1xuICAgICAgICB9IGVsc2UgaWYgKGlzQXJyYXkoZGF0YVtuYW1lXSkpIHtcbiAgICAgICAgICBkYXRhW25hbWVdID0gZmlsdGVyQXJyYXlWYWx1ZXMoZGF0YVtuYW1lXSwgdmFsdWUpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkYXRhW25hbWVdID0gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdVUkkucmVtb3ZlUXVlcnkoKSBhY2NlcHRzIGFuIG9iamVjdCwgc3RyaW5nLCBSZWdFeHAgYXMgdGhlIGZpcnN0IHBhcmFtZXRlcicpO1xuICAgIH1cbiAgfTtcbiAgVVJJLmhhc1F1ZXJ5ID0gZnVuY3Rpb24oZGF0YSwgbmFtZSwgdmFsdWUsIHdpdGhpbkFycmF5KSB7XG4gICAgaWYgKHR5cGVvZiBuYW1lID09PSAnb2JqZWN0Jykge1xuICAgICAgZm9yICh2YXIga2V5IGluIG5hbWUpIHtcbiAgICAgICAgaWYgKGhhc093bi5jYWxsKG5hbWUsIGtleSkpIHtcbiAgICAgICAgICBpZiAoIVVSSS5oYXNRdWVyeShkYXRhLCBrZXksIG5hbWVba2V5XSkpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBlbHNlIGlmICh0eXBlb2YgbmFtZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1VSSS5oYXNRdWVyeSgpIGFjY2VwdHMgYW4gb2JqZWN0LCBzdHJpbmcgYXMgdGhlIG5hbWUgcGFyYW1ldGVyJyk7XG4gICAgfVxuXG4gICAgc3dpdGNoIChnZXRUeXBlKHZhbHVlKSkge1xuICAgICAgY2FzZSAnVW5kZWZpbmVkJzpcbiAgICAgICAgLy8gdHJ1ZSBpZiBleGlzdHMgKGJ1dCBtYXkgYmUgZW1wdHkpXG4gICAgICAgIHJldHVybiBuYW1lIGluIGRhdGE7IC8vIGRhdGFbbmFtZV0gIT09IHVuZGVmaW5lZDtcblxuICAgICAgY2FzZSAnQm9vbGVhbic6XG4gICAgICAgIC8vIHRydWUgaWYgZXhpc3RzIGFuZCBub24tZW1wdHlcbiAgICAgICAgdmFyIF9ib29seSA9IEJvb2xlYW4oaXNBcnJheShkYXRhW25hbWVdKSA/IGRhdGFbbmFtZV0ubGVuZ3RoIDogZGF0YVtuYW1lXSk7XG4gICAgICAgIHJldHVybiB2YWx1ZSA9PT0gX2Jvb2x5O1xuXG4gICAgICBjYXNlICdGdW5jdGlvbic6XG4gICAgICAgIC8vIGFsbG93IGNvbXBsZXggY29tcGFyaXNvblxuICAgICAgICByZXR1cm4gISF2YWx1ZShkYXRhW25hbWVdLCBuYW1lLCBkYXRhKTtcblxuICAgICAgY2FzZSAnQXJyYXknOlxuICAgICAgICBpZiAoIWlzQXJyYXkoZGF0YVtuYW1lXSkpIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgb3AgPSB3aXRoaW5BcnJheSA/IGFycmF5Q29udGFpbnMgOiBhcnJheXNFcXVhbDtcbiAgICAgICAgcmV0dXJuIG9wKGRhdGFbbmFtZV0sIHZhbHVlKTtcblxuICAgICAgY2FzZSAnUmVnRXhwJzpcbiAgICAgICAgaWYgKCFpc0FycmF5KGRhdGFbbmFtZV0pKSB7XG4gICAgICAgICAgcmV0dXJuIEJvb2xlYW4oZGF0YVtuYW1lXSAmJiBkYXRhW25hbWVdLm1hdGNoKHZhbHVlKSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXdpdGhpbkFycmF5KSB7XG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGFycmF5Q29udGFpbnMoZGF0YVtuYW1lXSwgdmFsdWUpO1xuXG4gICAgICBjYXNlICdOdW1iZXInOlxuICAgICAgICB2YWx1ZSA9IFN0cmluZyh2YWx1ZSk7XG4gICAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICAgIGNhc2UgJ1N0cmluZyc6XG4gICAgICAgIGlmICghaXNBcnJheShkYXRhW25hbWVdKSkge1xuICAgICAgICAgIHJldHVybiBkYXRhW25hbWVdID09PSB2YWx1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghd2l0aGluQXJyYXkpIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gYXJyYXlDb250YWlucyhkYXRhW25hbWVdLCB2YWx1ZSk7XG5cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1VSSS5oYXNRdWVyeSgpIGFjY2VwdHMgdW5kZWZpbmVkLCBib29sZWFuLCBzdHJpbmcsIG51bWJlciwgUmVnRXhwLCBGdW5jdGlvbiBhcyB0aGUgdmFsdWUgcGFyYW1ldGVyJyk7XG4gICAgfVxuICB9O1xuXG5cbiAgVVJJLmNvbW1vblBhdGggPSBmdW5jdGlvbihvbmUsIHR3bykge1xuICAgIHZhciBsZW5ndGggPSBNYXRoLm1pbihvbmUubGVuZ3RoLCB0d28ubGVuZ3RoKTtcbiAgICB2YXIgcG9zO1xuXG4gICAgLy8gZmluZCBmaXJzdCBub24tbWF0Y2hpbmcgY2hhcmFjdGVyXG4gICAgZm9yIChwb3MgPSAwOyBwb3MgPCBsZW5ndGg7IHBvcysrKSB7XG4gICAgICBpZiAob25lLmNoYXJBdChwb3MpICE9PSB0d28uY2hhckF0KHBvcykpIHtcbiAgICAgICAgcG9zLS07XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChwb3MgPCAxKSB7XG4gICAgICByZXR1cm4gb25lLmNoYXJBdCgwKSA9PT0gdHdvLmNoYXJBdCgwKSAmJiBvbmUuY2hhckF0KDApID09PSAnLycgPyAnLycgOiAnJztcbiAgICB9XG5cbiAgICAvLyByZXZlcnQgdG8gbGFzdCAvXG4gICAgaWYgKG9uZS5jaGFyQXQocG9zKSAhPT0gJy8nIHx8IHR3by5jaGFyQXQocG9zKSAhPT0gJy8nKSB7XG4gICAgICBwb3MgPSBvbmUuc3Vic3RyaW5nKDAsIHBvcykubGFzdEluZGV4T2YoJy8nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gb25lLnN1YnN0cmluZygwLCBwb3MgKyAxKTtcbiAgfTtcblxuICBVUkkud2l0aGluU3RyaW5nID0gZnVuY3Rpb24oc3RyaW5nLCBjYWxsYmFjaywgb3B0aW9ucykge1xuICAgIG9wdGlvbnMgfHwgKG9wdGlvbnMgPSB7fSk7XG4gICAgdmFyIF9zdGFydCA9IG9wdGlvbnMuc3RhcnQgfHwgVVJJLmZpbmRVcmkuc3RhcnQ7XG4gICAgdmFyIF9lbmQgPSBvcHRpb25zLmVuZCB8fCBVUkkuZmluZFVyaS5lbmQ7XG4gICAgdmFyIF90cmltID0gb3B0aW9ucy50cmltIHx8IFVSSS5maW5kVXJpLnRyaW07XG4gICAgdmFyIF9hdHRyaWJ1dGVPcGVuID0gL1thLXowLTktXT1bXCInXT8kL2k7XG5cbiAgICBfc3RhcnQubGFzdEluZGV4ID0gMDtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgdmFyIG1hdGNoID0gX3N0YXJ0LmV4ZWMoc3RyaW5nKTtcbiAgICAgIGlmICghbWF0Y2gpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG5cbiAgICAgIHZhciBzdGFydCA9IG1hdGNoLmluZGV4O1xuICAgICAgaWYgKG9wdGlvbnMuaWdub3JlSHRtbCkge1xuICAgICAgICAvLyBhdHRyaWJ1dChlPVtcIiddPyQpXG4gICAgICAgIHZhciBhdHRyaWJ1dGVPcGVuID0gc3RyaW5nLnNsaWNlKE1hdGgubWF4KHN0YXJ0IC0gMywgMCksIHN0YXJ0KTtcbiAgICAgICAgaWYgKGF0dHJpYnV0ZU9wZW4gJiYgX2F0dHJpYnV0ZU9wZW4udGVzdChhdHRyaWJ1dGVPcGVuKSkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHZhciBlbmQgPSBzdGFydCArIHN0cmluZy5zbGljZShzdGFydCkuc2VhcmNoKF9lbmQpO1xuICAgICAgdmFyIHNsaWNlID0gc3RyaW5nLnNsaWNlKHN0YXJ0LCBlbmQpLnJlcGxhY2UoX3RyaW0sICcnKTtcbiAgICAgIGlmIChvcHRpb25zLmlnbm9yZSAmJiBvcHRpb25zLmlnbm9yZS50ZXN0KHNsaWNlKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgZW5kID0gc3RhcnQgKyBzbGljZS5sZW5ndGg7XG4gICAgICB2YXIgcmVzdWx0ID0gY2FsbGJhY2soc2xpY2UsIHN0YXJ0LCBlbmQsIHN0cmluZyk7XG4gICAgICBzdHJpbmcgPSBzdHJpbmcuc2xpY2UoMCwgc3RhcnQpICsgcmVzdWx0ICsgc3RyaW5nLnNsaWNlKGVuZCk7XG4gICAgICBfc3RhcnQubGFzdEluZGV4ID0gc3RhcnQgKyByZXN1bHQubGVuZ3RoO1xuICAgIH1cblxuICAgIF9zdGFydC5sYXN0SW5kZXggPSAwO1xuICAgIHJldHVybiBzdHJpbmc7XG4gIH07XG5cbiAgVVJJLmVuc3VyZVZhbGlkSG9zdG5hbWUgPSBmdW5jdGlvbih2KSB7XG4gICAgLy8gVGhlb3JldGljYWxseSBVUklzIGFsbG93IHBlcmNlbnQtZW5jb2RpbmcgaW4gSG9zdG5hbWVzIChhY2NvcmRpbmcgdG8gUkZDIDM5ODYpXG4gICAgLy8gdGhleSBhcmUgbm90IHBhcnQgb2YgRE5TIGFuZCB0aGVyZWZvcmUgaWdub3JlZCBieSBVUkkuanNcblxuICAgIGlmICh2Lm1hdGNoKFVSSS5pbnZhbGlkX2hvc3RuYW1lX2NoYXJhY3RlcnMpKSB7XG4gICAgICAvLyB0ZXN0IHB1bnljb2RlXG4gICAgICBpZiAoIXB1bnljb2RlKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0hvc3RuYW1lIFwiJyArIHYgKyAnXCIgY29udGFpbnMgY2hhcmFjdGVycyBvdGhlciB0aGFuIFtBLVowLTkuLV0gYW5kIFB1bnljb2RlLmpzIGlzIG5vdCBhdmFpbGFibGUnKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHB1bnljb2RlLnRvQVNDSUkodikubWF0Y2goVVJJLmludmFsaWRfaG9zdG5hbWVfY2hhcmFjdGVycykpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignSG9zdG5hbWUgXCInICsgdiArICdcIiBjb250YWlucyBjaGFyYWN0ZXJzIG90aGVyIHRoYW4gW0EtWjAtOS4tXScpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICAvLyBub0NvbmZsaWN0XG4gIFVSSS5ub0NvbmZsaWN0ID0gZnVuY3Rpb24ocmVtb3ZlQWxsKSB7XG4gICAgaWYgKHJlbW92ZUFsbCkge1xuICAgICAgdmFyIHVuY29uZmxpY3RlZCA9IHtcbiAgICAgICAgVVJJOiB0aGlzLm5vQ29uZmxpY3QoKVxuICAgICAgfTtcblxuICAgICAgaWYgKHJvb3QuVVJJVGVtcGxhdGUgJiYgdHlwZW9mIHJvb3QuVVJJVGVtcGxhdGUubm9Db25mbGljdCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB1bmNvbmZsaWN0ZWQuVVJJVGVtcGxhdGUgPSByb290LlVSSVRlbXBsYXRlLm5vQ29uZmxpY3QoKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHJvb3QuSVB2NiAmJiB0eXBlb2Ygcm9vdC5JUHY2Lm5vQ29uZmxpY3QgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgdW5jb25mbGljdGVkLklQdjYgPSByb290LklQdjYubm9Db25mbGljdCgpO1xuICAgICAgfVxuXG4gICAgICBpZiAocm9vdC5TZWNvbmRMZXZlbERvbWFpbnMgJiYgdHlwZW9mIHJvb3QuU2Vjb25kTGV2ZWxEb21haW5zLm5vQ29uZmxpY3QgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgdW5jb25mbGljdGVkLlNlY29uZExldmVsRG9tYWlucyA9IHJvb3QuU2Vjb25kTGV2ZWxEb21haW5zLm5vQ29uZmxpY3QoKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHVuY29uZmxpY3RlZDtcbiAgICB9IGVsc2UgaWYgKHJvb3QuVVJJID09PSB0aGlzKSB7XG4gICAgICByb290LlVSSSA9IF9VUkk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH07XG5cbiAgcC5idWlsZCA9IGZ1bmN0aW9uKGRlZmVyQnVpbGQpIHtcbiAgICBpZiAoZGVmZXJCdWlsZCA9PT0gdHJ1ZSkge1xuICAgICAgdGhpcy5fZGVmZXJyZWRfYnVpbGQgPSB0cnVlO1xuICAgIH0gZWxzZSBpZiAoZGVmZXJCdWlsZCA9PT0gdW5kZWZpbmVkIHx8IHRoaXMuX2RlZmVycmVkX2J1aWxkKSB7XG4gICAgICB0aGlzLl9zdHJpbmcgPSBVUkkuYnVpbGQodGhpcy5fcGFydHMpO1xuICAgICAgdGhpcy5fZGVmZXJyZWRfYnVpbGQgPSBmYWxzZTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfTtcblxuICBwLmNsb25lID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIG5ldyBVUkkodGhpcyk7XG4gIH07XG5cbiAgcC52YWx1ZU9mID0gcC50b1N0cmluZyA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLmJ1aWxkKGZhbHNlKS5fc3RyaW5nO1xuICB9O1xuXG5cbiAgZnVuY3Rpb24gZ2VuZXJhdGVTaW1wbGVBY2Nlc3NvcihfcGFydCl7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKHYsIGJ1aWxkKSB7XG4gICAgICBpZiAodiA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9wYXJ0c1tfcGFydF0gfHwgJyc7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl9wYXJ0c1tfcGFydF0gPSB2IHx8IG51bGw7XG4gICAgICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGdlbmVyYXRlUHJlZml4QWNjZXNzb3IoX3BhcnQsIF9rZXkpe1xuICAgIHJldHVybiBmdW5jdGlvbih2LCBidWlsZCkge1xuICAgICAgaWYgKHYgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fcGFydHNbX3BhcnRdIHx8ICcnO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKHYgIT09IG51bGwpIHtcbiAgICAgICAgICB2ID0gdiArICcnO1xuICAgICAgICAgIGlmICh2LmNoYXJBdCgwKSA9PT0gX2tleSkge1xuICAgICAgICAgICAgdiA9IHYuc3Vic3RyaW5nKDEpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX3BhcnRzW19wYXJ0XSA9IHY7XG4gICAgICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIHAucHJvdG9jb2wgPSBnZW5lcmF0ZVNpbXBsZUFjY2Vzc29yKCdwcm90b2NvbCcpO1xuICBwLnVzZXJuYW1lID0gZ2VuZXJhdGVTaW1wbGVBY2Nlc3NvcigndXNlcm5hbWUnKTtcbiAgcC5wYXNzd29yZCA9IGdlbmVyYXRlU2ltcGxlQWNjZXNzb3IoJ3Bhc3N3b3JkJyk7XG4gIHAuaG9zdG5hbWUgPSBnZW5lcmF0ZVNpbXBsZUFjY2Vzc29yKCdob3N0bmFtZScpO1xuICBwLnBvcnQgPSBnZW5lcmF0ZVNpbXBsZUFjY2Vzc29yKCdwb3J0Jyk7XG4gIHAucXVlcnkgPSBnZW5lcmF0ZVByZWZpeEFjY2Vzc29yKCdxdWVyeScsICc/Jyk7XG4gIHAuZnJhZ21lbnQgPSBnZW5lcmF0ZVByZWZpeEFjY2Vzc29yKCdmcmFnbWVudCcsICcjJyk7XG5cbiAgcC5zZWFyY2ggPSBmdW5jdGlvbih2LCBidWlsZCkge1xuICAgIHZhciB0ID0gdGhpcy5xdWVyeSh2LCBidWlsZCk7XG4gICAgcmV0dXJuIHR5cGVvZiB0ID09PSAnc3RyaW5nJyAmJiB0Lmxlbmd0aCA/ICgnPycgKyB0KSA6IHQ7XG4gIH07XG4gIHAuaGFzaCA9IGZ1bmN0aW9uKHYsIGJ1aWxkKSB7XG4gICAgdmFyIHQgPSB0aGlzLmZyYWdtZW50KHYsIGJ1aWxkKTtcbiAgICByZXR1cm4gdHlwZW9mIHQgPT09ICdzdHJpbmcnICYmIHQubGVuZ3RoID8gKCcjJyArIHQpIDogdDtcbiAgfTtcblxuICBwLnBhdGhuYW1lID0gZnVuY3Rpb24odiwgYnVpbGQpIHtcbiAgICBpZiAodiA9PT0gdW5kZWZpbmVkIHx8IHYgPT09IHRydWUpIHtcbiAgICAgIHZhciByZXMgPSB0aGlzLl9wYXJ0cy5wYXRoIHx8ICh0aGlzLl9wYXJ0cy5ob3N0bmFtZSA/ICcvJyA6ICcnKTtcbiAgICAgIHJldHVybiB2ID8gKHRoaXMuX3BhcnRzLnVybiA/IFVSSS5kZWNvZGVVcm5QYXRoIDogVVJJLmRlY29kZVBhdGgpKHJlcykgOiByZXM7XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICh0aGlzLl9wYXJ0cy51cm4pIHtcbiAgICAgICAgdGhpcy5fcGFydHMucGF0aCA9IHYgPyBVUkkucmVjb2RlVXJuUGF0aCh2KSA6ICcnO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5fcGFydHMucGF0aCA9IHYgPyBVUkkucmVjb2RlUGF0aCh2KSA6ICcvJztcbiAgICAgIH1cbiAgICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgfTtcbiAgcC5wYXRoID0gcC5wYXRobmFtZTtcbiAgcC5ocmVmID0gZnVuY3Rpb24oaHJlZiwgYnVpbGQpIHtcbiAgICB2YXIga2V5O1xuXG4gICAgaWYgKGhyZWYgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIHRoaXMudG9TdHJpbmcoKTtcbiAgICB9XG5cbiAgICB0aGlzLl9zdHJpbmcgPSAnJztcbiAgICB0aGlzLl9wYXJ0cyA9IFVSSS5fcGFydHMoKTtcblxuICAgIHZhciBfVVJJID0gaHJlZiBpbnN0YW5jZW9mIFVSSTtcbiAgICB2YXIgX29iamVjdCA9IHR5cGVvZiBocmVmID09PSAnb2JqZWN0JyAmJiAoaHJlZi5ob3N0bmFtZSB8fCBocmVmLnBhdGggfHwgaHJlZi5wYXRobmFtZSk7XG4gICAgaWYgKGhyZWYubm9kZU5hbWUpIHtcbiAgICAgIHZhciBhdHRyaWJ1dGUgPSBVUkkuZ2V0RG9tQXR0cmlidXRlKGhyZWYpO1xuICAgICAgaHJlZiA9IGhyZWZbYXR0cmlidXRlXSB8fCAnJztcbiAgICAgIF9vYmplY3QgPSBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyB3aW5kb3cubG9jYXRpb24gaXMgcmVwb3J0ZWQgdG8gYmUgYW4gb2JqZWN0LCBidXQgaXQncyBub3QgdGhlIHNvcnRcbiAgICAvLyBvZiBvYmplY3Qgd2UncmUgbG9va2luZyBmb3I6XG4gICAgLy8gKiBsb2NhdGlvbi5wcm90b2NvbCBlbmRzIHdpdGggYSBjb2xvblxuICAgIC8vICogbG9jYXRpb24ucXVlcnkgIT0gb2JqZWN0LnNlYXJjaFxuICAgIC8vICogbG9jYXRpb24uaGFzaCAhPSBvYmplY3QuZnJhZ21lbnRcbiAgICAvLyBzaW1wbHkgc2VyaWFsaXppbmcgdGhlIHVua25vd24gb2JqZWN0IHNob3VsZCBkbyB0aGUgdHJpY2tcbiAgICAvLyAoZm9yIGxvY2F0aW9uLCBub3QgZm9yIGV2ZXJ5dGhpbmcuLi4pXG4gICAgaWYgKCFfVVJJICYmIF9vYmplY3QgJiYgaHJlZi5wYXRobmFtZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBocmVmID0gaHJlZi50b1N0cmluZygpO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgaHJlZiA9PT0gJ3N0cmluZycgfHwgaHJlZiBpbnN0YW5jZW9mIFN0cmluZykge1xuICAgICAgdGhpcy5fcGFydHMgPSBVUkkucGFyc2UoU3RyaW5nKGhyZWYpLCB0aGlzLl9wYXJ0cyk7XG4gICAgfSBlbHNlIGlmIChfVVJJIHx8IF9vYmplY3QpIHtcbiAgICAgIHZhciBzcmMgPSBfVVJJID8gaHJlZi5fcGFydHMgOiBocmVmO1xuICAgICAgZm9yIChrZXkgaW4gc3JjKSB7XG4gICAgICAgIGlmIChoYXNPd24uY2FsbCh0aGlzLl9wYXJ0cywga2V5KSkge1xuICAgICAgICAgIHRoaXMuX3BhcnRzW2tleV0gPSBzcmNba2V5XTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdpbnZhbGlkIGlucHV0Jyk7XG4gICAgfVxuXG4gICAgdGhpcy5idWlsZCghYnVpbGQpO1xuICAgIHJldHVybiB0aGlzO1xuICB9O1xuXG4gIC8vIGlkZW50aWZpY2F0aW9uIGFjY2Vzc29yc1xuICBwLmlzID0gZnVuY3Rpb24od2hhdCkge1xuICAgIHZhciBpcCA9IGZhbHNlO1xuICAgIHZhciBpcDQgPSBmYWxzZTtcbiAgICB2YXIgaXA2ID0gZmFsc2U7XG4gICAgdmFyIG5hbWUgPSBmYWxzZTtcbiAgICB2YXIgc2xkID0gZmFsc2U7XG4gICAgdmFyIGlkbiA9IGZhbHNlO1xuICAgIHZhciBwdW55Y29kZSA9IGZhbHNlO1xuICAgIHZhciByZWxhdGl2ZSA9ICF0aGlzLl9wYXJ0cy51cm47XG5cbiAgICBpZiAodGhpcy5fcGFydHMuaG9zdG5hbWUpIHtcbiAgICAgIHJlbGF0aXZlID0gZmFsc2U7XG4gICAgICBpcDQgPSBVUkkuaXA0X2V4cHJlc3Npb24udGVzdCh0aGlzLl9wYXJ0cy5ob3N0bmFtZSk7XG4gICAgICBpcDYgPSBVUkkuaXA2X2V4cHJlc3Npb24udGVzdCh0aGlzLl9wYXJ0cy5ob3N0bmFtZSk7XG4gICAgICBpcCA9IGlwNCB8fCBpcDY7XG4gICAgICBuYW1lID0gIWlwO1xuICAgICAgc2xkID0gbmFtZSAmJiBTTEQgJiYgU0xELmhhcyh0aGlzLl9wYXJ0cy5ob3N0bmFtZSk7XG4gICAgICBpZG4gPSBuYW1lICYmIFVSSS5pZG5fZXhwcmVzc2lvbi50ZXN0KHRoaXMuX3BhcnRzLmhvc3RuYW1lKTtcbiAgICAgIHB1bnljb2RlID0gbmFtZSAmJiBVUkkucHVueWNvZGVfZXhwcmVzc2lvbi50ZXN0KHRoaXMuX3BhcnRzLmhvc3RuYW1lKTtcbiAgICB9XG5cbiAgICBzd2l0Y2ggKHdoYXQudG9Mb3dlckNhc2UoKSkge1xuICAgICAgY2FzZSAncmVsYXRpdmUnOlxuICAgICAgICByZXR1cm4gcmVsYXRpdmU7XG5cbiAgICAgIGNhc2UgJ2Fic29sdXRlJzpcbiAgICAgICAgcmV0dXJuICFyZWxhdGl2ZTtcblxuICAgICAgLy8gaG9zdG5hbWUgaWRlbnRpZmljYXRpb25cbiAgICAgIGNhc2UgJ2RvbWFpbic6XG4gICAgICBjYXNlICduYW1lJzpcbiAgICAgICAgcmV0dXJuIG5hbWU7XG5cbiAgICAgIGNhc2UgJ3NsZCc6XG4gICAgICAgIHJldHVybiBzbGQ7XG5cbiAgICAgIGNhc2UgJ2lwJzpcbiAgICAgICAgcmV0dXJuIGlwO1xuXG4gICAgICBjYXNlICdpcDQnOlxuICAgICAgY2FzZSAnaXB2NCc6XG4gICAgICBjYXNlICdpbmV0NCc6XG4gICAgICAgIHJldHVybiBpcDQ7XG5cbiAgICAgIGNhc2UgJ2lwNic6XG4gICAgICBjYXNlICdpcHY2JzpcbiAgICAgIGNhc2UgJ2luZXQ2JzpcbiAgICAgICAgcmV0dXJuIGlwNjtcblxuICAgICAgY2FzZSAnaWRuJzpcbiAgICAgICAgcmV0dXJuIGlkbjtcblxuICAgICAgY2FzZSAndXJsJzpcbiAgICAgICAgcmV0dXJuICF0aGlzLl9wYXJ0cy51cm47XG5cbiAgICAgIGNhc2UgJ3Vybic6XG4gICAgICAgIHJldHVybiAhIXRoaXMuX3BhcnRzLnVybjtcblxuICAgICAgY2FzZSAncHVueWNvZGUnOlxuICAgICAgICByZXR1cm4gcHVueWNvZGU7XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH07XG5cbiAgLy8gY29tcG9uZW50IHNwZWNpZmljIGlucHV0IHZhbGlkYXRpb25cbiAgdmFyIF9wcm90b2NvbCA9IHAucHJvdG9jb2w7XG4gIHZhciBfcG9ydCA9IHAucG9ydDtcbiAgdmFyIF9ob3N0bmFtZSA9IHAuaG9zdG5hbWU7XG5cbiAgcC5wcm90b2NvbCA9IGZ1bmN0aW9uKHYsIGJ1aWxkKSB7XG4gICAgaWYgKHYgIT09IHVuZGVmaW5lZCkge1xuICAgICAgaWYgKHYpIHtcbiAgICAgICAgLy8gYWNjZXB0IHRyYWlsaW5nIDovL1xuICAgICAgICB2ID0gdi5yZXBsYWNlKC86KFxcL1xcLyk/JC8sICcnKTtcblxuICAgICAgICBpZiAoIXYubWF0Y2goVVJJLnByb3RvY29sX2V4cHJlc3Npb24pKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignUHJvdG9jb2wgXCInICsgdiArICdcIiBjb250YWlucyBjaGFyYWN0ZXJzIG90aGVyIHRoYW4gW0EtWjAtOS4rLV0gb3IgZG9lc25cXCd0IHN0YXJ0IHdpdGggW0EtWl0nKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gX3Byb3RvY29sLmNhbGwodGhpcywgdiwgYnVpbGQpO1xuICB9O1xuICBwLnNjaGVtZSA9IHAucHJvdG9jb2w7XG4gIHAucG9ydCA9IGZ1bmN0aW9uKHYsIGJ1aWxkKSB7XG4gICAgaWYgKHRoaXMuX3BhcnRzLnVybikge1xuICAgICAgcmV0dXJuIHYgPT09IHVuZGVmaW5lZCA/ICcnIDogdGhpcztcbiAgICB9XG5cbiAgICBpZiAodiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBpZiAodiA9PT0gMCkge1xuICAgICAgICB2ID0gbnVsbDtcbiAgICAgIH1cblxuICAgICAgaWYgKHYpIHtcbiAgICAgICAgdiArPSAnJztcbiAgICAgICAgaWYgKHYuY2hhckF0KDApID09PSAnOicpIHtcbiAgICAgICAgICB2ID0gdi5zdWJzdHJpbmcoMSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodi5tYXRjaCgvW14wLTldLykpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdQb3J0IFwiJyArIHYgKyAnXCIgY29udGFpbnMgY2hhcmFjdGVycyBvdGhlciB0aGFuIFswLTldJyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIF9wb3J0LmNhbGwodGhpcywgdiwgYnVpbGQpO1xuICB9O1xuICBwLmhvc3RuYW1lID0gZnVuY3Rpb24odiwgYnVpbGQpIHtcbiAgICBpZiAodGhpcy5fcGFydHMudXJuKSB7XG4gICAgICByZXR1cm4gdiA9PT0gdW5kZWZpbmVkID8gJycgOiB0aGlzO1xuICAgIH1cblxuICAgIGlmICh2ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHZhciB4ID0ge307XG4gICAgICB2YXIgcmVzID0gVVJJLnBhcnNlSG9zdCh2LCB4KTtcbiAgICAgIGlmIChyZXMgIT09ICcvJykge1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdIb3N0bmFtZSBcIicgKyB2ICsgJ1wiIGNvbnRhaW5zIGNoYXJhY3RlcnMgb3RoZXIgdGhhbiBbQS1aMC05Li1dJyk7XG4gICAgICB9XG5cbiAgICAgIHYgPSB4Lmhvc3RuYW1lO1xuICAgIH1cbiAgICByZXR1cm4gX2hvc3RuYW1lLmNhbGwodGhpcywgdiwgYnVpbGQpO1xuICB9O1xuXG4gIC8vIGNvbXBvdW5kIGFjY2Vzc29yc1xuICBwLm9yaWdpbiA9IGZ1bmN0aW9uKHYsIGJ1aWxkKSB7XG4gICAgdmFyIHBhcnRzO1xuXG4gICAgaWYgKHRoaXMuX3BhcnRzLnVybikge1xuICAgICAgcmV0dXJuIHYgPT09IHVuZGVmaW5lZCA/ICcnIDogdGhpcztcbiAgICB9XG5cbiAgICBpZiAodiA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB2YXIgcHJvdG9jb2wgPSB0aGlzLnByb3RvY29sKCk7XG4gICAgICB2YXIgYXV0aG9yaXR5ID0gdGhpcy5hdXRob3JpdHkoKTtcbiAgICAgIGlmICghYXV0aG9yaXR5KSByZXR1cm4gJyc7XG4gICAgICByZXR1cm4gKHByb3RvY29sID8gcHJvdG9jb2wgKyAnOi8vJyA6ICcnKSArIHRoaXMuYXV0aG9yaXR5KCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciBvcmlnaW4gPSBVUkkodik7XG4gICAgICB0aGlzXG4gICAgICAgIC5wcm90b2NvbChvcmlnaW4ucHJvdG9jb2woKSlcbiAgICAgICAgLmF1dGhvcml0eShvcmlnaW4uYXV0aG9yaXR5KCkpXG4gICAgICAgIC5idWlsZCghYnVpbGQpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICB9O1xuICBwLmhvc3QgPSBmdW5jdGlvbih2LCBidWlsZCkge1xuICAgIGlmICh0aGlzLl9wYXJ0cy51cm4pIHtcbiAgICAgIHJldHVybiB2ID09PSB1bmRlZmluZWQgPyAnJyA6IHRoaXM7XG4gICAgfVxuXG4gICAgaWYgKHYgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIHRoaXMuX3BhcnRzLmhvc3RuYW1lID8gVVJJLmJ1aWxkSG9zdCh0aGlzLl9wYXJ0cykgOiAnJztcbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIHJlcyA9IFVSSS5wYXJzZUhvc3QodiwgdGhpcy5fcGFydHMpO1xuICAgICAgaWYgKHJlcyAhPT0gJy8nKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0hvc3RuYW1lIFwiJyArIHYgKyAnXCIgY29udGFpbnMgY2hhcmFjdGVycyBvdGhlciB0aGFuIFtBLVowLTkuLV0nKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5idWlsZCghYnVpbGQpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICB9O1xuICBwLmF1dGhvcml0eSA9IGZ1bmN0aW9uKHYsIGJ1aWxkKSB7XG4gICAgaWYgKHRoaXMuX3BhcnRzLnVybikge1xuICAgICAgcmV0dXJuIHYgPT09IHVuZGVmaW5lZCA/ICcnIDogdGhpcztcbiAgICB9XG5cbiAgICBpZiAodiA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gdGhpcy5fcGFydHMuaG9zdG5hbWUgPyBVUkkuYnVpbGRBdXRob3JpdHkodGhpcy5fcGFydHMpIDogJyc7XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciByZXMgPSBVUkkucGFyc2VBdXRob3JpdHkodiwgdGhpcy5fcGFydHMpO1xuICAgICAgaWYgKHJlcyAhPT0gJy8nKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0hvc3RuYW1lIFwiJyArIHYgKyAnXCIgY29udGFpbnMgY2hhcmFjdGVycyBvdGhlciB0aGFuIFtBLVowLTkuLV0nKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5idWlsZCghYnVpbGQpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICB9O1xuICBwLnVzZXJpbmZvID0gZnVuY3Rpb24odiwgYnVpbGQpIHtcbiAgICBpZiAodGhpcy5fcGFydHMudXJuKSB7XG4gICAgICByZXR1cm4gdiA9PT0gdW5kZWZpbmVkID8gJycgOiB0aGlzO1xuICAgIH1cblxuICAgIGlmICh2ID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGlmICghdGhpcy5fcGFydHMudXNlcm5hbWUpIHtcbiAgICAgICAgcmV0dXJuICcnO1xuICAgICAgfVxuXG4gICAgICB2YXIgdCA9IFVSSS5idWlsZFVzZXJpbmZvKHRoaXMuX3BhcnRzKTtcbiAgICAgIHJldHVybiB0LnN1YnN0cmluZygwLCB0Lmxlbmd0aCAtMSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICh2W3YubGVuZ3RoLTFdICE9PSAnQCcpIHtcbiAgICAgICAgdiArPSAnQCc7XG4gICAgICB9XG5cbiAgICAgIFVSSS5wYXJzZVVzZXJpbmZvKHYsIHRoaXMuX3BhcnRzKTtcbiAgICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgfTtcbiAgcC5yZXNvdXJjZSA9IGZ1bmN0aW9uKHYsIGJ1aWxkKSB7XG4gICAgdmFyIHBhcnRzO1xuXG4gICAgaWYgKHYgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIHRoaXMucGF0aCgpICsgdGhpcy5zZWFyY2goKSArIHRoaXMuaGFzaCgpO1xuICAgIH1cblxuICAgIHBhcnRzID0gVVJJLnBhcnNlKHYpO1xuICAgIHRoaXMuX3BhcnRzLnBhdGggPSBwYXJ0cy5wYXRoO1xuICAgIHRoaXMuX3BhcnRzLnF1ZXJ5ID0gcGFydHMucXVlcnk7XG4gICAgdGhpcy5fcGFydHMuZnJhZ21lbnQgPSBwYXJ0cy5mcmFnbWVudDtcbiAgICB0aGlzLmJ1aWxkKCFidWlsZCk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH07XG5cbiAgLy8gZnJhY3Rpb24gYWNjZXNzb3JzXG4gIHAuc3ViZG9tYWluID0gZnVuY3Rpb24odiwgYnVpbGQpIHtcbiAgICBpZiAodGhpcy5fcGFydHMudXJuKSB7XG4gICAgICByZXR1cm4gdiA9PT0gdW5kZWZpbmVkID8gJycgOiB0aGlzO1xuICAgIH1cblxuICAgIC8vIGNvbnZlbmllbmNlLCByZXR1cm4gXCJ3d3dcIiBmcm9tIFwid3d3LmV4YW1wbGUub3JnXCJcbiAgICBpZiAodiA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBpZiAoIXRoaXMuX3BhcnRzLmhvc3RuYW1lIHx8IHRoaXMuaXMoJ0lQJykpIHtcbiAgICAgICAgcmV0dXJuICcnO1xuICAgICAgfVxuXG4gICAgICAvLyBncmFiIGRvbWFpbiBhbmQgYWRkIGFub3RoZXIgc2VnbWVudFxuICAgICAgdmFyIGVuZCA9IHRoaXMuX3BhcnRzLmhvc3RuYW1lLmxlbmd0aCAtIHRoaXMuZG9tYWluKCkubGVuZ3RoIC0gMTtcbiAgICAgIHJldHVybiB0aGlzLl9wYXJ0cy5ob3N0bmFtZS5zdWJzdHJpbmcoMCwgZW5kKSB8fCAnJztcbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIGUgPSB0aGlzLl9wYXJ0cy5ob3N0bmFtZS5sZW5ndGggLSB0aGlzLmRvbWFpbigpLmxlbmd0aDtcbiAgICAgIHZhciBzdWIgPSB0aGlzLl9wYXJ0cy5ob3N0bmFtZS5zdWJzdHJpbmcoMCwgZSk7XG4gICAgICB2YXIgcmVwbGFjZSA9IG5ldyBSZWdFeHAoJ14nICsgZXNjYXBlUmVnRXgoc3ViKSk7XG5cbiAgICAgIGlmICh2ICYmIHYuY2hhckF0KHYubGVuZ3RoIC0gMSkgIT09ICcuJykge1xuICAgICAgICB2ICs9ICcuJztcbiAgICAgIH1cblxuICAgICAgaWYgKHYpIHtcbiAgICAgICAgVVJJLmVuc3VyZVZhbGlkSG9zdG5hbWUodik7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuX3BhcnRzLmhvc3RuYW1lID0gdGhpcy5fcGFydHMuaG9zdG5hbWUucmVwbGFjZShyZXBsYWNlLCB2KTtcbiAgICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgfTtcbiAgcC5kb21haW4gPSBmdW5jdGlvbih2LCBidWlsZCkge1xuICAgIGlmICh0aGlzLl9wYXJ0cy51cm4pIHtcbiAgICAgIHJldHVybiB2ID09PSB1bmRlZmluZWQgPyAnJyA6IHRoaXM7XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiB2ID09PSAnYm9vbGVhbicpIHtcbiAgICAgIGJ1aWxkID0gdjtcbiAgICAgIHYgPSB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgLy8gY29udmVuaWVuY2UsIHJldHVybiBcImV4YW1wbGUub3JnXCIgZnJvbSBcInd3dy5leGFtcGxlLm9yZ1wiXG4gICAgaWYgKHYgPT09IHVuZGVmaW5lZCkge1xuICAgICAgaWYgKCF0aGlzLl9wYXJ0cy5ob3N0bmFtZSB8fCB0aGlzLmlzKCdJUCcpKSB7XG4gICAgICAgIHJldHVybiAnJztcbiAgICAgIH1cblxuICAgICAgLy8gaWYgaG9zdG5hbWUgY29uc2lzdHMgb2YgMSBvciAyIHNlZ21lbnRzLCBpdCBtdXN0IGJlIHRoZSBkb21haW5cbiAgICAgIHZhciB0ID0gdGhpcy5fcGFydHMuaG9zdG5hbWUubWF0Y2goL1xcLi9nKTtcbiAgICAgIGlmICh0ICYmIHQubGVuZ3RoIDwgMikge1xuICAgICAgICByZXR1cm4gdGhpcy5fcGFydHMuaG9zdG5hbWU7XG4gICAgICB9XG5cbiAgICAgIC8vIGdyYWIgdGxkIGFuZCBhZGQgYW5vdGhlciBzZWdtZW50XG4gICAgICB2YXIgZW5kID0gdGhpcy5fcGFydHMuaG9zdG5hbWUubGVuZ3RoIC0gdGhpcy50bGQoYnVpbGQpLmxlbmd0aCAtIDE7XG4gICAgICBlbmQgPSB0aGlzLl9wYXJ0cy5ob3N0bmFtZS5sYXN0SW5kZXhPZignLicsIGVuZCAtMSkgKyAxO1xuICAgICAgcmV0dXJuIHRoaXMuX3BhcnRzLmhvc3RuYW1lLnN1YnN0cmluZyhlbmQpIHx8ICcnO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoIXYpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignY2Fubm90IHNldCBkb21haW4gZW1wdHknKTtcbiAgICAgIH1cblxuICAgICAgVVJJLmVuc3VyZVZhbGlkSG9zdG5hbWUodik7XG5cbiAgICAgIGlmICghdGhpcy5fcGFydHMuaG9zdG5hbWUgfHwgdGhpcy5pcygnSVAnKSkge1xuICAgICAgICB0aGlzLl9wYXJ0cy5ob3N0bmFtZSA9IHY7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgcmVwbGFjZSA9IG5ldyBSZWdFeHAoZXNjYXBlUmVnRXgodGhpcy5kb21haW4oKSkgKyAnJCcpO1xuICAgICAgICB0aGlzLl9wYXJ0cy5ob3N0bmFtZSA9IHRoaXMuX3BhcnRzLmhvc3RuYW1lLnJlcGxhY2UocmVwbGFjZSwgdik7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgfTtcbiAgcC50bGQgPSBmdW5jdGlvbih2LCBidWlsZCkge1xuICAgIGlmICh0aGlzLl9wYXJ0cy51cm4pIHtcbiAgICAgIHJldHVybiB2ID09PSB1bmRlZmluZWQgPyAnJyA6IHRoaXM7XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiB2ID09PSAnYm9vbGVhbicpIHtcbiAgICAgIGJ1aWxkID0gdjtcbiAgICAgIHYgPSB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgLy8gcmV0dXJuIFwib3JnXCIgZnJvbSBcInd3dy5leGFtcGxlLm9yZ1wiXG4gICAgaWYgKHYgPT09IHVuZGVmaW5lZCkge1xuICAgICAgaWYgKCF0aGlzLl9wYXJ0cy5ob3N0bmFtZSB8fCB0aGlzLmlzKCdJUCcpKSB7XG4gICAgICAgIHJldHVybiAnJztcbiAgICAgIH1cblxuICAgICAgdmFyIHBvcyA9IHRoaXMuX3BhcnRzLmhvc3RuYW1lLmxhc3RJbmRleE9mKCcuJyk7XG4gICAgICB2YXIgdGxkID0gdGhpcy5fcGFydHMuaG9zdG5hbWUuc3Vic3RyaW5nKHBvcyArIDEpO1xuXG4gICAgICBpZiAoYnVpbGQgIT09IHRydWUgJiYgU0xEICYmIFNMRC5saXN0W3RsZC50b0xvd2VyQ2FzZSgpXSkge1xuICAgICAgICByZXR1cm4gU0xELmdldCh0aGlzLl9wYXJ0cy5ob3N0bmFtZSkgfHwgdGxkO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gdGxkO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgcmVwbGFjZTtcblxuICAgICAgaWYgKCF2KSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ2Nhbm5vdCBzZXQgVExEIGVtcHR5Jyk7XG4gICAgICB9IGVsc2UgaWYgKHYubWF0Y2goL1teYS16QS1aMC05LV0vKSkge1xuICAgICAgICBpZiAoU0xEICYmIFNMRC5pcyh2KSkge1xuICAgICAgICAgIHJlcGxhY2UgPSBuZXcgUmVnRXhwKGVzY2FwZVJlZ0V4KHRoaXMudGxkKCkpICsgJyQnKTtcbiAgICAgICAgICB0aGlzLl9wYXJ0cy5ob3N0bmFtZSA9IHRoaXMuX3BhcnRzLmhvc3RuYW1lLnJlcGxhY2UocmVwbGFjZSwgdik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignVExEIFwiJyArIHYgKyAnXCIgY29udGFpbnMgY2hhcmFjdGVycyBvdGhlciB0aGFuIFtBLVowLTldJyk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoIXRoaXMuX3BhcnRzLmhvc3RuYW1lIHx8IHRoaXMuaXMoJ0lQJykpIHtcbiAgICAgICAgdGhyb3cgbmV3IFJlZmVyZW5jZUVycm9yKCdjYW5ub3Qgc2V0IFRMRCBvbiBub24tZG9tYWluIGhvc3QnKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlcGxhY2UgPSBuZXcgUmVnRXhwKGVzY2FwZVJlZ0V4KHRoaXMudGxkKCkpICsgJyQnKTtcbiAgICAgICAgdGhpcy5fcGFydHMuaG9zdG5hbWUgPSB0aGlzLl9wYXJ0cy5ob3N0bmFtZS5yZXBsYWNlKHJlcGxhY2UsIHYpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmJ1aWxkKCFidWlsZCk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gIH07XG4gIHAuZGlyZWN0b3J5ID0gZnVuY3Rpb24odiwgYnVpbGQpIHtcbiAgICBpZiAodGhpcy5fcGFydHMudXJuKSB7XG4gICAgICByZXR1cm4gdiA9PT0gdW5kZWZpbmVkID8gJycgOiB0aGlzO1xuICAgIH1cblxuICAgIGlmICh2ID09PSB1bmRlZmluZWQgfHwgdiA9PT0gdHJ1ZSkge1xuICAgICAgaWYgKCF0aGlzLl9wYXJ0cy5wYXRoICYmICF0aGlzLl9wYXJ0cy5ob3N0bmFtZSkge1xuICAgICAgICByZXR1cm4gJyc7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLl9wYXJ0cy5wYXRoID09PSAnLycpIHtcbiAgICAgICAgcmV0dXJuICcvJztcbiAgICAgIH1cblxuICAgICAgdmFyIGVuZCA9IHRoaXMuX3BhcnRzLnBhdGgubGVuZ3RoIC0gdGhpcy5maWxlbmFtZSgpLmxlbmd0aCAtIDE7XG4gICAgICB2YXIgcmVzID0gdGhpcy5fcGFydHMucGF0aC5zdWJzdHJpbmcoMCwgZW5kKSB8fCAodGhpcy5fcGFydHMuaG9zdG5hbWUgPyAnLycgOiAnJyk7XG5cbiAgICAgIHJldHVybiB2ID8gVVJJLmRlY29kZVBhdGgocmVzKSA6IHJlcztcblxuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgZSA9IHRoaXMuX3BhcnRzLnBhdGgubGVuZ3RoIC0gdGhpcy5maWxlbmFtZSgpLmxlbmd0aDtcbiAgICAgIHZhciBkaXJlY3RvcnkgPSB0aGlzLl9wYXJ0cy5wYXRoLnN1YnN0cmluZygwLCBlKTtcbiAgICAgIHZhciByZXBsYWNlID0gbmV3IFJlZ0V4cCgnXicgKyBlc2NhcGVSZWdFeChkaXJlY3RvcnkpKTtcblxuICAgICAgLy8gZnVsbHkgcXVhbGlmaWVyIGRpcmVjdG9yaWVzIGJlZ2luIHdpdGggYSBzbGFzaFxuICAgICAgaWYgKCF0aGlzLmlzKCdyZWxhdGl2ZScpKSB7XG4gICAgICAgIGlmICghdikge1xuICAgICAgICAgIHYgPSAnLyc7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodi5jaGFyQXQoMCkgIT09ICcvJykge1xuICAgICAgICAgIHYgPSAnLycgKyB2O1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIGRpcmVjdG9yaWVzIGFsd2F5cyBlbmQgd2l0aCBhIHNsYXNoXG4gICAgICBpZiAodiAmJiB2LmNoYXJBdCh2Lmxlbmd0aCAtIDEpICE9PSAnLycpIHtcbiAgICAgICAgdiArPSAnLyc7XG4gICAgICB9XG5cbiAgICAgIHYgPSBVUkkucmVjb2RlUGF0aCh2KTtcbiAgICAgIHRoaXMuX3BhcnRzLnBhdGggPSB0aGlzLl9wYXJ0cy5wYXRoLnJlcGxhY2UocmVwbGFjZSwgdik7XG4gICAgICB0aGlzLmJ1aWxkKCFidWlsZCk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gIH07XG4gIHAuZmlsZW5hbWUgPSBmdW5jdGlvbih2LCBidWlsZCkge1xuICAgIGlmICh0aGlzLl9wYXJ0cy51cm4pIHtcbiAgICAgIHJldHVybiB2ID09PSB1bmRlZmluZWQgPyAnJyA6IHRoaXM7XG4gICAgfVxuXG4gICAgaWYgKHYgPT09IHVuZGVmaW5lZCB8fCB2ID09PSB0cnVlKSB7XG4gICAgICBpZiAoIXRoaXMuX3BhcnRzLnBhdGggfHwgdGhpcy5fcGFydHMucGF0aCA9PT0gJy8nKSB7XG4gICAgICAgIHJldHVybiAnJztcbiAgICAgIH1cblxuICAgICAgdmFyIHBvcyA9IHRoaXMuX3BhcnRzLnBhdGgubGFzdEluZGV4T2YoJy8nKTtcbiAgICAgIHZhciByZXMgPSB0aGlzLl9wYXJ0cy5wYXRoLnN1YnN0cmluZyhwb3MrMSk7XG5cbiAgICAgIHJldHVybiB2ID8gVVJJLmRlY29kZVBhdGhTZWdtZW50KHJlcykgOiByZXM7XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciBtdXRhdGVkRGlyZWN0b3J5ID0gZmFsc2U7XG5cbiAgICAgIGlmICh2LmNoYXJBdCgwKSA9PT0gJy8nKSB7XG4gICAgICAgIHYgPSB2LnN1YnN0cmluZygxKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHYubWF0Y2goL1xcLj9cXC8vKSkge1xuICAgICAgICBtdXRhdGVkRGlyZWN0b3J5ID0gdHJ1ZTtcbiAgICAgIH1cblxuICAgICAgdmFyIHJlcGxhY2UgPSBuZXcgUmVnRXhwKGVzY2FwZVJlZ0V4KHRoaXMuZmlsZW5hbWUoKSkgKyAnJCcpO1xuICAgICAgdiA9IFVSSS5yZWNvZGVQYXRoKHYpO1xuICAgICAgdGhpcy5fcGFydHMucGF0aCA9IHRoaXMuX3BhcnRzLnBhdGgucmVwbGFjZShyZXBsYWNlLCB2KTtcblxuICAgICAgaWYgKG11dGF0ZWREaXJlY3RvcnkpIHtcbiAgICAgICAgdGhpcy5ub3JtYWxpemVQYXRoKGJ1aWxkKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICB9O1xuICBwLnN1ZmZpeCA9IGZ1bmN0aW9uKHYsIGJ1aWxkKSB7XG4gICAgaWYgKHRoaXMuX3BhcnRzLnVybikge1xuICAgICAgcmV0dXJuIHYgPT09IHVuZGVmaW5lZCA/ICcnIDogdGhpcztcbiAgICB9XG5cbiAgICBpZiAodiA9PT0gdW5kZWZpbmVkIHx8IHYgPT09IHRydWUpIHtcbiAgICAgIGlmICghdGhpcy5fcGFydHMucGF0aCB8fCB0aGlzLl9wYXJ0cy5wYXRoID09PSAnLycpIHtcbiAgICAgICAgcmV0dXJuICcnO1xuICAgICAgfVxuXG4gICAgICB2YXIgZmlsZW5hbWUgPSB0aGlzLmZpbGVuYW1lKCk7XG4gICAgICB2YXIgcG9zID0gZmlsZW5hbWUubGFzdEluZGV4T2YoJy4nKTtcbiAgICAgIHZhciBzLCByZXM7XG5cbiAgICAgIGlmIChwb3MgPT09IC0xKSB7XG4gICAgICAgIHJldHVybiAnJztcbiAgICAgIH1cblxuICAgICAgLy8gc3VmZml4IG1heSBvbmx5IGNvbnRhaW4gYWxudW0gY2hhcmFjdGVycyAoeXVwLCBJIG1hZGUgdGhpcyB1cC4pXG4gICAgICBzID0gZmlsZW5hbWUuc3Vic3RyaW5nKHBvcysxKTtcbiAgICAgIHJlcyA9ICgvXlthLXowLTklXSskL2kpLnRlc3QocykgPyBzIDogJyc7XG4gICAgICByZXR1cm4gdiA/IFVSSS5kZWNvZGVQYXRoU2VnbWVudChyZXMpIDogcmVzO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAodi5jaGFyQXQoMCkgPT09ICcuJykge1xuICAgICAgICB2ID0gdi5zdWJzdHJpbmcoMSk7XG4gICAgICB9XG5cbiAgICAgIHZhciBzdWZmaXggPSB0aGlzLnN1ZmZpeCgpO1xuICAgICAgdmFyIHJlcGxhY2U7XG5cbiAgICAgIGlmICghc3VmZml4KSB7XG4gICAgICAgIGlmICghdikge1xuICAgICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fcGFydHMucGF0aCArPSAnLicgKyBVUkkucmVjb2RlUGF0aCh2KTtcbiAgICAgIH0gZWxzZSBpZiAoIXYpIHtcbiAgICAgICAgcmVwbGFjZSA9IG5ldyBSZWdFeHAoZXNjYXBlUmVnRXgoJy4nICsgc3VmZml4KSArICckJyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXBsYWNlID0gbmV3IFJlZ0V4cChlc2NhcGVSZWdFeChzdWZmaXgpICsgJyQnKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHJlcGxhY2UpIHtcbiAgICAgICAgdiA9IFVSSS5yZWNvZGVQYXRoKHYpO1xuICAgICAgICB0aGlzLl9wYXJ0cy5wYXRoID0gdGhpcy5fcGFydHMucGF0aC5yZXBsYWNlKHJlcGxhY2UsIHYpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmJ1aWxkKCFidWlsZCk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gIH07XG4gIHAuc2VnbWVudCA9IGZ1bmN0aW9uKHNlZ21lbnQsIHYsIGJ1aWxkKSB7XG4gICAgdmFyIHNlcGFyYXRvciA9IHRoaXMuX3BhcnRzLnVybiA/ICc6JyA6ICcvJztcbiAgICB2YXIgcGF0aCA9IHRoaXMucGF0aCgpO1xuICAgIHZhciBhYnNvbHV0ZSA9IHBhdGguc3Vic3RyaW5nKDAsIDEpID09PSAnLyc7XG4gICAgdmFyIHNlZ21lbnRzID0gcGF0aC5zcGxpdChzZXBhcmF0b3IpO1xuXG4gICAgaWYgKHNlZ21lbnQgIT09IHVuZGVmaW5lZCAmJiB0eXBlb2Ygc2VnbWVudCAhPT0gJ251bWJlcicpIHtcbiAgICAgIGJ1aWxkID0gdjtcbiAgICAgIHYgPSBzZWdtZW50O1xuICAgICAgc2VnbWVudCA9IHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBpZiAoc2VnbWVudCAhPT0gdW5kZWZpbmVkICYmIHR5cGVvZiBzZWdtZW50ICE9PSAnbnVtYmVyJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdCYWQgc2VnbWVudCBcIicgKyBzZWdtZW50ICsgJ1wiLCBtdXN0IGJlIDAtYmFzZWQgaW50ZWdlcicpO1xuICAgIH1cblxuICAgIGlmIChhYnNvbHV0ZSkge1xuICAgICAgc2VnbWVudHMuc2hpZnQoKTtcbiAgICB9XG5cbiAgICBpZiAoc2VnbWVudCA8IDApIHtcbiAgICAgIC8vIGFsbG93IG5lZ2F0aXZlIGluZGV4ZXMgdG8gYWRkcmVzcyBmcm9tIHRoZSBlbmRcbiAgICAgIHNlZ21lbnQgPSBNYXRoLm1heChzZWdtZW50cy5sZW5ndGggKyBzZWdtZW50LCAwKTtcbiAgICB9XG5cbiAgICBpZiAodiA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAvKmpzaGludCBsYXhicmVhazogdHJ1ZSAqL1xuICAgICAgcmV0dXJuIHNlZ21lbnQgPT09IHVuZGVmaW5lZFxuICAgICAgICA/IHNlZ21lbnRzXG4gICAgICAgIDogc2VnbWVudHNbc2VnbWVudF07XG4gICAgICAvKmpzaGludCBsYXhicmVhazogZmFsc2UgKi9cbiAgICB9IGVsc2UgaWYgKHNlZ21lbnQgPT09IG51bGwgfHwgc2VnbWVudHNbc2VnbWVudF0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgaWYgKGlzQXJyYXkodikpIHtcbiAgICAgICAgc2VnbWVudHMgPSBbXTtcbiAgICAgICAgLy8gY29sbGFwc2UgZW1wdHkgZWxlbWVudHMgd2l0aGluIGFycmF5XG4gICAgICAgIGZvciAodmFyIGk9MCwgbD12Lmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgICAgIGlmICghdltpXS5sZW5ndGggJiYgKCFzZWdtZW50cy5sZW5ndGggfHwgIXNlZ21lbnRzW3NlZ21lbnRzLmxlbmd0aCAtMV0ubGVuZ3RoKSkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKHNlZ21lbnRzLmxlbmd0aCAmJiAhc2VnbWVudHNbc2VnbWVudHMubGVuZ3RoIC0xXS5sZW5ndGgpIHtcbiAgICAgICAgICAgIHNlZ21lbnRzLnBvcCgpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHNlZ21lbnRzLnB1c2godHJpbVNsYXNoZXModltpXSkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHYgfHwgdHlwZW9mIHYgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIHYgPSB0cmltU2xhc2hlcyh2KTtcbiAgICAgICAgaWYgKHNlZ21lbnRzW3NlZ21lbnRzLmxlbmd0aCAtMV0gPT09ICcnKSB7XG4gICAgICAgICAgLy8gZW1wdHkgdHJhaWxpbmcgZWxlbWVudHMgaGF2ZSB0byBiZSBvdmVyd3JpdHRlblxuICAgICAgICAgIC8vIHRvIHByZXZlbnQgcmVzdWx0cyBzdWNoIGFzIC9mb28vL2JhclxuICAgICAgICAgIHNlZ21lbnRzW3NlZ21lbnRzLmxlbmd0aCAtMV0gPSB2O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHNlZ21lbnRzLnB1c2godik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKHYpIHtcbiAgICAgICAgc2VnbWVudHNbc2VnbWVudF0gPSB0cmltU2xhc2hlcyh2KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNlZ21lbnRzLnNwbGljZShzZWdtZW50LCAxKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoYWJzb2x1dGUpIHtcbiAgICAgIHNlZ21lbnRzLnVuc2hpZnQoJycpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnBhdGgoc2VnbWVudHMuam9pbihzZXBhcmF0b3IpLCBidWlsZCk7XG4gIH07XG4gIHAuc2VnbWVudENvZGVkID0gZnVuY3Rpb24oc2VnbWVudCwgdiwgYnVpbGQpIHtcbiAgICB2YXIgc2VnbWVudHMsIGksIGw7XG5cbiAgICBpZiAodHlwZW9mIHNlZ21lbnQgIT09ICdudW1iZXInKSB7XG4gICAgICBidWlsZCA9IHY7XG4gICAgICB2ID0gc2VnbWVudDtcbiAgICAgIHNlZ21lbnQgPSB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgaWYgKHYgPT09IHVuZGVmaW5lZCkge1xuICAgICAgc2VnbWVudHMgPSB0aGlzLnNlZ21lbnQoc2VnbWVudCwgdiwgYnVpbGQpO1xuICAgICAgaWYgKCFpc0FycmF5KHNlZ21lbnRzKSkge1xuICAgICAgICBzZWdtZW50cyA9IHNlZ21lbnRzICE9PSB1bmRlZmluZWQgPyBVUkkuZGVjb2RlKHNlZ21lbnRzKSA6IHVuZGVmaW5lZDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGZvciAoaSA9IDAsIGwgPSBzZWdtZW50cy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgICBzZWdtZW50c1tpXSA9IFVSSS5kZWNvZGUoc2VnbWVudHNbaV0pO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBzZWdtZW50cztcbiAgICB9XG5cbiAgICBpZiAoIWlzQXJyYXkodikpIHtcbiAgICAgIHYgPSAodHlwZW9mIHYgPT09ICdzdHJpbmcnIHx8IHYgaW5zdGFuY2VvZiBTdHJpbmcpID8gVVJJLmVuY29kZSh2KSA6IHY7XG4gICAgfSBlbHNlIHtcbiAgICAgIGZvciAoaSA9IDAsIGwgPSB2Lmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgICB2W2ldID0gVVJJLmVuY29kZSh2W2ldKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5zZWdtZW50KHNlZ21lbnQsIHYsIGJ1aWxkKTtcbiAgfTtcblxuICAvLyBtdXRhdGluZyBxdWVyeSBzdHJpbmdcbiAgdmFyIHEgPSBwLnF1ZXJ5O1xuICBwLnF1ZXJ5ID0gZnVuY3Rpb24odiwgYnVpbGQpIHtcbiAgICBpZiAodiA9PT0gdHJ1ZSkge1xuICAgICAgcmV0dXJuIFVSSS5wYXJzZVF1ZXJ5KHRoaXMuX3BhcnRzLnF1ZXJ5LCB0aGlzLl9wYXJ0cy5lc2NhcGVRdWVyeVNwYWNlKTtcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiB2ID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICB2YXIgZGF0YSA9IFVSSS5wYXJzZVF1ZXJ5KHRoaXMuX3BhcnRzLnF1ZXJ5LCB0aGlzLl9wYXJ0cy5lc2NhcGVRdWVyeVNwYWNlKTtcbiAgICAgIHZhciByZXN1bHQgPSB2LmNhbGwodGhpcywgZGF0YSk7XG4gICAgICB0aGlzLl9wYXJ0cy5xdWVyeSA9IFVSSS5idWlsZFF1ZXJ5KHJlc3VsdCB8fCBkYXRhLCB0aGlzLl9wYXJ0cy5kdXBsaWNhdGVRdWVyeVBhcmFtZXRlcnMsIHRoaXMuX3BhcnRzLmVzY2FwZVF1ZXJ5U3BhY2UpO1xuICAgICAgdGhpcy5idWlsZCghYnVpbGQpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSBlbHNlIGlmICh2ICE9PSB1bmRlZmluZWQgJiYgdHlwZW9mIHYgIT09ICdzdHJpbmcnKSB7XG4gICAgICB0aGlzLl9wYXJ0cy5xdWVyeSA9IFVSSS5idWlsZFF1ZXJ5KHYsIHRoaXMuX3BhcnRzLmR1cGxpY2F0ZVF1ZXJ5UGFyYW1ldGVycywgdGhpcy5fcGFydHMuZXNjYXBlUXVlcnlTcGFjZSk7XG4gICAgICB0aGlzLmJ1aWxkKCFidWlsZCk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHEuY2FsbCh0aGlzLCB2LCBidWlsZCk7XG4gICAgfVxuICB9O1xuICBwLnNldFF1ZXJ5ID0gZnVuY3Rpb24obmFtZSwgdmFsdWUsIGJ1aWxkKSB7XG4gICAgdmFyIGRhdGEgPSBVUkkucGFyc2VRdWVyeSh0aGlzLl9wYXJ0cy5xdWVyeSwgdGhpcy5fcGFydHMuZXNjYXBlUXVlcnlTcGFjZSk7XG5cbiAgICBpZiAodHlwZW9mIG5hbWUgPT09ICdzdHJpbmcnIHx8IG5hbWUgaW5zdGFuY2VvZiBTdHJpbmcpIHtcbiAgICAgIGRhdGFbbmFtZV0gPSB2YWx1ZSAhPT0gdW5kZWZpbmVkID8gdmFsdWUgOiBudWxsO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIG5hbWUgPT09ICdvYmplY3QnKSB7XG4gICAgICBmb3IgKHZhciBrZXkgaW4gbmFtZSkge1xuICAgICAgICBpZiAoaGFzT3duLmNhbGwobmFtZSwga2V5KSkge1xuICAgICAgICAgIGRhdGFba2V5XSA9IG5hbWVba2V5XTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdVUkkuYWRkUXVlcnkoKSBhY2NlcHRzIGFuIG9iamVjdCwgc3RyaW5nIGFzIHRoZSBuYW1lIHBhcmFtZXRlcicpO1xuICAgIH1cblxuICAgIHRoaXMuX3BhcnRzLnF1ZXJ5ID0gVVJJLmJ1aWxkUXVlcnkoZGF0YSwgdGhpcy5fcGFydHMuZHVwbGljYXRlUXVlcnlQYXJhbWV0ZXJzLCB0aGlzLl9wYXJ0cy5lc2NhcGVRdWVyeVNwYWNlKTtcbiAgICBpZiAodHlwZW9mIG5hbWUgIT09ICdzdHJpbmcnKSB7XG4gICAgICBidWlsZCA9IHZhbHVlO1xuICAgIH1cblxuICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICByZXR1cm4gdGhpcztcbiAgfTtcbiAgcC5hZGRRdWVyeSA9IGZ1bmN0aW9uKG5hbWUsIHZhbHVlLCBidWlsZCkge1xuICAgIHZhciBkYXRhID0gVVJJLnBhcnNlUXVlcnkodGhpcy5fcGFydHMucXVlcnksIHRoaXMuX3BhcnRzLmVzY2FwZVF1ZXJ5U3BhY2UpO1xuICAgIFVSSS5hZGRRdWVyeShkYXRhLCBuYW1lLCB2YWx1ZSA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IHZhbHVlKTtcbiAgICB0aGlzLl9wYXJ0cy5xdWVyeSA9IFVSSS5idWlsZFF1ZXJ5KGRhdGEsIHRoaXMuX3BhcnRzLmR1cGxpY2F0ZVF1ZXJ5UGFyYW1ldGVycywgdGhpcy5fcGFydHMuZXNjYXBlUXVlcnlTcGFjZSk7XG4gICAgaWYgKHR5cGVvZiBuYW1lICE9PSAnc3RyaW5nJykge1xuICAgICAgYnVpbGQgPSB2YWx1ZTtcbiAgICB9XG5cbiAgICB0aGlzLmJ1aWxkKCFidWlsZCk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH07XG4gIHAucmVtb3ZlUXVlcnkgPSBmdW5jdGlvbihuYW1lLCB2YWx1ZSwgYnVpbGQpIHtcbiAgICB2YXIgZGF0YSA9IFVSSS5wYXJzZVF1ZXJ5KHRoaXMuX3BhcnRzLnF1ZXJ5LCB0aGlzLl9wYXJ0cy5lc2NhcGVRdWVyeVNwYWNlKTtcbiAgICBVUkkucmVtb3ZlUXVlcnkoZGF0YSwgbmFtZSwgdmFsdWUpO1xuICAgIHRoaXMuX3BhcnRzLnF1ZXJ5ID0gVVJJLmJ1aWxkUXVlcnkoZGF0YSwgdGhpcy5fcGFydHMuZHVwbGljYXRlUXVlcnlQYXJhbWV0ZXJzLCB0aGlzLl9wYXJ0cy5lc2NhcGVRdWVyeVNwYWNlKTtcbiAgICBpZiAodHlwZW9mIG5hbWUgIT09ICdzdHJpbmcnKSB7XG4gICAgICBidWlsZCA9IHZhbHVlO1xuICAgIH1cblxuICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICByZXR1cm4gdGhpcztcbiAgfTtcbiAgcC5oYXNRdWVyeSA9IGZ1bmN0aW9uKG5hbWUsIHZhbHVlLCB3aXRoaW5BcnJheSkge1xuICAgIHZhciBkYXRhID0gVVJJLnBhcnNlUXVlcnkodGhpcy5fcGFydHMucXVlcnksIHRoaXMuX3BhcnRzLmVzY2FwZVF1ZXJ5U3BhY2UpO1xuICAgIHJldHVybiBVUkkuaGFzUXVlcnkoZGF0YSwgbmFtZSwgdmFsdWUsIHdpdGhpbkFycmF5KTtcbiAgfTtcbiAgcC5zZXRTZWFyY2ggPSBwLnNldFF1ZXJ5O1xuICBwLmFkZFNlYXJjaCA9IHAuYWRkUXVlcnk7XG4gIHAucmVtb3ZlU2VhcmNoID0gcC5yZW1vdmVRdWVyeTtcbiAgcC5oYXNTZWFyY2ggPSBwLmhhc1F1ZXJ5O1xuXG4gIC8vIHNhbml0aXppbmcgVVJMc1xuICBwLm5vcm1hbGl6ZSA9IGZ1bmN0aW9uKCkge1xuICAgIGlmICh0aGlzLl9wYXJ0cy51cm4pIHtcbiAgICAgIHJldHVybiB0aGlzXG4gICAgICAgIC5ub3JtYWxpemVQcm90b2NvbChmYWxzZSlcbiAgICAgICAgLm5vcm1hbGl6ZVBhdGgoZmFsc2UpXG4gICAgICAgIC5ub3JtYWxpemVRdWVyeShmYWxzZSlcbiAgICAgICAgLm5vcm1hbGl6ZUZyYWdtZW50KGZhbHNlKVxuICAgICAgICAuYnVpbGQoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpc1xuICAgICAgLm5vcm1hbGl6ZVByb3RvY29sKGZhbHNlKVxuICAgICAgLm5vcm1hbGl6ZUhvc3RuYW1lKGZhbHNlKVxuICAgICAgLm5vcm1hbGl6ZVBvcnQoZmFsc2UpXG4gICAgICAubm9ybWFsaXplUGF0aChmYWxzZSlcbiAgICAgIC5ub3JtYWxpemVRdWVyeShmYWxzZSlcbiAgICAgIC5ub3JtYWxpemVGcmFnbWVudChmYWxzZSlcbiAgICAgIC5idWlsZCgpO1xuICB9O1xuICBwLm5vcm1hbGl6ZVByb3RvY29sID0gZnVuY3Rpb24oYnVpbGQpIHtcbiAgICBpZiAodHlwZW9mIHRoaXMuX3BhcnRzLnByb3RvY29sID09PSAnc3RyaW5nJykge1xuICAgICAgdGhpcy5fcGFydHMucHJvdG9jb2wgPSB0aGlzLl9wYXJ0cy5wcm90b2NvbC50b0xvd2VyQ2FzZSgpO1xuICAgICAgdGhpcy5idWlsZCghYnVpbGQpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzO1xuICB9O1xuICBwLm5vcm1hbGl6ZUhvc3RuYW1lID0gZnVuY3Rpb24oYnVpbGQpIHtcbiAgICBpZiAodGhpcy5fcGFydHMuaG9zdG5hbWUpIHtcbiAgICAgIGlmICh0aGlzLmlzKCdJRE4nKSAmJiBwdW55Y29kZSkge1xuICAgICAgICB0aGlzLl9wYXJ0cy5ob3N0bmFtZSA9IHB1bnljb2RlLnRvQVNDSUkodGhpcy5fcGFydHMuaG9zdG5hbWUpO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLmlzKCdJUHY2JykgJiYgSVB2Nikge1xuICAgICAgICB0aGlzLl9wYXJ0cy5ob3N0bmFtZSA9IElQdjYuYmVzdCh0aGlzLl9wYXJ0cy5ob3N0bmFtZSk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuX3BhcnRzLmhvc3RuYW1lID0gdGhpcy5fcGFydHMuaG9zdG5hbWUudG9Mb3dlckNhc2UoKTtcbiAgICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfTtcbiAgcC5ub3JtYWxpemVQb3J0ID0gZnVuY3Rpb24oYnVpbGQpIHtcbiAgICAvLyByZW1vdmUgcG9ydCBvZiBpdCdzIHRoZSBwcm90b2NvbCdzIGRlZmF1bHRcbiAgICBpZiAodHlwZW9mIHRoaXMuX3BhcnRzLnByb3RvY29sID09PSAnc3RyaW5nJyAmJiB0aGlzLl9wYXJ0cy5wb3J0ID09PSBVUkkuZGVmYXVsdFBvcnRzW3RoaXMuX3BhcnRzLnByb3RvY29sXSkge1xuICAgICAgdGhpcy5fcGFydHMucG9ydCA9IG51bGw7XG4gICAgICB0aGlzLmJ1aWxkKCFidWlsZCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH07XG4gIHAubm9ybWFsaXplUGF0aCA9IGZ1bmN0aW9uKGJ1aWxkKSB7XG4gICAgdmFyIF9wYXRoID0gdGhpcy5fcGFydHMucGF0aDtcbiAgICBpZiAoIV9wYXRoKSB7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICBpZiAodGhpcy5fcGFydHMudXJuKSB7XG4gICAgICB0aGlzLl9wYXJ0cy5wYXRoID0gVVJJLnJlY29kZVVyblBhdGgodGhpcy5fcGFydHMucGF0aCk7XG4gICAgICB0aGlzLmJ1aWxkKCFidWlsZCk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICBpZiAodGhpcy5fcGFydHMucGF0aCA9PT0gJy8nKSB7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICB2YXIgX3dhc19yZWxhdGl2ZTtcbiAgICB2YXIgX2xlYWRpbmdQYXJlbnRzID0gJyc7XG4gICAgdmFyIF9wYXJlbnQsIF9wb3M7XG5cbiAgICAvLyBoYW5kbGUgcmVsYXRpdmUgcGF0aHNcbiAgICBpZiAoX3BhdGguY2hhckF0KDApICE9PSAnLycpIHtcbiAgICAgIF93YXNfcmVsYXRpdmUgPSB0cnVlO1xuICAgICAgX3BhdGggPSAnLycgKyBfcGF0aDtcbiAgICB9XG5cbiAgICAvLyBoYW5kbGUgcmVsYXRpdmUgZmlsZXMgKGFzIG9wcG9zZWQgdG8gZGlyZWN0b3JpZXMpXG4gICAgaWYgKF9wYXRoLnNsaWNlKC0zKSA9PT0gJy8uLicgfHwgX3BhdGguc2xpY2UoLTIpID09PSAnLy4nKSB7XG4gICAgICBfcGF0aCArPSAnLyc7XG4gICAgfVxuXG4gICAgLy8gcmVzb2x2ZSBzaW1wbGVzXG4gICAgX3BhdGggPSBfcGF0aFxuICAgICAgLnJlcGxhY2UoLyhcXC8oXFwuXFwvKSspfChcXC9cXC4kKS9nLCAnLycpXG4gICAgICAucmVwbGFjZSgvXFwvezIsfS9nLCAnLycpO1xuXG4gICAgLy8gcmVtZW1iZXIgbGVhZGluZyBwYXJlbnRzXG4gICAgaWYgKF93YXNfcmVsYXRpdmUpIHtcbiAgICAgIF9sZWFkaW5nUGFyZW50cyA9IF9wYXRoLnN1YnN0cmluZygxKS5tYXRjaCgvXihcXC5cXC5cXC8pKy8pIHx8ICcnO1xuICAgICAgaWYgKF9sZWFkaW5nUGFyZW50cykge1xuICAgICAgICBfbGVhZGluZ1BhcmVudHMgPSBfbGVhZGluZ1BhcmVudHNbMF07XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gcmVzb2x2ZSBwYXJlbnRzXG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIF9wYXJlbnQgPSBfcGF0aC5pbmRleE9mKCcvLi4nKTtcbiAgICAgIGlmIChfcGFyZW50ID09PSAtMSkge1xuICAgICAgICAvLyBubyBtb3JlIC4uLyB0byByZXNvbHZlXG4gICAgICAgIGJyZWFrO1xuICAgICAgfSBlbHNlIGlmIChfcGFyZW50ID09PSAwKSB7XG4gICAgICAgIC8vIHRvcCBsZXZlbCBjYW5ub3QgYmUgcmVsYXRpdmUsIHNraXAgaXRcbiAgICAgICAgX3BhdGggPSBfcGF0aC5zdWJzdHJpbmcoMyk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBfcG9zID0gX3BhdGguc3Vic3RyaW5nKDAsIF9wYXJlbnQpLmxhc3RJbmRleE9mKCcvJyk7XG4gICAgICBpZiAoX3BvcyA9PT0gLTEpIHtcbiAgICAgICAgX3BvcyA9IF9wYXJlbnQ7XG4gICAgICB9XG4gICAgICBfcGF0aCA9IF9wYXRoLnN1YnN0cmluZygwLCBfcG9zKSArIF9wYXRoLnN1YnN0cmluZyhfcGFyZW50ICsgMyk7XG4gICAgfVxuXG4gICAgLy8gcmV2ZXJ0IHRvIHJlbGF0aXZlXG4gICAgaWYgKF93YXNfcmVsYXRpdmUgJiYgdGhpcy5pcygncmVsYXRpdmUnKSkge1xuICAgICAgX3BhdGggPSBfbGVhZGluZ1BhcmVudHMgKyBfcGF0aC5zdWJzdHJpbmcoMSk7XG4gICAgfVxuXG4gICAgX3BhdGggPSBVUkkucmVjb2RlUGF0aChfcGF0aCk7XG4gICAgdGhpcy5fcGFydHMucGF0aCA9IF9wYXRoO1xuICAgIHRoaXMuYnVpbGQoIWJ1aWxkKTtcbiAgICByZXR1cm4gdGhpcztcbiAgfTtcbiAgcC5ub3JtYWxpemVQYXRobmFtZSA9IHAubm9ybWFsaXplUGF0aDtcbiAgcC5ub3JtYWxpemVRdWVyeSA9IGZ1bmN0aW9uKGJ1aWxkKSB7XG4gICAgaWYgKHR5cGVvZiB0aGlzLl9wYXJ0cy5xdWVyeSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIGlmICghdGhpcy5fcGFydHMucXVlcnkubGVuZ3RoKSB7XG4gICAgICAgIHRoaXMuX3BhcnRzLnF1ZXJ5ID0gbnVsbDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMucXVlcnkoVVJJLnBhcnNlUXVlcnkodGhpcy5fcGFydHMucXVlcnksIHRoaXMuX3BhcnRzLmVzY2FwZVF1ZXJ5U3BhY2UpKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5idWlsZCghYnVpbGQpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzO1xuICB9O1xuICBwLm5vcm1hbGl6ZUZyYWdtZW50ID0gZnVuY3Rpb24oYnVpbGQpIHtcbiAgICBpZiAoIXRoaXMuX3BhcnRzLmZyYWdtZW50KSB7XG4gICAgICB0aGlzLl9wYXJ0cy5mcmFnbWVudCA9IG51bGw7XG4gICAgICB0aGlzLmJ1aWxkKCFidWlsZCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH07XG4gIHAubm9ybWFsaXplU2VhcmNoID0gcC5ub3JtYWxpemVRdWVyeTtcbiAgcC5ub3JtYWxpemVIYXNoID0gcC5ub3JtYWxpemVGcmFnbWVudDtcblxuICBwLmlzbzg4NTkgPSBmdW5jdGlvbigpIHtcbiAgICAvLyBleHBlY3QgdW5pY29kZSBpbnB1dCwgaXNvODg1OSBvdXRwdXRcbiAgICB2YXIgZSA9IFVSSS5lbmNvZGU7XG4gICAgdmFyIGQgPSBVUkkuZGVjb2RlO1xuXG4gICAgVVJJLmVuY29kZSA9IGVzY2FwZTtcbiAgICBVUkkuZGVjb2RlID0gZGVjb2RlVVJJQ29tcG9uZW50O1xuICAgIHRyeSB7XG4gICAgICB0aGlzLm5vcm1hbGl6ZSgpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBVUkkuZW5jb2RlID0gZTtcbiAgICAgIFVSSS5kZWNvZGUgPSBkO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcztcbiAgfTtcblxuICBwLnVuaWNvZGUgPSBmdW5jdGlvbigpIHtcbiAgICAvLyBleHBlY3QgaXNvODg1OSBpbnB1dCwgdW5pY29kZSBvdXRwdXRcbiAgICB2YXIgZSA9IFVSSS5lbmNvZGU7XG4gICAgdmFyIGQgPSBVUkkuZGVjb2RlO1xuXG4gICAgVVJJLmVuY29kZSA9IHN0cmljdEVuY29kZVVSSUNvbXBvbmVudDtcbiAgICBVUkkuZGVjb2RlID0gdW5lc2NhcGU7XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMubm9ybWFsaXplKCk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIFVSSS5lbmNvZGUgPSBlO1xuICAgICAgVVJJLmRlY29kZSA9IGQ7XG4gICAgfVxuICAgIHJldHVybiB0aGlzO1xuICB9O1xuXG4gIHAucmVhZGFibGUgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgdXJpID0gdGhpcy5jbG9uZSgpO1xuICAgIC8vIHJlbW92aW5nIHVzZXJuYW1lLCBwYXNzd29yZCwgYmVjYXVzZSB0aGV5IHNob3VsZG4ndCBiZSBkaXNwbGF5ZWQgYWNjb3JkaW5nIHRvIFJGQyAzOTg2XG4gICAgdXJpLnVzZXJuYW1lKCcnKS5wYXNzd29yZCgnJykubm9ybWFsaXplKCk7XG4gICAgdmFyIHQgPSAnJztcbiAgICBpZiAodXJpLl9wYXJ0cy5wcm90b2NvbCkge1xuICAgICAgdCArPSB1cmkuX3BhcnRzLnByb3RvY29sICsgJzovLyc7XG4gICAgfVxuXG4gICAgaWYgKHVyaS5fcGFydHMuaG9zdG5hbWUpIHtcbiAgICAgIGlmICh1cmkuaXMoJ3B1bnljb2RlJykgJiYgcHVueWNvZGUpIHtcbiAgICAgICAgdCArPSBwdW55Y29kZS50b1VuaWNvZGUodXJpLl9wYXJ0cy5ob3N0bmFtZSk7XG4gICAgICAgIGlmICh1cmkuX3BhcnRzLnBvcnQpIHtcbiAgICAgICAgICB0ICs9ICc6JyArIHVyaS5fcGFydHMucG9ydDtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdCArPSB1cmkuaG9zdCgpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmICh1cmkuX3BhcnRzLmhvc3RuYW1lICYmIHVyaS5fcGFydHMucGF0aCAmJiB1cmkuX3BhcnRzLnBhdGguY2hhckF0KDApICE9PSAnLycpIHtcbiAgICAgIHQgKz0gJy8nO1xuICAgIH1cblxuICAgIHQgKz0gdXJpLnBhdGgodHJ1ZSk7XG4gICAgaWYgKHVyaS5fcGFydHMucXVlcnkpIHtcbiAgICAgIHZhciBxID0gJyc7XG4gICAgICBmb3IgKHZhciBpID0gMCwgcXAgPSB1cmkuX3BhcnRzLnF1ZXJ5LnNwbGl0KCcmJyksIGwgPSBxcC5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgdmFyIGt2ID0gKHFwW2ldIHx8ICcnKS5zcGxpdCgnPScpO1xuICAgICAgICBxICs9ICcmJyArIFVSSS5kZWNvZGVRdWVyeShrdlswXSwgdGhpcy5fcGFydHMuZXNjYXBlUXVlcnlTcGFjZSlcbiAgICAgICAgICAucmVwbGFjZSgvJi9nLCAnJTI2Jyk7XG5cbiAgICAgICAgaWYgKGt2WzFdICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBxICs9ICc9JyArIFVSSS5kZWNvZGVRdWVyeShrdlsxXSwgdGhpcy5fcGFydHMuZXNjYXBlUXVlcnlTcGFjZSlcbiAgICAgICAgICAgIC5yZXBsYWNlKC8mL2csICclMjYnKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdCArPSAnPycgKyBxLnN1YnN0cmluZygxKTtcbiAgICB9XG5cbiAgICB0ICs9IFVSSS5kZWNvZGVRdWVyeSh1cmkuaGFzaCgpLCB0cnVlKTtcbiAgICByZXR1cm4gdDtcbiAgfTtcblxuICAvLyByZXNvbHZpbmcgcmVsYXRpdmUgYW5kIGFic29sdXRlIFVSTHNcbiAgcC5hYnNvbHV0ZVRvID0gZnVuY3Rpb24oYmFzZSkge1xuICAgIHZhciByZXNvbHZlZCA9IHRoaXMuY2xvbmUoKTtcbiAgICB2YXIgcHJvcGVydGllcyA9IFsncHJvdG9jb2wnLCAndXNlcm5hbWUnLCAncGFzc3dvcmQnLCAnaG9zdG5hbWUnLCAncG9ydCddO1xuICAgIHZhciBiYXNlZGlyLCBpLCBwO1xuXG4gICAgaWYgKHRoaXMuX3BhcnRzLnVybikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdVUk5zIGRvIG5vdCBoYXZlIGFueSBnZW5lcmFsbHkgZGVmaW5lZCBoaWVyYXJjaGljYWwgY29tcG9uZW50cycpO1xuICAgIH1cblxuICAgIGlmICghKGJhc2UgaW5zdGFuY2VvZiBVUkkpKSB7XG4gICAgICBiYXNlID0gbmV3IFVSSShiYXNlKTtcbiAgICB9XG5cbiAgICBpZiAoIXJlc29sdmVkLl9wYXJ0cy5wcm90b2NvbCkge1xuICAgICAgcmVzb2x2ZWQuX3BhcnRzLnByb3RvY29sID0gYmFzZS5fcGFydHMucHJvdG9jb2w7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuX3BhcnRzLmhvc3RuYW1lKSB7XG4gICAgICByZXR1cm4gcmVzb2x2ZWQ7XG4gICAgfVxuXG4gICAgZm9yIChpID0gMDsgKHAgPSBwcm9wZXJ0aWVzW2ldKTsgaSsrKSB7XG4gICAgICByZXNvbHZlZC5fcGFydHNbcF0gPSBiYXNlLl9wYXJ0c1twXTtcbiAgICB9XG5cbiAgICBpZiAoIXJlc29sdmVkLl9wYXJ0cy5wYXRoKSB7XG4gICAgICByZXNvbHZlZC5fcGFydHMucGF0aCA9IGJhc2UuX3BhcnRzLnBhdGg7XG4gICAgICBpZiAoIXJlc29sdmVkLl9wYXJ0cy5xdWVyeSkge1xuICAgICAgICByZXNvbHZlZC5fcGFydHMucXVlcnkgPSBiYXNlLl9wYXJ0cy5xdWVyeTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHJlc29sdmVkLl9wYXJ0cy5wYXRoLnN1YnN0cmluZygtMikgPT09ICcuLicpIHtcbiAgICAgIHJlc29sdmVkLl9wYXJ0cy5wYXRoICs9ICcvJztcbiAgICB9XG5cbiAgICBpZiAocmVzb2x2ZWQucGF0aCgpLmNoYXJBdCgwKSAhPT0gJy8nKSB7XG4gICAgICBiYXNlZGlyID0gYmFzZS5kaXJlY3RvcnkoKTtcbiAgICAgIGJhc2VkaXIgPSBiYXNlZGlyID8gYmFzZWRpciA6IGJhc2UucGF0aCgpLmluZGV4T2YoJy8nKSA9PT0gMCA/ICcvJyA6ICcnO1xuICAgICAgcmVzb2x2ZWQuX3BhcnRzLnBhdGggPSAoYmFzZWRpciA/IChiYXNlZGlyICsgJy8nKSA6ICcnKSArIHJlc29sdmVkLl9wYXJ0cy5wYXRoO1xuICAgICAgcmVzb2x2ZWQubm9ybWFsaXplUGF0aCgpO1xuICAgIH1cblxuICAgIHJlc29sdmVkLmJ1aWxkKCk7XG4gICAgcmV0dXJuIHJlc29sdmVkO1xuICB9O1xuICBwLnJlbGF0aXZlVG8gPSBmdW5jdGlvbihiYXNlKSB7XG4gICAgdmFyIHJlbGF0aXZlID0gdGhpcy5jbG9uZSgpLm5vcm1hbGl6ZSgpO1xuICAgIHZhciByZWxhdGl2ZVBhcnRzLCBiYXNlUGFydHMsIGNvbW1vbiwgcmVsYXRpdmVQYXRoLCBiYXNlUGF0aDtcblxuICAgIGlmIChyZWxhdGl2ZS5fcGFydHMudXJuKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VSTnMgZG8gbm90IGhhdmUgYW55IGdlbmVyYWxseSBkZWZpbmVkIGhpZXJhcmNoaWNhbCBjb21wb25lbnRzJyk7XG4gICAgfVxuXG4gICAgYmFzZSA9IG5ldyBVUkkoYmFzZSkubm9ybWFsaXplKCk7XG4gICAgcmVsYXRpdmVQYXJ0cyA9IHJlbGF0aXZlLl9wYXJ0cztcbiAgICBiYXNlUGFydHMgPSBiYXNlLl9wYXJ0cztcbiAgICByZWxhdGl2ZVBhdGggPSByZWxhdGl2ZS5wYXRoKCk7XG4gICAgYmFzZVBhdGggPSBiYXNlLnBhdGgoKTtcblxuICAgIGlmIChyZWxhdGl2ZVBhdGguY2hhckF0KDApICE9PSAnLycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVVJJIGlzIGFscmVhZHkgcmVsYXRpdmUnKTtcbiAgICB9XG5cbiAgICBpZiAoYmFzZVBhdGguY2hhckF0KDApICE9PSAnLycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IGNhbGN1bGF0ZSBhIFVSSSByZWxhdGl2ZSB0byBhbm90aGVyIHJlbGF0aXZlIFVSSScpO1xuICAgIH1cblxuICAgIGlmIChyZWxhdGl2ZVBhcnRzLnByb3RvY29sID09PSBiYXNlUGFydHMucHJvdG9jb2wpIHtcbiAgICAgIHJlbGF0aXZlUGFydHMucHJvdG9jb2wgPSBudWxsO1xuICAgIH1cblxuICAgIGlmIChyZWxhdGl2ZVBhcnRzLnVzZXJuYW1lICE9PSBiYXNlUGFydHMudXNlcm5hbWUgfHwgcmVsYXRpdmVQYXJ0cy5wYXNzd29yZCAhPT0gYmFzZVBhcnRzLnBhc3N3b3JkKSB7XG4gICAgICByZXR1cm4gcmVsYXRpdmUuYnVpbGQoKTtcbiAgICB9XG5cbiAgICBpZiAocmVsYXRpdmVQYXJ0cy5wcm90b2NvbCAhPT0gbnVsbCB8fCByZWxhdGl2ZVBhcnRzLnVzZXJuYW1lICE9PSBudWxsIHx8IHJlbGF0aXZlUGFydHMucGFzc3dvcmQgIT09IG51bGwpIHtcbiAgICAgIHJldHVybiByZWxhdGl2ZS5idWlsZCgpO1xuICAgIH1cblxuICAgIGlmIChyZWxhdGl2ZVBhcnRzLmhvc3RuYW1lID09PSBiYXNlUGFydHMuaG9zdG5hbWUgJiYgcmVsYXRpdmVQYXJ0cy5wb3J0ID09PSBiYXNlUGFydHMucG9ydCkge1xuICAgICAgcmVsYXRpdmVQYXJ0cy5ob3N0bmFtZSA9IG51bGw7XG4gICAgICByZWxhdGl2ZVBhcnRzLnBvcnQgPSBudWxsO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gcmVsYXRpdmUuYnVpbGQoKTtcbiAgICB9XG5cbiAgICBpZiAocmVsYXRpdmVQYXRoID09PSBiYXNlUGF0aCkge1xuICAgICAgcmVsYXRpdmVQYXJ0cy5wYXRoID0gJyc7XG4gICAgICByZXR1cm4gcmVsYXRpdmUuYnVpbGQoKTtcbiAgICB9XG5cbiAgICAvLyBkZXRlcm1pbmUgY29tbW9uIHN1YiBwYXRoXG4gICAgY29tbW9uID0gVVJJLmNvbW1vblBhdGgocmVsYXRpdmVQYXRoLCBiYXNlUGF0aCk7XG5cbiAgICAvLyBJZiB0aGUgcGF0aHMgaGF2ZSBub3RoaW5nIGluIGNvbW1vbiwgcmV0dXJuIGEgcmVsYXRpdmUgVVJMIHdpdGggdGhlIGFic29sdXRlIHBhdGguXG4gICAgaWYgKCFjb21tb24pIHtcbiAgICAgIHJldHVybiByZWxhdGl2ZS5idWlsZCgpO1xuICAgIH1cblxuICAgIHZhciBwYXJlbnRzID0gYmFzZVBhcnRzLnBhdGhcbiAgICAgIC5zdWJzdHJpbmcoY29tbW9uLmxlbmd0aClcbiAgICAgIC5yZXBsYWNlKC9bXlxcL10qJC8sICcnKVxuICAgICAgLnJlcGxhY2UoLy4qP1xcLy9nLCAnLi4vJyk7XG5cbiAgICByZWxhdGl2ZVBhcnRzLnBhdGggPSAocGFyZW50cyArIHJlbGF0aXZlUGFydHMucGF0aC5zdWJzdHJpbmcoY29tbW9uLmxlbmd0aCkpIHx8ICcuLyc7XG5cbiAgICByZXR1cm4gcmVsYXRpdmUuYnVpbGQoKTtcbiAgfTtcblxuICAvLyBjb21wYXJpbmcgVVJJc1xuICBwLmVxdWFscyA9IGZ1bmN0aW9uKHVyaSkge1xuICAgIHZhciBvbmUgPSB0aGlzLmNsb25lKCk7XG4gICAgdmFyIHR3byA9IG5ldyBVUkkodXJpKTtcbiAgICB2YXIgb25lX21hcCA9IHt9O1xuICAgIHZhciB0d29fbWFwID0ge307XG4gICAgdmFyIGNoZWNrZWQgPSB7fTtcbiAgICB2YXIgb25lX3F1ZXJ5LCB0d29fcXVlcnksIGtleTtcblxuICAgIG9uZS5ub3JtYWxpemUoKTtcbiAgICB0d28ubm9ybWFsaXplKCk7XG5cbiAgICAvLyBleGFjdCBtYXRjaFxuICAgIGlmIChvbmUudG9TdHJpbmcoKSA9PT0gdHdvLnRvU3RyaW5nKCkpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIC8vIGV4dHJhY3QgcXVlcnkgc3RyaW5nXG4gICAgb25lX3F1ZXJ5ID0gb25lLnF1ZXJ5KCk7XG4gICAgdHdvX3F1ZXJ5ID0gdHdvLnF1ZXJ5KCk7XG4gICAgb25lLnF1ZXJ5KCcnKTtcbiAgICB0d28ucXVlcnkoJycpO1xuXG4gICAgLy8gZGVmaW5pdGVseSBub3QgZXF1YWwgaWYgbm90IGV2ZW4gbm9uLXF1ZXJ5IHBhcnRzIG1hdGNoXG4gICAgaWYgKG9uZS50b1N0cmluZygpICE9PSB0d28udG9TdHJpbmcoKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIHF1ZXJ5IHBhcmFtZXRlcnMgaGF2ZSB0aGUgc2FtZSBsZW5ndGgsIGV2ZW4gaWYgdGhleSdyZSBwZXJtdXRlZFxuICAgIGlmIChvbmVfcXVlcnkubGVuZ3RoICE9PSB0d29fcXVlcnkubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgb25lX21hcCA9IFVSSS5wYXJzZVF1ZXJ5KG9uZV9xdWVyeSwgdGhpcy5fcGFydHMuZXNjYXBlUXVlcnlTcGFjZSk7XG4gICAgdHdvX21hcCA9IFVSSS5wYXJzZVF1ZXJ5KHR3b19xdWVyeSwgdGhpcy5fcGFydHMuZXNjYXBlUXVlcnlTcGFjZSk7XG5cbiAgICBmb3IgKGtleSBpbiBvbmVfbWFwKSB7XG4gICAgICBpZiAoaGFzT3duLmNhbGwob25lX21hcCwga2V5KSkge1xuICAgICAgICBpZiAoIWlzQXJyYXkob25lX21hcFtrZXldKSkge1xuICAgICAgICAgIGlmIChvbmVfbWFwW2tleV0gIT09IHR3b19tYXBba2V5XSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmICghYXJyYXlzRXF1YWwob25lX21hcFtrZXldLCB0d29fbWFwW2tleV0pKSB7XG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgY2hlY2tlZFtrZXldID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmb3IgKGtleSBpbiB0d29fbWFwKSB7XG4gICAgICBpZiAoaGFzT3duLmNhbGwodHdvX21hcCwga2V5KSkge1xuICAgICAgICBpZiAoIWNoZWNrZWRba2V5XSkge1xuICAgICAgICAgIC8vIHR3byBjb250YWlucyBhIHBhcmFtZXRlciBub3QgcHJlc2VudCBpbiBvbmVcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfTtcblxuICAvLyBzdGF0ZVxuICBwLmR1cGxpY2F0ZVF1ZXJ5UGFyYW1ldGVycyA9IGZ1bmN0aW9uKHYpIHtcbiAgICB0aGlzLl9wYXJ0cy5kdXBsaWNhdGVRdWVyeVBhcmFtZXRlcnMgPSAhIXY7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH07XG5cbiAgcC5lc2NhcGVRdWVyeVNwYWNlID0gZnVuY3Rpb24odikge1xuICAgIHRoaXMuX3BhcnRzLmVzY2FwZVF1ZXJ5U3BhY2UgPSAhIXY7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH07XG5cbiAgcmV0dXJuIFVSSTtcbn0pKTtcbiIsIi8qISBodHRwOi8vbXRocy5iZS9wdW55Y29kZSB2MS4yLjMgYnkgQG1hdGhpYXMgKi9cbjsoZnVuY3Rpb24ocm9vdCkge1xuXG5cdC8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZXMgKi9cblx0dmFyIGZyZWVFeHBvcnRzID0gdHlwZW9mIGV4cG9ydHMgPT0gJ29iamVjdCcgJiYgZXhwb3J0cztcblx0dmFyIGZyZWVNb2R1bGUgPSB0eXBlb2YgbW9kdWxlID09ICdvYmplY3QnICYmIG1vZHVsZSAmJlxuXHRcdG1vZHVsZS5leHBvcnRzID09IGZyZWVFeHBvcnRzICYmIG1vZHVsZTtcblx0dmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbDtcblx0aWYgKGZyZWVHbG9iYWwuZ2xvYmFsID09PSBmcmVlR2xvYmFsIHx8IGZyZWVHbG9iYWwud2luZG93ID09PSBmcmVlR2xvYmFsKSB7XG5cdFx0cm9vdCA9IGZyZWVHbG9iYWw7XG5cdH1cblxuXHQvKipcblx0ICogVGhlIGBwdW55Y29kZWAgb2JqZWN0LlxuXHQgKiBAbmFtZSBwdW55Y29kZVxuXHQgKiBAdHlwZSBPYmplY3Rcblx0ICovXG5cdHZhciBwdW55Y29kZSxcblxuXHQvKiogSGlnaGVzdCBwb3NpdGl2ZSBzaWduZWQgMzItYml0IGZsb2F0IHZhbHVlICovXG5cdG1heEludCA9IDIxNDc0ODM2NDcsIC8vIGFrYS4gMHg3RkZGRkZGRiBvciAyXjMxLTFcblxuXHQvKiogQm9vdHN0cmluZyBwYXJhbWV0ZXJzICovXG5cdGJhc2UgPSAzNixcblx0dE1pbiA9IDEsXG5cdHRNYXggPSAyNixcblx0c2tldyA9IDM4LFxuXHRkYW1wID0gNzAwLFxuXHRpbml0aWFsQmlhcyA9IDcyLFxuXHRpbml0aWFsTiA9IDEyOCwgLy8gMHg4MFxuXHRkZWxpbWl0ZXIgPSAnLScsIC8vICdcXHgyRCdcblxuXHQvKiogUmVndWxhciBleHByZXNzaW9ucyAqL1xuXHRyZWdleFB1bnljb2RlID0gL154bi0tLyxcblx0cmVnZXhOb25BU0NJSSA9IC9bXiAtfl0vLCAvLyB1bnByaW50YWJsZSBBU0NJSSBjaGFycyArIG5vbi1BU0NJSSBjaGFyc1xuXHRyZWdleFNlcGFyYXRvcnMgPSAvXFx4MkV8XFx1MzAwMnxcXHVGRjBFfFxcdUZGNjEvZywgLy8gUkZDIDM0OTAgc2VwYXJhdG9yc1xuXG5cdC8qKiBFcnJvciBtZXNzYWdlcyAqL1xuXHRlcnJvcnMgPSB7XG5cdFx0J292ZXJmbG93JzogJ092ZXJmbG93OiBpbnB1dCBuZWVkcyB3aWRlciBpbnRlZ2VycyB0byBwcm9jZXNzJyxcblx0XHQnbm90LWJhc2ljJzogJ0lsbGVnYWwgaW5wdXQgPj0gMHg4MCAobm90IGEgYmFzaWMgY29kZSBwb2ludCknLFxuXHRcdCdpbnZhbGlkLWlucHV0JzogJ0ludmFsaWQgaW5wdXQnXG5cdH0sXG5cblx0LyoqIENvbnZlbmllbmNlIHNob3J0Y3V0cyAqL1xuXHRiYXNlTWludXNUTWluID0gYmFzZSAtIHRNaW4sXG5cdGZsb29yID0gTWF0aC5mbG9vcixcblx0c3RyaW5nRnJvbUNoYXJDb2RlID0gU3RyaW5nLmZyb21DaGFyQ29kZSxcblxuXHQvKiogVGVtcG9yYXJ5IHZhcmlhYmxlICovXG5cdGtleTtcblxuXHQvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuXHQvKipcblx0ICogQSBnZW5lcmljIGVycm9yIHV0aWxpdHkgZnVuY3Rpb24uXG5cdCAqIEBwcml2YXRlXG5cdCAqIEBwYXJhbSB7U3RyaW5nfSB0eXBlIFRoZSBlcnJvciB0eXBlLlxuXHQgKiBAcmV0dXJucyB7RXJyb3J9IFRocm93cyBhIGBSYW5nZUVycm9yYCB3aXRoIHRoZSBhcHBsaWNhYmxlIGVycm9yIG1lc3NhZ2UuXG5cdCAqL1xuXHRmdW5jdGlvbiBlcnJvcih0eXBlKSB7XG5cdFx0dGhyb3cgUmFuZ2VFcnJvcihlcnJvcnNbdHlwZV0pO1xuXHR9XG5cblx0LyoqXG5cdCAqIEEgZ2VuZXJpYyBgQXJyYXkjbWFwYCB1dGlsaXR5IGZ1bmN0aW9uLlxuXHQgKiBAcHJpdmF0ZVxuXHQgKiBAcGFyYW0ge0FycmF5fSBhcnJheSBUaGUgYXJyYXkgdG8gaXRlcmF0ZSBvdmVyLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFjayBUaGUgZnVuY3Rpb24gdGhhdCBnZXRzIGNhbGxlZCBmb3IgZXZlcnkgYXJyYXlcblx0ICogaXRlbS5cblx0ICogQHJldHVybnMge0FycmF5fSBBIG5ldyBhcnJheSBvZiB2YWx1ZXMgcmV0dXJuZWQgYnkgdGhlIGNhbGxiYWNrIGZ1bmN0aW9uLlxuXHQgKi9cblx0ZnVuY3Rpb24gbWFwKGFycmF5LCBmbikge1xuXHRcdHZhciBsZW5ndGggPSBhcnJheS5sZW5ndGg7XG5cdFx0d2hpbGUgKGxlbmd0aC0tKSB7XG5cdFx0XHRhcnJheVtsZW5ndGhdID0gZm4oYXJyYXlbbGVuZ3RoXSk7XG5cdFx0fVxuXHRcdHJldHVybiBhcnJheTtcblx0fVxuXG5cdC8qKlxuXHQgKiBBIHNpbXBsZSBgQXJyYXkjbWFwYC1saWtlIHdyYXBwZXIgdG8gd29yayB3aXRoIGRvbWFpbiBuYW1lIHN0cmluZ3MuXG5cdCAqIEBwcml2YXRlXG5cdCAqIEBwYXJhbSB7U3RyaW5nfSBkb21haW4gVGhlIGRvbWFpbiBuYW1lLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFjayBUaGUgZnVuY3Rpb24gdGhhdCBnZXRzIGNhbGxlZCBmb3IgZXZlcnlcblx0ICogY2hhcmFjdGVyLlxuXHQgKiBAcmV0dXJucyB7QXJyYXl9IEEgbmV3IHN0cmluZyBvZiBjaGFyYWN0ZXJzIHJldHVybmVkIGJ5IHRoZSBjYWxsYmFja1xuXHQgKiBmdW5jdGlvbi5cblx0ICovXG5cdGZ1bmN0aW9uIG1hcERvbWFpbihzdHJpbmcsIGZuKSB7XG5cdFx0cmV0dXJuIG1hcChzdHJpbmcuc3BsaXQocmVnZXhTZXBhcmF0b3JzKSwgZm4pLmpvaW4oJy4nKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG51bWVyaWMgY29kZSBwb2ludHMgb2YgZWFjaCBVbmljb2RlXG5cdCAqIGNoYXJhY3RlciBpbiB0aGUgc3RyaW5nLiBXaGlsZSBKYXZhU2NyaXB0IHVzZXMgVUNTLTIgaW50ZXJuYWxseSxcblx0ICogdGhpcyBmdW5jdGlvbiB3aWxsIGNvbnZlcnQgYSBwYWlyIG9mIHN1cnJvZ2F0ZSBoYWx2ZXMgKGVhY2ggb2Ygd2hpY2hcblx0ICogVUNTLTIgZXhwb3NlcyBhcyBzZXBhcmF0ZSBjaGFyYWN0ZXJzKSBpbnRvIGEgc2luZ2xlIGNvZGUgcG9pbnQsXG5cdCAqIG1hdGNoaW5nIFVURi0xNi5cblx0ICogQHNlZSBgcHVueWNvZGUudWNzMi5lbmNvZGVgXG5cdCAqIEBzZWUgPGh0dHA6Ly9tYXRoaWFzYnluZW5zLmJlL25vdGVzL2phdmFzY3JpcHQtZW5jb2Rpbmc+XG5cdCAqIEBtZW1iZXJPZiBwdW55Y29kZS51Y3MyXG5cdCAqIEBuYW1lIGRlY29kZVxuXHQgKiBAcGFyYW0ge1N0cmluZ30gc3RyaW5nIFRoZSBVbmljb2RlIGlucHV0IHN0cmluZyAoVUNTLTIpLlxuXHQgKiBAcmV0dXJucyB7QXJyYXl9IFRoZSBuZXcgYXJyYXkgb2YgY29kZSBwb2ludHMuXG5cdCAqL1xuXHRmdW5jdGlvbiB1Y3MyZGVjb2RlKHN0cmluZykge1xuXHRcdHZhciBvdXRwdXQgPSBbXSxcblx0XHQgICAgY291bnRlciA9IDAsXG5cdFx0ICAgIGxlbmd0aCA9IHN0cmluZy5sZW5ndGgsXG5cdFx0ICAgIHZhbHVlLFxuXHRcdCAgICBleHRyYTtcblx0XHR3aGlsZSAoY291bnRlciA8IGxlbmd0aCkge1xuXHRcdFx0dmFsdWUgPSBzdHJpbmcuY2hhckNvZGVBdChjb3VudGVyKyspO1xuXHRcdFx0aWYgKHZhbHVlID49IDB4RDgwMCAmJiB2YWx1ZSA8PSAweERCRkYgJiYgY291bnRlciA8IGxlbmd0aCkge1xuXHRcdFx0XHQvLyBoaWdoIHN1cnJvZ2F0ZSwgYW5kIHRoZXJlIGlzIGEgbmV4dCBjaGFyYWN0ZXJcblx0XHRcdFx0ZXh0cmEgPSBzdHJpbmcuY2hhckNvZGVBdChjb3VudGVyKyspO1xuXHRcdFx0XHRpZiAoKGV4dHJhICYgMHhGQzAwKSA9PSAweERDMDApIHsgLy8gbG93IHN1cnJvZ2F0ZVxuXHRcdFx0XHRcdG91dHB1dC5wdXNoKCgodmFsdWUgJiAweDNGRikgPDwgMTApICsgKGV4dHJhICYgMHgzRkYpICsgMHgxMDAwMCk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0Ly8gdW5tYXRjaGVkIHN1cnJvZ2F0ZTsgb25seSBhcHBlbmQgdGhpcyBjb2RlIHVuaXQsIGluIGNhc2UgdGhlIG5leHRcblx0XHRcdFx0XHQvLyBjb2RlIHVuaXQgaXMgdGhlIGhpZ2ggc3Vycm9nYXRlIG9mIGEgc3Vycm9nYXRlIHBhaXJcblx0XHRcdFx0XHRvdXRwdXQucHVzaCh2YWx1ZSk7XG5cdFx0XHRcdFx0Y291bnRlci0tO1xuXHRcdFx0XHR9XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRvdXRwdXQucHVzaCh2YWx1ZSk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiBvdXRwdXQ7XG5cdH1cblxuXHQvKipcblx0ICogQ3JlYXRlcyBhIHN0cmluZyBiYXNlZCBvbiBhbiBhcnJheSBvZiBudW1lcmljIGNvZGUgcG9pbnRzLlxuXHQgKiBAc2VlIGBwdW55Y29kZS51Y3MyLmRlY29kZWBcblx0ICogQG1lbWJlck9mIHB1bnljb2RlLnVjczJcblx0ICogQG5hbWUgZW5jb2RlXG5cdCAqIEBwYXJhbSB7QXJyYXl9IGNvZGVQb2ludHMgVGhlIGFycmF5IG9mIG51bWVyaWMgY29kZSBwb2ludHMuXG5cdCAqIEByZXR1cm5zIHtTdHJpbmd9IFRoZSBuZXcgVW5pY29kZSBzdHJpbmcgKFVDUy0yKS5cblx0ICovXG5cdGZ1bmN0aW9uIHVjczJlbmNvZGUoYXJyYXkpIHtcblx0XHRyZXR1cm4gbWFwKGFycmF5LCBmdW5jdGlvbih2YWx1ZSkge1xuXHRcdFx0dmFyIG91dHB1dCA9ICcnO1xuXHRcdFx0aWYgKHZhbHVlID4gMHhGRkZGKSB7XG5cdFx0XHRcdHZhbHVlIC09IDB4MTAwMDA7XG5cdFx0XHRcdG91dHB1dCArPSBzdHJpbmdGcm9tQ2hhckNvZGUodmFsdWUgPj4+IDEwICYgMHgzRkYgfCAweEQ4MDApO1xuXHRcdFx0XHR2YWx1ZSA9IDB4REMwMCB8IHZhbHVlICYgMHgzRkY7XG5cdFx0XHR9XG5cdFx0XHRvdXRwdXQgKz0gc3RyaW5nRnJvbUNoYXJDb2RlKHZhbHVlKTtcblx0XHRcdHJldHVybiBvdXRwdXQ7XG5cdFx0fSkuam9pbignJyk7XG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgYSBiYXNpYyBjb2RlIHBvaW50IGludG8gYSBkaWdpdC9pbnRlZ2VyLlxuXHQgKiBAc2VlIGBkaWdpdFRvQmFzaWMoKWBcblx0ICogQHByaXZhdGVcblx0ICogQHBhcmFtIHtOdW1iZXJ9IGNvZGVQb2ludCBUaGUgYmFzaWMgbnVtZXJpYyBjb2RlIHBvaW50IHZhbHVlLlxuXHQgKiBAcmV0dXJucyB7TnVtYmVyfSBUaGUgbnVtZXJpYyB2YWx1ZSBvZiBhIGJhc2ljIGNvZGUgcG9pbnQgKGZvciB1c2UgaW5cblx0ICogcmVwcmVzZW50aW5nIGludGVnZXJzKSBpbiB0aGUgcmFuZ2UgYDBgIHRvIGBiYXNlIC0gMWAsIG9yIGBiYXNlYCBpZlxuXHQgKiB0aGUgY29kZSBwb2ludCBkb2VzIG5vdCByZXByZXNlbnQgYSB2YWx1ZS5cblx0ICovXG5cdGZ1bmN0aW9uIGJhc2ljVG9EaWdpdChjb2RlUG9pbnQpIHtcblx0XHRpZiAoY29kZVBvaW50IC0gNDggPCAxMCkge1xuXHRcdFx0cmV0dXJuIGNvZGVQb2ludCAtIDIyO1xuXHRcdH1cblx0XHRpZiAoY29kZVBvaW50IC0gNjUgPCAyNikge1xuXHRcdFx0cmV0dXJuIGNvZGVQb2ludCAtIDY1O1xuXHRcdH1cblx0XHRpZiAoY29kZVBvaW50IC0gOTcgPCAyNikge1xuXHRcdFx0cmV0dXJuIGNvZGVQb2ludCAtIDk3O1xuXHRcdH1cblx0XHRyZXR1cm4gYmFzZTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDb252ZXJ0cyBhIGRpZ2l0L2ludGVnZXIgaW50byBhIGJhc2ljIGNvZGUgcG9pbnQuXG5cdCAqIEBzZWUgYGJhc2ljVG9EaWdpdCgpYFxuXHQgKiBAcHJpdmF0ZVxuXHQgKiBAcGFyYW0ge051bWJlcn0gZGlnaXQgVGhlIG51bWVyaWMgdmFsdWUgb2YgYSBiYXNpYyBjb2RlIHBvaW50LlxuXHQgKiBAcmV0dXJucyB7TnVtYmVyfSBUaGUgYmFzaWMgY29kZSBwb2ludCB3aG9zZSB2YWx1ZSAod2hlbiB1c2VkIGZvclxuXHQgKiByZXByZXNlbnRpbmcgaW50ZWdlcnMpIGlzIGBkaWdpdGAsIHdoaWNoIG5lZWRzIHRvIGJlIGluIHRoZSByYW5nZVxuXHQgKiBgMGAgdG8gYGJhc2UgLSAxYC4gSWYgYGZsYWdgIGlzIG5vbi16ZXJvLCB0aGUgdXBwZXJjYXNlIGZvcm0gaXNcblx0ICogdXNlZDsgZWxzZSwgdGhlIGxvd2VyY2FzZSBmb3JtIGlzIHVzZWQuIFRoZSBiZWhhdmlvciBpcyB1bmRlZmluZWRcblx0ICogaWYgYGZsYWdgIGlzIG5vbi16ZXJvIGFuZCBgZGlnaXRgIGhhcyBubyB1cHBlcmNhc2UgZm9ybS5cblx0ICovXG5cdGZ1bmN0aW9uIGRpZ2l0VG9CYXNpYyhkaWdpdCwgZmxhZykge1xuXHRcdC8vICAwLi4yNSBtYXAgdG8gQVNDSUkgYS4ueiBvciBBLi5aXG5cdFx0Ly8gMjYuLjM1IG1hcCB0byBBU0NJSSAwLi45XG5cdFx0cmV0dXJuIGRpZ2l0ICsgMjIgKyA3NSAqIChkaWdpdCA8IDI2KSAtICgoZmxhZyAhPSAwKSA8PCA1KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBCaWFzIGFkYXB0YXRpb24gZnVuY3Rpb24gYXMgcGVyIHNlY3Rpb24gMy40IG9mIFJGQyAzNDkyLlxuXHQgKiBodHRwOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9yZmMzNDkyI3NlY3Rpb24tMy40XG5cdCAqIEBwcml2YXRlXG5cdCAqL1xuXHRmdW5jdGlvbiBhZGFwdChkZWx0YSwgbnVtUG9pbnRzLCBmaXJzdFRpbWUpIHtcblx0XHR2YXIgayA9IDA7XG5cdFx0ZGVsdGEgPSBmaXJzdFRpbWUgPyBmbG9vcihkZWx0YSAvIGRhbXApIDogZGVsdGEgPj4gMTtcblx0XHRkZWx0YSArPSBmbG9vcihkZWx0YSAvIG51bVBvaW50cyk7XG5cdFx0Zm9yICgvKiBubyBpbml0aWFsaXphdGlvbiAqLzsgZGVsdGEgPiBiYXNlTWludXNUTWluICogdE1heCA+PiAxOyBrICs9IGJhc2UpIHtcblx0XHRcdGRlbHRhID0gZmxvb3IoZGVsdGEgLyBiYXNlTWludXNUTWluKTtcblx0XHR9XG5cdFx0cmV0dXJuIGZsb29yKGsgKyAoYmFzZU1pbnVzVE1pbiArIDEpICogZGVsdGEgLyAoZGVsdGEgKyBza2V3KSk7XG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgYSBQdW55Y29kZSBzdHJpbmcgb2YgQVNDSUktb25seSBzeW1ib2xzIHRvIGEgc3RyaW5nIG9mIFVuaWNvZGVcblx0ICogc3ltYm9scy5cblx0ICogQG1lbWJlck9mIHB1bnljb2RlXG5cdCAqIEBwYXJhbSB7U3RyaW5nfSBpbnB1dCBUaGUgUHVueWNvZGUgc3RyaW5nIG9mIEFTQ0lJLW9ubHkgc3ltYm9scy5cblx0ICogQHJldHVybnMge1N0cmluZ30gVGhlIHJlc3VsdGluZyBzdHJpbmcgb2YgVW5pY29kZSBzeW1ib2xzLlxuXHQgKi9cblx0ZnVuY3Rpb24gZGVjb2RlKGlucHV0KSB7XG5cdFx0Ly8gRG9uJ3QgdXNlIFVDUy0yXG5cdFx0dmFyIG91dHB1dCA9IFtdLFxuXHRcdCAgICBpbnB1dExlbmd0aCA9IGlucHV0Lmxlbmd0aCxcblx0XHQgICAgb3V0LFxuXHRcdCAgICBpID0gMCxcblx0XHQgICAgbiA9IGluaXRpYWxOLFxuXHRcdCAgICBiaWFzID0gaW5pdGlhbEJpYXMsXG5cdFx0ICAgIGJhc2ljLFxuXHRcdCAgICBqLFxuXHRcdCAgICBpbmRleCxcblx0XHQgICAgb2xkaSxcblx0XHQgICAgdyxcblx0XHQgICAgayxcblx0XHQgICAgZGlnaXQsXG5cdFx0ICAgIHQsXG5cdFx0ICAgIGxlbmd0aCxcblx0XHQgICAgLyoqIENhY2hlZCBjYWxjdWxhdGlvbiByZXN1bHRzICovXG5cdFx0ICAgIGJhc2VNaW51c1Q7XG5cblx0XHQvLyBIYW5kbGUgdGhlIGJhc2ljIGNvZGUgcG9pbnRzOiBsZXQgYGJhc2ljYCBiZSB0aGUgbnVtYmVyIG9mIGlucHV0IGNvZGVcblx0XHQvLyBwb2ludHMgYmVmb3JlIHRoZSBsYXN0IGRlbGltaXRlciwgb3IgYDBgIGlmIHRoZXJlIGlzIG5vbmUsIHRoZW4gY29weVxuXHRcdC8vIHRoZSBmaXJzdCBiYXNpYyBjb2RlIHBvaW50cyB0byB0aGUgb3V0cHV0LlxuXG5cdFx0YmFzaWMgPSBpbnB1dC5sYXN0SW5kZXhPZihkZWxpbWl0ZXIpO1xuXHRcdGlmIChiYXNpYyA8IDApIHtcblx0XHRcdGJhc2ljID0gMDtcblx0XHR9XG5cblx0XHRmb3IgKGogPSAwOyBqIDwgYmFzaWM7ICsraikge1xuXHRcdFx0Ly8gaWYgaXQncyBub3QgYSBiYXNpYyBjb2RlIHBvaW50XG5cdFx0XHRpZiAoaW5wdXQuY2hhckNvZGVBdChqKSA+PSAweDgwKSB7XG5cdFx0XHRcdGVycm9yKCdub3QtYmFzaWMnKTtcblx0XHRcdH1cblx0XHRcdG91dHB1dC5wdXNoKGlucHV0LmNoYXJDb2RlQXQoaikpO1xuXHRcdH1cblxuXHRcdC8vIE1haW4gZGVjb2RpbmcgbG9vcDogc3RhcnQganVzdCBhZnRlciB0aGUgbGFzdCBkZWxpbWl0ZXIgaWYgYW55IGJhc2ljIGNvZGVcblx0XHQvLyBwb2ludHMgd2VyZSBjb3BpZWQ7IHN0YXJ0IGF0IHRoZSBiZWdpbm5pbmcgb3RoZXJ3aXNlLlxuXG5cdFx0Zm9yIChpbmRleCA9IGJhc2ljID4gMCA/IGJhc2ljICsgMSA6IDA7IGluZGV4IDwgaW5wdXRMZW5ndGg7IC8qIG5vIGZpbmFsIGV4cHJlc3Npb24gKi8pIHtcblxuXHRcdFx0Ly8gYGluZGV4YCBpcyB0aGUgaW5kZXggb2YgdGhlIG5leHQgY2hhcmFjdGVyIHRvIGJlIGNvbnN1bWVkLlxuXHRcdFx0Ly8gRGVjb2RlIGEgZ2VuZXJhbGl6ZWQgdmFyaWFibGUtbGVuZ3RoIGludGVnZXIgaW50byBgZGVsdGFgLFxuXHRcdFx0Ly8gd2hpY2ggZ2V0cyBhZGRlZCB0byBgaWAuIFRoZSBvdmVyZmxvdyBjaGVja2luZyBpcyBlYXNpZXJcblx0XHRcdC8vIGlmIHdlIGluY3JlYXNlIGBpYCBhcyB3ZSBnbywgdGhlbiBzdWJ0cmFjdCBvZmYgaXRzIHN0YXJ0aW5nXG5cdFx0XHQvLyB2YWx1ZSBhdCB0aGUgZW5kIHRvIG9idGFpbiBgZGVsdGFgLlxuXHRcdFx0Zm9yIChvbGRpID0gaSwgdyA9IDEsIGsgPSBiYXNlOyAvKiBubyBjb25kaXRpb24gKi87IGsgKz0gYmFzZSkge1xuXG5cdFx0XHRcdGlmIChpbmRleCA+PSBpbnB1dExlbmd0aCkge1xuXHRcdFx0XHRcdGVycm9yKCdpbnZhbGlkLWlucHV0Jyk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRkaWdpdCA9IGJhc2ljVG9EaWdpdChpbnB1dC5jaGFyQ29kZUF0KGluZGV4KyspKTtcblxuXHRcdFx0XHRpZiAoZGlnaXQgPj0gYmFzZSB8fCBkaWdpdCA+IGZsb29yKChtYXhJbnQgLSBpKSAvIHcpKSB7XG5cdFx0XHRcdFx0ZXJyb3IoJ292ZXJmbG93Jyk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpICs9IGRpZ2l0ICogdztcblx0XHRcdFx0dCA9IGsgPD0gYmlhcyA/IHRNaW4gOiAoayA+PSBiaWFzICsgdE1heCA/IHRNYXggOiBrIC0gYmlhcyk7XG5cblx0XHRcdFx0aWYgKGRpZ2l0IDwgdCkge1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0YmFzZU1pbnVzVCA9IGJhc2UgLSB0O1xuXHRcdFx0XHRpZiAodyA+IGZsb29yKG1heEludCAvIGJhc2VNaW51c1QpKSB7XG5cdFx0XHRcdFx0ZXJyb3IoJ292ZXJmbG93Jyk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHR3ICo9IGJhc2VNaW51c1Q7XG5cblx0XHRcdH1cblxuXHRcdFx0b3V0ID0gb3V0cHV0Lmxlbmd0aCArIDE7XG5cdFx0XHRiaWFzID0gYWRhcHQoaSAtIG9sZGksIG91dCwgb2xkaSA9PSAwKTtcblxuXHRcdFx0Ly8gYGlgIHdhcyBzdXBwb3NlZCB0byB3cmFwIGFyb3VuZCBmcm9tIGBvdXRgIHRvIGAwYCxcblx0XHRcdC8vIGluY3JlbWVudGluZyBgbmAgZWFjaCB0aW1lLCBzbyB3ZSdsbCBmaXggdGhhdCBub3c6XG5cdFx0XHRpZiAoZmxvb3IoaSAvIG91dCkgPiBtYXhJbnQgLSBuKSB7XG5cdFx0XHRcdGVycm9yKCdvdmVyZmxvdycpO1xuXHRcdFx0fVxuXG5cdFx0XHRuICs9IGZsb29yKGkgLyBvdXQpO1xuXHRcdFx0aSAlPSBvdXQ7XG5cblx0XHRcdC8vIEluc2VydCBgbmAgYXQgcG9zaXRpb24gYGlgIG9mIHRoZSBvdXRwdXRcblx0XHRcdG91dHB1dC5zcGxpY2UoaSsrLCAwLCBuKTtcblxuXHRcdH1cblxuXHRcdHJldHVybiB1Y3MyZW5jb2RlKG91dHB1dCk7XG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgYSBzdHJpbmcgb2YgVW5pY29kZSBzeW1ib2xzIHRvIGEgUHVueWNvZGUgc3RyaW5nIG9mIEFTQ0lJLW9ubHlcblx0ICogc3ltYm9scy5cblx0ICogQG1lbWJlck9mIHB1bnljb2RlXG5cdCAqIEBwYXJhbSB7U3RyaW5nfSBpbnB1dCBUaGUgc3RyaW5nIG9mIFVuaWNvZGUgc3ltYm9scy5cblx0ICogQHJldHVybnMge1N0cmluZ30gVGhlIHJlc3VsdGluZyBQdW55Y29kZSBzdHJpbmcgb2YgQVNDSUktb25seSBzeW1ib2xzLlxuXHQgKi9cblx0ZnVuY3Rpb24gZW5jb2RlKGlucHV0KSB7XG5cdFx0dmFyIG4sXG5cdFx0ICAgIGRlbHRhLFxuXHRcdCAgICBoYW5kbGVkQ1BDb3VudCxcblx0XHQgICAgYmFzaWNMZW5ndGgsXG5cdFx0ICAgIGJpYXMsXG5cdFx0ICAgIGosXG5cdFx0ICAgIG0sXG5cdFx0ICAgIHEsXG5cdFx0ICAgIGssXG5cdFx0ICAgIHQsXG5cdFx0ICAgIGN1cnJlbnRWYWx1ZSxcblx0XHQgICAgb3V0cHV0ID0gW10sXG5cdFx0ICAgIC8qKiBgaW5wdXRMZW5ndGhgIHdpbGwgaG9sZCB0aGUgbnVtYmVyIG9mIGNvZGUgcG9pbnRzIGluIGBpbnB1dGAuICovXG5cdFx0ICAgIGlucHV0TGVuZ3RoLFxuXHRcdCAgICAvKiogQ2FjaGVkIGNhbGN1bGF0aW9uIHJlc3VsdHMgKi9cblx0XHQgICAgaGFuZGxlZENQQ291bnRQbHVzT25lLFxuXHRcdCAgICBiYXNlTWludXNULFxuXHRcdCAgICBxTWludXNUO1xuXG5cdFx0Ly8gQ29udmVydCB0aGUgaW5wdXQgaW4gVUNTLTIgdG8gVW5pY29kZVxuXHRcdGlucHV0ID0gdWNzMmRlY29kZShpbnB1dCk7XG5cblx0XHQvLyBDYWNoZSB0aGUgbGVuZ3RoXG5cdFx0aW5wdXRMZW5ndGggPSBpbnB1dC5sZW5ndGg7XG5cblx0XHQvLyBJbml0aWFsaXplIHRoZSBzdGF0ZVxuXHRcdG4gPSBpbml0aWFsTjtcblx0XHRkZWx0YSA9IDA7XG5cdFx0YmlhcyA9IGluaXRpYWxCaWFzO1xuXG5cdFx0Ly8gSGFuZGxlIHRoZSBiYXNpYyBjb2RlIHBvaW50c1xuXHRcdGZvciAoaiA9IDA7IGogPCBpbnB1dExlbmd0aDsgKytqKSB7XG5cdFx0XHRjdXJyZW50VmFsdWUgPSBpbnB1dFtqXTtcblx0XHRcdGlmIChjdXJyZW50VmFsdWUgPCAweDgwKSB7XG5cdFx0XHRcdG91dHB1dC5wdXNoKHN0cmluZ0Zyb21DaGFyQ29kZShjdXJyZW50VmFsdWUpKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRoYW5kbGVkQ1BDb3VudCA9IGJhc2ljTGVuZ3RoID0gb3V0cHV0Lmxlbmd0aDtcblxuXHRcdC8vIGBoYW5kbGVkQ1BDb3VudGAgaXMgdGhlIG51bWJlciBvZiBjb2RlIHBvaW50cyB0aGF0IGhhdmUgYmVlbiBoYW5kbGVkO1xuXHRcdC8vIGBiYXNpY0xlbmd0aGAgaXMgdGhlIG51bWJlciBvZiBiYXNpYyBjb2RlIHBvaW50cy5cblxuXHRcdC8vIEZpbmlzaCB0aGUgYmFzaWMgc3RyaW5nIC0gaWYgaXQgaXMgbm90IGVtcHR5IC0gd2l0aCBhIGRlbGltaXRlclxuXHRcdGlmIChiYXNpY0xlbmd0aCkge1xuXHRcdFx0b3V0cHV0LnB1c2goZGVsaW1pdGVyKTtcblx0XHR9XG5cblx0XHQvLyBNYWluIGVuY29kaW5nIGxvb3A6XG5cdFx0d2hpbGUgKGhhbmRsZWRDUENvdW50IDwgaW5wdXRMZW5ndGgpIHtcblxuXHRcdFx0Ly8gQWxsIG5vbi1iYXNpYyBjb2RlIHBvaW50cyA8IG4gaGF2ZSBiZWVuIGhhbmRsZWQgYWxyZWFkeS4gRmluZCB0aGUgbmV4dFxuXHRcdFx0Ly8gbGFyZ2VyIG9uZTpcblx0XHRcdGZvciAobSA9IG1heEludCwgaiA9IDA7IGogPCBpbnB1dExlbmd0aDsgKytqKSB7XG5cdFx0XHRcdGN1cnJlbnRWYWx1ZSA9IGlucHV0W2pdO1xuXHRcdFx0XHRpZiAoY3VycmVudFZhbHVlID49IG4gJiYgY3VycmVudFZhbHVlIDwgbSkge1xuXHRcdFx0XHRcdG0gPSBjdXJyZW50VmFsdWU7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gSW5jcmVhc2UgYGRlbHRhYCBlbm91Z2ggdG8gYWR2YW5jZSB0aGUgZGVjb2RlcidzIDxuLGk+IHN0YXRlIHRvIDxtLDA+LFxuXHRcdFx0Ly8gYnV0IGd1YXJkIGFnYWluc3Qgb3ZlcmZsb3dcblx0XHRcdGhhbmRsZWRDUENvdW50UGx1c09uZSA9IGhhbmRsZWRDUENvdW50ICsgMTtcblx0XHRcdGlmIChtIC0gbiA+IGZsb29yKChtYXhJbnQgLSBkZWx0YSkgLyBoYW5kbGVkQ1BDb3VudFBsdXNPbmUpKSB7XG5cdFx0XHRcdGVycm9yKCdvdmVyZmxvdycpO1xuXHRcdFx0fVxuXG5cdFx0XHRkZWx0YSArPSAobSAtIG4pICogaGFuZGxlZENQQ291bnRQbHVzT25lO1xuXHRcdFx0biA9IG07XG5cblx0XHRcdGZvciAoaiA9IDA7IGogPCBpbnB1dExlbmd0aDsgKytqKSB7XG5cdFx0XHRcdGN1cnJlbnRWYWx1ZSA9IGlucHV0W2pdO1xuXG5cdFx0XHRcdGlmIChjdXJyZW50VmFsdWUgPCBuICYmICsrZGVsdGEgPiBtYXhJbnQpIHtcblx0XHRcdFx0XHRlcnJvcignb3ZlcmZsb3cnKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmIChjdXJyZW50VmFsdWUgPT0gbikge1xuXHRcdFx0XHRcdC8vIFJlcHJlc2VudCBkZWx0YSBhcyBhIGdlbmVyYWxpemVkIHZhcmlhYmxlLWxlbmd0aCBpbnRlZ2VyXG5cdFx0XHRcdFx0Zm9yIChxID0gZGVsdGEsIGsgPSBiYXNlOyAvKiBubyBjb25kaXRpb24gKi87IGsgKz0gYmFzZSkge1xuXHRcdFx0XHRcdFx0dCA9IGsgPD0gYmlhcyA/IHRNaW4gOiAoayA+PSBiaWFzICsgdE1heCA/IHRNYXggOiBrIC0gYmlhcyk7XG5cdFx0XHRcdFx0XHRpZiAocSA8IHQpIHtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRxTWludXNUID0gcSAtIHQ7XG5cdFx0XHRcdFx0XHRiYXNlTWludXNUID0gYmFzZSAtIHQ7XG5cdFx0XHRcdFx0XHRvdXRwdXQucHVzaChcblx0XHRcdFx0XHRcdFx0c3RyaW5nRnJvbUNoYXJDb2RlKGRpZ2l0VG9CYXNpYyh0ICsgcU1pbnVzVCAlIGJhc2VNaW51c1QsIDApKVxuXHRcdFx0XHRcdFx0KTtcblx0XHRcdFx0XHRcdHEgPSBmbG9vcihxTWludXNUIC8gYmFzZU1pbnVzVCk7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0b3V0cHV0LnB1c2goc3RyaW5nRnJvbUNoYXJDb2RlKGRpZ2l0VG9CYXNpYyhxLCAwKSkpO1xuXHRcdFx0XHRcdGJpYXMgPSBhZGFwdChkZWx0YSwgaGFuZGxlZENQQ291bnRQbHVzT25lLCBoYW5kbGVkQ1BDb3VudCA9PSBiYXNpY0xlbmd0aCk7XG5cdFx0XHRcdFx0ZGVsdGEgPSAwO1xuXHRcdFx0XHRcdCsraGFuZGxlZENQQ291bnQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0KytkZWx0YTtcblx0XHRcdCsrbjtcblxuXHRcdH1cblx0XHRyZXR1cm4gb3V0cHV0LmpvaW4oJycpO1xuXHR9XG5cblx0LyoqXG5cdCAqIENvbnZlcnRzIGEgUHVueWNvZGUgc3RyaW5nIHJlcHJlc2VudGluZyBhIGRvbWFpbiBuYW1lIHRvIFVuaWNvZGUuIE9ubHkgdGhlXG5cdCAqIFB1bnljb2RlZCBwYXJ0cyBvZiB0aGUgZG9tYWluIG5hbWUgd2lsbCBiZSBjb252ZXJ0ZWQsIGkuZS4gaXQgZG9lc24ndFxuXHQgKiBtYXR0ZXIgaWYgeW91IGNhbGwgaXQgb24gYSBzdHJpbmcgdGhhdCBoYXMgYWxyZWFkeSBiZWVuIGNvbnZlcnRlZCB0b1xuXHQgKiBVbmljb2RlLlxuXHQgKiBAbWVtYmVyT2YgcHVueWNvZGVcblx0ICogQHBhcmFtIHtTdHJpbmd9IGRvbWFpbiBUaGUgUHVueWNvZGUgZG9tYWluIG5hbWUgdG8gY29udmVydCB0byBVbmljb2RlLlxuXHQgKiBAcmV0dXJucyB7U3RyaW5nfSBUaGUgVW5pY29kZSByZXByZXNlbnRhdGlvbiBvZiB0aGUgZ2l2ZW4gUHVueWNvZGVcblx0ICogc3RyaW5nLlxuXHQgKi9cblx0ZnVuY3Rpb24gdG9Vbmljb2RlKGRvbWFpbikge1xuXHRcdHJldHVybiBtYXBEb21haW4oZG9tYWluLCBmdW5jdGlvbihzdHJpbmcpIHtcblx0XHRcdHJldHVybiByZWdleFB1bnljb2RlLnRlc3Qoc3RyaW5nKVxuXHRcdFx0XHQ/IGRlY29kZShzdHJpbmcuc2xpY2UoNCkudG9Mb3dlckNhc2UoKSlcblx0XHRcdFx0OiBzdHJpbmc7XG5cdFx0fSk7XG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgYSBVbmljb2RlIHN0cmluZyByZXByZXNlbnRpbmcgYSBkb21haW4gbmFtZSB0byBQdW55Y29kZS4gT25seSB0aGVcblx0ICogbm9uLUFTQ0lJIHBhcnRzIG9mIHRoZSBkb21haW4gbmFtZSB3aWxsIGJlIGNvbnZlcnRlZCwgaS5lLiBpdCBkb2Vzbid0XG5cdCAqIG1hdHRlciBpZiB5b3UgY2FsbCBpdCB3aXRoIGEgZG9tYWluIHRoYXQncyBhbHJlYWR5IGluIEFTQ0lJLlxuXHQgKiBAbWVtYmVyT2YgcHVueWNvZGVcblx0ICogQHBhcmFtIHtTdHJpbmd9IGRvbWFpbiBUaGUgZG9tYWluIG5hbWUgdG8gY29udmVydCwgYXMgYSBVbmljb2RlIHN0cmluZy5cblx0ICogQHJldHVybnMge1N0cmluZ30gVGhlIFB1bnljb2RlIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBnaXZlbiBkb21haW4gbmFtZS5cblx0ICovXG5cdGZ1bmN0aW9uIHRvQVNDSUkoZG9tYWluKSB7XG5cdFx0cmV0dXJuIG1hcERvbWFpbihkb21haW4sIGZ1bmN0aW9uKHN0cmluZykge1xuXHRcdFx0cmV0dXJuIHJlZ2V4Tm9uQVNDSUkudGVzdChzdHJpbmcpXG5cdFx0XHRcdD8gJ3huLS0nICsgZW5jb2RlKHN0cmluZylcblx0XHRcdFx0OiBzdHJpbmc7XG5cdFx0fSk7XG5cdH1cblxuXHQvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuXHQvKiogRGVmaW5lIHRoZSBwdWJsaWMgQVBJICovXG5cdHB1bnljb2RlID0ge1xuXHRcdC8qKlxuXHRcdCAqIEEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY3VycmVudCBQdW55Y29kZS5qcyB2ZXJzaW9uIG51bWJlci5cblx0XHQgKiBAbWVtYmVyT2YgcHVueWNvZGVcblx0XHQgKiBAdHlwZSBTdHJpbmdcblx0XHQgKi9cblx0XHQndmVyc2lvbic6ICcxLjIuMycsXG5cdFx0LyoqXG5cdFx0ICogQW4gb2JqZWN0IG9mIG1ldGhvZHMgdG8gY29udmVydCBmcm9tIEphdmFTY3JpcHQncyBpbnRlcm5hbCBjaGFyYWN0ZXJcblx0XHQgKiByZXByZXNlbnRhdGlvbiAoVUNTLTIpIHRvIFVuaWNvZGUgY29kZSBwb2ludHMsIGFuZCBiYWNrLlxuXHRcdCAqIEBzZWUgPGh0dHA6Ly9tYXRoaWFzYnluZW5zLmJlL25vdGVzL2phdmFzY3JpcHQtZW5jb2Rpbmc+XG5cdFx0ICogQG1lbWJlck9mIHB1bnljb2RlXG5cdFx0ICogQHR5cGUgT2JqZWN0XG5cdFx0ICovXG5cdFx0J3VjczInOiB7XG5cdFx0XHQnZGVjb2RlJzogdWNzMmRlY29kZSxcblx0XHRcdCdlbmNvZGUnOiB1Y3MyZW5jb2RlXG5cdFx0fSxcblx0XHQnZGVjb2RlJzogZGVjb2RlLFxuXHRcdCdlbmNvZGUnOiBlbmNvZGUsXG5cdFx0J3RvQVNDSUknOiB0b0FTQ0lJLFxuXHRcdCd0b1VuaWNvZGUnOiB0b1VuaWNvZGVcblx0fTtcblxuXHQvKiogRXhwb3NlIGBwdW55Y29kZWAgKi9cblx0Ly8gU29tZSBBTUQgYnVpbGQgb3B0aW1pemVycywgbGlrZSByLmpzLCBjaGVjayBmb3Igc3BlY2lmaWMgY29uZGl0aW9uIHBhdHRlcm5zXG5cdC8vIGxpa2UgdGhlIGZvbGxvd2luZzpcblx0aWYgKFxuXHRcdHR5cGVvZiBkZWZpbmUgPT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdHR5cGVvZiBkZWZpbmUuYW1kID09ICdvYmplY3QnICYmXG5cdFx0ZGVmaW5lLmFtZFxuXHQpIHtcblx0XHRkZWZpbmUoZnVuY3Rpb24oKSB7XG5cdFx0XHRyZXR1cm4gcHVueWNvZGU7XG5cdFx0fSk7XG5cdH1cdGVsc2UgaWYgKGZyZWVFeHBvcnRzICYmICFmcmVlRXhwb3J0cy5ub2RlVHlwZSkge1xuXHRcdGlmIChmcmVlTW9kdWxlKSB7IC8vIGluIE5vZGUuanMgb3IgUmluZ29KUyB2MC44LjArXG5cdFx0XHRmcmVlTW9kdWxlLmV4cG9ydHMgPSBwdW55Y29kZTtcblx0XHR9IGVsc2UgeyAvLyBpbiBOYXJ3aGFsIG9yIFJpbmdvSlMgdjAuNy4wLVxuXHRcdFx0Zm9yIChrZXkgaW4gcHVueWNvZGUpIHtcblx0XHRcdFx0cHVueWNvZGUuaGFzT3duUHJvcGVydHkoa2V5KSAmJiAoZnJlZUV4cG9ydHNba2V5XSA9IHB1bnljb2RlW2tleV0pO1xuXHRcdFx0fVxuXHRcdH1cblx0fSBlbHNlIHsgLy8gaW4gUmhpbm8gb3IgYSB3ZWIgYnJvd3NlclxuXHRcdHJvb3QucHVueWNvZGUgPSBwdW55Y29kZTtcblx0fVxuXG59KHRoaXMpKTtcbiIsIid1c2Ugc3RyaWN0JztcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywge1xuICB2YWx1ZTogdHJ1ZVxufSk7XG5cbmZ1bmN0aW9uIF9pbnRlcm9wUmVxdWlyZURlZmF1bHQob2JqKSB7IHJldHVybiBvYmogJiYgb2JqLl9fZXNNb2R1bGUgPyBvYmogOiB7ICdkZWZhdWx0Jzogb2JqIH07IH1cblxudmFyIF9oZWxwZXJzRXZlbnQgPSByZXF1aXJlKCcuL2hlbHBlcnMvZXZlbnQnKTtcblxudmFyIF9oZWxwZXJzRXZlbnQyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfaGVscGVyc0V2ZW50KTtcblxudmFyIF9oZWxwZXJzTWVzc2FnZUV2ZW50ID0gcmVxdWlyZSgnLi9oZWxwZXJzL21lc3NhZ2UtZXZlbnQnKTtcblxudmFyIF9oZWxwZXJzTWVzc2FnZUV2ZW50MiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX2hlbHBlcnNNZXNzYWdlRXZlbnQpO1xuXG52YXIgX2hlbHBlcnNDbG9zZUV2ZW50ID0gcmVxdWlyZSgnLi9oZWxwZXJzL2Nsb3NlLWV2ZW50Jyk7XG5cbnZhciBfaGVscGVyc0Nsb3NlRXZlbnQyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfaGVscGVyc0Nsb3NlRXZlbnQpO1xuXG4vKlxuKiBDcmVhdGVzIGFuIEV2ZW50IG9iamVjdCBhbmQgZXh0ZW5kcyBpdCB0byBhbGxvdyBmdWxsIG1vZGlmaWNhdGlvbiBvZlxuKiBpdHMgcHJvcGVydGllcy5cbipcbiogQHBhcmFtIHtvYmplY3R9IGNvbmZpZyAtIHdpdGhpbiBjb25maWcgeW91IHdpbGwgbmVlZCB0byBwYXNzIHR5cGUgYW5kIG9wdGlvbmFsbHkgdGFyZ2V0XG4qL1xuZnVuY3Rpb24gY3JlYXRlRXZlbnQoY29uZmlnKSB7XG4gIHZhciB0eXBlID0gY29uZmlnLnR5cGU7XG4gIHZhciB0YXJnZXQgPSBjb25maWcudGFyZ2V0O1xuXG4gIHZhciBldmVudE9iamVjdCA9IG5ldyBfaGVscGVyc0V2ZW50MlsnZGVmYXVsdCddKHR5cGUpO1xuXG4gIGlmICh0YXJnZXQpIHtcbiAgICBldmVudE9iamVjdC50YXJnZXQgPSB0YXJnZXQ7XG4gICAgZXZlbnRPYmplY3Quc3JjRWxlbWVudCA9IHRhcmdldDtcbiAgICBldmVudE9iamVjdC5jdXJyZW50VGFyZ2V0ID0gdGFyZ2V0O1xuICB9XG5cbiAgcmV0dXJuIGV2ZW50T2JqZWN0O1xufVxuXG4vKlxuKiBDcmVhdGVzIGEgTWVzc2FnZUV2ZW50IG9iamVjdCBhbmQgZXh0ZW5kcyBpdCB0byBhbGxvdyBmdWxsIG1vZGlmaWNhdGlvbiBvZlxuKiBpdHMgcHJvcGVydGllcy5cbipcbiogQHBhcmFtIHtvYmplY3R9IGNvbmZpZyAtIHdpdGhpbiBjb25maWcgeW91IHdpbGwgbmVlZCB0byBwYXNzIHR5cGUsIG9yaWdpbiwgZGF0YSBhbmQgb3B0aW9uYWxseSB0YXJnZXRcbiovXG5mdW5jdGlvbiBjcmVhdGVNZXNzYWdlRXZlbnQoY29uZmlnKSB7XG4gIHZhciB0eXBlID0gY29uZmlnLnR5cGU7XG4gIHZhciBvcmlnaW4gPSBjb25maWcub3JpZ2luO1xuICB2YXIgZGF0YSA9IGNvbmZpZy5kYXRhO1xuICB2YXIgdGFyZ2V0ID0gY29uZmlnLnRhcmdldDtcblxuICB2YXIgbWVzc2FnZUV2ZW50ID0gbmV3IF9oZWxwZXJzTWVzc2FnZUV2ZW50MlsnZGVmYXVsdCddKHR5cGUsIHtcbiAgICBkYXRhOiBkYXRhLFxuICAgIG9yaWdpbjogb3JpZ2luXG4gIH0pO1xuXG4gIGlmICh0YXJnZXQpIHtcbiAgICBtZXNzYWdlRXZlbnQudGFyZ2V0ID0gdGFyZ2V0O1xuICAgIG1lc3NhZ2VFdmVudC5zcmNFbGVtZW50ID0gdGFyZ2V0O1xuICAgIG1lc3NhZ2VFdmVudC5jdXJyZW50VGFyZ2V0ID0gdGFyZ2V0O1xuICB9XG5cbiAgcmV0dXJuIG1lc3NhZ2VFdmVudDtcbn1cblxuLypcbiogQ3JlYXRlcyBhIENsb3NlRXZlbnQgb2JqZWN0IGFuZCBleHRlbmRzIGl0IHRvIGFsbG93IGZ1bGwgbW9kaWZpY2F0aW9uIG9mXG4qIGl0cyBwcm9wZXJ0aWVzLlxuKlxuKiBAcGFyYW0ge29iamVjdH0gY29uZmlnIC0gd2l0aGluIGNvbmZpZyB5b3Ugd2lsbCBuZWVkIHRvIHBhc3MgdHlwZSBhbmQgb3B0aW9uYWxseSB0YXJnZXQsIGNvZGUsIGFuZCByZWFzb25cbiovXG5mdW5jdGlvbiBjcmVhdGVDbG9zZUV2ZW50KGNvbmZpZykge1xuICB2YXIgY29kZSA9IGNvbmZpZy5jb2RlO1xuICB2YXIgcmVhc29uID0gY29uZmlnLnJlYXNvbjtcbiAgdmFyIHR5cGUgPSBjb25maWcudHlwZTtcbiAgdmFyIHRhcmdldCA9IGNvbmZpZy50YXJnZXQ7XG4gIHZhciB3YXNDbGVhbiA9IGNvbmZpZy53YXNDbGVhbjtcblxuICBpZiAoIXdhc0NsZWFuKSB7XG4gICAgd2FzQ2xlYW4gPSBjb2RlID09PSAxMDAwO1xuICB9XG5cbiAgdmFyIGNsb3NlRXZlbnQgPSBuZXcgX2hlbHBlcnNDbG9zZUV2ZW50MlsnZGVmYXVsdCddKHR5cGUsIHtcbiAgICBjb2RlOiBjb2RlLFxuICAgIHJlYXNvbjogcmVhc29uLFxuICAgIHdhc0NsZWFuOiB3YXNDbGVhblxuICB9KTtcblxuICBpZiAodGFyZ2V0KSB7XG4gICAgY2xvc2VFdmVudC50YXJnZXQgPSB0YXJnZXQ7XG4gICAgY2xvc2VFdmVudC5zcmNFbGVtZW50ID0gdGFyZ2V0O1xuICAgIGNsb3NlRXZlbnQuY3VycmVudFRhcmdldCA9IHRhcmdldDtcbiAgfVxuXG4gIHJldHVybiBjbG9zZUV2ZW50O1xufVxuXG5leHBvcnRzLmNyZWF0ZUV2ZW50ID0gY3JlYXRlRXZlbnQ7XG5leHBvcnRzLmNyZWF0ZU1lc3NhZ2VFdmVudCA9IGNyZWF0ZU1lc3NhZ2VFdmVudDtcbmV4cG9ydHMuY3JlYXRlQ2xvc2VFdmVudCA9IGNyZWF0ZUNsb3NlRXZlbnQ7IiwiJ3VzZSBzdHJpY3QnO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxudmFyIF9jcmVhdGVDbGFzcyA9IChmdW5jdGlvbiAoKSB7IGZ1bmN0aW9uIGRlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoJ3ZhbHVlJyBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH0gcmV0dXJuIGZ1bmN0aW9uIChDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfTsgfSkoKTtcblxuZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgeyBpZiAoIShpbnN0YW5jZSBpbnN0YW5jZW9mIENvbnN0cnVjdG9yKSkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKCdDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb24nKTsgfSB9XG5cbnZhciBfaGVscGVyc0FycmF5SGVscGVycyA9IHJlcXVpcmUoJy4vaGVscGVycy9hcnJheS1oZWxwZXJzJyk7XG5cbi8qXG4qIEV2ZW50VGFyZ2V0IGlzIGFuIGludGVyZmFjZSBpbXBsZW1lbnRlZCBieSBvYmplY3RzIHRoYXQgY2FuXG4qIHJlY2VpdmUgZXZlbnRzIGFuZCBtYXkgaGF2ZSBsaXN0ZW5lcnMgZm9yIHRoZW0uXG4qXG4qIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9FdmVudFRhcmdldFxuKi9cblxudmFyIEV2ZW50VGFyZ2V0ID0gKGZ1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gRXZlbnRUYXJnZXQoKSB7XG4gICAgX2NsYXNzQ2FsbENoZWNrKHRoaXMsIEV2ZW50VGFyZ2V0KTtcblxuICAgIHRoaXMubGlzdGVuZXJzID0ge307XG4gIH1cblxuICAvKlxuICAqIFRpZXMgYSBsaXN0ZW5lciBmdW5jdGlvbiB0byBhIGV2ZW50IHR5cGUgd2hpY2ggY2FuIGxhdGVyIGJlIGludm9rZWQgdmlhIHRoZVxuICAqIGRpc3BhdGNoRXZlbnQgbWV0aG9kLlxuICAqXG4gICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSB0aGUgdHlwZSBvZiBldmVudCAoaWU6ICdvcGVuJywgJ21lc3NhZ2UnLCBldGMuKVxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IGxpc3RlbmVyIC0gdGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGludm9rZSB3aGVuZXZlciBhIGV2ZW50IGlzIGRpc3BhdGNoZWQgbWF0Y2hpbmcgdGhlIGdpdmVuIHR5cGVcbiAgKiBAcGFyYW0ge2Jvb2xlYW59IHVzZUNhcHR1cmUgLSBOL0EgVE9ETzogaW1wbGVtZW50IHVzZUNhcHR1cmUgZnVuY3Rpb25hbGl0eVxuICAqL1xuXG4gIF9jcmVhdGVDbGFzcyhFdmVudFRhcmdldCwgW3tcbiAgICBrZXk6ICdhZGRFdmVudExpc3RlbmVyJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gYWRkRXZlbnRMaXN0ZW5lcih0eXBlLCBsaXN0ZW5lciAvKiAsIHVzZUNhcHR1cmUgKi8pIHtcbiAgICAgIGlmICh0eXBlb2YgbGlzdGVuZXIgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgaWYgKCFBcnJheS5pc0FycmF5KHRoaXMubGlzdGVuZXJzW3R5cGVdKSkge1xuICAgICAgICAgIHRoaXMubGlzdGVuZXJzW3R5cGVdID0gW107XG4gICAgICAgIH1cblxuICAgICAgICAvLyBPbmx5IGFkZCB0aGUgc2FtZSBmdW5jdGlvbiBvbmNlXG4gICAgICAgIGlmICgoMCwgX2hlbHBlcnNBcnJheUhlbHBlcnMuZmlsdGVyKSh0aGlzLmxpc3RlbmVyc1t0eXBlXSwgZnVuY3Rpb24gKGl0ZW0pIHtcbiAgICAgICAgICByZXR1cm4gaXRlbSA9PT0gbGlzdGVuZXI7XG4gICAgICAgIH0pLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIHRoaXMubGlzdGVuZXJzW3R5cGVdLnB1c2gobGlzdGVuZXIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLypcbiAgICAqIFJlbW92ZXMgdGhlIGxpc3RlbmVyIHNvIGl0IHdpbGwgbm8gbG9uZ2VyIGJlIGludm9rZWQgdmlhIHRoZSBkaXNwYXRjaEV2ZW50IG1ldGhvZC5cbiAgICAqXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIHRoZSB0eXBlIG9mIGV2ZW50IChpZTogJ29wZW4nLCAnbWVzc2FnZScsIGV0Yy4pXG4gICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBsaXN0ZW5lciAtIHRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBpbnZva2Ugd2hlbmV2ZXIgYSBldmVudCBpcyBkaXNwYXRjaGVkIG1hdGNoaW5nIHRoZSBnaXZlbiB0eXBlXG4gICAgKiBAcGFyYW0ge2Jvb2xlYW59IHVzZUNhcHR1cmUgLSBOL0EgVE9ETzogaW1wbGVtZW50IHVzZUNhcHR1cmUgZnVuY3Rpb25hbGl0eVxuICAgICovXG4gIH0sIHtcbiAgICBrZXk6ICdyZW1vdmVFdmVudExpc3RlbmVyJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gcmVtb3ZlRXZlbnRMaXN0ZW5lcih0eXBlLCByZW1vdmluZ0xpc3RlbmVyIC8qICwgdXNlQ2FwdHVyZSAqLykge1xuICAgICAgdmFyIGFycmF5T2ZMaXN0ZW5lcnMgPSB0aGlzLmxpc3RlbmVyc1t0eXBlXTtcbiAgICAgIHRoaXMubGlzdGVuZXJzW3R5cGVdID0gKDAsIF9oZWxwZXJzQXJyYXlIZWxwZXJzLnJlamVjdCkoYXJyYXlPZkxpc3RlbmVycywgZnVuY3Rpb24gKGxpc3RlbmVyKSB7XG4gICAgICAgIHJldHVybiBsaXN0ZW5lciA9PT0gcmVtb3ZpbmdMaXN0ZW5lcjtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qXG4gICAgKiBJbnZva2VzIGFsbCBsaXN0ZW5lciBmdW5jdGlvbnMgdGhhdCBhcmUgbGlzdGVuaW5nIHRvIHRoZSBnaXZlbiBldmVudC50eXBlIHByb3BlcnR5LiBFYWNoXG4gICAgKiBsaXN0ZW5lciB3aWxsIGJlIHBhc3NlZCB0aGUgZXZlbnQgYXMgdGhlIGZpcnN0IGFyZ3VtZW50LlxuICAgICpcbiAgICAqIEBwYXJhbSB7b2JqZWN0fSBldmVudCAtIGV2ZW50IG9iamVjdCB3aGljaCB3aWxsIGJlIHBhc3NlZCB0byBhbGwgbGlzdGVuZXJzIG9mIHRoZSBldmVudC50eXBlIHByb3BlcnR5XG4gICAgKi9cbiAgfSwge1xuICAgIGtleTogJ2Rpc3BhdGNoRXZlbnQnLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBkaXNwYXRjaEV2ZW50KGV2ZW50KSB7XG4gICAgICB2YXIgX3RoaXMgPSB0aGlzO1xuXG4gICAgICBmb3IgKHZhciBfbGVuID0gYXJndW1lbnRzLmxlbmd0aCwgY3VzdG9tQXJndW1lbnRzID0gQXJyYXkoX2xlbiA+IDEgPyBfbGVuIC0gMSA6IDApLCBfa2V5ID0gMTsgX2tleSA8IF9sZW47IF9rZXkrKykge1xuICAgICAgICBjdXN0b21Bcmd1bWVudHNbX2tleSAtIDFdID0gYXJndW1lbnRzW19rZXldO1xuICAgICAgfVxuXG4gICAgICB2YXIgZXZlbnROYW1lID0gZXZlbnQudHlwZTtcbiAgICAgIHZhciBsaXN0ZW5lcnMgPSB0aGlzLmxpc3RlbmVyc1tldmVudE5hbWVdO1xuXG4gICAgICBpZiAoIUFycmF5LmlzQXJyYXkobGlzdGVuZXJzKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIGxpc3RlbmVycy5mb3JFYWNoKGZ1bmN0aW9uIChsaXN0ZW5lcikge1xuICAgICAgICBpZiAoY3VzdG9tQXJndW1lbnRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBsaXN0ZW5lci5hcHBseShfdGhpcywgY3VzdG9tQXJndW1lbnRzKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsaXN0ZW5lci5jYWxsKF90aGlzLCBldmVudCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgfV0pO1xuXG4gIHJldHVybiBFdmVudFRhcmdldDtcbn0pKCk7XG5cbmV4cG9ydHNbJ2RlZmF1bHQnXSA9IEV2ZW50VGFyZ2V0O1xubW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzWydkZWZhdWx0J107IiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwge1xuICB2YWx1ZTogdHJ1ZVxufSk7XG5leHBvcnRzLnJlamVjdCA9IHJlamVjdDtcbmV4cG9ydHMuZmlsdGVyID0gZmlsdGVyO1xuXG5mdW5jdGlvbiByZWplY3QoYXJyYXksIGNhbGxiYWNrKSB7XG4gIHZhciByZXN1bHRzID0gW107XG4gIGFycmF5LmZvckVhY2goZnVuY3Rpb24gKGl0ZW1JbkFycmF5KSB7XG4gICAgaWYgKCFjYWxsYmFjayhpdGVtSW5BcnJheSkpIHtcbiAgICAgIHJlc3VsdHMucHVzaChpdGVtSW5BcnJheSk7XG4gICAgfVxuICB9KTtcblxuICByZXR1cm4gcmVzdWx0cztcbn1cblxuZnVuY3Rpb24gZmlsdGVyKGFycmF5LCBjYWxsYmFjaykge1xuICB2YXIgcmVzdWx0cyA9IFtdO1xuICBhcnJheS5mb3JFYWNoKGZ1bmN0aW9uIChpdGVtSW5BcnJheSkge1xuICAgIGlmIChjYWxsYmFjayhpdGVtSW5BcnJheSkpIHtcbiAgICAgIHJlc3VsdHMucHVzaChpdGVtSW5BcnJheSk7XG4gICAgfVxuICB9KTtcblxuICByZXR1cm4gcmVzdWx0cztcbn0iLCIvKlxuKiBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9BUEkvQ2xvc2VFdmVudFxuKi9cblwidXNlIHN0cmljdFwiO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHtcbiAgdmFsdWU6IHRydWVcbn0pO1xudmFyIGNvZGVzID0ge1xuICBDTE9TRV9OT1JNQUw6IDEwMDAsXG4gIENMT1NFX0dPSU5HX0FXQVk6IDEwMDEsXG4gIENMT1NFX1BST1RPQ09MX0VSUk9SOiAxMDAyLFxuICBDTE9TRV9VTlNVUFBPUlRFRDogMTAwMyxcbiAgQ0xPU0VfTk9fU1RBVFVTOiAxMDA1LFxuICBDTE9TRV9BQk5PUk1BTDogMTAwNixcbiAgQ0xPU0VfVE9PX0xBUkdFOiAxMDA5XG59O1xuXG5leHBvcnRzW1wiZGVmYXVsdFwiXSA9IGNvZGVzO1xubW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzW1wiZGVmYXVsdFwiXTsiLCIndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHtcbiAgdmFsdWU6IHRydWVcbn0pO1xuXG52YXIgX2dldCA9IGZ1bmN0aW9uIGdldChfeDIsIF94MywgX3g0KSB7IHZhciBfYWdhaW4gPSB0cnVlOyBfZnVuY3Rpb246IHdoaWxlIChfYWdhaW4pIHsgdmFyIG9iamVjdCA9IF94MiwgcHJvcGVydHkgPSBfeDMsIHJlY2VpdmVyID0gX3g0OyBfYWdhaW4gPSBmYWxzZTsgaWYgKG9iamVjdCA9PT0gbnVsbCkgb2JqZWN0ID0gRnVuY3Rpb24ucHJvdG90eXBlOyB2YXIgZGVzYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iob2JqZWN0LCBwcm9wZXJ0eSk7IGlmIChkZXNjID09PSB1bmRlZmluZWQpIHsgdmFyIHBhcmVudCA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihvYmplY3QpOyBpZiAocGFyZW50ID09PSBudWxsKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gZWxzZSB7IF94MiA9IHBhcmVudDsgX3gzID0gcHJvcGVydHk7IF94NCA9IHJlY2VpdmVyOyBfYWdhaW4gPSB0cnVlOyBkZXNjID0gcGFyZW50ID0gdW5kZWZpbmVkOyBjb250aW51ZSBfZnVuY3Rpb247IH0gfSBlbHNlIGlmICgndmFsdWUnIGluIGRlc2MpIHsgcmV0dXJuIGRlc2MudmFsdWU7IH0gZWxzZSB7IHZhciBnZXR0ZXIgPSBkZXNjLmdldDsgaWYgKGdldHRlciA9PT0gdW5kZWZpbmVkKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gcmV0dXJuIGdldHRlci5jYWxsKHJlY2VpdmVyKTsgfSB9IH07XG5cbmZ1bmN0aW9uIF9pbnRlcm9wUmVxdWlyZURlZmF1bHQob2JqKSB7IHJldHVybiBvYmogJiYgb2JqLl9fZXNNb2R1bGUgPyBvYmogOiB7ICdkZWZhdWx0Jzogb2JqIH07IH1cblxuZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgeyBpZiAoIShpbnN0YW5jZSBpbnN0YW5jZW9mIENvbnN0cnVjdG9yKSkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKCdDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb24nKTsgfSB9XG5cbmZ1bmN0aW9uIF9pbmhlcml0cyhzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBpZiAodHlwZW9mIHN1cGVyQ2xhc3MgIT09ICdmdW5jdGlvbicgJiYgc3VwZXJDbGFzcyAhPT0gbnVsbCkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKCdTdXBlciBleHByZXNzaW9uIG11c3QgZWl0aGVyIGJlIG51bGwgb3IgYSBmdW5jdGlvbiwgbm90ICcgKyB0eXBlb2Ygc3VwZXJDbGFzcyk7IH0gc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzICYmIHN1cGVyQ2xhc3MucHJvdG90eXBlLCB7IGNvbnN0cnVjdG9yOiB7IHZhbHVlOiBzdWJDbGFzcywgZW51bWVyYWJsZTogZmFsc2UsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSB9KTsgaWYgKHN1cGVyQ2xhc3MpIE9iamVjdC5zZXRQcm90b3R5cGVPZiA/IE9iamVjdC5zZXRQcm90b3R5cGVPZihzdWJDbGFzcywgc3VwZXJDbGFzcykgOiBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbnZhciBfZXZlbnRQcm90b3R5cGUgPSByZXF1aXJlKCcuL2V2ZW50LXByb3RvdHlwZScpO1xuXG52YXIgX2V2ZW50UHJvdG90eXBlMiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX2V2ZW50UHJvdG90eXBlKTtcblxudmFyIENsb3NlRXZlbnQgPSAoZnVuY3Rpb24gKF9FdmVudFByb3RvdHlwZSkge1xuICBfaW5oZXJpdHMoQ2xvc2VFdmVudCwgX0V2ZW50UHJvdG90eXBlKTtcblxuICBmdW5jdGlvbiBDbG9zZUV2ZW50KHR5cGUpIHtcbiAgICB2YXIgZXZlbnRJbml0Q29uZmlnID0gYXJndW1lbnRzLmxlbmd0aCA8PSAxIHx8IGFyZ3VtZW50c1sxXSA9PT0gdW5kZWZpbmVkID8ge30gOiBhcmd1bWVudHNbMV07XG5cbiAgICBfY2xhc3NDYWxsQ2hlY2sodGhpcywgQ2xvc2VFdmVudCk7XG5cbiAgICBfZ2V0KE9iamVjdC5nZXRQcm90b3R5cGVPZihDbG9zZUV2ZW50LnByb3RvdHlwZSksICdjb25zdHJ1Y3RvcicsIHRoaXMpLmNhbGwodGhpcyk7XG5cbiAgICBpZiAoIXR5cGUpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0ZhaWxlZCB0byBjb25zdHJ1Y3QgXFwnQ2xvc2VFdmVudFxcJzogMSBhcmd1bWVudCByZXF1aXJlZCwgYnV0IG9ubHkgMCBwcmVzZW50LicpO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgZXZlbnRJbml0Q29uZmlnICE9PSAnb2JqZWN0Jykge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignRmFpbGVkIHRvIGNvbnN0cnVjdCBcXCdDbG9zZUV2ZW50XFwnOiBwYXJhbWV0ZXIgMiAoXFwnZXZlbnRJbml0RGljdFxcJykgaXMgbm90IGFuIG9iamVjdCcpO1xuICAgIH1cblxuICAgIHZhciBidWJibGVzID0gZXZlbnRJbml0Q29uZmlnLmJ1YmJsZXM7XG4gICAgdmFyIGNhbmNlbGFibGUgPSBldmVudEluaXRDb25maWcuY2FuY2VsYWJsZTtcbiAgICB2YXIgY29kZSA9IGV2ZW50SW5pdENvbmZpZy5jb2RlO1xuICAgIHZhciByZWFzb24gPSBldmVudEluaXRDb25maWcucmVhc29uO1xuICAgIHZhciB3YXNDbGVhbiA9IGV2ZW50SW5pdENvbmZpZy53YXNDbGVhbjtcblxuICAgIHRoaXMudHlwZSA9IFN0cmluZyh0eXBlKTtcbiAgICB0aGlzLnRpbWVTdGFtcCA9IERhdGUubm93KCk7XG4gICAgdGhpcy50YXJnZXQgPSBudWxsO1xuICAgIHRoaXMuc3JjRWxlbWVudCA9IG51bGw7XG4gICAgdGhpcy5yZXR1cm5WYWx1ZSA9IHRydWU7XG4gICAgdGhpcy5pc1RydXN0ZWQgPSBmYWxzZTtcbiAgICB0aGlzLmV2ZW50UGhhc2UgPSAwO1xuICAgIHRoaXMuZGVmYXVsdFByZXZlbnRlZCA9IGZhbHNlO1xuICAgIHRoaXMuY3VycmVudFRhcmdldCA9IG51bGw7XG4gICAgdGhpcy5jYW5jZWxhYmxlID0gY2FuY2VsYWJsZSA/IEJvb2xlYW4oY2FuY2VsYWJsZSkgOiBmYWxzZTtcbiAgICB0aGlzLmNhbm5jZWxCdWJibGUgPSBmYWxzZTtcbiAgICB0aGlzLmJ1YmJsZXMgPSBidWJibGVzID8gQm9vbGVhbihidWJibGVzKSA6IGZhbHNlO1xuICAgIHRoaXMuY29kZSA9IHR5cGVvZiBjb2RlID09PSAnbnVtYmVyJyA/IE51bWJlcihjb2RlKSA6IDA7XG4gICAgdGhpcy5yZWFzb24gPSByZWFzb24gPyBTdHJpbmcocmVhc29uKSA6ICcnO1xuICAgIHRoaXMud2FzQ2xlYW4gPSB3YXNDbGVhbiA/IEJvb2xlYW4od2FzQ2xlYW4pIDogZmFsc2U7XG4gIH1cblxuICByZXR1cm4gQ2xvc2VFdmVudDtcbn0pKF9ldmVudFByb3RvdHlwZTJbJ2RlZmF1bHQnXSk7XG5cbmV4cG9ydHNbJ2RlZmF1bHQnXSA9IENsb3NlRXZlbnQ7XG5tb2R1bGUuZXhwb3J0cyA9IGV4cG9ydHNbJ2RlZmF1bHQnXTsiLCIvKlxuKiBUaGlzIGRlbGF5IGFsbG93cyB0aGUgdGhyZWFkIHRvIGZpbmlzaCBhc3NpZ25pbmcgaXRzIG9uKiBtZXRob2RzXG4qIGJlZm9yZSBpbnZva2luZyB0aGUgZGVsYXkgY2FsbGJhY2suIFRoaXMgaXMgcHVyZWx5IGEgdGltaW5nIGhhY2suXG4qIGh0dHA6Ly9nZWVrYWJ5dGUuYmxvZ3Nwb3QuY29tLzIwMTQvMDEvamF2YXNjcmlwdC1lZmZlY3Qtb2Ytc2V0dGluZy1zZXR0aW1lb3V0Lmh0bWxcbipcbiogQHBhcmFtIHtjYWxsYmFjazogZnVuY3Rpb259IHRoZSBjYWxsYmFjayB3aGljaCB3aWxsIGJlIGludm9rZWQgYWZ0ZXIgdGhlIHRpbWVvdXRcbiogQHBhcm1hIHtjb250ZXh0OiBvYmplY3R9IHRoZSBjb250ZXh0IGluIHdoaWNoIHRvIGludm9rZSB0aGUgZnVuY3Rpb25cbiovXG5cInVzZSBzdHJpY3RcIjtcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcbmZ1bmN0aW9uIGRlbGF5KGNhbGxiYWNrLCBjb250ZXh0KSB7XG4gIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZW91dCh0aW1lb3V0Q29udGV4dCkge1xuICAgIGNhbGxiYWNrLmNhbGwodGltZW91dENvbnRleHQpO1xuICB9LCA0LCBjb250ZXh0KTtcbn1cblxuZXhwb3J0c1tcImRlZmF1bHRcIl0gPSBkZWxheTtcbm1vZHVsZS5leHBvcnRzID0gZXhwb3J0c1tcImRlZmF1bHRcIl07IiwiJ3VzZSBzdHJpY3QnO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxudmFyIF9jcmVhdGVDbGFzcyA9IChmdW5jdGlvbiAoKSB7IGZ1bmN0aW9uIGRlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoJ3ZhbHVlJyBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH0gcmV0dXJuIGZ1bmN0aW9uIChDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfTsgfSkoKTtcblxuZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgeyBpZiAoIShpbnN0YW5jZSBpbnN0YW5jZW9mIENvbnN0cnVjdG9yKSkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKCdDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb24nKTsgfSB9XG5cbnZhciBFdmVudFByb3RvdHlwZSA9IChmdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIEV2ZW50UHJvdG90eXBlKCkge1xuICAgIF9jbGFzc0NhbGxDaGVjayh0aGlzLCBFdmVudFByb3RvdHlwZSk7XG4gIH1cblxuICBfY3JlYXRlQ2xhc3MoRXZlbnRQcm90b3R5cGUsIFt7XG4gICAga2V5OiAnc3RvcFByb3BhZ2F0aW9uJyxcblxuICAgIC8vIE5vb3BzXG4gICAgdmFsdWU6IGZ1bmN0aW9uIHN0b3BQcm9wYWdhdGlvbigpIHt9XG4gIH0sIHtcbiAgICBrZXk6ICdzdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24nLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBzdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKSB7fVxuXG4gICAgLy8gaWYgbm8gYXJndW1lbnRzIGFyZSBwYXNzZWQgdGhlbiB0aGUgdHlwZSBpcyBzZXQgdG8gXCJ1bmRlZmluZWRcIiBvblxuICAgIC8vIGNocm9tZSBhbmQgc2FmYXJpLlxuICB9LCB7XG4gICAga2V5OiAnaW5pdEV2ZW50JyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gaW5pdEV2ZW50KCkge1xuICAgICAgdmFyIHR5cGUgPSBhcmd1bWVudHMubGVuZ3RoIDw9IDAgfHwgYXJndW1lbnRzWzBdID09PSB1bmRlZmluZWQgPyAndW5kZWZpbmVkJyA6IGFyZ3VtZW50c1swXTtcbiAgICAgIHZhciBidWJibGVzID0gYXJndW1lbnRzLmxlbmd0aCA8PSAxIHx8IGFyZ3VtZW50c1sxXSA9PT0gdW5kZWZpbmVkID8gZmFsc2UgOiBhcmd1bWVudHNbMV07XG4gICAgICB2YXIgY2FuY2VsYWJsZSA9IGFyZ3VtZW50cy5sZW5ndGggPD0gMiB8fCBhcmd1bWVudHNbMl0gPT09IHVuZGVmaW5lZCA/IGZhbHNlIDogYXJndW1lbnRzWzJdO1xuXG4gICAgICBPYmplY3QuYXNzaWduKHRoaXMsIHtcbiAgICAgICAgdHlwZTogU3RyaW5nKHR5cGUpLFxuICAgICAgICBidWJibGVzOiBCb29sZWFuKGJ1YmJsZXMpLFxuICAgICAgICBjYW5jZWxhYmxlOiBCb29sZWFuKGNhbmNlbGFibGUpXG4gICAgICB9KTtcbiAgICB9XG4gIH1dKTtcblxuICByZXR1cm4gRXZlbnRQcm90b3R5cGU7XG59KSgpO1xuXG5leHBvcnRzWydkZWZhdWx0J10gPSBFdmVudFByb3RvdHlwZTtcbm1vZHVsZS5leHBvcnRzID0gZXhwb3J0c1snZGVmYXVsdCddOyIsIid1c2Ugc3RyaWN0JztcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywge1xuICB2YWx1ZTogdHJ1ZVxufSk7XG5cbnZhciBfZ2V0ID0gZnVuY3Rpb24gZ2V0KF94MiwgX3gzLCBfeDQpIHsgdmFyIF9hZ2FpbiA9IHRydWU7IF9mdW5jdGlvbjogd2hpbGUgKF9hZ2FpbikgeyB2YXIgb2JqZWN0ID0gX3gyLCBwcm9wZXJ0eSA9IF94MywgcmVjZWl2ZXIgPSBfeDQ7IF9hZ2FpbiA9IGZhbHNlOyBpZiAob2JqZWN0ID09PSBudWxsKSBvYmplY3QgPSBGdW5jdGlvbi5wcm90b3R5cGU7IHZhciBkZXNjID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihvYmplY3QsIHByb3BlcnR5KTsgaWYgKGRlc2MgPT09IHVuZGVmaW5lZCkgeyB2YXIgcGFyZW50ID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKG9iamVjdCk7IGlmIChwYXJlbnQgPT09IG51bGwpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfSBlbHNlIHsgX3gyID0gcGFyZW50OyBfeDMgPSBwcm9wZXJ0eTsgX3g0ID0gcmVjZWl2ZXI7IF9hZ2FpbiA9IHRydWU7IGRlc2MgPSBwYXJlbnQgPSB1bmRlZmluZWQ7IGNvbnRpbnVlIF9mdW5jdGlvbjsgfSB9IGVsc2UgaWYgKCd2YWx1ZScgaW4gZGVzYykgeyByZXR1cm4gZGVzYy52YWx1ZTsgfSBlbHNlIHsgdmFyIGdldHRlciA9IGRlc2MuZ2V0OyBpZiAoZ2V0dGVyID09PSB1bmRlZmluZWQpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfSByZXR1cm4gZ2V0dGVyLmNhbGwocmVjZWl2ZXIpOyB9IH0gfTtcblxuZnVuY3Rpb24gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChvYmopIHsgcmV0dXJuIG9iaiAmJiBvYmouX19lc01vZHVsZSA/IG9iaiA6IHsgJ2RlZmF1bHQnOiBvYmogfTsgfVxuXG5mdW5jdGlvbiBfY2xhc3NDYWxsQ2hlY2soaW5zdGFuY2UsIENvbnN0cnVjdG9yKSB7IGlmICghKGluc3RhbmNlIGluc3RhbmNlb2YgQ29uc3RydWN0b3IpKSB7IHRocm93IG5ldyBUeXBlRXJyb3IoJ0Nhbm5vdCBjYWxsIGEgY2xhc3MgYXMgYSBmdW5jdGlvbicpOyB9IH1cblxuZnVuY3Rpb24gX2luaGVyaXRzKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IGlmICh0eXBlb2Ygc3VwZXJDbGFzcyAhPT0gJ2Z1bmN0aW9uJyAmJiBzdXBlckNsYXNzICE9PSBudWxsKSB7IHRocm93IG5ldyBUeXBlRXJyb3IoJ1N1cGVyIGV4cHJlc3Npb24gbXVzdCBlaXRoZXIgYmUgbnVsbCBvciBhIGZ1bmN0aW9uLCBub3QgJyArIHR5cGVvZiBzdXBlckNsYXNzKTsgfSBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MgJiYgc3VwZXJDbGFzcy5wcm90b3R5cGUsIHsgY29uc3RydWN0b3I6IHsgdmFsdWU6IHN1YkNsYXNzLCBlbnVtZXJhYmxlOiBmYWxzZSwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9IH0pOyBpZiAoc3VwZXJDbGFzcykgT2JqZWN0LnNldFByb3RvdHlwZU9mID8gT2JqZWN0LnNldFByb3RvdHlwZU9mKHN1YkNsYXNzLCBzdXBlckNsYXNzKSA6IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxudmFyIF9ldmVudFByb3RvdHlwZSA9IHJlcXVpcmUoJy4vZXZlbnQtcHJvdG90eXBlJyk7XG5cbnZhciBfZXZlbnRQcm90b3R5cGUyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfZXZlbnRQcm90b3R5cGUpO1xuXG52YXIgRXZlbnQgPSAoZnVuY3Rpb24gKF9FdmVudFByb3RvdHlwZSkge1xuICBfaW5oZXJpdHMoRXZlbnQsIF9FdmVudFByb3RvdHlwZSk7XG5cbiAgZnVuY3Rpb24gRXZlbnQodHlwZSkge1xuICAgIHZhciBldmVudEluaXRDb25maWcgPSBhcmd1bWVudHMubGVuZ3RoIDw9IDEgfHwgYXJndW1lbnRzWzFdID09PSB1bmRlZmluZWQgPyB7fSA6IGFyZ3VtZW50c1sxXTtcblxuICAgIF9jbGFzc0NhbGxDaGVjayh0aGlzLCBFdmVudCk7XG5cbiAgICBfZ2V0KE9iamVjdC5nZXRQcm90b3R5cGVPZihFdmVudC5wcm90b3R5cGUpLCAnY29uc3RydWN0b3InLCB0aGlzKS5jYWxsKHRoaXMpO1xuXG4gICAgaWYgKCF0eXBlKSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdGYWlsZWQgdG8gY29uc3RydWN0IFxcJ0V2ZW50XFwnOiAxIGFyZ3VtZW50IHJlcXVpcmVkLCBidXQgb25seSAwIHByZXNlbnQuJyk7XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBldmVudEluaXRDb25maWcgIT09ICdvYmplY3QnKSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdGYWlsZWQgdG8gY29uc3RydWN0IFxcJ0V2ZW50XFwnOiBwYXJhbWV0ZXIgMiAoXFwnZXZlbnRJbml0RGljdFxcJykgaXMgbm90IGFuIG9iamVjdCcpO1xuICAgIH1cblxuICAgIHZhciBidWJibGVzID0gZXZlbnRJbml0Q29uZmlnLmJ1YmJsZXM7XG4gICAgdmFyIGNhbmNlbGFibGUgPSBldmVudEluaXRDb25maWcuY2FuY2VsYWJsZTtcblxuICAgIHRoaXMudHlwZSA9IFN0cmluZyh0eXBlKTtcbiAgICB0aGlzLnRpbWVTdGFtcCA9IERhdGUubm93KCk7XG4gICAgdGhpcy50YXJnZXQgPSBudWxsO1xuICAgIHRoaXMuc3JjRWxlbWVudCA9IG51bGw7XG4gICAgdGhpcy5yZXR1cm5WYWx1ZSA9IHRydWU7XG4gICAgdGhpcy5pc1RydXN0ZWQgPSBmYWxzZTtcbiAgICB0aGlzLmV2ZW50UGhhc2UgPSAwO1xuICAgIHRoaXMuZGVmYXVsdFByZXZlbnRlZCA9IGZhbHNlO1xuICAgIHRoaXMuY3VycmVudFRhcmdldCA9IG51bGw7XG4gICAgdGhpcy5jYW5jZWxhYmxlID0gY2FuY2VsYWJsZSA/IEJvb2xlYW4oY2FuY2VsYWJsZSkgOiBmYWxzZTtcbiAgICB0aGlzLmNhbm5jZWxCdWJibGUgPSBmYWxzZTtcbiAgICB0aGlzLmJ1YmJsZXMgPSBidWJibGVzID8gQm9vbGVhbihidWJibGVzKSA6IGZhbHNlO1xuICB9XG5cbiAgcmV0dXJuIEV2ZW50O1xufSkoX2V2ZW50UHJvdG90eXBlMlsnZGVmYXVsdCddKTtcblxuZXhwb3J0c1snZGVmYXVsdCddID0gRXZlbnQ7XG5tb2R1bGUuZXhwb3J0cyA9IGV4cG9ydHNbJ2RlZmF1bHQnXTsiLCIndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHtcbiAgdmFsdWU6IHRydWVcbn0pO1xuXG52YXIgX2dldCA9IGZ1bmN0aW9uIGdldChfeDIsIF94MywgX3g0KSB7IHZhciBfYWdhaW4gPSB0cnVlOyBfZnVuY3Rpb246IHdoaWxlIChfYWdhaW4pIHsgdmFyIG9iamVjdCA9IF94MiwgcHJvcGVydHkgPSBfeDMsIHJlY2VpdmVyID0gX3g0OyBfYWdhaW4gPSBmYWxzZTsgaWYgKG9iamVjdCA9PT0gbnVsbCkgb2JqZWN0ID0gRnVuY3Rpb24ucHJvdG90eXBlOyB2YXIgZGVzYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iob2JqZWN0LCBwcm9wZXJ0eSk7IGlmIChkZXNjID09PSB1bmRlZmluZWQpIHsgdmFyIHBhcmVudCA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihvYmplY3QpOyBpZiAocGFyZW50ID09PSBudWxsKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gZWxzZSB7IF94MiA9IHBhcmVudDsgX3gzID0gcHJvcGVydHk7IF94NCA9IHJlY2VpdmVyOyBfYWdhaW4gPSB0cnVlOyBkZXNjID0gcGFyZW50ID0gdW5kZWZpbmVkOyBjb250aW51ZSBfZnVuY3Rpb247IH0gfSBlbHNlIGlmICgndmFsdWUnIGluIGRlc2MpIHsgcmV0dXJuIGRlc2MudmFsdWU7IH0gZWxzZSB7IHZhciBnZXR0ZXIgPSBkZXNjLmdldDsgaWYgKGdldHRlciA9PT0gdW5kZWZpbmVkKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gcmV0dXJuIGdldHRlci5jYWxsKHJlY2VpdmVyKTsgfSB9IH07XG5cbmZ1bmN0aW9uIF9pbnRlcm9wUmVxdWlyZURlZmF1bHQob2JqKSB7IHJldHVybiBvYmogJiYgb2JqLl9fZXNNb2R1bGUgPyBvYmogOiB7ICdkZWZhdWx0Jzogb2JqIH07IH1cblxuZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgeyBpZiAoIShpbnN0YW5jZSBpbnN0YW5jZW9mIENvbnN0cnVjdG9yKSkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKCdDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb24nKTsgfSB9XG5cbmZ1bmN0aW9uIF9pbmhlcml0cyhzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBpZiAodHlwZW9mIHN1cGVyQ2xhc3MgIT09ICdmdW5jdGlvbicgJiYgc3VwZXJDbGFzcyAhPT0gbnVsbCkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKCdTdXBlciBleHByZXNzaW9uIG11c3QgZWl0aGVyIGJlIG51bGwgb3IgYSBmdW5jdGlvbiwgbm90ICcgKyB0eXBlb2Ygc3VwZXJDbGFzcyk7IH0gc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzICYmIHN1cGVyQ2xhc3MucHJvdG90eXBlLCB7IGNvbnN0cnVjdG9yOiB7IHZhbHVlOiBzdWJDbGFzcywgZW51bWVyYWJsZTogZmFsc2UsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSB9KTsgaWYgKHN1cGVyQ2xhc3MpIE9iamVjdC5zZXRQcm90b3R5cGVPZiA/IE9iamVjdC5zZXRQcm90b3R5cGVPZihzdWJDbGFzcywgc3VwZXJDbGFzcykgOiBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbnZhciBfZXZlbnRQcm90b3R5cGUgPSByZXF1aXJlKCcuL2V2ZW50LXByb3RvdHlwZScpO1xuXG52YXIgX2V2ZW50UHJvdG90eXBlMiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX2V2ZW50UHJvdG90eXBlKTtcblxudmFyIE1lc3NhZ2VFdmVudCA9IChmdW5jdGlvbiAoX0V2ZW50UHJvdG90eXBlKSB7XG4gIF9pbmhlcml0cyhNZXNzYWdlRXZlbnQsIF9FdmVudFByb3RvdHlwZSk7XG5cbiAgZnVuY3Rpb24gTWVzc2FnZUV2ZW50KHR5cGUpIHtcbiAgICB2YXIgZXZlbnRJbml0Q29uZmlnID0gYXJndW1lbnRzLmxlbmd0aCA8PSAxIHx8IGFyZ3VtZW50c1sxXSA9PT0gdW5kZWZpbmVkID8ge30gOiBhcmd1bWVudHNbMV07XG5cbiAgICBfY2xhc3NDYWxsQ2hlY2sodGhpcywgTWVzc2FnZUV2ZW50KTtcblxuICAgIF9nZXQoT2JqZWN0LmdldFByb3RvdHlwZU9mKE1lc3NhZ2VFdmVudC5wcm90b3R5cGUpLCAnY29uc3RydWN0b3InLCB0aGlzKS5jYWxsKHRoaXMpO1xuXG4gICAgaWYgKCF0eXBlKSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdGYWlsZWQgdG8gY29uc3RydWN0IFxcJ01lc3NhZ2VFdmVudFxcJzogMSBhcmd1bWVudCByZXF1aXJlZCwgYnV0IG9ubHkgMCBwcmVzZW50LicpO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgZXZlbnRJbml0Q29uZmlnICE9PSAnb2JqZWN0Jykge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignRmFpbGVkIHRvIGNvbnN0cnVjdCBcXCdNZXNzYWdlRXZlbnRcXCc6IHBhcmFtZXRlciAyIChcXCdldmVudEluaXREaWN0XFwnKSBpcyBub3QgYW4gb2JqZWN0Jyk7XG4gICAgfVxuXG4gICAgdmFyIGJ1YmJsZXMgPSBldmVudEluaXRDb25maWcuYnViYmxlcztcbiAgICB2YXIgY2FuY2VsYWJsZSA9IGV2ZW50SW5pdENvbmZpZy5jYW5jZWxhYmxlO1xuICAgIHZhciBkYXRhID0gZXZlbnRJbml0Q29uZmlnLmRhdGE7XG4gICAgdmFyIG9yaWdpbiA9IGV2ZW50SW5pdENvbmZpZy5vcmlnaW47XG4gICAgdmFyIGxhc3RFdmVudElkID0gZXZlbnRJbml0Q29uZmlnLmxhc3RFdmVudElkO1xuICAgIHZhciBwb3J0cyA9IGV2ZW50SW5pdENvbmZpZy5wb3J0cztcblxuICAgIHRoaXMudHlwZSA9IFN0cmluZyh0eXBlKTtcbiAgICB0aGlzLnRpbWVTdGFtcCA9IERhdGUubm93KCk7XG4gICAgdGhpcy50YXJnZXQgPSBudWxsO1xuICAgIHRoaXMuc3JjRWxlbWVudCA9IG51bGw7XG4gICAgdGhpcy5yZXR1cm5WYWx1ZSA9IHRydWU7XG4gICAgdGhpcy5pc1RydXN0ZWQgPSBmYWxzZTtcbiAgICB0aGlzLmV2ZW50UGhhc2UgPSAwO1xuICAgIHRoaXMuZGVmYXVsdFByZXZlbnRlZCA9IGZhbHNlO1xuICAgIHRoaXMuY3VycmVudFRhcmdldCA9IG51bGw7XG4gICAgdGhpcy5jYW5jZWxhYmxlID0gY2FuY2VsYWJsZSA/IEJvb2xlYW4oY2FuY2VsYWJsZSkgOiBmYWxzZTtcbiAgICB0aGlzLmNhbm5jZWxCdWJibGUgPSBmYWxzZTtcbiAgICB0aGlzLmJ1YmJsZXMgPSBidWJibGVzID8gQm9vbGVhbihidWJibGVzKSA6IGZhbHNlO1xuICAgIHRoaXMub3JpZ2luID0gb3JpZ2luID8gU3RyaW5nKG9yaWdpbikgOiAnJztcbiAgICB0aGlzLnBvcnRzID0gdHlwZW9mIHBvcnRzID09PSAndW5kZWZpbmVkJyA/IG51bGwgOiBwb3J0cztcbiAgICB0aGlzLmRhdGEgPSB0eXBlb2YgZGF0YSA9PT0gJ3VuZGVmaW5lZCcgPyBudWxsIDogZGF0YTtcbiAgICB0aGlzLmxhc3RFdmVudElkID0gbGFzdEV2ZW50SWQgPyBTdHJpbmcobGFzdEV2ZW50SWQpIDogJyc7XG4gIH1cblxuICByZXR1cm4gTWVzc2FnZUV2ZW50O1xufSkoX2V2ZW50UHJvdG90eXBlMlsnZGVmYXVsdCddKTtcblxuZXhwb3J0c1snZGVmYXVsdCddID0gTWVzc2FnZUV2ZW50O1xubW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzWydkZWZhdWx0J107IiwiJ3VzZSBzdHJpY3QnO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxuZnVuY3Rpb24gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChvYmopIHsgcmV0dXJuIG9iaiAmJiBvYmouX19lc01vZHVsZSA/IG9iaiA6IHsgJ2RlZmF1bHQnOiBvYmogfTsgfVxuXG52YXIgX3NlcnZlciA9IHJlcXVpcmUoJy4vc2VydmVyJyk7XG5cbnZhciBfc2VydmVyMiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX3NlcnZlcik7XG5cbnZhciBfc29ja2V0SW8gPSByZXF1aXJlKCcuL3NvY2tldC1pbycpO1xuXG52YXIgX3NvY2tldElvMiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX3NvY2tldElvKTtcblxudmFyIF93ZWJzb2NrZXQgPSByZXF1aXJlKCcuL3dlYnNvY2tldCcpO1xuXG52YXIgX3dlYnNvY2tldDIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF93ZWJzb2NrZXQpO1xuXG5pZiAodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgd2luZG93Lk1vY2tTZXJ2ZXIgPSBfc2VydmVyMlsnZGVmYXVsdCddO1xuICB3aW5kb3cuTW9ja1dlYlNvY2tldCA9IF93ZWJzb2NrZXQyWydkZWZhdWx0J107XG4gIHdpbmRvdy5Nb2NrU29ja2V0SU8gPSBfc29ja2V0SW8yWydkZWZhdWx0J107XG59XG5cbnZhciBTZXJ2ZXIgPSBfc2VydmVyMlsnZGVmYXVsdCddO1xuZXhwb3J0cy5TZXJ2ZXIgPSBTZXJ2ZXI7XG52YXIgV2ViU29ja2V0ID0gX3dlYnNvY2tldDJbJ2RlZmF1bHQnXTtcbmV4cG9ydHMuV2ViU29ja2V0ID0gV2ViU29ja2V0O1xudmFyIFNvY2tldElPID0gX3NvY2tldElvMlsnZGVmYXVsdCddO1xuZXhwb3J0cy5Tb2NrZXRJTyA9IFNvY2tldElPOyIsIid1c2Ugc3RyaWN0JztcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywge1xuICB2YWx1ZTogdHJ1ZVxufSk7XG5cbnZhciBfY3JlYXRlQ2xhc3MgPSAoZnVuY3Rpb24gKCkgeyBmdW5jdGlvbiBkZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKCd2YWx1ZScgaW4gZGVzY3JpcHRvcikgZGVzY3JpcHRvci53cml0YWJsZSA9IHRydWU7IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGRlc2NyaXB0b3Iua2V5LCBkZXNjcmlwdG9yKTsgfSB9IHJldHVybiBmdW5jdGlvbiAoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH07IH0pKCk7XG5cbmZ1bmN0aW9uIF9jbGFzc0NhbGxDaGVjayhpbnN0YW5jZSwgQ29uc3RydWN0b3IpIHsgaWYgKCEoaW5zdGFuY2UgaW5zdGFuY2VvZiBDb25zdHJ1Y3RvcikpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcignQ2Fubm90IGNhbGwgYSBjbGFzcyBhcyBhIGZ1bmN0aW9uJyk7IH0gfVxuXG52YXIgX2hlbHBlcnNBcnJheUhlbHBlcnMgPSByZXF1aXJlKCcuL2hlbHBlcnMvYXJyYXktaGVscGVycycpO1xuXG4vKlxuKiBUaGUgbmV0d29yayBicmlkZ2UgaXMgYSB3YXkgZm9yIHRoZSBtb2NrIHdlYnNvY2tldCBvYmplY3QgdG8gJ2NvbW11bmljYXRlJyB3aXRoXG4qIGFsbCBhdmFsaWJsZSBzZXJ2ZXJzLiBUaGlzIGlzIGEgc2luZ2xldG9uIG9iamVjdCBzbyBpdCBpcyBpbXBvcnRhbnQgdGhhdCB5b3VcbiogY2xlYW4gdXAgdXJsTWFwIHdoZW5ldmVyIHlvdSBhcmUgZmluaXNoZWQuXG4qL1xuXG52YXIgTmV0d29ya0JyaWRnZSA9IChmdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIE5ldHdvcmtCcmlkZ2UoKSB7XG4gICAgX2NsYXNzQ2FsbENoZWNrKHRoaXMsIE5ldHdvcmtCcmlkZ2UpO1xuXG4gICAgdGhpcy51cmxNYXAgPSB7fTtcbiAgfVxuXG4gIC8qXG4gICogQXR0YWNoZXMgYSB3ZWJzb2NrZXQgb2JqZWN0IHRvIHRoZSB1cmxNYXAgaGFzaCBzbyB0aGF0IGl0IGNhbiBmaW5kIHRoZSBzZXJ2ZXJcbiAgKiBpdCBpcyBjb25uZWN0ZWQgdG8gYW5kIHRoZSBzZXJ2ZXIgaW4gdHVybiBjYW4gZmluZCBpdC5cbiAgKlxuICAqIEBwYXJhbSB7b2JqZWN0fSB3ZWJzb2NrZXQgLSB3ZWJzb2NrZXQgb2JqZWN0IHRvIGFkZCB0byB0aGUgdXJsTWFwIGhhc2hcbiAgKiBAcGFyYW0ge3N0cmluZ30gdXJsXG4gICovXG5cbiAgX2NyZWF0ZUNsYXNzKE5ldHdvcmtCcmlkZ2UsIFt7XG4gICAga2V5OiAnYXR0YWNoV2ViU29ja2V0JyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gYXR0YWNoV2ViU29ja2V0KHdlYnNvY2tldCwgdXJsKSB7XG4gICAgICB2YXIgY29ubmVjdGlvbkxvb2t1cCA9IHRoaXMudXJsTWFwW3VybF07XG5cbiAgICAgIGlmIChjb25uZWN0aW9uTG9va3VwICYmIGNvbm5lY3Rpb25Mb29rdXAuc2VydmVyICYmIGNvbm5lY3Rpb25Mb29rdXAud2Vic29ja2V0cy5pbmRleE9mKHdlYnNvY2tldCkgPT09IC0xKSB7XG4gICAgICAgIGNvbm5lY3Rpb25Mb29rdXAud2Vic29ja2V0cy5wdXNoKHdlYnNvY2tldCk7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uTG9va3VwLnNlcnZlcjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKlxuICAgICogQXR0YWNoZXMgYSB3ZWJzb2NrZXQgdG8gYSByb29tXG4gICAgKi9cbiAgfSwge1xuICAgIGtleTogJ2FkZE1lbWJlcnNoaXBUb1Jvb20nLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBhZGRNZW1iZXJzaGlwVG9Sb29tKHdlYnNvY2tldCwgcm9vbSkge1xuICAgICAgdmFyIGNvbm5lY3Rpb25Mb29rdXAgPSB0aGlzLnVybE1hcFt3ZWJzb2NrZXQudXJsXTtcblxuICAgICAgaWYgKGNvbm5lY3Rpb25Mb29rdXAgJiYgY29ubmVjdGlvbkxvb2t1cC5zZXJ2ZXIgJiYgY29ubmVjdGlvbkxvb2t1cC53ZWJzb2NrZXRzLmluZGV4T2Yod2Vic29ja2V0KSAhPT0gLTEpIHtcbiAgICAgICAgaWYgKCFjb25uZWN0aW9uTG9va3VwLnJvb21NZW1iZXJzaGlwc1tyb29tXSkge1xuICAgICAgICAgIGNvbm5lY3Rpb25Mb29rdXAucm9vbU1lbWJlcnNoaXBzW3Jvb21dID0gW107XG4gICAgICAgIH1cblxuICAgICAgICBjb25uZWN0aW9uTG9va3VwLnJvb21NZW1iZXJzaGlwc1tyb29tXS5wdXNoKHdlYnNvY2tldCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLypcbiAgICAqIEF0dGFjaGVzIGEgc2VydmVyIG9iamVjdCB0byB0aGUgdXJsTWFwIGhhc2ggc28gdGhhdCBpdCBjYW4gZmluZCBhIHdlYnNvY2tldHNcbiAgICAqIHdoaWNoIGFyZSBjb25uZWN0ZWQgdG8gaXQgYW5kIHNvIHRoYXQgd2Vic29ja2V0cyBjYW4gaW4gdHVybiBjYW4gZmluZCBpdC5cbiAgICAqXG4gICAgKiBAcGFyYW0ge29iamVjdH0gc2VydmVyIC0gc2VydmVyIG9iamVjdCB0byBhZGQgdG8gdGhlIHVybE1hcCBoYXNoXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gdXJsXG4gICAgKi9cbiAgfSwge1xuICAgIGtleTogJ2F0dGFjaFNlcnZlcicsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGF0dGFjaFNlcnZlcihzZXJ2ZXIsIHVybCkge1xuICAgICAgdmFyIGNvbm5lY3Rpb25Mb29rdXAgPSB0aGlzLnVybE1hcFt1cmxdO1xuXG4gICAgICBpZiAoIWNvbm5lY3Rpb25Mb29rdXApIHtcbiAgICAgICAgdGhpcy51cmxNYXBbdXJsXSA9IHtcbiAgICAgICAgICBzZXJ2ZXI6IHNlcnZlcixcbiAgICAgICAgICB3ZWJzb2NrZXRzOiBbXSxcbiAgICAgICAgICByb29tTWVtYmVyc2hpcHM6IHt9XG4gICAgICAgIH07XG5cbiAgICAgICAgcmV0dXJuIHNlcnZlcjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKlxuICAgICogRmluZHMgdGhlIHNlcnZlciB3aGljaCBpcyAncnVubmluZycgb24gdGhlIGdpdmVuIHVybC5cbiAgICAqXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gdGhlIHVybCB0byB1c2UgdG8gZmluZCB3aGljaCBzZXJ2ZXIgaXMgcnVubmluZyBvbiBpdFxuICAgICovXG4gIH0sIHtcbiAgICBrZXk6ICdzZXJ2ZXJMb29rdXAnLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBzZXJ2ZXJMb29rdXAodXJsKSB7XG4gICAgICB2YXIgY29ubmVjdGlvbkxvb2t1cCA9IHRoaXMudXJsTWFwW3VybF07XG5cbiAgICAgIGlmIChjb25uZWN0aW9uTG9va3VwKSB7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uTG9va3VwLnNlcnZlcjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKlxuICAgICogRmluZHMgYWxsIHdlYnNvY2tldHMgd2hpY2ggaXMgJ2xpc3RlbmluZycgb24gdGhlIGdpdmVuIHVybC5cbiAgICAqXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gdGhlIHVybCB0byB1c2UgdG8gZmluZCBhbGwgd2Vic29ja2V0cyB3aGljaCBhcmUgYXNzb2NpYXRlZCB3aXRoIGl0XG4gICAgKiBAcGFyYW0ge3N0cmluZ30gcm9vbSAtIGlmIGEgcm9vbSBpcyBwcm92aWRlZCwgd2lsbCBvbmx5IHJldHVybiBzb2NrZXRzIGluIHRoaXMgcm9vbVxuICAgICovXG4gIH0sIHtcbiAgICBrZXk6ICd3ZWJzb2NrZXRzTG9va3VwJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gd2Vic29ja2V0c0xvb2t1cCh1cmwsIHJvb20pIHtcbiAgICAgIHZhciBjb25uZWN0aW9uTG9va3VwID0gdGhpcy51cmxNYXBbdXJsXTtcblxuICAgICAgaWYgKCFjb25uZWN0aW9uTG9va3VwKSB7XG4gICAgICAgIHJldHVybiBbXTtcbiAgICAgIH1cblxuICAgICAgaWYgKHJvb20pIHtcbiAgICAgICAgdmFyIG1lbWJlcnMgPSBjb25uZWN0aW9uTG9va3VwLnJvb21NZW1iZXJzaGlwc1tyb29tXTtcbiAgICAgICAgcmV0dXJuIG1lbWJlcnMgPyBtZW1iZXJzIDogW107XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBjb25uZWN0aW9uTG9va3VwLndlYnNvY2tldHM7XG4gICAgfVxuXG4gICAgLypcbiAgICAqIFJlbW92ZXMgdGhlIGVudHJ5IGFzc29jaWF0ZWQgd2l0aCB0aGUgdXJsLlxuICAgICpcbiAgICAqIEBwYXJhbSB7c3RyaW5nfSB1cmxcbiAgICAqL1xuICB9LCB7XG4gICAga2V5OiAncmVtb3ZlU2VydmVyJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gcmVtb3ZlU2VydmVyKHVybCkge1xuICAgICAgZGVsZXRlIHRoaXMudXJsTWFwW3VybF07XG4gICAgfVxuXG4gICAgLypcbiAgICAqIFJlbW92ZXMgdGhlIGluZGl2aWR1YWwgd2Vic29ja2V0IGZyb20gdGhlIG1hcCBvZiBhc3NvY2lhdGVkIHdlYnNvY2tldHMuXG4gICAgKlxuICAgICogQHBhcmFtIHtvYmplY3R9IHdlYnNvY2tldCAtIHdlYnNvY2tldCBvYmplY3QgdG8gcmVtb3ZlIGZyb20gdGhlIHVybCBtYXBcbiAgICAqIEBwYXJhbSB7c3RyaW5nfSB1cmxcbiAgICAqL1xuICB9LCB7XG4gICAga2V5OiAncmVtb3ZlV2ViU29ja2V0JyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gcmVtb3ZlV2ViU29ja2V0KHdlYnNvY2tldCwgdXJsKSB7XG4gICAgICB2YXIgY29ubmVjdGlvbkxvb2t1cCA9IHRoaXMudXJsTWFwW3VybF07XG5cbiAgICAgIGlmIChjb25uZWN0aW9uTG9va3VwKSB7XG4gICAgICAgIGNvbm5lY3Rpb25Mb29rdXAud2Vic29ja2V0cyA9ICgwLCBfaGVscGVyc0FycmF5SGVscGVycy5yZWplY3QpKGNvbm5lY3Rpb25Mb29rdXAud2Vic29ja2V0cywgZnVuY3Rpb24gKHNvY2tldCkge1xuICAgICAgICAgIHJldHVybiBzb2NrZXQgPT09IHdlYnNvY2tldDtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLypcbiAgICAqIFJlbW92ZXMgYSB3ZWJzb2NrZXQgZnJvbSBhIHJvb21cbiAgICAqL1xuICB9LCB7XG4gICAga2V5OiAncmVtb3ZlTWVtYmVyc2hpcEZyb21Sb29tJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gcmVtb3ZlTWVtYmVyc2hpcEZyb21Sb29tKHdlYnNvY2tldCwgcm9vbSkge1xuICAgICAgdmFyIGNvbm5lY3Rpb25Mb29rdXAgPSB0aGlzLnVybE1hcFt3ZWJzb2NrZXQudXJsXTtcbiAgICAgIHZhciBtZW1iZXJzaGlwcyA9IGNvbm5lY3Rpb25Mb29rdXAucm9vbU1lbWJlcnNoaXBzW3Jvb21dO1xuXG4gICAgICBpZiAoY29ubmVjdGlvbkxvb2t1cCAmJiBtZW1iZXJzaGlwcyAhPT0gbnVsbCkge1xuICAgICAgICBjb25uZWN0aW9uTG9va3VwLnJvb21NZW1iZXJzaGlwc1tyb29tXSA9ICgwLCBfaGVscGVyc0FycmF5SGVscGVycy5yZWplY3QpKG1lbWJlcnNoaXBzLCBmdW5jdGlvbiAoc29ja2V0KSB7XG4gICAgICAgICAgcmV0dXJuIHNvY2tldCA9PT0gd2Vic29ja2V0O1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1dKTtcblxuICByZXR1cm4gTmV0d29ya0JyaWRnZTtcbn0pKCk7XG5cbmV4cG9ydHNbJ2RlZmF1bHQnXSA9IG5ldyBOZXR3b3JrQnJpZGdlKCk7XG4vLyBOb3RlOiB0aGlzIGlzIGEgc2luZ2xldG9uXG5tb2R1bGUuZXhwb3J0cyA9IGV4cG9ydHNbJ2RlZmF1bHQnXTsiLCIndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHtcbiAgdmFsdWU6IHRydWVcbn0pO1xuXG52YXIgX2NyZWF0ZUNsYXNzID0gKGZ1bmN0aW9uICgpIHsgZnVuY3Rpb24gZGVmaW5lUHJvcGVydGllcyh0YXJnZXQsIHByb3BzKSB7IGZvciAodmFyIGkgPSAwOyBpIDwgcHJvcHMubGVuZ3RoOyBpKyspIHsgdmFyIGRlc2NyaXB0b3IgPSBwcm9wc1tpXTsgZGVzY3JpcHRvci5lbnVtZXJhYmxlID0gZGVzY3JpcHRvci5lbnVtZXJhYmxlIHx8IGZhbHNlOyBkZXNjcmlwdG9yLmNvbmZpZ3VyYWJsZSA9IHRydWU7IGlmICgndmFsdWUnIGluIGRlc2NyaXB0b3IpIGRlc2NyaXB0b3Iud3JpdGFibGUgPSB0cnVlOyBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBkZXNjcmlwdG9yLmtleSwgZGVzY3JpcHRvcik7IH0gfSByZXR1cm4gZnVuY3Rpb24gKENvbnN0cnVjdG9yLCBwcm90b1Byb3BzLCBzdGF0aWNQcm9wcykgeyBpZiAocHJvdG9Qcm9wcykgZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpOyBpZiAoc3RhdGljUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IsIHN0YXRpY1Byb3BzKTsgcmV0dXJuIENvbnN0cnVjdG9yOyB9OyB9KSgpO1xuXG52YXIgX2dldCA9IGZ1bmN0aW9uIGdldChfeDQsIF94NSwgX3g2KSB7IHZhciBfYWdhaW4gPSB0cnVlOyBfZnVuY3Rpb246IHdoaWxlIChfYWdhaW4pIHsgdmFyIG9iamVjdCA9IF94NCwgcHJvcGVydHkgPSBfeDUsIHJlY2VpdmVyID0gX3g2OyBfYWdhaW4gPSBmYWxzZTsgaWYgKG9iamVjdCA9PT0gbnVsbCkgb2JqZWN0ID0gRnVuY3Rpb24ucHJvdG90eXBlOyB2YXIgZGVzYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iob2JqZWN0LCBwcm9wZXJ0eSk7IGlmIChkZXNjID09PSB1bmRlZmluZWQpIHsgdmFyIHBhcmVudCA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihvYmplY3QpOyBpZiAocGFyZW50ID09PSBudWxsKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gZWxzZSB7IF94NCA9IHBhcmVudDsgX3g1ID0gcHJvcGVydHk7IF94NiA9IHJlY2VpdmVyOyBfYWdhaW4gPSB0cnVlOyBkZXNjID0gcGFyZW50ID0gdW5kZWZpbmVkOyBjb250aW51ZSBfZnVuY3Rpb247IH0gfSBlbHNlIGlmICgndmFsdWUnIGluIGRlc2MpIHsgcmV0dXJuIGRlc2MudmFsdWU7IH0gZWxzZSB7IHZhciBnZXR0ZXIgPSBkZXNjLmdldDsgaWYgKGdldHRlciA9PT0gdW5kZWZpbmVkKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gcmV0dXJuIGdldHRlci5jYWxsKHJlY2VpdmVyKTsgfSB9IH07XG5cbmZ1bmN0aW9uIF9pbnRlcm9wUmVxdWlyZURlZmF1bHQob2JqKSB7IHJldHVybiBvYmogJiYgb2JqLl9fZXNNb2R1bGUgPyBvYmogOiB7ICdkZWZhdWx0Jzogb2JqIH07IH1cblxuZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgeyBpZiAoIShpbnN0YW5jZSBpbnN0YW5jZW9mIENvbnN0cnVjdG9yKSkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKCdDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb24nKTsgfSB9XG5cbmZ1bmN0aW9uIF9pbmhlcml0cyhzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBpZiAodHlwZW9mIHN1cGVyQ2xhc3MgIT09ICdmdW5jdGlvbicgJiYgc3VwZXJDbGFzcyAhPT0gbnVsbCkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKCdTdXBlciBleHByZXNzaW9uIG11c3QgZWl0aGVyIGJlIG51bGwgb3IgYSBmdW5jdGlvbiwgbm90ICcgKyB0eXBlb2Ygc3VwZXJDbGFzcyk7IH0gc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzICYmIHN1cGVyQ2xhc3MucHJvdG90eXBlLCB7IGNvbnN0cnVjdG9yOiB7IHZhbHVlOiBzdWJDbGFzcywgZW51bWVyYWJsZTogZmFsc2UsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSB9KTsgaWYgKHN1cGVyQ2xhc3MpIE9iamVjdC5zZXRQcm90b3R5cGVPZiA/IE9iamVjdC5zZXRQcm90b3R5cGVPZihzdWJDbGFzcywgc3VwZXJDbGFzcykgOiBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbnZhciBfdXJpanMgPSByZXF1aXJlKCd1cmlqcycpO1xuXG52YXIgX3VyaWpzMiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX3VyaWpzKTtcblxudmFyIF93ZWJzb2NrZXQgPSByZXF1aXJlKCcuL3dlYnNvY2tldCcpO1xuXG52YXIgX3dlYnNvY2tldDIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF93ZWJzb2NrZXQpO1xuXG52YXIgX2V2ZW50VGFyZ2V0ID0gcmVxdWlyZSgnLi9ldmVudC10YXJnZXQnKTtcblxudmFyIF9ldmVudFRhcmdldDIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF9ldmVudFRhcmdldCk7XG5cbnZhciBfbmV0d29ya0JyaWRnZSA9IHJlcXVpcmUoJy4vbmV0d29yay1icmlkZ2UnKTtcblxudmFyIF9uZXR3b3JrQnJpZGdlMiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX25ldHdvcmtCcmlkZ2UpO1xuXG52YXIgX2hlbHBlcnNDbG9zZUNvZGVzID0gcmVxdWlyZSgnLi9oZWxwZXJzL2Nsb3NlLWNvZGVzJyk7XG5cbnZhciBfaGVscGVyc0Nsb3NlQ29kZXMyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfaGVscGVyc0Nsb3NlQ29kZXMpO1xuXG52YXIgX2V2ZW50RmFjdG9yeSA9IHJlcXVpcmUoJy4vZXZlbnQtZmFjdG9yeScpO1xuXG4vKlxuKiBodHRwczovL2dpdGh1Yi5jb20vd2Vic29ja2V0cy93cyNzZXJ2ZXItZXhhbXBsZVxuKi9cblxudmFyIFNlcnZlciA9IChmdW5jdGlvbiAoX0V2ZW50VGFyZ2V0KSB7XG4gIF9pbmhlcml0cyhTZXJ2ZXIsIF9FdmVudFRhcmdldCk7XG5cbiAgLypcbiAgKiBAcGFyYW0ge3N0cmluZ30gdXJsXG4gICovXG5cbiAgZnVuY3Rpb24gU2VydmVyKHVybCkge1xuICAgIF9jbGFzc0NhbGxDaGVjayh0aGlzLCBTZXJ2ZXIpO1xuXG4gICAgX2dldChPYmplY3QuZ2V0UHJvdG90eXBlT2YoU2VydmVyLnByb3RvdHlwZSksICdjb25zdHJ1Y3RvcicsIHRoaXMpLmNhbGwodGhpcyk7XG4gICAgdGhpcy51cmwgPSAoMCwgX3VyaWpzMlsnZGVmYXVsdCddKSh1cmwpLnRvU3RyaW5nKCk7XG4gICAgdmFyIHNlcnZlciA9IF9uZXR3b3JrQnJpZGdlMlsnZGVmYXVsdCddLmF0dGFjaFNlcnZlcih0aGlzLCB0aGlzLnVybCk7XG5cbiAgICBpZiAoIXNlcnZlcikge1xuICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KCgwLCBfZXZlbnRGYWN0b3J5LmNyZWF0ZUV2ZW50KSh7IHR5cGU6ICdlcnJvcicgfSkpO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdBIG1vY2sgc2VydmVyIGlzIGFscmVhZHkgbGlzdGVuaW5nIG9uIHRoaXMgdXJsJyk7XG4gICAgfVxuICB9XG5cbiAgLypcbiAgICogQWx0ZXJuYXRpdmUgY29uc3RydWN0b3IgdG8gc3VwcG9ydCBuYW1lc3BhY2VzIGluIHNvY2tldC5pb1xuICAgKlxuICAgKiBodHRwOi8vc29ja2V0LmlvL2RvY3Mvcm9vbXMtYW5kLW5hbWVzcGFjZXMvI2N1c3RvbS1uYW1lc3BhY2VzXG4gICAqL1xuXG4gIC8qXG4gICogVGhpcyBpcyB0aGUgbWFpbiBmdW5jdGlvbiBmb3IgdGhlIG1vY2sgc2VydmVyIHRvIHN1YnNjcmliZSB0byB0aGUgb24gZXZlbnRzLlxuICAqXG4gICogaWU6IG1vY2tTZXJ2ZXIub24oJ2Nvbm5lY3Rpb24nLCBmdW5jdGlvbigpIHsgY29uc29sZS5sb2coJ2EgbW9jayBjbGllbnQgY29ubmVjdGVkJyk7IH0pO1xuICAqXG4gICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgZXZlbnQga2V5IHRvIHN1YnNjcmliZSB0by4gVmFsaWQga2V5cyBhcmU6IGNvbm5lY3Rpb24sIG1lc3NhZ2UsIGFuZCBjbG9zZS5cbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBjYWxsYmFjayAtIFRoZSBjYWxsYmFjayB3aGljaCBzaG91bGQgYmUgY2FsbGVkIHdoZW4gYSBjZXJ0YWluIGV2ZW50IGlzIGZpcmVkLlxuICAqL1xuXG4gIF9jcmVhdGVDbGFzcyhTZXJ2ZXIsIFt7XG4gICAga2V5OiAnb24nLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBvbih0eXBlLCBjYWxsYmFjaykge1xuICAgICAgdGhpcy5hZGRFdmVudExpc3RlbmVyKHR5cGUsIGNhbGxiYWNrKTtcbiAgICB9XG5cbiAgICAvKlxuICAgICogVGhpcyBzZW5kIGZ1bmN0aW9uIHdpbGwgbm90aWZ5IGFsbCBtb2NrIGNsaWVudHMgdmlhIHRoZWlyIG9ubWVzc2FnZSBjYWxsYmFja3MgdGhhdCB0aGUgc2VydmVyXG4gICAgKiBoYXMgYSBtZXNzYWdlIGZvciB0aGVtLlxuICAgICpcbiAgICAqIEBwYXJhbSB7Kn0gZGF0YSAtIEFueSBqYXZhc2NyaXB0IG9iamVjdCB3aGljaCB3aWxsIGJlIGNyYWZ0ZWQgaW50byBhIE1lc3NhZ2VPYmplY3QuXG4gICAgKi9cbiAgfSwge1xuICAgIGtleTogJ3NlbmQnLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBzZW5kKGRhdGEpIHtcbiAgICAgIHZhciBvcHRpb25zID0gYXJndW1lbnRzLmxlbmd0aCA8PSAxIHx8IGFyZ3VtZW50c1sxXSA9PT0gdW5kZWZpbmVkID8ge30gOiBhcmd1bWVudHNbMV07XG5cbiAgICAgIHRoaXMuZW1pdCgnbWVzc2FnZScsIGRhdGEsIG9wdGlvbnMpO1xuICAgIH1cblxuICAgIC8qXG4gICAgKiBTZW5kcyBhIGdlbmVyaWMgbWVzc2FnZSBldmVudCB0byBhbGwgbW9jayBjbGllbnRzLlxuICAgICovXG4gIH0sIHtcbiAgICBrZXk6ICdlbWl0JyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gZW1pdChldmVudCwgZGF0YSkge1xuICAgICAgdmFyIF90aGlzMiA9IHRoaXM7XG5cbiAgICAgIHZhciBvcHRpb25zID0gYXJndW1lbnRzLmxlbmd0aCA8PSAyIHx8IGFyZ3VtZW50c1syXSA9PT0gdW5kZWZpbmVkID8ge30gOiBhcmd1bWVudHNbMl07XG4gICAgICB2YXIgd2Vic29ja2V0cyA9IG9wdGlvbnMud2Vic29ja2V0cztcblxuICAgICAgaWYgKCF3ZWJzb2NrZXRzKSB7XG4gICAgICAgIHdlYnNvY2tldHMgPSBfbmV0d29ya0JyaWRnZTJbJ2RlZmF1bHQnXS53ZWJzb2NrZXRzTG9va3VwKHRoaXMudXJsKTtcbiAgICAgIH1cblxuICAgICAgd2Vic29ja2V0cy5mb3JFYWNoKGZ1bmN0aW9uIChzb2NrZXQpIHtcbiAgICAgICAgc29ja2V0LmRpc3BhdGNoRXZlbnQoKDAsIF9ldmVudEZhY3RvcnkuY3JlYXRlTWVzc2FnZUV2ZW50KSh7XG4gICAgICAgICAgdHlwZTogZXZlbnQsXG4gICAgICAgICAgZGF0YTogZGF0YSxcbiAgICAgICAgICBvcmlnaW46IF90aGlzMi51cmwsXG4gICAgICAgICAgdGFyZ2V0OiBzb2NrZXRcbiAgICAgICAgfSkpO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLypcbiAgICAqIENsb3NlcyB0aGUgY29ubmVjdGlvbiBhbmQgdHJpZ2dlcnMgdGhlIG9uY2xvc2UgbWV0aG9kIG9mIGFsbCBsaXN0ZW5pbmdcbiAgICAqIHdlYnNvY2tldHMuIEFmdGVyIHRoYXQgaXQgcmVtb3ZlcyBpdHNlbGYgZnJvbSB0aGUgdXJsTWFwIHNvIGFub3RoZXIgc2VydmVyXG4gICAgKiBjb3VsZCBhZGQgaXRzZWxmIHRvIHRoZSB1cmwuXG4gICAgKlxuICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnNcbiAgICAqL1xuICB9LCB7XG4gICAga2V5OiAnY2xvc2UnLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBjbG9zZSgpIHtcbiAgICAgIHZhciBvcHRpb25zID0gYXJndW1lbnRzLmxlbmd0aCA8PSAwIHx8IGFyZ3VtZW50c1swXSA9PT0gdW5kZWZpbmVkID8ge30gOiBhcmd1bWVudHNbMF07XG4gICAgICB2YXIgY29kZSA9IG9wdGlvbnMuY29kZTtcbiAgICAgIHZhciByZWFzb24gPSBvcHRpb25zLnJlYXNvbjtcbiAgICAgIHZhciB3YXNDbGVhbiA9IG9wdGlvbnMud2FzQ2xlYW47XG5cbiAgICAgIHZhciBsaXN0ZW5lcnMgPSBfbmV0d29ya0JyaWRnZTJbJ2RlZmF1bHQnXS53ZWJzb2NrZXRzTG9va3VwKHRoaXMudXJsKTtcblxuICAgICAgbGlzdGVuZXJzLmZvckVhY2goZnVuY3Rpb24gKHNvY2tldCkge1xuICAgICAgICBzb2NrZXQucmVhZHlTdGF0ZSA9IF93ZWJzb2NrZXQyWydkZWZhdWx0J10uQ0xPU0U7XG4gICAgICAgIHNvY2tldC5kaXNwYXRjaEV2ZW50KCgwLCBfZXZlbnRGYWN0b3J5LmNyZWF0ZUNsb3NlRXZlbnQpKHtcbiAgICAgICAgICB0eXBlOiAnY2xvc2UnLFxuICAgICAgICAgIHRhcmdldDogc29ja2V0LFxuICAgICAgICAgIGNvZGU6IGNvZGUgfHwgX2hlbHBlcnNDbG9zZUNvZGVzMlsnZGVmYXVsdCddLkNMT1NFX05PUk1BTCxcbiAgICAgICAgICByZWFzb246IHJlYXNvbiB8fCAnJyxcbiAgICAgICAgICB3YXNDbGVhbjogd2FzQ2xlYW5cbiAgICAgICAgfSkpO1xuICAgICAgfSk7XG5cbiAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudCgoMCwgX2V2ZW50RmFjdG9yeS5jcmVhdGVDbG9zZUV2ZW50KSh7IHR5cGU6ICdjbG9zZScgfSksIHRoaXMpO1xuICAgICAgX25ldHdvcmtCcmlkZ2UyWydkZWZhdWx0J10ucmVtb3ZlU2VydmVyKHRoaXMudXJsKTtcbiAgICB9XG5cbiAgICAvKlxuICAgICogUmV0dXJucyBhbiBhcnJheSBvZiB3ZWJzb2NrZXRzIHdoaWNoIGFyZSBsaXN0ZW5pbmcgdG8gdGhpcyBzZXJ2ZXJcbiAgICAqL1xuICB9LCB7XG4gICAga2V5OiAnY2xpZW50cycsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGNsaWVudHMoKSB7XG4gICAgICByZXR1cm4gX25ldHdvcmtCcmlkZ2UyWydkZWZhdWx0J10ud2Vic29ja2V0c0xvb2t1cCh0aGlzLnVybCk7XG4gICAgfVxuXG4gICAgLypcbiAgICAqIFByZXBhcmVzIGEgbWV0aG9kIHRvIHN1Ym1pdCBhbiBldmVudCB0byBtZW1iZXJzIG9mIHRoZSByb29tXG4gICAgKlxuICAgICogZS5nLiBzZXJ2ZXIudG8oJ215LXJvb20nKS5lbWl0KCdoaSEnKTtcbiAgICAqL1xuICB9LCB7XG4gICAga2V5OiAndG8nLFxuICAgIHZhbHVlOiBmdW5jdGlvbiB0byhyb29tKSB7XG4gICAgICB2YXIgX3RoaXMgPSB0aGlzO1xuICAgICAgdmFyIHdlYnNvY2tldHMgPSBfbmV0d29ya0JyaWRnZTJbJ2RlZmF1bHQnXS53ZWJzb2NrZXRzTG9va3VwKHRoaXMudXJsLCByb29tKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGVtaXQ6IGZ1bmN0aW9uIGVtaXQoZXZlbnQsIGRhdGEpIHtcbiAgICAgICAgICBfdGhpcy5lbWl0KGV2ZW50LCBkYXRhLCB7IHdlYnNvY2tldHM6IHdlYnNvY2tldHMgfSk7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIFNlcnZlcjtcbn0pKF9ldmVudFRhcmdldDJbJ2RlZmF1bHQnXSk7XG5cblNlcnZlci5vZiA9IGZ1bmN0aW9uIG9mKHVybCkge1xuICByZXR1cm4gbmV3IFNlcnZlcih1cmwpO1xufTtcblxuZXhwb3J0c1snZGVmYXVsdCddID0gU2VydmVyO1xubW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzWydkZWZhdWx0J107IiwiJ3VzZSBzdHJpY3QnO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxudmFyIF9jcmVhdGVDbGFzcyA9IChmdW5jdGlvbiAoKSB7IGZ1bmN0aW9uIGRlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoJ3ZhbHVlJyBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH0gcmV0dXJuIGZ1bmN0aW9uIChDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfTsgfSkoKTtcblxudmFyIF9nZXQgPSBmdW5jdGlvbiBnZXQoX3gzLCBfeDQsIF94NSkgeyB2YXIgX2FnYWluID0gdHJ1ZTsgX2Z1bmN0aW9uOiB3aGlsZSAoX2FnYWluKSB7IHZhciBvYmplY3QgPSBfeDMsIHByb3BlcnR5ID0gX3g0LCByZWNlaXZlciA9IF94NTsgX2FnYWluID0gZmFsc2U7IGlmIChvYmplY3QgPT09IG51bGwpIG9iamVjdCA9IEZ1bmN0aW9uLnByb3RvdHlwZTsgdmFyIGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKG9iamVjdCwgcHJvcGVydHkpOyBpZiAoZGVzYyA9PT0gdW5kZWZpbmVkKSB7IHZhciBwYXJlbnQgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2Yob2JqZWN0KTsgaWYgKHBhcmVudCA9PT0gbnVsbCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9IGVsc2UgeyBfeDMgPSBwYXJlbnQ7IF94NCA9IHByb3BlcnR5OyBfeDUgPSByZWNlaXZlcjsgX2FnYWluID0gdHJ1ZTsgZGVzYyA9IHBhcmVudCA9IHVuZGVmaW5lZDsgY29udGludWUgX2Z1bmN0aW9uOyB9IH0gZWxzZSBpZiAoJ3ZhbHVlJyBpbiBkZXNjKSB7IHJldHVybiBkZXNjLnZhbHVlOyB9IGVsc2UgeyB2YXIgZ2V0dGVyID0gZGVzYy5nZXQ7IGlmIChnZXR0ZXIgPT09IHVuZGVmaW5lZCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9IHJldHVybiBnZXR0ZXIuY2FsbChyZWNlaXZlcik7IH0gfSB9O1xuXG5mdW5jdGlvbiBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KG9iaikgeyByZXR1cm4gb2JqICYmIG9iai5fX2VzTW9kdWxlID8gb2JqIDogeyAnZGVmYXVsdCc6IG9iaiB9OyB9XG5cbmZ1bmN0aW9uIF9jbGFzc0NhbGxDaGVjayhpbnN0YW5jZSwgQ29uc3RydWN0b3IpIHsgaWYgKCEoaW5zdGFuY2UgaW5zdGFuY2VvZiBDb25zdHJ1Y3RvcikpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcignQ2Fubm90IGNhbGwgYSBjbGFzcyBhcyBhIGZ1bmN0aW9uJyk7IH0gfVxuXG5mdW5jdGlvbiBfaW5oZXJpdHMoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgaWYgKHR5cGVvZiBzdXBlckNsYXNzICE9PSAnZnVuY3Rpb24nICYmIHN1cGVyQ2xhc3MgIT09IG51bGwpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcignU3VwZXIgZXhwcmVzc2lvbiBtdXN0IGVpdGhlciBiZSBudWxsIG9yIGEgZnVuY3Rpb24sIG5vdCAnICsgdHlwZW9mIHN1cGVyQ2xhc3MpOyB9IHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcyAmJiBzdXBlckNsYXNzLnByb3RvdHlwZSwgeyBjb25zdHJ1Y3RvcjogeyB2YWx1ZTogc3ViQ2xhc3MsIGVudW1lcmFibGU6IGZhbHNlLCB3cml0YWJsZTogdHJ1ZSwgY29uZmlndXJhYmxlOiB0cnVlIH0gfSk7IGlmIChzdXBlckNsYXNzKSBPYmplY3Quc2V0UHJvdG90eXBlT2YgPyBPYmplY3Quc2V0UHJvdG90eXBlT2Yoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIDogc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzczsgfVxuXG52YXIgX3VyaWpzID0gcmVxdWlyZSgndXJpanMnKTtcblxudmFyIF91cmlqczIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF91cmlqcyk7XG5cbnZhciBfaGVscGVyc0RlbGF5ID0gcmVxdWlyZSgnLi9oZWxwZXJzL2RlbGF5Jyk7XG5cbnZhciBfaGVscGVyc0RlbGF5MiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX2hlbHBlcnNEZWxheSk7XG5cbnZhciBfZXZlbnRUYXJnZXQgPSByZXF1aXJlKCcuL2V2ZW50LXRhcmdldCcpO1xuXG52YXIgX2V2ZW50VGFyZ2V0MiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX2V2ZW50VGFyZ2V0KTtcblxudmFyIF9uZXR3b3JrQnJpZGdlID0gcmVxdWlyZSgnLi9uZXR3b3JrLWJyaWRnZScpO1xuXG52YXIgX25ldHdvcmtCcmlkZ2UyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfbmV0d29ya0JyaWRnZSk7XG5cbnZhciBfaGVscGVyc0Nsb3NlQ29kZXMgPSByZXF1aXJlKCcuL2hlbHBlcnMvY2xvc2UtY29kZXMnKTtcblxudmFyIF9oZWxwZXJzQ2xvc2VDb2RlczIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF9oZWxwZXJzQ2xvc2VDb2Rlcyk7XG5cbnZhciBfZXZlbnRGYWN0b3J5ID0gcmVxdWlyZSgnLi9ldmVudC1mYWN0b3J5Jyk7XG5cbi8qXG4qIFRoZSBzb2NrZXQtaW8gY2xhc3MgaXMgZGVzaWduZWQgdG8gbWltaWNrIHRoZSByZWFsIEFQSSBhcyBjbG9zZWx5IGFzIHBvc3NpYmxlLlxuKlxuKiBodHRwOi8vc29ja2V0LmlvL2RvY3MvXG4qL1xuXG52YXIgU29ja2V0SU8gPSAoZnVuY3Rpb24gKF9FdmVudFRhcmdldCkge1xuICBfaW5oZXJpdHMoU29ja2V0SU8sIF9FdmVudFRhcmdldCk7XG5cbiAgLypcbiAgKiBAcGFyYW0ge3N0cmluZ30gdXJsXG4gICovXG5cbiAgZnVuY3Rpb24gU29ja2V0SU8oKSB7XG4gICAgdmFyIF90aGlzID0gdGhpcztcblxuICAgIHZhciB1cmwgPSBhcmd1bWVudHMubGVuZ3RoIDw9IDAgfHwgYXJndW1lbnRzWzBdID09PSB1bmRlZmluZWQgPyAnc29ja2V0LmlvJyA6IGFyZ3VtZW50c1swXTtcbiAgICB2YXIgcHJvdG9jb2wgPSBhcmd1bWVudHMubGVuZ3RoIDw9IDEgfHwgYXJndW1lbnRzWzFdID09PSB1bmRlZmluZWQgPyAnJyA6IGFyZ3VtZW50c1sxXTtcblxuICAgIF9jbGFzc0NhbGxDaGVjayh0aGlzLCBTb2NrZXRJTyk7XG5cbiAgICBfZ2V0KE9iamVjdC5nZXRQcm90b3R5cGVPZihTb2NrZXRJTy5wcm90b3R5cGUpLCAnY29uc3RydWN0b3InLCB0aGlzKS5jYWxsKHRoaXMpO1xuXG4gICAgdGhpcy5iaW5hcnlUeXBlID0gJ2Jsb2InO1xuICAgIHRoaXMudXJsID0gKDAsIF91cmlqczJbJ2RlZmF1bHQnXSkodXJsKS50b1N0cmluZygpO1xuICAgIHRoaXMucmVhZHlTdGF0ZSA9IFNvY2tldElPLkNPTk5FQ1RJTkc7XG4gICAgdGhpcy5wcm90b2NvbCA9ICcnO1xuXG4gICAgaWYgKHR5cGVvZiBwcm90b2NvbCA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHRoaXMucHJvdG9jb2wgPSBwcm90b2NvbDtcbiAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkocHJvdG9jb2wpICYmIHByb3RvY29sLmxlbmd0aCA+IDApIHtcbiAgICAgIHRoaXMucHJvdG9jb2wgPSBwcm90b2NvbFswXTtcbiAgICB9XG5cbiAgICB2YXIgc2VydmVyID0gX25ldHdvcmtCcmlkZ2UyWydkZWZhdWx0J10uYXR0YWNoV2ViU29ja2V0KHRoaXMsIHRoaXMudXJsKTtcblxuICAgIC8qXG4gICAgKiBEZWxheSB0cmlnZ2VyaW5nIHRoZSBjb25uZWN0aW9uIGV2ZW50cyBzbyB0aGV5IGNhbiBiZSBkZWZpbmVkIGluIHRpbWUuXG4gICAgKi9cbiAgICAoMCwgX2hlbHBlcnNEZWxheTJbJ2RlZmF1bHQnXSkoZnVuY3Rpb24gZGVsYXlDYWxsYmFjaygpIHtcbiAgICAgIGlmIChzZXJ2ZXIpIHtcbiAgICAgICAgdGhpcy5yZWFkeVN0YXRlID0gU29ja2V0SU8uT1BFTjtcbiAgICAgICAgc2VydmVyLmRpc3BhdGNoRXZlbnQoKDAsIF9ldmVudEZhY3RvcnkuY3JlYXRlRXZlbnQpKHsgdHlwZTogJ2Nvbm5lY3Rpb24nIH0pLCBzZXJ2ZXIsIHRoaXMpO1xuICAgICAgICBzZXJ2ZXIuZGlzcGF0Y2hFdmVudCgoMCwgX2V2ZW50RmFjdG9yeS5jcmVhdGVFdmVudCkoeyB0eXBlOiAnY29ubmVjdCcgfSksIHNlcnZlciwgdGhpcyk7IC8vIGFsaWFzXG4gICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudCgoMCwgX2V2ZW50RmFjdG9yeS5jcmVhdGVFdmVudCkoeyB0eXBlOiAnY29ubmVjdCcsIHRhcmdldDogdGhpcyB9KSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnJlYWR5U3RhdGUgPSBTb2NrZXRJTy5DTE9TRUQ7XG4gICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudCgoMCwgX2V2ZW50RmFjdG9yeS5jcmVhdGVFdmVudCkoeyB0eXBlOiAnZXJyb3InLCB0YXJnZXQ6IHRoaXMgfSkpO1xuICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQoKDAsIF9ldmVudEZhY3RvcnkuY3JlYXRlQ2xvc2VFdmVudCkoe1xuICAgICAgICAgIHR5cGU6ICdjbG9zZScsXG4gICAgICAgICAgdGFyZ2V0OiB0aGlzLFxuICAgICAgICAgIGNvZGU6IF9oZWxwZXJzQ2xvc2VDb2RlczJbJ2RlZmF1bHQnXS5DTE9TRV9OT1JNQUxcbiAgICAgICAgfSkpO1xuXG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ1NvY2tldC5pbyBjb25uZWN0aW9uIHRvIFxcJycgKyB0aGlzLnVybCArICdcXCcgZmFpbGVkJyk7XG4gICAgICB9XG4gICAgfSwgdGhpcyk7XG5cbiAgICAvKipcbiAgICAgIEFkZCBhbiBhbGlhc2VkIGV2ZW50IGxpc3RlbmVyIGZvciBjbG9zZSAvIGRpc2Nvbm5lY3RcbiAgICAgKi9cbiAgICB0aGlzLmFkZEV2ZW50TGlzdGVuZXIoJ2Nsb3NlJywgZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICBfdGhpcy5kaXNwYXRjaEV2ZW50KCgwLCBfZXZlbnRGYWN0b3J5LmNyZWF0ZUNsb3NlRXZlbnQpKHtcbiAgICAgICAgdHlwZTogJ2Rpc2Nvbm5lY3QnLFxuICAgICAgICB0YXJnZXQ6IGV2ZW50LnRhcmdldCxcbiAgICAgICAgY29kZTogZXZlbnQuY29kZVxuICAgICAgfSkpO1xuICAgIH0pO1xuICB9XG5cbiAgLypcbiAgKiBDbG9zZXMgdGhlIFNvY2tldElPIGNvbm5lY3Rpb24gb3IgY29ubmVjdGlvbiBhdHRlbXB0LCBpZiBhbnkuXG4gICogSWYgdGhlIGNvbm5lY3Rpb24gaXMgYWxyZWFkeSBDTE9TRUQsIHRoaXMgbWV0aG9kIGRvZXMgbm90aGluZy5cbiAgKi9cblxuICBfY3JlYXRlQ2xhc3MoU29ja2V0SU8sIFt7XG4gICAga2V5OiAnY2xvc2UnLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBjbG9zZSgpIHtcbiAgICAgIGlmICh0aGlzLnJlYWR5U3RhdGUgIT09IFNvY2tldElPLk9QRU4pIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgIH1cblxuICAgICAgdmFyIHNlcnZlciA9IF9uZXR3b3JrQnJpZGdlMlsnZGVmYXVsdCddLnNlcnZlckxvb2t1cCh0aGlzLnVybCk7XG4gICAgICBfbmV0d29ya0JyaWRnZTJbJ2RlZmF1bHQnXS5yZW1vdmVXZWJTb2NrZXQodGhpcywgdGhpcy51cmwpO1xuXG4gICAgICB0aGlzLnJlYWR5U3RhdGUgPSBTb2NrZXRJTy5DTE9TRUQ7XG4gICAgICB0aGlzLmRpc3BhdGNoRXZlbnQoKDAsIF9ldmVudEZhY3RvcnkuY3JlYXRlQ2xvc2VFdmVudCkoe1xuICAgICAgICB0eXBlOiAnY2xvc2UnLFxuICAgICAgICB0YXJnZXQ6IHRoaXMsXG4gICAgICAgIGNvZGU6IF9oZWxwZXJzQ2xvc2VDb2RlczJbJ2RlZmF1bHQnXS5DTE9TRV9OT1JNQUxcbiAgICAgIH0pKTtcblxuICAgICAgaWYgKHNlcnZlcikge1xuICAgICAgICBzZXJ2ZXIuZGlzcGF0Y2hFdmVudCgoMCwgX2V2ZW50RmFjdG9yeS5jcmVhdGVDbG9zZUV2ZW50KSh7XG4gICAgICAgICAgdHlwZTogJ2Rpc2Nvbm5lY3QnLFxuICAgICAgICAgIHRhcmdldDogdGhpcyxcbiAgICAgICAgICBjb2RlOiBfaGVscGVyc0Nsb3NlQ29kZXMyWydkZWZhdWx0J10uQ0xPU0VfTk9STUFMXG4gICAgICAgIH0pLCBzZXJ2ZXIpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8qXG4gICAgKiBBbGlhcyBmb3IgU29ja2V0I2Nsb3NlXG4gICAgKlxuICAgICogaHR0cHM6Ly9naXRodWIuY29tL3NvY2tldGlvL3NvY2tldC5pby1jbGllbnQvYmxvYi9tYXN0ZXIvbGliL3NvY2tldC5qcyNMMzgzXG4gICAgKi9cbiAgfSwge1xuICAgIGtleTogJ2Rpc2Nvbm5lY3QnLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBkaXNjb25uZWN0KCkge1xuICAgICAgdGhpcy5jbG9zZSgpO1xuICAgIH1cblxuICAgIC8qXG4gICAgKiBTdWJtaXRzIGFuIGV2ZW50IHRvIHRoZSBzZXJ2ZXIgd2l0aCBhIHBheWxvYWRcbiAgICAqL1xuICB9LCB7XG4gICAga2V5OiAnZW1pdCcsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGVtaXQoZXZlbnQsIGRhdGEpIHtcbiAgICAgIGlmICh0aGlzLnJlYWR5U3RhdGUgIT09IFNvY2tldElPLk9QRU4pIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdTb2NrZXRJTyBpcyBhbHJlYWR5IGluIENMT1NJTkcgb3IgQ0xPU0VEIHN0YXRlJyk7XG4gICAgICB9XG5cbiAgICAgIHZhciBtZXNzYWdlRXZlbnQgPSAoMCwgX2V2ZW50RmFjdG9yeS5jcmVhdGVNZXNzYWdlRXZlbnQpKHtcbiAgICAgICAgdHlwZTogZXZlbnQsXG4gICAgICAgIG9yaWdpbjogdGhpcy51cmwsXG4gICAgICAgIGRhdGE6IGRhdGFcbiAgICAgIH0pO1xuXG4gICAgICB2YXIgc2VydmVyID0gX25ldHdvcmtCcmlkZ2UyWydkZWZhdWx0J10uc2VydmVyTG9va3VwKHRoaXMudXJsKTtcblxuICAgICAgaWYgKHNlcnZlcikge1xuICAgICAgICBzZXJ2ZXIuZGlzcGF0Y2hFdmVudChtZXNzYWdlRXZlbnQsIGRhdGEpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8qXG4gICAgKiBTdWJtaXRzIGEgJ21lc3NhZ2UnIGV2ZW50IHRvIHRoZSBzZXJ2ZXIuXG4gICAgKlxuICAgICogU2hvdWxkIGJlaGF2ZSBleGFjdGx5IGxpa2UgV2ViU29ja2V0I3NlbmRcbiAgICAqXG4gICAgKiBodHRwczovL2dpdGh1Yi5jb20vc29ja2V0aW8vc29ja2V0LmlvLWNsaWVudC9ibG9iL21hc3Rlci9saWIvc29ja2V0LmpzI0wxMTNcbiAgICAqL1xuICB9LCB7XG4gICAga2V5OiAnc2VuZCcsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIHNlbmQoZGF0YSkge1xuICAgICAgdGhpcy5lbWl0KCdtZXNzYWdlJywgZGF0YSk7XG4gICAgfVxuXG4gICAgLypcbiAgICAqIEZvciByZWdpc3RlcmluZyBldmVudHMgdG8gYmUgcmVjZWl2ZWQgZnJvbSB0aGUgc2VydmVyXG4gICAgKi9cbiAgfSwge1xuICAgIGtleTogJ29uJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gb24odHlwZSwgY2FsbGJhY2spIHtcbiAgICAgIHRoaXMuYWRkRXZlbnRMaXN0ZW5lcih0eXBlLCBjYWxsYmFjayk7XG4gICAgfVxuXG4gICAgLypcbiAgICAgKiBKb2luIGEgcm9vbSBvbiBhIHNlcnZlclxuICAgICAqXG4gICAgICogaHR0cDovL3NvY2tldC5pby9kb2NzL3Jvb21zLWFuZC1uYW1lc3BhY2VzLyNqb2luaW5nLWFuZC1sZWF2aW5nXG4gICAgICovXG4gIH0sIHtcbiAgICBrZXk6ICdqb2luJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gam9pbihyb29tKSB7XG4gICAgICBfbmV0d29ya0JyaWRnZTJbJ2RlZmF1bHQnXS5hZGRNZW1iZXJzaGlwVG9Sb29tKHRoaXMsIHJvb20pO1xuICAgIH1cblxuICAgIC8qXG4gICAgICogR2V0IHRoZSB3ZWJzb2NrZXQgdG8gbGVhdmUgdGhlIHJvb21cbiAgICAgKlxuICAgICAqIGh0dHA6Ly9zb2NrZXQuaW8vZG9jcy9yb29tcy1hbmQtbmFtZXNwYWNlcy8jam9pbmluZy1hbmQtbGVhdmluZ1xuICAgICAqL1xuICB9LCB7XG4gICAga2V5OiAnbGVhdmUnLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBsZWF2ZShyb29tKSB7XG4gICAgICBfbmV0d29ya0JyaWRnZTJbJ2RlZmF1bHQnXS5yZW1vdmVNZW1iZXJzaGlwRnJvbVJvb20odGhpcywgcm9vbSk7XG4gICAgfVxuXG4gICAgLypcbiAgICAgKiBJbnZva2VzIGFsbCBsaXN0ZW5lciBmdW5jdGlvbnMgdGhhdCBhcmUgbGlzdGVuaW5nIHRvIHRoZSBnaXZlbiBldmVudC50eXBlIHByb3BlcnR5LiBFYWNoXG4gICAgICogbGlzdGVuZXIgd2lsbCBiZSBwYXNzZWQgdGhlIGV2ZW50IGFzIHRoZSBmaXJzdCBhcmd1bWVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBldmVudCAtIGV2ZW50IG9iamVjdCB3aGljaCB3aWxsIGJlIHBhc3NlZCB0byBhbGwgbGlzdGVuZXJzIG9mIHRoZSBldmVudC50eXBlIHByb3BlcnR5XG4gICAgICovXG4gIH0sIHtcbiAgICBrZXk6ICdkaXNwYXRjaEV2ZW50JyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gZGlzcGF0Y2hFdmVudChldmVudCkge1xuICAgICAgdmFyIF90aGlzMiA9IHRoaXM7XG5cbiAgICAgIGZvciAodmFyIF9sZW4gPSBhcmd1bWVudHMubGVuZ3RoLCBjdXN0b21Bcmd1bWVudHMgPSBBcnJheShfbGVuID4gMSA/IF9sZW4gLSAxIDogMCksIF9rZXkgPSAxOyBfa2V5IDwgX2xlbjsgX2tleSsrKSB7XG4gICAgICAgIGN1c3RvbUFyZ3VtZW50c1tfa2V5IC0gMV0gPSBhcmd1bWVudHNbX2tleV07XG4gICAgICB9XG5cbiAgICAgIHZhciBldmVudE5hbWUgPSBldmVudC50eXBlO1xuICAgICAgdmFyIGxpc3RlbmVycyA9IHRoaXMubGlzdGVuZXJzW2V2ZW50TmFtZV07XG5cbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShsaXN0ZW5lcnMpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgbGlzdGVuZXJzLmZvckVhY2goZnVuY3Rpb24gKGxpc3RlbmVyKSB7XG4gICAgICAgIGlmIChjdXN0b21Bcmd1bWVudHMubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGxpc3RlbmVyLmFwcGx5KF90aGlzMiwgY3VzdG9tQXJndW1lbnRzKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBSZWd1bGFyIFdlYlNvY2tldHMgZXhwZWN0IGEgTWVzc2FnZUV2ZW50IGJ1dCBTb2NrZXRpby5pbyBqdXN0IHdhbnRzIHJhdyBkYXRhXG4gICAgICAgICAgLy8gIHBheWxvYWQgaW5zdGFuY2VvZiBNZXNzYWdlRXZlbnQgd29ya3MsIGJ1dCB5b3UgY2FuJ3QgaXNudGFuY2Ugb2YgTm9kZUV2ZW50XG4gICAgICAgICAgLy8gIGZvciBub3cgd2UgZGV0ZWN0IGlmIHRoZSBvdXRwdXQgaGFzIGRhdGEgZGVmaW5lZCBvbiBpdFxuICAgICAgICAgIGxpc3RlbmVyLmNhbGwoX3RoaXMyLCBldmVudC5kYXRhID8gZXZlbnQuZGF0YSA6IGV2ZW50KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIFNvY2tldElPO1xufSkoX2V2ZW50VGFyZ2V0MlsnZGVmYXVsdCddKTtcblxuU29ja2V0SU8uQ09OTkVDVElORyA9IDA7XG5Tb2NrZXRJTy5PUEVOID0gMTtcblNvY2tldElPLkNMT1NJTkcgPSAyO1xuU29ja2V0SU8uQ0xPU0VEID0gMztcblxuLypcbiogU3RhdGljIGNvbnN0cnVjdG9yIG1ldGhvZHMgZm9yIHRoZSBJTyBTb2NrZXRcbiovXG52YXIgSU8gPSBmdW5jdGlvbiBpb0NvbnN0cnVjdG9yKHVybCkge1xuICByZXR1cm4gbmV3IFNvY2tldElPKHVybCk7XG59O1xuXG4vKlxuKiBBbGlhcyB0aGUgcmF3IElPKCkgY29uc3RydWN0b3JcbiovXG5JTy5jb25uZWN0ID0gZnVuY3Rpb24gaW9Db25uZWN0KHVybCkge1xuICAvKiBlc2xpbnQtZGlzYWJsZSBuZXctY2FwICovXG4gIHJldHVybiBJTyh1cmwpO1xuICAvKiBlc2xpbnQtZW5hYmxlIG5ldy1jYXAgKi9cbn07XG5cbmV4cG9ydHNbJ2RlZmF1bHQnXSA9IElPO1xubW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzWydkZWZhdWx0J107IiwiJ3VzZSBzdHJpY3QnO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxudmFyIF9jcmVhdGVDbGFzcyA9IChmdW5jdGlvbiAoKSB7IGZ1bmN0aW9uIGRlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoJ3ZhbHVlJyBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH0gcmV0dXJuIGZ1bmN0aW9uIChDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfTsgfSkoKTtcblxudmFyIF9nZXQgPSBmdW5jdGlvbiBnZXQoX3gyLCBfeDMsIF94NCkgeyB2YXIgX2FnYWluID0gdHJ1ZTsgX2Z1bmN0aW9uOiB3aGlsZSAoX2FnYWluKSB7IHZhciBvYmplY3QgPSBfeDIsIHByb3BlcnR5ID0gX3gzLCByZWNlaXZlciA9IF94NDsgX2FnYWluID0gZmFsc2U7IGlmIChvYmplY3QgPT09IG51bGwpIG9iamVjdCA9IEZ1bmN0aW9uLnByb3RvdHlwZTsgdmFyIGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKG9iamVjdCwgcHJvcGVydHkpOyBpZiAoZGVzYyA9PT0gdW5kZWZpbmVkKSB7IHZhciBwYXJlbnQgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2Yob2JqZWN0KTsgaWYgKHBhcmVudCA9PT0gbnVsbCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9IGVsc2UgeyBfeDIgPSBwYXJlbnQ7IF94MyA9IHByb3BlcnR5OyBfeDQgPSByZWNlaXZlcjsgX2FnYWluID0gdHJ1ZTsgZGVzYyA9IHBhcmVudCA9IHVuZGVmaW5lZDsgY29udGludWUgX2Z1bmN0aW9uOyB9IH0gZWxzZSBpZiAoJ3ZhbHVlJyBpbiBkZXNjKSB7IHJldHVybiBkZXNjLnZhbHVlOyB9IGVsc2UgeyB2YXIgZ2V0dGVyID0gZGVzYy5nZXQ7IGlmIChnZXR0ZXIgPT09IHVuZGVmaW5lZCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9IHJldHVybiBnZXR0ZXIuY2FsbChyZWNlaXZlcik7IH0gfSB9O1xuXG5mdW5jdGlvbiBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KG9iaikgeyByZXR1cm4gb2JqICYmIG9iai5fX2VzTW9kdWxlID8gb2JqIDogeyAnZGVmYXVsdCc6IG9iaiB9OyB9XG5cbmZ1bmN0aW9uIF9jbGFzc0NhbGxDaGVjayhpbnN0YW5jZSwgQ29uc3RydWN0b3IpIHsgaWYgKCEoaW5zdGFuY2UgaW5zdGFuY2VvZiBDb25zdHJ1Y3RvcikpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcignQ2Fubm90IGNhbGwgYSBjbGFzcyBhcyBhIGZ1bmN0aW9uJyk7IH0gfVxuXG5mdW5jdGlvbiBfaW5oZXJpdHMoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgaWYgKHR5cGVvZiBzdXBlckNsYXNzICE9PSAnZnVuY3Rpb24nICYmIHN1cGVyQ2xhc3MgIT09IG51bGwpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcignU3VwZXIgZXhwcmVzc2lvbiBtdXN0IGVpdGhlciBiZSBudWxsIG9yIGEgZnVuY3Rpb24sIG5vdCAnICsgdHlwZW9mIHN1cGVyQ2xhc3MpOyB9IHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcyAmJiBzdXBlckNsYXNzLnByb3RvdHlwZSwgeyBjb25zdHJ1Y3RvcjogeyB2YWx1ZTogc3ViQ2xhc3MsIGVudW1lcmFibGU6IGZhbHNlLCB3cml0YWJsZTogdHJ1ZSwgY29uZmlndXJhYmxlOiB0cnVlIH0gfSk7IGlmIChzdXBlckNsYXNzKSBPYmplY3Quc2V0UHJvdG90eXBlT2YgPyBPYmplY3Quc2V0UHJvdG90eXBlT2Yoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIDogc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzczsgfVxuXG52YXIgX3VyaWpzID0gcmVxdWlyZSgndXJpanMnKTtcblxudmFyIF91cmlqczIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF91cmlqcyk7XG5cbnZhciBfaGVscGVyc0RlbGF5ID0gcmVxdWlyZSgnLi9oZWxwZXJzL2RlbGF5Jyk7XG5cbnZhciBfaGVscGVyc0RlbGF5MiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX2hlbHBlcnNEZWxheSk7XG5cbnZhciBfZXZlbnRUYXJnZXQgPSByZXF1aXJlKCcuL2V2ZW50LXRhcmdldCcpO1xuXG52YXIgX2V2ZW50VGFyZ2V0MiA9IF9pbnRlcm9wUmVxdWlyZURlZmF1bHQoX2V2ZW50VGFyZ2V0KTtcblxudmFyIF9uZXR3b3JrQnJpZGdlID0gcmVxdWlyZSgnLi9uZXR3b3JrLWJyaWRnZScpO1xuXG52YXIgX25ldHdvcmtCcmlkZ2UyID0gX2ludGVyb3BSZXF1aXJlRGVmYXVsdChfbmV0d29ya0JyaWRnZSk7XG5cbnZhciBfaGVscGVyc0Nsb3NlQ29kZXMgPSByZXF1aXJlKCcuL2hlbHBlcnMvY2xvc2UtY29kZXMnKTtcblxudmFyIF9oZWxwZXJzQ2xvc2VDb2RlczIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF9oZWxwZXJzQ2xvc2VDb2Rlcyk7XG5cbnZhciBfZXZlbnRGYWN0b3J5ID0gcmVxdWlyZSgnLi9ldmVudC1mYWN0b3J5Jyk7XG5cbi8qXG4qIFRoZSBtYWluIHdlYnNvY2tldCBjbGFzcyB3aGljaCBpcyBkZXNpZ25lZCB0byBtaW1pY2sgdGhlIG5hdGl2ZSBXZWJTb2NrZXQgY2xhc3MgYXMgY2xvc2VcbiogYXMgcG9zc2libGUuXG4qXG4qIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9XZWJTb2NrZXRcbiovXG5cbnZhciBXZWJTb2NrZXQgPSAoZnVuY3Rpb24gKF9FdmVudFRhcmdldCkge1xuICBfaW5oZXJpdHMoV2ViU29ja2V0LCBfRXZlbnRUYXJnZXQpO1xuXG4gIC8qXG4gICogQHBhcmFtIHtzdHJpbmd9IHVybFxuICAqL1xuXG4gIGZ1bmN0aW9uIFdlYlNvY2tldCh1cmwpIHtcbiAgICB2YXIgcHJvdG9jb2wgPSBhcmd1bWVudHMubGVuZ3RoIDw9IDEgfHwgYXJndW1lbnRzWzFdID09PSB1bmRlZmluZWQgPyAnJyA6IGFyZ3VtZW50c1sxXTtcblxuICAgIF9jbGFzc0NhbGxDaGVjayh0aGlzLCBXZWJTb2NrZXQpO1xuXG4gICAgX2dldChPYmplY3QuZ2V0UHJvdG90eXBlT2YoV2ViU29ja2V0LnByb3RvdHlwZSksICdjb25zdHJ1Y3RvcicsIHRoaXMpLmNhbGwodGhpcyk7XG5cbiAgICBpZiAoIXVybCkge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignRmFpbGVkIHRvIGNvbnN0cnVjdCBcXCdXZWJTb2NrZXRcXCc6IDEgYXJndW1lbnQgcmVxdWlyZWQsIGJ1dCBvbmx5IDAgcHJlc2VudC4nKTtcbiAgICB9XG5cbiAgICB0aGlzLmJpbmFyeVR5cGUgPSAnYmxvYic7XG4gICAgdGhpcy51cmwgPSAoMCwgX3VyaWpzMlsnZGVmYXVsdCddKSh1cmwpLnRvU3RyaW5nKCk7XG4gICAgdGhpcy5yZWFkeVN0YXRlID0gV2ViU29ja2V0LkNPTk5FQ1RJTkc7XG4gICAgdGhpcy5wcm90b2NvbCA9ICcnO1xuXG4gICAgaWYgKHR5cGVvZiBwcm90b2NvbCA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHRoaXMucHJvdG9jb2wgPSBwcm90b2NvbDtcbiAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkocHJvdG9jb2wpICYmIHByb3RvY29sLmxlbmd0aCA+IDApIHtcbiAgICAgIHRoaXMucHJvdG9jb2wgPSBwcm90b2NvbFswXTtcbiAgICB9XG5cbiAgICAvKlxuICAgICogSW4gb3JkZXIgdG8gY2FwdHVyZSB0aGUgY2FsbGJhY2sgZnVuY3Rpb24gd2UgbmVlZCB0byBkZWZpbmUgY3VzdG9tIHNldHRlcnMuXG4gICAgKiBUbyBpbGx1c3RyYXRlOlxuICAgICogICBteVNvY2tldC5vbm9wZW4gPSBmdW5jdGlvbigpIHsgYWxlcnQodHJ1ZSkgfTtcbiAgICAqXG4gICAgKiBUaGUgb25seSB3YXkgdG8gY2FwdHVyZSB0aGF0IGZ1bmN0aW9uIGFuZCBob2xkIG9udG8gaXQgZm9yIGxhdGVyIGlzIHdpdGggdGhlXG4gICAgKiBiZWxvdyBjb2RlOlxuICAgICovXG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnRpZXModGhpcywge1xuICAgICAgb25vcGVuOiB7XG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICAgICAgZW51bWVyYWJsZTogdHJ1ZSxcbiAgICAgICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMubGlzdGVuZXJzLm9wZW47XG4gICAgICAgIH0sXG4gICAgICAgIHNldDogZnVuY3Rpb24gc2V0KGxpc3RlbmVyKSB7XG4gICAgICAgICAgdGhpcy5hZGRFdmVudExpc3RlbmVyKCdvcGVuJywgbGlzdGVuZXIpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgb25tZXNzYWdlOiB7XG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICAgICAgZW51bWVyYWJsZTogdHJ1ZSxcbiAgICAgICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMubGlzdGVuZXJzLm1lc3NhZ2U7XG4gICAgICAgIH0sXG4gICAgICAgIHNldDogZnVuY3Rpb24gc2V0KGxpc3RlbmVyKSB7XG4gICAgICAgICAgdGhpcy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgbGlzdGVuZXIpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgb25jbG9zZToge1xuICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICAgIHJldHVybiB0aGlzLmxpc3RlbmVycy5jbG9zZTtcbiAgICAgICAgfSxcbiAgICAgICAgc2V0OiBmdW5jdGlvbiBzZXQobGlzdGVuZXIpIHtcbiAgICAgICAgICB0aGlzLmFkZEV2ZW50TGlzdGVuZXIoJ2Nsb3NlJywgbGlzdGVuZXIpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgb25lcnJvcjoge1xuICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICAgIHJldHVybiB0aGlzLmxpc3RlbmVycy5lcnJvcjtcbiAgICAgICAgfSxcbiAgICAgICAgc2V0OiBmdW5jdGlvbiBzZXQobGlzdGVuZXIpIHtcbiAgICAgICAgICB0aGlzLmFkZEV2ZW50TGlzdGVuZXIoJ2Vycm9yJywgbGlzdGVuZXIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICB2YXIgc2VydmVyID0gX25ldHdvcmtCcmlkZ2UyWydkZWZhdWx0J10uYXR0YWNoV2ViU29ja2V0KHRoaXMsIHRoaXMudXJsKTtcblxuICAgIC8qXG4gICAgKiBUaGlzIGRlbGF5IGlzIG5lZWRlZCBzbyB0aGF0IHdlIGRvbnQgdHJpZ2dlciBhbiBldmVudCBiZWZvcmUgdGhlIGNhbGxiYWNrcyBoYXZlIGJlZW5cbiAgICAqIHNldHVwLiBGb3IgZXhhbXBsZTpcbiAgICAqXG4gICAgKiB2YXIgc29ja2V0ID0gbmV3IFdlYlNvY2tldCgnd3M6Ly9sb2NhbGhvc3QnKTtcbiAgICAqXG4gICAgKiAvLyBJZiB3ZSBkb250IGhhdmUgdGhlIGRlbGF5IHRoZW4gdGhlIGV2ZW50IHdvdWxkIGJlIHRyaWdnZXJlZCByaWdodCBoZXJlIGFuZCB0aGlzIGlzXG4gICAgKiAvLyBiZWZvcmUgdGhlIG9ub3BlbiBoYWQgYSBjaGFuY2UgdG8gcmVnaXN0ZXIgaXRzZWxmLlxuICAgICpcbiAgICAqIHNvY2tldC5vbm9wZW4gPSAoKSA9PiB7IC8vIHRoaXMgd291bGQgbmV2ZXIgYmUgY2FsbGVkIH07XG4gICAgKlxuICAgICogLy8gYW5kIHdpdGggdGhlIGRlbGF5IHRoZSBldmVudCBnZXRzIHRyaWdnZXJlZCBoZXJlIGFmdGVyIGFsbCBvZiB0aGUgY2FsbGJhY2tzIGhhdmUgYmVlblxuICAgICogLy8gcmVnaXN0ZXJlZCA6LSlcbiAgICAqL1xuICAgICgwLCBfaGVscGVyc0RlbGF5MlsnZGVmYXVsdCddKShmdW5jdGlvbiBkZWxheUNhbGxiYWNrKCkge1xuICAgICAgaWYgKHNlcnZlcikge1xuICAgICAgICB0aGlzLnJlYWR5U3RhdGUgPSBXZWJTb2NrZXQuT1BFTjtcbiAgICAgICAgc2VydmVyLmRpc3BhdGNoRXZlbnQoKDAsIF9ldmVudEZhY3RvcnkuY3JlYXRlRXZlbnQpKHsgdHlwZTogJ2Nvbm5lY3Rpb24nIH0pLCBzZXJ2ZXIsIHRoaXMpO1xuICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQoKDAsIF9ldmVudEZhY3RvcnkuY3JlYXRlRXZlbnQpKHsgdHlwZTogJ29wZW4nLCB0YXJnZXQ6IHRoaXMgfSkpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5yZWFkeVN0YXRlID0gV2ViU29ja2V0LkNMT1NFRDtcbiAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KCgwLCBfZXZlbnRGYWN0b3J5LmNyZWF0ZUV2ZW50KSh7IHR5cGU6ICdlcnJvcicsIHRhcmdldDogdGhpcyB9KSk7XG4gICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudCgoMCwgX2V2ZW50RmFjdG9yeS5jcmVhdGVDbG9zZUV2ZW50KSh7IHR5cGU6ICdjbG9zZScsIHRhcmdldDogdGhpcywgY29kZTogX2hlbHBlcnNDbG9zZUNvZGVzMlsnZGVmYXVsdCddLkNMT1NFX05PUk1BTCB9KSk7XG5cbiAgICAgICAgY29uc29sZS5lcnJvcignV2ViU29ja2V0IGNvbm5lY3Rpb24gdG8gXFwnJyArIHRoaXMudXJsICsgJ1xcJyBmYWlsZWQnKTtcbiAgICAgIH1cbiAgICB9LCB0aGlzKTtcbiAgfVxuXG4gIC8qXG4gICogVHJhbnNtaXRzIGRhdGEgdG8gdGhlIHNlcnZlciBvdmVyIHRoZSBXZWJTb2NrZXQgY29ubmVjdGlvbi5cbiAgKlxuICAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9XZWJTb2NrZXQjc2VuZCgpXG4gICovXG5cbiAgX2NyZWF0ZUNsYXNzKFdlYlNvY2tldCwgW3tcbiAgICBrZXk6ICdzZW5kJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gc2VuZChkYXRhKSB7XG4gICAgICBpZiAodGhpcy5yZWFkeVN0YXRlID09PSBXZWJTb2NrZXQuQ0xPU0lORyB8fCB0aGlzLnJlYWR5U3RhdGUgPT09IFdlYlNvY2tldC5DTE9TRUQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdXZWJTb2NrZXQgaXMgYWxyZWFkeSBpbiBDTE9TSU5HIG9yIENMT1NFRCBzdGF0ZScpO1xuICAgICAgfVxuXG4gICAgICB2YXIgbWVzc2FnZUV2ZW50ID0gKDAsIF9ldmVudEZhY3RvcnkuY3JlYXRlTWVzc2FnZUV2ZW50KSh7XG4gICAgICAgIHR5cGU6ICdtZXNzYWdlJyxcbiAgICAgICAgb3JpZ2luOiB0aGlzLnVybCxcbiAgICAgICAgZGF0YTogZGF0YVxuICAgICAgfSk7XG5cbiAgICAgIHZhciBzZXJ2ZXIgPSBfbmV0d29ya0JyaWRnZTJbJ2RlZmF1bHQnXS5zZXJ2ZXJMb29rdXAodGhpcy51cmwpO1xuXG4gICAgICBpZiAoc2VydmVyKSB7XG4gICAgICAgIHNlcnZlci5kaXNwYXRjaEV2ZW50KG1lc3NhZ2VFdmVudCwgZGF0YSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLypcbiAgICAqIENsb3NlcyB0aGUgV2ViU29ja2V0IGNvbm5lY3Rpb24gb3IgY29ubmVjdGlvbiBhdHRlbXB0LCBpZiBhbnkuXG4gICAgKiBJZiB0aGUgY29ubmVjdGlvbiBpcyBhbHJlYWR5IENMT1NFRCwgdGhpcyBtZXRob2QgZG9lcyBub3RoaW5nLlxuICAgICpcbiAgICAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9XZWJTb2NrZXQjY2xvc2UoKVxuICAgICovXG4gIH0sIHtcbiAgICBrZXk6ICdjbG9zZScsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGNsb3NlKCkge1xuICAgICAgaWYgKHRoaXMucmVhZHlTdGF0ZSAhPT0gV2ViU29ja2V0Lk9QRU4pIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgIH1cblxuICAgICAgdmFyIHNlcnZlciA9IF9uZXR3b3JrQnJpZGdlMlsnZGVmYXVsdCddLnNlcnZlckxvb2t1cCh0aGlzLnVybCk7XG4gICAgICB2YXIgY2xvc2VFdmVudCA9ICgwLCBfZXZlbnRGYWN0b3J5LmNyZWF0ZUNsb3NlRXZlbnQpKHtcbiAgICAgICAgdHlwZTogJ2Nsb3NlJyxcbiAgICAgICAgdGFyZ2V0OiB0aGlzLFxuICAgICAgICBjb2RlOiBfaGVscGVyc0Nsb3NlQ29kZXMyWydkZWZhdWx0J10uQ0xPU0VfTk9STUFMXG4gICAgICB9KTtcblxuICAgICAgX25ldHdvcmtCcmlkZ2UyWydkZWZhdWx0J10ucmVtb3ZlV2ViU29ja2V0KHRoaXMsIHRoaXMudXJsKTtcblxuICAgICAgdGhpcy5yZWFkeVN0YXRlID0gV2ViU29ja2V0LkNMT1NFRDtcbiAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChjbG9zZUV2ZW50KTtcblxuICAgICAgaWYgKHNlcnZlcikge1xuICAgICAgICBzZXJ2ZXIuZGlzcGF0Y2hFdmVudChjbG9zZUV2ZW50LCBzZXJ2ZXIpO1xuICAgICAgfVxuICAgIH1cbiAgfV0pO1xuXG4gIHJldHVybiBXZWJTb2NrZXQ7XG59KShfZXZlbnRUYXJnZXQyWydkZWZhdWx0J10pO1xuXG5XZWJTb2NrZXQuQ09OTkVDVElORyA9IDA7XG5XZWJTb2NrZXQuT1BFTiA9IDE7XG5XZWJTb2NrZXQuQ0xPU0lORyA9IDI7XG5XZWJTb2NrZXQuQ0xPU0VEID0gMztcblxuZXhwb3J0c1snZGVmYXVsdCddID0gV2ViU29ja2V0O1xubW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzWydkZWZhdWx0J107Il19
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 76d99f31c7..3b9f503a0b 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,134 +1,6 @@
-* Disallow calling `#deliver_later` after making local modifications to
- the message which would be lost when the delivery job is enqueued.
-
- Prevents a common, hard-to-find bug like:
-
- message = Notifier.welcome(user, foo)
- message.message_id = my_generated_message_id
- message.deliver_later
-
- The message_id is silently lost! *Only the mailer arguments are passed
- to the delivery job.*
-
- This raises an exception now. Make modifications to the message within
- the mailer method instead, or use a custom Active Job to manage delivery
- instead of using #deliver_later.
+* Exception handling: use `rescue_from` to handle exceptions raised by
+ mailer actions, by message delivery, and by deferred delivery jobs.
*Jeremy Daer*
-* Removes `-t` from default Sendmail arguments to match the underlying
- `Mail::Sendmail` setting.
-
- *Clayton Liggitt*
-
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* Add support for fragment caching in Action Mailer views.
-
- *Stan Lo*
-
-* Reset `ActionMailer::Base.deliveries` after every test in
- `ActionDispatch::IntegrationTest`.
-
- *Yves Senn*
-
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* No changes.
-
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* `config.action_mailer.default_url_options[:protocol]` is now set to `https` if `config.force_ssl` is set to `true`.
-
- *Andrew Kampjes*
-
-* Add `config.action_mailer.deliver_later_queue_name` configuration to set the
- mailer queue name.
-
- *Chris McGrath*
-
-* `assert_emails` in block form, uses the given number as expected value.
- This makes the error message much easier to understand.
-
- *Yuji Yaginuma*
-
-* Add support for inline images in mailer previews by using an interceptor
- class to convert cid: urls in image src attributes to data urls.
-
- *Andrew White*
-
-* Mailer preview now uses `url_for` to fix links to emails for apps running on
- a subdirectory.
-
- *Remo Mueller*
-
-* Mailer previews no longer crash when the `mail` method wasn't called
- (`NullMail`).
-
- Fixes #19849.
-
- *Yves Senn*
-
-* Make sure labels and values line up in mailer previews.
-
- *Yves Senn*
-
-* Add `assert_enqueued_emails` and `assert_no_enqueued_emails`.
-
- Example:
-
- def test_emails
- assert_enqueued_emails 2 do
- ContactMailer.welcome.deliver_later
- ContactMailer.welcome.deliver_later
- end
- end
-
- def test_no_emails
- assert_no_enqueued_emails do
- # No emails enqueued here
- end
- end
-
- *George Claghorn*
-
-* Add `_mailer` suffix to mailers created via generator, following the same
- naming convention used in controllers and jobs.
-
- *Carlos Souza*
-
-* Remove deprecated `*_path` helpers in email views.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `deliver` and `deliver!` methods.
-
- *claudiob*
-
-* Template lookup now respects default locale and I18n fallbacks.
-
- Given the following templates:
-
- mailer/demo.html.erb
- mailer/demo.en.html.erb
- mailer/demo.pt.html.erb
-
- Before this change, for a locale that doesn't have its associated file, the
- `mailer/demo.html.erb` would be rendered even if `en` was the default locale.
-
- Now `mailer/demo.en.html.erb` has precedence over the file without locale.
-
- Also, it is possible to give a fallback.
-
- mailer/demo.pt.html.erb
- mailer/demo.pt-BR.html.erb
-
- So if the locale is `pt-PT`, `mailer/demo.pt.html.erb` will be rendered given
- the right I18n fallback configuration.
-
- *Rafael Mendonça França*
-
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/actionmailer/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actionmailer/CHANGELOG.md) for previous changes.
diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile
index 54e6cff48d..416c9ee33a 100644
--- a/actionmailer/Rakefile
+++ b/actionmailer/Rakefile
@@ -4,7 +4,6 @@ desc "Default Task"
task default: [ :test ]
task :package
-task "package:clean"
# Run the unit tests
Rake::TestTask.new { |t|
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index fa6043fdd7..25e3bcb2e9 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -24,5 +24,5 @@ Gem::Specification.new do |s|
s.add_dependency 'activejob', version
s.add_dependency 'mail', ['~> 2.5', '>= 2.5.4']
- s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
+ s.add_dependency 'rails-dom-testing', '~> 2.0'
end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 4bb7842297..ea5af9e4f2 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -5,6 +5,7 @@ require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/module/anonymous'
require 'action_mailer/log_subscriber'
+require 'action_mailer/rescuable'
module ActionMailer
# Action Mailer allows you to send email from your application using a mailer model and views.
@@ -392,6 +393,7 @@ module ActionMailer
# of an OpenSSL verify constant (<tt>'none'</tt>, <tt>'peer'</tt>, <tt>'client_once'</tt>,
# <tt>'fail_if_no_peer_cert'</tt>) or directly the constant (<tt>OpenSSL::SSL::VERIFY_NONE</tt>,
# <tt>OpenSSL::SSL::VERIFY_PEER</tt>, ...).
+ # <tt>:ssl/:tls</tt> Enables the SMTP connection to use SMTP/TLS (SMTPS: SMTP over direct TLS connection)
#
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
@@ -419,6 +421,7 @@ module ActionMailer
# * <tt>deliver_later_queue_name</tt> - The name of the queue used with <tt>deliver_later</tt>.
class Base < AbstractController::Base
include DeliveryMethods
+ include Rescuable
include Previews
abstract!
diff --git a/actionmailer/lib/action_mailer/delivery_job.rb b/actionmailer/lib/action_mailer/delivery_job.rb
index 52772af2d3..d371c1b61a 100644
--- a/actionmailer/lib/action_mailer/delivery_job.rb
+++ b/actionmailer/lib/action_mailer/delivery_job.rb
@@ -3,11 +3,32 @@ require 'active_job'
module ActionMailer
# The <tt>ActionMailer::DeliveryJob</tt> class is used when you
# want to send emails outside of the request-response cycle.
+ #
+ # Exceptions are rescued and handled by the mailer class.
class DeliveryJob < ActiveJob::Base # :nodoc:
queue_as { ActionMailer::Base.deliver_later_queue_name }
+ rescue_from StandardError, with: :handle_exception_with_mailer_class
+
def perform(mailer, mail_method, delivery_method, *args) #:nodoc:
mailer.constantize.public_send(mail_method, *args).send(delivery_method)
end
+
+ private
+ # "Deserialize" the mailer class name by hand in case another argument
+ # (like a Global ID reference) raised DeserializationError.
+ def mailer_class
+ if mailer = Array(@serialized_arguments).first || Array(arguments).first
+ mailer.constantize
+ end
+ end
+
+ def handle_exception_with_mailer_class(exception)
+ if klass = mailer_class
+ klass.handle_exception exception
+ else
+ raise exception
+ end
+ end
end
end
diff --git a/actionmailer/lib/action_mailer/gem_version.rb b/actionmailer/lib/action_mailer/gem_version.rb
index cbe5fc3e64..7dafceef2b 100644
--- a/actionmailer/lib/action_mailer/gem_version.rb
+++ b/actionmailer/lib/action_mailer/gem_version.rb
@@ -6,9 +6,9 @@ module ActionMailer
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb
index d638057d72..c5ba5f9f1d 100644
--- a/actionmailer/lib/action_mailer/message_delivery.rb
+++ b/actionmailer/lib/action_mailer/message_delivery.rb
@@ -1,7 +1,6 @@
require 'delegate'
module ActionMailer
-
# The <tt>ActionMailer::MessageDelivery</tt> class is used by
# <tt>ActionMailer::Base</tt> when creating a new mailer.
# <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy
@@ -14,30 +13,35 @@ module ActionMailer
# Notifier.welcome(User.first).deliver_later # enqueue email delivery as a job through Active Job
# Notifier.welcome(User.first).message # a Mail::Message object
class MessageDelivery < Delegator
- def initialize(mailer, mail_method, *args) #:nodoc:
- @mailer = mailer
- @mail_method = mail_method
- @args = args
- @obj = nil
+ def initialize(mailer_class, action, *args) #:nodoc:
+ @mailer_class, @action, @args = mailer_class, action, args
+
+ # The mail is only processed if we try to call any methods on it.
+ # Typical usage will leave it unloaded and call deliver_later.
+ @processed_mailer = nil
+ @mail_message = nil
end
+ # Method calls are delegated to the Mail::Message that's ready to deliver.
def __getobj__ #:nodoc:
- @obj ||= begin
- mailer = @mailer.new
- mailer.process @mail_method, *@args
- mailer.message
- end
+ @mail_message ||= processed_mailer.message
end
- def __setobj__(obj) #:nodoc:
- @obj = obj
+ # Unused except for delegator internals (dup, marshaling).
+ def __setobj__(mail_message) #:nodoc:
+ @mail_message = mail_message
end
- # Returns the Mail::Message object
+ # Returns the resulting Mail::Message
def message
__getobj__
end
+ # Was the delegate loaded, causing the mailer action to be processed?
+ def processed?
+ @processed_mailer || @mail_message
+ end
+
# Enqueues the email to be delivered through Active Job. When the
# job runs it will send the email using +deliver_now!+. That means
# that the message will be sent bypassing checking +perform_deliveries+
@@ -78,7 +82,9 @@ module ActionMailer
# Notifier.welcome(User.first).deliver_now!
#
def deliver_now!
- message.deliver!
+ processed_mailer.handle_exceptions do
+ message.deliver!
+ end
end
# Delivers an email:
@@ -86,24 +92,33 @@ module ActionMailer
# Notifier.welcome(User.first).deliver_now
#
def deliver_now
- message.deliver
+ processed_mailer.handle_exceptions do
+ message.deliver
+ end
end
private
+ # Returns the processed Mailer instance. We keep this instance
+ # on hand so we can delegate exception handling to it.
+ def processed_mailer
+ @processed_mailer ||= @mailer_class.new.tap do |mailer|
+ mailer.process @action, *@args
+ end
+ end
def enqueue_delivery(delivery_method, options={})
- if @obj
- raise "You've accessed the message before asking to deliver it " \
- "later, so you may have made local changes that would be " \
- "silently lost if we enqueued a job to deliver it. Why? Only " \
+ if processed?
+ ::Kernel.raise "You've accessed the message before asking to " \
+ "deliver it later, so you may have made local changes that would " \
+ "be silently lost if we enqueued a job to deliver it. Why? Only " \
"the mailer method *arguments* are passed with the delivery job! " \
"Do not access the message in any way if you mean to deliver it " \
"later. Workarounds: 1. don't touch the message before calling " \
"#deliver_later, 2. only touch the message *within your mailer " \
"method*, or 3. use a custom Active Job instead of #deliver_later."
else
- args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args
- ActionMailer::DeliveryJob.set(options).perform_later(*args)
+ args = @mailer_class.name, @action.to_s, delivery_method.to_s, *@args
+ ::ActionMailer::DeliveryJob.set(options).perform_later(*args)
end
end
end
diff --git a/actionmailer/lib/action_mailer/rescuable.rb b/actionmailer/lib/action_mailer/rescuable.rb
new file mode 100644
index 0000000000..f2eabfa057
--- /dev/null
+++ b/actionmailer/lib/action_mailer/rescuable.rb
@@ -0,0 +1,27 @@
+module ActionMailer #:nodoc:
+ # Provides `rescue_from` for mailers. Wraps mailer action processing,
+ # mail job processing, and mail delivery.
+ module Rescuable
+ extend ActiveSupport::Concern
+ include ActiveSupport::Rescuable
+
+ class_methods do
+ def handle_exception(exception) #:nodoc:
+ rescue_with_handler(exception) || raise(exception)
+ end
+ end
+
+ def handle_exceptions #:nodoc:
+ yield
+ rescue => exception
+ rescue_with_handler(exception) || raise
+ end
+
+ private
+ def process(*)
+ handle_exceptions do
+ super
+ end
+ end
+ end
+end
diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb
index b4344eb167..22e1bdb5f1 100644
--- a/actionmailer/test/caching_test.rb
+++ b/actionmailer/test/caching_test.rb
@@ -125,15 +125,16 @@ class FunctionalFragmentCachingTest < BaseCachingTest
expected_body = "\"Welcome\""
assert_match expected_body, email.body.encoded
- assert_match "\"Welcome\"",
+ assert_match expected_body,
@store.read("views/caching/#{template_digest("caching_mailer/fragment_cache")}")
end
def test_fragment_caching_in_partials
email = @mailer.fragment_cache_in_partials
- assert_match(/Old fragment caching in a partial/, email.body.encoded)
+ expected_body = 'Old fragment caching in a partial'
+ assert_match(expected_body, email.body.encoded)
- assert_match("Old fragment caching in a partial",
+ assert_match(expected_body,
@store.read("views/caching/#{template_digest("caching_mailer/_partial")}"))
end
@@ -145,6 +146,47 @@ class FunctionalFragmentCachingTest < BaseCachingTest
assert_match expected_body, @store.read("views/no_digest")
end
+ def test_fragment_caching_options
+ time = Time.now
+ email = @mailer.fragment_caching_options
+ expected_body = "No Digest"
+
+ assert_match expected_body, email.body.encoded
+ Time.stub(:now, time + 11) do
+ assert_nil @store.read("views/no_digest")
+ end
+ end
+
+ def test_multipart_fragment_caching
+ email = @mailer.multipart_cache
+
+ expected_text_body = "\"Welcome text\""
+ expected_html_body = "\"Welcome html\""
+ encoded_body = email.body.encoded
+ assert_match expected_text_body, encoded_body
+ assert_match expected_html_body, encoded_body
+ assert_match expected_text_body,
+ @store.read("views/text_caching")
+ assert_match expected_html_body,
+ @store.read("views/html_caching")
+ end
+
+ def test_fragment_cache_instrumentation
+ payload = nil
+
+ subscriber = proc do |*args|
+ event = ActiveSupport::Notifications::Event.new(*args)
+ payload = event.payload
+ end
+
+ ActiveSupport::Notifications.subscribed(subscriber, "read_fragment.action_mailer") do
+ @mailer.fragment_cache
+ end
+
+ assert_equal "caching_mailer", payload[:mailer]
+ assert_equal "views/caching/#{template_digest("caching_mailer/fragment_cache")}", payload[:key]
+ end
+
private
def template_digest(name)
diff --git a/actionmailer/test/fixtures/caching_mailer/fragment_caching_options.html.erb b/actionmailer/test/fixtures/caching_mailer/fragment_caching_options.html.erb
new file mode 100644
index 0000000000..0541ac321b
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/fragment_caching_options.html.erb
@@ -0,0 +1,3 @@
+<%= cache :no_digest, skip_digest: true, expires_in: 0 do %>
+ No Digest
+<% end %>
diff --git a/actionmailer/test/fixtures/caching_mailer/multipart_cache.html.erb b/actionmailer/test/fixtures/caching_mailer/multipart_cache.html.erb
new file mode 100644
index 0000000000..0d26baa2d7
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/multipart_cache.html.erb
@@ -0,0 +1,3 @@
+<% cache :html_caching, skip_digest: true do %>
+ "Welcome html"
+<% end %>
diff --git a/actionmailer/test/fixtures/caching_mailer/multipart_cache.text.erb b/actionmailer/test/fixtures/caching_mailer/multipart_cache.text.erb
new file mode 100644
index 0000000000..ef97326e03
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/multipart_cache.text.erb
@@ -0,0 +1,3 @@
+<% cache :text_caching, skip_digest: true do %>
+ "Welcome text"
+<% end %>
diff --git a/actionmailer/test/mailers/caching_mailer.rb b/actionmailer/test/mailers/caching_mailer.rb
index 345d267a36..92d3cff7c9 100644
--- a/actionmailer/test/mailers/caching_mailer.rb
+++ b/actionmailer/test/mailers/caching_mailer.rb
@@ -12,4 +12,12 @@ class CachingMailer < ActionMailer::Base
def skip_fragment_cache_digesting
mail(subject: "welcome", template_name: "skip_fragment_cache_digesting")
end
+
+ def fragment_caching_options
+ mail(subject: "welcome", template_name: "fragment_caching_options")
+ end
+
+ def multipart_cache
+ mail(subject: "welcome", template_name: "multipart_cache")
+ end
end
diff --git a/actionmailer/test/mailers/delayed_mailer.rb b/actionmailer/test/mailers/delayed_mailer.rb
index 62d4baa434..e6211ef028 100644
--- a/actionmailer/test/mailers/delayed_mailer.rb
+++ b/actionmailer/test/mailers/delayed_mailer.rb
@@ -1,6 +1,26 @@
+require 'active_job/arguments'
+
+class DelayedMailerError < StandardError; end
+
class DelayedMailer < ActionMailer::Base
+ cattr_accessor :last_error
+ cattr_accessor :last_rescue_from_instance
+
+ rescue_from DelayedMailerError do |error|
+ @@last_error = error
+ @@last_rescue_from_instance = self
+ end
+
+ rescue_from ActiveJob::DeserializationError do |error|
+ @@last_error = error
+ @@last_rescue_from_instance = self
+ end
def test_message(*)
mail(from: 'test-sender@test.com', to: 'test-receiver@test.com', subject: 'Test Subject', body: 'Test Body')
end
+
+ def test_raise(klass_name)
+ raise klass_name.constantize, 'boom'
+ end
end
diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb
index f06d69369f..aaed94d519 100644
--- a/actionmailer/test/message_delivery_test.rb
+++ b/actionmailer/test/message_delivery_test.rb
@@ -12,16 +12,23 @@ class MessageDeliveryTest < ActiveSupport::TestCase
ActionMailer::Base.deliver_later_queue_name = :test_queue
ActionMailer::Base.delivery_method = :test
ActiveJob::Base.logger = Logger.new(nil)
- @mail = DelayedMailer.test_message(1, 2, 3)
ActionMailer::Base.deliveries.clear
ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
+
+ DelayedMailer.last_error = nil
+ DelayedMailer.last_rescue_from_instance = nil
+
+ @mail = DelayedMailer.test_message(1, 2, 3)
end
teardown do
ActiveJob::Base.logger = @previous_logger
ActionMailer::Base.delivery_method = @previous_delivery_method
ActionMailer::Base.deliver_later_queue_name = @previous_deliver_later_queue_name
+
+ DelayedMailer.last_error = nil
+ DelayedMailer.last_rescue_from_instance = nil
end
test 'should have a message' do
@@ -101,4 +108,46 @@ class MessageDeliveryTest < ActiveSupport::TestCase
@mail.deliver_later
end
end
+
+ test 'job delegates error handling to mailer' do
+ # Superclass not rescued by mailer's rescue_from RuntimeError
+ message = DelayedMailer.test_raise('StandardError')
+ assert_raise(StandardError) { message.deliver_later }
+ assert_nil DelayedMailer.last_error
+ assert_nil DelayedMailer.last_rescue_from_instance
+
+ # Rescued by mailer's rescue_from RuntimeError
+ message = DelayedMailer.test_raise('DelayedMailerError')
+ assert_nothing_raised { message.deliver_later }
+ assert_equal 'boom', DelayedMailer.last_error.message
+ assert_kind_of DelayedMailer, DelayedMailer.last_rescue_from_instance
+ end
+
+ class DeserializationErrorFixture
+ include GlobalID::Identification
+
+ def self.find(id)
+ raise 'boom, missing find'
+ end
+
+ attr_reader :id
+ def initialize(id = 1)
+ @id = id
+ end
+
+ def to_global_id(options = {})
+ super app: 'foo'
+ end
+ end
+
+ test 'job delegates deserialization errors to mailer class' do
+ # Inject an argument that can't be deserialized.
+ message = DelayedMailer.test_message(DeserializationErrorFixture.new)
+
+ # DeserializationError is raised, rescued, and delegated to the handler
+ # on the mailer class.
+ assert_nothing_raised { message.deliver_later }
+ assert_equal DelayedMailer, DelayedMailer.last_rescue_from_instance
+ assert_equal 'Error while trying to deserialize arguments: boom, missing find', DelayedMailer.last_error.message
+ end
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 5c856f1b36..be911b147c 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,813 +1,2 @@
-* Routing: Refactor `:action` default handling to ensure that path
- parameters are not mutated during route generation.
- *Andrew White*
-
-* Add extension synonyms `yml` and `yaml` for MIME type `application/x-yaml`.
-
- *bogdanvlviv*
-
-* Adds support for including ActionController::Cookies in API controllers.
- Previously, including the module would raise when trying to define
- a `cookies` helper method. Skip calling #helper_method if it is not
- defined -- if we don't have helpers, we needn't define one.
-
- Fixes #24304
-
- *Ryan T. Hosford*
-
-* ETags: Introduce `Response#strong_etag=` and `#weak_etag=` and analogous
- options for `fresh_when` and `stale?`. `Response#etag=` sets a weak ETag.
-
- Strong ETags are desirable when you're serving byte-for-byte identical
- responses that support Range requests, like PDFs or videos (typically
- done by reproxying the response from a backend storage service).
- Also desirable when fronted by some CDNs that support strong ETags
- only, like Akamai.
-
- *Jeremy Daer*
-
-* ETags: No longer strips quotes (") from ETag values before comparing them.
- Quotes are significant, part of the ETag. A quoted ETag and an unquoted
- one are not the same entity.
-
- *Jeremy Daer*
-
-* ETags: Support `If-None-Match: *`. Rarely useful for GET requests; meant
- to provide some optimistic concurrency control for PUT requests.
-
- *Jeremy Daer*
-
-* `ActionDispatch::ParamsParser` is deprecated and was removed from the middleware
- stack. To configure the parameter parsers use `ActionDispatch::Request.parameter_parsers=`.
-
- *tenderlove*
-
-* When a `respond_to` collector with a block doesn't have a response, then
- a `:no_content` response should be rendered. This brings the default
- rendering behavior introduced by https://github.com/rails/rails/issues/19036
- to controller methods employing `respond_to`.
-
- *Justin Coyne*
-
-* Add `ActionController::Parameters#dig` on Ruby 2.3 and greater, which
- behaves the same as `Hash#dig`.
-
- *Sean Griffin*
-
-* Add request headers in the payload of the `start_processing.action_controller`
- and `process_action.action_controller` notifications.
-
- *Gareth du Plooy*
-
-* Add `action_dispatch_integration_test` load hook. The hook can be used to
- extend `ActionDispatch::IntegrationTest` once it has been loaded.
-
- *Yuichiro Kaneko*
-
-* Update default rendering policies when the controller action did
- not explicitly indicate a response.
-
- For API controllers, the implicit render always renders "204 No Content"
- and does not account for any templates.
-
- For other controllers, the following conditions are checked:
-
- First, if a template exists for the controller action, it is rendered.
- This template lookup takes into account the action name, locales, format,
- variant, template handlers, etc. (see `render` for details).
-
- Second, if other templates exist for the controller action but is not in
- the right format (or variant, etc.), an `ActionController::UnknownFormat`
- is raised. The list of available templates is assumed to be a complete
- enumeration of all the possible formats (or variants, etc.); that is,
- having only HTML and JSON templates indicate that the controller action is
- not meant to handle XML requests.
-
- Third, if the current request is an "interactive" browser request (the user
- navigated here by entering the URL in the address bar, submitting a form,
- clicking on a link, etc. as opposed to an XHR or non-browser API request),
- `ActionView::UnknownFormat` is raised to display a helpful error
- message.
-
- Finally, it falls back to the same "204 No Content" behavior as API controllers.
-
- *Godfrey Chan*, *Jon Moss*, *Kasper Timm Hansen*, *Mike Clark*, *Matthew Draper*
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* Add "application/gzip" as a default mime type.
-
- *Mehmet Emin İNAÇ*
-
-* Add request encoding and response parsing to integration tests.
-
- What previously was:
-
- ```ruby
- require 'test_helper'
-
- class ApiTest < ActionDispatch::IntegrationTest
- test 'creates articles' do
- assert_difference -> { Article.count } do
- post articles_path(format: :json),
- params: { article: { title: 'Ahoy!' } }.to_json,
- headers: { 'Content-Type' => 'application/json' }
- end
-
- assert_equal({ 'id' => Article.last.id, 'title' => 'Ahoy!' }, JSON.parse(response.body))
- end
- end
- ```
-
- Can now be written as:
-
- ```ruby
- require 'test_helper'
-
- class ApiTest < ActionDispatch::IntegrationTest
- test 'creates articles' do
- assert_difference -> { Article.count } do
- post articles_path, params: { article: { title: 'Ahoy!' } }, as: :json
- end
-
- assert_equal({ 'id' => Article.last.id, 'title' => 'Ahoy!' }, response.parsed_body)
- end
- end
- ```
-
- Passing `as: :json` to integration test request helpers will set the format,
- content type and encode the parameters as JSON.
-
- Then on the response side, `parsed_body` will parse the body according to the
- content type the response has.
-
- Currently JSON is the only supported MIME type. Add your own with
- `ActionDispatch::IntegrationTest.register_encoder`.
-
- *Kasper Timm Hansen*
-
-* Add "image/svg+xml" as a default mime type.
-
- *DHH*
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* Add `-g` and `-c` options to `bin/rails routes`. These options return the url `name`, `verb` and
- `path` field that match the pattern or match a specific controller.
-
- Deprecate `CONTROLLER` env variable in `bin/rails routes`.
-
- See #18902.
-
- *Anton Davydov*, *Vipul A M*
-
-* Response etags to always be weak: Prefixes 'W/' to value returned by
- `ActionDispatch::Http::Cache::Response#etag=`, such that etags set in
- `fresh_when` and `stale?` are weak.
-
- Fixes #17556.
-
- *Abhishek Yadav*
-
-* Provide the name of HTTP Status code in assertions.
-
- *Sean Collins*
-
-* More explicit error message when running `rake routes`. `CONTROLLER` argument
- can now be supplied in different ways:
- `Rails::WelcomeController`, `Rails::Welcome`, `rails/welcome`.
-
- Fixes #22918.
-
- *Edouard Chin*
-
-* Allow `ActionController::Parameters` instances as an argument to URL
- helper methods. An `ArgumentError` will be raised if the passed parameters
- are not secure.
-
- Fixes #22832.
-
- *Prathamesh Sonpatki*
-
-* Add option for per-form CSRF tokens.
-
- *Greg Ose*, *Ben Toews*
-
-* Fix `ActionController::Parameters#convert_parameters_to_hashes` to return filtered
- or unfiltered values based on from where it is called, `to_h` or `to_unsafe_h`
- respectively.
-
- Fixes #22841.
-
- *Prathamesh Sonpatki*
-
-* Add `ActionController::Parameters#include?`
-
- *Justin Coyne*
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* Deprecate `redirect_to :back` in favor of `redirect_back`, which accepts a
- required `fallback_location` argument, thus eliminating the possibility of a
- `RedirectBackError`.
-
- *Derek Prior*
-
-* Add `redirect_back` method to `ActionController::Redirecting` to provide a
- way to safely redirect to the `HTTP_REFERER` if it is present, falling back
- to a provided redirect otherwise.
-
- *Derek Prior*
-
-* `ActionController::TestCase` will be moved to its own gem in Rails 5.1.
-
- With the speed improvements made to `ActionDispatch::IntegrationTest` we no
- longer need to keep two separate code bases for testing controllers. In
- Rails 5.1 `ActionController::TestCase` will be deprecated and moved into a
- gem outside of Rails source.
-
- This is a documentation deprecation so that going forward new tests will use
- `ActionDispatch::IntegrationTest` instead of `ActionController::TestCase`.
-
- *Eileen M. Uchitelle*
-
-* Add a `response_format` option to `ActionDispatch::DebugExceptions`
- to configure the format of the response when errors occur in
- development mode.
-
- If `response_format` is `:default` the debug info will be rendered
- in an HTML page. In the other hand, if the provided value is `:api`
- the debug info will be rendered in the original response format.
-
- *Jorge Bejar*
-
-* Change the `protect_from_forgery` prepend default to `false`.
-
- Per this comment
- https://github.com/rails/rails/pull/18334#issuecomment-69234050 we want
- `protect_from_forgery` to default to `prepend: false`.
-
- `protect_from_forgery` will now be inserted into the callback chain at the
- point it is called in your application. This is useful for cases where you
- want to `protect_from_forgery` after you perform required authentication
- callbacks or other callbacks that are required to run after forgery protection.
-
- If you want `protect_from_forgery` callbacks to always run first, regardless of
- position they are called in your application then you can add `prepend: true`
- to your `protect_from_forgery` call.
-
- Example:
-
- ```ruby
- protect_from_forgery prepend: true
- ```
-
- *Eileen M. Uchitelle*
-
-* In url_for, never append a question mark to the URL when the query string
- is empty anyway. (It used to do that when called like `url_for(controller:
- 'x', action: 'y', q: {})`.)
-
- *Paul Grayson*
-
-* Catch invalid UTF-8 querystring values and respond with BadRequest
-
- Check querystring params for invalid UTF-8 characters, and raise an
- ActionController::BadRequest error if present. Previously these strings
- would typically trigger errors further down the stack.
-
- *Grey Baker*
-
-* Parse RSS/ATOM responses as XML, not HTML.
-
- *Alexander Kaupanin*
-
-* Show helpful message in `BadRequest` exceptions due to invalid path
- parameter encodings.
-
- Fixes #21923.
-
- *Agis Anastasopoulos*
-
-* Add the ability of returning arbitrary headers to `ActionDispatch::Static`.
-
- Now ActionDispatch::Static can accept HTTP headers so that developers
- will have control of returning arbitrary headers like
- 'Access-Control-Allow-Origin' when a response is delivered. They can be
- configured with `#config`:
-
- Example:
-
- config.public_file_server.headers = {
- "Cache-Control" => "public, max-age=60",
- "Access-Control-Allow-Origin" => "http://rubyonrails.org"
- }
-
- *Yuki Nishijima*
-
-* Allow multiple `root` routes in same scope level. Example:
-
- Example:
-
- root 'blog#show', constraints: ->(req) { Hostname.blog_site?(req.host) }
- root 'landing#show'
-
- *Rafael Sales*
-
-* Fix regression in mounted engine named routes generation for app deployed to
- a subdirectory. `relative_url_root` was prepended to the path twice (e.g.
- "/subdir/subdir/engine_path" instead of "/subdir/engine_path")
-
- Fixes #20920. Fixes #21459.
-
- *Matthew Erhard*
-
-* `ActionDispatch::Response#new` no longer applies default headers. If you want
- default headers applied to the response object, then call
- `ActionDispatch::Response.create`. This change only impacts people who are
- directly constructing an `ActionDispatch::Response` object.
-
-* Accessing mime types via constants like `Mime::HTML` is deprecated. Please
- change code like this:
-
- Mime::HTML
-
- To this:
-
- Mime[:html]
-
- This change is so that Rails will not manage a list of constants, and fixes
- an issue where if a type isn't registered you could possibly get the wrong
- object.
-
- `Mime[:html]` is available in older versions of Rails, too, so you can
- safely change libraries and plugins and maintain compatibility with
- multiple versions of Rails.
-
-* `url_for` does not modify its arguments when generating polymorphic URLs.
-
- *Bernerd Schaefer*
-
-* Make it easier to opt in to `config.force_ssl` and `config.ssl_options` by
- making them less dangerous to try and easier to disable.
-
- SSL redirect:
- * Move `:host` and `:port` options within `redirect: { … }`. Deprecate.
- * Introduce `:status` and `:body` to customize the redirect response.
- The 301 permanent default makes it difficult to test the redirect and
- back out of it since browsers remember the 301. Test with a 302 or 307
- instead, then switch to 301 once you're confident that all is well.
-
- HTTP Strict Transport Security (HSTS):
- * Shorter max-age. Shorten the default max-age from 1 year to 180 days,
- the low end for https://www.ssllabs.com/ssltest/ grading and greater
- than the 18-week minimum to qualify for browser preload lists.
- * Disabling HSTS. Setting `hsts: false` now sets `hsts { expires: 0 }`
- instead of omitting the header. Omitting does nothing to disable HSTS
- since browsers hang on to your previous settings until they expire.
- Sending `{ hsts: { expires: 0 }}` flushes out old browser settings and
- actually disables HSTS:
- http://tools.ietf.org/html/rfc6797#section-6.1.1
- * HSTS Preload. Introduce `preload: true` to set the `preload` flag,
- indicating that your site may be included in browser preload lists,
- including Chrome, Firefox, Safari, IE11, and Edge. Submit your site:
- https://hstspreload.appspot.com
-
- *Jeremy Daer*
-
-* Update `ActionController::TestSession#fetch` to behave more like
- `ActionDispatch::Request::Session#fetch` when using non-string keys.
-
- *Jeremy Friesen*
-
-* Using strings or symbols for middleware class names is deprecated. Convert
- things like this:
-
- middleware.use "Foo::Bar"
-
- to this:
-
- middleware.use Foo::Bar
-
-* `ActionController::TestSession` now accepts a default value as well as
- a block for generating a default value based off the key provided.
-
- This fixes calls to `session#fetch` in `ApplicationController` instances that
- take more two arguments or a block from raising `ArgumentError: wrong
- number of arguments (2 for 1)` when performing controller tests.
-
- *Matthew Gerrior*
-
-* Fix `ActionController::Parameters#fetch` overwriting `KeyError` returned by
- default block.
-
- *Jonas Schuber Erlandsson*, *Roque Pinel*
-
-* `ActionController::Parameters` no longer inherits from
- `HashWithIndifferentAccess`
-
- Inheriting from `HashWithIndifferentAccess` allowed users to call any
- enumerable methods on `Parameters` object, resulting in a risk of losing the
- `permitted?` status or even getting back a pure `Hash` object instead of
- a `Parameters` object with proper sanitization.
-
- By not inheriting from `HashWithIndifferentAccess`, we are able to make
- sure that all methods that are defined in `Parameters` object will return
- a proper `Parameters` object with a correct `permitted?` flag.
-
- *Prem Sichanugrist*
-
-* Replaced `ActiveSupport::Concurrency::Latch` with `Concurrent::CountDownLatch`
- from the concurrent-ruby gem.
-
- *Jerry D'Antonio*
-
-* Add ability to filter parameters based on parent keys.
-
- # matches {credit_card: {code: "xxxx"}}
- # doesn't match {file: { code: "xxxx"}}
- config.filter_parameters += [ "credit_card.code" ]
-
- See #13897.
-
- *Guillaume Malette*
-
-* Deprecate passing first parameter as `Hash` and default status code for `head` method.
-
- *Mehmet Emin İNAÇ*
-
-* Adds`Rack::Utils::ParameterTypeError` and `Rack::Utils::InvalidParameterError`
- to the rescue_responses hash in `ExceptionWrapper` (Rack recommends
- integrators serve 400s for both of these).
-
- *Grey Baker*
-
-* Add support for API only apps.
- `ActionController::API` is added as a replacement of
- `ActionController::Base` for this kind of applications.
-
- *Santiago Pastorino*, *Jorge Bejar*
-
-* Remove `assigns` and `assert_template`. Both methods have been extracted
- into a gem at https://github.com/rails/rails-controller-testing.
-
- See #18950.
-
- *Alan Guo Xiang Tan*
-
-* `FileHandler` and `Static` middleware initializers accept `index` argument
- to configure the directory index file name. Defaults to `index` (as in
- `index.html`).
-
- See #20017.
-
- *Eliot Sykes*
-
-* Deprecate `:nothing` option for `render` method.
-
- *Mehmet Emin İNAÇ*
-
-* Fix `rake routes` not showing the right format when
- nesting multiple routes.
-
- See #18373.
-
- *Ravil Bayramgalin*
-
-* Add ability to override default form builder for a controller.
-
- class AdminController < ApplicationController
- default_form_builder AdminFormBuilder
- end
-
- *Kevin McPhillips*
-
-* For actions with no corresponding templates, render `head :no_content`
- instead of raising an error. This allows for slimmer API controller
- methods that simply work, without needing further instructions.
-
- See #19036.
-
- *Stephen Bussey*
-
-* Provide friendlier access to request variants.
-
- request.variant = :phone
- request.variant.phone? # true
- request.variant.tablet? # false
-
- request.variant = [:phone, :tablet]
- request.variant.phone? # true
- request.variant.desktop? # false
- request.variant.any?(:phone, :desktop) # true
- request.variant.any?(:desktop, :watch) # false
-
- *George Claghorn*
-
-* Fix regression where a gzip file response would have a Content-type,
- even when it was a 304 status code.
-
- See #19271.
-
- *Kohei Suzuki*
-
-* Fix handling of empty `X_FORWARDED_HOST` header in `raw_host_with_port`.
-
- Previously, an empty `X_FORWARDED_HOST` header would cause
- `Actiondispatch::Http:URL.raw_host_with_port` to return `nil`, causing
- `Actiondispatch::Http:URL.host` to raise a `NoMethodError`.
-
- *Adam Forsyth*
-
-* Allow `Bearer` as token-keyword in `Authorization-Header`.
-
- Additionally to `Token`, the keyword `Bearer` is acceptable as a keyword
- for the auth-token. The `Bearer` keyword is described in the original
- OAuth RFC and used in libraries like Angular-JWT.
-
- See #19094.
-
- *Peter Schröder*
-
-* Drop request class from `RouteSet` constructor.
-
- If you would like to use a custom request class, please subclass and implement
- the `request_class` method.
-
- *tenderlove@ruby-lang.org*
-
-* Fallback to `ENV['RAILS_RELATIVE_URL_ROOT']` in `url_for`.
-
- Fixed an issue where the `RAILS_RELATIVE_URL_ROOT` environment variable is not
- prepended to the path when `url_for` is called. If `SCRIPT_NAME` (used by Rack)
- is set, it takes precedence.
-
- Fixes #5122.
-
- *Yasyf Mohamedali*
-
-* Partitioning of routes is now done when the routes are being drawn. This
- helps to decrease the time spent filtering the routes during the first request.
-
- *Guo Xiang Tan*
-
-* Fix regression in functional tests. Responses should have default headers
- assigned.
-
- See #18423.
-
- *Jeremy Kemper*, *Yves Senn*
-
-* Deprecate `AbstractController#skip_action_callback` in favor of individual skip_callback methods
- (which can be made to raise an error if no callback was removed).
-
- *Iain Beeston*
-
-* Alias the `ActionDispatch::Request#uuid` method to `ActionDispatch::Request#request_id`.
- Due to implementation, `config.log_tags = [:request_id]` also works in substitute
- for `config.log_tags = [:uuid]`.
-
- *David Ilizarov*
-
-* Change filter on /rails/info/routes to use an actual path regexp from rails
- and not approximate javascript version. Oniguruma supports much more
- extensive list of features than javascript regexp engine.
-
- Fixes #18402.
-
- *Ravil Bayramgalin*
-
-* Non-string authenticity tokens do not raise NoMethodError when decoding
- the masked token.
-
- *Ville Lautanala*
-
-* Add `http_cache_forever` to Action Controller, so we can cache a response
- that never gets expired.
-
- *arthurnn*
-
-* `ActionController#translate` supports symbols as shortcuts.
- When a shortcut is given it also performs the lookup without the action
- name.
-
- *Max Melentiev*
-
-* Expand `ActionController::ConditionalGet#fresh_when` and `stale?` to also
- accept a collection of records as the first argument, so that the
- following code can be written in a shorter form.
-
- # Before
- def index
- @articles = Article.all
- fresh_when(etag: @articles, last_modified: @articles.maximum(:updated_at))
- end
-
- # After
- def index
- @articles = Article.all
- fresh_when(@articles)
- end
-
- *claudiob*
-
-* Explicitly ignored wildcard verbs when searching for HEAD routes before fallback
-
- Fixes an issue where a mounted rack app at root would intercept the HEAD
- request causing an incorrect behavior during the fall back to GET requests.
-
- Example:
-
- draw do
- get '/home' => 'test#index'
- mount rack_app, at: '/'
- end
- head '/home'
- assert_response :success
-
- In this case, a HEAD request runs through the routes the first time and fails
- to match anything. Then, it runs through the list with the fallback and matches
- `get '/home'`. The original behavior would match the rack app in the first pass.
-
- *Terence Sun*
-
-* Migrating xhr methods to keyword arguments syntax
- in `ActionController::TestCase` and `ActionDispatch::Integration`
-
- Old syntax:
-
- xhr :get, :create, params: { id: 1 }
-
- New syntax example:
-
- get :create, params: { id: 1 }, xhr: true
-
- *Kir Shatrov*
-
-* Migrating to keyword arguments syntax in `ActionController::TestCase` and
- `ActionDispatch::Integration` HTTP request methods.
-
- Example:
-
- post :create, params: { y: x }, session: { a: 'b' }
- get :view, params: { id: 1 }
- get :view, params: { id: 1 }, format: :json
-
- *Kir Shatrov*
-
-* Preserve default url options when generating URLs.
-
- Fixes an issue that would cause `default_url_options` to be lost when
- generating URLs with fewer positional arguments than parameters in the
- route definition.
-
- *Tekin Suleyman*
-
-* Deprecate `*_via_redirect` integration test methods.
-
- Use `follow_redirect!` manually after the request call for the same behavior.
-
- *Aditya Kapoor*
-
-* Add `ActionController::Renderer` to render arbitrary templates
- outside controller actions.
-
- Its functionality is accessible through class methods `render` and
- `renderer` of `ActionController::Base`.
-
- *Ravil Bayramgalin*
-
-* Support `:assigns` option when rendering with controllers/mailers.
-
- *Ravil Bayramgalin*
-
-* Default headers, removed in controller actions, are no longer reapplied on
- the test response.
-
- *Jonas Baumann*
-
-* Deprecate all `*_filter` callbacks in favor of `*_action` callbacks.
-
- *Rafael Mendonça França*
-
-* Allow you to pass `prepend: false` to `protect_from_forgery` to have the
- verification callback appended instead of prepended to the chain.
- This allows you to let the verification step depend on prior callbacks.
-
- Example:
-
- class ApplicationController < ActionController::Base
- before_action :authenticate
- protect_from_forgery prepend: false, unless: -> { @authenticated_by.oauth? }
-
- private
- def authenticate
- if oauth_request?
- # authenticate with oauth
- @authenticated_by = 'oauth'.inquiry
- else
- # authenticate with cookies
- @authenticated_by = 'cookie'.inquiry
- end
- end
- end
-
- *Josef Šimánek*
-
-* Remove `ActionController::HideActions`.
-
- *Ravil Bayramgalin*
-
-* Remove `respond_to`/`respond_with` placeholder methods, this functionality
- has been extracted to the `responders` gem.
-
- *Carlos Antonio da Silva*
-
-* Remove deprecated assertion files.
-
- *Rafael Mendonça França*
-
-* Remove deprecated usage of string keys in URL helpers.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `only_path` option on `*_path` helpers.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `NamedRouteCollection#helpers`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to define routes with `:to` option that doesn't contain `#`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActionDispatch::Response#to_ary`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActionDispatch::Request#deep_munge`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActionDispatch::Http::Parameters#symbolized_path_parameters`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated option `use_route` in controller tests.
-
- *Rafael Mendonça França*
-
-* Ensure `append_info_to_payload` is called even if an exception is raised.
-
- Fixes an issue where when an exception is raised in the request the additional
- payload data is not available.
-
- See #14903.
-
- *Dieter Komendera*, *Margus Pärt*
-
-* Correctly rely on the response's status code to handle calls to `head`.
-
- *Robin Dupret*
-
-* Using `head` method returns empty response_body instead
- of returning a single space " ".
-
- The old behavior was added as a workaround for a bug in an early
- version of Safari, where the HTTP headers are not returned correctly
- if the response body has a 0-length. This is been fixed since and
- the workaround is no longer necessary.
-
- Fixes #18253.
-
- *Prathamesh Sonpatki*
-
-* Fix how polymorphic routes works with objects that implement `to_model`.
-
- *Travis Grathwell*
-
-* Stop converting empty arrays in `params` to `nil`.
-
- This behavior was introduced in response to CVE-2012-2660, CVE-2012-2694
- and CVE-2013-0155
-
- ActiveRecord now issues a safe query when passing an empty array into
- a where clause, so there is no longer a need to defend against this type
- of input (any nils are still stripped from the array).
-
- *Chris Sinjakli*
-
-* Fixed usage of optional scopes in url helpers.
-
- *Alex Robbin*
-
-* Fixed handling of positional url helper arguments when `format: false`.
-
- Fixes #17819.
-
- *Andrew White*, *Tatiana Soukiassian*
-
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/actionpack/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actionpack/CHANGELOG.md) for previous changes.
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index 37a269cffd..4a05c067a9 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -6,7 +6,6 @@ desc "Default Task"
task :default => :test
task :package
-task "package:clean"
# Run the unit tests
Rake::TestTask.new do |t|
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 66300754e3..965fafff5f 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
s.add_dependency 'rack', '~> 2.x'
s.add_dependency 'rack-test', '~> 0.6.3'
s.add_dependency 'rails-html-sanitizer', '~> 1.0', '>= 1.0.2'
- s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
+ s.add_dependency 'rails-dom-testing', '~> 2.0'
s.add_dependency 'actionview', version
s.add_development_dependency 'activemodel', version
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 9f192c54f7..a6fb0dbe1d 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -60,9 +60,7 @@ module AbstractController
end
DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %i(
- @_action_name @_response_body @_formats @_prefixes @_config
- @_view_context_class @_view_renderer @_lookup_context
- @_routes @_db_runtime
+ @_action_name @_response_body @_formats @_prefixes
)
# This method should return a hash with assigns.
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 04e5922ce8..d546d7260c 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -229,7 +229,7 @@ module ActionController
HttpAuthentication::Digest::ControllerMethods,
HttpAuthentication::Token::ControllerMethods,
- # Before callbacks should also be executed the earliest as possible, so
+ # Before callbacks should also be executed as early as possible, so
# also include them at the bottom.
AbstractController::Callbacks,
@@ -251,9 +251,10 @@ module ActionController
setup_renderer!
# Define some internal variables that should not be propagated to the view.
- PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
- :@_params, :@_response, :@_request,
- :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
+ PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + %i(
+ @_params @_response @_request @_config @_url_options @_action_has_layout @_view_context_class
+ @_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy
+ )
def _protected_ivars # :nodoc:
PROTECTED_IVARS
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index d3853e2e83..295f0cb66f 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -5,7 +5,7 @@ module ActionController
#
# In addition to using the standard template helpers provided, creating custom helpers to
# extract complicated logic or reusable functionality is strongly encouraged. By default, each controller
- # will include all helpers. These helpers are only accessible on the controller through <tt>.helpers</tt>
+ # will include all helpers. These helpers are only accessible on the controller through <tt>#helpers</tt>
#
# In previous versions of \Rails the controller will include a helper which
# matches the name of the controller, e.g., <tt>MyController</tt> will automatically
@@ -71,7 +71,7 @@ module ActionController
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
end
- # Provides a proxy to access helpers methods from outside the view.
+ # Provides a proxy to access helper methods from outside the view.
def helpers
@helper_proxy ||= begin
proxy = ActionView::Base.new
@@ -113,5 +113,10 @@ module ActionController
all_helpers_from_path(helpers_path)
end
end
+
+ # Provides a proxy to access helper methods from outside the view.
+ def helpers
+ @_helper_proxy ||= view_context
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index 6055fde4f7..5d395cd8bd 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -163,14 +163,6 @@ module ActionController
end
end
- def each
- @response.sending!
- while str = @buf.pop
- yield str
- end
- @response.sent!
- end
-
# Write a 'close' event to the buffer; the producer/writing thread
# uses this to notify us that it's finished supplying content.
#
@@ -210,6 +202,14 @@ module ActionController
def call_on_error
@error_callback.call
end
+
+ private
+
+ def each_chunk(&block)
+ while str = @buf.pop
+ yield str
+ end
+ end
end
class Response < ActionDispatch::Response #:nodoc: all
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 90fb34e386..1735609cd9 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -103,7 +103,7 @@ module ActionController
#
# Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt>
# include <tt>ActionController::Renderers::All</tt>, making all renderers
- # avaialable in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
+ # available in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
#
# Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
# must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index f7e8d06f10..0559fbc6ce 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -235,7 +235,9 @@ module ActionController #:nodoc:
# we aren't serving an unauthorized cross-origin response.
def verify_same_origin_request
if marked_for_same_origin_verification? && non_xhr_javascript_response?
- logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING if logger
+ if logger && log_warning_on_csrf_failure
+ logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING
+ end
raise ActionController::InvalidCrossOriginRequest, CROSS_ORIGIN_JAVASCRIPT_WARNING
end
end
diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb
index 0621a7368c..17f4030f25 100644
--- a/actionpack/lib/action_controller/metal/rescue.rb
+++ b/actionpack/lib/action_controller/metal/rescue.rb
@@ -1,22 +1,11 @@
module ActionController #:nodoc:
- # This module is responsible to provide `rescue_from` helpers
- # to controllers and configure when detailed exceptions must be
+ # This module is responsible for providing `rescue_from` helpers
+ # to controllers and configuring when detailed exceptions must be
# shown.
module Rescue
extend ActiveSupport::Concern
include ActiveSupport::Rescuable
- def rescue_with_handler(exception)
- if exception.cause
- handler_index = index_of_handler_for_rescue(exception) || Float::INFINITY
- cause_handler_index = index_of_handler_for_rescue(exception.cause)
- if cause_handler_index && cause_handler_index <= handler_index
- exception = exception.cause
- end
- end
- super(exception)
- end
-
# Override this method if you want to customize when detailed
# exceptions must be shown. This method is only called when
# consider_all_requests_local is false. By default, it returns
@@ -31,7 +20,7 @@ module ActionController #:nodoc:
super
rescue Exception => exception
request.env['action_dispatch.show_detailed_exceptions'] ||= show_detailed_exceptions?
- rescue_with_handler(exception) || raise(exception)
+ rescue_with_handler(exception) || raise
end
end
end
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index f9b80dd805..46589901fd 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -43,7 +43,7 @@ module ActionController
# == Action Controller \Parameters
#
- # Allows to choose which attributes should be whitelisted for mass updating
+ # Allows you to choose which attributes should be whitelisted for mass updating
# and thus prevent accidentally exposing that which shouldn't be exposed.
# Provides two methods for this purpose: #require and #permit. The former is
# used to mark parameters as required. The latter is used to set the parameter
@@ -196,7 +196,7 @@ module ActionController
end
alias_method :to_unsafe_hash, :to_unsafe_h
- # Convert all hashes in values into parameters, then yield each pair like
+ # Convert all hashes in values into parameters, then yield each pair in
# the same way as <tt>Hash#each_pair</tt>
def each_pair(&block)
@parameters.each_pair do |key, value|
@@ -278,7 +278,7 @@ module ActionController
# params = ActionController::Parameters.new(user: { ... }, profile: { ... })
# user_params, profile_params = params.require(:user, :profile)
#
- # Otherwise, the method reraises the first exception found:
+ # Otherwise, the method re-raises the first exception found:
#
# params = ActionController::Parameters.new(user: {}, profile: {})
# user_params, profile_params = params.require(:user, :profile)
@@ -441,12 +441,12 @@ module ActionController
# Extracts the nested parameter from the given +keys+ by calling +dig+
# at each step. Returns +nil+ if any intermediate step is +nil+.
#
- # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
- # params.dig(:foo, :bar, :baz) # => 1
- # params.dig(:foo, :zot, :xyz) # => nil
+ # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
+ # params.dig(:foo, :bar, :baz) # => 1
+ # params.dig(:foo, :zot, :xyz) # => nil
#
- # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
- # params2.dig(:foo, 1) # => 11
+ # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
+ # params2.dig(:foo, 1) # => 11
def dig(*keys)
convert_value_to_parameters(@parameters.dig(*keys))
end
@@ -797,7 +797,7 @@ module ActionController
#
# class PeopleController < ActionController::Base
# # Using "Person.create(params[:person])" would raise an
- # # ActiveModel::ForbiddenAttributes exception because it'd
+ # # ActiveModel::ForbiddenAttributesError exception because it'd
# # be using mass assignment without an explicit permit step.
# # This is the recommended form:
# def create
diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb
index 2775a24e56..5ff4a658ad 100644
--- a/actionpack/lib/action_controller/renderer.rb
+++ b/actionpack/lib/action_controller/renderer.rb
@@ -13,11 +13,11 @@ module ActionController
#
# ApplicationController.renderer.render template: '...'
#
- # You can use a shortcut on controller to replace previous example with:
+ # You can use this shortcut in a controller, instead of the previous example:
#
# ApplicationController.render template: '...'
#
- # #render method allows you to use any options as when rendering in controller.
+ # #render allows you to use the same options that you can use when rendering in a controller.
# For example,
#
# FooController.render :action, locals: { ... }, assigns: { ... }
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index ecd21f29ce..ed2edcbe06 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -554,6 +554,8 @@ module ActionController
end
@request.query_string = ''
+ @response.sent!
+
@response
end
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index e9b25339dc..0a58ce2b96 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -164,7 +164,7 @@ module ActionDispatch
end
def format_from_path_extension
- path = @env['action_dispatch.original_path'] || @env['PATH_INFO']
+ path = get_header('action_dispatch.original_path') || get_header('PATH_INFO')
if match = path && path.match(/\.(\w+)\z/)
Mime[match.captures.first]
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index fa4c54701a..1515d59df3 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -68,7 +68,13 @@ module ActionDispatch # :nodoc:
alias_method :headers, :header
delegate :[], :[]=, :to => :@header
- delegate :each, :to => :@stream
+
+ def each(&block)
+ sending!
+ x = @stream.each(&block)
+ sent!
+ x
+ end
CONTENT_TYPE = "Content-Type".freeze
SET_COOKIE = "Set-Cookie".freeze
@@ -97,10 +103,10 @@ module ActionDispatch # :nodoc:
def body
@str_body ||= begin
- buf = ''
- each { |chunk| buf << chunk }
- buf
- end
+ buf = ''
+ each { |chunk| buf << chunk }
+ buf
+ end
end
def write(string)
@@ -112,10 +118,13 @@ module ActionDispatch # :nodoc:
end
def each(&block)
- @response.sending!
- x = @buf.each(&block)
- @response.sent!
- x
+ if @str_body
+ return enum_for(:each) unless block_given?
+
+ yield @str_body
+ else
+ each_chunk(&block)
+ end
end
def abort
@@ -129,6 +138,12 @@ module ActionDispatch # :nodoc:
def closed?
@closed
end
+
+ private
+
+ def each_chunk(&block)
+ @buf.each(&block) # extract into own method
+ end
end
def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 37f41ae988..7a1350a46d 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -217,7 +217,7 @@ module ActionDispatch
@protocol ||= ssl? ? 'https://' : 'http://'
end
- # Returns the \host for this request, such as "example.com".
+ # Returns the \host and port for this request, such as "example.com:8080".
#
# class Request < Rack::Request
# include ActionDispatch::Http::URL
@@ -226,6 +226,9 @@ module ActionDispatch
# req = Request.new 'HTTP_HOST' => 'example.com'
# req.raw_host_with_port # => "example.com"
#
+ # req = Request.new 'HTTP_HOST' => 'example.com:80'
+ # req.raw_host_with_port # => "example.com:80"
+ #
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
# req.raw_host_with_port # => "example.com:8080"
def raw_host_with_port
@@ -236,7 +239,7 @@ module ActionDispatch
end
end
- # Returns the host for this request, such as example.com.
+ # Returns the host for this request, such as "example.com".
#
# class Request < Rack::Request
# include ActionDispatch::Http::URL
@@ -249,12 +252,16 @@ module ActionDispatch
end
# Returns a \host:\port string for this request, such as "example.com" or
- # "example.com:8080".
+ # "example.com:8080". Port is only included if it is not a default port
+ # (80 or 443)
#
# class Request < Rack::Request
# include ActionDispatch::Http::URL
# end
#
+ # req = Request.new 'HTTP_HOST' => 'example.com'
+ # req.host_with_port # => "example.com"
+ #
# req = Request.new 'HTTP_HOST' => 'example.com:80'
# req.host_with_port # => "example.com"
#
@@ -347,6 +354,17 @@ module ActionDispatch
standard_port? ? '' : ":#{port}"
end
+ # Returns the requested port, such as 8080, based on SERVER_PORT
+ #
+ # class Request < Rack::Request
+ # include ActionDispatch::Http::URL
+ # end
+ #
+ # req = Request.new 'SERVER_PORT' => '80'
+ # req.server_port # => 80
+ #
+ # req = Request.new 'SERVER_PORT' => '8080'
+ # req.server_port # => 8080
def server_port
get_header('SERVER_PORT').to_i
end
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index 51a471fb23..5f758d641a 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -67,18 +67,19 @@ module ActionDispatch
log_error(request, wrapper)
if request.get_header('action_dispatch.show_detailed_exceptions')
- case @response_format
- when :api
- render_for_api_application(request, wrapper)
- when :default
- render_for_default_application(request, wrapper)
+ content_type = request.formats.first
+
+ if api_request?(content_type)
+ render_for_api_request(content_type, wrapper)
+ else
+ render_for_browser_request(request, wrapper)
end
else
raise exception
end
end
- def render_for_default_application(request, wrapper)
+ def render_for_browser_request(request, wrapper)
template = create_template(request, wrapper)
file = "rescues/#{wrapper.rescue_template}"
@@ -92,7 +93,7 @@ module ActionDispatch
render(wrapper.status_code, body, format)
end
- def render_for_api_application(request, wrapper)
+ def render_for_api_request(content_type, wrapper)
body = {
status: wrapper.status_code,
error: Rack::Utils::HTTP_STATUS_CODES.fetch(
@@ -103,7 +104,6 @@ module ActionDispatch
traces: wrapper.traces
}
- content_type = request.formats.first
to_format = "to_#{content_type.to_sym}"
if content_type && body.respond_to?(to_format)
@@ -181,5 +181,9 @@ module ActionDispatch
ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
end
end
+
+ def api_request?(content_type)
+ @response_format == :api && !content_type.html?
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 06038af571..80703940ed 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -133,7 +133,7 @@ module ActionDispatch
def to_session_value #:nodoc:
flashes_to_keep = @flashes.except(*@discard)
return nil if flashes_to_keep.empty?
- {'flashes' => flashes_to_keep}
+ { 'discard' => [], 'flashes' => flashes_to_keep }
end
def initialize(flashes = {}, discard = []) #:nodoc:
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 41c220236a..2c5721dc22 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -27,8 +27,8 @@ module ActionDispatch
# in the server's `public/` directory (see Static#call).
def match?(path)
path = ::Rack::Utils.unescape_path path
- return false unless valid_path?(path)
- path = Rack::Utils.clean_path_info path
+ return false unless ::Rack::Utils.valid_path? path
+ path = ::Rack::Utils.clean_path_info path
paths = [path, "#{path}#{ext}", "#{path}/#{@index}#{ext}"]
@@ -94,10 +94,6 @@ module ActionDispatch
false
end
end
-
- def valid_path?(path)
- path.valid_encoding? && !path.include?("\0")
- end
end
# This middleware will attempt to return the contents of a file's body from
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index 42890225fa..47568f6ad0 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -9,7 +9,7 @@ module ActionDispatch
# Singleton object used to determine if an optional param wasn't specified
Unspecified = Object.new
-
+
# Creates a session hash, merging the properties of the previous session if any
def self.create(store, req, default_options)
session_was = find req
@@ -198,6 +198,10 @@ module ActionDispatch
@delegate.merge!(other)
end
+ def each(&block)
+ to_hash.each(&block)
+ end
+
private
def load_for_read!
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 67f441dfec..dd6ac9db9c 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -118,11 +118,11 @@ module ActionDispatch
# controller :blog do
# get 'blog/show' => :list
# get 'blog/delete' => :delete
- # get 'blog/edit/:id' => :edit
+ # get 'blog/edit' => :edit
# end
#
# # provides named routes for show, delete, and edit
- # link_to @article.title, show_path(id: @article.id)
+ # link_to @article.title, blog_show_path(id: @article.id)
#
# == Pretty URLs
#
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index faa93ecc17..8ff3b42a40 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -120,7 +120,7 @@ module ActionDispatch
if options_constraints.is_a?(Hash)
@defaults = Hash[options_constraints.find_all { |key, default|
- URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
+ URL_OPTIONS.include?(key) && (String === default || Integer === default)
}].merge @defaults
@blocks = blocks
constraints.merge! options_constraints
@@ -824,7 +824,7 @@ module ActionDispatch
if options[:constraints].is_a?(Hash)
defaults = options[:constraints].select do |k, v|
- URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum))
+ URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Integer))
end
options[:defaults] = defaults.merge(options[:defaults] || {})
@@ -2088,8 +2088,7 @@ to this:
def each
node = self
- loop do
- break if node.equal? NULL
+ until node.equal? NULL
yield node
node = node.parent
end
diff --git a/actionpack/lib/action_dispatch/testing/assertion_response.rb b/actionpack/lib/action_dispatch/testing/assertion_response.rb
index 3fb81ff083..404b96bbcd 100644
--- a/actionpack/lib/action_dispatch/testing/assertion_response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertion_response.rb
@@ -1,14 +1,7 @@
module ActionDispatch
- # This is a class that abstracts away an asserted response.
- # It purposely does not inherit from Response, because it doesn't need it.
- # That means it does not have headers or a body.
- #
- # As an input to the initializer, we take a Fixnum, a String, or a Symbol.
- # If it's a Fixnum or String, we figure out what its symbolized name.
- # If it's a Symbol, we figure out what its corresponding code is.
- # The resulting code will be a Fixnum, for real HTTP codes, and it will
- # be a String for the pseudo-HTTP codes, such as:
- # :success, :missing, :redirect and :error
+ # This is a class that abstracts away an asserted response. It purposely
+ # does not inherit from Response because it doesn't need it. That means it
+ # does not have headers or a body.
class AssertionResponse
attr_reader :code, :name
@@ -19,6 +12,9 @@ module ActionDispatch
error: "5XX"
}
+ # Accepts a specific response status code as an Integer (404) or String
+ # ('404') or a response status range as a Symbol pseudo-code (:success,
+ # indicating any 200-299 status code).
def initialize(code_or_name)
if code_or_name.is_a?(Symbol)
@name = code_or_name
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 384254b131..8777666f9f 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -300,7 +300,7 @@ module ActionDispatch
end
end
- REQUEST_KWARGS = %i(params headers env xhr)
+ REQUEST_KWARGS = %i(params headers env xhr as)
def kwarg_request?(args)
args[0].respond_to?(:keys) && args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) }
end
@@ -317,7 +317,8 @@ module ActionDispatch
params: { id: 1 },
headers: { 'X-Extra-Header' => '123' },
env: { 'action_dispatch.custom' => 'custom' },
- xhr: true
+ xhr: true,
+ as: :json
MSG
end
diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb
index ad1a7f7109..46523a8600 100644
--- a/actionpack/lib/action_dispatch/testing/test_request.rb
+++ b/actionpack/lib/action_dispatch/testing/test_request.rb
@@ -22,23 +22,23 @@ module ActionDispatch
private_class_method :default_env
def request_method=(method)
- @env['REQUEST_METHOD'] = method.to_s.upcase
+ set_header('REQUEST_METHOD', method.to_s.upcase)
end
def host=(host)
- @env['HTTP_HOST'] = host
+ set_header('HTTP_HOST', host)
end
def port=(number)
- @env['SERVER_PORT'] = number.to_i
+ set_header('SERVER_PORT', number.to_i)
end
def request_uri=(uri)
- @env['REQUEST_URI'] = uri
+ set_header('REQUEST_URI', uri)
end
def path=(path)
- @env['PATH_INFO'] = path
+ set_header('PATH_INFO', path)
end
def action=(action_name)
@@ -46,24 +46,24 @@ module ActionDispatch
end
def if_modified_since=(last_modified)
- @env['HTTP_IF_MODIFIED_SINCE'] = last_modified
+ set_header('HTTP_IF_MODIFIED_SINCE', last_modified)
end
def if_none_match=(etag)
- @env['HTTP_IF_NONE_MATCH'] = etag
+ set_header('HTTP_IF_NONE_MATCH', etag)
end
def remote_addr=(addr)
- @env['REMOTE_ADDR'] = addr
+ set_header('REMOTE_ADDR', addr)
end
def user_agent=(user_agent)
- @env['HTTP_USER_AGENT'] = user_agent
+ set_header('HTTP_USER_AGENT', user_agent)
end
def accept=(mime_types)
- @env.delete('action_dispatch.request.accepts')
- @env['HTTP_ACCEPT'] = Array(mime_types).collect(&:to_s).join(",")
+ delete_header('action_dispatch.request.accepts')
+ set_header('HTTP_ACCEPT', Array(mime_types).collect(&:to_s).join(","))
end
end
end
diff --git a/actionpack/lib/action_pack/gem_version.rb b/actionpack/lib/action_pack/gem_version.rb
index 157f401f54..d8f86630b1 100644
--- a/actionpack/lib/action_pack/gem_version.rb
+++ b/actionpack/lib/action_pack/gem_version.rb
@@ -6,9 +6,9 @@ module ActionPack
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actionpack/test/assertions/response_assertions_test.rb b/actionpack/test/assertions/response_assertions_test.rb
index 579ce0ed29..57a67a48b5 100644
--- a/actionpack/test/assertions/response_assertions_test.rb
+++ b/actionpack/test/assertions/response_assertions_test.rb
@@ -35,7 +35,7 @@ module ActionDispatch
end
end
- def test_assert_response_fixnum
+ def test_assert_response_integer
@response = FakeResponse.new 400
assert_response 400
diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb
index 081288ef21..f87077dd86 100644
--- a/actionpack/test/controller/flash_hash_test.rb
+++ b/actionpack/test/controller/flash_hash_test.rb
@@ -57,10 +57,10 @@ module ActionDispatch
def test_to_session_value
@hash['foo'] = 'bar'
- assert_equal({'flashes' => {'foo' => 'bar'}}, @hash.to_session_value)
+ assert_equal({ 'discard' => [], 'flashes' => { 'foo' => 'bar' } }, @hash.to_session_value)
@hash.now['qux'] = 1
- assert_equal({'flashes' => {'foo' => 'bar'}}, @hash.to_session_value)
+ assert_equal({ 'flashes' => { 'foo' => 'bar' }, 'discard' => [] }, @hash.to_session_value)
@hash.discard('foo')
assert_equal(nil, @hash.to_session_value)
diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb
index feb882a2b3..ef85e141a0 100644
--- a/actionpack/test/controller/helper_test.rb
+++ b/actionpack/test/controller/helper_test.rb
@@ -207,6 +207,22 @@ class HelperTest < ActiveSupport::TestCase
assert methods.include?(:foobar)
end
+ def test_helper_proxy_in_instance
+ methods = AllHelpersController.new.helpers.methods
+
+ # Action View
+ assert_includes methods, :pluralize
+
+ # abc_helper.rb
+ assert_includes methods, :bare_a
+
+ # fun/games_helper.rb
+ assert_includes methods, :stratego
+
+ # fun/pdf_helper.rb
+ assert_includes methods, :foobar
+ end
+
def test_helper_proxy_config
AllHelpersController.config.my_var = 'smth'
diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb
index a7759c080b..5977124594 100644
--- a/actionpack/test/controller/live_stream_test.rb
+++ b/actionpack/test/controller/live_stream_test.rb
@@ -246,7 +246,8 @@ module ActionController
def assert_stream_closed
assert response.stream.closed?, 'stream should be closed'
- assert response.sent?, 'stream should be sent'
+ assert response.committed?, 'response should be committed'
+ assert response.sent?, 'response should be sent'
end
def capture_log_output
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index d56241f9cd..d3f2ec6aa1 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -407,6 +407,37 @@ module RequestForgeryProtectionTests
end
end
+ def test_should_warn_on_not_same_origin_js
+ old_logger = ActionController::Base.logger
+ logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+ ActionController::Base.logger = logger
+
+ begin
+ assert_cross_origin_blocked { get :same_origin_js }
+
+ assert_equal 1, logger.logged(:warn).size
+ assert_match(/<script> tag on another site requested protected JavaScript/, logger.logged(:warn).last)
+ ensure
+ ActionController::Base.logger = old_logger
+ end
+ end
+
+ def test_should_not_warn_if_csrf_logging_disabled_and_not_same_origin_js
+ old_logger = ActionController::Base.logger
+ logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+ ActionController::Base.logger = logger
+ ActionController::Base.log_warning_on_csrf_failure = false
+
+ begin
+ assert_cross_origin_blocked { get :same_origin_js }
+
+ assert_equal 0, logger.logged(:warn).size
+ ensure
+ ActionController::Base.logger = old_logger
+ ActionController::Base.log_warning_on_csrf_failure = true
+ end
+ end
+
# Allow non-GET requests since GET is all a remote <script> tag can muster.
def test_should_allow_non_get_js_without_xhr_header
session[:_csrf_token] = @token
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index ed78f859ce..c088e5a043 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -131,22 +131,6 @@ class RescueController < ActionController::Base
def missing_template
end
- def io_error_in_view
- begin
- raise IOError.new('this is io error')
- rescue
- raise ActionView::TemplateError.new(nil)
- end
- end
-
- def zero_division_error_in_view
- begin
- raise ZeroDivisionError.new('this is zero division error')
- rescue
- raise ActionView::TemplateError.new(nil)
- end
- end
-
def exception_with_more_specific_handler_for_wrapper
raise RecordInvalid
rescue
@@ -251,17 +235,6 @@ class ControllerInheritanceRescueControllerTest < ActionController::TestCase
end
class RescueControllerTest < ActionController::TestCase
-
- def test_io_error_in_view
- get :io_error_in_view
- assert_equal 'io error', @response.body
- end
-
- def test_zero_division_error_in_view
- get :zero_division_error_in_view
- assert_equal 'action_view templater error', @response.body
- end
-
def test_rescue_handler
get :not_authorized
assert_response :forbidden
@@ -276,7 +249,6 @@ class RescueControllerTest < ActionController::TestCase
get :record_invalid
end
end
-
def test_rescue_handler_with_argument_as_string
assert_called_with @controller, :show_errors, [Exception] do
get :record_invalid_raise_as_string
@@ -314,7 +286,6 @@ class RescueControllerTest < ActionController::TestCase
get :resource_unavailable
assert_equal "RescueController::ResourceUnavailable", @response.body
end
-
def test_block_rescue_handler_with_argument_as_string
get :resource_unavailable_raise_as_string
assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body
@@ -322,7 +293,7 @@ class RescueControllerTest < ActionController::TestCase
test 'rescue when wrapper has more specific handler than cause' do
get :exception_with_more_specific_handler_for_wrapper
- assert_response :unprocessable_entity
+ assert_response :forbidden
end
test 'rescue when cause has more specific handler than wrapper' do
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 168677829a..03bf8f8295 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -626,7 +626,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
assert_equal '/pages/boo', url_for(rs, { :controller => 'pages', :action => 'boo' })
end
- def test_route_with_fixnum_default
+ def test_route_with_integer_default
rs.draw do
get 'page(/:id)' => 'content#show_page', :id => 1
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index ebcdda6074..6160b3395a 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -553,7 +553,7 @@ XML
assert_equal 'created', flash[:notice]
end
- def test_params_passing_with_fixnums
+ def test_params_passing_with_integer
get :test_params, params: {
page: { name: "Page name", month: 4, year: 2004, day: 6 }
}
@@ -565,7 +565,7 @@ XML
)
end
- def test_params_passing_with_fixnums_when_not_html_request
+ def test_params_passing_with_integers_when_not_html_request
get :test_params, params: { format: 'json', count: 999 }
parsed_params = ::JSON.parse(@response.body)
assert_equal(
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
index 159bf10545..5a39db145e 100644
--- a/actionpack/test/dispatch/debug_exceptions_test.rb
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -27,37 +27,37 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
env['action_dispatch.show_detailed_exceptions'] = @detailed
req = ActionDispatch::Request.new(env)
case req.path
- when "/pass"
+ when %r{/pass}
[404, { "X-Cascade" => "pass" }, self]
- when "/not_found"
+ when %r{/not_found}
raise AbstractController::ActionNotFound
- when "/runtime_error"
+ when %r{/runtime_error}
raise RuntimeError
- when "/method_not_allowed"
+ when %r{/method_not_allowed}
raise ActionController::MethodNotAllowed
- when "/unknown_http_method"
+ when %r{/unknown_http_method}
raise ActionController::UnknownHttpMethod
- when "/not_implemented"
+ when %r{/not_implemented}
raise ActionController::NotImplemented
- when "/unprocessable_entity"
+ when %r{/unprocessable_entity}
raise ActionController::InvalidAuthenticityToken
- when "/not_found_original_exception"
+ when %r{/not_found_original_exception}
begin
raise AbstractController::ActionNotFound.new
rescue
raise ActionView::Template::Error.new('template')
end
- when "/missing_template"
+ when %r{/missing_template}
raise ActionView::MissingTemplate.new(%w(foo), 'foo/index', %w(foo), false, 'mailer')
- when "/bad_request"
+ when %r{/bad_request}
raise ActionController::BadRequest
- when "/missing_keys"
+ when %r{/missing_keys}
raise ActionController::UrlGenerationError, "No route matches"
- when "/parameter_missing"
+ when %r{/parameter_missing}
raise ActionController::ParameterMissing, :missing_param_key
- when "/original_syntax_error"
+ when %r{/original_syntax_error}
eval 'broke_syntax =' # `eval` need for raise native SyntaxError at runtime
- when "/syntax_error_into_view"
+ when %r{/syntax_error_into_view}
begin
eval 'broke_syntax ='
rescue Exception
@@ -67,7 +67,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
{})
raise ActionView::Template::Error.new(template)
end
- when "/framework_raises"
+ when %r{/framework_raises}
method_that_raises
else
raise "puke!"
@@ -75,13 +75,6 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
end
end
- class BoomerAPI < Boomer
- def call(env)
- env['action_dispatch.show_detailed_exceptions'] = @detailed
- raise "puke!"
- end
- end
-
RoutesApp = Struct.new(:routes).new(SharedTestRoutes)
ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false), RoutesApp)
DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp)
@@ -212,61 +205,60 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
assert_match(/ActionController::ParameterMissing/, body)
end
- test "rescue with json error for API request" do
+ test "rescue with JSON error for JSON API request" do
@app = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :api)
- get "/", headers: { 'action_dispatch.show_exceptions' => true }
+ get "/", headers: { 'action_dispatch.show_exceptions' => true }, as: :json
assert_response 500
assert_no_match(/<header>/, body)
assert_no_match(/<body>/, body)
assert_equal "application/json", response.content_type
assert_match(/RuntimeError: puke/, body)
- get "/not_found", headers: { 'action_dispatch.show_exceptions' => true }
+ get "/not_found", headers: { 'action_dispatch.show_exceptions' => true }, as: :json
assert_response 404
assert_no_match(/<body>/, body)
assert_equal "application/json", response.content_type
assert_match(/#{AbstractController::ActionNotFound.name}/, body)
- get "/method_not_allowed", headers: { 'action_dispatch.show_exceptions' => true }
+ get "/method_not_allowed", headers: { 'action_dispatch.show_exceptions' => true }, as: :json
assert_response 405
assert_no_match(/<body>/, body)
assert_equal "application/json", response.content_type
assert_match(/ActionController::MethodNotAllowed/, body)
- get "/unknown_http_method", headers: { 'action_dispatch.show_exceptions' => true }
+ get "/unknown_http_method", headers: { 'action_dispatch.show_exceptions' => true }, as: :json
assert_response 405
assert_no_match(/<body>/, body)
assert_equal "application/json", response.content_type
assert_match(/ActionController::UnknownHttpMethod/, body)
- get "/bad_request", headers: { 'action_dispatch.show_exceptions' => true }
+ get "/bad_request", headers: { 'action_dispatch.show_exceptions' => true }, as: :json
assert_response 400
assert_no_match(/<body>/, body)
assert_equal "application/json", response.content_type
assert_match(/ActionController::BadRequest/, body)
- get "/parameter_missing", headers: { 'action_dispatch.show_exceptions' => true }
+ get "/parameter_missing", headers: { 'action_dispatch.show_exceptions' => true }, as: :json
assert_response 400
assert_no_match(/<body>/, body)
assert_equal "application/json", response.content_type
assert_match(/ActionController::ParameterMissing/, body)
end
- test "rescue with json on API request returns only allowed formats or json as a fallback" do
+ test "rescue with HTML format for HTML API request" do
@app = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :api)
- get "/index.json", headers: { 'action_dispatch.show_exceptions' => true }
- assert_response 500
- assert_equal "application/json", response.content_type
- assert_match(/RuntimeError: puke/, body)
-
get "/index.html", headers: { 'action_dispatch.show_exceptions' => true }
assert_response 500
- assert_no_match(/<header>/, body)
- assert_no_match(/<body>/, body)
- assert_equal "application/json", response.content_type
- assert_match(/RuntimeError: puke/, body)
+ assert_match(/<header>/, body)
+ assert_match(/<body>/, body)
+ assert_equal "text/html", response.content_type
+ assert_match(/puke/, body)
+ end
+
+ test "rescue with XML format for XML API requests" do
+ @app = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :api)
get "/index.xml", headers: { 'action_dispatch.show_exceptions' => true }
assert_response 500
@@ -274,6 +266,25 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
assert_match(/RuntimeError: puke/, body)
end
+ test "rescue with JSON format as fallback if API request format is not supported" do
+ begin
+ Mime::Type.register 'text/wibble', :wibble
+
+ ActionDispatch::IntegrationTest.register_encoder(:wibble,
+ param_encoder: -> params { params })
+
+ @app = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :api)
+
+ get "/index", headers: { 'action_dispatch.show_exceptions' => true }, as: :wibble
+ assert_response 500
+ assert_equal "application/json", response.content_type
+ assert_match(/RuntimeError: puke/, body)
+
+ ensure
+ Mime::Type.unregister :wibble
+ end
+ end
+
test "does not show filtered parameters" do
@app = DevelopmentApp
diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb
index e4475f4233..de57c4be1d 100644
--- a/actionpack/test/dispatch/live_response_test.rb
+++ b/actionpack/test/dispatch/live_response_test.rb
@@ -65,7 +65,7 @@ module ActionController
latch = Concurrent::CountDownLatch.new
t = Thread.new {
- @response.stream.each do
+ @response.each do
latch.count_down
end
}
diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb
index 7dcbcc5c21..e022e7e21e 100644
--- a/actionpack/test/dispatch/request/session_test.rb
+++ b/actionpack/test/dispatch/request/session_test.rb
@@ -114,5 +114,31 @@ module ActionDispatch
}.new
end
end
+
+ class SessionIntegrationTest < ActionDispatch::IntegrationTest
+ class MySessionApp
+ def call(env)
+ request = Rack::Request.new(env)
+ request.session['hello'] = 'Hello from MySessionApp!'
+ [ 200, {}, ['Hello from MySessionApp!'] ]
+ end
+ end
+
+ Router = ActionDispatch::Routing::RouteSet.new
+ Router.draw do
+ get '/mysessionapp' => MySessionApp.new
+ end
+
+ def app
+ @app ||= RoutedRackApp.new(Router)
+ end
+
+ def test_session_follows_rack_api_contract_1
+ get '/mysessionapp'
+ assert_response :ok
+ assert_equal 'Hello from MySessionApp!', @response.body
+ assert_equal 'Hello from MySessionApp!', session['hello']
+ end
+ end
end
end
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index a4cb8ce449..8a5d85ab84 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -358,6 +358,17 @@ class RequestPort < BaseRequestTest
request = stub_request 'HTTP_HOST' => 'www.example.org:8080'
assert_equal ':8080', request.port_string
end
+
+ test "server port" do
+ request = stub_request 'SERVER_PORT' => '8080'
+ assert_equal 8080, request.server_port
+
+ request = stub_request 'SERVER_PORT' => '80'
+ assert_equal 80, request.server_port
+
+ request = stub_request 'SERVER_PORT' => ''
+ assert_equal 0, request.server_port
+ end
end
class RequestPath < BaseRequestTest
@@ -417,6 +428,11 @@ class RequestPath < BaseRequestTest
end
class RequestHost < BaseRequestTest
+ test "host without specifying port" do
+ request = stub_request 'HTTP_HOST' => 'rubyonrails.org'
+ assert_equal "rubyonrails.org", request.host_with_port
+ end
+
test "host with default port" do
request = stub_request 'HTTP_HOST' => 'rubyonrails.org:80'
assert_equal "rubyonrails.org", request.host_with_port
@@ -427,6 +443,21 @@ class RequestHost < BaseRequestTest
assert_equal "rubyonrails.org:81", request.host_with_port
end
+ test "raw without specifying port" do
+ request = stub_request 'HTTP_HOST' => 'rubyonrails.org'
+ assert_equal "rubyonrails.org", request.raw_host_with_port
+ end
+
+ test "raw host with default port" do
+ request = stub_request 'HTTP_HOST' => 'rubyonrails.org:80'
+ assert_equal "rubyonrails.org:80", request.raw_host_with_port
+ end
+
+ test "raw host with non default port" do
+ request = stub_request 'HTTP_HOST' => 'rubyonrails.org:81'
+ assert_equal "rubyonrails.org:81", request.raw_host_with_port
+ end
+
test "proxy request" do
request = stub_request 'HTTP_HOST' => 'glu.ttono.us:80'
assert_equal "glu.ttono.us", request.host_with_port
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 658e0d004b..aa90433505 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -37,6 +37,39 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal "closed stream", e.message
end
+ def test_each_isnt_called_if_str_body_is_written
+ # Controller writes and reads response body
+ each_counter = 0
+ @response.body = Object.new.tap {|o| o.singleton_class.send(:define_method, :each) { |&block| each_counter += 1; block.call 'foo' } }
+ @response['X-Foo'] = @response.body
+
+ assert_equal 1, each_counter, "#each was not called once"
+
+ # Build response
+ status, headers, body = @response.to_a
+
+ assert_equal 200, status
+ assert_equal "foo", headers['X-Foo']
+ assert_equal "foo", body.each.to_a.join
+
+ # Show that #each was not called twice
+ assert_equal 1, each_counter, "#each was not called once"
+ end
+
+ def test_set_header_after_read_body_during_action
+ @response.body
+
+ # set header after the action reads back @response.body
+ @response['x-header'] = "Best of all possible worlds."
+
+ # the response can be built.
+ status, headers, body = @response.to_a
+ assert_equal 200, status
+ assert_equal "", body.body
+
+ assert_equal "Best of all possible worlds.", headers['x-header']
+ end
+
def test_read_body_during_action
@response.body = "Hello, World!"
diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb
index 51c469a61a..3c19cbd68a 100644
--- a/actionpack/test/dispatch/test_request_test.rb
+++ b/actionpack/test/dispatch/test_request_test.rb
@@ -88,6 +88,33 @@ class TestRequestTest < ActiveSupport::TestCase
assert_equal 'GoogleBot', req.user_agent
end
+ test "setter methods" do
+ req = ActionDispatch::TestRequest.create({})
+ get = 'GET'
+
+ [
+ 'request_method=', 'host=', 'request_uri=', 'path=', 'if_modified_since=', 'if_none_match=',
+ 'remote_addr=', 'user_agent=', 'accept='
+ ].each do |method|
+ req.send(method, get)
+ end
+
+ req.port = 8080
+ req.accept = 'hello goodbye'
+
+ assert_equal(get, req.get_header('REQUEST_METHOD'))
+ assert_equal(get, req.get_header('HTTP_HOST'))
+ assert_equal(8080, req.get_header('SERVER_PORT'))
+ assert_equal(get, req.get_header('REQUEST_URI'))
+ assert_equal(get, req.get_header('PATH_INFO'))
+ assert_equal(get, req.get_header('HTTP_IF_MODIFIED_SINCE'))
+ assert_equal(get, req.get_header('HTTP_IF_NONE_MATCH'))
+ assert_equal(get, req.get_header('REMOTE_ADDR'))
+ assert_equal(get, req.get_header('HTTP_USER_AGENT'))
+ assert_nil(req.get_header('action_dispatch.request.accepts'))
+ assert_equal('hello goodbye', req.get_header('HTTP_ACCEPT'))
+ end
+
private
def assert_cookies(expected, cookie_jar)
assert_equal(expected, cookie_jar.instance_variable_get("@cookies"))
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 6fb41e9d4c..9d669c7cd8 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,319 +1,19 @@
-* `date_select` helper `:with_css_classes` option now accepts a hash of strings
- for `:year`, `:month`, `:day`, `:hour`, `:minute`, `:second` that will extend
- the select type with the given css class value.
+* `select_tag`'s `include_blank` option for generation for blank option tag, now adds an empty space label,
+ when the value as well as content for option tag are empty, so that we confirm with html specification.
+ Ref: https://www.w3.org/TR/html5/forms.html#the-option-element.
- ```erb
- <%= f.date_select :birthday, with_css_classes: { month: "my-month", year: "my-year" } %>
- ```
+ Generation of option before:
```html
- <select id="user_birthday_3i" name="user[birthday(3i)]">…</select>
- <select id="user_birthday_2i" name="user[birthday(2i)]" class="my-month">…</select>
- <select id="user_birthday_1i" name="user[birthday(1i)]" class="my-year">…</select>
- ```
-
- *Matthias Neumayr*
-
-* Add `to_sentence` helper that is a HTML-safe aware version of `Array#to_sentence`.
-
- *Neil Matatall*
-
-* Deprecate `datetime_field` and `datetime_field_tag` helpers.
- Datetime input type was removed from HTML specification.
- One can use `datetime_local_field` and `datetime_local_field_tag` instead.
-
- *Wojciech Wnętrzak*
-
-* Added log "Rendering ...", when starting to render a template to log that
- we have started rendering something. This helps to easily identify the origin
- of queries in the log whether they came from controller or views.
-
- *Vipul A M and Prem Sichanugrist*
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* Collection rendering can cache and fetch multiple partials at once.
-
- Collections rendered as:
-
- ```ruby
- <%= render partial: 'notifications/notification', collection: @notifications, as: :notification, cached: true %>
+ <option value=""></option>
```
- will read several partials from cache at once. The templates in the collection
- that haven't been cached already will automatically be written to cache. Works
- great alongside individual template fragment caching. For instance if the
- template the collection renders is cached like:
+ Generation of option after:
- ```ruby
- # notifications/_notification.html.erb
- <% cache notification do %>
- <%# ... %>
- <% end %>
+ ```html
+ <option value="" label=" "></option>
```
- Then any collection renders shares that cache when attempting to read multiple
- ones at once.
-
- *Kasper Timm Hansen*
-
-* Add support for nested hashes/arrays to `:params` option of `button_to` helper.
-
- *James Coleman*
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* Fix stripping the digest from the automatically generated img tag alt
- attribute when assets are handled by Sprockets >=3.0.
-
- *Bart de Water*
-
-* Create a new `ActiveSupport::SafeBuffer` instance when `content_for` is flushed.
-
- Fixes #19890.
-
- *Yoong Kang Lim*
-
-* Fix `collection_radio_buttons` hidden_field name and make it appear
- before the actual input radio tags to make the real value override
- the hidden when passed.
-
- Fixes #22773.
-
- *Santiago Pastorino*
-
-* `ActionView::TestCase::Controller#params` returns an instance of
- `ActionController::Parameters`.
-
- *Justin Coyne*
-
-* Fix regression in `submit_tag` when a symbol is used as label argument.
-
- *Yuuji Yaginuma*
-
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* `I18n.translate` helper will wrap the missing translation keys
- in a <span> tag only if `debug_missing_translation` configuration
- be true. Default value is `true`. For example in `application.rb`:
-
- # in order to turn off missing key wrapping
- config.action_view.debug_missing_translation = false
-
- *Sameer Rahmani*
-
-* Respect value of `:object` if `:object` is false when rendering.
-
- Fixes #22260.
-
- *Yuichiro Kaneko*
-
-* Generate `week_field` input values using a 1-based index and not a 0-based index
- as per the W3 spec: http://www.w3.org/TR/html-markup/datatypes.html#form.data.week
-
- *Christoph Geschwind*
-
-* Allow `host` option in `javascript_include_tag` and `stylesheet_link_tag` helpers
-
- *Grzegorz Witek*
-
-* Restrict `url_for :back` to valid, non-JavaScript URLs. GH#14444
-
- *Damien Burke*
-
-* Allow `date_select` helper selected option to accept hash like the default options.
-
- *Lecky Lao*
-
-* Collection input propagates input's `id` to the label's `for` attribute when
- using html options as the last element of collection.
-
- *Vasiliy Ermolovich*
-
-* Add a `hidden_field` on the `collection_radio_buttons` to avoid raising an error
- when the only input on the form is the `collection_radio_buttons`.
-
- *Mauro George*
-
-* `url_for` does not modify its arguments when generating polymorphic URLs.
-
- *Bernerd Schaefer*
-
-* `number_to_currency` and `number_with_delimiter` now accept a custom `delimiter_pattern` option
- to handle placement of delimiter, to support currency formats like INR.
-
- Example:
-
- number_to_currency(1230000, delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/, unit: '₹', format: "%u %n")
- # => '₹ 12,30,000.00'
-
*Vipul A M*
-* Make `disable_with` the default behavior for submit tags. Disables the
- button on submit to prevent double submits.
-
- *Justin Schiff*
-
-* Add a break_sequence option to word_wrap so you can specify a custom break.
-
- *Mauricio Gomez*
-
-* Add wildcard matching to explicit dependencies.
-
- Turns:
-
- ```erb
- <% # Template Dependency: recordings/threads/events/subscribers_changed %>
- <% # Template Dependency: recordings/threads/events/completed %>
- <% # Template Dependency: recordings/threads/events/uncompleted %>
- ```
-
- Into:
-
- ```erb
- <% # Template Dependency: recordings/threads/events/* %>
- ```
-
- *Kasper Timm Hansen*
-
-* Allow defining explicit collection caching using a `# Template Collection: ...`
- directive inside templates.
-
- *Dov Murik*
-
-* Asset helpers raise `ArgumentError` when `nil` is passed as a source.
-
- *Anton Kolomiychuk*
-
-* Always attach the template digest to the cache key for collection caching
- even when `virtual_path` is not available from the view context.
- Which could happen if the rendering was done directly in the controller
- and not in a template.
-
- Fixes #20535.
-
- *Roque Pinel*
-
-* Improve detection of partial templates eligible for collection caching,
- now allowing multi-line comments at the beginning of the template file.
-
- *Dov Murik*
-
-* Raise an `ArgumentError` when a false value for `include_blank` is passed to a
- required select field (to comply with the HTML5 spec).
-
- *Grey Baker*
-
-* Do not put partial name to `local_assigns` when rendering without
- an object or a collection.
-
- *Henrik Nygren*
-
-* Remove `:rescue_format` option for `translate` helper since it's no longer
- supported by I18n.
-
- *Bernard Potocki*
-
-* `translate` should handle `raise` flag correctly in case of both main and default
- translation is missing.
-
- Fixes #19967.
-
- *Bernard Potocki*
-
-* Load the `default_form_builder` from the controller on initialization, which overrides
- the global config if it is present.
-
- *Kevin McPhillips*
-
-* Accept lambda as `child_index` option in `fields_for` method.
-
- *Karol Galanciak*
-
-* `translate` allows `default: [[]]` again for a default value of `[]`.
-
- Fixes #19640.
-
- *Adam Prescott*
-
-* `translate` should accept nils as members of the `:default`
- parameter without raising a translation missing error.
-
- Fixes #19419.
-
- *Justin Coyne*
-
-* `number_to_percentage` does not crash with `Float::NAN` or `Float::INFINITY`
- as input when `precision: 0` is used.
-
- Fixes #19227.
-
- *Yves Senn*
-
-* Fixed the translation helper method to accept different default values types
- besides String.
-
- *Ulisses Almeida*
-
-* Fixed a dependency tracker bug that caused template dependencies not
- count layouts as dependencies for partials.
-
- *Juho Leinonen*
-
-* Extracted `ActionView::Helpers::RecordTagHelper` to external gem
- (`record_tag_helper`) and added removal notices.
-
- *Todd Bealmear*
-
-* Allow to pass a string value to `size` option in `image_tag` and `video_tag`.
-
- This makes the behavior more consistent with `width` or `height` options.
-
- *Mehdi Lahmam*
-
-* Partial template name does no more have to be a valid Ruby identifier.
-
- There used to be a naming rule that the partial name should start with
- underscore, and should be followed by any combination of letters, numbers
- and underscores.
- But now we can give our partials any name starting with underscore, such as
- _🍔.html.erb.
-
- *Akira Matsuda*
-
-* Change the default template handler from `ERB` to `Raw`.
-
- Files without a template handler in their extension will be rendered using the raw
- handler instead of ERB.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `AbstractController::Base::parent_prefixes`.
-
- *Rafael Mendonça França*
-
-* Default translations that have a lower precedence than a html safe default,
- but are not themselves safe, should not be marked as html_safe.
-
- *Justin Coyne*
-
-* Make possible to use blocks with short version of `render "partial"` helper.
-
- *Nikolay Shebanov*
-
-* Add a `hidden_field` on the `file_field` to avoid raising an error when the only
- input on the form is the `file_field`.
-
- *Mauro George*
-
-* Add an explicit error message, in `ActionView::PartialRenderer` for partial
- `rendering`, when the value of option `as` has invalid characters.
-
- *Angelo Capilleri*
-
-* Allow entries without a link tag in `AtomFeedHelper`.
-
- *Daniel Gomez de Souza*
-
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/actionview/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actionview/CHANGELOG.md) for previous changes.
diff --git a/actionview/Rakefile b/actionview/Rakefile
index d41030c650..f0f9bda3e4 100644
--- a/actionview/Rakefile
+++ b/actionview/Rakefile
@@ -4,7 +4,6 @@ desc "Default Task"
task :default => :test
task :package
-task "package:clean"
# Run the unit tests
diff --git a/actionview/actionview.gemspec b/actionview/actionview.gemspec
index 8b0e031dee..75c5045ec0 100644
--- a/actionview/actionview.gemspec
+++ b/actionview/actionview.gemspec
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
s.add_dependency 'builder', '~> 3.1'
s.add_dependency 'erubis', '~> 2.7.0'
s.add_dependency 'rails-html-sanitizer', '~> 1.0', '>= 1.0.2'
- s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
+ s.add_dependency 'rails-dom-testing', '~> 2.0'
s.add_development_dependency 'actionpack', version
s.add_development_dependency 'activemodel', version
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index b99d1af998..b91e61da18 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -4,14 +4,7 @@ require 'monitor'
module ActionView
class Digestor
- @@digest_mutex = Mutex.new
-
- class PerRequestDigestCacheExpiry < Struct.new(:app) # :nodoc:
- def call(env)
- ActionView::LookupContext::DetailsKey.clear
- app.call(env)
- end
- end
+ @@digest_mutex = Mutex.new
class << self
# Supported options:
diff --git a/actionview/lib/action_view/gem_version.rb b/actionview/lib/action_view/gem_version.rb
index efb565bf59..5fc4f3f1b9 100644
--- a/actionview/lib/action_view/gem_version.rb
+++ b/actionview/lib/action_view/gem_version.rb
@@ -6,9 +6,9 @@ module ActionView
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 4c7c4b91c6..4eaaa239e2 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -41,11 +41,11 @@ module ActionView
#
# ==== \Template digest
#
- # The template digest that's added to the cache key is computed by taking an md5 of the
+ # The template digest that's added to the cache key is computed by taking an MD5 of the
# contents of the entire template file. This ensures that your caches will automatically
# expire when you change the template file.
#
- # Note that the md5 is taken of the entire template file, not just what's within the
+ # Note that the MD5 is taken of the entire template file, not just what's within the
# cache do/end call. So it's possible that changing something outside of that call will
# still expire the cache.
#
@@ -118,7 +118,7 @@ module ActionView
#
# If you use a helper method, for example, inside a cached block and
# you then update that helper, you'll have to bump the cache as well.
- # It doesn't really matter how you do it, but the md5 of the template file
+ # It doesn't really matter how you do it, but the MD5 of the template file
# must change. One recommendation is to simply be explicit in a comment, like:
#
# <%# Helper Dependency Updated: May 6, 2012 at 6pm %>
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index cfff0bef5d..82f2fd30c7 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -134,13 +134,15 @@ module ActionView
if options.include?(:include_blank)
include_blank = options.delete(:include_blank)
+ options_for_blank_options_tag = { value: '' }
if include_blank == true
include_blank = ''
+ options_for_blank_options_tag[:label] = ' '
end
if include_blank
- option_tags = content_tag("option".freeze, include_blank, value: '').safe_concat(option_tags)
+ option_tags = content_tag("option".freeze, include_blank, options_for_blank_options_tag).safe_concat(option_tags)
end
end
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index 626c4b8f5e..9db1460ee7 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -63,7 +63,7 @@ module ActionView
details = details.dup
details[:formats] &= Template::Types.symbols
end
- @details_keys[details] ||= new
+ @details_keys[details] ||= Concurrent::Map.new
end
def self.clear
@@ -71,13 +71,7 @@ module ActionView
end
def self.digest_caches
- @details_keys.values.map(&:digest_cache)
- end
-
- attr_reader :digest_cache
-
- def initialize
- @digest_cache = Concurrent::Map.new
+ @details_keys.values
end
end
@@ -236,7 +230,7 @@ module ActionView
end
def digest_cache
- details_key.digest_cache
+ details_key
end
def initialize_details(target, details)
diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb
index 67c80d22d8..dfb99f4ea9 100644
--- a/actionview/lib/action_view/railtie.rb
+++ b/actionview/lib/action_view/railtie.rb
@@ -37,16 +37,10 @@ module ActionView
end
end
- initializer "action_view.collection_caching", after: "action_controller.set_configs" do |app|
- ActiveSupport.on_load(:action_controller) do
- PartialRenderer.collection_cache = app.config.action_controller.cache_store
- end
- end
-
initializer "action_view.per_request_digest_cache" do |app|
ActiveSupport.on_load(:action_view) do
if app.config.consider_all_requests_local
- app.middleware.use ActionView::Digestor::PerRequestDigestCacheExpiry
+ app.executor.to_run { ActionView::LookupContext::DetailsKey.clear }
end
end
end
@@ -57,6 +51,10 @@ module ActionView
end
end
+ initializer "action_view.collection_caching", after: "action_controller.set_configs" do |app|
+ PartialRenderer.collection_cache = app.config.action_controller.cache_store
+ end
+
rake_tasks do |app|
unless app.config.api_only
load "action_view/tasks/cache_digests.rake"
diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb
index 9d15bbfca7..1d6afb90fe 100644
--- a/actionview/lib/action_view/renderer/template_renderer.rb
+++ b/actionview/lib/action_view/renderer/template_renderer.rb
@@ -84,13 +84,13 @@ module ActionView
when String
begin
if layout =~ /^\//
- with_fallbacks { find_template(layout, nil, false, keys, details) }
+ with_fallbacks { find_template(layout, nil, false, [], details) }
else
- find_template(layout, nil, false, keys, details)
+ find_template(layout, nil, false, [], details)
end
rescue ActionView::MissingTemplate
all_details = @details.merge(:formats => @lookup_context.default_formats)
- raise unless template_exists?(layout, nil, false, keys, all_details)
+ raise unless template_exists?(layout, nil, false, [], all_details)
end
when Proc
resolve_layout(layout.call(formats), keys, formats)
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index c5e69b1833..bf68e93c58 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -88,6 +88,23 @@ module ActionView
@query_cache.clear
end
+ # Get the cache size. Do not call this
+ # method. This method is not guaranteed to be here ever.
+ def size # :nodoc:
+ size = 0
+ @data.each_value do |v1|
+ v1.each_value do |v2|
+ v2.each_value do |v3|
+ v3.each_value do |v4|
+ size += v4.size
+ end
+ end
+ end
+ end
+
+ size + @query_cache.size
+ end
+
private
def canonical_no_templates(templates)
diff --git a/actionview/test/actionpack/abstract/layouts_test.rb b/actionview/test/actionpack/abstract/layouts_test.rb
index 80bc665b0a..78f6e78c61 100644
--- a/actionview/test/actionpack/abstract/layouts_test.rb
+++ b/actionview/test/actionpack/abstract/layouts_test.rb
@@ -12,7 +12,9 @@ module AbstractControllerTests
abstract!
self.view_paths = [ActionView::FixtureResolver.new(
+ "some/template.erb" => "hello <%= foo %> bar",
"layouts/hello.erb" => "With String <%= yield %>",
+ "layouts/hello_locals.erb" => "With String <%= yield %>",
"layouts/hello_override.erb" => "With Override <%= yield %>",
"layouts/overwrite.erb" => "Overwrite <%= yield %>",
"layouts/with_false_layout.erb" => "False Layout <%= yield %>",
@@ -32,6 +34,14 @@ module AbstractControllerTests
end
end
+ class WithStringLocals < Base
+ layout "hello_locals"
+
+ def index
+ render :template => 'some/template', locals: { foo: "less than 3" }
+ end
+ end
+
class WithString < Base
layout "hello"
@@ -39,6 +49,10 @@ module AbstractControllerTests
render :template => ActionView::Template::Text.new("Hello string!")
end
+ def action_has_layout_false
+ render template: ActionView::Template::Text.new("Hello string!")
+ end
+
def overwrite_default
render :template => ActionView::Template::Text.new("Hello string!"), :layout => :default
end
@@ -82,7 +96,7 @@ module AbstractControllerTests
end
end
- class WithProcReturningNil < Base
+ class WithProcReturningNil < WithString
layout proc { nil }
def index
@@ -90,6 +104,14 @@ module AbstractControllerTests
end
end
+ class WithProcReturningFalse < WithString
+ layout proc { false }
+
+ def index
+ render template: ActionView::Template::Text.new("Hello false!")
+ end
+ end
+
class WithZeroArityProc < Base
layout proc { "overwrite" }
@@ -189,6 +211,14 @@ module AbstractControllerTests
end
end
+ class WithOnlyConditionalFlipped < WithOnlyConditional
+ layout "hello_override", only: :index
+ end
+
+ class WithOnlyConditionalFlippedAndInheriting < WithOnlyConditional
+ layout nil, only: :index
+ end
+
class WithExceptConditional < WithStringImpliedChild
layout "overwrite", :except => :show
@@ -201,6 +231,45 @@ module AbstractControllerTests
end
end
+ class AbstractWithString < Base
+ layout "hello"
+ abstract!
+ end
+
+ class AbstractWithStringChild < AbstractWithString
+ def index
+ render template: ActionView::Template::Text.new("Hello abstract child!")
+ end
+ end
+
+ class AbstractWithStringChildDefaultsToInherited < AbstractWithString
+ layout nil
+
+ def index
+ render template: ActionView::Template::Text.new("Hello abstract child!")
+ end
+ end
+
+ class WithConditionalOverride < WithString
+ layout "overwrite", only: :overwritten
+
+ def non_overwritten
+ render template: ActionView::Template::Text.new("Hello non overwritten!")
+ end
+
+ def overwritten
+ render template: ActionView::Template::Text.new("Hello overwritten!")
+ end
+ end
+
+ class WithConditionalOverrideFlipped < WithConditionalOverride
+ layout "hello_override", only: :non_overwritten
+ end
+
+ class WithConditionalOverrideFlippedAndInheriting < WithConditionalOverride
+ layout nil, only: :non_overwritten
+ end
+
class TestBase < ActiveSupport::TestCase
test "when no layout is specified, and no default is available, render without a layout" do
controller = Blank.new
@@ -208,6 +277,31 @@ module AbstractControllerTests
assert_equal "Hello blank!", controller.response_body
end
+ test "with locals" do
+ controller = WithStringLocals.new
+ controller.process(:index)
+ assert_equal "With String hello less than 3 bar", controller.response_body
+ end
+
+ test "cache should not grow when locals change for a string template" do
+ cache = WithString.view_paths.paths.first.instance_variable_get(:@cache)
+
+ controller = WithString.new
+ controller.process(:index) # heat the cache
+
+ size = cache.size
+
+ 10.times do |x|
+ controller = WithString.new
+ controller.define_singleton_method :index do
+ render :template => ActionView::Template::Text.new("Hello string!"), :locals => { :"x#{x}" => :omg }
+ end
+ controller.process(:index)
+ end
+
+ assert_equal size, cache.size
+ end
+
test "when layout is specified as a string, render with that layout" do
controller = WithString.new
controller.process(:index)
@@ -264,10 +358,16 @@ module AbstractControllerTests
assert_equal "Overwrite Hello proc!", controller.response_body
end
- test "when layout is specified as a proc and the proc returns nil, don't use a layout" do
+ test "when layout is specified as a proc and the proc returns nil, use inherited layout" do
controller = WithProcReturningNil.new
controller.process(:index)
- assert_equal "Hello nil!", controller.response_body
+ assert_equal "With String Hello nil!", controller.response_body
+ end
+
+ test "when layout is specified as a proc and the proc returns false, use no layout instead of inherited layout" do
+ controller = WithProcReturningFalse.new
+ controller.process(:index)
+ assert_equal "Hello false!", controller.response_body
end
test "when layout is specified as a proc without parameters it works just the same" do
@@ -328,12 +428,24 @@ module AbstractControllerTests
end
test "when a grandchild has nil layout specified, the child has an implied layout, and the " \
- "parent has specified a layout, use the child controller layout" do
+ "parent has specified a layout, use the grand child controller layout" do
controller = WithGrandChildOfImplied.new
controller.process(:index)
assert_equal "With Grand Child Hello string!", controller.response_body
end
+ test "a child inherits layout from abstract controller" do
+ controller = AbstractWithStringChild.new
+ controller.process(:index)
+ assert_equal "With String Hello abstract child!", controller.response_body
+ end
+
+ test "a child inherits layout from abstract controller2" do
+ controller = AbstractWithStringChildDefaultsToInherited.new
+ controller.process(:index)
+ assert_equal "With String Hello abstract child!", controller.response_body
+ end
+
test "raises an exception when specifying layout true" do
assert_raises ArgumentError do
Object.class_eval do
@@ -356,6 +468,30 @@ module AbstractControllerTests
assert_equal "With Implied Hello index!", controller.response_body
end
+ test "when specify an :only option which match current action name and is opposite from parent controller" do
+ controller = WithOnlyConditionalFlipped.new
+ controller.process(:show)
+ assert_equal "With Implied Hello show!", controller.response_body
+ end
+
+ test "when specify an :only option which does not match current action name and is opposite from parent controller" do
+ controller = WithOnlyConditionalFlipped.new
+ controller.process(:index)
+ assert_equal "With Override Hello index!", controller.response_body
+ end
+
+ test "when specify to inherit and an :only option which match current action name and is opposite from parent controller" do
+ controller = WithOnlyConditionalFlippedAndInheriting.new
+ controller.process(:show)
+ assert_equal "With Implied Hello show!", controller.response_body
+ end
+
+ test "when specify to inherit and an :only option which does not match current action name and is opposite from parent controller" do
+ controller = WithOnlyConditionalFlippedAndInheriting.new
+ controller.process(:index)
+ assert_equal "Overwrite Hello index!", controller.response_body
+ end
+
test "when specify an :except option which match current action name" do
controller = WithExceptConditional.new
controller.process(:show)
@@ -368,6 +504,42 @@ module AbstractControllerTests
assert_equal "Overwrite Hello index!", controller.response_body
end
+ test "when specify overwrite as an :only option which match current action name" do
+ controller = WithConditionalOverride.new
+ controller.process(:overwritten)
+ assert_equal "Overwrite Hello overwritten!", controller.response_body
+ end
+
+ test "when specify overwrite as an :only option which does not match current action name" do
+ controller = WithConditionalOverride.new
+ controller.process(:non_overwritten)
+ assert_equal "Hello non overwritten!", controller.response_body
+ end
+
+ test "when specify overwrite as an :only option which match current action name and is opposite from parent controller" do
+ controller = WithConditionalOverrideFlipped.new
+ controller.process(:overwritten)
+ assert_equal "Hello overwritten!", controller.response_body
+ end
+
+ test "when specify overwrite as an :only option which does not match current action name and is opposite from parent controller" do
+ controller = WithConditionalOverrideFlipped.new
+ controller.process(:non_overwritten)
+ assert_equal "With Override Hello non overwritten!", controller.response_body
+ end
+
+ test "when specify to inherit and overwrite as an :only option which match current action name and is opposite from parent controller" do
+ controller = WithConditionalOverrideFlippedAndInheriting.new
+ controller.process(:overwritten)
+ assert_equal "Hello overwritten!", controller.response_body
+ end
+
+ test "when specify to inherit and overwrite as an :only option which does not match current action name and is opposite from parent controller" do
+ controller = WithConditionalOverrideFlippedAndInheriting.new
+ controller.process(:non_overwritten)
+ assert_equal "Overwrite Hello non overwritten!", controller.response_body
+ end
+
test "layout for anonymous controller" do
klass = Class.new(WithString) do
def index
@@ -379,6 +551,17 @@ module AbstractControllerTests
controller.process(:index)
assert_equal "With String index", controller.response_body
end
+
+ test "when layout is disabled with #action_has_layout? returning false, render no layout" do
+ controller = WithString.new
+ controller.instance_eval do
+ def action_has_layout?
+ false
+ end
+ end
+ controller.process(:action_has_layout_false)
+ assert_equal "Hello string!", controller.response_body
+ end
end
end
end
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index d4c5048bde..4750d2a5a3 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -61,25 +61,21 @@ class TemplateDigestorTest < ActionView::TestCase
end
def test_explicit_dependency_wildcard_picks_up_added_file
- old_caching, ActionView::Resolver.caching = ActionView::Resolver.caching, false
-
- assert_digest_difference("events/index") do
- add_template("events/_uncompleted")
+ disable_resolver_caching do
+ assert_digest_difference("events/index") do
+ add_template("events/_uncompleted")
+ end
end
- ensure
- remove_template("events/_uncompleted")
- ActionView::Resolver.caching = old_caching
end
def test_explicit_dependency_wildcard_picks_up_removed_file
- old_caching, ActionView::Resolver.caching = ActionView::Resolver.caching, false
- add_template("events/_subscribers_changed")
+ disable_resolver_caching do
+ add_template("events/_subscribers_changed")
- assert_digest_difference("events/index") do
- remove_template("events/_subscribers_changed")
+ assert_digest_difference("events/index") do
+ remove_template("events/_subscribers_changed")
+ end
end
- ensure
- ActionView::Resolver.caching = old_caching
end
def test_second_level_dependency
@@ -273,18 +269,15 @@ class TemplateDigestorTest < ActionView::TestCase
end
def test_digest_cache_cleanup_with_recursion_and_template_caching_off
- resolver_before = ActionView::Resolver.caching
- ActionView::Resolver.caching = false
+ disable_resolver_caching do
+ first_digest = digest("level/_recursion")
+ second_digest = digest("level/_recursion")
- first_digest = digest("level/_recursion")
- second_digest = digest("level/_recursion")
+ assert first_digest
- assert first_digest
-
- # If the cache is cleaned up correctly, subsequent digests should return the same
- assert_equal first_digest, second_digest
- ensure
- ActionView::Resolver.caching = resolver_before
+ # If the cache is cleaned up correctly, subsequent digests should return the same
+ assert_equal first_digest, second_digest
+ end
end
@@ -331,6 +324,13 @@ class TemplateDigestorTest < ActionView::TestCase
tree.children.map(&:to_dep_map)
end
+ def disable_resolver_caching
+ old_caching, ActionView::Resolver.caching = ActionView::Resolver.caching, false
+ yield
+ ensure
+ ActionView::Resolver.caching = old_caching
+ end
+
def finder
@finder ||= FixtureFinder.new
end
diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb
index c5b63d33f1..7a5904f151 100644
--- a/actionview/test/template/form_options_helper_test.rb
+++ b/actionview/test/template/form_options_helper_test.rb
@@ -798,7 +798,7 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
- def test_select_with_fixnum
+ def test_select_with_integer
@post = Post.new
@post.category = ""
assert_dom_equal(
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index 7b93c8dc29..5b0b708618 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -239,8 +239,8 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_select_tag_with_include_blank
- actual = select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), :include_blank => true
- expected = %(<select id="places" name="places"><option value=""></option><option>Home</option><option>Work</option><option>Pub</option></select>)
+ actual = select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), include_blank: true
+ expected = %(<select id="places" name="places"><option value="" label=" "></option><option>Home</option><option>Work</option><option>Pub</option></select>)
assert_dom_equal expected, actual
end
@@ -269,14 +269,14 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_select_tag_with_prompt_and_include_blank
- actual = select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), :prompt => "string", :include_blank => true
- expected = %(<select name="places" id="places"><option value="">string</option><option value=""></option><option>Home</option><option>Work</option><option>Pub</option></select>)
+ actual = select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), prompt: "string", include_blank: true
+ expected = %(<select name="places" id="places"><option value="">string</option><option value="" label=" "></option><option>Home</option><option>Work</option><option>Pub</option></select>)
assert_dom_equal expected, actual
end
def test_select_tag_with_nil_option_tags_and_include_blank
actual = select_tag "places", nil, :include_blank => true
- expected = %(<select id="places" name="places"><option value=""></option></select>)
+ expected = %(<select id="places" name="places"><option value="" label=" "></option></select>)
assert_dom_equal expected, actual
end
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index efe46ce5ab..5e72c67aea 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,160 +1,2 @@
-* Enable class reloading prior to job dispatch, and ensure Active Record
- connections are returned to the pool when jobs are run in separate threads.
- *Matthew Draper*
-
-* Tune the async adapter for low-footprint dev/test usage. Use a single
- thread pool for all queues and limit to 0 to #CPU total threads, down from
- 2 to 10*#CPU per queue.
-
- *Jeremy Daer*
-
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* Change the default adapter from inline to async. It's a better default as tests will then not mistakenly
- come to rely on behavior happening synchronously. This is especially important with things like jobs kicked off
- in Active Record lifecycle callbacks.
-
- *DHH*
-
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* No changes.
-
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* Fixed serializing `:at` option for `assert_enqueued_with`
- and `assert_performed_with`.
-
- *Wojciech Wnętrzak*
-
-* Support passing array to `assert_enqueued_jobs` in `:only` option.
-
- *Wojciech Wnętrzak*
-
-* Add job priorities to Active Job.
-
- *wvengen*
-
-* Implement a simple `AsyncJob` processor and associated `AsyncAdapter` that
- queue jobs to a `concurrent-ruby` thread pool.
-
- *Jerry D'Antonio*
-
-* Implement `provider_job_id` for `queue_classic` adapter. This requires the
- latest, currently unreleased, version of queue_classic.
-
- *Yves Senn*
-
-* `assert_enqueued_with` and `assert_performed_with` now returns the matched
- job instance for further assertions.
-
- *Jean Boussier*
-
-* Include `I18n.locale` into job serialization/deserialization and use it around
- `perform`.
-
- Fixes #20799.
-
- *Johannes Opper*
-
-* Allow `DelayedJob`, `Sidekiq`, `qu`, and `que` to report the job id back to
- `ActiveJob::Base` as `provider_job_id`.
-
- Fixes #18821.
-
- *Kevin Deisz*, *Jeroen van Baarsen*
-
-* `assert_enqueued_jobs` and `assert_performed_jobs` in block form use the
- given number as expected value. This makes the error message much easier to
- understand.
-
- *y-yagi*
-
-* A generated job now inherits from `app/jobs/application_job.rb` by default.
-
- *Jeroen van Baarsen*
-
-* Add an `:only` option to `perform_enqueued_jobs` to filter jobs based on
- type.
-
- This allows specific jobs to be tested, while preventing others from
- being performed unnecessarily.
-
- Example:
-
- def test_hello_job
- assert_performed_jobs 1, only: HelloJob do
- HelloJob.perform_later('jeremy')
- LoggingJob.perform_later
- end
- end
-
- An array may also be specified, to support testing multiple jobs.
-
- Example:
-
- def test_hello_and_logging_jobs
- assert_nothing_raised do
- assert_performed_jobs 2, only: [HelloJob, LoggingJob] do
- HelloJob.perform_later('jeremy')
- LoggingJob.perform_later('stewie')
- RescueJob.perform_later('david')
- end
- end
- end
-
- Fixes #18802.
-
- *Michael Ryan*
-
-* Allow keyword arguments to be used with Active Job.
-
- Fixes #18741.
-
- *Sean Griffin*
-
-* Add `:only` option to `assert_enqueued_jobs`, to check the number of times
- a specific kind of job is enqueued.
-
- Example:
-
- def test_logging_job
- assert_enqueued_jobs 1, only: LoggingJob do
- LoggingJob.perform_later
- HelloJob.perform_later('jeremy')
- end
- end
-
- *George Claghorn*
-
-* `ActiveJob::Base.deserialize` delegates to the job class.
-
- Since `ActiveJob::Base#deserialize` can be overridden by subclasses (like
- `ActiveJob::Base#serialize`) this allows jobs to attach arbitrary metadata
- when they get serialized and read it back when they get performed.
-
- Example:
-
- class DeliverWebhookJob < ActiveJob::Base
- def serialize
- super.merge('attempt_number' => (@attempt_number || 0) + 1)
- end
-
- def deserialize(job_data)
- super
- @attempt_number = job_data['attempt_number']
- end
-
- rescue_from(TimeoutError) do |exception|
- raise exception if @attempt_number > 5
- retry_job(wait: 10)
- end
- end
-
- *Isaac Seymour*
-
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activejob/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/activejob/CHANGELOG.md) for previous changes.
diff --git a/activejob/Rakefile b/activejob/Rakefile
index 2a853b4b6b..e47f17b740 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -1,13 +1,13 @@
require 'rake/testtask'
-ACTIVEJOB_ADAPTERS = %w(async inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner test)
+#TODO: add qu back to the list after it support Rails 5.1
+ACTIVEJOB_ADAPTERS = %w(async inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch backburner test)
ACTIVEJOB_ADAPTERS -= %w(queue_classic) if defined?(JRUBY_VERSION)
task default: :test
task test: 'test:default'
task :package
-task "package:clean"
namespace :test do
desc 'Run all adapter tests'
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index 33bd5b4eb3..a5c749e5e7 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -24,7 +24,7 @@ module ActiveJob
end
# Raised when an unsupported argument type is set as a job argument. We
- # currently support NilClass, Fixnum, Float, String, TrueClass, FalseClass,
+ # currently support NilClass, Integer, Fixnum, Float, String, TrueClass, FalseClass,
# Bignum, BigDecimal, and objects that can be represented as GlobalIDs (ex: Active Record).
# Raised if you set the key for a Hash something else than a string or
# a symbol. Also raised when trying to serialize an object which can't be
@@ -34,7 +34,8 @@ module ActiveJob
module Arguments
extend self
# :nodoc:
- TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum, BigDecimal ]
+ # Calls #uniq since Integer, Fixnum, and Bignum are all the same class on Ruby 2.4+
+ TYPE_WHITELIST = [ NilClass, String, Integer, Fixnum, Bignum, Float, BigDecimal, TrueClass, FalseClass ].uniq
# Serializes a set of arguments. Whitelisted types are returned
# as-is. Arrays/Hashes are serialized element by element.
diff --git a/activejob/lib/active_job/callbacks.rb b/activejob/lib/active_job/callbacks.rb
index a6591c6a05..b206522a60 100644
--- a/activejob/lib/active_job/callbacks.rb
+++ b/activejob/lib/active_job/callbacks.rb
@@ -126,8 +126,8 @@ module ActiveJob
set_callback(:enqueue, :after, *filters, &blk)
end
- # Defines a callback that will get called before and after the
- # job is enqueued.
+ # Defines a callback that will get called around the enqueueing
+ # of the job.
#
# class VideoProcessJob < ActiveJob::Base
# queue_as :default
diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb
index 7c4151fc90..4e4acfc2c2 100644
--- a/activejob/lib/active_job/execution.rb
+++ b/activejob/lib/active_job/execution.rb
@@ -34,7 +34,7 @@ module ActiveJob
perform(*arguments)
end
rescue => exception
- rescue_with_handler(exception) || raise(exception)
+ rescue_with_handler(exception) || raise
end
def perform(*)
diff --git a/activejob/lib/active_job/gem_version.rb b/activejob/lib/active_job/gem_version.rb
index be4fabf545..0d50c27938 100644
--- a/activejob/lib/active_job/gem_version.rb
+++ b/activejob/lib/active_job/gem_version.rb
@@ -6,9 +6,9 @@ module ActiveJob
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb
index 9897f76fd0..fdf25c67a1 100644
--- a/activejob/test/support/integration/test_case_helpers.rb
+++ b/activejob/test/support/integration/test_case_helpers.rb
@@ -1,4 +1,5 @@
require 'active_support/concern'
+require 'active_support/core_ext/string/inflections'
require 'support/integration/jobs_manager'
module TestCaseHelpers
@@ -28,7 +29,8 @@ module TestCaseHelpers
end
def adapter_is?(*adapter_class_symbols)
- adapter_class_symbols.map(&:to_s).include?(ActiveJob::Base.queue_adapter.class.name.split("::").last.gsub(/Adapter$/, '').underscore)
+ adapter = ActiveJob::Base.queue_adapter.class.name.demodulize.chomp('Adapter').underscore
+ adapter_class_symbols.map(&:to_s).include? adapter
end
def wait_for_jobs_to_finish_for(seconds=60)
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 7be8b2e522..206699c036 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,147 +1,2 @@
-* Allow passing record being validated to the message proc to generate
- customized error messages for that object using I18n helper.
- *Prathamesh Sonpatki*
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* No changes.
-
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* No changes.
-
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* Validate multiple contexts on `valid?` and `invalid?` at once.
-
- Example:
-
- class Person
- include ActiveModel::Validations
-
- attr_reader :name, :title
- validates_presence_of :name, on: :create
- validates_presence_of :title, on: :update
- end
-
- person = Person.new
- person.valid?([:create, :update]) # => false
- person.errors.messages # => {:name=>["can't be blank"], :title=>["can't be blank"]}
-
- *Dmitry Polushkin*
-
-* Add case_sensitive option for confirmation validator in models.
-
- *Akshat Sharma*
-
-* Ensure `method_missing` is called for methods passed to
- `ActiveModel::Serialization#serializable_hash` that don't exist.
-
- *Jay Elaraj*
-
-* Remove `ActiveModel::Serializers::Xml` from core.
-
- *Zachary Scott*
-
-* Add `ActiveModel::Dirty#[attr_name]_previously_changed?` and
- `ActiveModel::Dirty#[attr_name]_previous_change` to improve access
- to recorded changes after the model has been saved.
-
- It makes the dirty-attributes query methods consistent before and after
- saving.
-
- *Fernando Tapia Rico*
-
-* Deprecate the `:tokenizer` option for `validates_length_of`, in favor of
- plain Ruby.
-
- *Sean Griffin*
-
-* Deprecate `ActiveModel::Errors#add_on_empty` and `ActiveModel::Errors#add_on_blank`
- with no replacement.
-
- *Wojciech Wnętrzak*
-
-* Deprecate `ActiveModel::Errors#get`, `ActiveModel::Errors#set` and
- `ActiveModel::Errors#[]=` methods that have inconsistent behavior.
-
- *Wojciech Wnętrzak*
-
-* Allow symbol as values for `tokenize` of `LengthValidator`.
-
- *Kensuke Naito*
-
-* Assigning an unknown attribute key to an `ActiveModel` instance during initialization
- will now raise `ActiveModel::AttributeAssignment::UnknownAttributeError` instead of
- `NoMethodError`.
-
- Example:
-
- User.new(foo: 'some value')
- # => ActiveModel::AttributeAssignment::UnknownAttributeError: unknown attribute 'foo' for User.
-
- *Eugene Gilburg*
-
-* Extracted `ActiveRecord::AttributeAssignment` to `ActiveModel::AttributeAssignment`
- allowing to use it for any object as an includable module.
-
- Example:
-
- class Cat
- include ActiveModel::AttributeAssignment
- attr_accessor :name, :status
- end
-
- cat = Cat.new
- cat.assign_attributes(name: "Gorby", status: "yawning")
- cat.name # => 'Gorby'
- cat.status # => 'yawning'
- cat.assign_attributes(status: "sleeping")
- cat.name # => 'Gorby'
- cat.status # => 'sleeping'
-
- *Bogdan Gusiev*
-
-* Add `ActiveModel::Errors#details`
-
- To be able to return type of used validator, one can now call `details`
- on errors instance.
-
- Example:
-
- class User < ActiveRecord::Base
- validates :name, presence: true
- end
-
- user = User.new; user.valid?; user.errors.details
- => {name: [{error: :blank}]}
-
- *Wojciech Wnętrzak*
-
-* Change validates_acceptance_of to accept true by default.
-
- The default for validates_acceptance_of is now "1" and true.
- In the past, only "1" was the default and you were required to add
- accept: true.
-
-* Remove deprecated `ActiveModel::Dirty#reset_#{attribute}` and
- `ActiveModel::Dirty#reset_changes`.
-
- *Rafael Mendonça França*
-
-* Change the way in which callback chains can be halted.
-
- The preferred method to halt a callback chain from now on is to explicitly
- `throw(:abort)`.
- In the past, returning `false` in an Active Model `before_` callback had
- the side effect of halting the callback chain.
- This is not recommended anymore and, depending on the value of the
- `ActiveSupport.halt_callback_chains_on_return_false` option, will
- either not work at all or display a deprecation warning.
-
- *claudiob*
-
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activemodel/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/activemodel/CHANGELOG.md) for previous changes.
diff --git a/activemodel/Rakefile b/activemodel/Rakefile
index 9982d49bcb..036815a987 100644
--- a/activemodel/Rakefile
+++ b/activemodel/Rakefile
@@ -5,7 +5,6 @@ dir = File.dirname(__FILE__)
task :default => :test
task :package
-task "package:clean"
Rake::TestTask.new do |t|
t.libs << "test"
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index b95c174d6e..7de259a60d 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -49,6 +49,7 @@ module ActiveModel
eager_autoload do
autoload :Errors
+ autoload :RangeError, 'active_model/errors'
autoload :StrictValidationFailed, 'active_model/errors'
autoload :UnknownAttributeError, 'active_model/errors'
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index d106f65fa2..696e6d31da 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -310,9 +310,9 @@ module ActiveModel
# <tt>:strict</tt> option can also be set to any other exception.
#
# person.errors.add(:name, :invalid, strict: true)
- # # => ActiveModel::StrictValidationFailed: name is invalid
+ # # => ActiveModel::StrictValidationFailed: Name is invalid
# person.errors.add(:name, :invalid, strict: NameIsInvalid)
- # # => NameIsInvalid: name is invalid
+ # # => NameIsInvalid: Name is invalid
#
# person.errors.messages # => {}
#
@@ -526,7 +526,20 @@ module ActiveModel
class StrictValidationFailed < StandardError
end
+ # Raised when attribute values are out of range.
+ class RangeError < ::RangeError
+ end
+
# Raised when unknown attributes are supplied via mass assignment.
+ #
+ # class Person
+ # include ActiveModel::AttributeAssignment
+ # include ActiveModel::Validations
+ # end
+ #
+ # person = Person.new
+ # person.assign_attributes(name: 'Gorby')
+ # # => ActiveModel::UnknownAttributeError: unknown attribute 'name' for Person.
class UnknownAttributeError < NoMethodError
attr_reader :record, :attribute
diff --git a/activemodel/lib/active_model/gem_version.rb b/activemodel/lib/active_model/gem_version.rb
index 62862aa4e9..4a8ee915cf 100644
--- a/activemodel/lib/active_model/gem_version.rb
+++ b/activemodel/lib/active_model/gem_version.rb
@@ -6,9 +6,9 @@ module ActiveModel
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activemodel/lib/active_model/type/integer.rb b/activemodel/lib/active_model/type/integer.rb
index 2f73ede009..eea2280b22 100644
--- a/activemodel/lib/active_model/type/integer.rb
+++ b/activemodel/lib/active_model/type/integer.rb
@@ -46,7 +46,7 @@ module ActiveModel
def ensure_in_range(value)
unless range.cover?(value)
- raise RangeError, "#{value} is out of range for #{self.class} with limit #{_limit}"
+ raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit}"
end
end
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index c5c0cd4636..a04e5f347e 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -64,11 +64,7 @@ module ActiveModel
private
def convert_to_reader_name(method_name)
- attr_name = method_name.to_s
- if attr_name.end_with?("=")
- attr_name = attr_name[0..-2]
- end
- attr_name
+ method_name.to_s.chomp('=')
end
end
end
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index 52111e5442..a201f72ed0 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -29,8 +29,7 @@ module ActiveModel
end
module ClassMethods
- # Defines a callback that will get called right before validation
- # happens.
+ # Defines a callback that will get called right before validation.
#
# class Person
# include ActiveModel::Validations
@@ -65,8 +64,7 @@ module ActiveModel
set_callback(:validation, :before, *args, &block)
end
- # Defines a callback that will get called right after validation
- # happens.
+ # Defines a callback that will get called right after validation.
#
# class Person
# include ActiveModel::Validations
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index ad7012df48..9a0a0655de 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -120,7 +120,7 @@ module ActiveModel
# * <tt>:only_integer</tt> - Specifies whether the value has to be an
# integer, e.g. an integral value (default is +false+).
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is
- # +false+). Notice that for fixnum and float columns empty strings are
+ # +false+). Notice that for Integer and Float columns empty strings are
# converted to +nil+.
# * <tt>:greater_than</tt> - Specifies the value must be greater than the
# supplied value.
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 109bf038b0..699f74ed17 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -100,7 +100,7 @@ module ActiveModel
# PresenceValidator.kind # => :presence
# UniquenessValidator.kind # => :uniqueness
def self.kind
- @kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
+ @kind ||= name.split('::').last.underscore.chomp('_validator').to_sym unless anonymous?
end
# Accepts options that will be made available through the +options+ reader.
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index 26ba6ba4fb..fbf208836f 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -11,7 +11,7 @@ class ErrorsTest < ActiveModel::TestCase
attr_reader :errors
def validate!
- errors.add(:name, "cannot be nil") if name == nil
+ errors.add(:name, :blank, message: "cannot be nil") if name == nil
end
def read_attribute_for_validation(attr)
diff --git a/activemodel/test/cases/type/integer_test.rb b/activemodel/test/cases/type/integer_test.rb
index dac922db42..6603f25c9a 100644
--- a/activemodel/test/cases/type/integer_test.rb
+++ b/activemodel/test/cases/type/integer_test.rb
@@ -53,25 +53,25 @@ module ActiveModel
end
test "values below int min value are out of range" do
- assert_raises(::RangeError) do
+ assert_raises(ActiveModel::RangeError) do
Integer.new.serialize(-2147483649)
end
end
test "values above int max value are out of range" do
- assert_raises(::RangeError) do
+ assert_raises(ActiveModel::RangeError) do
Integer.new.serialize(2147483648)
end
end
test "very small numbers are out of range" do
- assert_raises(::RangeError) do
+ assert_raises(ActiveModel::RangeError) do
Integer.new.serialize(-9999999999999999999999999999999)
end
end
test "very large numbers are out of range" do
- assert_raises(::RangeError) do
+ assert_raises(ActiveModel::RangeError) do
Integer.new.serialize(9999999999999999999999999999999)
end
end
@@ -96,10 +96,10 @@ module ActiveModel
assert_equal(9223372036854775807, type.serialize(9223372036854775807))
assert_equal(-9223372036854775808, type.serialize(-9223372036854775808))
- assert_raises(::RangeError) do
+ assert_raises(ActiveModel::RangeError) do
type.serialize(-9999999999999999999999999999999)
end
- assert_raises(::RangeError) do
+ assert_raises(ActiveModel::RangeError) do
type.serialize(9999999999999999999999999999999)
end
end
diff --git a/activemodel/test/cases/type/unsigned_integer_test.rb b/activemodel/test/cases/type/unsigned_integer_test.rb
index 16301b3ac0..026cb08a06 100644
--- a/activemodel/test/cases/type/unsigned_integer_test.rb
+++ b/activemodel/test/cases/type/unsigned_integer_test.rb
@@ -9,7 +9,7 @@ module ActiveModel
end
test "minus value is out of range" do
- assert_raises(::RangeError) do
+ assert_raises(ActiveModel::RangeError) do
UnsignedInteger.new.serialize(-1)
end
end
diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb
index ee901b75fb..11dce1df20 100644
--- a/activemodel/test/cases/validations/length_validation_test.rb
+++ b/activemodel/test/cases/validations/length_validation_test.rb
@@ -355,7 +355,7 @@ class LengthValidationTest < ActiveModel::TestCase
assert_equal ["Your essay must be at least 5 words."], t.errors[:content]
end
- def test_validates_length_of_for_fixnum
+ def test_validates_length_of_for_integer
Topic.validates_length_of(:approved, is: 4)
t = Topic.new("title" => "uhohuhoh", "content" => "whatever", approved: 1)
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index e76b0973ca..929f241b1b 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,2109 +1,16 @@
-* PostgreSQL: Support Expression Indexes and Operator Classes.
+* Fix logging edge case where if an attribute was of the binary type and
+ was provided as a Hash.
- Example:
+ *Jon Moss*
- create_table :users do |t|
- t.string :name
- t.index 'lower(name) varchar_pattern_ops'
- end
+* Handle JSON deserialization correctly if the column default from database
+ adapter returns `''` instead of `nil`.
- Fixes #19090, #21765, #21819, #24359.
+ *Johannes Opper*
- *Ryuta Kamizono*
+* Introduce ActiveRecord::TransactionSerializationError for catching
+ transaction serialization failures or deadlocks.
-* MySQL: Prepared statements support.
+ *Erol Fornoles*
- To enable, set `prepared_statements: true` in config/database.yml.
- Requires mysql2 0.4.4+.
-
- *Ryuta Kamizono*
-
-* Schema dumper: Indexes are now included in the `create_table` block
- instead of listed afterward as separate `add_index` lines.
-
- This tidies up schema.rb and makes it easy to read as a list of tables.
-
- Bonus: Allows databases that support it (MySQL) to perform as single
- `CREATE TABLE` query, no additional query per index.
-
- *Ryuta Kamizono*
-
-* SQLite: Fix uniqueness validation when values exceed the column limit.
-
- SQLite doesn't impose length restrictions on strings, BLOBs, or numeric
- values. It treats them as helpful metadata. When we truncate strings
- before checking uniqueness, we'd miss values that exceed the column limit.
-
- Other databases enforce length limits. A large value will pass uniqueness
- validation since the column limit guarantees no value that long exists.
- When we insert the row, it'll raise `ActiveRecord::ValueTooLong` as we
- expect.
-
- This fixes edge-case incorrect validation failures for values that exceed
- the column limit but are identical to an existing value *when truncated*.
- Now these will pass validation and raise an exception.
-
- *Ryuta Kamizono*
-
-* Raise `ActiveRecord::ValueTooLong` when column limits are exceeded.
- Supported by MySQL and PostgreSQL adapters.
-
- *Ryuta Kamizono*
-
-* Migrations: `#foreign_key` respects `table_name_prefix` and `_suffix`.
-
- *Ryuta Kamizono*
-
-* SQLite: Force NOT NULL primary keys.
-
- From SQLite docs: https://www.sqlite.org/lang_createtable.html
- According to the SQL standard, PRIMARY KEY should always imply NOT
- NULL. Unfortunately, due to a bug in some early versions, this is not
- the case in SQLite. Unless the column is an INTEGER PRIMARY KEY or the
- table is a WITHOUT ROWID table or the column is declared NOT NULL,
- SQLite allows NULL values in a PRIMARY KEY column. SQLite could be
- fixed to conform to the standard, but doing so might break legacy
- applications. Hence, it has been decided to merely document the fact
- that SQLite allowing NULLs in most PRIMARY KEY columns.
-
- Now we override column options to explicitly set NOT NULL rather than rely
- on implicit NOT NULL like MySQL and PostgreSQL adapters.
-
- *Ryuta Kamizono*
-
-* Added notice when a database is successfully created or dropped.
-
- Example:
-
- $ bin/rails db:create
- Created database 'blog_development'
- Created database 'blog_test'
-
- $ bin/rails db:drop
- Dropped database 'blog_development'
- Dropped database 'blog_test'
-
- Changed older notices
- `blog_development already exists` to `Database 'blog_development' already exists`.
- and
- `Couldn't drop blog_development` to `Couldn't drop database 'blog_development'`.
-
- *bogdanvlviv*
-
-* Database comments. Annotate database objects (tables, columns, indexes)
- with comments stored in database metadata. PostgreSQL & MySQL support.
-
- create_table :pages, force: :cascade, comment: 'CMS content pages' do |t|
- t.string :path, comment: 'Path fragment of page URL used for routing'
- t.string :locale, comment: 'RFC 3066 locale code of website language section'
- t.index [:path, :locale], comment: 'Look up pages by URI'
- end
-
- *Andrey Novikov*
-
-* Add `quoted_time` for truncating the date part of a TIME column value.
- This fixes queries on TIME column on MariaDB, as it doesn't ignore the
- date part of the string when it coerces to time.
-
- *Ryuta Kamizono*
-
-* Properly accept all valid JSON primitives in the JSON data type.
-
- Fixes #24234
-
- *Sean Griffin*
-
-* MariaDB 5.3+ supports microsecond datetime precision.
-
- *Jeremy Daer*
-
-* Delegate `empty?`, `none?` and `one?`. Now they can be invoked as model class methods.
-
- Example:
-
- # When no record is found on the table
- Topic.empty? # => true
- Topic.none? # => true
-
- # When only one record is found on the table
- Topic.one? # => true
-
- *Kenta Shirai*
-
-* The form builder now properly displays values when passing a proc form
- default to the attributes API.
-
- Fixes #24249.
-
- *Sean Griffin*
-
-* The schema cache is now cleared after the `db:migrate` task is run.
-
- Closes #24273.
-
- *Chris Arcand*
-
-* MySQL: strict mode respects other SQL modes rather than overwriting them.
- Setting `strict: true` adds `STRICT_ALL_TABLES` to `sql_mode`. Setting
- `strict: false` removes `STRICT_TRANS_TABLES`, `STRICT_ALL_TABLES`, and
- `TRADITIONAL` from `sql_mode`.
-
- *Ryuta Kamizono*
-
-* Execute default_scope defined by abstract class in the context of subclass.
-
- Fixes #23413.
- Fixes #10658.
-
- *Mehmet Emin İNAÇ*
-
-* Fix an issue when preloading associations with extensions.
- Previously every association with extension methods was transformed into an
- instance dependent scope. This is no longer the case.
-
- Fixes #23934.
-
- *Yves Senn*
-
-* Deprecate `{insert|update|delete}_sql` in `DatabaseStatements`.
- Use the `{insert|update|delete}` public methods instead.
-
- *Ryuta Kamizono*
-
-* Added a configuration option to have active record raise an ArgumentError
- if the order or limit is ignored in a batch query, rather than logging a
- warning message.
-
- *Scott Ringwelski*
-
-* Honour the order of the joining model in a `has_many :through` association when eager loading.
-
- Example:
-
- The below will now follow the order of `by_lines` when eager loading `authors`.
-
- class Article < ActiveRecord::Base
- has_many :by_lines, -> { order(:position) }
- has_many :authors, through: :by_lines
- end
-
- Fixes #17864.
-
- *Yasyf Mohamedali*, *Joel Turkel*
-
-* Ensure that the Suppressor runs before validations.
-
- This moves the suppressor up to be run before validations rather than after
- validations. There's no reason to validate a record you aren't planning on saving.
-
- *Eileen M. Uchitelle*
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* Save many-to-many objects based on association primary key.
-
- Fixes #20995.
-
- *himesh-r*
-
-* Ensure that mutations of the array returned from `ActiveRecord::Relation#to_a`
- do not affect the original relation, by returning a duplicate array each time.
-
- This brings the behavior in line with `CollectionProxy#to_a`, which was
- already more careful.
-
- *Matthew Draper*
-
-* Fixed `where` for polymorphic associations when passed an array containing different types.
-
- Fixes #17011.
-
- Example:
-
- PriceEstimate.where(estimate_of: [Treasure.find(1), Car.find(2)])
- # => SELECT "price_estimates".* FROM "price_estimates"
- WHERE (("price_estimates"."estimate_of_type" = 'Treasure' AND "price_estimates"."estimate_of_id" = 1)
- OR ("price_estimates"."estimate_of_type" = 'Car' AND "price_estimates"."estimate_of_id" = 2))
-
- *Philippe Huibonhoa*
-
-* Fix a bug where using `t.foreign_key` twice with the same `to_table` within
- the same table definition would only create one foreign key.
-
- *George Millo*
-
-* Fix a regression on has many association, where calling a child from parent in child's callback
- results in same child records getting added repeatedly to target.
-
- Fixes #13387.
-
- *Bogdan Gusiev*, *Jon Hinson*
-
-* Rework `ActiveRecord::Relation#last`.
-
- 1. Never perform additional SQL on loaded relation
- 2. Use SQL reverse order instead of loading relation if relation doesn't have limit
- 3. Deprecated relation loading when SQL order can not be automatically reversed
-
- Topic.order("title").load.last(3)
- # before: SELECT ...
- # after: No SQL
-
- Topic.order("title").last
- # before: SELECT * FROM `topics`
- # after: SELECT * FROM `topics` ORDER BY `topics`.`title` DESC LIMIT 1
-
- Topic.order("coalesce(author, title)").last
- # before: SELECT * FROM `topics`
- # after: Deprecation Warning for irreversible order
-
- *Bogdan Gusiev*
-
-* Allow `joins` to be unscoped.
-
- Fixes #13775.
-
- *Takashi Kokubun*
-
-* Add ActiveRecord `#second_to_last` and `#third_to_last` methods.
-
- *Brian Christian*
-
-* Added `numeric` helper into migrations.
-
- Example:
-
- create_table(:numeric_types) do |t|
- t.numeric :numeric_type, precision: 10, scale: 2
- end
-
- *Mehmet Emin İNAÇ*
-
-* Bumped the minimum supported version of PostgreSQL to >= 9.1.
- Both PG 9.0 and 8.4 are past their end of life date:
- http://www.postgresql.org/support/versioning/
-
- *Remo Mueller*
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* `ActiveRecord::Relation#reverse_order` throws `ActiveRecord::IrreversibleOrderError`
- when the order can not be reversed using current trivial algorithm.
- Also raises the same error when `#reverse_order` is called on
- relation without any order and table has no primary key:
-
- Topic.order("concat(author_name, title)").reverse_order
- # Before: SELECT `topics`.* FROM `topics` ORDER BY concat(author_name DESC, title) DESC
- # After: raises ActiveRecord::IrreversibleOrderError
- Edge.all.reverse_order
- # Before: SELECT `edges`.* FROM `edges` ORDER BY `edges`.`` DESC
- # After: raises ActiveRecord::IrreversibleOrderError
-
- *Bogdan Gusiev*
-
-* Improve schema_migrations insertion performance by inserting all versions
- in one INSERT SQL.
-
- *Akira Matsuda*, *Naoto Koshikawa*
-
-* Using `references` or `belongs_to` in migrations will always add index
- for the referenced column by default, without adding `index: true` option
- to generated migration file. Users can opt out of this by passing
- `index: false`.
-
- Fixes #18146.
-
- *Matthew Draper*, *Prathamesh Sonpatki*
-
-* Run `type` attributes through attributes API type-casting before
- instantiating the corresponding subclass. This makes it possible to define
- custom STI mappings.
-
- Fixes #21986.
-
- *Yves Senn*
-
-* Don't try to quote functions or expressions passed to `:default` option if
- they are passed as procs.
-
- This will generate proper query with the passed function or expression for
- the default option, instead of trying to quote it in incorrect fashion.
-
- Example:
-
- create_table :posts do |t|
- t.datetime :published_at, default: -> { 'NOW()' }
- end
-
- *Ryuta Kamizono*
-
-* Fix regression when loading fixture files with symbol keys.
-
- Fixes #22584.
-
- *Yves Senn*
-
-* Use `version` column as primary key for schema_migrations table because
- `schema_migrations` versions are guaranteed to be unique.
-
- This makes it possible to use `update_attributes` on models that do
- not have a primary key.
-
- *Richard Schneeman*
-
-* Add short-hand methods for text and blob types in MySQL.
-
- In Pg and Sqlite3, `:text` and `:binary` have variable unlimited length.
- But in MySQL, these have limited length for each types (ref #21591, #21619).
- This change adds short-hand methods for each text and blob types.
-
- Example:
-
- create_table :foos do |t|
- t.tinyblob :tiny_blob
- t.mediumblob :medium_blob
- t.longblob :long_blob
- t.tinytext :tiny_text
- t.mediumtext :medium_text
- t.longtext :long_text
- end
-
- *Ryuta Kamizono*
-
-* Take into account UTC offset when assigning string representation of
- timestamp with offset specified to attribute of time type.
-
- *Andrey Novikov*
-
-* When calling `first` with a `limit` argument, return directly from the
- `loaded?` records if available.
-
- *Ben Woosley*
-
-* Deprecate sending the `offset` argument to `find_nth`. Please use the
- `offset` method on relation instead.
-
- *Ben Woosley*
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* Limit record touching to once per transaction.
-
- If you have a parent/grand-parent relation like:
-
- Comment belongs_to :message, touch: true
- Message belongs_to :project, touch: true
- Project belongs_to :account, touch: true
-
- When the lowest entry(`Comment`) is saved, now, it won't repeat the touch
- call multiple times for the parent records.
-
- Related #18606.
-
- *arthurnn*
-
-* Order the result of `find(ids)` to match the passed array, if the relation
- has no explicit order defined.
-
- Fixes #20338.
-
- *Miguel Grazziotin*, *Matthew Draper*
-
-* Omit default limit values in dumped schema. It's tidier, and if the defaults
- change in the future, we can address that via Migration API Versioning.
-
- *Jean Boussier*
-
-* Support passing the schema name as a prefix to table name in
- `ConnectionAdapters::SchemaStatements#indexes`. Previously the prefix would
- be considered a full part of the index name, and only the schema in the
- current search path would be considered.
-
- *Grey Baker*
-
-* Ignore index name in `index_exists?` and `remove_index` when not passed a
- name to check for.
-
- *Grey Baker*
-
-* Extract support for the legacy `mysql` database adapter from core. It will
- live on in a separate gem for now, but most users should just use `mysql2`.
-
- *Abdelkader Boudih*
-
-* ApplicationRecord is a new superclass for all app models, analogous to app
- controllers subclassing ApplicationController instead of
- ActionController::Base. This gives apps a single spot to configure app-wide
- model behavior.
-
- Newly generated applications have `app/models/application_record.rb`
- present by default.
-
- *Genadi Samokovarov*
-
-* Version the API presented to migration classes, so we can change parameter
- defaults without breaking existing migrations, or forcing them to be
- rewritten through a deprecation cycle.
-
- New migrations specify the Rails version they were written for:
-
- class AddStatusToOrders < ActiveRecord::Migration[5.0]
- def change
- # ...
- end
- end
-
- *Matthew Draper*, *Ravil Bayramgalin*
-
-* Use bind params for `limit` and `offset`. This will generate significantly
- fewer prepared statements for common tasks like pagination. To support this
- change, passing a string containing a comma to `limit` has been deprecated,
- and passing an Arel node to `limit` is no longer supported.
-
- Fixes #22250.
-
- *Sean Griffin*
-
-* Introduce after_{create,update,delete}_commit callbacks.
-
- Before:
-
- after_commit :add_to_index_later, on: :create
- after_commit :update_in_index_later, on: :update
- after_commit :remove_from_index_later, on: :destroy
-
- After:
-
- after_create_commit :add_to_index_later
- after_update_commit :update_in_index_later
- after_destroy_commit :remove_from_index_later
-
- Fixes #22515.
-
- *Genadi Samokovarov*
-
-* Respect the column default values for `inheritance_column` when
- instantiating records through the base class.
-
- Fixes #17121.
-
- Example:
-
- # The schema of BaseModel has `t.string :type, default: 'SubType'`
- subtype = BaseModel.new
- assert_equals SubType, subtype.class
-
- *Kuldeep Aggarwal*
-
-* Fix `rake db:structure:dump` on Postgres when multiple schemas are used.
-
- Fixes #22346.
-
- *Nick Muerdter*, *ckoenig*
-
-* Add schema dumping support for PostgreSQL geometric data types.
-
- *Ryuta Kamizono*
-
-* Except keys of `build_record`'s argument from `create_scope` in `initialize_attributes`.
-
- Fixes #21893.
-
- *Yuichiro Kaneko*
-
-* Deprecate `connection.tables` on the SQLite3 and MySQL adapters.
- Also deprecate passing arguments to `#tables`.
- And deprecate `table_exists?`.
-
- The `#tables` method of some adapters (mysql, mysql2, sqlite3) would return
- both tables and views while others (postgresql) just return tables. To make
- their behavior consistent, `#tables` will return only tables in the future.
-
- The `#table_exists?` method would check both tables and views. To make
- their behavior consistent with `#tables`, `#table_exists?` will check only
- tables in the future.
-
- *Yuichiro Kaneko*
-
-* Improve support for non Active Record objects on `validates_associated`
-
- Skipping `marked_for_destruction?` when the associated object does not responds
- to it make easier to validate virtual associations built on top of Active Model
- objects and/or serialized objects that implement a `valid?` instance method.
-
- *Kassio Borges*, *Lucas Mazza*
-
-* Change connection management middleware to return a new response with
- a body proxy, rather than mutating the original.
-
- *Kevin Buchanan*
-
-* Make `db:migrate:status` to render `1_some.rb` format migrate files.
-
- These files are in `db/migrate`:
-
- * 1_valid_people_have_last_names.rb
- * 20150819202140_irreversible_migration.rb
- * 20150823202140_add_admin_flag_to_users.rb
- * 20150823202141_migration_tests.rb
- * 2_we_need_reminders.rb
- * 3_innocent_jointable.rb
-
- Before:
-
- $ bundle exec rake db:migrate:status
- ...
-
- Status Migration ID Migration Name
- --------------------------------------------------
- up 001 ********** NO FILE **********
- up 002 ********** NO FILE **********
- up 003 ********** NO FILE **********
- up 20150819202140 Irreversible migration
- up 20150823202140 Add admin flag to users
- up 20150823202141 Migration tests
-
- After:
-
- $ bundle exec rake db:migrate:status
- ...
-
- Status Migration ID Migration Name
- --------------------------------------------------
- up 001 Valid people have last names
- up 002 We need reminders
- up 003 Innocent jointable
- up 20150819202140 Irreversible migration
- up 20150823202140 Add admin flag to users
- up 20150823202141 Migration tests
-
- *Yuichiro Kaneko*
-
-* Define `ActiveRecord::Sanitization.sanitize_sql_for_order` and use it inside
- `preprocess_order_args`.
-
- *Yuichiro Kaneko*
-
-* Allow bigint with default nil for avoiding auto increment primary key.
-
- *Ryuta Kamizono*
-
-* Remove `DEFAULT_CHARSET` and `DEFAULT_COLLATION` in `MySQLDatabaseTasks`.
-
- We should omit the collation entirely rather than providing a default.
- Then the choice is the responsibility of the server and MySQL distribution.
-
- *Ryuta Kamizono*
-
-* Alias `ActiveRecord::Relation#left_joins` to
- `ActiveRecord::Relation#left_outer_joins`.
-
- *Takashi Kokubun*
-
-* Use advisory locking to raise a `ConcurrentMigrationError` instead of
- attempting to migrate when another migration is currently running.
-
- *Sam Davies*
-
-* Added `ActiveRecord::Relation#left_outer_joins`.
-
- Example:
-
- User.left_outer_joins(:posts)
- # => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON
- "posts"."user_id" = "users"."id"
-
- *Florian Thomas*
-
-* Support passing an array to `order` for SQL parameter sanitization.
-
- *Aaron Suggs*
-
-* Avoid disabling errors on the PostgreSQL connection when enabling the
- `standard_conforming_strings` setting. Errors were previously disabled because
- the setting wasn't writable in Postgres 8.1 and didn't exist in earlier
- versions. Now Rails only supports Postgres 8.2+ we're fine to assume the
- setting exists. Disabling errors caused problems when using a connection
- pooling tool like PgBouncer because it's not guaranteed to have the same
- connection between calls to `execute` and it could leave the connection
- with errors disabled.
-
- Fixes #22101.
-
- *Harry Marr*
-
-* Set `scope.reordering_value` to `true` if `:reordering`-values are specified.
-
- Fixes #21886.
-
- *Hiroaki Izu*
-
-* Add support for bidirectional destroy dependencies.
-
- Fixes #13609.
-
- Example:
-
- class Content < ActiveRecord::Base
- has_one :position, dependent: :destroy
- end
-
- class Position < ActiveRecord::Base
- belongs_to :content, dependent: :destroy
- end
-
- *Seb Jacobs*
-
-* Includes HABTM returns correct size now. It's caused by the join dependency
- only instantiates one HABTM object because the join table hasn't a primary key.
-
- Fixes #16032.
-
- Examples:
-
- before:
-
- Project.first.salaried_developers.size # => 3
- Project.includes(:salaried_developers).first.salaried_developers.size # => 1
-
- after:
-
- Project.first.salaried_developers.size # => 3
- Project.includes(:salaried_developers).first.salaried_developers.size # => 3
-
- *Bigxiang*
-
-* Add option to index errors in nested attributes
-
- For models which have nested attributes, errors within those models will
- now be indexed if `:index_errors` is specified when defining a
- has_many relationship, or if its set in the global config.
-
- Example:
-
- class Guitar < ActiveRecord::Base
- has_many :tuning_pegs, index_errors: true
- accepts_nested_attributes_for :tuning_pegs
- end
-
- class TuningPeg < ActiveRecord::Base
- belongs_to :guitar
- validates_numericality_of :pitch
- end
-
- # Old style
- guitar.errors["tuning_pegs.pitch"] = ["is not a number"]
-
- # New style (if defined globally, or set in has_many_relationship)
- guitar.errors["tuning_pegs[1].pitch"] = ["is not a number"]
-
- *Michael Probber*, *Terence Sun*
-
-* Exit with non-zero status for failed database rake tasks.
-
- *Jay Hayes*
-
-* Queries such as `Computer.joins(:monitor).group(:status).count` will now be
- interpreted as `Computer.joins(:monitor).group('computers.status').count`
- so that when `Computer` and `Monitor` have both `status` columns we don't
- have conflicts in projection.
-
- *Rafael Sales*
-
-* Add ability to default to `uuid` as primary key when generating database migrations.
-
- Example:
-
- config.generators do |g|
- g.orm :active_record, primary_key_type: :uuid
- end
-
- *Jon McCartie*
-
-* Don't cache arguments in `#find_by` if they are an `ActiveRecord::Relation`.
-
- Fixes #20817.
-
- *Hiroaki Izu*
-
-* Qualify column name inserted by `group` in calculation.
-
- Giving `group` an unqualified column name now works, even if the relation
- has `JOIN` with another table which also has a column of the name.
-
- *Soutaro Matsumoto*
-
-* Don't cache prepared statements containing an IN clause or a SQL literal, as
- these queries will change often and are unlikely to have a cache hit.
-
- *Sean Griffin*
-
-* Fix `rewhere` in a `has_many` association.
-
- Fixes #21955.
-
- *Josh Branchaud*, *Kal*
-
-* `where` raises ArgumentError on unsupported types.
-
- Fixes #20473.
-
- *Jake Worth*
-
-* Add an immutable string type to help reduce memory usage for apps which do
- not need mutation detection on strings.
-
- *Sean Griffin*
-
-* Give `ActiveRecord::Relation#update` its own deprecation warning when
- passed an `ActiveRecord::Base` instance.
-
- Fixes #21945.
-
- *Ted Johansson*
-
-* Make it possible to pass `:to_table` when adding a foreign key through
- `add_reference`.
-
- Fixes #21563.
-
- *Yves Senn*
-
-* No longer pass deprecated option `-i` to `pg_dump`.
-
- *Paul Sadauskas*
-
-* Concurrent `AR::Base#increment!` and `#decrement!` on the same record
- are all reflected in the database rather than overwriting each other.
-
- *Bogdan Gusiev*
-
-* Avoid leaking the first relation we call `first` on, per model.
-
- Fixes #21921.
-
- *Matthew Draper*, *Jean Boussier*
-
-* Remove unused `pk_and_sequence_for` in `AbstractMysqlAdapter`.
-
- *Ryuta Kamizono*
-
-* Allow fixtures files to set the model class in the YAML file itself.
-
- To load the fixtures file `accounts.yml` as the `User` model, use:
-
- _fixture:
- model_class: User
- david:
- name: David
-
- Fixes #9516.
-
- *Roque Pinel*
-
-* Don't require a database connection to load a class which uses acceptance
- validations.
-
- *Sean Griffin*
-
-* Correctly apply `unscope` when preloading through associations.
-
- *Jimmy Bourassa*
-
-* Fixed taking precision into count when assigning a value to timestamp attribute.
-
- Timestamp column can have less precision than ruby timestamp
- In result in how big a fraction of a second can be stored in the
- database.
-
-
- m = Model.create!
- m.created_at.usec == m.reload.created_at.usec # => false
- # due to different precision in Time.now and database column
-
- If the precision is low enough, (mysql default is 0, so it is always low
- enough by default) the value changes when model is reloaded from the
- database. This patch fixes that issue ensuring that any timestamp
- assigned as an attribute is converted to column precision under the
- attribute.
-
- *Bogdan Gusiev*
-
-* Introduce `connection.data_sources` and `connection.data_source_exists?`.
- These methods determine what relations can be used to back Active Record
- models (usually tables and views).
-
- Also deprecate `SchemaCache#tables`, `SchemaCache#table_exists?` and
- `SchemaCache#clear_table_cache!` in favor of their new data source
- counterparts.
-
- *Yves Senn*, *Matthew Draper*
-
-* Add `ActiveRecord::Base.ignored_columns` to make some columns
- invisible from Active Record.
-
- *Jean Boussier*
-
-* `ActiveRecord::Tasks::MySQLDatabaseTasks` fails if shellout to
- mysql commands (like `mysqldump`) is not successful.
-
- *Steve Mitchell*
-
-* Ensure `select` quotes aliased attributes, even when using `from`.
-
- Fixes #21488.
-
- *Sean Griffin*, *@johanlunds*
-
-* MySQL: support `unsigned` numeric data types.
-
- Example:
-
- create_table :foos do |t|
- t.unsigned_integer :quantity
- t.unsigned_bigint :total
- t.unsigned_float :percentage
- t.unsigned_decimal :price, precision: 10, scale: 2
- end
-
- The `unsigned: true` option may be used for the primary key:
-
- create_table :foos, id: :bigint, unsigned: true do |t|
- …
- end
-
- *Ryuta Kamizono*
-
-* Add `#views` and `#view_exists?` methods on connection adapters.
-
- *Ryuta Kamizono*
-
-* Correctly dump composite primary key.
-
- Example:
-
- create_table :barcodes, primary_key: ["region", "code"] do |t|
- t.string :region
- t.integer :code
- end
-
- *Ryuta Kamizono*
-
-* Lookup the attribute name for `restrict_with_error` messages on the
- model class that defines the association.
-
- *kuboon*, *Ronak Jangir*
-
-* Correct query for PostgreSQL 8.2 compatibility.
-
- *Ben Murphy*, *Matthew Draper*
-
-* `bin/rails db:migrate` uses
- `ActiveRecord::Tasks::DatabaseTasks.migrations_paths` instead of
- `Migrator.migrations_paths`.
-
- *Tobias Bielohlawek*
-
-* Support dropping indexes concurrently in PostgreSQL.
-
- See http://www.postgresql.org/docs/9.4/static/sql-dropindex.html for more
- details.
-
- *Grey Baker*
-
-* Deprecate passing conditions to `ActiveRecord::Relation#delete_all`
- and `ActiveRecord::Relation#destroy_all`.
-
- *Wojciech Wnętrzak*
-
-* Instantiating an AR model with `ActionController::Parameters` now raises
- an `ActiveModel::ForbiddenAttributesError` if the parameters include a
- `type` field that has not been explicitly permitted. Previously, the
- `type` field was simply ignored in the same situation.
-
- *Prem Sichanugrist*
-
-* PostgreSQL, `create_schema`, `drop_schema` and `rename_table` now quote
- schema names.
-
- Fixes #21418.
-
- Example:
-
- create_schema("my.schema")
- # CREATE SCHEMA "my.schema";
-
- *Yves Senn*
-
-* PostgreSQL, add `:if_exists` option to `#drop_schema`. This makes it
- possible to drop a schema that might exist without raising an exception if
- it doesn't.
-
- *Yves Senn*
-
-* Only try to nullify has_one target association if the record is persisted.
-
- Fixes #21223.
-
- *Agis Anastasopoulos*
-
-* Uniqueness validator raises descriptive error when running on a persisted
- record without primary key.
-
- Fixes #21304.
-
- *Yves Senn*
-
-* Add a native JSON data type support in MySQL.
-
- Example:
-
- create_table :json_data_type do |t|
- t.json :settings
- end
-
- *Ryuta Kamizono*
-
-* Descriptive error message when fixtures contain a missing column.
-
- Fixes #21201.
-
- *Yves Senn*
-
-* `ActiveRecord::Tasks::PostgreSQLDatabaseTasks` fail if shellout to
- postgresql commands (like `pg_dump`) is not successful.
-
- *Bryan Paxton*, *Nate Berkopec*
-
-* Add `ActiveRecord::Relation#in_batches` to work with records and relations
- in batches.
-
- Available options are `of` (batch size), `load`, `start`, and `finish`.
-
- Examples:
-
- Person.in_batches.each_record(&:party_all_night!)
- Person.in_batches.update_all(awesome: true)
- Person.in_batches.delete_all
- Person.in_batches.each do |relation|
- relation.delete_all
- sleep 10 # Throttles the delete queries
- end
-
- Fixes #20933.
-
- *Sina Siadat*
-
-* Added methods for PostgreSQL geometric data types to use in migrations.
-
- Example:
-
- create_table :foo do |t|
- t.line :foo_line
- t.lseg :foo_lseg
- t.box :foo_box
- t.path :foo_path
- t.polygon :foo_polygon
- t.circle :foo_circle
- end
-
- *Mehmet Emin İNAÇ*
-
-* Add `cache_key` to ActiveRecord::Relation.
-
- Example:
-
- @users = User.where("name like ?", "%Alberto%")
- @users.cache_key
- # => "/users/query-5942b155a43b139f2471b872ac54251f-3-20150714212107656125000"
-
- *Alberto Fernández-Capel*
-
-* Properly allow uniqueness validations on primary keys.
-
- Fixes #20966.
-
- *Sean Griffin*, *presskey*
-
-* Don't raise an error if an association failed to destroy when `destroy` was
- called on the parent (as opposed to `destroy!`).
-
- Fixes #20991.
-
- *Sean Griffin*
-
-* `ActiveRecord::RecordNotFound` modified to store model name, primary_key and
- id of the caller model. It allows the catcher of this exception to make
- a better decision to what to do with it.
-
- Example:
-
- class SomeAbstractController < ActionController::Base
- rescue_from ActiveRecord::RecordNotFound, with: :redirect_to_404
-
- private def redirect_to_404(e)
- return redirect_to(posts_url) if e.model == 'Post'
- raise
- end
- end
-
- *Sameer Rahmani*
-
-* Deprecate the keys for association `restrict_dependent_destroy` errors in favor
- of new key names.
-
- Previously `has_one` and `has_many` associations were using the
- `one` and `many` keys respectively. Both of these keys have special
- meaning in I18n (they are considered to be pluralizations) so by
- renaming them to `has_one` and `has_many` we make the messages more explicit
- and most importantly they don't clash with linguistical systems that need to
- validate translation keys (and their pluralizations).
-
- The `:'restrict_dependent_destroy.one'` key should be replaced with
- `:'restrict_dependent_destroy.has_one'`, and `:'restrict_dependent_destroy.many'`
- with `:'restrict_dependent_destroy.has_many'`.
-
- *Roque Pinel*, *Christopher Dell*
-
-* Fix state being carried over from previous transaction.
-
- Considering the following example where `name` is a required attribute.
- Before we had `new_record?` returning `true` for a persisted record:
-
- author = Author.create! name: 'foo'
- author.name = nil
- author.save # => false
- author.new_record? # => true
-
- Fixes #20824.
-
- *Roque Pinel*
-
-* Correctly ignore `mark_for_destruction` when `autosave` isn't set to `true`
- when validating associations.
-
- Fixes #20882.
-
- *Sean Griffin*
-
-* Fix a bug where counter_cache doesn't always work with polymorphic
- relations.
-
- Fixes #16407.
-
- *Stefan Kanev*, *Sean Griffin*
-
-* Ensure that cyclic associations with autosave don't cause duplicate errors
- to be added to the parent record.
-
- Fixes #20874.
-
- *Sean Griffin*
-
-* Ensure that `ActionController::Parameters` can still be passed to nested
- attributes.
-
- Fixes #20922.
-
- *Sean Griffin*
-
-* Deprecate force association reload by passing a truthy argument to
- association method.
-
- For collection association, you can call `#reload` on association proxy to
- force a reload:
-
- @user.posts.reload # Instead of @user.posts(true)
-
- For singular association, you can call `#reload` on the parent object to
- clear its association cache then call the association method:
-
- @user.reload.profile # Instead of @user.profile(true)
-
- Passing a truthy argument to force association to reload will be removed in
- Rails 5.1.
-
- *Prem Sichanugrist*
-
-* Replaced `ActiveSupport::Concurrency::Latch` with `Concurrent::CountDownLatch`
- from the concurrent-ruby gem.
-
- *Jerry D'Antonio*
-
-* Fix through associations using scopes having the scope merged multiple
- times.
-
- Fixes #20721.
- Fixes #20727.
-
- *Sean Griffin*
-
-* `ActiveRecord::Base.dump_schema_after_migration` applies migration tasks
- other than `db:migrate`. (eg. `db:rollback`, `db:migrate:dup`, ...)
-
- Fixes #20743.
-
- *Yves Senn*
-
-* Add alternate syntax to make `change_column_default` reversible.
-
- User can pass in `:from` and `:to` to make `change_column_default` command
- become reversible.
-
- Example:
-
- change_column_default :posts, :status, from: nil, to: "draft"
- change_column_default :users, :authorized, from: true, to: false
-
- *Prem Sichanugrist*
-
-* Prevent error when using `force_reload: true` on an unassigned polymorphic
- belongs_to association.
-
- Fixes #20426.
-
- *James Dabbs*
-
-* Correctly raise `ActiveRecord::AssociationTypeMismatch` when assigning
- a wrong type to a namespaced association.
-
- Fixes #20545.
-
- *Diego Carrion*
-
-* `validates_absence_of` respects `marked_for_destruction?`.
-
- Fixes #20449.
-
- *Yves Senn*
-
-* Include the `Enumerable` module in `ActiveRecord::Relation`
-
- *Sean Griffin*, *bogdan*
-
-* Use `Enumerable#sum` in `ActiveRecord::Relation` if a block is given.
-
- *Sean Griffin*
-
-* Let `WITH` queries (Common Table Expressions) be explainable.
-
- *Vladimir Kochnev*
-
-* Make `remove_index :table, :column` reversible.
-
- *Yves Senn*
-
-* Fixed an error which would occur in dirty checking when calling
- `update_attributes` from a getter.
-
- Fixes #20531.
-
- *Sean Griffin*
-
-* Make `remove_foreign_key` reversible. Any foreign key options must be
- specified, similar to `remove_column`.
-
- *Aster Ryan*
-
-* Add `:_prefix` and `:_suffix` options to `enum` definition.
-
- Fixes #17511, #17415.
-
- *Igor Kapkov*
-
-* Correctly handle decimal arrays with defaults in the schema dumper.
-
- Fixes #20515.
-
- *Sean Griffin*, *jmondo*
-
-* Deprecate the PostgreSQL `:point` type in favor of a new one which will return
- `Point` objects instead of an `Array`
-
- *Sean Griffin*
-
-* Ensure symbols passed to `ActiveRecord::Relation#select` are always treated
- as columns.
-
- Fixes #20360.
-
- *Sean Griffin*
-
-* Do not set `sql_mode` if `strict: :default` is specified.
-
- # config/database.yml
- production:
- adapter: mysql2
- database: foo_prod
- user: foo
- strict: :default
-
- *Ryuta Kamizono*
-
-* Allow proc defaults to be passed to the attributes API. See documentation
- for examples.
-
- *Sean Griffin*, *Kir Shatrov*
-
-* SQLite: `:collation` support for string and text columns.
-
- Example:
-
- create_table :foo do |t|
- t.string :string_nocase, collation: 'NOCASE'
- t.text :text_rtrim, collation: 'RTRIM'
- end
-
- add_column :foo, :title, :string, collation: 'RTRIM'
-
- change_column :foo, :title, :string, collation: 'NOCASE'
-
- *Akshay Vishnoi*
-
-* Allow the use of symbols or strings to specify enum values in test
- fixtures:
-
- awdr:
- title: "Agile Web Development with Rails"
- status: :proposed
-
- *George Claghorn*
-
-* Clear query cache when `ActiveRecord::Base#reload` is called.
-
- *Shane Hender, Pierre Nespo*
-
-* Include stored procedures and function on the MySQL structure dump.
-
- *Jonathan Worek*
-
-* Pass `:extend` option for `has_and_belongs_to_many` associations to the
- underlying `has_many :through`.
-
- *Jaehyun Shin*
-
-* Deprecate `Relation#uniq` use `Relation#distinct` instead.
-
- See #9683.
-
- *Yves Senn*
-
-* Allow single table inheritance instantiation to work when storing
- demodulized class names.
-
- *Alex Robbin*
-
-* Correctly pass MySQL options when using `structure_dump` or
- `structure_load`.
-
- Specifically, it fixes an issue when using SSL authentication.
-
- *Alex Coomans*
-
-* Correctly dump `:options` on `create_table` for MySQL.
-
- *Ryuta Kamizono*
-
-* PostgreSQL: `:collation` support for string and text columns.
-
- Example:
-
- create_table :foos do |t|
- t.string :string_en, collation: 'en_US.UTF-8'
- t.text :text_ja, collation: 'ja_JP.UTF-8'
- end
-
- *Ryuta Kamizono*
-
-* Remove `ActiveRecord::Serialization::XmlSerializer` from core.
-
- *Zachary Scott*
-
-* Make `unscope` aware of "less than" and "greater than" conditions.
-
- *TAKAHASHI Kazuaki*
-
-* `find_by` and `find_by!` raise `ArgumentError` when called without
- arguments.
-
- *Kohei Suzuki*
-
-* Revert behavior of `db:schema:load` back to loading the full
- environment. This ensures that initializers are run.
-
- Fixes #19545.
-
- *Yves Senn*
-
-* Fix missing index when using `timestamps` with the `index` option.
-
- The `index` option used with `timestamps` should be passed to both
- `column` definitions for `created_at` and `updated_at` rather than just
- the first.
-
- *Paul Mucur*
-
-* Rename `:class` to `:anonymous_class` in association options.
-
- Fixes #19659.
-
- *Andrew White*
-
-* Autosave existing records on a has many through association when the parent
- is new.
-
- Fixes #19782.
-
- *Sean Griffin*
-
-* Fixed a bug where uniqueness validations would error on out of range values,
- even if an validation should have prevented it from hitting the database.
-
- *Andrey Voronkov*
-
-* MySQL: `:charset` and `:collation` support for string and text columns.
-
- Example:
-
- create_table :foos do |t|
- t.string :string_utf8_bin, charset: 'utf8', collation: 'utf8_bin'
- t.text :text_ascii, charset: 'ascii'
- end
-
- *Ryuta Kamizono*
-
-* Foreign key related methods in the migration DSL respect
- `ActiveRecord::Base.pluralize_table_names = false`.
-
- Fixes #19643.
-
- *Mehmet Emin İNAÇ*
-
-* Reduce memory usage from loading types on PostgreSQL.
-
- Fixes #19578.
-
- *Sean Griffin*
-
-* Add `config.active_record.warn_on_records_fetched_greater_than` option.
-
- When set to an integer, a warning will be logged whenever a result set
- larger than the specified size is returned by a query.
-
- Fixes #16463.
-
- *Jason Nochlin*
-
-* Ignore `.psqlrc` when loading database structure.
-
- *Jason Weathered*
-
-* Fix referencing wrong table aliases while joining tables of has many through
- association (only when calling calculation methods).
-
- Fixes #19276.
-
- *pinglamb*
-
-* Correctly persist a serialized attribute that has been returned to
- its default value by an in-place modification.
-
- Fixes #19467.
-
- *Matthew Draper*
-
-* Fix generating the schema file when using PostgreSQL `BigInt[]` data type.
- Previously the `limit: 8` was not coming through, and this caused it to
- become `Int[]` data type after rebuilding from the schema.
-
- Fixes #19420.
-
- *Jake Waller*
-
-* Reuse the `CollectionAssociation#reader` cache when the foreign key is
- available prior to save.
-
- *Ben Woosley*
-
-* Add `config.active_record.dump_schemas` to fix `db:structure:dump`
- when using schema_search_path and PostgreSQL extensions.
-
- Fixes #17157.
-
- *Ryan Wallace*
-
-* Renaming `use_transactional_fixtures` to `use_transactional_tests` for clarity.
-
- Fixes #18864.
-
- *Brandon Weiss*
-
-* Increase pg gem version requirement to `~> 0.18`. Earlier versions of the
- pg gem are known to have problems with Ruby 2.2.
-
- *Matt Brictson*
-
-* Correctly dump `serial` and `bigserial`.
-
- *Ryuta Kamizono*
-
-* Fix default `format` value in `ActiveRecord::Tasks::DatabaseTasks#schema_file`.
-
- *James Cox*
-
-* Don't enroll records in the transaction if they don't have commit callbacks.
- This was causing a memory leak when creating many records inside a transaction.
-
- Fixes #15549.
-
- *Will Bryant*, *Aaron Patterson*
-
-* Correctly create through records when created on a has many through
- association when using `where`.
-
- Fixes #19073.
-
- *Sean Griffin*
-
-* Add `SchemaMigration.create_table` support for any unicode charsets with MySQL.
-
- *Ryuta Kamizono*
-
-* PostgreSQL no longer disables user triggers if system triggers can't be
- disabled. Disabling user triggers does not fulfill what the method promises.
- Rails currently requires superuser privileges for this method.
-
- If you absolutely rely on this behavior, consider patching
- `disable_referential_integrity`.
-
- *Yves Senn*
-
-* Restore aborted transaction state when `disable_referential_integrity` fails
- due to missing permissions.
-
- *Toby Ovod-Everett*, *Yves Senn*
-
-* In PostgreSQL, print a warning message if `disable_referential_integrity`
- fails due to missing permissions.
-
- *Andrey Nering*, *Yves Senn*
-
-* Allow a `:limit` option for MySQL bigint primary key support.
-
- Example:
-
- create_table :foos, id: :primary_key, limit: 8 do |t|
- end
-
- # or
-
- create_table :foos, id: false do |t|
- t.primary_key :id, limit: 8
- end
-
- *Ryuta Kamizono*
-
-* `belongs_to` will now trigger a validation error by default if the association is not present.
- You can turn this off on a per-association basis with `optional: true`.
- (Note this new default only applies to new Rails apps that will be generated with
- `config.active_record.belongs_to_required_by_default = true` in initializer.)
-
- *Josef Šimánek*
-
-* Fixed `ActiveRecord::Relation#becomes!` and `changed_attributes` issues for type
- columns.
-
- Fixes #17139.
-
- *Miklos Fazekas*
-
-* Format the time string according to the precision of the time column.
-
- *Ryuta Kamizono*
-
-* Allow a `:precision` option for time type columns.
-
- *Ryuta Kamizono*
-
-* Add `ActiveRecord::Base.suppress` to prevent the receiver from being saved
- during the given block.
-
- For example, here's a pattern of creating notifications when new comments
- are posted. (The notification may in turn trigger an email, a push
- notification, or just appear in the UI somewhere):
-
- class Comment < ActiveRecord::Base
- belongs_to :commentable, polymorphic: true
- after_create -> { Notification.create! comment: self,
- recipients: commentable.recipients }
- end
-
- That's what you want the bulk of the time. A new comment creates a new
- Notification. There may be edge cases where you don't want that, like
- when copying a commentable and its comments, in which case write a
- concern with something like this:
-
- module Copyable
- def copy_to(destination)
- Notification.suppress do
- # Copy logic that creates new comments that we do not want triggering
- # notifications.
- end
- end
- end
-
- *Michael Ryan*
-
-* `:time` option added for `#touch`.
-
- Fixes #18905.
-
- *Hyonjee Joo*
-
-* Add `foreign_key_exists?` method.
-
- *Tõnis Simo*
-
-* Use SQL COUNT and LIMIT 1 queries for `none?` and `one?` methods
- if no block or limit is given, instead of loading the entire
- collection into memory. This applies to relations (e.g. `User.all`)
- as well as associations (e.g. `account.users`)
-
- # Before:
-
- users.none?
- # SELECT "users".* FROM "users"
-
- users.one?
- # SELECT "users".* FROM "users"
-
- # After:
-
- users.none?
- # SELECT 1 AS one FROM "users" LIMIT 1
-
- users.one?
- # SELECT COUNT(*) FROM "users"
-
- *Eugene Gilburg*
-
-* Have `enum` perform type casting consistently with the rest of Active
- Record, such as `where`.
-
- *Sean Griffin*
-
-* `scoping` no longer pollutes the current scope of sibling classes when using
- STI.
-
- Fixes #18806.
-
- Example:
-
- StiOne.none.scoping do
- StiTwo.all
- end
-
-
- *Sean Griffin*
-
-* `remove_reference` with `foreign_key: true` removes the foreign key before
- removing the column. This fixes a bug where it was not possible to remove
- the column on MySQL.
-
- Fixes #18664.
-
- *Yves Senn*
-
-* `find_in_batches` now accepts an `:finish` parameter that complements the `:start`
- parameter to specify where to stop batch processing.
-
- *Vipul A M*
-
-* Fix a rounding problem for PostgreSQL timestamp columns.
-
- If a timestamp column has a precision specified, it needs to
- format according to that.
-
- *Ryuta Kamizono*
-
-* Respect the database default charset for `schema_migrations` table.
-
- The charset of `version` column in `schema_migrations` table depends
- on the database default charset and collation rather than the encoding
- of the connection.
-
- *Ryuta Kamizono*
-
-* Raise `ArgumentError` when passing `nil` or `false` to `Relation#merge`.
-
- These are not valid values to merge in a relation, so it should warn users
- early.
-
- *Rafael Mendonça França*
-
-* Use `SCHEMA` instead of `DB_STRUCTURE` for specifying a structure file.
-
- This makes the `db:structure` tasks consistent with `test:load_structure`.
-
- *Dieter Komendera*
-
-* Respect custom primary keys for associations when calling `Relation#where`
-
- Fixes #18813.
-
- *Sean Griffin*
-
-* Fix several edge cases which could result in a counter cache updating
- twice or not updating at all for `has_many` and `has_many :through`.
-
- Fixes #10865.
-
- *Sean Griffin*
-
-* Foreign keys added by migrations were given random, generated names. This
- meant a different `structure.sql` would be generated every time a developer
- ran migrations on their machine.
-
- The generated part of foreign key names is now a hash of the table name and
- column name, which is consistent every time you run the migration.
-
- *Chris Sinjakli*
-
-* Fix n+1 query problem when eager loading nil associations (fixes #18312)
-
- *Sammy Larbi*
-
-* Change the default error message from `can't be blank` to `must exist` for
- the presence validator of the `:required` option on `belongs_to`/`has_one`
- associations.
-
- *Henrik Nygren*
-
-* Fixed `ActiveRecord::Relation#group` method when an argument is an SQL
- reserved keyword:
-
- Example:
-
- SplitTest.group(:key).count
- Property.group(:value).count
-
- *Bogdan Gusiev*
-
-* Added the `#or` method on `ActiveRecord::Relation`, allowing use of the OR
- operator to combine WHERE or HAVING clauses.
-
- Example:
-
- Post.where('id = 1').or(Post.where('id = 2'))
- # => SELECT * FROM posts WHERE (id = 1) OR (id = 2)
-
- *Sean Griffin*, *Matthew Draper*, *Gael Muller*, *Olivier El Mekki*
-
-* Don't define autosave association callbacks twice from
- `accepts_nested_attributes_for`.
-
- Fixes #18704.
-
- *Sean Griffin*
-
-* Integer types will no longer raise a `RangeError` when assigning an
- attribute, but will instead raise when going to the database.
-
- Fixes several vague issues which were never reported directly. See the
- commit message from the commit which added this line for some examples.
-
- *Sean Griffin*
-
-* Values which would error while being sent to the database (such as an
- ASCII-8BIT string with invalid UTF-8 bytes on SQLite3), no longer error on
- assignment. They will still error when sent to the database, but you are
- given the ability to re-assign it to a valid value.
-
- Fixes #18580.
-
- *Sean Griffin*
-
-* Don't remove join dependencies in `Relation#exists?`
-
- Fixes #18632.
-
- *Sean Griffin*
-
-* Invalid values assigned to a JSON column are assumed to be `nil`.
-
- Fixes #18629.
-
- *Sean Griffin*
-
-* Add `ActiveRecord::Base#accessed_fields`, which can be used to quickly
- discover which fields were read from a model when you are looking to only
- select the data you need from the database.
-
- *Sean Griffin*
-
-* Introduce the `:if_exists` option for `drop_table`.
-
- Example:
-
- drop_table(:posts, if_exists: true)
-
- That would execute:
-
- DROP TABLE IF EXISTS posts
-
- If the table doesn't exist, `if_exists: false` (the default) raises an
- exception whereas `if_exists: true` does nothing.
-
- *Cody Cutrer*, *Stefan Kanev*, *Ryuta Kamizono*
-
-* Don't run SQL if attribute value is not changed for update_attribute method.
-
- *Prathamesh Sonpatki*
-
-* `time` columns can now get affected by `time_zone_aware_attributes`. If you have
- set `config.time_zone` to a value other than `'UTC'`, they will be treated
- as in that time zone by default in Rails 5.1. If this is not the desired
- behavior, you can set
-
- ActiveRecord::Base.time_zone_aware_types = [:datetime]
-
- A deprecation warning will be emitted if you have a `:time` column, and have
- not explicitly opted out.
-
- Fixes #3145.
-
- *Sean Griffin*
-
-* Tests now run after_commit callbacks. You no longer have to declare
- `uses_transaction ‘test name’` to test the results of an after_commit.
-
- after_commit callbacks run after committing a transaction whose parent
- is not `joinable?`: un-nested transactions, transactions within test cases,
- and transactions in `console --sandbox`.
-
- *arthurnn*, *Ravil Bayramgalin*, *Matthew Draper*
-
-* `nil` as a value for a binary column in a query no longer logs as
- "<NULL binary data>", and instead logs as just "nil".
-
- *Sean Griffin*
-
-* `attribute_will_change!` will no longer cause non-persistable attributes to
- be sent to the database.
-
- Fixes #18407.
-
- *Sean Griffin*
-
-* Remove support for the `protected_attributes` gem.
-
- *Carlos Antonio da Silva*, *Roberto Miranda*
-
-* Fix accessing of fixtures having non-string labels like Fixnum.
-
- *Prathamesh Sonpatki*
-
-* Remove deprecated support to preload instance-dependent associations.
-
- *Yves Senn*
-
-* Remove deprecated support for PostgreSQL ranges with exclusive lower bounds.
-
- *Yves Senn*
-
-* Remove deprecation when modifying a relation with cached Arel.
- This raises an `ImmutableRelation` error instead.
-
- *Yves Senn*
-
-* Added `ActiveRecord::SecureToken` in order to encapsulate generation of
- unique tokens for attributes in a model using `SecureRandom`.
-
- *Roberto Miranda*
-
-* Change the behavior of boolean columns to be closer to Ruby's semantics.
-
- Before this change we had a small set of "truthy", and all others are "falsy".
-
- Now, we have a small set of "falsy" values and all others are "truthy" matching
- Ruby's semantics.
-
- *Rafael Mendonça França*
-
-* Deprecate `ActiveRecord::Base.errors_in_transactional_callbacks=`.
-
- *Rafael Mendonça França*
-
-* Change transaction callbacks to not swallow errors.
-
- Before this change any errors raised inside a transaction callback
- were getting rescued and printed in the logs.
-
- Now these errors are not rescued anymore and just bubble up, as the other callbacks.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `sanitize_sql_hash_for_conditions`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `Reflection#source_macro`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `symbolized_base_class` and `symbolized_sti_name`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActiveRecord::Base.disable_implicit_join_references=`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated access to connection specification using a string accessor.
-
- Now all strings will be handled as a URL.
-
- *Rafael Mendonça França*
-
-* Change the default `null` value for `timestamps` to `false`.
-
- *Rafael Mendonça França*
-
-* Return an array of pools from `connection_pools`.
-
- *Rafael Mendonça França*
-
-* Return a null column from `column_for_attribute` when no column exists.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `serialized_attributes`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated automatic counter caches on `has_many :through`.
-
- *Rafael Mendonça França*
-
-* Change the way in which callback chains can be halted.
-
- The preferred method to halt a callback chain from now on is to explicitly
- `throw(:abort)`.
- In the past, returning `false` in an Active Record `before_` callback had the
- side effect of halting the callback chain.
- This is not recommended anymore and, depending on the value of the
- `ActiveSupport.halt_callback_chains_on_return_false` option, will
- either not work at all or display a deprecation warning.
-
- *claudiob*
-
-* Clear query cache on rollback.
-
- *Florian Weingarten*
-
-* Fix setting of foreign_key for through associations when building a new record.
-
- Fixes #12698.
-
- *Ivan Antropov*
-
-* Improve dumping of the primary key. If it is not a default primary key,
- correctly dump the type and options.
-
- Fixes #14169, #16599.
-
- *Ryuta Kamizono*
-
-* Format the datetime string according to the precision of the datetime field.
-
- Incompatible to rounding behavior between MySQL 5.6 and earlier.
-
- In 5.5, when you insert `2014-08-17 12:30:00.999999` the fractional part
- is ignored. In 5.6, it's rounded to `2014-08-17 12:30:01`:
-
- http://bugs.mysql.com/bug.php?id=68760
-
- *Ryuta Kamizono*
-
-* Allow a precision option for MySQL datetimes.
-
- *Ryuta Kamizono*
-
-* Fixed automatic `inverse_of` for models nested in a module.
-
- *Andrew McCloud*
-
-* Change `ActiveRecord::Relation#update` behavior so that it can
- be called without passing ids of the records to be updated.
-
- This change allows updating multiple records returned by
- `ActiveRecord::Relation` with callbacks and validations.
-
- # Before
- # ArgumentError: wrong number of arguments (1 for 2)
- Comment.where(group: 'expert').update(body: "Group of Rails Experts")
-
- # After
- # Comments with group expert updated with body "Group of Rails Experts"
- Comment.where(group: 'expert').update(body: "Group of Rails Experts")
-
- *Prathamesh Sonpatki*
-
-* Fix `reaping_frequency` option when the value is a string.
-
- This usually happens when it is configured using `DATABASE_URL`.
-
- *korbin*
-
-* Fix error message when trying to create an associated record and the foreign
- key is missing.
-
- Before this fix the following exception was being raised:
-
- NoMethodError: undefined method `val' for #<Arel::Nodes::BindParam:0x007fc64d19c218>
-
- Now the message is:
-
- ActiveRecord::UnknownAttributeError: unknown attribute 'foreign_key' for Model.
-
- *Rafael Mendonça França*
-
-* Fix change detection problem for PostgreSQL bytea type and
- `ArgumentError: string contains null byte` exception with pg-0.18.
-
- Fixes #17680.
-
- *Lars Kanis*
-
-* When a table has a composite primary key, the `primary_key` method for
- SQLite3 and PostgreSQL adapters was only returning the first field of the key.
- Ensures that it will return nil instead, as Active Record doesn't support
- composite primary keys.
-
- Fixes #18070.
-
- *arthurnn*
-
-* `validates_size_of` / `validates_length_of` do not count records
- which are `marked_for_destruction?`.
-
- Fixes #7247.
-
- *Yves Senn*
-
-* Ensure `first!` and friends work on loaded associations.
-
- Fixes #18237.
-
- *Sean Griffin*
-
-* `eager_load` preserves readonly flag for associations.
-
- Fixes #15853.
-
- *Takashi Kokubun*
-
-* Provide `:touch` option to `save()` to accommodate saving without updating
- timestamps.
-
- Fixes #18202.
-
- *Dan Olson*
-
-* Provide a more helpful error message when an unsupported class is passed to
- `serialize`.
-
- Fixes #18224.
-
- *Sean Griffin*
-
-* Add bigint primary key support for MySQL.
-
- Example:
-
- create_table :foos, id: :bigint do |t|
- end
-
- *Ryuta Kamizono*
-
-* Support for any type of primary key.
-
- Fixes #14194.
-
- *Ryuta Kamizono*
-
-* Dump the default `nil` for PostgreSQL UUID primary key.
-
- *Ryuta Kamizono*
-
-* Add a `:foreign_key` option to `references` and associated migration
- methods. The model and migration generators now use this option, rather than
- the `add_foreign_key` form.
-
- *Sean Griffin*
-
-* Don't raise when writing an attribute with an out-of-range datetime passed
- by the user.
-
- *Grey Baker*
-
-* Replace deprecated `ActiveRecord::Tasks::DatabaseTasks#load_schema` with
- `ActiveRecord::Tasks::DatabaseTasks#load_schema_for`.
-
- *Yves Senn*
-
-* Fix bug with `ActiveRecord::Type::Numeric` that caused negative values to
- be marked as having changed when set to the same negative value.
-
- Fixes #18161.
-
- *Daniel Fox*
-
-* Introduce `force: :cascade` option for `create_table`. Using this option
- will recreate tables even if they have dependent objects (like foreign keys).
- `db/schema.rb` now uses `force: :cascade`. This makes it possible to
- reload the schema when foreign keys are in place.
-
- *Matthew Draper*, *Yves Senn*
-
-* `db:schema:load` and `db:structure:load` no longer purge the database
- before loading the schema. This is left for the user to do.
- `db:test:prepare` will still purge the database.
-
- Fixes #17945.
-
- *Yves Senn*
-
-* Fix undesirable RangeError by `Type::Integer`. Add `Type::UnsignedInteger`.
-
- *Ryuta Kamizono*
-
-* Add `foreign_type` option to `has_one` and `has_many` association macros.
-
- This option enables to define the column name of associated object's type for polymorphic associations.
-
- *Ulisses Almeida*, *Kassio Borges*
-
-* Remove deprecated behavior allowing nested arrays to be passed as query
- values.
-
- *Melanie Gilman*
-
-* Deprecate passing a class as a value in a query. Users should pass strings
- instead.
-
- *Melanie Gilman*
-
-* `add_timestamps` and `remove_timestamps` now properly reversible with
- options.
-
- *Noam Gagliardi-Rabinovich*
-
-* `ActiveRecord::ConnectionAdapters::ColumnDumper#column_spec` and
- `ActiveRecord::ConnectionAdapters::ColumnDumper#prepare_column_options` no
- longer have a `types` argument. They should access
- `connection#native_database_types` directly.
-
- *Yves Senn*
-
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activerecord/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/activerecord/CHANGELOG.md) for previous changes.
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 46df733cfe..012db35765 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -21,7 +21,6 @@ desc 'Run mysql2, sqlite, and postgresql tests by default'
task :default => :test
task :package
-task "package:clean"
desc 'Run mysql2, sqlite, and postgresql tests'
task :test do
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 5a973fa801..ec9b073c18 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -300,10 +300,10 @@ module ActiveRecord
#
# === A word of warning
#
- # Don't create associations that have the same name as instance methods of
- # ActiveRecord::Base. Since the association adds a method with that name to
- # its model, it will override the inherited method and break things.
- # For instance, +attributes+ and +connection+ would be bad choices for association names.
+ # Don't create associations that have the same name as [instance methods](http://api.rubyonrails.org/classes/ActiveRecord/Core.html) of
+ # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
+ # its model, using an association with the same name as one provided by <tt>ActiveRecord::Base</tt> will override the method inherited through <tt>ActiveRecord::Base</tt> and will break things.
+ # For instance, +attributes+ and +connection+ would be bad choices for association names, because those names already exist in the list of <tt>ActiveRecord::Base</tt> instance methods.
#
# == Auto-generated methods
# See also Instance Public methods below for more details.
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index f7edfbfb5f..62e867a353 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -217,7 +217,8 @@ module ActiveRecord
unless record.is_a?(reflection.klass)
fresh_class = reflection.class_name.safe_constantize
unless fresh_class && record.is_a?(fresh_class)
- message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
+ message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, "\
+ "got #{record.inspect} which is an instance of #{record.class}(##{record.class.object_id})"
raise ActiveRecord::AssociationTypeMismatch, message
end
end
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 48437a1c9e..15844de0bc 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -124,8 +124,7 @@ module ActiveRecord
scope = last_chain_scope(scope, table, owner_reflection, owner, association_klass)
reflection = chain_head
- loop do
- break unless reflection
+ while reflection
table = reflection.alias_name
unless reflection == chain_tail
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 41698c5360..24997370b2 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -61,6 +61,7 @@ module ActiveRecord
def update_counters_on_replace(record)
if require_counter_update? && different_target?(record)
+ owner.instance_variable_set :@_after_replace_counter_called, true
record.increment!(reflection.counter_cache_column)
decrement_counters
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 346329c610..3121e70a04 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -33,6 +33,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
if (@_after_create_counter_called ||= false)
@_after_create_counter_called = false
+ elsif (@_after_replace_counter_called ||= false)
+ @_after_replace_counter_called = false
elsif attribute_changed?(foreign_key) && !new_record?
if reflection.polymorphic?
model = attribute(reflection.foreign_type).try(:constantize)
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 2dca6b612e..0eaa0a4f36 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -10,9 +10,9 @@ module ActiveRecord
# HasManyAssociation => has_many
# HasManyThroughAssociation + ThroughAssociation => has_many :through
#
- # CollectionAssociation class provides common methods to the collections
+ # The CollectionAssociation class provides common methods to the collections
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
- # +:through association+ option.
+ # the +:through association+ option.
#
# You need to be careful with assumptions regarding the target: The proxy
# does not fetch records from the database until it needs them, but new
@@ -246,9 +246,12 @@ module ActiveRecord
end
end
- # Count all records using SQL. Construct options and pass them with
- # scope to the target class's +count+.
+ # Returns the number of records. If no arguments are given, it counts all
+ # columns using SQL. If one argument is given, it counts only the passed
+ # column using SQL. If a block is given, it counts the number of records
+ # yielding a true value.
def count(column_name = nil)
+ return super if block_given?
relation = scope
if association_scope.distinct_value
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
@@ -280,7 +283,7 @@ module ActiveRecord
_options = records.extract_options!
dependent = _options[:dependent] || options[:dependent]
- records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
+ records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
delete_or_destroy(records, dependent)
end
@@ -291,7 +294,7 @@ module ActiveRecord
# +:dependent+ option.
def destroy(*records)
return if records.empty?
- records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
+ records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
delete_or_destroy(records, :destroy)
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index b9aed05135..5d1e7ffb73 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -597,7 +597,7 @@ module ActiveRecord
# Pet.find(1)
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
#
- # You can pass +Fixnum+ or +String+ values, it finds the records
+ # You can pass +Integer+ or +String+ values, it finds the records
# responding to the +id+ and executes delete on them.
#
# class Person < ActiveRecord::Base
@@ -661,7 +661,7 @@ module ActiveRecord
#
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
#
- # You can pass +Fixnum+ or +String+ values, it finds the records
+ # You can pass +Integer+ or +String+ values, it finds the records
# responding to the +id+ and then deletes them from the database.
#
# person.pets.size # => 3
@@ -715,12 +715,13 @@ module ActiveRecord
end
alias uniq distinct
- # Count all records using SQL.
+ # Count all records.
#
# class Person < ActiveRecord::Base
# has_many :pets
# end
#
+ # # This will perform the count using SQL.
# person.pets.count # => 3
# person.pets
# # => [
@@ -728,8 +729,13 @@ module ActiveRecord
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
- def count(column_name = nil)
- @association.count(column_name)
+ #
+ # Passing a block will select all of a person's pets in SQL and then
+ # perform the count using Ruby.
+ #
+ # person.pets.count { |pet| pet.name.include?('-') } # => 2
+ def count(column_name = nil, &block)
+ @association.count(column_name, &block)
end
# Returns the size of the collection. If the collection hasn't been loaded,
diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb
index 3c4c8f10ec..24231dc9e1 100644
--- a/activerecord/lib/active_record/attribute.rb
+++ b/activerecord/lib/active_record/attribute.rb
@@ -170,7 +170,7 @@ module ActiveRecord
super(name, nil, Type::Value.new)
end
- def value
+ def type_cast(*)
nil
end
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index 4c22be8235..b96d8e9352 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -38,7 +38,7 @@ module ActiveRecord
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
- # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum and
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
# f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
def assign_multiparameter_attributes(pairs)
execute_callstack_for_multiparameter_attributes(
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index e902eb7531..1fb5eb28cd 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -334,8 +334,6 @@ module ActiveRecord
#
# Note: +:id+ is always present.
#
- # Alias for the #read_attribute method.
- #
# class Person < ActiveRecord::Base
# belongs_to :organization
# end
@@ -360,7 +358,7 @@ module ActiveRecord
# person = Person.new
# person[:age] = '22'
# person[:age] # => 22
- # person[:age] # => Fixnum
+ # person[:age].class # => Integer
def []=(attr_name, value)
write_attribute(attr_name, value)
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 5599b590ca..70c2d2f25d 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -26,7 +26,7 @@ module ActiveRecord
end
# Updates the attribute identified by <tt>attr_name</tt> with the
- # specified +value+. Empty strings for fixnum and float columns are
+ # specified +value+. Empty strings for Integer and Float columns are
# turned into +nil+.
def write_attribute(attr_name, value)
write_attribute_with_type_cast(attr_name, value, true)
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index e0ceafc617..519de271c3 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -67,12 +67,14 @@ module ActiveRecord
#
# A default can also be provided.
#
+ # # db/schema.rb
# create_table :store_listings, force: true do |t|
# t.string :my_string, default: "original default"
# end
#
# StoreListing.new.my_string # => "original default"
#
+ # # app/models/store_listing.rb
# class StoreListing < ActiveRecord::Base
# attribute :my_string, :string, default: "new default"
# end
@@ -89,6 +91,7 @@ module ActiveRecord
#
# \Attributes do not need to be backed by a database column.
#
+ # # app/models/my_model.rb
# class MyModel < ActiveRecord::Base
# attribute :my_string, :string
# attribute :my_int_array, :integer, array: true
@@ -131,7 +134,7 @@ module ActiveRecord
# # config/initializers/types.rb
# ActiveRecord::Type.register(:money, MoneyType)
#
- # # /app/models/store_listing.rb
+ # # app/models/store_listing.rb
# class StoreListing < ActiveRecord::Base
# attribute :price_in_cents, :money
# end
@@ -167,8 +170,10 @@ module ActiveRecord
# end
# end
#
+ # # config/initializers/types.rb
# ActiveRecord::Type.register(:money, MoneyType)
#
+ # # app/models/product.rb
# class Product < ActiveRecord::Base
# currency_converter = ConversionRatesFromTheInternet.new
# attribute :price_in_bitcoins, :money, currency_converter: currency_converter
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 1f1b11eb68..95de6937af 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -53,9 +53,9 @@ module ActiveRecord
# end
#
# class Firm < ActiveRecord::Base
- # # Destroys the associated clients and people when the firm is destroyed
- # before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
- # before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
+ # # Disables access to the system, for associated clients and people when the firm is destroyed
+ # before_destroy { |record| Person.where(firm_id: record.id).update_all(access: 'disabled') }
+ # before_destroy { |record| Client.where(client_of: record.id).update_all(access: 'disabled') }
# end
#
# == Inheritable callback queues
diff --git a/activerecord/lib/active_record/coders/json.rb b/activerecord/lib/active_record/coders/json.rb
index 75d3bfe625..cb185a881e 100644
--- a/activerecord/lib/active_record/coders/json.rb
+++ b/activerecord/lib/active_record/coders/json.rb
@@ -6,7 +6,7 @@ module ActiveRecord
end
def self.load(json)
- ActiveSupport::JSON.decode(json) unless json.nil?
+ ActiveSupport::JSON.decode(json) unless json.blank?
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index e389d818fd..c341773be1 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -298,7 +298,7 @@ module ActiveRecord
def run
return unless frequency
Thread.new(frequency, pool) { |t, p|
- while true
+ loop do
sleep t
p.reap
end
@@ -618,7 +618,7 @@ module ActiveRecord
timeout_time = Time.now + (@checkout_timeout * 2)
@available.with_a_bias_for(Thread.current) do
- while true
+ loop do
synchronize do
return if collected_conns.size == @connections.size && @now_connecting == 0
remaining_timeout = timeout_time - Time.now
@@ -778,8 +778,7 @@ module ActiveRecord
end
# ConnectionHandler is a collection of ConnectionPool objects. It is used
- # for keeping separate connection pools for Active Record models that connect
- # to different databases.
+ # for keeping separate connection pools that connect to different databases.
#
# For example, suppose that you have 5 models, with the following hierarchy:
#
@@ -821,17 +820,16 @@ module ActiveRecord
# ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
# All Active Record models use this handler to determine the connection pool that they
# should use.
+ #
+ # The ConnectionHandler class is not coupled with the Active models, as it has no knowlodge
+ # about the model. The model, needs to pass a specification name to the handler,
+ # in order to lookup the correct connection pool.
class ConnectionHandler
def initialize
- # These caches are keyed by klass.name, NOT klass. Keying them by klass
- # alone would lead to memory leaks in development mode as all previous
- # instances of the class would stay in memory.
+ # These caches are keyed by spec.name (ConnectionSpecification#name).
@owner_to_pool = Concurrent::Map.new(:initial_capacity => 2) do |h,k|
h[k] = Concurrent::Map.new(:initial_capacity => 2)
end
- @class_to_pool = Concurrent::Map.new(:initial_capacity => 2) do |h,k|
- h[k] = Concurrent::Map.new
- end
end
def connection_pool_list
@@ -839,10 +837,12 @@ module ActiveRecord
end
alias :connection_pools :connection_pool_list
- def establish_connection(owner, spec)
- @class_to_pool.clear
- raise RuntimeError, "Anonymous class is not allowed." unless owner.name
- owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
+ def establish_connection(config)
+ resolver = ConnectionSpecification::Resolver.new(Base.configurations)
+ spec = resolver.spec(config)
+
+ remove_connection(spec.name)
+ owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec)
end
# Returns true if there are any active connections among the connection
@@ -873,18 +873,18 @@ module ActiveRecord
# active or defined connection: if it is the latter, it will be
# opened and set as the active connection for the class it was defined
# for (not necessarily the current class).
- def retrieve_connection(klass) #:nodoc:
- pool = retrieve_connection_pool(klass)
- raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool
+ def retrieve_connection(spec_name) #:nodoc:
+ pool = retrieve_connection_pool(spec_name)
+ raise ConnectionNotEstablished, "No connection pool with id '#{spec_name}' found." unless pool
conn = pool.connection
- raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
+ raise ConnectionNotEstablished, "No connection for '#{spec_name}' in connection pool" unless conn
conn
end
# Returns true if a connection that's accessible to this class has
# already been opened.
- def connected?(klass)
- conn = retrieve_connection_pool(klass)
+ def connected?(spec_name)
+ conn = retrieve_connection_pool(spec_name)
conn && conn.connected?
end
@@ -892,32 +892,31 @@ module ActiveRecord
# connection and the defined connection (if they exist). The result
# can be used as an argument for establish_connection, for easily
# re-establishing the connection.
- def remove_connection(owner)
- if pool = owner_to_pool.delete(owner.name)
- @class_to_pool.clear
+ def remove_connection(spec_name)
+ if pool = owner_to_pool.delete(spec_name)
pool.automatic_reconnect = false
pool.disconnect!
pool.spec.config
end
end
- # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
+ # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool.
# This makes retrieving the connection pool O(1) once the process is warm.
# When a connection is established or removed, we invalidate the cache.
- #
- # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
- # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
- # #fetch is significantly slower than #[]. So in the nil case, no caching will
- # take place, but that's ok since the nil case is not the common one that we wish
- # to optimise for.
- def retrieve_connection_pool(klass)
- class_to_pool[klass.name] ||= begin
- until pool = pool_for(klass)
- klass = klass.superclass
- break unless klass <= Base
+ def retrieve_connection_pool(spec_name)
+ owner_to_pool.fetch(spec_name) do
+ # Check if a connection was previously established in an ancestor process,
+ # which may have been forked.
+ if ancestor_pool = pool_from_any_process_for(spec_name)
+ # A connection was established in an ancestor process that must have
+ # subsequently forked. We can't reuse the connection, but we can copy
+ # the specification and establish a new connection with it.
+ establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
+ pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
+ end
+ else
+ owner_to_pool[spec_name] = nil
end
-
- class_to_pool[klass.name] = pool
end
end
@@ -927,28 +926,9 @@ module ActiveRecord
@owner_to_pool[Process.pid]
end
- def class_to_pool
- @class_to_pool[Process.pid]
- end
-
- def pool_for(owner)
- owner_to_pool.fetch(owner.name) {
- if ancestor_pool = pool_from_any_process_for(owner)
- # A connection was established in an ancestor process that must have
- # subsequently forked. We can't reuse the connection, but we can copy
- # the specification and establish a new connection with it.
- establish_connection(owner, ancestor_pool.spec).tap do |pool|
- pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
- end
- else
- owner_to_pool[owner.name] = nil
- end
- }
- end
-
- def pool_from_any_process_for(owner)
- owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
- owner_to_pool && owner_to_pool[owner.name]
+ def pool_from_any_process_for(spec_name)
+ owner_to_pool = @owner_to_pool.values.find { |v| v[spec_name] }
+ owner_to_pool && owner_to_pool[spec_name]
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 51ed2bf8f2..d4b9e301bc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -454,6 +454,24 @@ module ActiveRecord
visitor.accept(node, collector).value
end
+ def combine_bind_parameters(
+ from_clause: [],
+ join_clause: [],
+ where_clause: [],
+ having_clause: [],
+ limit: nil,
+ offset: nil
+ ) # :nodoc:
+ result = from_clause + join_clause + where_clause + having_clause
+ if limit
+ result << limit
+ end
+ if offset
+ result << offset
+ end
+ result
+ end
+
protected
def initialize_type_map(m) # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index d5fe880841..44b4b547f3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -20,7 +20,7 @@ module ActiveRecord
MySQL::Table.new(table_name, base)
end
- def schema_creation
+ def schema_creation # :nodoc:
MySQL::SchemaCreation.new(self)
end
@@ -146,11 +146,11 @@ module ActiveRecord
end
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
- select_value("SELECT GET_LOCK('#{lock_name}', #{timeout});").to_s == '1'
+ select_value("SELECT GET_LOCK(#{quote(lock_name)}, #{timeout})") == 1
end
def release_advisory_lock(lock_name) # :nodoc:
- select_value("SELECT RELEASE_LOCK('#{lock_name}')").to_s == '1'
+ select_value("SELECT RELEASE_LOCK(#{quote(lock_name)})") == 1
end
def native_database_types
@@ -502,8 +502,12 @@ module ActiveRecord
def add_index(table_name, column_name, options = {}) #:nodoc:
index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
+ execute add_sql_comment!(sql, comment)
+ end
+
+ def add_sql_comment!(sql, comment) # :nodoc:
sql << " COMMENT #{quote(comment)}" if comment
- execute sql
+ sql
end
def foreign_keys(table_name)
@@ -703,7 +707,7 @@ module ActiveRecord
case length
when Hash
column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
- when Fixnum
+ when Integer
column_names.each {|name| option_strings[name] += "(#{length})"}
end
end
@@ -723,14 +727,22 @@ module ActiveRecord
column_names.map {|name| quote_column_name(name) + option_strings[name]}
end
+ # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
+ ER_DUP_ENTRY = 1062
+ ER_NO_REFERENCED_ROW_2 = 1452
+ ER_DATA_TOO_LONG = 1406
+ ER_LOCK_DEADLOCK = 1213
+
def translate_exception(exception, message)
case error_number(exception)
- when 1062
+ when ER_DUP_ENTRY
RecordNotUnique.new(message)
- when 1452
+ when ER_NO_REFERENCED_ROW_2
InvalidForeignKey.new(message)
- when 1406
+ when ER_DATA_TOO_LONG
ValueTooLong.new(message)
+ when ER_LOCK_DEADLOCK
+ TransactionSerializationError.new(message)
else
super
end
@@ -828,7 +840,7 @@ module ActiveRecord
# Increase timeout so the server doesn't disconnect us.
wait_timeout = @config[:wait_timeout]
- wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
+ wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
defaults = [':default', :default].to_set
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 4bc6447368..346916337e 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -3,16 +3,20 @@ require 'uri'
module ActiveRecord
module ConnectionAdapters
class ConnectionSpecification #:nodoc:
- attr_reader :config, :adapter_method
+ attr_reader :name, :config, :adapter_method
- def initialize(config, adapter_method)
- @config, @adapter_method = config, adapter_method
+ def initialize(name, config, adapter_method)
+ @name, @config, @adapter_method = name, config, adapter_method
end
def initialize_dup(original)
@config = original.config.dup
end
+ def to_hash
+ @config.merge(name: @name)
+ end
+
# Expands a connection string into a hash.
class ConnectionUrlResolver # :nodoc:
@@ -179,7 +183,12 @@ module ActiveRecord
end
adapter_method = "#{spec[:adapter]}_connection"
- ConnectionSpecification.new(spec, adapter_method)
+
+ unless ActiveRecord::Base.respond_to?(adapter_method)
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
+ end
+
+ ConnectionSpecification.new(spec.delete(:name) || "primary", spec, adapter_method)
end
private
@@ -224,7 +233,7 @@ module ActiveRecord
#
def resolve_symbol_connection(spec)
if config = configurations[spec.to_s]
- resolve_connection(config)
+ resolve_connection(config).merge("name" => spec.to_s)
else
raise(AdapterNotSpecified, "'#{spec}' database is not configured. Available: #{configurations.keys.inspect}")
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
index 0384079da2..fd2dc2aee8 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
@@ -2,8 +2,8 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
class SchemaCreation < AbstractAdapter::SchemaCreation
- delegate :quote, to: :@conn
- private :quote
+ delegate :add_sql_comment!, to: :@conn
+ private :add_sql_comment!
private
@@ -26,11 +26,7 @@ module ActiveRecord
end
def add_table_options!(create_sql, options)
- super
-
- if comment = options[:comment]
- create_sql << " COMMENT #{quote(comment)}"
- end
+ add_sql_comment!(super, options[:comment])
end
def column_options(o)
@@ -48,13 +44,7 @@ module ActiveRecord
sql << " COLLATE #{collation}"
end
- super
-
- if comment = options[:comment]
- sql << " COMMENT #{quote(comment)}"
- end
-
- sql
+ add_sql_comment!(super, options[:comment])
end
def add_column_position!(sql, options)
@@ -69,8 +59,7 @@ module ActiveRecord
def index_in_create(table_name, column_name, options)
index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
- index_option = " COMMENT #{quote(comment)}" if comment
- "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_option} "
+ add_sql_comment!("#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})", comment)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb
index 7427a25ad5..4da240edb2 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb
@@ -14,6 +14,8 @@ module ActiveRecord
def cast(value)
case value
when ::String
+ return if value.blank?
+
if value[0] == '(' && value[-1] == ')'
value = value[1...-1]
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 074ebb90a5..6318b1c65a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -217,7 +217,7 @@ module ActiveRecord
]
end
- IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment)
+ IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment.presence)
end.compact
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 39a2cbbda7..ddfc560747 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -168,10 +168,6 @@ module ActiveRecord
true
end
- def supports_comments_in_create?
- false
- end
-
def supports_savepoints?
true
end
@@ -410,6 +406,7 @@ module ActiveRecord
VALUE_LIMIT_VIOLATION = "22001"
FOREIGN_KEY_VIOLATION = "23503"
UNIQUE_VIOLATION = "23505"
+ SERIALIZATION_FAILURE = "40001"
def translate_exception(exception, message)
return exception unless exception.respond_to?(:result)
@@ -421,6 +418,8 @@ module ActiveRecord
InvalidForeignKey.new(message)
when VALUE_LIMIT_VIOLATION
ValueTooLong.new(message)
+ when SERIALIZATION_FAILURE
+ TransactionSerializationError.new(message)
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index e3793e6c77..eb2268157b 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -541,7 +541,7 @@ module ActiveRecord
result = exec_query(sql, 'SCHEMA').first
if result
- # Splitting with left parantheses and picking up last will return all
+ # Splitting with left parentheses and picking up last will return all
# columns separated with comma(,).
columns_string = result["sql"].split('(').last
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index a8b3d03ba5..086c678af5 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -44,17 +44,18 @@ module ActiveRecord
#
# The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
# may be returned on an error.
- def establish_connection(spec = nil)
- spec ||= DEFAULT_ENV.call.to_sym
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations
- spec = resolver.spec(spec)
+ def establish_connection(config = nil)
+ raise "Anonymous class is not allowed." unless name
- unless respond_to?(spec.adapter_method)
- raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
- end
+ config ||= DEFAULT_ENV.call.to_sym
+ spec_name = self == Base ? "primary" : name
+ self.connection_specification_name = spec_name
+
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
+ spec = resolver.resolve(config).symbolize_keys
+ spec[:name] = spec_name
- remove_connection
- connection_handler.establish_connection self, spec
+ connection_handler.establish_connection(spec)
end
class MergeAndResolveDefaultUrlConfig # :nodoc:
@@ -87,6 +88,16 @@ module ActiveRecord
retrieve_connection
end
+ attr_writer :connection_specification_name
+
+ # Return the specification name from the current class or its parent.
+ def connection_specification_name
+ if !defined?(@connection_specification_name) || @connection_specification_name.nil?
+ return self == Base ? "primary" : superclass.connection_specification_name
+ end
+ @connection_specification_name
+ end
+
def connection_id
ActiveRecord::RuntimeRegistry.connection_id ||= Thread.current.object_id
end
@@ -106,20 +117,28 @@ module ActiveRecord
end
def connection_pool
- connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished
+ connection_handler.retrieve_connection_pool(connection_specification_name) or raise ConnectionNotEstablished
end
def retrieve_connection
- connection_handler.retrieve_connection(self)
+ connection_handler.retrieve_connection(connection_specification_name)
end
# Returns +true+ if Active Record is connected.
def connected?
- connection_handler.connected?(self)
+ connection_handler.connected?(connection_specification_name)
end
- def remove_connection(klass = self)
- connection_handler.remove_connection(klass)
+ def remove_connection(name = nil)
+ name ||= @connection_specification_name if defined?(@connection_specification_name)
+ # if removing a connection that have a pool, we reset the
+ # connection_specification_name so it will use the parent
+ # pool.
+ if connection_handler.retrieve_connection_pool(name)
+ self.connection_specification_name = nil
+ end
+
+ connection_handler.remove_connection(name)
end
def clear_cache! # :nodoc:
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 5d74631e32..f936e865e4 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -257,7 +257,7 @@ module ActiveRecord
# Returns the Arel engine.
def arel_engine # :nodoc:
@arel_engine ||=
- if Base == self || connection_handler.retrieve_connection_pool(self)
+ if Base == self || connection_handler.retrieve_connection_pool(connection_specification_name)
self
else
superclass.arel_engine
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index b8b8684cff..38e4fbec8b 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -285,6 +285,16 @@ module ActiveRecord
class TransactionIsolationError < ActiveRecordError
end
+ # TransactionSerializationError will be raised when a transaction is rolled
+ # back by the database due to a serialization failure or a deadlock.
+ #
+ # See the following:
+ #
+ # * http://www.postgresql.org/docs/current/static/transaction-iso.html
+ # * https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_lock_deadlock
+ class TransactionSerializationError < ActiveRecordError
+ end
+
# IrreversibleOrderError is raised when a relation's order is too complex for
# +reverse_order+ to automatically reverse.
class IrreversibleOrderError < ActiveRecordError
diff --git a/activerecord/lib/active_record/gem_version.rb b/activerecord/lib/active_record/gem_version.rb
index 73be4cb271..f33456a744 100644
--- a/activerecord/lib/active_record/gem_version.rb
+++ b/activerecord/lib/active_record/gem_version.rb
@@ -6,9 +6,9 @@ module ActiveRecord
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activerecord/lib/active_record/internal_metadata.rb b/activerecord/lib/active_record/internal_metadata.rb
index 81db96bffd..17a5dc1d1b 100644
--- a/activerecord/lib/active_record/internal_metadata.rb
+++ b/activerecord/lib/active_record/internal_metadata.rb
@@ -19,7 +19,7 @@ module ActiveRecord
end
def []=(key, value)
- first_or_initialize(key: key).update_attributes!(value: value)
+ find_or_initialize_by(key: key).update_attributes!(value: value)
end
def [](key)
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 1040327a5d..67b8efac66 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -150,7 +150,7 @@ module ActiveRecord
# The version column used for optimistic locking. Defaults to +lock_version+.
def locking_column
- reset_locking_column unless defined?(@locking_column)
+ @locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
@locking_column
end
@@ -184,9 +184,12 @@ module ActiveRecord
end
end
+
+ # In de/serialize we change `nil` to 0, so that we can allow passing
+ # `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
+ # during update record.
class LockingType < DelegateClass(Type::Value) # :nodoc:
def deserialize(value)
- # `nil` *should* be changed to 0
super.to_i
end
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index efa2a4df02..8e32af1c49 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -22,7 +22,11 @@ module ActiveRecord
def render_bind(attribute)
value = if attribute.type.binary? && attribute.value
- "<#{attribute.value.bytesize} bytes of binary data>"
+ if attribute.value.is_a?(Hash)
+ "<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>"
+ else
+ "<#{attribute.value.bytesize} bytes of binary data>"
+ end
else
attribute.value_for_database
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 99a79024ad..81fe053fe1 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -166,13 +166,13 @@ module ActiveRecord
class EnvironmentMismatchError < ActiveRecordError
def initialize(current: nil, stored: nil)
msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
- msg << "You are running in `#{ current }` environment."
+ msg << "You are running in `#{ current }` environment. "
msg << "If you are sure you want to continue, first set the environment using:\n\n"
msg << "\tbin/rails db:environment:set"
if defined?(Rails.env)
- super("#{msg} RAILS_ENV=#{::Rails.env}")
+ super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
else
- super(msg)
+ super("#{msg}\n\n")
end
end
end
@@ -277,7 +277,7 @@ module ActiveRecord
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
# the column to a different type using the same parameters as add_column.
# * <tt>change_column_default(table_name, column_name, default)</tt>: Sets a
- # default value for +column_name+ definded by +default+ on +table_name+.
+ # default value for +column_name+ defined by +default+ on +table_name+.
# * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
# Sets or removes a +NOT NULL+ constraint on +column_name+. The +null+ flag
# indicates whether the value can be +NULL+. See
@@ -524,17 +524,11 @@ module ActiveRecord
end
def self.[](version)
- version = version.to_s
- name = "V#{version.tr('.', '_')}"
- unless Compatibility.const_defined?(name)
- versions = Compatibility.constants.grep(/\AV[0-9_]+\z/).map { |s| s.to_s.delete('V').tr('_', '.').inspect }
- raise ArgumentError, "Unknown migration version #{version.inspect}; expected one of #{versions.sort.join(', ')}"
- end
- Compatibility.const_get(name)
+ Compatibility.find(version)
end
def self.current_version
- Rails.version.to_f
+ ActiveRecord::VERSION::STRING.to_f
end
MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc:
diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb
index a20d7e0820..74833f938f 100644
--- a/activerecord/lib/active_record/migration/compatibility.rb
+++ b/activerecord/lib/active_record/migration/compatibility.rb
@@ -1,7 +1,17 @@
module ActiveRecord
class Migration
module Compatibility # :nodoc: all
- V5_0 = Current
+ def self.find(version)
+ version = version.to_s
+ name = "V#{version.tr('.', '_')}"
+ unless const_defined?(name)
+ versions = constants.grep(/\AV[0-9_]+\z/).map { |s| s.to_s.delete('V').tr('_', '.').inspect }
+ raise ArgumentError, "Unknown migration version #{version.inspect}; expected one of #{versions.sort.join(', ')}"
+ end
+ const_get(name)
+ end
+
+ V5_1 = Current
module FourTwoShared
module TableDefinition
@@ -92,6 +102,9 @@ module ActiveRecord
end
end
+ class V5_0 < V5_1
+ end
+
class V4_2 < V5_0
# 4.2 is defined as a module because it needs to be shared with
# Legacy. When the time comes, V5_0 should be defined straight
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 52eab952e1..f691a8319d 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -238,7 +238,7 @@ module ActiveRecord
end
# Returns the next value that will be used as the primary key on
- # an insert statment.
+ # an insert statement.
def next_sequence_value
connection.next_sequence_value(sequence_name)
end
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 4e32d73001..53ddd95bb0 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -1,6 +1,6 @@
module ActiveRecord
module Querying
- delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :empty?, :none?, :one?, to: :all
+ delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :none?, :one?, to: :all
delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!, to: :all
delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all
delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all
diff --git a/activerecord/lib/active_record/relation/batches/batch_enumerator.rb b/activerecord/lib/active_record/relation/batches/batch_enumerator.rb
index 13393dc605..333b3a63cf 100644
--- a/activerecord/lib/active_record/relation/batches/batch_enumerator.rb
+++ b/activerecord/lib/active_record/relation/batches/batch_enumerator.rb
@@ -42,7 +42,7 @@ module ActiveRecord
# Delegates #delete_all, #update_all, #destroy_all methods to each batch.
#
# People.in_batches.delete_all
- # People.in_batches.destroy_all('age < 10')
+ # People.where('age < 10').in_batches.destroy_all
# People.in_batches.update_all('age = age + 1')
[:delete_all, :update_all, :destroy_all].each do |method|
define_method(method) do |*args, &block|
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 54c9af4898..d6d92b8607 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -37,7 +37,11 @@ module ActiveRecord
# Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
# between databases. In invalid cases, an error from the database is thrown.
def count(column_name = nil)
- calculate(:count, column_name)
+ if block_given?
+ to_a.count { |*block_args| yield(*block_args) }
+ else
+ calculate(:count, column_name)
+ end
end
# Calculates the average value on a given column. Returns +nil+ if there's
@@ -89,7 +93,7 @@ module ActiveRecord
#
# There are two basic forms of output:
#
- # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
+ # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
# for AVG, and the given column's type for everything else.
#
# * Grouped values: This returns an ordered hash of the values and groups them. It
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index b397d1fd07..2484cb3264 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -35,7 +35,7 @@ module ActiveRecord
# may vary depending on the klass of a relation, so we create a subclass of Relation
# for each different klass, and the delegations are compiled into that subclass only.
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
+ delegate :to_xml, :encode_with, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
:[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
:shuffle, :split, to: :records
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 4533f3263f..2a831c2017 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -99,22 +99,28 @@ module ActiveRecord
end
def bound_attributes
- result = from_clause.binds + arel.bind_values + where_clause.binds + having_clause.binds
if limit_value && !string_containing_comma?(limit_value)
- result << Attribute.with_cast_value(
+ limit_bind = Attribute.with_cast_value(
"LIMIT".freeze,
connection.sanitize_limit(limit_value),
Type::Value.new,
)
end
if offset_value
- result << Attribute.with_cast_value(
+ offset_bind = Attribute.with_cast_value(
"OFFSET".freeze,
offset_value.to_i,
Type::Value.new,
)
end
- result
+ connection.combine_bind_parameters(
+ from_clause: from_clause.binds,
+ join_clause: arel.bind_values,
+ where_clause: where_clause.binds,
+ having_clause: having_clause.binds,
+ limit: limit_bind,
+ offset: offset_bind,
+ )
end
FROZEN_EMPTY_HASH = {}.freeze
@@ -1002,12 +1008,6 @@ module ActiveRecord
self.send(unscope_code, result)
end
- def association_for_table(table_name)
- table_name = table_name.to_s
- @klass._reflect_on_association(table_name) ||
- @klass._reflect_on_association(table_name.singularize)
- end
-
def build_from
opts = from_clause.value
name = from_clause.name
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 301718b874..d769376d1a 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -50,10 +50,6 @@ module ActiveRecord
def header(stream)
define_params = @version ? "version: #{@version}" : ""
- if stream.respond_to?(:external_encoding) && stream.external_encoding
- stream.puts "# encoding: #{stream.external_encoding.name}"
- end
-
stream.puts <<HEADER
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
diff --git a/activerecord/lib/active_record/suppressor.rb b/activerecord/lib/active_record/suppressor.rb
index 8ec4b48d31..d9acb1a1dc 100644
--- a/activerecord/lib/active_record/suppressor.rb
+++ b/activerecord/lib/active_record/suppressor.rb
@@ -30,10 +30,11 @@ module ActiveRecord
module ClassMethods
def suppress(&block)
+ previous_state = SuppressorRegistry.suppressed[name]
SuppressorRegistry.suppressed[name] = true
yield
ensure
- SuppressorRegistry.suppressed[name] = false
+ SuppressorRegistry.suppressed[name] = previous_state
end
end
diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb
index 0faad48ce3..a1326aa359 100644
--- a/activerecord/lib/active_record/table_metadata.rb
+++ b/activerecord/lib/active_record/table_metadata.rb
@@ -44,7 +44,8 @@ module ActiveRecord
def associated_table(table_name)
return self if table_name == arel_table.name
- association = klass._reflect_on_association(table_name)
+ association = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.singularize)
+
if association && !association.polymorphic?
association_klass = association.klass
arel_table = association_klass.arel_table.alias(table_name)
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 9aea5b360b..e3e665e149 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -117,10 +117,10 @@ module ActiveRecord
end
def create_all
- old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base)
+ old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
each_local_configuration { |configuration| create configuration }
if old_pool
- ActiveRecord::Base.connection_handler.establish_connection(ActiveRecord::Base, old_pool.spec)
+ ActiveRecord::Base.connection_handler.establish_connection(old_pool.spec.to_hash)
end
end
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index 4ff0740cfb..a3a5241780 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -32,7 +32,7 @@ module ActiveRecord
def changed_in_place?(raw_old_value, value)
return false if value.nil?
- raw_new_value = serialize(value)
+ raw_new_value = encoded(value)
raw_old_value.nil? != raw_new_value.nil? ||
subtype.changed_in_place?(raw_old_value, raw_new_value)
end
@@ -52,6 +52,12 @@ module ActiveRecord
def default_value?(value)
value == coder.load(nil)
end
+
+ def encoded(value)
+ unless default_value?(value)
+ coder.dump(value)
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 99f97c7914..95d1f6b8a3 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -63,14 +63,14 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
def (ActiveRecord::Base.connection).data_source_exists?(*); false; end
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
- expected = "CREATE TABLE `people` (#{type} INDEX `index_people_on_last_name` (`last_name`) ) ENGINE=InnoDB"
+ expected = "CREATE TABLE `people` (#{type} INDEX `index_people_on_last_name` (`last_name`)) ENGINE=InnoDB"
actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t|
t.index :last_name, type: type
end
assert_equal expected, actual
end
- expected = "CREATE TABLE `people` ( INDEX `index_people_on_last_name` USING btree (`last_name`(10)) ) ENGINE=InnoDB"
+ expected = "CREATE TABLE `people` ( INDEX `index_people_on_last_name` USING btree (`last_name`(10))) ENGINE=InnoDB"
actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t|
t.index :last_name, length: 10, using: :btree
end
@@ -155,7 +155,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
ActiveRecord::Base.connection.stubs(:data_source_exists?).with(:temp).returns(false)
ActiveRecord::Base.connection.stubs(:index_name_exists?).with(:index_temp_on_zip).returns(false)
- expected = "CREATE TEMPORARY TABLE `temp` ( INDEX `index_temp_on_zip` (`zip`) ) ENGINE=InnoDB AS SELECT id, name, zip FROM a_really_complicated_query"
+ expected = "CREATE TEMPORARY TABLE `temp` ( INDEX `index_temp_on_zip` (`zip`)) ENGINE=InnoDB AS SELECT id, name, zip FROM a_really_complicated_query"
actual = ActiveRecord::Base.connection.create_table(:temp, temporary: true, as: "SELECT id, name, zip FROM a_really_complicated_query") do |t|
t.index :zip
end
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index c4715393b3..fe610ae951 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -144,7 +144,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
def test_get_and_release_advisory_lock
- lock_name = "test_lock_name"
+ lock_name = "test lock'n'name"
got_lock = @connection.get_advisory_lock(lock_name)
assert got_lock, "get_advisory_lock should have returned true but it didn't"
@@ -159,7 +159,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
def test_release_non_existent_advisory_lock
- lock_name = "fake_lock_name"
+ lock_name = "fake lock'n'name"
released_non_existent_lock = @connection.release_advisory_lock(lock_name)
assert_equal released_non_existent_lock, false,
'expected release_advisory_lock to return false when there was no lock to release'
@@ -168,6 +168,6 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
protected
def test_lock_free(lock_name)
- @connection.select_value("SELECT IS_FREE_LOCK('#{lock_name}');") == 1
+ @connection.select_value("SELECT IS_FREE_LOCK(#{@connection.quote(lock_name)})") == 1
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
new file mode 100644
index 0000000000..0e37c70e5c
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
@@ -0,0 +1,62 @@
+require "cases/helper"
+require 'support/connection_helper'
+
+module ActiveRecord
+ class Mysql2TransactionTest < ActiveRecord::Mysql2TestCase
+ self.use_transactional_tests = false
+
+ class Sample < ActiveRecord::Base
+ self.table_name = 'samples'
+ end
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.clear_cache!
+
+ @connection.transaction do
+ @connection.drop_table 'samples', if_exists: true
+ @connection.create_table('samples') do |t|
+ t.integer 'value'
+ end
+ end
+
+ Sample.reset_column_information
+ end
+
+ teardown do
+ @connection.drop_table 'samples', if_exists: true
+ end
+
+ test "raises error when a serialization failure occurs" do
+ assert_raises(ActiveRecord::TransactionSerializationError) do
+ thread = Thread.new do
+ Sample.transaction isolation: :serializable do
+ Sample.delete_all
+
+ 10.times do |i|
+ sleep 0.1
+
+ Sample.create value: i
+ end
+ end
+ end
+
+ sleep 0.1
+
+ Sample.transaction isolation: :serializable do
+ Sample.delete_all
+
+ 10.times do |i|
+ sleep 0.1
+
+ Sample.create value: i
+ end
+
+ sleep 1
+ end
+
+ thread.join
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
index 0a9703263e..3df11ce11b 100644
--- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
@@ -28,10 +28,10 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
end
test "minus value is out of range" do
- assert_raise(RangeError) do
+ assert_raise(ActiveModel::RangeError) do
UnsignedType.create(unsigned_integer: -10)
end
- assert_raise(RangeError) do
+ assert_raise(ActiveModel::RangeError) do
UnsignedType.create(unsigned_bigint: -10)
end
assert_raise(ActiveRecord::StatementInvalid) do
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index 9e250c2b7c..66f0a70394 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -104,6 +104,13 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
assert_equal ActiveRecord::Point.new(1, 2), p.x
end
+ def test_empty_string_assignment
+ assert_nothing_raised { PostgresqlPoint.new(x: "") }
+
+ p = PostgresqlPoint.new(x: "")
+ assert_equal nil, p.x
+ end
+
def test_array_of_points_round_trip
expected_value = [
ActiveRecord::Point.new(1, 2),
diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
new file mode 100644
index 0000000000..e76705a802
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
@@ -0,0 +1,72 @@
+require "cases/helper"
+require 'support/connection_helper'
+
+module ActiveRecord
+ class PostgresqlTransactionTest < ActiveRecord::PostgreSQLTestCase
+ self.use_transactional_tests = false
+
+ class Sample < ActiveRecord::Base
+ self.table_name = 'samples'
+ end
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+
+ @connection.transaction do
+ @connection.drop_table 'samples', if_exists: true
+ @connection.create_table('samples') do |t|
+ t.integer 'value'
+ end
+ end
+
+ Sample.reset_column_information
+ end
+
+ teardown do
+ @connection.drop_table 'samples', if_exists: true
+ end
+
+ test "raises error when a serialization failure occurs" do
+ with_warning_suppression do
+ assert_raises(ActiveRecord::TransactionSerializationError) do
+ thread = Thread.new do
+ Sample.transaction isolation: :serializable do
+ Sample.delete_all
+
+ 10.times do |i|
+ sleep 0.1
+
+ Sample.create value: i
+ end
+ end
+ end
+
+ sleep 0.1
+
+ Sample.transaction isolation: :serializable do
+ Sample.delete_all
+
+ 10.times do |i|
+ sleep 0.1
+
+ Sample.create value: i
+ end
+
+ sleep 1
+ end
+
+ thread.join
+ end
+ end
+ end
+
+ protected
+
+ def with_warning_suppression
+ log_level = @connection.client_min_messages
+ @connection.client_min_messages = 'error'
+ yield
+ @connection.client_min_messages = log_level
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
index 77a99ca778..ea0f0b8fa5 100644
--- a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
@@ -18,7 +18,7 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
bigint_array = @connection.type_map.lookup(1016, -1, "bigint[]")
big_array = [123456789123456789]
- assert_raises(RangeError) { int_array.serialize(big_array) }
+ assert_raises(ActiveModel::RangeError) { int_array.serialize(big_array) }
assert_equal "{123456789123456789}", bigint_array.serialize(big_array)
end
@@ -27,7 +27,7 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
bigint_range = @connection.type_map.lookup(3926, -1, "int8range")
big_range = 0..123456789123456789
- assert_raises(RangeError) { int_range.serialize(big_range) }
+ assert_raises(ActiveModel::RangeError) { int_range.serialize(big_range) }
assert_equal "[0,123456789123456789]", bigint_range.serialize(big_range)
end
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index a3046d526e..9dadd114a1 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -168,7 +168,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
e = assert_raise(ActiveRecord::AssociationTypeMismatch) {
Admin::RegionalUser.new(region: 'wrong value')
}
- assert_match(/^Region\([^)]+\) expected, got String\([^)]+\)$/, e.message)
+ assert_match(/^Region\([^)]+\) expected, got "wrong value" which is an instance of String\([^)]+\)$/, e.message)
ensure
Admin.send :remove_const, "Region" if Admin.const_defined?("Region")
Admin.send :remove_const, "RegionalUser" if Admin.const_defined?("RegionalUser")
@@ -700,6 +700,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 17, reply.replies.size
end
+ def test_replace_counter_cache
+ topic = Topic.create(title: "Zoom-zoom-zoom")
+ reply = Reply.create(title: "re: zoom", content: "speedy quick!")
+
+ reply.topic = topic
+ reply.save
+ topic.reload
+
+ assert_equal 1, topic.replies_count
+ end
+
def test_association_assignment_sticks
post = Post.first
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index e975f4fbdd..7ec0dfce7a 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1315,7 +1315,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, summit.client_of
end
- def test_deleting_by_fixnum_id
+ def test_deleting_by_integer_id
david = Developer.find(1)
assert_difference 'david.projects.count', -1 do
@@ -1352,7 +1352,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
- def test_destroying_by_fixnum_id
+ def test_destroying_by_integer_id
force_signal37_to_load_all_clients_of_firm
assert_difference "Client.count", -1 do
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index aff0dabee7..aa35844a03 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -65,7 +65,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
end
- def test_ordered_habtm
+ def test_ordered_has_many_through
person_prime = Class.new(ActiveRecord::Base) do
def self.name; 'Person'; end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index c9d9e29f09..1574f373c2 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -659,4 +659,22 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_deprecated { firm.account(true) }
end
+
+ class SpecialBook < ActiveRecord::Base
+ self.table_name = 'books'
+ belongs_to :author, class_name: 'SpecialAuthor'
+ end
+
+ class SpecialAuthor < ActiveRecord::Base
+ self.table_name = 'authors'
+ has_one :book, class_name: 'SpecialBook', foreign_key: 'author_id'
+ end
+
+ def test_assocation_enum_works_properly
+ author = SpecialAuthor.create!(name: 'Test')
+ book = SpecialBook.create!(status: 'published')
+ author.book = book
+
+ refute_equal 0, SpecialAuthor.joins(:book).where(books: { status: 'published' } ).count
+ end
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index c7bd9d2119..3047914b70 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -598,7 +598,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags.delete(Object.new) }
end
- def test_deleting_by_fixnum_id_from_has_many_through
+ def test_deleting_by_integer_id_from_has_many_through
post = posts(:thinking)
assert_difference 'post.tags.count', -1 do
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index 2bebbfa205..7bcaa53aa2 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -38,7 +38,7 @@ module ActiveRecord
data.reload
assert_equal 2, data.overloaded_float
- assert_kind_of Fixnum, OverloadedType.last.overloaded_float
+ assert_kind_of Integer, OverloadedType.last.overloaded_float
assert_equal 2.0, UnoverloadedType.last.overloaded_float
assert_kind_of Float, UnoverloadedType.last.overloaded_float
end
@@ -63,6 +63,15 @@ module ActiveRecord
end
end
+ test "model with nonexistent attribute with default value can be saved" do
+ klass = Class.new(OverloadedType) do
+ attribute :non_existent_string_with_default, :string, default: 'nonexistent'
+ end
+
+ model = klass.new
+ assert model.save
+ end
+
test "changing defaults" do
data = OverloadedType.new
unoverloaded_data = UnoverloadedType.new
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index eef2d29d02..3191393a41 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -940,7 +940,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_kind_of Integer, m1.world_population
assert_equal 6000000000, m1.world_population
- assert_kind_of Fixnum, m1.my_house_population
+ assert_kind_of Integer, m1.my_house_population
assert_equal 3, m1.my_house_population
assert_kind_of BigDecimal, m1.bank_balance
@@ -968,7 +968,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_kind_of Integer, m1.world_population
assert_equal 6000000000, m1.world_population
- assert_kind_of Fixnum, m1.my_house_population
+ assert_kind_of Integer, m1.my_house_population
assert_equal 3, m1.my_house_population
assert_kind_of BigDecimal, m1.bank_balance
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 8f2682c781..cfae700159 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -482,6 +482,10 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 1, Account.where(firm_name: '37signals').order(:firm_name).reverse_order.count
end
+ def test_count_with_block
+ assert_equal 4, Account.count { |account| account.credit_limit.modulo(10).zero? }
+ end
+
def test_should_sum_expression
# Oracle adapter returns floating point value 636.0 after SUM
if current_adapter?(:OracleAdapter)
diff --git a/activerecord/test/cases/coders/json_test.rb b/activerecord/test/cases/coders/json_test.rb
new file mode 100644
index 0000000000..d22d93d129
--- /dev/null
+++ b/activerecord/test/cases/coders/json_test.rb
@@ -0,0 +1,15 @@
+require "cases/helper"
+
+module ActiveRecord
+ module Coders
+ class JSONTest < ActiveRecord::TestCase
+ def test_returns_nil_if_empty_string_given
+ assert_nil JSON.load("")
+ end
+
+ def test_returns_nil_if_nil_given
+ assert_nil JSON.load(nil)
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb
index 37d951ad88..839fdbe578 100644
--- a/activerecord/test/cases/comment_test.rb
+++ b/activerecord/test/cases/comment_test.rb
@@ -5,7 +5,6 @@ if ActiveRecord::Base.connection.supports_comments?
class CommentTest < ActiveRecord::TestCase
include SchemaDumpingHelper
- self.use_transactional_tests = false if current_adapter?(:Mysql2Adapter)
class Commented < ActiveRecord::Base
self.table_name = 'commenteds'
@@ -29,6 +28,10 @@ class CommentTest < ActiveRecord::TestCase
t.string :empty_comment, comment: ''
t.string :nil_comment, comment: nil
t.string :absent_comment
+ t.index :space_comment, comment: ' '
+ t.index :empty_comment, comment: ''
+ t.index :nil_comment, comment: nil
+ t.index :absent_comment
end
Commented.reset_column_information
@@ -54,6 +57,12 @@ class CommentTest < ActiveRecord::TestCase
end
end
+ def test_blank_indexes_created_in_block
+ @connection.indexes('blank_comments').each do |index|
+ assert_nil index.comment
+ end
+ end
+
def test_add_column_with_comment_later
@connection.add_column :commenteds, :rating, :integer, comment: 'I am running out of imagination'
Commented.reset_column_information
diff --git a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
index 580568c8ac..c7ca428ab7 100644
--- a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
+++ b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
@@ -37,7 +37,7 @@ module ActiveRecord
end
def test_close
- pool = Pool.new(ConnectionSpecification.new({}, nil))
+ pool = Pool.new(ConnectionSpecification.new("primary", {}, nil))
pool.insert_connection_for_test! @adapter
@adapter.pool = pool
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index 9b1865e8bb..a019cc6490 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -4,43 +4,40 @@ module ActiveRecord
module ConnectionAdapters
class ConnectionHandlerTest < ActiveRecord::TestCase
def setup
- @klass = Class.new(Base) { def self.name; 'klass'; end }
- @subklass = Class.new(@klass) { def self.name; 'subklass'; end }
-
@handler = ConnectionHandler.new
- @pool = @handler.establish_connection(@klass, Base.connection_pool.spec)
+ @spec_name = "primary"
+ @pool = @handler.establish_connection(ActiveRecord::Base.configurations['arunit'])
+ end
+
+ def test_establish_connection_uses_spec_name
+ config = {"readonly" => {"adapter" => 'sqlite3'}}
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(config)
+ spec = resolver.spec(:readonly)
+ @handler.establish_connection(spec.to_hash)
+
+ assert_not_nil @handler.retrieve_connection_pool('readonly')
+ ensure
+ @handler.remove_connection('readonly')
end
def test_retrieve_connection
- assert @handler.retrieve_connection(@klass)
+ assert @handler.retrieve_connection(@spec_name)
end
def test_active_connections?
assert !@handler.active_connections?
- assert @handler.retrieve_connection(@klass)
+ assert @handler.retrieve_connection(@spec_name)
assert @handler.active_connections?
@handler.clear_active_connections!
assert !@handler.active_connections?
end
- def test_retrieve_connection_pool_with_ar_base
- assert_nil @handler.retrieve_connection_pool(ActiveRecord::Base)
- end
-
def test_retrieve_connection_pool
- assert_not_nil @handler.retrieve_connection_pool(@klass)
+ assert_not_nil @handler.retrieve_connection_pool(@spec_name)
end
- def test_retrieve_connection_pool_uses_superclass_when_no_subclass_connection
- assert_not_nil @handler.retrieve_connection_pool(@subklass)
- end
-
- def test_retrieve_connection_pool_uses_superclass_pool_after_subclass_establish_and_remove
- sub_pool = @handler.establish_connection(@subklass, Base.connection_pool.spec)
- assert_same sub_pool, @handler.retrieve_connection_pool(@subklass)
-
- @handler.remove_connection @subklass
- assert_same @pool, @handler.retrieve_connection_pool(@subklass)
+ def test_retrieve_connection_pool_with_invalid_id
+ assert_nil @handler.retrieve_connection_pool("foo")
end
def test_connection_pools
@@ -79,7 +76,7 @@ module ActiveRecord
pid = fork {
rd.close
- pool = @handler.retrieve_connection_pool(@klass)
+ pool = @handler.retrieve_connection_pool(@spec_name)
wr.write Marshal.dump pool.schema_cache.size
wr.close
exit!
@@ -91,6 +88,36 @@ module ActiveRecord
assert_equal @pool.schema_cache.size, Marshal.load(rd.read)
rd.close
end
+
+ def test_a_class_using_custom_pool_and_switching_back_to_primary
+ klass2 = Class.new(Base) { def self.name; 'klass2'; end }
+
+ assert_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
+
+ pool = klass2.establish_connection(ActiveRecord::Base.connection_pool.spec.config)
+ assert_equal klass2.connection.object_id, pool.connection.object_id
+ refute_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
+
+ klass2.remove_connection
+
+ assert_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
+ end
+
+ def test_connection_specification_name_should_fallback_to_parent
+ klassA = Class.new(Base)
+ klassB = Class.new(klassA)
+
+ assert_equal klassB.connection_specification_name, klassA.connection_specification_name
+ klassA.connection_specification_name = "readonly"
+ assert_equal "readonly", klassB.connection_specification_name
+ end
+
+ def test_remove_connection_should_not_remove_parent
+ klass2 = Class.new(Base) { def self.name; 'klass2'; end }
+ klass2.remove_connection
+ refute_nil ActiveRecord::Base.connection.object_id
+ assert_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/connection_specification_test.rb b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
index ea2196cda2..d204fce59f 100644
--- a/activerecord/test/cases/connection_adapters/connection_specification_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
@@ -4,7 +4,7 @@ module ActiveRecord
module ConnectionAdapters
class ConnectionSpecificationTest < ActiveRecord::TestCase
def test_dup_deep_copy_config
- spec = ConnectionSpecification.new({ :a => :b }, "bar")
+ spec = ConnectionSpecification.new("primary", { :a => :b }, "bar")
assert_not_equal(spec.config.object_id, spec.dup.config.object_id)
end
end
diff --git a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
index 9ee92a3cd2..f25b85e8a7 100644
--- a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
+++ b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
@@ -27,7 +27,7 @@ module ActiveRecord
ENV['DATABASE_URL'] = "postgres://localhost/foo"
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_spec(:default_env, config)
- expected = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" }
+ expected = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost", "name"=>"default_env" }
assert_equal expected, actual
end
@@ -37,7 +37,7 @@ module ActiveRecord
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_spec(:foo, config)
- expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost","name"=>"foo" }
assert_equal expected, actual
end
@@ -47,7 +47,7 @@ module ActiveRecord
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_spec(:foo, config)
- expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost","name"=>"foo" }
assert_equal expected, actual
end
@@ -55,7 +55,7 @@ module ActiveRecord
ENV['DATABASE_URL'] = "postgres://localhost/foo"
config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
actual = resolve_spec(:production, config)
- expected = { "adapter"=>"not_postgres", "database"=>"not_foo", "host"=>"localhost" }
+ expected = { "adapter"=>"not_postgres", "database"=>"not_foo", "host"=>"localhost", "name"=>"production" }
assert_equal expected, actual
end
@@ -93,7 +93,7 @@ module ActiveRecord
ENV['DATABASE_URL'] = "ibm-db://localhost/foo"
config = { "default_env" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
actual = resolve_spec(:default_env, config)
- expected = { "adapter"=>"ibm_db", "database"=>"foo", "host"=>"localhost" }
+ expected = { "adapter"=>"ibm_db", "database"=>"foo", "host"=>"localhost", "name"=>"default_env" }
assert_equal expected, actual
end
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
index 7566863653..3acbafbff4 100644
--- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strigns for lookup
+unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strings for lookup
module ActiveRecord
module ConnectionAdapters
class TypeLookupTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index efa3e0455e..a45ee281c7 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -335,11 +335,10 @@ module ActiveRecord
# is called with an anonymous class
def test_anonymous_class_exception
anonymous = Class.new(ActiveRecord::Base)
- handler = ActiveRecord::Base.connection_handler
- assert_raises(RuntimeError) {
- handler.establish_connection anonymous, nil
- }
+ assert_raises(RuntimeError) do
+ anonymous.establish_connection
+ end
end
def test_pool_sets_connection_schema_cache
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index 358b6ad537..b30a83d9ce 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -28,7 +28,8 @@ module ActiveRecord
assert_equal({
"adapter" => "abstract",
"host" => "foo",
- "encoding" => "utf8" }, spec)
+ "encoding" => "utf8",
+ "name" => "production"}, spec)
end
def test_url_sub_key
@@ -36,7 +37,8 @@ module ActiveRecord
assert_equal({
"adapter" => "abstract",
"host" => "foo",
- "encoding" => "utf8" }, spec)
+ "encoding" => "utf8",
+ "name" => "production"}, spec)
end
def test_url_sub_key_merges_correctly
@@ -46,7 +48,8 @@ module ActiveRecord
"adapter" => "abstract",
"host" => "foo",
"encoding" => "utf8",
- "pool" => "3" }, spec)
+ "pool" => "3",
+ "name" => "production"}, spec)
end
def test_url_host_no_db
@@ -113,9 +116,19 @@ module ActiveRecord
assert_equal({
"adapter" => "sqlite3",
"database" => "foo",
- "encoding" => "utf8" }, spec)
+ "encoding" => "utf8",
+ "name" => "production"}, spec)
+ end
+
+ def test_spec_name_on_key_lookup
+ spec = spec(:readonly, 'readonly' => {'adapter' => 'sqlite3'})
+ assert_equal "readonly", spec.name
end
+ def test_spec_name_with_inline_config
+ spec = spec({'adapter' => 'sqlite3'})
+ assert_equal "primary", spec.name, "should default to primary id"
+ end
end
end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index f03df2d99e..374a8ba199 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -174,7 +174,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_exists_fails_when_parameter_has_invalid_type
- assert_raises(RangeError) do
+ assert_raises(ActiveModel::RangeError) do
assert_equal false, Topic.exists?(("9"*53).to_i) # number that's bigger than int
end
assert_equal false, Topic.exists?("foo")
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index da934ab8fe..9455d4886c 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -857,7 +857,7 @@ class FoxyFixturesTest < ActiveRecord::TestCase
assert_equal("X marks the spot!", pirates(:mark).catchphrase)
end
- def test_supports_label_interpolation_for_fixnum_label
+ def test_supports_label_interpolation_for_integer_label
assert_equal("#1 pirate!", pirates(1).catchphrase)
end
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index 707a2d1da1..c97960a412 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -215,5 +215,11 @@ class LogSubscriberTest < ActiveRecord::TestCase
wait
assert_match(/<16 bytes of binary data>/, @logger.logged(:debug).join)
end
+
+ def test_binary_data_hash
+ Binary.create(data: { a: 1 })
+ wait
+ assert_match(/<7 bytes of binary data>/, @logger.logged(:debug).join)
+ end
end
end
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
index c7a1b81a75..29546525f3 100644
--- a/activerecord/test/cases/migration/column_attributes_test.rb
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -154,7 +154,7 @@ module ActiveRecord
assert_equal String, bob.first_name.class
assert_equal String, bob.last_name.class
assert_equal String, bob.bio.class
- assert_equal Fixnum, bob.age.class
+ assert_kind_of Integer, bob.age
assert_equal Time, bob.birthday.class
if current_adapter?(:OracleAdapter)
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 5a6d2ce80c..36b6662820 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -69,6 +69,10 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::Migration.verbose = @verbose_was
end
+ def test_migration_version_matches_component_version
+ assert_equal ActiveRecord::VERSION::STRING.to_f, ActiveRecord::Migration.current_version
+ end
+
def test_migrator_versions
migrations_path = MIGRATIONS_ROOT + "/valid"
old_path = ActiveRecord::Migrator.migrations_paths
@@ -176,7 +180,7 @@ class MigrationTest < ActiveRecord::TestCase
# is_a?(Bignum)
assert_kind_of Integer, b.world_population
assert_equal 6000000000, b.world_population
- assert_kind_of Fixnum, b.my_house_population
+ assert_kind_of Integer, b.my_house_population
assert_equal 3, b.my_house_population
assert_kind_of BigDecimal, b.bank_balance
assert_equal BigDecimal("1586.43"), b.bank_balance
@@ -200,7 +204,7 @@ class MigrationTest < ActiveRecord::TestCase
assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
else
# - SQL standard is an integer
- assert_kind_of Fixnum, b.value_of_e
+ assert_kind_of Integer, b.value_of_e
assert_equal 2, b.value_of_e
end
@@ -424,6 +428,23 @@ class MigrationTest < ActiveRecord::TestCase
ENV["RACK_ENV"] = original_rack_env
end
+ def test_internal_metadata_stores_environment_when_other_data_exists
+ ActiveRecord::InternalMetadata.delete_all
+ ActiveRecord::InternalMetadata[:foo] = 'bar'
+
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ migrations_path = MIGRATIONS_ROOT + "/valid"
+ old_path = ActiveRecord::Migrator.migrations_paths
+ ActiveRecord::Migrator.migrations_paths = migrations_path
+
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ ActiveRecord::Migrator.up(migrations_path)
+ assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
+ assert_equal 'bar', ActiveRecord::InternalMetadata[:foo]
+ ensure
+ ActiveRecord::Migrator.migrations_paths = old_path
+ end
+
def test_rename_internal_metadata_table
original_internal_metadata_table_name = ActiveRecord::Base.internal_metadata_table_name
diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb
index af4183a601..a4fbf579a1 100644
--- a/activerecord/test/cases/multiple_db_test.rb
+++ b/activerecord/test/cases/multiple_db_test.rb
@@ -24,6 +24,13 @@ class MultipleDbTest < ActiveRecord::TestCase
assert_equal(ActiveRecord::Base.connection, Entrant.connection)
end
+ def test_swapping_the_connection
+ old_spec_name, Course.connection_specification_name = Course.connection_specification_name, "primary"
+ assert_equal(Entrant.connection, Course.connection)
+ ensure
+ Course.connection_specification_name = old_spec_name
+ end
+
def test_find
c1 = Course.find(1)
assert_equal "Ruby Development", c1.name
@@ -89,8 +96,8 @@ class MultipleDbTest < ActiveRecord::TestCase
end
def test_connection
- assert_equal Entrant.arel_engine.connection, Bird.arel_engine.connection
- assert_not_equal Entrant.arel_engine.connection, Course.arel_engine.connection
+ assert_equal Entrant.arel_engine.connection.object_id, Bird.arel_engine.connection.object_id
+ assert_not_equal Entrant.arel_engine.connection.object_id, Course.arel_engine.connection.object_id
end
unless in_memory_db?
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index e53239cdee..406643d6fb 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -144,13 +144,12 @@ class QueryCacheTest < ActiveRecord::TestCase
def test_cache_does_not_wrap_string_results_in_arrays
Task.cache do
- # Oracle adapter returns count() as Fixnum or Float
+ # Oracle adapter returns count() as Integer or Float
if current_adapter?(:OracleAdapter)
assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
elsif current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter)
# Future versions of the sqlite3 adapter will return numeric
- assert_instance_of Fixnum,
- Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
+ assert_instance_of Fixnum, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
else
assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
end
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 6d91f96bf6..c01c82f4f5 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -102,9 +102,9 @@ module ActiveRecord
assert_equal float.to_s, @quoter.quote(float, nil)
end
- def test_quote_fixnum
- fixnum = 1
- assert_equal fixnum.to_s, @quoter.quote(fixnum, nil)
+ def test_quote_integer
+ integer = 1
+ assert_equal integer.to_s, @quoter.quote(integer, nil)
end
def test_quote_bignum
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 95e4230a58..5604124bb3 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -1086,6 +1086,11 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 9, posts.where(:comments_count => 0).count
end
+ def test_count_with_block
+ posts = Post.all
+ assert_equal 10, posts.count { |p| p.comments_count.even? }
+ end
+
def test_count_on_association_relation
author = Author.last
another_author = Author.first
@@ -1989,4 +1994,22 @@ class RelationTest < ActiveRecord::TestCase
def test_relation_join_method
assert_equal 'Thank you for the welcome,Thank you again for the welcome', Post.first.comments.join(",")
end
+
+ def test_connection_adapters_can_reorder_binds
+ posts = Post.limit(1).offset(2)
+
+ stubbed_connection = Post.connection.dup
+ def stubbed_connection.combine_bind_parameters(**kwargs)
+ offset = kwargs[:offset]
+ kwargs[:offset] = kwargs[:limit]
+ kwargs[:limit] = offset
+ super(**kwargs)
+ end
+
+ posts.define_singleton_method(:connection) do
+ stubbed_connection
+ end
+
+ assert_equal 2, posts.to_a.length
+ end
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index f1927f561e..9dc1f5e2c2 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -47,10 +47,6 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
end
- def test_magic_comment
- assert_match "# encoding: #{Encoding.default_external.name}", standard_dump
- end
-
def test_schema_dump
output = standard_dump
assert_match %r{create_table "accounts"}, output
diff --git a/activerecord/test/cases/schema_loading_test.rb b/activerecord/test/cases/schema_loading_test.rb
new file mode 100644
index 0000000000..3d92a5e104
--- /dev/null
+++ b/activerecord/test/cases/schema_loading_test.rb
@@ -0,0 +1,52 @@
+require "cases/helper"
+
+module SchemaLoadCounter
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ attr_accessor :load_schema_calls
+
+ def load_schema!
+ self.load_schema_calls ||= 0
+ self.load_schema_calls +=1
+ super
+ end
+ end
+end
+
+class SchemaLoadingTest < ActiveRecord::TestCase
+ def test_basic_model_is_loaded_once
+ klass = define_model
+ klass.new
+ assert_equal 1, klass.load_schema_calls
+ end
+
+ def test_model_with_custom_lock_is_loaded_once
+ klass = define_model do |c|
+ c.table_name = :lock_without_defaults_cust
+ c.locking_column = :custom_lock_version
+ end
+ klass.new
+ assert_equal 1, klass.load_schema_calls
+ end
+
+ def test_model_with_changed_custom_lock_is_loaded_twice
+ klass = define_model do |c|
+ c.table_name = :lock_without_defaults_cust
+ end
+ klass.new
+ klass.locking_column = :custom_lock_version
+ klass.new
+ assert_equal 2, klass.load_schema_calls
+ end
+
+ private
+
+ def define_model
+ Class.new(ActiveRecord::Base) do
+ include SchemaLoadCounter
+ self.table_name = :lock_without_defaults
+ yield self if block_given?
+ end
+ end
+end
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 96c94eefa0..0e277ed235 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -301,7 +301,7 @@ class NamedScopingTest < ActiveRecord::TestCase
:relation, # private class method on AR::Base
:new, # redefined class method on AR::Base
:all, # a default scope
- :public, # some imporant methods on Module and Class
+ :public, # some important methods on Module and Class
:protected,
:private,
:name,
@@ -544,12 +544,6 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal 1, SpecialComment.where(body: 'go crazy').created.count
end
- def test_model_class_should_respond_to_empty
- assert !Topic.empty?
- Topic.delete_all
- assert Topic.empty?
- end
-
def test_model_class_should_respond_to_none
assert !Topic.none?
Topic.delete_all
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 6056156698..846be857d0 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -295,4 +295,37 @@ class SerializedAttributeTest < ActiveRecord::TestCase
topic.update_attribute :content, nil
assert_equal [topic], Topic.where(content: nil)
end
+
+ def test_mutation_detection_does_not_double_serialize
+ coder = Object.new
+ def coder.dump(value)
+ return if value.nil?
+ value + " encoded"
+ end
+ def coder.load(value)
+ return if value.nil?
+ value.gsub(" encoded", "")
+ end
+ type = Class.new(ActiveModel::Type::Value) do
+ include ActiveModel::Type::Helpers::Mutable
+
+ def serialize(value)
+ return if value.nil?
+ value + " serialized"
+ end
+
+ def deserialize(value)
+ return if value.nil?
+ value.gsub(" serialized", "")
+ end
+ end.new
+ model = Class.new(Topic) do
+ attribute :foo, type
+ serialize :foo, coder
+ end
+
+ topic = model.create!(foo: "bar")
+ topic.foo
+ refute topic.changed?
+ end
end
diff --git a/activerecord/test/cases/suppressor_test.rb b/activerecord/test/cases/suppressor_test.rb
index 7d44e36419..2f00241de2 100644
--- a/activerecord/test/cases/suppressor_test.rb
+++ b/activerecord/test/cases/suppressor_test.rb
@@ -60,4 +60,16 @@ class SuppressorTest < ActiveRecord::TestCase
end
end
end
+
+ def test_suppresses_when_nested_multiple_times
+ assert_no_difference -> { Notification.count } do
+ Notification.suppress do
+ Notification.suppress { }
+ Notification.create
+ Notification.create!
+ Notification.new.save
+ Notification.new.save!
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index e6d731e1e1..510bb088c8 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -316,6 +316,8 @@ module ActiveRecord
end
class DatabaseTasksMigrateTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
def setup
ActiveRecord::Tasks::DatabaseTasks.migrations_paths = 'custom/path'
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index e4497e1756..812e18a253 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,761 +1,53 @@
-* Time zones: Ensure that the UTC offset reflects DST changes that occurred
- since the app started. Removes UTC offset caching, reducing performance,
- but this is still relatively quick and isn't in any hot paths.
+* Introduce Module#delegate_missing_to.
- *Alexey Shein*
+ When building a decorator, a common pattern emerges:
-* Make `getlocal` and `getutc` always return instances of `Time` for
- `ActiveSupport::TimeWithZone` and `DateTime`. This eliminates a possible
- stack level too deep error in `to_time` where `ActiveSupport::TimeWithZone`
- was wrapping a `DateTime` instance. As a consequence of this the internal
- time value in `ActiveSupport::TimeWithZone` is now always an instance of
- `Time` in the UTC timezone, whether that's as the UTC time directly or
- a representation of the local time in the timezone. There should be no
- consequences of this internal change and if there are it's a bug due to
- leaky abstractions.
-
- *Andrew White*
-
-* Add `DateTime#subsec` to return the fraction of a second as a `Rational`.
-
- *Andrew White*
-
-* Add additional aliases for `DateTime#utc` to mirror the ones on
- `ActiveSupport::TimeWithZone` and `Time`.
-
- *Andrew White*
-
-* Add `DateTime#localtime` to return an instance of `Time` in the system's
- local timezone. Also aliased to `getlocal`.
-
- *Andrew White*, *Yuichiro Kaneko*
-
-* Add `Time#sec_fraction` to return the fraction of a second as a `Rational`.
-
- *Andrew White*
-
-* Add `ActiveSupport.to_time_preserves_timezone` config option to control
- how `to_time` handles timezones. In Ruby 2.4+ the behavior will change
- from converting to the local system timezone, to preserving the timezone
- of the receiver. This config option defaults to false so that apps made
- with earlier versions of Rails are not affected when upgrading, e.g:
-
- >> ENV['TZ'] = 'US/Eastern'
-
- >> "2016-04-23T10:23:12.000Z".to_time
- => "2016-04-23T06:23:12.000-04:00"
-
- >> ActiveSupport.to_time_preserves_timezone = true
-
- >> "2016-04-23T10:23:12.000Z".to_time
- => "2016-04-23T10:23:12.000Z"
-
- Fixes #24617.
-
- *Andrew White*
-
-* `ActiveSupport::TimeZone.country_zones(country_code)` looks up the
- country's time zones by its two-letter ISO3166 country code, e.g.
-
- >> ActiveSupport::TimeZone.country_zones(:jp).map(&:to_s)
- => ["(GMT+09:00) Osaka"]
-
- >> ActiveSupport::TimeZone.country_zones(:uy).map(&:to_s)
- => ["(GMT-03:00) Montevideo"]
-
- *Andrey Novikov*
-
-* `Array#sum` compat with Ruby 2.4's native method.
-
- Ruby 2.4 introduces `Array#sum`, but it only supports numeric elements,
- breaking our `Enumerable#sum` which supports arbitrary `Object#+`.
- To fix, override `Array#sum` with our compatible implementation.
-
- Native Ruby 2.4:
-
- %w[ a b ].sum
- # => TypeError: String can't be coerced into Fixnum
-
- With `Enumerable#sum` shim:
-
- %w[ a b ].sum
- # => 'ab'
-
- We tried shimming the fast path and falling back to the compatible path
- if it fails, but that ends up slower even in simple cases due to the cost
- of exception handling. Our only choice is to override the native `Array#sum`
- with our `Enumerable#sum`.
-
- *Jeremy Daer*
-
-* `ActiveSupport::Duration` supports ISO8601 formatting and parsing.
-
- ActiveSupport::Duration.parse('P3Y6M4DT12H30M5S')
- # => 3 years, 6 months, 4 days, 12 hours, 30 minutes, and 5 seconds
-
- (3.years + 3.days).iso8601
- # => "P3Y3D"
-
- Inspired by Arnau Siches' [ISO8601 gem](https://github.com/arnau/ISO8601/)
- and rewritten by Andrey Novikov with suggestions from Andrew White. Test
- data from the ISO8601 gem redistributed under MIT license.
-
- (Will be used to support the PostgreSQL interval data type.)
-
- *Andrey Novikov*, *Arnau Siches*, *Andrew White*
-
-* `Cache#fetch(key, force: true)` forces a cache miss, so it must be called
- with a block to provide a new value to cache. Fetching with `force: true`
- but without a block now raises ArgumentError.
-
- cache.fetch('key', force: true) # => ArgumentError
-
- *Santosh Wadghule*
-
-* `ActiveSupport::Duration` supports weeks and hours.
-
- [1.hour.inspect, 1.hour.value, 1.hour.parts]
- # => ["3600 seconds", 3600, [[:seconds, 3600]]] # Before
- # => ["1 hour", 3600, [[:hours, 1]]] # After
-
- [1.week.inspect, 1.week.value, 1.week.parts]
- # => ["7 days", 604800, [[:days, 7]]] # Before
- # => ["1 week", 604800, [[:weeks, 1]]] # After
-
- This brings us into closer conformance with ISO8601 and relieves some
- astonishment about getting `1.hour.inspect # => 3600 seconds`.
-
- Compatibility: The duration's `value` remains the same, so apps using
- durations are oblivious to the new time periods. Apps, libraries, and
- plugins that work with the internal `parts` hash will need to broaden
- their time period handling to cover hours & weeks.
-
- *Andrey Novikov*
-
-* Fix behavior of JSON encoding for `Exception`.
-
- *namusyaka*
-
-* Make `number_to_phone` format number with regexp pattern.
-
- number_to_phone(18812345678, pattern: /(\d{3})(\d{4})(\d{4})/)
- # => 188-1234-5678
-
- *Pan Gaoyong*
-
-* Match `String#to_time`'s behaviour to that of ruby's implementation for edge cases.
-
- `nil` is now returned instead of the current date if the string provided does
- contain time information, but none that is used to build the `Time` object.
-
- Fixes #22958.
-
- *Siim Liiser*
-
-* Rely on the native DateTime#<=> implementation to handle non-datetime like
- objects instead of returning `nil` ourselves. This restores the ability
- of `DateTime` instances to be compared with a `Numeric` that represents an
- astronomical julian day number.
-
- Fixes #24228.
-
- *Andrew White*
-
-* Add `String#upcase_first` method.
-
- *Glauco Custódio*, *bogdanvlviv*
-
-* Prevent `Marshal.load` from looping infinitely when trying to autoload a constant
- which resolves to a different name.
-
- *Olek Janiszewski*
-
-* Deprecate `Module.local_constants`. Please use `Module.constants(false)` instead.
-
- *Yuichiro Kaneko*
-
-* Publish `ActiveSupport::Executor` and `ActiveSupport::Reloader` APIs to allow
- components and libraries to manage, and participate in, the execution of
- application code, and the application reloading process.
-
- *Matthew Draper*
-
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* Deprecate arguments on `assert_nothing_raised`.
-
- `assert_nothing_raised` does not assert the arguments that have been passed
- in (usually a specific exception class) since the method only yields the
- block. So as not to confuse the users that the arguments have meaning, they
- are being deprecated.
-
- *Tara Scherner de la Fuente*
-
-* Make `benchmark('something', silence: true)` actually work.
-
- *DHH*
-
-* Add `#on_weekday?` method to `Date`, `Time`, and `DateTime`.
-
- `#on_weekday?` returns `true` if the receiving date/time does not fall on a Saturday
- or Sunday.
-
- *Vipul A M*
-
-* Add `Array#second_to_last` and `Array#third_to_last` methods.
-
- *Brian Christian*
-
-* Fix regression in `Hash#dig` for HashWithIndifferentAccess.
-
- *Jon Moss*
-
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* Change `number_to_currency` behavior for checking negativity.
-
- Used `to_f.negative` instead of using `to_f.phase` for checking negativity
- of a number in number_to_currency helper.
- This change works same for all cases except when number is "-0.0".
-
- -0.0.to_f.negative? => false
- -0.0.to_f.phase? => 3.14
-
- This change reverts changes from https://github.com/rails/rails/pull/6512.
- But it should be acceptable as we could not find any currency which
- supports negative zeros.
-
- *Prathamesh Sonpatki*, *Rafael Mendonça França*
-
-* Match `HashWithIndifferentAccess#default`'s behaviour with `Hash#default`.
-
- *David Cornu*
-
-* Adds `:exception_object` key to `ActiveSupport::Notifications::Instrumenter`
- payload when an exception is raised.
-
- Adds new key/value pair to payload when an exception is raised:
- e.g. `:exception_object => #<RuntimeError: FAIL>`.
-
- *Ryan T. Hosford*
-
-* Support extended grapheme clusters and UAX 29.
-
- *Adam Roben*
-
-* Add petabyte and exabyte numeric conversion.
-
- *Akshay Vishnoi*
-
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* Add thread_m/cattr_accessor/reader/writer suite of methods for declaring class and module variables that live per-thread.
- This makes it easy to declare per-thread globals that are encapsulated. Note: This is a sharp edge. A wild proliferation
- of globals is A Bad Thing. But like other sharp tools, when it's right, it's right.
-
- Here's an example of a simple event tracking system where the object being tracked needs not pass a creator that it
- doesn't need itself along:
-
- module Current
- thread_mattr_accessor :account
- thread_mattr_accessor :user
-
- def self.reset() self.account = self.user = nil end
- end
-
- class ApplicationController < ActionController::Base
- before_action :set_current
- after_action { Current.reset }
+ class Partition
+ def initialize(first_event)
+ @events = [ first_event ]
+ end
- private
- def set_current
- Current.account = Account.find(params[:account_id])
- Current.user = Current.account.users.find(params[:user_id])
+ def people
+ if @events.first.detail.people.any?
+ @events.collect { |e| Array(e.detail.people) }.flatten.uniq
+ else
+ @events.collect(&:creator).uniq
end
- end
-
- class MessagesController < ApplicationController
- def create
- @message = Message.create!(message_params)
end
- end
-
- class Message < ApplicationRecord
- has_many :events
- after_create :track_created
private
- def track_created
- events.create! origin: self, action: :create
+ def respond_to_missing?(name, include_private = false)
+ @events.respond_to?(name, include_private)
end
- end
-
- class Event < ApplicationRecord
- belongs_to :creator, class_name: 'User'
- before_validation { self.creator ||= Current.user }
- end
-
- *DHH*
-
-
-* Deprecated `Module#qualified_const_` in favour of the builtin Module#const_
- methods.
-
- *Genadi Samokovarov*
-
-* Deprecate passing string to define callback.
-
- *Yuichiro Kaneko*
-
-* `ActiveSupport::Cache::Store#namespaced_key`,
- `ActiveSupport::Cache::MemCachedStore#escape_key`, and
- `ActiveSupport::Cache::FileStore#key_file_path`
- are deprecated and replaced with `normalize_key` that now calls `super`.
-
- `ActiveSupport::Cache::LocaleCache#set_cache_value` is deprecated and replaced with `write_cache_value`.
-
- *Michael Grosser*
-
-* Implements an evented file watcher to asynchronously detect changes in the
- application source code, routes, locales, etc.
-
- This watcher is disabled by default, applications my enable it in the configuration:
-
- # config/environments/development.rb
- config.file_watcher = ActiveSupport::EventedFileUpdateChecker
-
- This feature depends on the [listen](https://github.com/guard/listen) gem:
-
- group :development do
- gem 'listen', '~> 3.0.5'
- end
-
- *Puneet Agarwal* and *Xavier Noria*
-
-* Added `Time.days_in_year` to return the number of days in the given year, or the
- current year if no argument is provided.
-
- *Jon Pascoe*
-
-* Updated `parameterize` to preserve the case of a string, optionally.
-
- Example:
-
- parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
- parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
-
- *Swaathi Kakarla*
-
-* `HashWithIndifferentAccess.new` respects the default value or proc on objects
- that respond to `#to_hash`. `.new_from_hash_copying_default` simply invokes `.new`.
- All calls to `.new_from_hash_copying_default` are replaced with `.new`.
-
- *Gordon Chan*
-
-* Change Integer#year to return a Fixnum instead of a Float to improve
- consistency.
-
- Integer#years returned a Float while the rest of the accompanying methods
- (days, weeks, months, etc.) return a Fixnum.
-
- Before:
-
- 1.year # => 31557600.0
-
- After:
-
- 1.year # => 31557600
-
- *Konstantinos Rousis*
-
-* Handle invalid UTF-8 strings when HTML escaping.
-
- Use `ActiveSupport::Multibyte::Unicode.tidy_bytes` to handle invalid UTF-8
- strings in `ERB::Util.unwrapped_html_escape` and `ERB::Util.html_escape_once`.
- Prevents user-entered input passed from a querystring into a form field from
- causing invalid byte sequence errors.
- *Grey Baker*
-
-* Update `ActiveSupport::Multibyte::Chars#slice!` to return `nil` if the
- arguments are out of bounds, to mirror the behavior of `String#slice!`
-
- *Gourav Tiwari*
-
-* Fix `number_to_human` so that 999999999 rounds to "1 Billion" instead of
- "1000 Million".
-
- *Max Jacobson*
-
-* Fix `ActiveSupport::Deprecation#deprecate_methods` to report using the
- current deprecator instance, where applicable.
-
- *Brandon Dunne*
-
-* `Cache#fetch` instrumentation marks whether it was a `:hit`.
-
- *Robin Clowers*
-
-* `assert_difference` and `assert_no_difference` now returns the result of the
- yielded block.
-
- Example:
-
- post = assert_difference -> { Post.count }, 1 do
- Post.create
- end
-
- *Lucas Mazza*
-
-* Short-circuit `blank?` on date and time values since they are never blank.
-
- Fixes #21657.
-
- *Andrew White*
-
-* Replaced deprecated `ThreadSafe::Cache` with its successor `Concurrent::Map` now that
- the thread_safe gem has been merged into concurrent-ruby.
-
- *Jerry D'Antonio*
-
-* Updated Unicode version to 8.0.0
-
- *Anshul Sharma*
-
-* `number_to_currency` and `number_with_delimiter` now accept custom `delimiter_pattern` option
- to handle placement of delimiter, to support currency formats like INR
-
- Example:
-
- number_to_currency(1230000, delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/, unit: '₹', format: "%u %n")
- # => '₹ 12,30,000.00'
-
- *Vipul A M*
-
-* Deprecate `:prefix` option of `number_to_human_size` with no replacement.
-
- *Jean Boussier*
-
-* Fix `TimeWithZone#eql?` to properly handle `TimeWithZone` created from `DateTime`:
- twz = DateTime.now.in_time_zone
- twz.eql?(twz.dup) => true
-
- Fixes #14178.
-
- *Roque Pinel*
-
-* ActiveSupport::HashWithIndifferentAccess `select` and `reject` will now return
- enumerator if called without block.
-
- Fixes #20095.
-
- *Bernard Potocki*
-
-* Removed `ActiveSupport::Concurrency::Latch`, superseded by `Concurrent::CountDownLatch`
- from the concurrent-ruby gem.
-
- *Jerry D'Antonio*
-
-* Fix not calling `#default` on `HashWithIndifferentAccess#to_hash` when only
- `default_proc` is set, which could raise.
-
- *Simon Eskildsen*
-
-* Fix setting `default_proc` on `HashWithIndifferentAccess#dup`.
-
- *Simon Eskildsen*
-
-* Fix a range of values for parameters of the Time#change.
-
- *Nikolay Kondratyev*
-
-* Add `Enumerable#pluck` to get the same values from arrays as from ActiveRecord
- associations.
-
- Fixes #20339.
-
- *Kevin Deisz*
-
-* Add a bang version to `ActiveSupport::OrderedOptions` get methods which will raise
- an `KeyError` if the value is `.blank?`.
-
- Before:
-
- if (slack_url = Rails.application.secrets.slack_url).present?
- # Do something worthwhile
- else
- # Raise as important secret password is not specified
+ def method_missing(method, *args, &block)
+ @events.send(method, *args, &block)
+ end
end
- After:
-
- slack_url = Rails.application.secrets.slack_url!
-
- *Aditya Sanghi*, *Gaurish Sharma*
-
-* Remove deprecated `Class#superclass_delegating_accessor`.
- Use `Class#class_attribute` instead.
-
- *Akshay Vishnoi*
-
-* Patch `Delegator` to work with `#try`.
-
- Fixes #5790.
-
- *Nate Smith*
-
-* Add `Integer#positive?` and `Integer#negative?` query methods
- in the vein of `Fixnum#zero?`.
-
- This makes it nicer to do things like `bunch_of_numbers.select(&:positive?)`.
-
- *DHH*
-
-* Encoding `ActiveSupport::TimeWithZone` to YAML now preserves the timezone information.
-
- Fixes #9183.
-
- *Andrew White*
-
-* Added `ActiveSupport::TimeZone#strptime` to allow parsing times as if
- from a given timezone.
-
- *Paul A Jungwirth*
-
-* `ActiveSupport::Callbacks#skip_callback` now raises an `ArgumentError` if
- an unrecognized callback is removed.
-
- *Iain Beeston*
-
-* Added `ActiveSupport::ArrayInquirer` and `Array#inquiry`.
-
- Wrapping an array in an `ArrayInquirer` gives a friendlier way to check its
- contents:
-
- variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
-
- variants.phone? # => true
- variants.tablet? # => true
- variants.desktop? # => false
-
- variants.any?(:phone, :tablet) # => true
- variants.any?(:phone, :desktop) # => true
- variants.any?(:desktop, :watch) # => false
-
- `Array#inquiry` is a shortcut for wrapping the receiving array in an
- `ArrayInquirer`.
-
- *George Claghorn*
-
-* Deprecate `alias_method_chain` in favour of `Module#prepend` introduced in
- Ruby 2.0.
-
- *Kir Shatrov*
+ With `Module#delegate_missing_to`, the above is condensed to:
-* Added `#without` on `Enumerable` and `Array` to return a copy of an
- enumerable without the specified elements.
+ class Partition
+ delegate_missing_to :@events
- *Todd Bealmear*
-
-* Fixed a problem where `String#truncate_words` would get stuck with a complex
- string.
-
- *Henrik Nygren*
-
-* Fixed a roundtrip problem with `AS::SafeBuffer` where primitive-like strings
- will be dumped as primitives:
-
- Before:
-
- YAML.load ActiveSupport::SafeBuffer.new("Hello").to_yaml # => "Hello"
- YAML.load ActiveSupport::SafeBuffer.new("true").to_yaml # => true
- YAML.load ActiveSupport::SafeBuffer.new("false").to_yaml # => false
- YAML.load ActiveSupport::SafeBuffer.new("1").to_yaml # => 1
- YAML.load ActiveSupport::SafeBuffer.new("1.1").to_yaml # => 1.1
-
- After:
-
- YAML.load ActiveSupport::SafeBuffer.new("Hello").to_yaml # => "Hello"
- YAML.load ActiveSupport::SafeBuffer.new("true").to_yaml # => "true"
- YAML.load ActiveSupport::SafeBuffer.new("false").to_yaml # => "false"
- YAML.load ActiveSupport::SafeBuffer.new("1").to_yaml # => "1"
- YAML.load ActiveSupport::SafeBuffer.new("1.1").to_yaml # => "1.1"
-
- *Godfrey Chan*
-
-* Enable `number_to_percentage` to keep the number's precision by allowing
- `:precision` to be `nil`.
-
- *Jack Xu*
-
-* `config_accessor` became a private method, as with Ruby's `attr_accessor`.
-
- *Akira Matsuda*
-
-* `AS::Testing::TimeHelpers#travel_to` now changes `DateTime.now` as well as
- `Time.now` and `Date.today`.
-
- *Yuki Nishijima*
-
-* Add `file_fixture` to `ActiveSupport::TestCase`.
- It provides a simple mechanism to access sample files in your test cases.
-
- By default file fixtures are stored in `test/fixtures/files`. This can be
- configured per test-case using the `file_fixture_path` class attribute.
-
- *Yves Senn*
-
-* Return value of yielded block in `File.atomic_write`.
-
- *Ian Ker-Seymer*
-
-* Duplicate frozen array when assigning it to a `HashWithIndifferentAccess` so
- that it doesn't raise a `RuntimeError` when calling `map!` on it in `convert_value`.
-
- Fixes #18550.
-
- *Aditya Kapoor*
-
-* Add missing time zone definitions for Russian Federation and sync them
- with `zone.tab` file from tzdata version 2014j (latest).
-
- *Andrey Novikov*
-
-* Add `SecureRandom.base58` for generation of random base58 strings.
-
- *Matthew Draper*, *Guillermo Iguaran*
-
-* Add `#prev_day` and `#next_day` counterparts to `#yesterday` and
- `#tomorrow` for `Date`, `Time`, and `DateTime`.
-
- *George Claghorn*
-
-* Add `same_time` option to `#next_week` and `#prev_week` for `Date`, `Time`,
- and `DateTime`.
-
- *George Claghorn*
-
-* Add `#on_weekend?`, `#next_weekday`, `#prev_weekday` methods to `Date`,
- `Time`, and `DateTime`.
-
- `#on_weekend?` returns `true` if the receiving date/time falls on a Saturday
- or Sunday.
-
- `#next_weekday` returns a new date/time representing the next day that does
- not fall on a Saturday or Sunday.
-
- `#prev_weekday` returns a new date/time representing the previous day that
- does not fall on a Saturday or Sunday.
-
- *George Claghorn*
-
-* Change the default test order from `:sorted` to `:random`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActiveSupport::JSON::Encoding::CircularReferenceError`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated methods `ActiveSupport::JSON::Encoding.encode_big_decimal_as_string=`
- and `ActiveSupport::JSON::Encoding.encode_big_decimal_as_string`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActiveSupport::SafeBuffer#prepend`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated methods at `Kernel`.
-
- `silence_stderr`, `silence_stream`, `capture` and `quietly`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `active_support/core_ext/big_decimal/yaml_conversions`
- file.
-
- *Rafael Mendonça França*
-
-* Remove deprecated methods `ActiveSupport::Cache::Store.instrument` and
- `ActiveSupport::Cache::Store.instrument=`.
-
- *Rafael Mendonça França*
-
-* Change the way in which callback chains can be halted.
-
- The preferred method to halt a callback chain from now on is to explicitly
- `throw(:abort)`.
- In the past, callbacks could only be halted by explicitly providing a
- terminator and by having a callback match the conditions of the terminator.
-
-* Add `ActiveSupport.halt_callback_chains_on_return_false`
-
- Setting `ActiveSupport.halt_callback_chains_on_return_false`
- to `true` will let an app support the deprecated way of halting Active Record,
- and Active Model callback chains by returning `false`.
-
- Setting the value to `false` will tell the app to ignore any `false` value
- returned by those callbacks, and only halt the chain upon `throw(:abort)`.
-
- When the configuration option is missing, its value is `true`, so older apps
- ported to Rails 5.0 will not break (but display a deprecation warning).
- For new Rails 5.0 apps, its value is set to `false` in an initializer, so
- these apps will support the new behavior by default.
-
- *claudiob*, *Roque Pinel*
-
-* Changes arguments and default value of CallbackChain's `:terminator` option
-
- Chains of callbacks defined without an explicit `:terminator` option will
- now be halted as soon as a `before_` callback throws `:abort`.
-
- Chains of callbacks defined with a `:terminator` option will maintain their
- existing behavior of halting as soon as a `before_` callback matches the
- terminator's expectation.
-
- *claudiob*
-
-* Deprecate `MissingSourceFile` in favor of `LoadError`.
-
- `MissingSourceFile` was just an alias to `LoadError` and was not being
- raised inside the framework.
-
- *Rafael Mendonça França*
-
-* Add support for error dispatcher classes in `ActiveSupport::Rescuable`.
- Now it acts closer to Ruby's rescue.
-
- Example:
-
- class BaseController < ApplicationController
- module ErrorDispatcher
- def self.===(other)
- Exception === other && other.respond_to?(:status)
- end
+ def initialize(first_event)
+ @events = [ first_event ]
end
- rescue_from ErrorDispatcher do |error|
- render status: error.status, json: { error: error.to_s }
+ def people
+ if @events.first.detail.people.any?
+ @events.collect { |e| Array(e.detail.people) }.flatten.uniq
+ else
+ @events.collect(&:creator).uniq
+ end
end
end
- *Genadi Samokovarov*
-
-* Add `#verified` and `#valid_message?` methods to `ActiveSupport::MessageVerifier`
+ *Genadi Samokovarov*, *DHH*
- Previously, the only way to decode a message with `ActiveSupport::MessageVerifier`
- was to use `#verify`, which would raise an exception on invalid messages. Now
- `#verified` can also be used, which returns `nil` on messages that cannot be
- decoded.
+* Rescuable: If a handler doesn't match the exception, check for handlers
+ matching the exception's cause.
- Previously, there was no way to check if a message's format was valid without
- attempting to decode it. `#valid_message?` is a boolean convenience method that
- checks whether the message is valid without actually decoding it.
-
- *Logan Leger*
+ *Jeremy Daer*
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activesupport/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/activesupport/CHANGELOG.md) for previous changes.
diff --git a/activesupport/Rakefile b/activesupport/Rakefile
index 33ee62aa1b..7b56e36abf 100644
--- a/activesupport/Rakefile
+++ b/activesupport/Rakefile
@@ -3,7 +3,6 @@ require 'rake/testtask'
task :default => :test
task :package
-task "package:clean"
Rake::TestTask.new do |t|
t.libs << 'test'
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index 71fe4d7253..c6ca969844 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -23,5 +23,5 @@ Gem::Specification.new do |s|
s.add_dependency 'i18n', '~> 0.7'
s.add_dependency 'tzinfo', '~> 1.1'
s.add_dependency 'minitest', '~> 5.1'
- s.add_dependency 'concurrent-ruby', '~> 1.0'
+ s.add_dependency 'concurrent-ruby', '~> 1.0', '>= 1.0.2'
end
diff --git a/activesupport/lib/active_support/array_inquirer.rb b/activesupport/lib/active_support/array_inquirer.rb
index f59ddf5403..ea328f603e 100644
--- a/activesupport/lib/active_support/array_inquirer.rb
+++ b/activesupport/lib/active_support/array_inquirer.rb
@@ -9,8 +9,10 @@ module ActiveSupport
# variants.desktop? # => false
class ArrayInquirer < Array
# Passes each element of +candidates+ collection to ArrayInquirer collection.
- # The method returns true if at least one element is the same. If +candidates+
- # collection is not given, method returns true.
+ # 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])
#
diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb
index 34af83d1ab..ea9d85f6e3 100644
--- a/activesupport/lib/active_support/core_ext/array/grouping.rb
+++ b/activesupport/lib/active_support/core_ext/array/grouping.rb
@@ -89,24 +89,19 @@ class Array
# [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 = self.dup
+ result = []
if block_given?
- inject([[]]) do |results, element|
- if yield(element)
- results << []
- else
- results.last << element
- end
-
- results
+ while (idx = arr.index { |i| yield i })
+ result << arr.shift(idx)
+ arr.shift
end
else
- arr = self.dup
- result = []
while (idx = arr.index(value))
result << arr.shift(idx)
arr.shift
end
- result << arr
end
+ result << arr
end
end
diff --git a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
index 22fc7ecf92..074e2eabf8 100644
--- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
@@ -3,10 +3,8 @@ require 'bigdecimal/util'
module ActiveSupport
module BigDecimalWithDefaultFormat #:nodoc:
- DEFAULT_STRING_FORMAT = 'F'
-
- def to_s(format = nil)
- super(format || DEFAULT_STRING_FORMAT)
+ def to_s(format = 'F')
+ super(format)
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index b0f9a8be34..1d8c33b43e 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -26,8 +26,6 @@ class Class
# Returns an array with the direct children of +self+.
#
- # Integer.subclasses # => [Fixnum, Bignum]
- #
# class Foo; end
# class Bar < Foo; end
# class Baz < Bar; end
diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
index 4da7fdd159..6206546672 100644
--- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
@@ -294,6 +294,11 @@ module DateAndTime
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 <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
def all_week(start_day = Date.beginning_of_week)
diff --git a/activesupport/lib/active_support/core_ext/date_and_time/zones.rb b/activesupport/lib/active_support/core_ext/date_and_time/zones.rb
index d29a8db5cf..e2432c8f8a 100644
--- a/activesupport/lib/active_support/core_ext/date_and_time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/date_and_time/zones.rb
@@ -5,7 +5,7 @@ module DateAndTime
#
# 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
+ # 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 <tt>Time.zone</tt> as the local zone
# instead of the operating system's time zone.
@@ -14,7 +14,7 @@ module DateAndTime
# and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
#
# 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
+ # 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
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index 941f20c19d..8ebe758078 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -17,11 +17,12 @@ module Enumerable
# 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 = 0, &block)
+ def sum(identity = nil, &block)
if block_given?
map(&block).sum(identity)
else
- inject(:+) || identity
+ sum = identity ? inject(identity, :+) : inject(:+)
+ sum || identity || 0
end
end
@@ -33,7 +34,9 @@ module Enumerable
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
def index_by
if block_given?
- Hash[map { |elem| [yield(elem), elem] }]
+ result = {}
+ each { |elem| result[yield(elem)] = elem }
+ result
else
to_enum(:index_by) { size if respond_to?(:size) }
end
@@ -91,15 +94,16 @@ 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 = 0)
+ 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
- (actual_last - first + 1) * (actual_last + first) / 2
+ sum = identity || 0
+ sum + (actual_last - first + 1) * (actual_last + first) / 2
else
- identity
+ identity || 0
end
end
end
@@ -112,11 +116,15 @@ end
# just calling the compat method in the first place.
if Array.instance_methods(false).include?(:sum) && !(%w[a].sum rescue false)
class Array
- remove_method :sum
+ alias :orig_sum :sum
- def sum(*args) #:nodoc:
- # Use Enumerable#sum instead.
- super
+ 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/activesupport/lib/active_support/core_ext/hash/compact.rb b/activesupport/lib/active_support/core_ext/hash/compact.rb
index 5dc9a05ec7..62ea579c65 100644
--- a/activesupport/lib/active_support/core_ext/hash/compact.rb
+++ b/activesupport/lib/active_support/core_ext/hash/compact.rb
@@ -1,9 +1,9 @@
class Hash
# 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}
+ # hash = { a: true, b: false, c: nil }
+ # hash.compact # => { a: true, b: false }
+ # hash # => { a: true, b: false, c: nil }
# { c: nil }.compact # => {}
def compact
self.select { |_, value| !value.nil? }
@@ -11,9 +11,9 @@ class Hash
# Replaces current hash with non +nil+ values.
#
- # hash = { a: true, b: false, c: nil}
- # hash.compact! # => { a: true, b: false}
- # hash # => { a: true, b: false}
+ # hash = { a: true, b: false, c: nil }
+ # hash.compact! # => { a: true, b: false }
+ # hash # => { a: true, b: false }
def compact!
self.reject! { |_, value| value.nil? }
end
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index dd5ebe6d8d..2fc514cfce 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -55,8 +55,7 @@ class Hash
#
# XML_TYPE_NAMES = {
# "Symbol" => "symbol",
- # "Fixnum" => "integer",
- # "Bignum" => "integer",
+ # "Integer" => "integer",
# "BigDecimal" => "decimal",
# "Float" => "float",
# "TrueClass" => "boolean",
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 24450cd221..7f968d10b5 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -148,7 +148,6 @@ class Module
# 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).'
@@ -212,4 +211,68 @@ class Module
module_eval(method_def, file, line)
end
end
+
+ # When building decorators, a common pattern may emerge:
+ #
+ # class Partition
+ # def initialize(first_event)
+ # @events = [ first_event ]
+ # end
+ #
+ # def people
+ # if @events.first.detail.people.any?
+ # @events.collect { |e| Array(e.detail.people) }.flatten.uniq
+ # else
+ # @events.collect(&:creator).uniq
+ # end
+ # end
+ #
+ # private
+ # def respond_to_missing?(name, include_private = false)
+ # @events.respond_to?(name, include_private)
+ # end
+ #
+ # def method_missing(method, *args, &block)
+ # @events.send(method, *args, &block)
+ # end
+ # end
+ #
+ # With `Module#delegate_missing_to`, the above is condensed to:
+ #
+ # class Partition
+ # delegate_missing_to :@events
+ #
+ # def initialize(first_event)
+ # @events = [ first_event ]
+ # end
+ #
+ # def people
+ # if @events.first.detail.people.any?
+ # @events.collect { |e| Array(e.detail.people) }.flatten.uniq
+ # else
+ # @events.collect(&:creator).uniq
+ # end
+ # end
+ # end
+ #
+ # The target can be anything callable withing the object. E.g. instance
+ # variables, methods, constants ant the likes.
+ 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)
+ #{target}.respond_to?(name, include_private)
+ end
+
+ def method_missing(method, *args, &block)
+ if #{target}.respond_to?(method)
+ #{target}.public_send(method, *args, &block)
+ else
+ super
+ end
+ end
+ RUBY
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
index b25925b9d4..6586a351f8 100644
--- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
@@ -134,6 +134,12 @@ module ActiveSupport::NumericWithFormat
deprecate to_formatted_s: :to_s
end
-[Fixnum, Bignum, Float, BigDecimal].each do |klass|
- klass.prepend(ActiveSupport::NumericWithFormat)
+# Ruby 2.4+ unifies Fixnum & Bignum into Integer.
+if Integer == Fixnum
+ 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/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb
index 699b2fb42b..cb74bad73e 100644
--- a/activesupport/lib/active_support/core_ext/object/blank.rb
+++ b/activesupport/lib/active_support/core_ext/object/blank.rb
@@ -97,6 +97,8 @@ class Hash
end
class String
+ BLANK_RE = /\A[[:space:]]*\z/
+
# A string is blank if it's empty or contains whitespaces only:
#
# ''.blank? # => true
@@ -113,10 +115,7 @@ class String
# 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.
- #
- # Double negation in the second operand is also a performance tweak, it is
- # faster than the positive \A[[:space:]]*\z.
- empty? || !(/[[:^space:]]/ === self)
+ empty? || BLANK_RE === self
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index befa5aee21..9bc5ee65ba 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -70,7 +70,7 @@ class Numeric
# Numbers are not duplicable:
#
# 3.duplicable? # => false
- # 3.dup # => TypeError: can't dup Fixnum
+ # 3.dup # => TypeError: can't dup Integer
def duplicable?
false
end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 8c16d95b62..3b6d9da216 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -99,7 +99,7 @@ class Object
#
# "a".try!(:upcase) # => "A"
# nil.try!(:upcase) # => nil
- # 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Fixnum
+ # 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Integer
end
class Delegator
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index ebd0dd3fc7..213a91aa7a 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -1,5 +1,5 @@
class String
- # If you pass a single Fixnum, returns a substring of one character at that
+ # 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
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index b755726db2..e81b48ab26 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -227,11 +227,6 @@ class Time
end
alias :at_end_of_minute :end_of_minute
- # Returns a Range representing the whole day of the current time.
- def all_day
- beginning_of_day..end_of_day
- end
-
def plus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
other.since(self)
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index 24545d766c..b581710067 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -32,7 +32,7 @@ module ActiveSupport
# and the second is a library name
#
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
- def initialize(deprecation_horizon = '5.1', gem_name = 'Rails')
+ def initialize(deprecation_horizon = '5.2', gem_name = 'Rails')
self.gem_name = gem_name
self.deprecation_horizon = deprecation_horizon
# By default, warnings are not silenced and debugging is off.
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 3bde541009..47d09f4f5a 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -159,8 +159,10 @@ module ActiveSupport
if t.acts_like?(:time) || t.acts_like?(:date)
if type == :seconds
t.since(sign * number)
- elsif [:hours, :minutes].include?(type)
- t.in_time_zone.advance(type => 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
diff --git a/activesupport/lib/active_support/gem_version.rb b/activesupport/lib/active_support/gem_version.rb
index 4166ffc2fb..74f2d8dd4b 100644
--- a/activesupport/lib/active_support/gem_version.rb
+++ b/activesupport/lib/active_support/gem_version.rb
@@ -6,9 +6,9 @@ module ActiveSupport
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
index 73bc52b56f..2c05deee41 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -1,7 +1,6 @@
require 'active_support/concern'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/string/inflections'
-require 'active_support/core_ext/array/extract_options'
module ActiveSupport
# Rescuable module adds support for easier exception handling.
@@ -48,14 +47,12 @@ module ActiveSupport
# end
#
# Exceptions raised inside exception handlers are not propagated up.
- def rescue_from(*klasses, &block)
- options = klasses.extract_options!
-
- unless options.has_key?(:with)
+ def rescue_from(*klasses, with: nil, &block)
+ unless with
if block_given?
- options[:with] = block
+ with = block
else
- raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
+ raise ArgumentError, 'Need a handler. Pass the with: keyword argument or provide a block.'
end
end
@@ -65,65 +62,104 @@ module ActiveSupport
elsif klass.is_a?(String)
klass
else
- raise ArgumentError, "#{klass} is neither an Exception nor a String"
+ 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, options[:with]]]
+ self.rescue_handlers += [[key, with]]
end
end
- end
- # Tries to rescue the exception by looking up and calling a registered handler.
- def rescue_with_handler(exception)
- if handler = handler_for_rescue(exception)
- handler.arity != 0 ? handler.call(exception) : handler.call
- true # don't rely on the return value of the handler
+ # 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)
+ if handler = handler_for_rescue(exception, object: object)
+ handler.call exception
+ exception
+ end
end
- end
- def handler_for_rescue(exception)
- # We go from right to left because pairs are pushed onto rescue_handlers
- # as rescue_from declarations are found.
- _, rescuer = self.class.rescue_handlers.reverse.detect do |klass_name, handler|
- # The purpose of allowing strings in rescue_from is to support the
- # declaration of handler associations for exception classes whose
- # definition is yet unknown.
- #
- # Since this loop needs the constants it would be inconsistent to
- # assume they should exist at this point. An early raised exception
- # could trigger some other handler and the array could include
- # precisely a string whose corresponding constant has not yet been
- # seen. This is why we are tolerant to unknown constants.
- #
- # Note that this tolerance only matters if the exception was given as
- # a string, otherwise a NameError will be raised by the interpreter
- # itself when rescue_from CONSTANT is executed.
- klass = self.class.const_get(klass_name) rescue nil
- klass ||= (klass_name.constantize rescue nil)
- klass === exception if klass
+ 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
- case rescuer
- when Symbol
- method(rescuer)
- when Proc
- if rescuer.arity == 0
- Proc.new { instance_exec(&rescuer) }
- else
- Proc.new { |_exception| instance_exec(_exception, &rescuer) }
+ 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 || find_rescue_handler(exception.cause)
+ 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
end
- def index_of_handler_for_rescue(exception)
- handlers = self.class.rescue_handlers.reverse_each.with_index
- _, index = handlers.detect do |(klass_name, _), _|
- klass = self.class.const_get(klass_name) rescue nil
- klass ||= (klass_name.constantize rescue nil)
- klass === exception if klass
- end
- index
+ # 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/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index df7b081993..99fc26549e 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -32,20 +32,25 @@ module ActiveSupport
"binary" => "base64"
} unless defined?(DEFAULT_ENCODINGS)
- TYPE_NAMES = {
- "Symbol" => "symbol",
- "Fixnum" => "integer",
- "Bignum" => "integer",
- "BigDecimal" => "decimal",
- "Float" => "float",
- "TrueClass" => "boolean",
- "FalseClass" => "boolean",
- "Date" => "date",
- "DateTime" => "dateTime",
- "Time" => "dateTime",
- "Array" => "array",
- "Hash" => "hash"
- } unless defined?(TYPE_NAMES)
+ 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 Fixnum == Integer
+ TYPE_NAMES["Bignum"] = "integer" unless Bignum == Integer
+ end
FORMATTING = {
"symbol" => Proc.new { |symbol| symbol.to_s },
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index 924ed72345..95af5af2c0 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -20,11 +20,9 @@ module ActiveSupport
data = StringIO.new(data || '')
end
- char = data.getc
- if char.nil?
+ if data.eof?
{}
else
- data.ungetc(char)
silence_warnings { require 'rexml/document' } unless defined?(REXML::Document)
doc = REXML::Document.new(data)
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 7f0fcd6996..bab7440397 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -18,6 +18,9 @@ Thread.abort_on_exception = true
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
+# Default to old to_time behavior but allow running tests with new behavior
+ActiveSupport.to_time_preserves_timezone = ENV['PRESERVE_TIMEZONES'] == '1'
+
# Disable available locale checks to avoid warnings running the test suite.
I18n.enforce_available_locales = false
diff --git a/activesupport/test/core_ext/array/conversions_test.rb b/activesupport/test/core_ext/array/conversions_test.rb
index 507e13f968..de36e2026d 100644
--- a/activesupport/test/core_ext/array/conversions_test.rb
+++ b/activesupport/test/core_ext/array/conversions_test.rb
@@ -101,10 +101,10 @@ class ToXmlTest < ActiveSupport::TestCase
end
def test_to_xml_with_non_hash_elements
- xml = [1, 2, 3].to_xml(skip_instruct: true, indent: 0)
+ xml = %w[1 2 3].to_xml(skip_instruct: true, indent: 0)
- assert_equal '<fixnums type="array"><fixnum', xml.first(29)
- assert xml.include?(%(<fixnum type="integer">2</fixnum>)), xml
+ assert_equal '<strings type="array"><string', xml.first(29)
+ assert xml.include?(%(<string>2</string>)), xml
end
def test_to_xml_with_non_hash_different_type_elements
diff --git a/activesupport/test/core_ext/array/grouping_test.rb b/activesupport/test/core_ext/array/grouping_test.rb
index fb7367b0bf..0682241f0b 100644
--- a/activesupport/test/core_ext/array/grouping_test.rb
+++ b/activesupport/test/core_ext/array/grouping_test.rb
@@ -3,11 +3,12 @@ require 'active_support/core_ext/array'
class GroupingTest < ActiveSupport::TestCase
def setup
- Fixnum.send :private, :/ # test we avoid Integer#/ (redefined by mathn)
+ # In Ruby < 2.4, test we avoid Integer#/ (redefined by mathn)
+ Fixnum.send :private, :/ unless Fixnum == Integer
end
def teardown
- Fixnum.send :public, :/
+ Fixnum.send :public, :/ unless Fixnum == Integer
end
def test_in_groups_of_with_perfect_fit
diff --git a/activesupport/test/core_ext/bigdecimal_test.rb b/activesupport/test/core_ext/bigdecimal_test.rb
index 423a3f2e9d..6e82e3892b 100644
--- a/activesupport/test/core_ext/bigdecimal_test.rb
+++ b/activesupport/test/core_ext/bigdecimal_test.rb
@@ -5,5 +5,7 @@ class BigDecimalTest < ActiveSupport::TestCase
def test_to_s
bd = BigDecimal.new '0.01'
assert_equal '0.01', bd.to_s
+ assert_equal '+0.01', bd.to_s('+F')
+ assert_equal '+0.0 1', bd.to_s('+1F')
end
end
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index 932675a50d..8052d38c33 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -284,6 +284,23 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
end
+ def test_all_day
+ beginning_of_day = Time.local(2011,6,7,0,0,0)
+ end_of_day = Time.local(2011,6,7,23,59,59,Rational(999999999, 1000))
+ assert_equal beginning_of_day..end_of_day, Date.new(2011,6,7).all_day
+ end
+
+ def test_all_day_when_zone_is_set
+ zone = ActiveSupport::TimeZone["Hawaii"]
+ with_env_tz "UTC" do
+ with_tz_default zone do
+ beginning_of_day = zone.local(2011,6,7,0,0,0)
+ end_of_day = zone.local(2011,6,7,23,59,59,Rational(999999999, 1000))
+ assert_equal beginning_of_day..end_of_day, Date.new(2011,6,7).all_day
+ end
+ end
+ end
+
def test_all_week
assert_equal Date.new(2011,6,6)..Date.new(2011,6,12), Date.new(2011,6,7).all_week
assert_equal Date.new(2011,6,5)..Date.new(2011,6,11), Date.new(2011,6,7).all_week(:sunday)
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index fcd739c804..306316efcd 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -69,8 +69,14 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
def test_to_time
with_env_tz 'US/Eastern' do
assert_instance_of Time, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
- assert_equal Time.local(2005, 2, 21, 5, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
- assert_equal Time.local(2005, 2, 21, 5, 11, 12).utc_offset, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time.utc_offset
+
+ if ActiveSupport.to_time_preserves_timezone
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12).getlocal(0), DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12).getlocal(0).utc_offset, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time.utc_offset
+ else
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12).utc_offset, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time.utc_offset
+ end
end
end
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index bef660fe12..502e2811fa 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -12,7 +12,7 @@ class DurationTest < ActiveSupport::TestCase
assert d.is_a?(ActiveSupport::Duration)
assert_kind_of ActiveSupport::Duration, d
assert_kind_of Numeric, d
- assert_kind_of Fixnum, d
+ assert_kind_of Integer, d
assert !d.is_a?(Hash)
k = Class.new
@@ -88,6 +88,15 @@ class DurationTest < ActiveSupport::TestCase
assert_equal 1 + 1.second, 1.second + 1, "Duration + Numeric should == Numeric + Duration"
end
+ def test_time_plus_duration_returns_same_time_datatype
+ twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone['Moscow'] , Time.utc(2016,4,28,00,45))
+ now = Time.now.utc
+ %w( second minute hour day week month year ).each do |unit|
+ assert_equal((now + 1.send(unit)).class, Time, "Time + 1.#{unit} must be Time")
+ assert_equal((twz + 1.send(unit)).class, ActiveSupport::TimeWithZone, "TimeWithZone + 1.#{unit} must be TimeWithZone")
+ end
+ end
+
def test_argument_error
e = assert_raise ArgumentError do
1.second.ago('')
diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index 976c8b2b81..99c3236c35 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -22,6 +22,11 @@ class EnumerableTests < ActiveSupport::TestCase
end
end
+ def assert_typed_equal(e, v, cls, msg=nil)
+ assert_kind_of(cls, v, msg)
+ assert_equal(e, v, msg)
+ end
+
def test_sums
enum = GenericEnumerable.new([5, 15, 10])
assert_equal 30, enum.sum
@@ -38,6 +43,40 @@ class EnumerableTests < ActiveSupport::TestCase
payments = GenericEnumerable.new([ SummablePayment.new(5), SummablePayment.new(15) ])
assert_equal SummablePayment.new(20), payments.sum
assert_equal SummablePayment.new(20), payments.sum { |p| p }
+
+ sum = GenericEnumerable.new([3, 5.quo(1)]).sum
+ assert_typed_equal(8, sum, Rational)
+
+ sum = GenericEnumerable.new([3, 5.quo(1)]).sum(0.0)
+ assert_typed_equal(8.0, sum, Float)
+
+ sum = GenericEnumerable.new([3, 5.quo(1), 7.0]).sum
+ assert_typed_equal(15.0, sum, Float)
+
+ sum = GenericEnumerable.new([3, 5.quo(1), Complex(7)]).sum
+ assert_typed_equal(Complex(15), sum, Complex)
+ assert_typed_equal(15, sum.real, Rational)
+ assert_typed_equal(0, sum.imag, Integer)
+
+ sum = GenericEnumerable.new([3.5, 5]).sum
+ assert_typed_equal(8.5, sum, Float)
+
+ sum = GenericEnumerable.new([2, 8.5]).sum
+ assert_typed_equal(10.5, sum, Float)
+
+ sum = GenericEnumerable.new([1.quo(2), 1]).sum
+ assert_typed_equal(3.quo(2), sum, Rational)
+
+ sum = GenericEnumerable.new([1.quo(2), 1.quo(3)]).sum
+ assert_typed_equal(5.quo(6), sum, Rational)
+
+ sum = GenericEnumerable.new([2.0, 3.0*Complex::I]).sum
+ assert_typed_equal(Complex(2.0, 3.0), sum, Complex)
+ assert_typed_equal(2.0, sum.real, Float)
+ assert_typed_equal(3.0, sum.imag, Float)
+
+ sum = GenericEnumerable.new([1, 2]).sum(10) {|v| v * 2 }
+ assert_typed_equal(16, sum, Integer)
end
def test_nil_sums
@@ -55,6 +94,7 @@ class EnumerableTests < ActiveSupport::TestCase
assert_equal 0, GenericEnumerable.new([]).sum
assert_equal 0, GenericEnumerable.new([]).sum { |i| i + 10 }
assert_equal Payment.new(0), GenericEnumerable.new([]).sum(Payment.new(0))
+ assert_typed_equal 0.0, GenericEnumerable.new([]).sum(0.0), Float
end
def test_range_sums
@@ -68,6 +108,10 @@ class EnumerableTests < ActiveSupport::TestCase
assert_equal 5, (10..0).sum(5)
assert_equal 10, (10..10).sum
assert_equal 42, (10...10).sum(42)
+ assert_typed_equal 20.0, (1..4).sum(0.0) { |i| i * 2 }, Float
+ assert_typed_equal 10.0, (1..4).sum(0.0), Float
+ assert_typed_equal 20.0, (1..4).sum(10.0), Float
+ assert_typed_equal 5.0, (10..0).sum(5.0), Float
end
def test_array_sums
@@ -86,6 +130,40 @@ class EnumerableTests < ActiveSupport::TestCase
payments = [ SummablePayment.new(5), SummablePayment.new(15) ]
assert_equal SummablePayment.new(20), payments.sum
assert_equal SummablePayment.new(20), payments.sum { |p| p }
+
+ sum = [3, 5.quo(1)].sum
+ assert_typed_equal(8, sum, Rational)
+
+ sum = [3, 5.quo(1)].sum(0.0)
+ assert_typed_equal(8.0, sum, Float)
+
+ sum = [3, 5.quo(1), 7.0].sum
+ assert_typed_equal(15.0, sum, Float)
+
+ sum = [3, 5.quo(1), Complex(7)].sum
+ assert_typed_equal(Complex(15), sum, Complex)
+ assert_typed_equal(15, sum.real, Rational)
+ assert_typed_equal(0, sum.imag, Integer)
+
+ sum = [3.5, 5].sum
+ assert_typed_equal(8.5, sum, Float)
+
+ sum = [2, 8.5].sum
+ assert_typed_equal(10.5, sum, Float)
+
+ sum = [1.quo(2), 1].sum
+ assert_typed_equal(3.quo(2), sum, Rational)
+
+ sum = [1.quo(2), 1.quo(3)].sum
+ assert_typed_equal(5.quo(6), sum, Rational)
+
+ sum = [2.0, 3.0*Complex::I].sum
+ assert_typed_equal(Complex(2.0, 3.0), sum, Complex)
+ assert_typed_equal(2.0, sum.real, Float)
+ assert_typed_equal(3.0, sum.imag, Float)
+
+ sum = [1, 2].sum(10) {|v| v * 2 }
+ assert_typed_equal(16, sum, Integer)
end
def test_index_by
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index be8583e704..f0a4c4dddc 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -36,12 +36,12 @@ class HashExtTest < ActiveSupport::TestCase
def setup
@strings = { 'a' => 1, 'b' => 2 }
@nested_strings = { 'a' => { 'b' => { 'c' => 3 } } }
- @symbols = { :a => 1, :b => 2 }
+ @symbols = { :a => 1, :b => 2 }
@nested_symbols = { :a => { :b => { :c => 3 } } }
- @mixed = { :a => 1, 'b' => 2 }
- @nested_mixed = { 'a' => { :b => { 'c' => 3 } } }
- @fixnums = { 0 => 1, 1 => 2 }
- @nested_fixnums = { 0 => { 1 => { 2 => 3} } }
+ @mixed = { :a => 1, 'b' => 2 }
+ @nested_mixed = { 'a' => { :b => { 'c' => 3 } } }
+ @integers = { 0 => 1, 1 => 2 }
+ @nested_integers = { 0 => { 1 => { 2 => 3} } }
@illegal_symbols = { [] => 3 }
@nested_illegal_symbols = { [] => { [] => 3} }
@upcase_strings = { 'A' => 1, 'B' => 2 }
@@ -196,14 +196,14 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal @nested_illegal_symbols, @nested_illegal_symbols.deep_dup.deep_symbolize_keys!
end
- def test_symbolize_keys_preserves_fixnum_keys
- assert_equal @fixnums, @fixnums.symbolize_keys
- assert_equal @fixnums, @fixnums.dup.symbolize_keys!
+ def test_symbolize_keys_preserves_integer_keys
+ assert_equal @integers, @integers.symbolize_keys
+ assert_equal @integers, @integers.dup.symbolize_keys!
end
- def test_deep_symbolize_keys_preserves_fixnum_keys
- assert_equal @nested_fixnums, @nested_fixnums.deep_symbolize_keys
- assert_equal @nested_fixnums, @nested_fixnums.deep_dup.deep_symbolize_keys!
+ def test_deep_symbolize_keys_preserves_integer_keys
+ assert_equal @nested_integers, @nested_integers.deep_symbolize_keys
+ assert_equal @nested_integers, @nested_integers.deep_dup.deep_symbolize_keys!
end
def test_stringify_keys
@@ -299,14 +299,14 @@ class HashExtTest < ActiveSupport::TestCase
assert_raise(NoMethodError) { @nested_illegal_symbols.with_indifferent_access.deep_dup.deep_symbolize_keys! }
end
- def test_symbolize_keys_preserves_fixnum_keys_for_hash_with_indifferent_access
- assert_equal @fixnums, @fixnums.with_indifferent_access.symbolize_keys
- assert_raise(NoMethodError) { @fixnums.with_indifferent_access.dup.symbolize_keys! }
+ def test_symbolize_keys_preserves_integer_keys_for_hash_with_indifferent_access
+ assert_equal @integers, @integers.with_indifferent_access.symbolize_keys
+ assert_raise(NoMethodError) { @integers.with_indifferent_access.dup.symbolize_keys! }
end
- def test_deep_symbolize_keys_preserves_fixnum_keys_for_hash_with_indifferent_access
- assert_equal @nested_fixnums, @nested_fixnums.with_indifferent_access.deep_symbolize_keys
- assert_raise(NoMethodError) { @nested_fixnums.with_indifferent_access.deep_dup.deep_symbolize_keys! }
+ def test_deep_symbolize_keys_preserves_integer_keys_for_hash_with_indifferent_access
+ assert_equal @nested_integers, @nested_integers.with_indifferent_access.deep_symbolize_keys
+ assert_raise(NoMethodError) { @nested_integers.with_indifferent_access.deep_dup.deep_symbolize_keys! }
end
def test_stringify_keys_for_hash_with_indifferent_access
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index ae4aed0554..566f29b470 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -40,6 +40,12 @@ class Someone < Struct.new(:name, :place)
FAILED_DELEGATE_LINE_2 = __LINE__ + 1
delegate :bar, :to => :place, :allow_nil => true
+
+ private
+
+ def private_name
+ "Private"
+ end
end
Invoice = Struct.new(:client) do
@@ -83,6 +89,20 @@ Product = Struct.new(:name) do
end
end
+DecoratedTester = Struct.new(:client) do
+ delegate_missing_to :client
+end
+
+class DecoratedReserved
+ delegate_missing_to :case
+
+ attr_reader :case
+
+ def initialize(kase)
+ @case = kase
+ end
+end
+
class Block
def hello?
true
@@ -173,6 +193,21 @@ class ModuleTest < ActiveSupport::TestCase
end
end
+ def test_delegation_target_when_prefix_is_true
+ assert_nothing_raised do
+ Name.send :delegate, :go, to: :you, prefix: true
+ end
+ assert_nothing_raised do
+ Name.send :delegate, :go, to: :_you, prefix: true
+ end
+ assert_raise(ArgumentError) do
+ Name.send :delegate, :go, to: :You, prefix: true
+ end
+ assert_raise(ArgumentError) do
+ Name.send :delegate, :go, to: :@you, prefix: true
+ end
+ end
+
def test_delegation_prefix
invoice = Invoice.new(@david)
assert_equal invoice.client_name, "David"
@@ -316,6 +351,30 @@ class ModuleTest < ActiveSupport::TestCase
assert has_block.hello?
end
+ def test_delegate_to_missing_with_method
+ assert_equal "David", DecoratedTester.new(@david).name
+ end
+
+ def test_delegate_to_missing_with_reserved_methods
+ assert_equal "David", DecoratedReserved.new(@david).name
+ end
+
+ def test_delegate_to_missing_does_not_delegate_to_private_methods
+ e = assert_raises(NoMethodError) do
+ DecoratedReserved.new(@david).private_name
+ end
+
+ assert_match(/undefined method `private_name' for/, e.message)
+ end
+
+ def test_delegate_to_missing_does_not_delegate_to_fake_methods
+ e = assert_raises(NoMethodError) do
+ DecoratedReserved.new(@david).my_fake_method
+ end
+
+ assert_match(/undefined method `my_fake_method' for/, e.message)
+ end
+
def test_parent
assert_equal Yz::Zy, Yz::Zy::Cd.parent
assert_equal Yz, Yz::Zy.parent
diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb
index 5654aeb4f8..69c30a8a9e 100644
--- a/activesupport/test/core_ext/numeric_ext_test.rb
+++ b/activesupport/test/core_ext/numeric_ext_test.rb
@@ -387,16 +387,9 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
end
def test_to_s__injected_on_proper_types
- assert_equal Fixnum, 1230.class
assert_equal '1.23 Thousand', 1230.to_s(:human)
-
- assert_equal Float, Float(1230).class
assert_equal '1.23 Thousand', Float(1230).to_s(:human)
-
- assert_equal Bignum, (100**10).class
assert_equal '100000 Quadrillion', (100**10).to_s(:human)
-
- assert_equal BigDecimal, BigDecimal("1000010").class
assert_equal '1 Million', BigDecimal("1000010").to_s(:human)
end
diff --git a/activesupport/test/core_ext/object/deep_dup_test.rb b/activesupport/test/core_ext/object/deep_dup_test.rb
index 791b5e7172..aa839201ea 100644
--- a/activesupport/test/core_ext/object/deep_dup_test.rb
+++ b/activesupport/test/core_ext/object/deep_dup_test.rb
@@ -51,7 +51,7 @@ class DeepDupTest < ActiveSupport::TestCase
end
def test_deep_dup_with_hash_class_key
- hash = { Fixnum => 1 }
+ hash = { Integer => 1 }
dup = hash.deep_dup
assert_equal 1, dup.keys.length
end
diff --git a/activesupport/test/core_ext/object/json_gem_encoding_test.rb b/activesupport/test/core_ext/object/json_gem_encoding_test.rb
index 02ab17fb64..2cbb1d590f 100644
--- a/activesupport/test/core_ext/object/json_gem_encoding_test.rb
+++ b/activesupport/test/core_ext/object/json_gem_encoding_test.rb
@@ -10,7 +10,7 @@ require 'json/encoding_test_cases'
# The AS::JSON encoder requires the BigDecimal core_ext, which, unfortunately,
# changes the BigDecimal#to_s output, and consequently the JSON gem output. So
-# we need to require this unfront to ensure we don't get a false failure, but
+# we need to require this upfront to ensure we don't get a false failure, but
# ideally we should just fix the BigDecimal core_ext to not change to_s without
# arguments.
require 'active_support/core_ext/big_decimal'
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index f38b225b38..d68a77680b 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -10,6 +10,7 @@ require 'active_support/core_ext/string/strip'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/string/indent'
require 'time_zone_test_helpers'
+require 'yaml'
class StringInflectionsTest < ActiveSupport::TestCase
include InflectorTestCases
@@ -344,7 +345,7 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
class StringAccessTest < ActiveSupport::TestCase
- test "#at with Fixnum, returns a substring of one character at that position" do
+ test "#at with Integer, returns a substring of one character at that position" do
assert_equal "h", "hello".at(0)
end
@@ -357,19 +358,19 @@ class StringAccessTest < ActiveSupport::TestCase
assert_equal nil, "hello".at(/nonexisting/)
end
- test "#from with positive Fixnum, returns substring from the given position to the end" do
+ test "#from with positive Integer, returns substring from the given position to the end" do
assert_equal "llo", "hello".from(2)
end
- test "#from with negative Fixnum, position is counted from the end" do
+ test "#from with negative Integer, position is counted from the end" do
assert_equal "lo", "hello".from(-2)
end
- test "#to with positive Fixnum, substring from the beginning to the given position" do
+ test "#to with positive Integer, substring from the beginning to the given position" do
assert_equal "hel", "hello".to(2)
end
- test "#to with negative Fixnum, position is counted from the end" do
+ test "#to with negative Integer, position is counted from the end" do
assert_equal "hell", "hello".to(-2)
end
@@ -383,14 +384,14 @@ class StringAccessTest < ActiveSupport::TestCase
assert_equal 'x', 'x'.first
end
- test "#first with Fixnum, returns a substring from the beginning to position" do
+ test "#first with Integer, returns a substring from the beginning to position" do
assert_equal "he", "hello".first(2)
assert_equal "", "hello".first(0)
assert_equal "hello", "hello".first(10)
assert_equal 'x', 'x'.first(4)
end
- test "#first with Fixnum >= string length still returns a new string" do
+ test "#first with Integer >= string length still returns a new string" do
string = "hello"
different_string = string.first(5)
assert_not_same different_string, string
@@ -401,14 +402,14 @@ class StringAccessTest < ActiveSupport::TestCase
assert_equal 'x', 'x'.last
end
- test "#last with Fixnum, returns a substring from the end to position" do
+ test "#last with Integer, returns a substring from the end to position" do
assert_equal "llo", "hello".last(3)
assert_equal "hello", "hello".last(10)
assert_equal "", "hello".last(0)
assert_equal 'x', 'x'.last(4)
end
- test "#last with Fixnum >= string length still returns a new string" do
+ test "#last with Integer >= string length still returns a new string" do
string = "hello"
different_string = string.last(5)
assert_not_same different_string, string
@@ -463,10 +464,17 @@ class StringConversionsTest < ActiveSupport::TestCase
def test_string_to_time_utc_offset
with_env_tz "US/Eastern" do
- assert_equal 0, "2005-02-27 23:50".to_time(:utc).utc_offset
- assert_equal(-18000, "2005-02-27 23:50".to_time.utc_offset)
- assert_equal 0, "2005-02-27 22:50 -0100".to_time(:utc).utc_offset
- assert_equal(-18000, "2005-02-27 22:50 -0100".to_time.utc_offset)
+ if ActiveSupport.to_time_preserves_timezone
+ assert_equal 0, "2005-02-27 23:50".to_time(:utc).utc_offset
+ assert_equal(-18000, "2005-02-27 23:50".to_time.utc_offset)
+ assert_equal 0, "2005-02-27 22:50 -0100".to_time(:utc).utc_offset
+ assert_equal(-3600, "2005-02-27 22:50 -0100".to_time.utc_offset)
+ else
+ assert_equal 0, "2005-02-27 23:50".to_time(:utc).utc_offset
+ assert_equal(-18000, "2005-02-27 23:50".to_time.utc_offset)
+ assert_equal 0, "2005-02-27 22:50 -0100".to_time(:utc).utc_offset
+ assert_equal(-18000, "2005-02-27 22:50 -0100".to_time.utc_offset)
+ end
end
end
@@ -674,7 +682,7 @@ class OutputSafetyTest < ActiveSupport::TestCase
assert_equal @string, @string.html_safe
end
- test "A fixnum is safe by default" do
+ test "An integer is safe by default" do
assert 5.html_safe?
end
@@ -805,7 +813,7 @@ class OutputSafetyTest < ActiveSupport::TestCase
assert_equal ["<p>", "<b>", "<h1>"], @other_string
end
- test "Concatting a fixnum to safe always yields safe" do
+ test "Concatting an integer to safe always yields safe" do
string = @string.html_safe
string = string.concat(13)
assert_equal "hello".concat(13), string
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index 45c88b79cb..ec34bd823d 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -341,7 +341,7 @@ class DeprecationTest < ActiveSupport::TestCase
end
def test_default_deprecation_horizon_should_always_bigger_than_current_rails_version
- assert ActiveSupport::Deprecation.new.deprecation_horizon > ActiveSupport::VERSION::STRING
+ assert_operator ActiveSupport::Deprecation.new.deprecation_horizon, :>, ActiveSupport::VERSION::STRING
end
def test_default_gem_name
diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb
index 14fe97a986..c7dc1dadb6 100644
--- a/activesupport/test/inflector_test_cases.rb
+++ b/activesupport/test/inflector_test_cases.rb
@@ -270,7 +270,8 @@ module InflectorTestCases
"maybe you'll be there" => "Maybe You'll Be There",
"¿por qué?" => '¿Por Qué?',
"Fred’s" => "Fred’s",
- "Fred`s" => "Fred`s"
+ "Fred`s" => "Fred`s",
+ ActiveSupport::SafeBuffer.new("confirmation num") => "Confirmation Num"
}
OrdinalNumbers = {
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
index bd43ad0797..e42e6d2973 100644
--- a/activesupport/test/rescuable_test.rb
+++ b/activesupport/test/rescuable_test.rb
@@ -3,9 +3,6 @@ require 'abstract_unit'
class WraithAttack < StandardError
end
-class NuclearExplosion < StandardError
-end
-
class MadRonon < StandardError
end
@@ -19,6 +16,10 @@ module WeirdError
end
class Stargate
+ # Nest this so the 'NuclearExplosion' handler needs a lexical const_get
+ # to find it.
+ class NuclearExplosion < StandardError; end
+
attr_accessor :result
include ActiveSupport::Rescuable
@@ -57,6 +58,14 @@ class Stargate
raise MadRonon.new("dex")
end
+ def fall_back_to_cause
+ # This exception is the cause and has a handler.
+ ronanize
+ rescue
+ # This is the exception we'll handle that doesn't have a cause.
+ raise 'unhandled RuntimeError with a handleable cause'
+ end
+
def weird
StandardError.new.tap do |exc|
def exc.weird?
@@ -74,7 +83,6 @@ class Stargate
def sos_first
@result = 'sos_first'
end
-
end
class CoolStargate < Stargate
@@ -127,4 +135,9 @@ class RescuableTest < ActiveSupport::TestCase
result = @cool_stargate.send(:rescue_handlers).collect(&:first)
assert_equal expected, result
end
+
+ def test_rescue_falls_back_to_exception_cause
+ @stargate.dispatch :fall_back_to_cause
+ assert_equal 'unhandled RuntimeError with a handleable cause', @stargate.result
+ end
end
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
index f0067ca656..6e9ce7ac11 100644
--- a/activesupport/test/xml_mini/rexml_engine_test.rb
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -22,14 +22,24 @@ class REXMLEngineTest < ActiveSupport::TestCase
morning
</root>
eoxml
- assert_equal_rexml(io)
+ hash = ActiveSupport::XmlMini.parse(io)
+ assert hash.has_key?('root')
+ assert hash['root'].has_key?('products')
+ assert_match "good", hash['root']['__content__']
+ products = hash['root']['products']
+ assert products.has_key?("__content__")
+ assert_match 'hello everyone', products['__content__']
+ end
+
+ def test_parse_from_empty_string
+ ActiveSupport::XmlMini.backend = 'REXML'
+ assert_equal({}, ActiveSupport::XmlMini.parse(""))
+ end
+
+ def test_parse_from_frozen_string
+ ActiveSupport::XmlMini.backend = 'REXML'
+ xml_string = "<root></root>".freeze
+ assert_equal({"root" => {}}, ActiveSupport::XmlMini.parse(xml_string))
end
- private
- def assert_equal_rexml(xml)
- parsed_xml = ActiveSupport::XmlMini.parse(xml)
- xml.rewind if xml.respond_to?(:rewind)
- hash = ActiveSupport::XmlMini.with_backend('REXML') { ActiveSupport::XmlMini.parse(xml) }
- assert_equal(hash, parsed_xml)
- end
end
diff --git a/ci/travis.rb b/ci/travis.rb
index 063c6acb07..b8891d6889 100755
--- a/ci/travis.rb
+++ b/ci/travis.rb
@@ -120,9 +120,9 @@ class Build
def env
if activesupport? && !isolated?
- # There is a known issue with the listen tests that casuses files to be
- # incorrectly GC'ed even when they are still in-use. The current is to
- # only run them in isolation to avoid randomly failing our test suite.
+ # There is a known issue with the listen tests that causes files to be
+ # incorrectly GC'ed even when they are still in-use. The current solution
+ # is to only run them in isolation to avoid randomly failing our test suite.
{ 'LISTEN' => '0' }
else
{}
@@ -148,6 +148,7 @@ ENV['GEM'].split(',').each do |gem|
next if ENV['TRAVIS_PULL_REQUEST'] && ENV['TRAVIS_PULL_REQUEST'] != 'false' && isolated
next if gem == 'railties' && isolated
next if gem == 'ac' && isolated
+ next if gem == 'ac:integration' && isolated
next if gem == 'aj:integration' && isolated
next if gem == 'guides' && isolated
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
index 4f4f27d6e4..2730d2dfea 100644
--- a/guides/CHANGELOG.md
+++ b/guides/CHANGELOG.md
@@ -1,46 +1,2 @@
-* Update example of passing a proc to `:message` option for validating records.
- This behavior was recently changed in [Pull Request #24199](https://github.com/rails/rails/pull/24119) to
- pass the object being validated as first argument to the `:message` proc,
- instead of the key of the field being validated.
-
- *Prathamesh Sonpatki*
-
-* Added new guide: Action Cable Overview.
-
- *David Kuhta*
-
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* No changes.
-
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* No changes.
-
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* Add code of conduct to contributing guide.
-
- *Jon Moss*
-
-* New section in Configuring: Configuring Active Job.
-
- *Eliot Sykes*
-
-* New section in Active Record Association Basics: Single Table Inheritance.
-
- *Andrey Nering*
-
-* New section in Active Record Querying: Understanding The Method Chaining.
-
- *Andrey Nering*
-
-* New section in Configuring: Search Engines Indexing.
-
- *Andrey Nering*
-
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/guides/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/guides/CHANGELOG.md) for previous changes.
diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb
index 58ba708a39..604cf4be41 100644
--- a/guides/bug_report_templates/action_controller_gem.rb
+++ b/guides/bug_report_templates/action_controller_gem.rb
@@ -8,7 +8,7 @@ end
gemfile(true) do
source 'https://rubygems.org'
# Activate the gem you are reporting the issue against.
- gem 'rails', '4.2.0'
+ gem 'rails', '5.0.0.rc1'
end
require 'rack/test'
diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb
index 09d6e7b331..d4ede4e2c1 100644
--- a/guides/bug_report_templates/active_record_gem.rb
+++ b/guides/bug_report_templates/active_record_gem.rb
@@ -8,7 +8,7 @@ end
gemfile(true) do
source 'https://rubygems.org'
# Activate the gem you are reporting the issue against.
- gem 'activerecord', '4.2.0'
+ gem 'activerecord', '5.0.0.rc1'
gem 'sqlite3'
end
diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb
index a86edd9121..6fd401bd50 100644
--- a/guides/bug_report_templates/active_record_master.rb
+++ b/guides/bug_report_templates/active_record_master.rb
@@ -20,10 +20,10 @@ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
- create_table :posts, force: true do |t|
+ create_table :posts, force: true do |t|
end
- create_table :comments, force: true do |t|
+ create_table :comments, force: true do |t|
t.integer :post_id
end
end
diff --git a/guides/bug_report_templates/generic_gem.rb b/guides/bug_report_templates/generic_gem.rb
index a4fe51156d..bb8af3be18 100644
--- a/guides/bug_report_templates/generic_gem.rb
+++ b/guides/bug_report_templates/generic_gem.rb
@@ -8,7 +8,7 @@ end
gemfile(true) do
source 'https://rubygems.org'
# Activate the gem you are reporting the issue against.
- gem 'activesupport', '4.2.0'
+ gem 'activesupport', '5.0.0.rc1'
end
require 'active_support/core_ext/object/blank'
diff --git a/guides/source/2_2_release_notes.md b/guides/source/2_2_release_notes.md
index 5e82af5a15..79634d8760 100644
--- a/guides/source/2_2_release_notes.md
+++ b/guides/source/2_2_release_notes.md
@@ -3,7 +3,7 @@
Ruby on Rails 2.2 Release Notes
===============================
-Rails 2.2 delivers a number of new and improved features. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/2-2-stable) in the main Rails repository on GitHub.
+Rails 2.2 delivers a number of new and improved features. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/2-2-stable) in the main Rails repository on GitHub.
Along with Rails, 2.2 marks the launch of the [Ruby on Rails Guides](http://guides.rubyonrails.org/), the first results of the ongoing [Rails Guides hackfest](http://hackfest.rubyonrails.org/guide). This site will deliver high-quality documentation of the major features of Rails.
@@ -22,7 +22,7 @@ Rails 2.2 supplies an easy system for internationalization (or i18n, for those o
* More information :
* [Official Rails i18 website](http://rails-i18n.org)
* [Finally. Ruby on Rails gets internationalized](https://web.archive.org/web/20140407075019/http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized)
- * [Localizing Rails : Demo application](http://github.com/clemens/i18n_demo_app)
+ * [Localizing Rails : Demo application](https://github.com/clemens/i18n_demo_app)
### Compatibility with Ruby 1.9 and JRuby
@@ -59,7 +59,7 @@ rake doc:guides
This will put the guides inside `Rails.root/doc/guides` and you may start surfing straight away by opening `Rails.root/doc/guides/index.html` in your favourite browser.
* Lead Contributors: [Rails Documentation Team](credits.html)
-* Major contributions from [Xavier Noria":http://advogato.org/person/fxn/diary.html and "Hongli Lai](http://izumi.plan99.net/blog/.)
+* Major contributions from [Xavier Noria](http://advogato.org/person/fxn/diary.html) and [Hongli Lai](http://izumi.plan99.net/blog/).
* More information:
* [Rails Guides hackfest](http://hackfest.rubyonrails.org/guide)
* [Help improve Rails documentation on Git branch](http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch)
@@ -146,7 +146,7 @@ development:
* Lead Contributor: [Nick Sieger](http://blog.nicksieger.com/)
* More information:
- * [What's New in Edge Rails: Connection Pools](http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-connection-pools)
+ * [What's New in Edge Rails: Connection Pools](http://archives.ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-connection-pools)
### Hashes for Join Table Conditions
@@ -166,7 +166,7 @@ Product.all(:joins => :photos, :conditions => { :photos => { :copyright => false
```
* More information:
- * [What's New in Edge Rails: Easy Join Table Conditions](http://ryandaigle.com/articles/2008/7/7/what-s-new-in-edge-rails-easy-join-table-conditions)
+ * [What's New in Edge Rails: Easy Join Table Conditions](http://archives.ryandaigle.com/articles/2008/7/7/what-s-new-in-edge-rails-easy-join-table-conditions)
### New Dynamic Finders
@@ -239,7 +239,7 @@ This will enable recognition of (among others) these routes:
* Lead Contributor: [S. Brent Faulkner](http://www.unwwwired.net/)
* More information:
* [Rails Routing from the Outside In](routing.html#nested-resources)
- * [What's New in Edge Rails: Shallow Routes](http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-shallow-routes)
+ * [What's New in Edge Rails: Shallow Routes](http://archives.ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-shallow-routes)
### Method Arrays for Member or Collection Routes
@@ -287,7 +287,7 @@ Action Mailer
Action Mailer now supports mailer layouts. You can make your HTML emails as pretty as your in-browser views by supplying an appropriately-named layout - for example, the `CustomerMailer` class expects to use `layouts/customer_mailer.html.erb`.
* More information:
- * [What's New in Edge Rails: Mailer Layouts](http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-mailer-layouts)
+ * [What's New in Edge Rails: Mailer Layouts](http://archives.ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-mailer-layouts)
Action Mailer now offers built-in support for GMail's SMTP servers, by turning on STARTTLS automatically. This requires Ruby 1.8.7 to be installed.
@@ -321,7 +321,7 @@ Other features of memoization include `unmemoize`, `unmemoize_all`, and `memoize
* Lead Contributor: [Josh Peek](http://joshpeek.com/)
* More information:
- * [What's New in Edge Rails: Easy Memoization](http://ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization)
+ * [What's New in Edge Rails: Easy Memoization](http://archives.ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization)
* [Memo-what? A Guide to Memoization](http://www.railway.at/articles/2008/09/20/a-guide-to-memoization)
### each_with_object
@@ -389,9 +389,9 @@ To avoid deployment issues and make Rails applications more self-contained, it's
You can unpack or install a single gem by specifying `GEM=_gem_name_` on the command line.
-* Lead Contributor: [Matt Jones](http://github.com/al2o3cr)
+* Lead Contributor: [Matt Jones](https://github.com/al2o3cr)
* More information:
- * [What's New in Edge Rails: Gem Dependencies](http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies)
+ * [What's New in Edge Rails: Gem Dependencies](http://archives.ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies)
* [Rails 2.1.2 and 2.2RC1: Update Your RubyGems](http://afreshcup.com/2008/10/25/rails-212-and-22rc1-update-your-rubygems/)
* [Detailed discussion on Lighthouse](http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1128)
@@ -411,7 +411,7 @@ Deprecated
A few pieces of older code are deprecated in this release:
* `Rails::SecretKeyGenerator` has been replaced by `ActiveSupport::SecureRandom`
-* `render_component` is deprecated. There's a [render_components plugin](http://github.com/rails/render_component/tree/master) available if you need this functionality.
+* `render_component` is deprecated. There's a [render_components plugin](https://github.com/rails/render_component/tree/master) available if you need this functionality.
* Implicit local assignments when rendering partials has been deprecated.
```ruby
diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md
index 0a62f34371..06761b67bb 100644
--- a/guides/source/2_3_release_notes.md
+++ b/guides/source/2_3_release_notes.md
@@ -3,7 +3,7 @@
Ruby on Rails 2.3 Release Notes
===============================
-Rails 2.3 delivers a variety of new and improved features, including pervasive Rack integration, refreshed support for Rails Engines, nested transactions for Active Record, dynamic and default scopes, unified rendering, more efficient routing, application templates, and quiet backtraces. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/2-3-stable) in the main Rails repository on GitHub or review the `CHANGELOG` files for the individual Rails components.
+Rails 2.3 delivers a variety of new and improved features, including pervasive Rack integration, refreshed support for Rails Engines, nested transactions for Active Record, dynamic and default scopes, unified rendering, more efficient routing, application templates, and quiet backtraces. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/2-3-stable) in the main Rails repository on GitHub or review the `CHANGELOG` files for the individual Rails components.
--------------------------------------------------------------------------------
@@ -125,14 +125,14 @@ Order.scoped_by_customer_id(12).scoped_by_status("open")
There's nothing to define to use dynamic scopes: they just work.
* Lead Contributor: [Yaroslav Markin](http://evilmartians.com/)
-* More Information: [What's New in Edge Rails: Dynamic Scope Methods](http://ryandaigle.com/articles/2008/12/29/what-s-new-in-edge-rails-dynamic-scope-methods.)
+* More Information: [What's New in Edge Rails: Dynamic Scope Methods](http://archives.ryandaigle.com/articles/2008/12/29/what-s-new-in-edge-rails-dynamic-scope-methods)
### Default Scopes
Rails 2.3 will introduce the notion of _default scopes_ similar to named scopes, but applying to all named scopes or find methods within the model. For example, you can write `default_scope :order => 'name ASC'` and any time you retrieve records from that model they'll come out sorted by name (unless you override the option, of course).
* Lead Contributor: Paweł Kondzior
-* More Information: [What's New in Edge Rails: Default Scoping](http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping)
+* More Information: [What's New in Edge Rails: Default Scoping](http://archives.ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping)
### Batch Processing
@@ -158,7 +158,7 @@ Note that you should only use this method for batch processing: for small number
* More Information (at that point the convenience method was called just `each`):
* [Rails 2.3: Batch Finding](http://afreshcup.com/2009/02/23/rails-23-batch-finding/)
- * [What's New in Edge Rails: Batched Find](http://ryandaigle.com/articles/2009/2/23/what-s-new-in-edge-rails-batched-find)
+ * [What's New in Edge Rails: Batched Find](http://archives.ryandaigle.com/articles/2009/2/23/what-s-new-in-edge-rails-batched-find)
### Multiple Conditions for Callbacks
@@ -179,7 +179,7 @@ developers = Developer.find(:all, :group => "salary",
:having => "sum(salary) > 10000", :select => "salary")
```
-* Lead Contributor: [Emilio Tagua](http://github.com/miloops)
+* Lead Contributor: [Emilio Tagua](https://github.com/miloops)
### Reconnecting MySQL Connections
@@ -235,7 +235,7 @@ If you're one of the people who has always been bothered by the special-case nam
* More Information:
* [The Death of Application.rb](http://afreshcup.com/2008/11/17/rails-2x-the-death-of-applicationrb/)
- * [What's New in Edge Rails: Application.rb Duality is no More](http://ryandaigle.com/articles/2008/11/19/what-s-new-in-edge-rails-application-rb-duality-is-no-more)
+ * [What's New in Edge Rails: Application.rb Duality is no More](http://archives.ryandaigle.com/articles/2008/11/19/what-s-new-in-edge-rails-application-rb-duality-is-no-more)
### HTTP Digest Authentication Support
@@ -261,7 +261,7 @@ end
```
* Lead Contributor: [Gregg Kellogg](http://www.kellogg-assoc.com/)
-* More Information: [What's New in Edge Rails: HTTP Digest Authentication](http://ryandaigle.com/articles/2009/1/30/what-s-new-in-edge-rails-http-digest-authentication)
+* More Information: [What's New in Edge Rails: HTTP Digest Authentication](http://archives.ryandaigle.com/articles/2009/1/30/what-s-new-in-edge-rails-http-digest-authentication)
### More Efficient Routing
@@ -377,8 +377,8 @@ You can write this view in Rails 2.3:
* Lead Contributor: [Eloy Duran](http://superalloy.nl/)
* More Information:
* [Nested Model Forms](http://weblog.rubyonrails.org/2009/1/26/nested-model-forms)
- * [complex-form-examples](http://github.com/alloy/complex-form-examples)
- * [What's New in Edge Rails: Nested Object Forms](http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes)
+ * [complex-form-examples](https://github.com/alloy/complex-form-examples)
+ * [What's New in Edge Rails: Nested Object Forms](http://archives.ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes)
### Smart Rendering of Partials
@@ -394,7 +394,7 @@ render @article
render @articles
```
-* More Information: [What's New in Edge Rails: render Stops Being High-Maintenance](http://ryandaigle.com/articles/2008/11/20/what-s-new-in-edge-rails-render-stops-being-high-maintenance)
+* More Information: [What's New in Edge Rails: render Stops Being High-Maintenance](http://archives.ryandaigle.com/articles/2008/11/20/what-s-new-in-edge-rails-render-stops-being-high-maintenance)
### Prompts for Date Select Helpers
@@ -421,7 +421,7 @@ You're likely familiar with Rails' practice of adding timestamps to static asset
Asset hosts get more flexible in edge Rails with the ability to declare an asset host as a specific object that responds to a call. This allows you to implement any complex logic you need in your asset hosting.
-* More Information: [asset-hosting-with-minimum-ssl](http://github.com/dhh/asset-hosting-with-minimum-ssl/tree/master)
+* More Information: [asset-hosting-with-minimum-ssl](https://github.com/dhh/asset-hosting-with-minimum-ssl/tree/master)
### grouped_options_for_select Helper Method
@@ -498,7 +498,7 @@ Active Support has a few interesting changes, including the introduction of `Obj
A lot of folks have adopted the notion of using try() to attempt operations on objects. It's especially helpful in views where you can avoid nil-checking by writing code like `<%= @person.try(:name) %>`. Well, now it's baked right into Rails. As implemented in Rails, it raises `NoMethodError` on private methods and always returns `nil` if the object is nil.
-* More Information: [try()](http://ozmm.org/posts/try.html.)
+* More Information: [try()](http://ozmm.org/posts/try.html)
### Object#tap Backport
@@ -533,7 +533,7 @@ If you look up the spec on the "json.org" site, you'll discover that all keys in
### Other Active Support Changes
* You can use `Enumerable#none?` to check that none of the elements match the supplied block.
-* If you're using Active Support [delegates](http://afreshcup.com/2008/10/19/coming-in-rails-22-delegate-prefixes/,) the new `:allow_nil` option lets you return `nil` instead of raising an exception when the target object is nil.
+* If you're using Active Support [delegates](http://afreshcup.com/2008/10/19/coming-in-rails-22-delegate-prefixes/) the new `:allow_nil` option lets you return `nil` instead of raising an exception when the target object is nil.
* `ActiveSupport::OrderedHash`: now implements `each_key` and `each_value`.
* `ActiveSupport::MessageEncryptor` provides a simple way to encrypt information for storage in an untrusted location (like cookies).
* Active Support's `from_xml` no longer depends on XmlSimple. Instead, Rails now includes its own XmlMini implementation, with just the functionality that it requires. This lets Rails dispense with the bundled copy of XmlSimple that it's been carting around.
@@ -555,11 +555,11 @@ Rails Metal is a new mechanism that provides superfast endpoints inside of your
* [Introducing Rails Metal](http://weblog.rubyonrails.org/2008/12/17/introducing-rails-metal)
* [Rails Metal: a micro-framework with the power of Rails](http://soylentfoo.jnewland.com/articles/2008/12/16/rails-metal-a-micro-framework-with-the-power-of-rails-m)
* [Metal: Super-fast Endpoints within your Rails Apps](http://www.railsinside.com/deployment/180-metal-super-fast-endpoints-within-your-rails-apps.html)
- * [What's New in Edge Rails: Rails Metal](http://ryandaigle.com/articles/2008/12/18/what-s-new-in-edge-rails-rails-metal)
+ * [What's New in Edge Rails: Rails Metal](http://archives.ryandaigle.com/articles/2008/12/18/what-s-new-in-edge-rails-rails-metal)
### Application Templates
-Rails 2.3 incorporates Jeremy McAnally's [rg](http://github.com/jeremymcanally/rg/tree/master) application generator. What this means is that we now have template-based application generation built right into Rails; if you have a set of plugins you include in every application (among many other use cases), you can just set up a template once and use it over and over again when you run the `rails` command. There's also a rake task to apply a template to an existing application:
+Rails 2.3 incorporates Jeremy McAnally's [rg](https://github.com/jm/rg) application generator. What this means is that we now have template-based application generation built right into Rails; if you have a set of plugins you include in every application (among many other use cases), you can just set up a template once and use it over and over again when you run the `rails` command. There's also a rake task to apply a template to an existing application:
```
rake rails:template LOCATION=~/template.rb
@@ -572,7 +572,7 @@ This will layer the changes from the template on top of whatever code the projec
### Quieter Backtraces
-Building on Thoughtbot's [Quiet Backtrace](https://github.com/thoughtbot/quietbacktrace) plugin, which allows you to selectively remove lines from `Test::Unit` backtraces, Rails 2.3 implements `ActiveSupport::BacktraceCleaner` and `Rails::BacktraceCleaner` in core. This supports both filters (to perform regex-based substitutions on backtrace lines) and silencers (to remove backtrace lines entirely). Rails automatically adds silencers to get rid of the most common noise in a new application, and builds a `config/backtrace_silencers.rb` file to hold your own additions. This feature also enables prettier printing from any gem in the backtrace.
+Building on thoughtbot's [Quiet Backtrace](https://github.com/thoughtbot/quietbacktrace) plugin, which allows you to selectively remove lines from `Test::Unit` backtraces, Rails 2.3 implements `ActiveSupport::BacktraceCleaner` and `Rails::BacktraceCleaner` in core. This supports both filters (to perform regex-based substitutions on backtrace lines) and silencers (to remove backtrace lines entirely). Rails automatically adds silencers to get rid of the most common noise in a new application, and builds a `config/backtrace_silencers.rb` file to hold your own additions. This feature also enables prettier printing from any gem in the backtrace.
### Faster Boot Time in Development Mode with Lazy Loading/Autoload
@@ -605,8 +605,8 @@ Deprecated
A few pieces of older code are deprecated in this release:
-* If you're one of the (fairly rare) Rails developers who deploys in a fashion that depends on the inspector, reaper, and spawner scripts, you'll need to know that those scripts are no longer included in core Rails. If you need them, you'll be able to pick up copies via the [irs_process_scripts](http://github.com/rails/irs_process_scripts/tree) plugin.
-* `render_component` goes from "deprecated" to "nonexistent" in Rails 2.3. If you still need it, you can install the [render_component plugin](http://github.com/rails/render_component/tree/master).
+* If you're one of the (fairly rare) Rails developers who deploys in a fashion that depends on the inspector, reaper, and spawner scripts, you'll need to know that those scripts are no longer included in core Rails. If you need them, you'll be able to pick up copies via the [irs_process_scripts](https://github.com/rails/irs_process_scripts/tree) plugin.
+* `render_component` goes from "deprecated" to "nonexistent" in Rails 2.3. If you still need it, you can install the [render_component plugin](https://github.com/rails/render_component/tree/master).
* Support for Rails components has been removed.
* If you were one of the people who got used to running `script/performance/request` to look at performance based on integration tests, you need to learn a new trick: that script has been removed from core Rails now. There's a new request_profiler plugin that you can install to get the exact same functionality back.
* `ActionController::Base#session_enabled?` is deprecated because sessions are lazy-loaded now.
@@ -614,7 +614,7 @@ A few pieces of older code are deprecated in this release:
* Some integration test helpers have been removed. `response.headers["Status"]` and `headers["Status"]` will no longer return anything. Rack does not allow "Status" in its return headers. However you can still use the `status` and `status_message` helpers. `response.headers["cookie"]` and `headers["cookie"]` will no longer return any CGI cookies. You can inspect `headers["Set-Cookie"]` to see the raw cookie header or use the `cookies` helper to get a hash of the cookies sent to the client.
* `formatted_polymorphic_url` is deprecated. Use `polymorphic_url` with `:format` instead.
* The `:http_only` option in `ActionController::Response#set_cookie` has been renamed to `:httponly`.
-* The `:connector` and `:skip_last_comma` options of `to_sentence` have been replaced by `:words_connnector`, `:two_words_connector`, and `:last_word_connector` options.
+* The `:connector` and `:skip_last_comma` options of `to_sentence` have been replaced by `:words_connector`, `:two_words_connector`, and `:last_word_connector` options.
* Posting a multipart form with an empty `file_field` control used to submit an empty string to the controller. Now it submits a nil, due to differences between Rack's multipart parser and the old Rails one.
Credits
diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md
index 70b0e89799..49d37ba489 100644
--- a/guides/source/3_0_release_notes.md
+++ b/guides/source/3_0_release_notes.md
@@ -17,7 +17,7 @@ Even if you don't give a hoot about any of our internal cleanups, Rails 3.0 is g
On top of all that, we've tried our best to deprecate the old APIs with nice warnings. That means that you can move your existing application to Rails 3 without immediately rewriting all your old code to the latest best practices.
-These release notes cover the major upgrades, but don't include every little bug fix and change. Rails 3.0 consists of almost 4,000 commits by more than 250 authors! If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/3-0-stable) in the main Rails repository on GitHub.
+These release notes cover the major upgrades, but don't include every little bug fix and change. Rails 3.0 consists of almost 4,000 commits by more than 250 authors! If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/3-0-stable) in the main Rails repository on GitHub.
--------------------------------------------------------------------------------
@@ -63,7 +63,7 @@ The `config.gem` method is gone and has been replaced by using `bundler` and a `
### Upgrade Process
-To help with the upgrade process, a plugin named [Rails Upgrade](http://github.com/rails/rails_upgrade) has been created to automate part of it.
+To help with the upgrade process, a plugin named [Rails Upgrade](https://github.com/rails/rails_upgrade) has been created to automate part of it.
Simply install the plugin, then run `rake rails:upgrade:check` to check your app for pieces that need to be updated (with links to information on how to update them). It also offers a task to generate a `Gemfile` based on your current `config.gem` calls and a task to generate a new routes file from your current one. To get the plugin, simply run the following:
@@ -86,7 +86,7 @@ $ cd myapp
### Vendoring Gems
-Rails now uses a `Gemfile` in the application root to determine the gems you require for your application to start. This `Gemfile` is processed by the [Bundler](http://github.com/bundler/bundler,) which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems.
+Rails now uses a `Gemfile` in the application root to determine the gems you require for your application to start. This `Gemfile` is processed by the [Bundler](https://github.com/bundler/bundler) which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems.
More information: - [bundler homepage](http://bundler.io/)
@@ -138,14 +138,14 @@ More Information: - [Rails Edge Architecture](http://yehudakatz.com/2009/06/11/r
### Arel Integration
-[Arel](http://github.com/brynary/arel) (or Active Relation) has been taken on as the underpinnings of Active Record and is now required for Rails. Arel provides an SQL abstraction that simplifies out Active Record and provides the underpinnings for the relation functionality in Active Record.
+[Arel](https://github.com/brynary/arel) (or Active Relation) has been taken on as the underpinnings of Active Record and is now required for Rails. Arel provides an SQL abstraction that simplifies out Active Record and provides the underpinnings for the relation functionality in Active Record.
More information: - [Why I wrote Arel](https://web.archive.org/web/20120718093140/http://magicscalingsprinkles.wordpress.com/2010/01/28/why-i-wrote-arel/)
### Mail Extraction
-Action Mailer ever since its beginnings has had monkey patches, pre parsers and even delivery and receiver agents, all in addition to having TMail vendored in the source tree. Version 3 changes that with all email message related functionality abstracted out to the [Mail](http://github.com/mikel/mail) gem. This again reduces code duplication and helps create definable boundaries between Action Mailer and the email parser.
+Action Mailer ever since its beginnings has had monkey patches, pre parsers and even delivery and receiver agents, all in addition to having TMail vendored in the source tree. Version 3 changes that with all email message related functionality abstracted out to the [Mail](https://github.com/mikel/mail) gem. This again reduces code duplication and helps create definable boundaries between Action Mailer and the email parser.
More information: - [New Action Mailer API in Rails 3](http://lindsaar.net/2010/1/26/new-actionmailer-api-in-rails-3)
@@ -161,7 +161,7 @@ More Information: - [Rails Documentation Projects](http://weblog.rubyonrails.org
Internationalization
--------------------
-A large amount of work has been done with I18n support in Rails 3, including the latest [I18n](http://github.com/svenfuchs/i18n) gem supplying many speed improvements.
+A large amount of work has been done with I18n support in Rails 3, including the latest [I18n](https://github.com/svenfuchs/i18n) gem supplying many speed improvements.
* I18n for any object - I18n behavior can be added to any object by including `ActiveModel::Translation` and `ActiveModel::Validations`. There is also an `errors.messages` fallback for translations.
* Attributes can have default translations.
@@ -309,7 +309,7 @@ More Information:
Major re-write was done in the Action View helpers, implementing Unobtrusive JavaScript (UJS) hooks and removing the old inline AJAX commands. This enables Rails to use any compliant UJS driver to implement the UJS hooks in the helpers.
-What this means is that all previous `remote_<method>` helpers have been removed from Rails core and put into the [Prototype Legacy Helper](http://github.com/rails/prototype_legacy_helper). To get UJS hooks into your HTML, you now pass `:remote => true` instead. For example:
+What this means is that all previous `remote_<method>` helpers have been removed from Rails core and put into the [Prototype Legacy Helper](https://github.com/rails/prototype_legacy_helper). To get UJS hooks into your HTML, you now pass `:remote => true` instead. For example:
```ruby
form_for @post, :remote => true
@@ -575,7 +575,7 @@ The following methods have been removed because they are no longer used in the f
Action Mailer
-------------
-Action Mailer has been given a new API with TMail being replaced out with the new [Mail](http://github.com/mikel/mail) as the email library. Action Mailer itself has been given an almost complete re-write with pretty much every line of code touched. The result is that Action Mailer now simply inherits from Abstract Controller and wraps the Mail gem in a Rails DSL. This reduces the amount of code and duplication of other libraries in Action Mailer considerably.
+Action Mailer has been given a new API with TMail being replaced out with the new [Mail](https://github.com/mikel/mail) as the email library. Action Mailer itself has been given an almost complete re-write with pretty much every line of code touched. The result is that Action Mailer now simply inherits from Abstract Controller and wraps the Mail gem in a Rails DSL. This reduces the amount of code and duplication of other libraries in Action Mailer considerably.
* All mailers are now in `app/mailers` by default.
* Can now send email using new API with three methods: `attachments`, `headers` and `mail`.
diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md
index 9c07a2f686..638547565c 100644
--- a/guides/source/5_0_release_notes.md
+++ b/guides/source/5_0_release_notes.md
@@ -48,7 +48,74 @@ ToDo...
### Active Record attributes API
-ToDo...
+Defines an attribute with a type on a model. It will override the type of existing attributes if needed.
+This allows control over how values are converted to and from SQL when assigned to a model.
+It also changes the behavior of values passed to ActiveRecord::Base.where, which lets use our domain objects across much of Active Record,
+without having to rely on implementation details or monkey patching.
+
+Some things that you can achieve with this:
+* The type detected by Active Record can be overridden.
+* A default can also be provided.
+* Attributes do not need to be backed by a database column.
+
+```ruby
+
+# db/schema.rb
+create_table :store_listings, force: true do |t|
+ t.decimal :price_in_cents
+ t.string :my_string, default: "original default"
+end
+
+# app/models/store_listing.rb
+class StoreListing < ActiveRecord::Base
+end
+
+store_listing = StoreListing.new(price_in_cents: '10.1')
+
+# before
+store_listing.price_in_cents # => BigDecimal.new(10.1)
+StoreListing.new.my_string # => "original default"
+
+class StoreListing < ActiveRecord::Base
+ attribute :price_in_cents, :integer # custom type
+ attribute :my_string, :string, default: "new default" # default value
+ attribute :my_default_proc, :datetime, default: -> { Time.now } # default value
+ attribute :field_without_db_column, :integer, array: true
+end
+
+# after
+store_listing.price_in_cents # => 10
+StoreListing.new.my_string # => "new default"
+StoreListing.new.my_default_proc # => 2015-05-30 11:04:48 -0600
+model = StoreListing.new(field_without_db_column: ["1", "2", "3"])
+model.attributes #=> {field_without_db_column: [1, 2, 3]}
+```
+
+**Creating Custom Types:**
+
+You can define your own custom types, as long as they respond
+to the methods defined on the value type. The method +deserialize+ or
++cast+ will be called on your type object, with raw input from the
+database or from your controllers. This is useful, for example, when doing custom conversion,
+like Money data.
+
+**Querying:**
+
+When `ActiveRecord::Base.where` is called, it will
+use the type defined by the model class to convert the value to SQL,
+calling +serialize+ on your type object.
+
+This gives the objects ability to specify, how to convert values when performing SQL queries.
+
+**Dirty Tracking:**
+
+The type of an attribute is given the opportunity to change how dirty
+tracking is performed.
+
+See its
+[documentation](http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html)
+for a detailed write up.
+
### Test Runner
[Pull Request](https://github.com/rails/rails/pull/19216)
@@ -230,6 +297,9 @@ Please refer to the [Changelog][action-pack] for detailed changes.
* `ActionDispatch::IntegrationTest` and `ActionController::TestCase` deprecate positional arguments in favor of
keyword arguments. ([Pull Request](https://github.com/rails/rails/pull/18323))
+* Deprecated `:controller` and `:action` path parameters.
+ ([Pull Request](https://github.com/rails/rails/pull/23980))
+
### Notable changes
* Added `ActionController::Renderer` to render arbitrary templates
@@ -297,6 +367,10 @@ Please refer to the [Changelog][action-pack] for detailed changes.
([Pull Request](https://github.com/rails/rails/pull/23827))
+* Add `ActionController#helpers` to get access to the view context
+ at the controller level.
+ ([Pull Request](https://github.com/rails/rails/pull/24866))
+
Action View
-------------
@@ -316,12 +390,19 @@ Please refer to the [Changelog][action-view] for detailed changes.
supported by I18n.
([Pull Request](https://github.com/rails/rails/pull/20019))
+### Deprecations
+
+* Deprecated `datetime_field` and `datetime_field_tag` helpers.
+ Datetime input type was removed from HTML specification.
+ One can use `datetime_local_field` and `datetime_local_field_tag` instead.
+ ([Pull Request](https://github.com/rails/rails/pull/24385))
+
### Notable Changes
* Changed the default template handler from `ERB` to `Raw`.
([commit](https://github.com/rails/rails/commit/4be859f0fdf7b3059a28d03c279f03f5938efc80))
-* Collection rendering can cache and fetches multiple partials.
+* Collection rendering can cache and fetches multiple partials at once.
([Pull Request](https://github.com/rails/rails/pull/18948),
[commit](https://github.com/rails/rails/commit/e93f0f0f133717f9b06b1eaefd3442bd0ff43985))
@@ -332,10 +413,6 @@ Please refer to the [Changelog][action-view] for detailed changes.
button on submit to prevent double submits.
([Pull Request](https://github.com/rails/rails/pull/21135))
-* Collection rendering can cache and fetch multiple partials at once.
- ([Pull Request](https://github.com/rails/rails/pull/21135))
-
-
Action Mailer
-------------
@@ -487,6 +564,10 @@ Please refer to the [Changelog][active-record] for detailed changes.
Use the `{insert|update|delete}` public methods instead.
([Pull Request](https://github.com/rails/rails/pull/23086))
+* Deprecated `use_transactional_fixtures` in favor of
+ `use_transactional_tests` for more clarity.
+ ([Pull Request](https://github.com/rails/rails/pull/19282))
+
### Notable changes
* Added a `foreign_key` option to `references` while creating the table.
@@ -495,16 +576,13 @@ Please refer to the [Changelog][active-record] for detailed changes.
* New attributes
API. ([commit](https://github.com/rails/rails/commit/8c752c7ac739d5a86d4136ab1e9d0142c4041e58))
-* Added `:enum_prefix`/`:enum_suffix` option to `enum`
- definition. ([Pull Request](https://github.com/rails/rails/pull/19813))
+* Added `:_prefix`/`:_suffix` option to `enum` definition.
+ ([Pull Request](https://github.com/rails/rails/pull/19813),
+ [Pull Request](https://github.com/rails/rails/pull/20999))
* Added `#cache_key` to `ActiveRecord::Relation`.
([Pull Request](https://github.com/rails/rails/pull/20884))
-* Require `belongs_to` by default.
- ([Pull Request](https://github.com/rails/rails/pull/18937)) - Deprecate
- `required` option in favor of `optional` for `belongs_to`
-
* Changed the default `null` value for `timestamps` to `false`.
([commit](https://github.com/rails/rails/commit/a939506f297b667291480f26fa32a373a18ae06a))
@@ -533,7 +611,8 @@ Please refer to the [Changelog][active-record] for detailed changes.
* `belongs_to` will now trigger a validation error by default if the
association is not present. You can turn this off on a per-association basis
- with `optional: true`.
+ with `optional: true`. Also deprecate `required` option in favor of `optional`
+ for `belongs_to`.
([Pull Request](https://github.com/rails/rails/pull/18937))
* Added `config.active_record.dump_schemas` to configure the behavior of
@@ -655,11 +734,15 @@ Please refer to the [Changelog][active-job] for detailed changes.
### Notable changes
-* `ActiveJob::Base.deserialize` delegates to the job class. this allows jobs
+* `ActiveJob::Base.deserialize` delegates to the job class. This allows jobs
to attach arbitrary metadata when they get serialized and read it back when
they get performed.
([Pull Request](https://github.com/rails/rails/pull/18260))
+* Add ability to configure the queue adapter on a per job basis without
+ affecting each other.
+ ([Pull Request](https://github.com/rails/rails/pull/16992))
+
* A generated job now inherits from `app/jobs/application_job.rb` by default.
([Pull Request](https://github.com/rails/rails/pull/19034))
@@ -802,7 +885,7 @@ Please refer to the [Changelog][active-support] for detailed changes.
([commit](https://github.com/rails/rails/commit/a5e507fa0b8180c3d97458a9b86c195e9857d8f6))
* Added `Integer#positive?` and `Integer#negative?` query methods
- in the vein of `Fixnum#zero?`.
+ in the vein of `Integer#zero?`.
([commit](https://github.com/rails/rails/commit/e54277a45da3c86fecdfa930663d7692fd083daa))
* Added a bang version to `ActiveSupport::OrderedOptions` get methods which will raise
diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md
index 0c486bb96c..c7770c16ea 100644
--- a/guides/source/action_cable_overview.md
+++ b/guides/source/action_cable_overview.md
@@ -12,41 +12,39 @@ After reading this guide, you will know:
Introduction
------------
-Action Cable seamlessly integrates WebSockets with the rest of your Rails application.
-It allows for real-time features to be written in Ruby in the same style and form as
-the rest of your Rails application, while still being performant and scalable. It's
-a full-stack offering that provides both a client-side JavaScript framework and a
-server-side Ruby framework. You have access to your full domain model written with
-Active Record or your ORM of choice.
+Action Cable seamlessly integrates
+[WebSockets](https://en.wikipedia.org/wiki/WebSocket) with the rest of your
+Rails application. It allows for real-time features to be written in Ruby in the
+same style and form as the rest of your Rails application, while still being
+performant and scalable. It's a full-stack offering that provides both a
+client-side JavaScript framework and a server-side Ruby framework. You have
+access to your full domain model written with Active Record or your ORM of
+choice.
What is Pub/Sub
---------------
-Pub/Sub, or Publish-Subscribe, refers to a message queue paradigm whereby senders
-of information (publishers), send data to an abstract class of recipients (subscribers),
-without specifying individual recipients. Action Cable uses this approach to communicate
-between the server and many clients.
-
-What is Action Cable
---------------------
-
-Action Cable is a server which can handle multiple connection instances, with one
-client-server connection instance established per WebSocket connection.
+[Pub/Sub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern), or
+Publish-Subscribe, refers to a message queue paradigm whereby senders of
+information (publishers), send data to an abstract class of recipients
+(subscribers), without specifying individual recipients. Action Cable uses this
+approach to communicate between the server and many clients.
## Server-Side Components
### Connections
-Connections form the foundation of the client-server relationship. For every WebSocket
-the cable server is accepting, a Connection object will be instantiated on the server side.
-This instance becomes the parent of all the channel subscriptions that are created from there on.
-The Connection itself does not deal with any specific application logic beyond authentication
-and authorization. The client of a WebSocket connection is called a consumer. An individual
-user will create one consumer-connection pair per browser tab, window, or device they have open.
+*Connections* form the foundation of the client-server relationship. For every
+WebSocket accepted by the server, a connection object is instantiated. This
+object becomes the parent of all the *channel subscriptions* that are created
+from there on. The connection itself does not deal with any specific application
+logic beyond authentication and authorization. The client of a WebSocket
+connection is called the connection *consumer*. An individual user will create
+one consumer-connection pair per browser tab, window, or device they have open.
-Connections are instantiated via the `ApplicationCable::Connection` class in Ruby.
-In this class, you authorize the incoming connection, and proceed to establish it
-if the user can be identified.
+Connections are instances of `ApplicationCable::Connection`. In this class, you
+authorize the incoming connection, and proceed to establish it if the user can
+be identified.
#### Connection Setup
@@ -73,24 +71,24 @@ end
```
Here `identified_by` is a connection identifier that can be used to find the
-specific connection later. Note that anything marked as an identifier will automatically
+specific connection later. Note that anything marked as an identifier will automatically
create a delegate by the same name on any channel instances created off the connection.
This example relies on the fact that you will already have handled authentication of the user
-somewhere else in your application, and that a successful authentication sets a signed
-cookie with the `user_id`.
+somewhere else in your application, and that a successful authentication sets a signed
+cookie with the user ID.
The cookie is then automatically sent to the connection instance when a new connection
is attempted, and you use that to set the `current_user`. By identifying the connection
-by this same current_user, you're also ensuring that you can later retrieve all open
+by this same current user, you're also ensuring that you can later retrieve all open
connections by a given user (and potentially disconnect them all if the user is deleted
or deauthorized).
### Channels
-A channel encapsulates a logical unit of work, similar to what a controller does in a
-regular MVC setup. By default, Rails creates a parent `ApplicationCable::Channel` class
-for encapsulating shared logic between your channels.
+A *channel* encapsulates a logical unit of work, similar to what a controller does in a
+regular MVC setup. By default, Rails creates a parent `ApplicationCable::Channel` class
+for encapsulating shared logic between your channels.
#### Parent Channel Setup
@@ -103,7 +101,7 @@ end
```
Then you would create your own channel classes. For example, you could have a
-**ChatChannel** and an **AppearanceChannel**:
+`ChatChannel` and an `AppearanceChannel`:
```ruby
# app/channels/chat_channel.rb
@@ -119,15 +117,15 @@ A consumer could then be subscribed to either or both of these channels.
#### Subscriptions
-When a consumer is subscribed to a channel, they act as a subscriber;
-This connection is called a subscription.
-Incoming messages are then routed to these channel subscriptions based on
-an identifier sent by the cable consumer.
+Consumers subscribe to channels, acting as *subscribers*. Their connection is
+called a *subscription*. Produced messages are then routed to these channel
+subscriptions based on an identifier sent by the cable consumer.
```ruby
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
- # Called when the consumer has successfully become a subscriber of this channel
+ # Called when the consumer has successfully
+ # become a subscriber of this channel.
def subscribed
end
end
@@ -138,30 +136,30 @@ end
### Connections
Consumers require an instance of the connection on their side. This can be
-established using the following Javascript, which is generated by default in Rails:
+established using the following JavaScript, which is generated by default by Rails:
#### Connect Consumer
-```coffeescript
-# app/assets/javascripts/cable.coffee
-#= require action_cable
+```js
+// app/assets/javascripts/cable.js
+//= require action_cable
+//= require_self
+//= require_tree ./channels
-@App = {}
-App.cable = ActionCable.createConsumer()
+(function() {
+ this.App || (this.App = {});
+
+ App.cable = ActionCable.createConsumer();
+}).call(this);
```
-This will ready a consumer that'll connect against /cable on your server by default.
+This will ready a consumer that'll connect against `/cable` on your server by default.
The connection won't be established until you've also specified at least one subscription
you're interested in having.
#### Subscriber
-When a consumer is subscribed to a channel, they act as a subscriber. A
-consumer can act as a subscriber to a given channel any number of times.
-For example, a consumer could subscribe to multiple chat rooms at the same time.
-(remember that a physical user may have multiple consumers, one per tab/device open to your connection).
-
-A consumer becomes a subscriber, by creating a subscription to a given channel:
+A consumer becomes a subscriber by creating a subscription to a given channel:
```coffeescript
# app/assets/javascripts/cable/subscriptions/chat.coffee
@@ -174,12 +172,20 @@ App.cable.subscriptions.create { channel: "AppearanceChannel" }
While this creates the subscription, the functionality needed to respond to
received data will be described later on.
+A consumer can act as a subscriber to a given channel any number of times. For
+example, a consumer could subscribe to multiple chat rooms at the same time:
+
+```coffeescript
+App.cable.subscriptions.create { channel: "ChatChannel", room: "1st Room" }
+App.cable.subscriptions.create { channel: "ChatChannel", room: "2nd Room" }
+```
+
## Client-Server Interactions
### Streams
-Streams provide the mechanism by which channels route published content
-(broadcasts) to its subscribers.
+*Streams* provide the mechanism by which channels route published content
+(broadcasts) to their subscribers.
```ruby
# app/channels/chat_channel.rb
@@ -203,20 +209,30 @@ class CommentsChannel < ApplicationCable::Channel
end
```
-You can then broadcast to this channel using: `CommentsChannel.broadcast_to(@post, @comment)`
+You can then broadcast to this channel like this:
-### Broadcastings
+```ruby
+CommentsChannel.broadcast_to(@post, @comment)
+```
-A broadcasting is a pub/sub link where anything transmitted by a publisher
+### Broadcasting
+
+A *broadcasting* is a pub/sub link where anything transmitted by a publisher
is routed directly to the channel subscribers who are streaming that named
broadcasting. Each channel can be streaming zero or more broadcastings.
-Broadcastings are purely an online queue and time dependent;
-If a consumer is not streaming (subscribed to a given channel), they'll not
-get the broadcast should they connect later.
+
+Broadcastings are purely an online queue and time dependent. If a consumer is
+not streaming (subscribed to a given channel), they'll not get the broadcast
+should they connect later.
Broadcasts are called elsewhere in your Rails application:
+
```ruby
- WebNotificationsChannel.broadcast_to current_user, title: 'New things!', body: 'All the news fit to print'
+WebNotificationsChannel.broadcast_to(
+ current_user,
+ title: 'New things!',
+ body: 'All the news fit to print'
+)
```
The `WebNotificationsChannel.broadcast_to` call places a message in the current
@@ -225,14 +241,14 @@ broadcasting name for each user. For a user with an ID of 1, the broadcasting
name would be `web_notifications_1`.
The channel has been instructed to stream everything that arrives at
-`web_notifications_1` directly to the client by invoking the `#received(data)`
+`web_notifications_1` directly to the client by invoking the `received`
callback.
### Subscriptions
-When a consumer is subscribed to a channel, they act as a subscriber;
-This connection is called a subscription. Incoming messages are then routed
-to these channel subscriptions based on an identifier sent by the cable consumer.
+When a consumer is subscribed to a channel, they act as a subscriber. This
+connection is called a subscription. Incoming messages are then routed to
+these channel subscriptions based on an identifier sent by the cable consumer.
```coffeescript
# app/assets/javascripts/cable/subscriptions/chat.coffee
@@ -254,10 +270,10 @@ App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" },
"""
```
-### Passing Parameters to Channel
+### Passing Parameters to Channels
-You can pass parameters from the client-side to the server-side when
-creating a subscription. For example:
+You can pass parameters from the client side to the server side when creating a
+subscription. For example:
```ruby
# app/channels/chat_channel.rb
@@ -268,8 +284,8 @@ class ChatChannel < ApplicationCable::Channel
end
```
-Pass an object as the first argument to `subscriptions.create`, and that object
-will become your params hash in your cable channel. The keyword `channel` is required.
+An object passed as the first argument to `subscriptions.create` becomes the
+params hash in the cable channel. The keyword `channel` is required:
```coffeescript
# app/assets/javascripts/cable/subscriptions/chat.coffee
@@ -291,14 +307,18 @@ App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" },
```
```ruby
-# Somewhere in your app this is called, perhaps from a NewCommentJob
-ChatChannel.broadcast_to "chat_#{room}", sent_by: 'Paul', body: 'This is a cool chat app.'
+# Somewhere in your app this is called, perhaps
+# from a NewCommentJob.
+ChatChannel.broadcast_to(
+ "chat_#{room}",
+ sent_by: 'Paul',
+ body: 'This is a cool chat app.'
+)
```
+### Rebroadcasting a Message
-### Rebroadcasting message
-
-A common use case is to rebroadcast a message sent by one client to any
+A common use case is to *rebroadcast* a message sent by one client to any
other connected clients.
```ruby
@@ -309,7 +329,7 @@ class ChatChannel < ApplicationCable::Channel
end
def receive(data)
- ChatChannel.broadcast_to "chat_#{params[:room]}", data
+ ChatChannel.broadcast_to("chat_#{params[:room]}", data)
end
end
```
@@ -327,20 +347,21 @@ The rebroadcast will be received by all connected clients, _including_ the
client that sent the message. Note that params are the same as they were when
you subscribed to the channel.
-## Full-stack examples
+## Full-Stack Examples
The following setup steps are common to both examples:
- 1. [Setup your connection](#connection-setup)
- 2. [Setup your parent channel](#parent-channel-setup)
- 3. [Connect your consumer](#connect-consumer)
+ 1. [Setup your connection](#connection-setup).
+ 2. [Setup your parent channel](#parent-channel-setup).
+ 3. [Connect your consumer](#connect-consumer).
+
+### Example 1: User Appearances
-### Example 1: User appearances
Here's a simple example of a channel that tracks whether a user is online or not
and what page they're on. (This is useful for creating presence features like showing
a green dot next to a user name if they're online).
-#### Create the server-side Appearance Channel:
+Create the server-side appearance channel:
```ruby
# app/channels/appearance_channel.rb
@@ -354,7 +375,7 @@ class AppearanceChannel < ApplicationCable::Channel
end
def appear(data)
- current_user.appear on: data['appearing_on']
+ current_user.appear(on: data['appearing_on'])
end
def away
@@ -363,35 +384,34 @@ class AppearanceChannel < ApplicationCable::Channel
end
```
-When `#subscribed` callback is invoked by the consumer, a client-side subscription
-is initiated. In this case, we take that opportunity to say "the current user has
-indeed appeared". That appear/disappear API could be backed by Redis, a database,
-or whatever else.
+When a subscription is initiated the `subscribed` callback gets fired and we
+take that opportunity to say "the current user has indeed appeared". That
+appear/disappear API could be backed by Redis, a database, or whatever else.
-#### Create the client-side Appearance Channel subscription:
+Create the client-side appearance channel subscription:
```coffeescript
# app/assets/javascripts/cable/subscriptions/appearance.coffee
App.cable.subscriptions.create "AppearanceChannel",
- # Called when the subscription is ready for use on the server
+ # Called when the subscription is ready for use on the server.
connected: ->
@install()
@appear()
- # Called when the WebSocket connection is closed
+ # Called when the WebSocket connection is closed.
disconnected: ->
@uninstall()
- # Called when the subscription is rejected by the server
+ # Called when the subscription is rejected by the server.
rejected: ->
@uninstall()
appear: ->
- # Calls `AppearanceChannel#appear(data)` on the server
+ # Calls `AppearanceChannel#appear(data)` on the server.
@perform("appear", appearing_on: $("main").data("appearing-on"))
away: ->
- # Calls `AppearanceChannel#away` on the server
+ # Calls `AppearanceChannel#away` on the server.
@perform("away")
@@ -413,13 +433,33 @@ App.cable.subscriptions.create "AppearanceChannel",
```
##### Client-Server Interaction
-1. **Client** establishes a connection with the **Server** via `App.cable = ActionCable.createConsumer("ws://cable.example.com")`. [*` cable.coffee`*] The **Server** identified this connection instance by `current_user`.
-2. **Client** initiates a subscription to the `Appearance Channel` for their connection via `App.cable.subscriptions.create "AppearanceChannel"`. [*`appearance.coffee`*]
-3. **Server** recognizes a new subscription has been initiated for `AppearanceChannel` channel performs the `subscribed` callback, which calls the `appear` method on the `current_user`. [*`appearance_channel.rb`*]
-4. **Client** recognizes that a subscription has been established and calls `connected` [*`appearance.coffee`*] which in turn calls `@install` and `@appear`. `@appear` calls`AppearanceChannel#appear(data)` on the server, and supplies a data hash of `appearing_on: $("main").data("appearing-on")`. This is possible because the server-side channel instance will automatically expose the public methods declared on the class (minus the callbacks), so that these can be reached as remote procedure calls via a subscription's `perform` method.
-5. **Server** receives the request for the `appear` action on the `AppearanceChannel` channel for the connection identified by `current_user`. [*`appearance_channel.rb`*] The server retrieves the data with the `appearing_on` key from the data hash and sets it as the value for the `on:` key being passed to `current_user.appear`.
-### Example 2: Receiving new web notifications
+1. **Client** connects to the **Server** via `App.cable =
+ActionCable.createConsumer("ws://cable.example.com")`. (`cable.js`). The
+**Server** identifies this connection by `current_user`.
+
+2. **Client** subscribes to the appearance channel via
+`App.cable.subscriptions.create(channel: "AppearanceChannel")`. (`appearance.coffee`)
+
+3. **Server** recognizes a new subscription has been initiated for the
+appearance channel and runs its `subscribed` callback, calling the `appear`
+method on `current_user`. (`appearance_channel.rb`)
+
+4. **Client** recognizes that a subscription has been established and calls
+`connected` (`appearance.coffee`) which in turn calls `@install` and `@appear`.
+`@appear` calls `AppearanceChannel#appear(data)` on the server, and supplies a
+data hash of `{ appearing_on: $("main").data("appearing-on") }`. This is
+possible because the server-side channel instance automatically exposes all
+public methods declared on the class (minus the callbacks), so that these can be
+reached as remote procedure calls via a subscription's `perform` method.
+
+5. **Server** receives the request for the `appear` action on the appearance
+channel for the connection identified by `current_user`
+(`appearance_channel.rb`). **Server** retrieves the data with the
+`:appearing_on` key from the data hash and sets it as the value for the `:on`
+key being passed to `current_user.appear`.
+
+### Example 2: Receiving New Web Notifications
The appearance example was all about exposing server functionality to
client-side invocation over the WebSocket connection. But the great thing
@@ -429,7 +469,7 @@ where the server invokes an action on the client.
This is a web notification channel that allows you to trigger client-side
web notifications when you broadcast to the right streams:
-#### Create the server-side Web Notifications Channel:
+Create the server-side web notifications channel:
```ruby
# app/channels/web_notifications_channel.rb
@@ -440,36 +480,43 @@ class WebNotificationsChannel < ApplicationCable::Channel
end
```
-#### Create the client-side Web Notifications Channel subscription:
+Create the client-side web notifications channel subscription:
+
```coffeescript
# app/assets/javascripts/cable/subscriptions/web_notifications.coffee
-# Client-side which assumes you've already requested the right to send web notifications
+# Client-side which assumes you've already requested
+# the right to send web notifications.
App.cable.subscriptions.create "WebNotificationsChannel",
received: (data) ->
new Notification data["title"], body: data["body"]
```
-#### Broadcast content to a Web Notification Channel instance from elsewhere in your application
+Broadcast content to a web notification channel instance from elsewhere in your
+application:
```ruby
# Somewhere in your app this is called, perhaps from a NewCommentJob
- WebNotificationsChannel.broadcast_to current_user, title: 'New things!', body: 'All the news fit to print'
+WebNotificationsChannel.broadcast_to(
+ current_user,
+ title: 'New things!',
+ body: 'All the news fit to print'
+)
```
The `WebNotificationsChannel.broadcast_to` call places a message in the current
-subscription adapter (Redis by default)'s pubsub queue under a separate
-broadcasting name for each user. For a user with an ID of 1, the broadcasting
-name would be `web_notifications_1`.
+subscription adapter's pubsub queue under a separate broadcasting name for each
+user. For a user with an ID of 1, the broadcasting name would be
+"web_notifications_1".
The channel has been instructed to stream everything that arrives at
-`web_notifications_1` directly to the client by invoking the `#received(data)`
-callback. The data is the hash sent as the second parameter to the server-side
-broadcast call, JSON encoded for the trip across the wire, and unpacked for
-the data argument arriving to `#received`.
+"web_notifications_1" directly to the client by invoking the `received`
+callback. The data passed as argument is the hash sent as the second parameter
+to the server-side broadcast call, JSON encoded for the trip across the wire,
+and unpacked for the data argument arriving to `received`.
-### More complete examples
+### More Complete Examples
-See the [rails/actioncable-examples](http://github.com/rails/actioncable-examples)
+See the [rails/actioncable-examples](https://github.com/rails/actioncable-examples)
repository for a full example of how to setup Action Cable in a Rails app and adding channels.
## Configuration
@@ -478,26 +525,20 @@ Action Cable has two required configurations: a subscription adapter and allowed
### Subscription Adapter
-By default, `ActionCable::Server::Base` will look for a configuration file
-in `Rails.root.join('config/cable.yml')`. The file must specify an adapter
-and a URL for each Rails environment. See the "Dependencies" section for
-additional information on adapters.
+By default, Action Cable looks for a configuration file in `config/cable.yml`.
+The file must specify an adapter and a URL for each Rails environment. See the
+[Dependencies](#dependencies) section for additional information on adapters.
```yaml
-production: &production
- adapter: redis
- url: redis://10.10.3.153:6381
-development: &development
+development:
adapter: async
-test: *development
-```
-This format allows you to specify one configuration per Rails environment.
-You can also change the location of the Action Cable config file in
-a Rails initializer with something like:
+test:
+ adapter: async
-```ruby
-Rails.application.paths.add "config/redis/cable", with: "somewhere/else/cable.yml"
+production:
+ adapter: redis
+ url: redis://10.10.3.153:6381
```
### Allowed Request Origins
@@ -507,44 +548,46 @@ passed to the server config as an array. The origins can be instances of
strings or regular expressions, against which a check for match will be performed.
```ruby
-Rails.application.config.action_cable.allowed_request_origins = ['http://rubyonrails.com', /http:\/\/ruby.*/]
+config.action_cable.allowed_request_origins = ['http://rubyonrails.com', %r{http://ruby.*}]
```
To disable and allow requests from any origin:
```ruby
-Rails.application.config.action_cable.disable_request_forgery_protection = true
+config.action_cable.disable_request_forgery_protection = true
```
By default, Action Cable allows all requests from localhost:3000 when running
in the development environment.
-
### Consumer Configuration
-To configure the URL, add a call to `action_cable_meta_tag` in your HTML layout HEAD.
-This uses a url or path typically set via `config.action_cable.url` in the environment configuration files.
+To configure the URL, add a call to `action_cable_meta_tag` in your HTML layout
+HEAD. This uses a URL or path typically set via `config.action_cable.url` in the
+environment configuration files.
### Other Configurations
-The other common option to configure is the log tags applied to the per-connection logger. Here's close to what we're using in Basecamp:
+The other common option to configure is the log tags applied to the
+per-connection logger. Here's close to what we're using in Basecamp:
```ruby
-Rails.application.config.action_cable.log_tags = [
+config.action_cable.log_tags = [
-> request { request.env['bc.account_id'] || "no-account" },
:action_cable,
-> request { request.uuid }
]
```
-For a full list of all configuration options, see the `ActionCable::Server::Configuration` class.
+For a full list of all configuration options, see the
+`ActionCable::Server::Configuration` class.
-Also note that your server must provide at least the same number of
-database connections as you have workers. The default worker pool is
-set to 100, so that means you have to make at least that available.
-You can change that in `config/database.yml` through the `pool` attribute.
+Also note that your server must provide at least the same number of database
+connections as you have workers. The default worker pool size is set to 4, so
+that means you have to make at least that available. You can change that in
+`config/database.yml` through the `pool` attribute.
-## Running standalone cable servers
+## Running Standalone Cable Servers
### In App
@@ -559,30 +602,30 @@ class Application < Rails::Application
end
```
-You can use `App.cable = ActionCable.createConsumer()` to connect to the
-cable server if `action_cable_meta_tag` is included in the layout. A custom
-path is specified as first argument to `createConsumer`
-(e.g. `App.cable = ActionCable.createConsumer("/websocket")`).
+You can use `App.cable = ActionCable.createConsumer()` to connect to the cable
+server if `action_cable_meta_tag` is invoked in the layout. A custom path is
+specified as first argument to `createConsumer` (e.g. `App.cable =
+ActionCable.createConsumer("/websocket")`).
-For every instance of your server you create and for every worker
-your server spawns, you will also have a new instance of ActionCable,
-but the use of Redis keeps messages synced across connections.
+For every instance of your server you create and for every worker your server
+spawns, you will also have a new instance of Action Cable, but the use of Redis
+keeps messages synced across connections.
### Standalone
-The cable servers can be separated from your normal application server.
-It's still a Rack application, but it is its own Rack application.
-The recommended basic setup is as follows:
+The cable servers can be separated from your normal application server. It's
+still a Rack application, but it is its own Rack application. The recommended
+basic setup is as follows:
```ruby
# cable/config.ru
-require ::File.expand_path('../../config/environment', __FILE__)
+require_relative '../config/environment'
Rails.application.eager_load!
run ActionCable.server
```
-Then you start the server using a binstub in bin/cable ala:
+Then you start the server using a binstub in `bin/cable` ala:
```
#!/bin/bash
@@ -618,4 +661,5 @@ The Action Cable server implements the Rack socket hijacking API,
thereby allowing the use of a multithreaded pattern for managing connections
internally, irrespective of whether the application server is multi-threaded or not.
-Accordingly, Action Cable works with all the popular application servers -- Unicorn, Puma and Passenger.
+Accordingly, Action Cable works with popular servers like Unicorn, Puma, and
+Passenger.
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 848c9caa59..a906f64633 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -203,7 +203,7 @@ predefined raise/rescue flow to end up as a 400 Bad Request.
```ruby
class PeopleController < ActionController::Base
- # This will raise an ActiveModel::ForbiddenAttributes exception
+ # This will raise an ActiveModel::ForbiddenAttributesError exception
# because it's using mass assignment without an explicit permit
# step.
def create
diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md
index 5eb19f5214..dee64e6439 100644
--- a/guides/source/active_record_postgresql.md
+++ b/guides/source/active_record_postgresql.md
@@ -435,7 +435,7 @@ create_table :documents do |t|
t.string 'body'
end
-execute "CREATE INDEX documents_idx ON documents USING gin(to_tsvector('english', title || ' ' || body));"
+add_index :documents, "to_tsvector('english', title || ' ' || body)", using: :gin, name: 'documents_idx'
# app/models/document.rb
class Document < ApplicationRecord
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 9a13e3bda7..928ab43b3b 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1121,7 +1121,7 @@ If you want to select a set of records whether or not they have associated
records you can use the `left_outer_joins` method.
```ruby
-Author.left_outer_joins(:posts).uniq.select('authors.*, COUNT(posts.*) AS posts_count').group('authors.id')
+Author.left_outer_joins(:posts).distinct.select('authors.*, COUNT(posts.*) AS posts_count').group('authors.id')
```
Which produces:
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index 1cf4abce10..936d6a30b8 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -278,12 +278,6 @@ form was submitted. This is typically used when the user needs to agree to your
application's terms of service, confirm that some text is read, or any similar
concept.
-This validation is very specific to web applications and this
-'acceptance' does not need to be recorded anywhere in your database. If you
-don't have a field for it, the helper will just create a virtual attribute. If
-the field does exist in your database, the `accept` option must be set to
-`true` or else the validation will not run.
-
```ruby
class Person < ApplicationRecord
validates :terms_of_service, acceptance: true
@@ -292,16 +286,31 @@ end
This check is performed only if `terms_of_service` is not `nil`.
The default error message for this helper is _"must be accepted"_.
+You can also pass custom message via the `message` option.
-It can receive an `:accept` option, which determines the value that will be
-considered acceptance. It defaults to "1" and can be easily changed.
+```ruby
+class Person < ApplicationRecord
+ validates :terms_of_service, acceptance: true, message: 'must be abided'
+end
+```
+
+It can also receive an `:accept` option, which determines the allowed values
+that will be considered as accepted. It defaults to `['1', true]` and can be
+easily changed.
```ruby
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { accept: 'yes' }
+ validates :eula, acceptance: { accept: ['TRUE', 'accepted'] }
end
```
+This validation is very specific to web applications and this
+'acceptance' does not need to be recorded anywhere in your database. If you
+don't have a field for it, the helper will just create a virtual attribute. If
+the field does exist in your database, the `accept` option must be set to
+or include `true` or else the validation will not run.
+
### `validates_associated`
You should use this helper when your model has associations with other models
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index d7f9201b26..565c87c4b5 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -252,7 +252,7 @@ Note that `try` will swallow no-method errors, returning nil instead. If you wan
```ruby
@number.try(:nest) # => nil
-@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Fixnum
+@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer
```
NOTE: Defined in `active_support/core_ext/object/try.rb`.
@@ -707,64 +707,6 @@ M.parents # => [X::Y, X, Object]
NOTE: Defined in `active_support/core_ext/module/introspection.rb`.
-#### Qualified Constant Names
-
-The standard methods `const_defined?`, `const_get`, and `const_set` accept
-bare constant names. Active Support extends this API to be able to pass
-relative qualified constant names.
-
-The new methods are `qualified_const_defined?`, `qualified_const_get`, and
-`qualified_const_set`. Their arguments are assumed to be qualified constant
-names relative to their receiver:
-
-```ruby
-Object.qualified_const_defined?("Math::PI") # => true
-Object.qualified_const_get("Math::PI") # => 3.141592653589793
-Object.qualified_const_set("Math::Phi", 1.618034) # => 1.618034
-```
-
-Arguments may be bare constant names:
-
-```ruby
-Math.qualified_const_get("E") # => 2.718281828459045
-```
-
-These methods are analogous to their built-in counterparts. In particular,
-`qualified_constant_defined?` accepts an optional second argument to be
-able to say whether you want the predicate to look in the ancestors.
-This flag is taken into account for each constant in the expression while
-walking down the path.
-
-For example, given
-
-```ruby
-module M
- X = 1
-end
-
-module N
- class C
- include M
- end
-end
-```
-
-`qualified_const_defined?` behaves this way:
-
-```ruby
-N.qualified_const_defined?("C::X", false) # => false
-N.qualified_const_defined?("C::X", true) # => true
-N.qualified_const_defined?("C::X") # => true
-```
-
-As the last example implies, the second argument defaults to true,
-as in `const_defined?`.
-
-For coherence with the built-in methods only relative paths are accepted.
-Absolute qualified constant names like `::Math::PI` raise `NameError`.
-
-NOTE: Defined in `active_support/core_ext/module/qualified_const.rb`.
-
### Reachable
A named module is reachable if it is stored in its corresponding constant. It means you can reach the module object via the constant.
@@ -1661,19 +1603,6 @@ Given a string with a qualified constant reference expression, `deconstantize` r
"Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel"
```
-Active Support for example uses this method in `Module#qualified_const_set`:
-
-```ruby
-def qualified_const_set(path, value)
- QualifiedConstUtils.raise_if_absolute(path)
-
- const_name = path.demodulize
- mod_name = path.deconstantize
- mod = mod_name.empty? ? self : qualified_const_get(mod_name)
- mod.const_set(const_name, value)
-end
-```
-
NOTE: Defined in `active_support/core_ext/string/inflections.rb`.
#### `parameterize`
@@ -1742,7 +1671,7 @@ NOTE: Defined in `active_support/core_ext/string/inflections.rb`.
The method `constantize` resolves the constant reference expression in its receiver:
```ruby
-"Fixnum".constantize # => Fixnum
+"Integer".constantize # => Integer
module M
X = 1
@@ -2349,7 +2278,7 @@ Contributor.limit(2).order(:rank).to_xml
To do so it sends `to_xml` to every item in turn, and collects the results under a root node. All items must respond to `to_xml`, an exception is raised otherwise.
-By default, the name of the root element is the underscorized and dasherized plural of the name of the class of the first item, provided the rest of elements belong to that type (checked with `is_a?`) and they are not hashes. In the example above that's "contributors".
+By default, the name of the root element is the underscored and dasherized plural of the name of the class of the first item, provided the rest of elements belong to that type (checked with `is_a?`) and they are not hashes. In the example above that's "contributors".
If there's any element that does not belong to the type of the first one the root node becomes "objects":
@@ -2611,8 +2540,7 @@ To do so, the method loops over the pairs and builds nodes that depend on the _v
```ruby
XML_TYPE_NAMES = {
"Symbol" => "symbol",
- "Fixnum" => "integer",
- "Bignum" => "integer",
+ "Integer" => "integer",
"BigDecimal" => "decimal",
"Float" => "float",
"TrueClass" => "boolean",
diff --git a/guides/source/api_app.md b/guides/source/api_app.md
index e50a24ce55..485294dc02 100644
--- a/guides/source/api_app.md
+++ b/guides/source/api_app.md
@@ -179,7 +179,7 @@ To render debugging information preserving the response format, use the value `:
config.debug_exception_response_format = :api
```
-By default, `config.debug_exception_response_format` is set to `:api`.
+By default, `config.debug_exception_response_format` is set to `:api`, when `config.api_only` is set to true.
Finally, inside `app/controllers/application_controller.rb`, instead of:
@@ -202,7 +202,7 @@ An API application comes with the following middleware by default:
- `Rack::Sendfile`
- `ActionDispatch::Static`
-- `ActionDispatch::LoadInterlock`
+- `ActionDispatch::Executor`
- `ActiveSupport::Cache::Strategy::LocalCache::Middleware`
- `Rack::Runtime`
- `ActionDispatch::RequestId`
@@ -289,7 +289,7 @@ You can learn more about how to use `Rack::Sendfile` with popular
front-ends in [the Rack::Sendfile
documentation](http://rubydoc.info/github/rack/rack/master/Rack/Sendfile).
-Here are some values for popular servers, once they are configured, to support
+Here are some values for this header for some popular servers, once these servers are configured to support
accelerated file sending:
```ruby
@@ -395,6 +395,13 @@ included into `ActionController::API` in the rails console:
```bash
$ bin/rails c
>> ActionController::API.ancestors - ActionController::Metal.ancestors
+=> [ActionController::API,
+ ActiveRecord::Railties::ControllerRuntime,
+ ActionDispatch::Routing::RouteSet::MountedHelpers,
+ ActionController::ParamsWrapper,
+ ... ,
+ AbstractController::Rendering,
+ ActionView::ViewPaths]
```
### Adding Other Modules
@@ -409,7 +416,7 @@ Some common modules you might want to add:
and translation methods.
- `ActionController::HttpAuthentication::Basic` (or `Digest` or `Token`): Support
for basic, digest or token HTTP authentication.
-- `AbstractController::Layouts`: Support for layouts when rendering.
+- `ActionView::Layouts`: Support for layouts when rendering.
- `ActionController::MimeResponds`: Support for `respond_to`.
- `ActionController::Cookies`: Support for `cookies`, which includes
support for signed and encrypted cookies. This requires the cookies middleware.
diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md
index cd208c2e13..5b34330936 100644
--- a/guides/source/api_documentation_guidelines.md
+++ b/guides/source/api_documentation_guidelines.md
@@ -120,7 +120,7 @@ On the other hand, big chunks of structured documentation may have a separate "E
The results of expressions follow them and are introduced by "# => ", vertically aligned:
```ruby
-# For checking if a fixnum is even or odd.
+# For checking if an integer is even or odd.
#
# 1.even? # => false
# 1.odd? # => true
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index cc3da47db9..e6631a513c 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -435,11 +435,11 @@ Sprockets uses manifest files to determine which assets to include and serve.
These manifest files contain _directives_ - instructions that tell Sprockets
which files to require in order to build a single CSS or JavaScript file. With
these directives, Sprockets loads the files specified, processes them if
-necessary, concatenates them into one single file and then compresses them (if
-`Rails.application.config.assets.compress` is true). By serving one file rather
-than many, the load time of pages can be greatly reduced because the browser
-makes fewer requests. Compression also reduces file size, enabling the
-browser to download them faster.
+necessary, concatenates them into one single file and then compresses them
+(based on value of `Rails.application.config.assets.js_compressor`). By serving
+one file rather than many, the load time of pages can be greatly reduced because
+the browser makes fewer requests. Compression also reduces file size, enabling
+the browser to download them faster.
For example, a new Rails application includes a default
@@ -1105,11 +1105,17 @@ NOTE: You will need an [ExecJS](https://github.com/rails/execjs#readme)
supported runtime in order to use `uglifier`. If you are using Mac OS X or
Windows you have a JavaScript runtime installed in your operating system.
-NOTE: The `config.assets.compress` initialization option is no longer used in
-Rails to enable either CSS or JavaScript compression. Setting it will have no
-effect on the application. Instead, setting `config.assets.css_compressor` and
-`config.assets.js_compressor` will control compression of CSS and JavaScript
-assets.
+
+
+### Serving GZipped version of assets
+
+By default, gzipped version of compiled assets will be generated, along
+with the non-gzipped version of assets. Gzipped assets help reduce the transmission of
+data over the wire. You can configure this by setting the `gzip` flag.
+
+```ruby
+config.assets.gzip = false # disable gzipped assets generation
+```
### Using Your Own Compressor
@@ -1280,7 +1286,7 @@ config.assets.js_compressor = :uglifier
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false
-# Generate digests for assets URLs. This is planned for deprecation.
+# Generate digests for assets URLs.
config.assets.digest = true
# Precompile additional assets (application.js, application.css, and all
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 4977d4f30e..3993fdb1dd 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1477,7 +1477,7 @@ WARNING: Objects will _always_ be removed from the database, ignoring the `:depe
##### `collection=(objects)`
-The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
+The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate. The changes are persisted to the database.
##### `collection_singular_ids`
@@ -1489,7 +1489,7 @@ The `collection_singular_ids` method returns an array of the ids of the objects
##### `collection_singular_ids=(ids)`
-The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.
+The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate. The changes are persisted to the database.
##### `collection.clear`
@@ -2006,7 +2006,7 @@ The `collection.destroy` method removes one or more objects from the collection
##### `collection=(objects)`
-The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
+The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate. The changes are persisted to the database.
##### `collection_singular_ids`
@@ -2018,7 +2018,7 @@ The `collection_singular_ids` method returns an array of the ids of the objects
##### `collection_singular_ids=(ids)`
-The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.
+The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate. The changes are persisted to the database.
##### `collection.clear`
diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md
index 246fde69d5..61657023e7 100644
--- a/guides/source/autoloading_and_reloading_constants.md
+++ b/guides/source/autoloading_and_reloading_constants.md
@@ -449,9 +449,10 @@ Alright, Rails has a collection of directories similar to `$LOAD_PATH` in which
to look up `post.rb`. That collection is called `autoload_paths` and by
default it contains:
-* All subdirectories of `app` in the application and engines. For example,
- `app/controllers`. They do not need to be the default ones, any custom
- directories like `app/workers` belong automatically to `autoload_paths`.
+* All subdirectories of `app` in the application and engines present at boot
+ time. For example, `app/controllers`. They do not need to be the default
+ ones, any custom directories like `app/workers` belong automatically to
+ `autoload_paths`.
* Any existing second level directories called `app/*/concerns` in the
application and engines.
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 745f09f523..6c734c1d78 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -100,8 +100,8 @@ called key-based expiration.
Cache fragments will also be expired when the view fragment changes (e.g., the
HTML in the view changes). The string of characters at the end of the key is a
-template tree digest. It is an md5 hash computed based on the contents of the
-view fragment you are caching. If you change the view fragment, the md5 hash
+template tree digest. It is an MD5 hash computed based on the contents of the
+view fragment you are caching. If you change the view fragment, the MD5 hash
will change, expiring the existing file.
TIP: Cache stores like Memcached will automatically delete old cache files.
@@ -258,7 +258,7 @@ comment format anywhere in the template, like:
If you use a helper method, for example, inside a cached block and you then update
that helper, you'll have to bump the cache as well. It doesn't really matter how
-you do it, but the md5 of the template file must change. One recommendation is to
+you do it, but the MD5 of the template file must change. One recommendation is to
simply be explicit in a comment, like:
```html+erb
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 0905c4bd16..0e6d119681 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -433,7 +433,7 @@ Ruby version 2.2.2 (x86_64-linux)
RubyGems version 2.4.6
Rack version 1.6
JavaScript Runtime Node.js (V8)
-Middleware Rack::Sendfile, ActionDispatch::Static, ActionDispatch::LoadInterlock, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007ffd131a7c88>, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, Rack::Head, Rack::ConditionalGet, Rack::ETag
+Middleware Rack::Sendfile, ActionDispatch::Static, ActionDispatch::Executor, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007ffd131a7c88>, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, Rack::Head, Rack::ConditionalGet, Rack::ETag
Application root /home/foobar/commandsapp
Environment development
Database adapter sqlite3
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index bb07bc2ca1..66aae112d8 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -60,11 +60,11 @@ These configuration methods are to be called on a `Rails::Railtie` object, such
* `config.asset_host` sets the host for the assets. Useful when CDNs are used for hosting assets, or when you want to work around the concurrency constraints built-in in browsers using different domain aliases. Shorter version of `config.action_controller.asset_host`.
-* `config.autoload_once_paths` accepts an array of paths from which Rails will autoload constants that won't be wiped per request. Relevant if `config.cache_classes` is false, which is the case in development mode by default. Otherwise, all autoloading happens only once. All elements of this array must also be in `autoload_paths`. Default is an empty array.
+* `config.autoload_once_paths` accepts an array of paths from which Rails will autoload constants that won't be wiped per request. Relevant if `config.cache_classes` is `false`, which is the case in development mode by default. Otherwise, all autoloading happens only once. All elements of this array must also be in `autoload_paths`. Default is an empty array.
* `config.autoload_paths` accepts an array of paths from which Rails will autoload constants. Default is all directories under `app`.
-* `config.cache_classes` controls whether or not application classes and modules should be reloaded on each request. Defaults to false in development mode, and true in test and production modes.
+* `config.cache_classes` controls whether or not application classes and modules should be reloaded on each request. Defaults to `false` in development mode, and `true` in test and production modes.
* `config.action_view.cache_template_loading` controls whether or not templates should be reloaded on each request. Defaults to whatever is set for `config.cache_classes`.
@@ -73,9 +73,9 @@ application. Accepts a valid week day symbol (e.g. `:monday`).
* `config.cache_store` configures which cache store to use for Rails caching. Options include one of the symbols `:memory_store`, `:file_store`, `:mem_cache_store`, `:null_store`, or an object that implements the cache API. Defaults to `:file_store` if the directory `tmp/cache` exists, and to `:memory_store` otherwise.
-* `config.colorize_logging` specifies whether or not to use ANSI color codes when logging information. Defaults to true.
+* `config.colorize_logging` specifies whether or not to use ANSI color codes when logging information. Defaults to `true`.
-* `config.consider_all_requests_local` is a flag. If true then any error will cause detailed debugging information to be dumped in the HTTP response, and the `Rails::Info` controller will show the application runtime context in `/rails/info/properties`. True by default in development and test environments, and false in production mode. For finer-grained control, set this to false and implement `local_request?` in controllers to specify which requests should provide debugging information on errors.
+* `config.consider_all_requests_local` is a flag. If `true` then any error will cause detailed debugging information to be dumped in the HTTP response, and the `Rails::Info` controller will show the application runtime context in `/rails/info/properties`. `true` by default in development and test environments, and `false` in production mode. For finer-grained control, set this to `false` and implement `local_request?` in controllers to specify which requests should provide debugging information on errors.
* `config.console` allows you to set class that will be used as console you run `rails console`. It's best to run it in `console` block:
@@ -88,9 +88,9 @@ application. Accepts a valid week day symbol (e.g. `:monday`).
end
```
-* `config.eager_load` when true, eager loads all registered `config.eager_load_namespaces`. This includes your application, engines, Rails frameworks and any other registered namespace.
+* `config.eager_load` when `true`, eager loads all registered `config.eager_load_namespaces`. This includes your application, engines, Rails frameworks and any other registered namespace.
-* `config.eager_load_namespaces` registers namespaces that are eager loaded when `config.eager_load` is true. All namespaces in the list must respond to the `eager_load!` method.
+* `config.eager_load_namespaces` registers namespaces that are eager loaded when `config.eager_load` is `true`. All namespaces in the list must respond to the `eager_load!` method.
* `config.eager_load_paths` accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. Defaults to every folder in the `app` directory of the application.
@@ -98,13 +98,13 @@ application. Accepts a valid week day symbol (e.g. `:monday`).
* `config.exceptions_app` sets the exceptions application invoked by the ShowException middleware when an exception happens. Defaults to `ActionDispatch::PublicExceptions.new(Rails.public_path)`.
-* `config.debug_exception_response_format` sets the format used in responses when errors occur in development mode.
+* `config.debug_exception_response_format` sets the format used in responses when errors occur in development mode. Defaults to `:api` for API only apps and `:default` for normal apps.
-* `config.file_watcher` is the class used to detect file updates in the file system when `config.reload_classes_only_on_change` is true. Rails ships with `ActiveSupport::FileUpdateChecker`, the default, and `ActiveSupport::EventedFileUpdateChecker` (this one depends on the [listen](https://github.com/guard/listen) gem). Custom classes must conform to the `ActiveSupport::FileUpdateChecker` API.
+* `config.file_watcher` is the class used to detect file updates in the file system when `config.reload_classes_only_on_change` is `true`. Rails ships with `ActiveSupport::FileUpdateChecker`, the default, and `ActiveSupport::EventedFileUpdateChecker` (this one depends on the [listen](https://github.com/guard/listen) gem). Custom classes must conform to the `ActiveSupport::FileUpdateChecker` API.
* `config.filter_parameters` used for filtering out the parameters that
you don't want shown in the logs, such as passwords or credit card
-numbers. New applications filter out passwords by adding the following `config.filter_parameters+=[:password]` in `config/initializers/filter_parameter_logging.rb`. Parameters filter works by partial matching regular expression.
+numbers. By default, Rails filters out passwords by adding `Rails.application.config.filter_parameters += [:password]` in `config/initializers/filter_parameter_logging.rb`. Parameters filter works by partial matching regular expression.
* `config.force_ssl` forces all requests to be served over HTTPS by using the `ActionDispatch::SSL` middleware, and sets `config.action_mailer.default_url_options` to be `{ protocol: 'https' }`. This can be configured by setting `config.ssl_options` - see the [ActionDispatch::SSL documentation](http://edgeapi.rubyonrails.org/classes/ActionDispatch/SSL.html) for details.
@@ -117,9 +117,9 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
* `config.log_tags` accepts a list of: methods that the `request` object responds to, a `Proc` that accepts the `request` object, or something that responds to `to_s`. This makes it easy to tag log lines with debug information like subdomain and request id - both very helpful in debugging multi-user production applications.
* `config.logger` is the logger that will be used for `Rails.logger` and any related Rails logging such as `ActiveRecord::Base.logger`. It defaults to an instance of `ActiveSupport::TaggedLogging` that wraps an instance of `ActiveSupport::Logger` which outputs a log to the `log/` directory. You can supply a custom logger, to get full compatibility you must follow these guidelines:
- * To support a formatter you must manually assign a formatter from the `config.log_formatter` value to the logger.
- * To support tagged loggs the log instance must be wrapped with `ActiveSupport::TaggedLogging`.
- * To support silencing the logger must include `LoggerSilence` and `ActiveSupport::LoggerThreadSafeLevel` modules. The `ActiveSupport::Logger` class already includes these modules.
+ * To support a formatter, you must manually assign a formatter from the `config.log_formatter` value to the logger.
+ * To support tagged logs, the log instance must be wrapped with `ActiveSupport::TaggedLogging`.
+ * To support silencing, the logger must include `LoggerSilence` and `ActiveSupport::LoggerThreadSafeLevel` modules. The `ActiveSupport::Logger` class already includes these modules.
```ruby
class MyLogger < ::Logger
@@ -134,11 +134,11 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
* `config.middleware` allows you to configure the application's middleware. This is covered in depth in the [Configuring Middleware](#configuring-middleware) section below.
-* `config.reload_classes_only_on_change` enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to true. If `config.cache_classes` is true, this option is ignored.
+* `config.reload_classes_only_on_change` enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to `true`. If `config.cache_classes` is `true`, this option is ignored.
* `secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`.
-* `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to true, but in the production environment it is set to false because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to true. Otherwise, you won't be able to use page caching and request for files that exist under the public directory.
+* `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to `true`, but in the production environment it is set to `false` because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to `true.` Otherwise, you won't be able to use page caching and request for files that exist under the public directory.
* `config.session_store` is usually set up in `config/initializers/session_store.rb` and specifies what class to use to store the session. Possible values are `:cookie_store` which is the default, `:mem_cache_store`, and `:disabled`. The last one tells Rails not to deal with sessions. Custom session stores can also be specified:
@@ -153,16 +153,16 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
### Configuring Assets
* `config.assets.enabled` a flag that controls whether the asset
-pipeline is enabled. It is set to true by default.
+pipeline is enabled. It is set to `true` by default.
* `config.assets.raise_runtime_errors` Set this flag to `true` to enable additional runtime error checking. Recommended in `config/environments/development.rb` to minimize unexpected behavior when deploying to `production`.
-* `config.assets.compress` a flag that enables the compression of compiled assets. It is explicitly set to true in `config/environments/production.rb`.
-
* `config.assets.css_compressor` defines the CSS compressor to use. It is set by default by `sass-rails`. The unique alternative value at the moment is `:yui`, which uses the `yui-compressor` gem.
* `config.assets.js_compressor` defines the JavaScript compressor to use. Possible values are `:closure`, `:uglifier` and `:yui` which require the use of the `closure-compiler`, `uglifier` or `yui-compressor` gems respectively.
+* `config.assets.gzip` a flag that enables the creation of gzipped version of compiled assets, along with non-gzipped assets. Set to `true` by default.
+
* `config.assets.paths` contains the paths which are used to look for assets. Appending paths to this configuration option will cause those paths to be used in the search for assets.
* `config.assets.precompile` allows you to specify additional assets (other than `application.css` and `application.js`) which are to be precompiled when `rake assets:precompile` is run.
@@ -177,7 +177,7 @@ pipeline is enabled. It is set to true by default.
* `config.assets.compile` is a boolean that can be used to turn on live Sprockets compilation in production.
-* `config.assets.logger` accepts a logger conforming to the interface of Log4r or the default Ruby `Logger` class. Defaults to the same configured at `config.logger`. Setting `config.assets.logger` to false will turn off served assets logging.
+* `config.assets.logger` accepts a logger conforming to the interface of Log4r or the default Ruby `Logger` class. Defaults to the same configured at `config.logger`. Setting `config.assets.logger` to `false` will turn off served assets logging.
### Configuring Generators
@@ -215,7 +215,7 @@ Every Rails application comes with a standard set of middleware which it uses in
* `ActionDispatch::SSL` forces every request to be served using HTTPS. Enabled if `config.force_ssl` is set to `true`. Options passed to this can be configured by setting `config.ssl_options`.
* `ActionDispatch::Static` is used to serve static assets. Disabled if `config.public_file_server.enabled` is `false`. Set `config.public_file_server.index_name` if you need to serve a static directory index file that is not named `index`. For example, to serve `main.html` instead of `index.html` for directory requests, set `config.public_file_server.index_name` to `"main"`.
-* `ActionDispatch::LoadInterlock` allows thread safe code reloading. Disabled if `config.allow_concurrency` is `false`, which causes `Rack::Lock` to be loaded. `Rack::Lock` wraps the app in mutex so it can only be called by a single thread at a time.
+* `ActionDispatch::Executor` allows thread safe code reloading. Disabled if `config.allow_concurrency` is `false`, which causes `Rack::Lock` to be loaded. `Rack::Lock` wraps the app in mutex so it can only be called by a single thread at a time.
* `ActiveSupport::Cache::Strategy::LocalCache` serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread.
* `Rack::Runtime` sets an `X-Runtime` header, containing the time (in seconds) taken to execute the request.
* `Rails::Rack::Logger` notifies the logs that the request has begun. After request is complete, flushes all the logs.
@@ -281,8 +281,8 @@ All these configuration options are delegated to the `I18n` library.
* `config.active_record.logger` accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then passed on to any new database connections made. You can retrieve this logger by calling `logger` on either an Active Record model class or an Active Record model instance. Set to `nil` to disable logging.
* `config.active_record.primary_key_prefix_type` lets you adjust the naming for primary key columns. By default, Rails assumes that primary key columns are named `id` (and this configuration option doesn't need to be set.) There are two other choices:
- * `:table_name` would make the primary key for the Customer class `customerid`
- * `:table_name_with_underscore` would make the primary key for the Customer class `customer_id`
+ * `:table_name` would make the primary key for the Customer class `customerid`.
+ * `:table_name_with_underscore` would make the primary key for the Customer class `customer_id`.
* `config.active_record.table_name_prefix` lets you set a global string to be prepended to table names. If you set this to `northwest_`, then the Customer class will look for `northwest_customers` as its table. The default is an empty string.
@@ -290,17 +290,17 @@ All these configuration options are delegated to the `I18n` library.
* `config.active_record.schema_migrations_table_name` lets you set a string to be used as the name of the schema migrations table.
-* `config.active_record.pluralize_table_names` specifies whether Rails will look for singular or plural table names in the database. If set to true (the default), then the Customer class will use the `customers` table. If set to false, then the Customer class will use the `customer` table.
+* `config.active_record.pluralize_table_names` specifies whether Rails will look for singular or plural table names in the database. If set to `true` (the default), then the Customer class will use the `customers` table. If set to false, then the Customer class will use the `customer` table.
* `config.active_record.default_timezone` determines whether to use `Time.local` (if set to `:local`) or `Time.utc` (if set to `:utc`) when pulling dates and times from the database. The default is `:utc`.
* `config.active_record.schema_format` controls the format for dumping the database schema to a file. The options are `:ruby` (the default) for a database-independent version that depends on migrations, or `:sql` for a set of (potentially database-dependent) SQL statements.
-* `config.active_record.error_on_ignored_order_or_limit` specifies if an error should be raised if the order or limit of a query is ignored during a batch query. The options are true (raise error) or false (warn). Default is false.
+* `config.active_record.error_on_ignored_order_or_limit` specifies if an error should be raised if the order or limit of a query is ignored during a batch query. The options are `true` (raise error) or `false` (warn). Default is `false`.
-* `config.active_record.timestamped_migrations` controls whether migrations are numbered with serial integers or with timestamps. The default is true, to use timestamps, which are preferred if there are multiple developers working on the same application.
+* `config.active_record.timestamped_migrations` controls whether migrations are numbered with serial integers or with timestamps. The default is `true`, to use timestamps, which are preferred if there are multiple developers working on the same application.
-* `config.active_record.lock_optimistically` controls whether Active Record will use optimistic locking and is true by default.
+* `config.active_record.lock_optimistically` controls whether Active Record will use optimistic locking and is `true` by default.
* `config.active_record.cache_timestamp_format` controls the format of the timestamp value in the cache key. Default is `:nsec`.
@@ -308,13 +308,13 @@ All these configuration options are delegated to the `I18n` library.
* `config.active_record.partial_writes` is a boolean value and controls whether or not partial writes are used (i.e. whether updates only set attributes that are dirty). Note that when using partial writes, you should also use optimistic locking `config.active_record.lock_optimistically` since concurrent updates may write attributes based on a possibly stale read state. The default value is `true`.
-* `config.active_record.maintain_test_schema` is a boolean value which controls whether Active Record should try to keep your test database schema up-to-date with `db/schema.rb` (or `db/structure.sql`) when you run your tests. The default is true.
+* `config.active_record.maintain_test_schema` is a boolean value which controls whether Active Record should try to keep your test database schema up-to-date with `db/schema.rb` (or `db/structure.sql`) when you run your tests. The default is `true`.
* `config.active_record.dump_schema_after_migration` is a flag which
controls whether or not schema dump should happen (`db/schema.rb` or
- `db/structure.sql`) when you run migrations. This is set to false in
+ `db/structure.sql`) when you run migrations. This is set to `false` in
`config/environments/production.rb` which is generated by Rails. The
- default value is true if this configuration is not set.
+ default value is `true` if this configuration is not set.
* `config.active_record.dump_schemas` controls which database schemas will be dumped when calling db:structure:dump.
The options are `:schema_search_path` (the default) which dumps any schemas listed in schema_search_path,
@@ -332,11 +332,11 @@ All these configuration options are delegated to the `I18n` library.
* `config.active_record.index_nested_attribute_errors` allows errors for nested
has_many relationships to be displayed with an index as well as the error.
- Defaults to false.
+ Defaults to `false`.
The MySQL adapter adds one additional configuration option:
-* `ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns as booleans. True by default.
+* `ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns as booleans. Defaults to `true`.
The schema dumper adds one additional configuration option:
@@ -348,7 +348,7 @@ The schema dumper adds one additional configuration option:
* `config.action_controller.asset_host` sets the host for the assets. Useful when CDNs are used for hosting assets rather than the application server itself.
-* `config.action_controller.perform_caching` configures whether the application should perform the caching features provided by the Action Controller component or not. Set to false in development mode, true in production.
+* `config.action_controller.perform_caching` configures whether the application should perform the caching features provided by the Action Controller component or not. Set to `false` in development mode, `true` in production.
* `config.action_controller.default_static_extension` configures the extension used for cached pages. Defaults to `.html`.
@@ -390,6 +390,10 @@ The schema dumper adds one additional configuration option:
* `config.action_dispatch.tld_length` sets the TLD (top-level domain) length for the application. Defaults to `1`.
+* `config.action_dispatch.ignore_accept_header` is used to determine whether to ignore accept headers from a request. Defaults to `false`.
+
+* `config.action_dispatch.x_sendfile_header` specifies server specific X-Sendfile header. This is useful for accelerated file sending from server. For example it can be set to 'X-Sendfile' for Apache.
+
* `config.action_dispatch.http_auth_salt` sets the HTTP Auth salt value. Defaults
to `'http authentication'`.
@@ -404,7 +408,7 @@ encrypted cookies salt value. Defaults to `'signed encrypted cookie'`.
* `config.action_dispatch.perform_deep_munge` configures whether `deep_munge`
method should be performed on the parameters. See [Security Guide](security.html#unsafe-query-generation)
- for more information. It defaults to true.
+ for more information. It defaults to `true`.
* `config.action_dispatch.rescue_responses` configures what exceptions are assigned to an HTTP status. It accepts a hash and you can specify pairs of exception/status. By default, this is defined as:
@@ -459,7 +463,7 @@ encrypted cookies salt value. Defaults to `'signed encrypted cookie'`.
* `config.action_view.embed_authenticity_token_in_remote_forms` allows you to
set the default behavior for `authenticity_token` in forms with `remote:
- true`. By default it's set to false, which means that remote forms will not
+ true`. By default it's set to `false`, which means that remote forms will not
include `authenticity_token`, which is helpful when you're fragment-caching
the form. Remote forms get the authenticity from the `meta` tag, so embedding
is unnecessary unless you support browsers without JavaScript. In such case
@@ -478,9 +482,9 @@ encrypted cookies salt value. Defaults to `'signed encrypted cookie'`.
error should be raised for missing translations.
* `config.action_view.automatically_disable_submit_tag` determines whether
- submit_tag should automatically disable on click, this defaults to true.
+ submit_tag should automatically disable on click, this defaults to `true`.
-* `config.action_view.debug_missing_translation` determines whether to wrap the missing translations key in a `<span>` tag or not. This defaults to true.
+* `config.action_view.debug_missing_translation` determines whether to wrap the missing translations key in a `<span>` tag or not. This defaults to `true`.
### Configuring Action Mailer
@@ -495,16 +499,19 @@ There are a number of settings available on `config.action_mailer`:
* `:user_name` - If your mail server requires authentication, set the username in this setting.
* `:password` - If your mail server requires authentication, set the password in this setting.
* `:authentication` - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of `:plain`, `:login`, `:cram_md5`.
+ * `:enable_starttls_auto` - Detects if STARTTLS is enabled in your SMTP server and starts to use it. It defaults to `true`.
+ * `:openssl_verify_mode` - When using TLS, you can set how OpenSSL checks the certificate. This is useful if you need to validate a self-signed and/or a wildcard certificate. This can be one of the OpenSSL verify constants, `:none`, `:peer`, `:client_once`, `:fail_if_no_peer_cert`, or the constant directly `OpenSSL::SSL::VERIFY_NONE`.
+ * `:ssl/:tls` - Enables the SMTP connection to use SMTP/TLS (SMTPS: SMTP over direct TLS connection).
* `config.action_mailer.sendmail_settings` allows detailed configuration for the `sendmail` delivery method. It accepts a hash of options, which can include any of these options:
* `:location` - The location of the sendmail executable. Defaults to `/usr/sbin/sendmail`.
* `:arguments` - The command line arguments. Defaults to `-i -t`.
-* `config.action_mailer.raise_delivery_errors` specifies whether to raise an error if email delivery cannot be completed. It defaults to true.
+* `config.action_mailer.raise_delivery_errors` specifies whether to raise an error if email delivery cannot be completed. It defaults to `true`.
* `config.action_mailer.delivery_method` defines the delivery method and defaults to `:smtp`. See the [configuration section in the Action Mailer guide](action_mailer_basics.html#action-mailer-configuration) for more info.
-* `config.action_mailer.perform_deliveries` specifies whether mail will actually be delivered and is true by default. It can be convenient to set it to false for testing.
+* `config.action_mailer.perform_deliveries` specifies whether mail will actually be delivered and is true by default. It can be convenient to set it to `false` for testing.
* `config.action_mailer.default_options` configures Action Mailer defaults. Use to set options like `from` or `reply_to` for every mailer. These default to:
@@ -550,7 +557,7 @@ There are a number of settings available on `config.action_mailer`:
* `config.action_mailer.deliver_later_queue_name` specifies the queue name for
mailers. By default this is `mailers`.
-* `config.action_mailer.perform_caching` specifies whether the mailer templates should perform fragment caching or not. By default this is false in all environments.
+* `config.action_mailer.perform_caching` specifies whether the mailer templates should perform fragment caching or not. By default this is `false` in all environments.
### Configuring Active Support
@@ -567,7 +574,7 @@ There are a few configuration options available in Active Support:
* `config.active_support.time_precision` sets the precision of JSON encoded time values. Defaults to `3`.
-* `ActiveSupport.halt_callback_chains_on_return_false` specifies whether Active Record and Active Model callback chains can be halted by returning `false` in a 'before' callback. When set to `false`, callback chains are halted only when explicitly done so with `throw(:abort)`. When set to `true`, callback chains are halted when a callback returns false (the previous behavior before Rails 5) and a deprecation warning is given. Defaults to `true` during the deprecation period. New Rails 5 apps generate an initializer file called `callback_terminator.rb` which sets the value to `false`. This file is *not* added when running `rails app:update`, so returning `false` will still work on older apps ported to Rails 5 and display a deprecation warning to prompt users to update their code.
+* `ActiveSupport.halt_callback_chains_on_return_false` specifies whether Active Record and Active Model callback chains can be halted by returning `false` in a 'before' callback. When set to `false`, callback chains are halted only when explicitly done so with `throw(:abort)`. When set to `true`, callback chains are halted when a callback returns `false` (the previous behavior before Rails 5) and a deprecation warning is given. Defaults to `true` during the deprecation period. New Rails 5 apps generate an initializer file called `callback_terminator.rb` which sets the value to `false`. This file is *not* added when running `rails app:update`, so returning `false` will still work on older apps ported to Rails 5 and display a deprecation warning to prompt users to update their code.
* `ActiveSupport::Logger.silencer` is set to `false` to disable the ability to silence logging in a block. The default is `true`.
@@ -898,7 +905,7 @@ Rails will now prepend "/app1" when generating links.
#### Using Passenger
-Passenger makes it easy to run your application in a subdirectory. You can find the relevant configuration in the [Passenger manual](http://www.modrails.com/documentation/Users%20guide%20Apache.html#deploying_rails_to_sub_uri).
+Passenger makes it easy to run your application in a subdirectory. You can find the relevant configuration in the [Passenger manual](https://www.phusionpassenger.com/library/deploy/apache/deploy/ruby/#deploying-an-app-to-a-sub-uri-or-subdirectory).
#### Using a Reverse Proxy
@@ -1023,110 +1030,110 @@ Because `Rails::Application` inherits from `Rails::Railtie` (indirectly), you ca
Below is a comprehensive list of all the initializers found in Rails in the order that they are defined (and therefore run in, unless otherwise stated).
-* `load_environment_hook` Serves as a placeholder so that `:load_environment_config` can be defined to run before it.
+* `load_environment_hook`: Serves as a placeholder so that `:load_environment_config` can be defined to run before it.
-* `load_active_support` Requires `active_support/dependencies` which sets up the basis for Active Support. Optionally requires `active_support/all` if `config.active_support.bare` is un-truthful, which is the default.
+* `load_active_support`: Requires `active_support/dependencies` which sets up the basis for Active Support. Optionally requires `active_support/all` if `config.active_support.bare` is un-truthful, which is the default.
-* `initialize_logger` Initializes the logger (an `ActiveSupport::Logger` object) for the application and makes it accessible at `Rails.logger`, provided that no initializer inserted before this point has defined `Rails.logger`.
+* `initialize_logger`: Initializes the logger (an `ActiveSupport::Logger` object) for the application and makes it accessible at `Rails.logger`, provided that no initializer inserted before this point has defined `Rails.logger`.
-* `initialize_cache` If `Rails.cache` isn't set yet, initializes the cache by referencing the value in `config.cache_store` and stores the outcome as `Rails.cache`. If this object responds to the `middleware` method, its middleware is inserted before `Rack::Runtime` in the middleware stack.
+* `initialize_cache`: If `Rails.cache` isn't set yet, initializes the cache by referencing the value in `config.cache_store` and stores the outcome as `Rails.cache`. If this object responds to the `middleware` method, its middleware is inserted before `Rack::Runtime` in the middleware stack.
-* `set_clear_dependencies_hook` This initializer - which runs only if `cache_classes` is set to `false` - uses `ActionDispatch::Callbacks.after` to remove the constants which have been referenced during the request from the object space so that they will be reloaded during the following request.
+* `set_clear_dependencies_hook`: This initializer - which runs only if `cache_classes` is set to `false` - uses `ActionDispatch::Callbacks.after` to remove the constants which have been referenced during the request from the object space so that they will be reloaded during the following request.
-* `initialize_dependency_mechanism` If `config.cache_classes` is true, configures `ActiveSupport::Dependencies.mechanism` to `require` dependencies rather than `load` them.
+* `initialize_dependency_mechanism`: If `config.cache_classes` is true, configures `ActiveSupport::Dependencies.mechanism` to `require` dependencies rather than `load` them.
-* `bootstrap_hook` Runs all configured `before_initialize` blocks.
+* `bootstrap_hook`: Runs all configured `before_initialize` blocks.
-* `i18n.callbacks` In the development environment, sets up a `to_prepare` callback which will call `I18n.reload!` if any of the locales have changed since the last request. In production mode this callback will only run on the first request.
+* `i18n.callbacks`: In the development environment, sets up a `to_prepare` callback which will call `I18n.reload!` if any of the locales have changed since the last request. In production mode this callback will only run on the first request.
-* `active_support.deprecation_behavior` Sets up deprecation reporting for environments, defaulting to `:log` for development, `:notify` for production and `:stderr` for test. If a value isn't set for `config.active_support.deprecation` then this initializer will prompt the user to configure this line in the current environment's `config/environments` file. Can be set to an array of values.
+* `active_support.deprecation_behavior`: Sets up deprecation reporting for environments, defaulting to `:log` for development, `:notify` for production and `:stderr` for test. If a value isn't set for `config.active_support.deprecation` then this initializer will prompt the user to configure this line in the current environment's `config/environments` file. Can be set to an array of values.
-* `active_support.initialize_time_zone` Sets the default time zone for the application based on the `config.time_zone` setting, which defaults to "UTC".
+* `active_support.initialize_time_zone`: Sets the default time zone for the application based on the `config.time_zone` setting, which defaults to "UTC".
-* `active_support.initialize_beginning_of_week` Sets the default beginning of week for the application based on `config.beginning_of_week` setting, which defaults to `:monday`.
+* `active_support.initialize_beginning_of_week`: Sets the default beginning of week for the application based on `config.beginning_of_week` setting, which defaults to `:monday`.
-* `active_support.set_configs` Sets up Active Support by using the settings in `config.active_support` by `send`'ing the method names as setters to `ActiveSupport` and passing the values through.
+* `active_support.set_configs`: Sets up Active Support by using the settings in `config.active_support` by `send`'ing the method names as setters to `ActiveSupport` and passing the values through.
-* `action_dispatch.configure` Configures the `ActionDispatch::Http::URL.tld_length` to be set to the value of `config.action_dispatch.tld_length`.
+* `action_dispatch.configure`: Configures the `ActionDispatch::Http::URL.tld_length` to be set to the value of `config.action_dispatch.tld_length`.
-* `action_view.set_configs` Sets up Action View by using the settings in `config.action_view` by `send`'ing the method names as setters to `ActionView::Base` and passing the values through.
+* `action_view.set_configs`: Sets up Action View by using the settings in `config.action_view` by `send`'ing the method names as setters to `ActionView::Base` and passing the values through.
-* `action_controller.assets_config` Initializes the `config.actions_controller.assets_dir` to the app's public directory if not explicitly configured
+* `action_controller.assets_config`: Initializes the `config.actions_controller.assets_dir` to the app's public directory if not explicitly configured.
-* `action_controller.set_helpers_path` Sets Action Controller's helpers_path to the application's helpers_path
+* `action_controller.set_helpers_path`: Sets Action Controller's `helpers_path` to the application's `helpers_path`.
-* `action_controller.parameters_config` Configures strong parameters options for `ActionController::Parameters`
+* `action_controller.parameters_config`: Configures strong parameters options for `ActionController::Parameters`.
-* `action_controller.set_configs` Sets up Action Controller by using the settings in `config.action_controller` by `send`'ing the method names as setters to `ActionController::Base` and passing the values through.
+* `action_controller.set_configs`: Sets up Action Controller by using the settings in `config.action_controller` by `send`'ing the method names as setters to `ActionController::Base` and passing the values through.
-* `action_controller.compile_config_methods` Initializes methods for the config settings specified so that they are quicker to access.
+* `action_controller.compile_config_methods`: Initializes methods for the config settings specified so that they are quicker to access.
-* `active_record.initialize_timezone` Sets `ActiveRecord::Base.time_zone_aware_attributes` to true, as well as setting `ActiveRecord::Base.default_timezone` to UTC. When attributes are read from the database, they will be converted into the time zone specified by `Time.zone`.
+* `active_record.initialize_timezone`: Sets `ActiveRecord::Base.time_zone_aware_attributes` to `true`, as well as setting `ActiveRecord::Base.default_timezone` to UTC. When attributes are read from the database, they will be converted into the time zone specified by `Time.zone`.
-* `active_record.logger` Sets `ActiveRecord::Base.logger` - if it's not already set - to `Rails.logger`.
+* `active_record.logger`: Sets `ActiveRecord::Base.logger` - if it's not already set - to `Rails.logger`.
-* `active_record.migration_error` Configures middleware to check for pending migrations
+* `active_record.migration_error`: Configures middleware to check for pending migrations.
-* `active_record.check_schema_cache_dump` Loads the schema cache dump if configured and available
+* `active_record.check_schema_cache_dump`: Loads the schema cache dump if configured and available.
-* `active_record.warn_on_records_fetched_greater_than` Enables warnings when queries return large numbers of records
+* `active_record.warn_on_records_fetched_greater_than`: Enables warnings when queries return large numbers of records.
-* `active_record.set_configs` Sets up Active Record by using the settings in `config.active_record` by `send`'ing the method names as setters to `ActiveRecord::Base` and passing the values through.
+* `active_record.set_configs`: Sets up Active Record by using the settings in `config.active_record` by `send`'ing the method names as setters to `ActiveRecord::Base` and passing the values through.
-* `active_record.initialize_database` Loads the database configuration (by default) from `config/database.yml` and establishes a connection for the current environment.
+* `active_record.initialize_database`: Loads the database configuration (by default) from `config/database.yml` and establishes a connection for the current environment.
-* `active_record.log_runtime` Includes `ActiveRecord::Railties::ControllerRuntime` which is responsible for reporting the time taken by Active Record calls for the request back to the logger.
+* `active_record.log_runtime`: Includes `ActiveRecord::Railties::ControllerRuntime` which is responsible for reporting the time taken by Active Record calls for the request back to the logger.
-* `active_record.set_reloader_hooks` Resets all reloadable connections to the database if `config.cache_classes` is set to `false`.
+* `active_record.set_reloader_hooks`: Resets all reloadable connections to the database if `config.cache_classes` is set to `false`.
-* `active_record.add_watchable_files` Adds `schema.rb` and `structure.sql` files to watchable files
+* `active_record.add_watchable_files`: Adds `schema.rb` and `structure.sql` files to watchable files.
-* `active_job.logger` Sets `ActiveJob::Base.logger` - if it's not already set -
+* `active_job.logger`: Sets `ActiveJob::Base.logger` - if it's not already set -
to `Rails.logger`.
-* `active_job.set_configs` Sets up Active Job by using the settings in `config.active_job` by `send`'ing the method names as setters to `ActiveJob::Base` and passing the values through.
+* `active_job.set_configs`: Sets up Active Job by using the settings in `config.active_job` by `send`'ing the method names as setters to `ActiveJob::Base` and passing the values through.
-* `action_mailer.logger` Sets `ActionMailer::Base.logger` - if it's not already set - to `Rails.logger`.
+* `action_mailer.logger`: Sets `ActionMailer::Base.logger` - if it's not already set - to `Rails.logger`.
-* `action_mailer.set_configs` Sets up Action Mailer by using the settings in `config.action_mailer` by `send`'ing the method names as setters to `ActionMailer::Base` and passing the values through.
+* `action_mailer.set_configs`: Sets up Action Mailer by using the settings in `config.action_mailer` by `send`'ing the method names as setters to `ActionMailer::Base` and passing the values through.
-* `action_mailer.compile_config_methods` Initializes methods for the config settings specified so that they are quicker to access.
+* `action_mailer.compile_config_methods`: Initializes methods for the config settings specified so that they are quicker to access.
-* `set_load_path` This initializer runs before `bootstrap_hook`. Adds paths specified by `config.load_paths` and all autoload paths to `$LOAD_PATH`.
+* `set_load_path`: This initializer runs before `bootstrap_hook`. Adds paths specified by `config.load_paths` and all autoload paths to `$LOAD_PATH`.
-* `set_autoload_paths` This initializer runs before `bootstrap_hook`. Adds all sub-directories of `app` and paths specified by `config.autoload_paths`, `config.eager_load_paths` and `config.autoload_once_paths` to `ActiveSupport::Dependencies.autoload_paths`.
+* `set_autoload_paths`: This initializer runs before `bootstrap_hook`. Adds all sub-directories of `app` and paths specified by `config.autoload_paths`, `config.eager_load_paths` and `config.autoload_once_paths` to `ActiveSupport::Dependencies.autoload_paths`.
-* `add_routing_paths` Loads (by default) all `config/routes.rb` files (in the application and railties, including engines) and sets up the routes for the application.
+* `add_routing_paths`: Loads (by default) all `config/routes.rb` files (in the application and railties, including engines) and sets up the routes for the application.
-* `add_locales` Adds the files in `config/locales` (from the application, railties and engines) to `I18n.load_path`, making available the translations in these files.
+* `add_locales`: Adds the files in `config/locales` (from the application, railties and engines) to `I18n.load_path`, making available the translations in these files.
-* `add_view_paths` Adds the directory `app/views` from the application, railties and engines to the lookup path for view files for the application.
+* `add_view_paths`: Adds the directory `app/views` from the application, railties and engines to the lookup path for view files for the application.
-* `load_environment_config` Loads the `config/environments` file for the current environment.
+* `load_environment_config`: Loads the `config/environments` file for the current environment.
-* `prepend_helpers_path` Adds the directory `app/helpers` from the application, railties and engines to the lookup path for helpers for the application.
+* `prepend_helpers_path`: Adds the directory `app/helpers` from the application, railties and engines to the lookup path for helpers for the application.
-* `load_config_initializers` Loads all Ruby files from `config/initializers` in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks are loaded.
+* `load_config_initializers`: Loads all Ruby files from `config/initializers` in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks are loaded.
-* `engines_blank_point` Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are run.
+* `engines_blank_point`: Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are run.
-* `add_generator_templates` Finds templates for generators at `lib/templates` for the application, railties and engines and adds these to the `config.generators.templates` setting, which will make the templates available for all generators to reference.
+* `add_generator_templates`: Finds templates for generators at `lib/templates` for the application, railties and engines and adds these to the `config.generators.templates` setting, which will make the templates available for all generators to reference.
-* `ensure_autoload_once_paths_as_subset` Ensures that the `config.autoload_once_paths` only contains paths from `config.autoload_paths`. If it contains extra paths, then an exception will be raised.
+* `ensure_autoload_once_paths_as_subset`: Ensures that the `config.autoload_once_paths` only contains paths from `config.autoload_paths`. If it contains extra paths, then an exception will be raised.
-* `add_to_prepare_blocks` The block for every `config.to_prepare` call in the application, a railtie or engine is added to the `to_prepare` callbacks for Action Dispatch which will be run per request in development, or before the first request in production.
+* `add_to_prepare_blocks`: The block for every `config.to_prepare` call in the application, a railtie or engine is added to the `to_prepare` callbacks for Action Dispatch which will be run per request in development, or before the first request in production.
-* `add_builtin_route` If the application is running under the development environment then this will append the route for `rails/info/properties` to the application routes. This route provides the detailed information such as Rails and Ruby version for `public/index.html` in a default Rails application.
+* `add_builtin_route`: If the application is running under the development environment then this will append the route for `rails/info/properties` to the application routes. This route provides the detailed information such as Rails and Ruby version for `public/index.html` in a default Rails application.
-* `build_middleware_stack` Builds the middleware stack for the application, returning an object which has a `call` method which takes a Rack environment object for the request.
+* `build_middleware_stack`: Builds the middleware stack for the application, returning an object which has a `call` method which takes a Rack environment object for the request.
-* `eager_load!` If `config.eager_load` is true, runs the `config.before_eager_load` hooks and then calls `eager_load!` which will load all `config.eager_load_namespaces`.
+* `eager_load!`: If `config.eager_load` is `true`, runs the `config.before_eager_load` hooks and then calls `eager_load!` which will load all `config.eager_load_namespaces`.
-* `finisher_hook` Provides a hook for after the initialization of process of the application is complete, as well as running all the `config.after_initialize` blocks for the application, railties and engines.
+* `finisher_hook`: Provides a hook for after the initialization of process of the application is complete, as well as running all the `config.after_initialize` blocks for the application, railties and engines.
-* `set_routes_reloader` Configures Action Dispatch to reload the routes file using `ActionDispatch::Callbacks.to_prepare`.
+* `set_routes_reloader`: Configures Action Dispatch to reload the routes file using `ActionDispatch::Callbacks.to_prepare`.
-* `disable_dependency_loading` Disables the automatic dependency loading if the `config.eager_load` is set to true.
+* `disable_dependency_loading`: Disables the automatic dependency loading if the `config.eager_load` is set to `true`.
Database pooling
----------------
@@ -1231,7 +1238,7 @@ Evented File System Monitor
If the [listen gem](https://github.com/guard/listen) is loaded Rails uses an
evented file system monitor to detect changes when `config.cache_classes` is
-false:
+`false`:
```ruby
group :development do
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index 12d0280116..59c902e148 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -130,11 +130,11 @@ learn about Ruby on Rails, and the API, which serves as a reference.
You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing them up to date with the latest edge Rails.
-You can either open a pull request to [Rails](http://github.com/rails/rails) or
+You can either open a pull request to [Rails](https://github.com/rails/rails) or
ask the [Rails core team](http://rubyonrails.org/core) for commit access on
docrails if you contribute regularly.
Please do not open pull requests in docrails, if you'd like to get feedback on your
-change, ask for it in [Rails](http://github.com/rails/rails) instead.
+change, ask for it in [Rails](https://github.com/rails/rails) instead.
Docrails is merged with master regularly, so you are effectively editing the Ruby on Rails documentation.
@@ -174,8 +174,8 @@ Translation efforts we know about (various versions):
* **Italian**: [https://github.com/rixlabs/docrails](https://github.com/rixlabs/docrails)
* **Spanish**: [http://wiki.github.com/gramos/docrails](http://wiki.github.com/gramos/docrails)
-* **Polish**: [http://github.com/apohllo/docrails/tree/master](http://github.com/apohllo/docrails/tree/master)
-* **French** : [http://github.com/railsfrance/docrails](http://github.com/railsfrance/docrails)
+* **Polish**: [https://github.com/apohllo/docrails/tree/master](https://github.com/apohllo/docrails/tree/master)
+* **French** : [https://github.com/railsfrance/docrails](https://github.com/railsfrance/docrails)
* **Czech** : [https://github.com/rubyonrails-cz/docrails/tree/czech](https://github.com/rubyonrails-cz/docrails/tree/czech)
* **Turkish** : [https://github.com/ujk/docrails/tree/master](https://github.com/ujk/docrails/tree/master)
* **Korean** : [https://github.com/rorlakr/rails-guides](https://github.com/rorlakr/rails-guides)
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index 877c87e9fa..f0d0f9753a 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -255,7 +255,8 @@ is your best companion.
The debugger can also help you if you want to learn about the Rails source code
but don't know where to start. Just debug any request to your application and
-use this guide to learn how to move from the code you have written into the underlying Rails code.
+use this guide to learn how to move from the code you have written into the
+underlying Rails code.
### Setup
@@ -315,13 +316,11 @@ For example:
=> Rails 5.0.0 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
-* Version 3.0.2 (ruby 2.3.0-p0), codename: Plethora of Penguin Pinatas
+* Version 3.4.0 (ruby 2.3.1-p112), codename: Owl Bowl Brawl
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3000
Use Ctrl-C to stop
-
-
Started GET "/" for 127.0.0.1 at 2014-04-11 13:11:48 +0200
ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by ArticlesController#index as HTML
@@ -337,7 +336,6 @@ Processing by ArticlesController#index as HTML
10: respond_to do |format|
11: format.html # index.html.erb
12: format.json { render json: @articles }
-
(byebug)
```
@@ -347,11 +345,45 @@ by asking the debugger for help. Type: `help`
```
(byebug) help
- h[elp][ <cmd>[ <subcmd>]]
+ break -- Sets breakpoints in the source code
+ catch -- Handles exception catchpoints
+ condition -- Sets conditions on breakpoints
+ continue -- Runs until program ends, hits a breakpoint or reaches a line
+ debug -- Spawns a subdebugger
+ delete -- Deletes breakpoints
+ disable -- Disables breakpoints or displays
+ display -- Evaluates expressions every time the debugger stops
+ down -- Moves to a lower frame in the stack trace
+ edit -- Edits source files
+ enable -- Enables breakpoints or displays
+ finish -- Runs the program until frame returns
+ frame -- Moves to a frame in the call stack
+ help -- Helps you using byebug
+ history -- Shows byebug's history of commands
+ info -- Shows several informations about the program being debugged
+ interrupt -- Interrupts the program
+ irb -- Starts an IRB session
+ kill -- Sends a signal to the current process
+ list -- Lists lines of source code
+ method -- Shows methods of an object, class or module
+ next -- Runs one or more lines of code
+ pry -- Starts a Pry session
+ quit -- Exits byebug
+ restart -- Restarts the debugged program
+ save -- Saves current byebug session to a file
+ set -- Modifies byebug settings
+ show -- Shows byebug settings
+ source -- Restores a previously saved byebug session
+ step -- Steps into blocks or methods one or more times
+ thread -- Commands to manipulate threads
+ tracevar -- Enables tracing of a global variable
+ undisplay -- Stops displaying all or some expressions when program stops
+ untracevar -- Stops tracing a global variable
+ up -- Moves to a higher frame in the stack trace
+ var -- Shows variables and its values
+ where -- Displays the backtrace
- help -- prints this help.
- help <cmd> -- prints help on command <cmd>.
- help <cmd> <subcmd> -- prints help on <cmd>'s subcommand <subcmd>.
+(byebug)
```
To see the previous ten lines you should type `list-` (or `l-`).
@@ -370,12 +402,11 @@ To see the previous ten lines you should type `list-` (or `l-`).
8 @articles = Article.find_recent
9
10 respond_to do |format|
-
```
-This way you can move inside the file and see the code above
-the line where you added the `byebug` call. Finally, to see where you are in
-the code again you can type `list=`
+This way you can move inside the file and see the code above the line where you
+added the `byebug` call. Finally, to see where you are in the code again you can
+type `list=`
```
(byebug) list=
@@ -391,7 +422,6 @@ the code again you can type `list=`
10: respond_to do |format|
11: format.html # index.html.erb
12: format.json { render json: @articles }
-
(byebug)
```
@@ -413,46 +443,45 @@ then `backtrace` will supply the answer.
```
(byebug) where
--> #0 ArticlesController.index
- at /PathTo/project/test_app/app/controllers/articles_controller.rb:8
- #1 ActionController::ImplicitRender.send_action(method#String, *args#Array)
- at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/implicit_render.rb:4
+ at /PathToProject/app/controllers/articles_controller.rb:8
+ #1 ActionController::BasicImplicitRender.send_action(method#String, *args#Array)
+ at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/basic_implicit_render.rb:4
#2 AbstractController::Base.process_action(action#NilClass, *args#Array)
- at /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb:189
- #3 ActionController::Rendering.process_action(action#NilClass, *args#NilClass)
- at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/rendering.rb:10
+ at /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb:181
+ #3 ActionController::Rendering.process_action(action, *args)
+ at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/rendering.rb:30
...
```
The current frame is marked with `-->`. You can move anywhere you want in this
-trace (thus changing the context) by using the `frame _n_` command, where _n_ is
+trace (thus changing the context) by using the `frame n` command, where _n_ is
the specified frame number. If you do that, `byebug` will display your new
context.
```
(byebug) frame 2
-[184, 193] in /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb
- 184: # is the intended way to override action dispatching.
- 185: #
- 186: # Notice that the first argument is the method to be dispatched
- 187: # which is *not* necessarily the same as the action name.
- 188: def process_action(method_name, *args)
-=> 189: send_action(method_name, *args)
- 190: end
- 191:
- 192: # Actually call the method associated with the action. Override
- 193: # this method if you wish to change how action methods are called,
-
+[176, 185] in /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb
+ 176: # is the intended way to override action dispatching.
+ 177: #
+ 178: # Notice that the first argument is the method to be dispatched
+ 179: # which is *not* necessarily the same as the action name.
+ 180: def process_action(method_name, *args)
+=> 181: send_action(method_name, *args)
+ 182: end
+ 183:
+ 184: # Actually call the method associated with the action. Override
+ 185: # this method if you wish to change how action methods are called,
(byebug)
```
The available variables are the same as if you were running the code line by
line. After all, that's what debugging is.
-You can also use `up [n]` (`u` for abbreviated) and `down [n]` commands in order
-to change the context _n_ frames up or down the stack respectively. _n_ defaults
-to one. Up in this case is towards higher-numbered stack frames, and down is
-towards lower-numbered stack frames.
+You can also use `up [n]` and `down [n]` commands in order to change the context
+_n_ frames up or down the stack respectively. _n_ defaults to one. Up in this
+case is towards higher-numbered stack frames, and down is towards lower-numbered
+stack frames.
### Threads
@@ -461,11 +490,11 @@ the `thread` command (or the abbreviated `th`). This command has a handful of
options:
* `thread`: shows the current thread.
-* `thread list`: is used to list all threads and their statuses. The plus +
-character and the number indicates the current thread of execution.
-* `thread stop _n_`: stop thread _n_.
-* `thread resume _n_`: resumes thread _n_.
-* `thread switch _n_`: switches the current thread context to _n_.
+* `thread list`: is used to list all threads and their statuses. The current
+thread is marked with a plus (+) sign.
+* `thread stop n`: stops thread _n_.
+* `thread resume n`: resumes thread _n_.
+* `thread switch n`: switches the current thread context to _n_.
This command is very helpful when you are debugging concurrent threads and need
to verify that there are no race conditions in your code.
@@ -492,9 +521,9 @@ current context:
12: format.json { render json: @articles }
(byebug) instance_variables
-[:@_action_has_layout, :@_routes, :@_headers, :@_status, :@_request,
- :@_response, :@_prefixes, :@_lookup_context, :@_action_name,
- :@_response_body, :@marked_for_same_origin_verification, :@_config]
+[:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lookup_context,
+ :@_action_name, :@_response_body, :@marked_for_same_origin_verification,
+ :@_config]
```
As you may have figured out, all of the variables that you can access from a
@@ -504,6 +533,7 @@ command later in this guide).
```
(byebug) next
+
[5, 14] in /PathTo/project/app/controllers/articles_controller.rb
5 # GET /articles.json
6 def index
@@ -523,29 +553,35 @@ And then ask again for the instance_variables:
```
(byebug) instance_variables
-[:@_action_has_layout, :@_routes, :@_headers, :@_status, :@_request,
- :@_response, :@_prefixes, :@_lookup_context, :@_action_name,
- :@_response_body, :@marked_for_same_origin_verification, :@_config,
- :@articles]
+[:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lookup_context,
+ :@_action_name, :@_response_body, :@marked_for_same_origin_verification,
+ :@_config, :@articles]
```
-Now `@articles` is included in the instance variables, because the line defining it
-was executed.
+Now `@articles` is included in the instance variables, because the line defining
+it was executed.
TIP: You can also step into **irb** mode with the command `irb` (of course!).
-This will start an irb session within the context you invoked it. But
-be warned: this is an experimental feature.
+This will start an irb session within the context you invoked it.
The `var` method is the most convenient way to show variables and their values.
Let's have `byebug` help us with it.
```
(byebug) help var
-v[ar] cl[ass] show class variables of self
-v[ar] const <object> show constants of object
-v[ar] g[lobal] show global variables
-v[ar] i[nstance] <object> show instance variables of object
-v[ar] l[ocal] show local variables
+
+ [v]ar <subcommand>
+
+ Shows variables and its values
+
+
+ var all -- Shows local, global and instance variables of self.
+ var args -- Information about arguments of the current scope
+ var const -- Shows constants of an object.
+ var global -- Shows global variables.
+ var instance -- Shows instance variables of self or a specific object.
+ var local -- Shows local variables in current scope.
+
```
This is a great way to inspect the values of the current context variables. For
@@ -563,16 +599,17 @@ You can also inspect for an object method this way:
@_start_transaction_state = {}
@aggregation_cache = {}
@association_cache = {}
-@attributes = {"id"=>nil, "created_at"=>nil, "updated_at"=>nil}
-@attributes_cache = {}
-@changed_attributes = nil
-...
+@attributes = #<ActiveRecord::AttributeSet:0x007fd0682a9b18 @attributes={"id"=>#<ActiveRecord::Attribute::FromDatabase:0x007fd0682a9a00 @name="id", @value_be...
+@destroyed = false
+@destroyed_by_association = nil
+@marked_for_destruction = false
+@new_record = true
+@readonly = false
+@transaction_state = nil
+@txn = nil
```
-TIP: The commands `p` (print) and `pp` (pretty print) can be used to evaluate
-Ruby expressions and display the value of variables to the console.
-
-You can use also `display` to start watching variables. This is a good way of
+You can also use `display` to start watching variables. This is a good way of
tracking the values of a variable while the execution goes on.
```
@@ -581,7 +618,7 @@ tracking the values of a variable while the execution goes on.
```
The variables inside the displayed list will be printed with their values after
-you move in the stack. To stop displaying a variable use `undisplay _n_` where
+you move in the stack. To stop displaying a variable use `undisplay n` where
_n_ is the variable number (1 in the last example).
### Step by Step
@@ -591,32 +628,23 @@ available variables. But let's continue and move on with the application
execution.
Use `step` (abbreviated `s`) to continue running your program until the next
-logical stopping point and return control to the debugger.
-
-You may also use `next` which is similar to step, but function or method calls
-that appear within the line of code are executed without stopping.
-
-TIP: You can also use `step n` or `next n` to move forwards `n` steps at once.
-
-The difference between `next` and `step` is that `step` stops at the next line
-of code executed, doing just a single step, while `next` moves to the next line
-without descending inside methods.
+logical stopping point and return control to the debugger. `next` is similar to
+`step`, but while `step` stops at the next line of code executed, doing just a
+single step, `next` moves to the next line without descending inside methods.
For example, consider the following situation:
-```ruby
+```
Started GET "/" for 127.0.0.1 at 2014-04-11 13:39:23 +0200
Processing by ArticlesController#index as HTML
-[1, 8] in /home/davidr/Proyectos/test_app/app/models/article.rb
+[1, 6] in /PathToProject/app/models/article.rb
1: class Article < ApplicationRecord
- 2:
- 3: def self.find_recent(limit = 10)
- 4: byebug
-=> 5: where('created_at > ?', 1.week.ago).limit(limit)
- 6: end
- 7:
- 8: end
+ 2: def self.find_recent(limit = 10)
+ 3: byebug
+=> 4: where('created_at > ?', 1.week.ago).limit(limit)
+ 5: end
+ 6: end
(byebug)
```
@@ -628,11 +656,7 @@ method.
```
(byebug) next
-
-Next advances to the next line (line 6: `end`), which returns to the next line
-of the caller method:
-
-[4, 13] in /PathTo/project/test_app/app/controllers/articles_controller.rb
+[4, 13] in /PathToProject/app/controllers/articles_controller.rb
4: # GET /articles
5: # GET /articles.json
6: def index
@@ -653,23 +677,24 @@ Ruby instruction to be executed -- in this case, Active Support's `week` method.
```
(byebug) step
-[50, 59] in /PathToGems/activesupport-5.0.0/lib/active_support/core_ext/numeric/time.rb
- 50: ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
- 51: end
- 52: alias :day :days
- 53:
- 54: def weeks
-=> 55: ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
- 56: end
- 57: alias :week :weeks
- 58:
- 59: def fortnights
-
+[49, 58] in /PathToGems/activesupport-5.0.0/lib/active_support/core_ext/numeric/time.rb
+ 49:
+ 50: # Returns a Duration instance matching the number of weeks provided.
+ 51: #
+ 52: # 2.weeks # => 14 days
+ 53: def weeks
+=> 54: ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
+ 55: end
+ 56: alias :week :weeks
+ 57:
+ 58: # Returns a Duration instance matching the number of fortnights provided.
(byebug)
```
This is one of the best ways to find bugs in your code.
+TIP: You can also use `step n` or `next n` to move forward `n` steps at once.
+
### Breakpoints
A breakpoint makes your application stop whenever a certain point in the program
@@ -678,19 +703,18 @@ is reached. The debugger shell is invoked in that line.
You can add breakpoints dynamically with the command `break` (or just `b`).
There are 3 possible ways of adding breakpoints manually:
-* `break line`: set breakpoint in the _line_ in the current source file.
-* `break file:line [if expression]`: set breakpoint in the _line_ number inside
-the _file_. If an _expression_ is given it must evaluated to _true_ to fire up
-the debugger.
+* `break n`: set breakpoint in line number _n_ in the current source file.
+* `break file:n [if expression]`: set breakpoint in line number _n_ inside
+file named _file_. If an _expression_ is given it must evaluated to _true_ to
+fire up the debugger.
* `break class(.|\#)method [if expression]`: set breakpoint in _method_ (. and
\# for class and instance method respectively) defined in _class_. The
-_expression_ works the same way as with file:line.
-
+_expression_ works the same way as with file:n.
For example, in the previous situation
```
-[4, 13] in /PathTo/project/app/controllers/articles_controller.rb
+[4, 13] in /PathToProject/app/controllers/articles_controller.rb
4: # GET /articles
5: # GET /articles.json
6: def index
@@ -703,20 +727,20 @@ For example, in the previous situation
13: end
(byebug) break 11
-Created breakpoint 1 at /PathTo/project/app/controllers/articles_controller.rb:11
+Successfully created breakpoint with id 1
```
-Use `info breakpoints _n_` or `info break _n_` to list breakpoints. If you
-supply a number, it lists that breakpoint. Otherwise it lists all breakpoints.
+Use `info breakpoints` to list breakpoints. If you supply a number, it lists
+that breakpoint. Otherwise it lists all breakpoints.
```
(byebug) info breakpoints
Num Enb What
-1 y at /PathTo/project/app/controllers/articles_controller.rb:11
+1 y at /PathToProject/app/controllers/articles_controller.rb:11
```
-To delete breakpoints: use the command `delete _n_` to remove the breakpoint
+To delete breakpoints: use the command `delete n` to remove the breakpoint
number _n_. If no number is specified, it deletes all breakpoints that are
currently active.
@@ -728,10 +752,11 @@ No breakpoints.
You can also enable or disable breakpoints:
-* `enable breakpoints`: allow a _breakpoints_ list or all of them if no list is
-specified, to stop your program. This is the default state when you create a
+* `enable breakpoints [n [m [...]]]`: allows a specific breakpoint list or all
+breakpoints to stop your program. This is the default state when you create a
breakpoint.
-* `disable breakpoints`: the _breakpoints_ will have no effect on your program.
+* `disable breakpoints [n [m [...]]]`: make certain (or all) breakpoints have
+no effect on your program.
### Catching Exceptions
@@ -746,24 +771,22 @@ To list all active catchpoints use `catch`.
There are two ways to resume execution of an application that is stopped in the
debugger:
-* `continue [line-specification]` \(or `c`): resume program execution, at the
-address where your script last stopped; any breakpoints set at that address are
-bypassed. The optional argument line-specification allows you to specify a line
-number to set a one-time breakpoint which is deleted when that breakpoint is
-reached.
-* `finish [frame-number]` \(or `fin`): execute until the selected stack frame
-returns. If no frame number is given, the application will run until the
-currently selected frame returns. The currently selected frame starts out the
-most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been
-performed. If a frame number is given it will run until the specified frame
-returns.
+* `continue [n]`: resumes program execution at the address where your script last
+stopped; any breakpoints set at that address are bypassed. The optional argument
+`n` allows you to specify a line number to set a one-time breakpoint which is
+deleted when that breakpoint is reached.
+* `finish [n]`: execute until the selected stack frame returns. If no frame
+number is given, the application will run until the currently selected frame
+returns. The currently selected frame starts out the most-recent frame or 0 if
+no frame positioning (e.g up, down or frame) has been performed. If a frame
+number is given it will run until the specified frame returns.
### Editing
Two commands allow you to open code from the debugger into an editor:
-* `edit [file:line]`: edit _file_ using the editor specified by the EDITOR
-environment variable. A specific _line_ can also be given.
+* `edit [file:n]`: edit file named _file_ using the editor specified by the
+EDITOR environment variable. A specific line _n_ can also be given.
### Quitting
@@ -777,21 +800,43 @@ will be stopped and you will have to start it again.
`byebug` has a few available options to tweak its behavior:
-* `set autoreload`: Reload source code when changed (defaults: true).
-* `set autolist`: Execute `list` command on every breakpoint (defaults: true).
-* `set listsize _n_`: Set number of source lines to list by default to _n_
-(defaults: 10)
-* `set forcestep`: Make sure the `next` and `step` commands always move to a new
-line.
+```
+(byebug) help set
+
+ set <setting> <value>
+
+ Modifies byebug settings
-You can see the full list by using `help set`. Use `help set _subcommand_` to
-learn about a particular `set` command.
+ Boolean values take "on", "off", "true", "false", "1" or "0". If you
+ don't specify a value, the boolean setting will be enabled. Conversely,
+ you can use "set no<setting>" to disable them.
+
+ You can see these environment settings with the "show" command.
+
+ List of supported settings:
+
+ autosave -- Automatically save command history record on exit
+ autolist -- Invoke list command on every stop
+ width -- Number of characters per line in byebug's output
+ autoirb -- Invoke IRB on every stop
+ basename -- <file>:<line> information after every stop uses short paths
+ linetrace -- Enable line execution tracing
+ autopry -- Invoke Pry on every stop
+ stack_on_error -- Display stack trace when `eval` raises an exception
+ fullpath -- Display full file names in backtraces
+ histfile -- File where cmd history is saved to. Default: ./.byebug_history
+ listsize -- Set number of source lines to list by default
+ post_mortem -- Enable/disable post-mortem mode
+ callstyle -- Set how you want method call parameters to be displayed
+ histsize -- Maximum number of commands that can be stored in byebug history
+ savefile -- File where settings are saved to. Default: ~/.byebug_save
+```
TIP: You can save these settings in an `.byebugrc` file in your home directory.
The debugger reads these global settings when it starts. For example:
```bash
-set forcestep
+set callstyle short
set listsize 25
```
diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml
index 03943d0f25..a5b8a75509 100644
--- a/guides/source/documents.yaml
+++ b/guides/source/documents.yaml
@@ -139,6 +139,10 @@
name: Using Rails for API-only Applications
url: api_app.html
description: This guide explains how to effectively use Rails to develop a JSON API application.
+ -
+ name: Action Cable Overview
+ url: action_cable_overview.html
+ description: This guide explains how Action Cable works, and how to use WebSockets to create real-time features.
-
name: Extending Rails
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index ae631ae58d..65fdd7ca0d 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -93,7 +93,7 @@ current version of Ruby installed:
```bash
$ ruby -v
-ruby 2.3.0p0
+ruby 2.3.1p112
```
TIP: A number of tools exist to help you quickly install Ruby and Ruby
@@ -164,7 +164,7 @@ of the files and folders that Rails created by default:
| File/Folder | Purpose |
| ----------- | ------- |
-|app/|Contains the controllers, models, views, helpers, mailers and assets for your application. You'll focus on this folder for the remainder of this guide.|
+|app/|Contains the controllers, models, views, helpers, mailers, channels, jobs and assets for your application. You'll focus on this folder for the remainder of this guide.|
|bin/|Contains the rails script that starts your app and can contain other scripts you use to setup, update, deploy or run your application.|
|config/|Configure your application's routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html).|
|config.ru|Rack configuration for Rack based servers used to start the application.|
@@ -244,11 +244,11 @@ Ruby) which is processed by the request cycle in Rails before being sent to the
user.
To create a new controller, you will need to run the "controller" generator and
-tell it you want a controller called "welcome" with an action called "index",
+tell it you want a controller called "Welcome" with an action called "index",
just like this:
```bash
-$ bin/rails generate controller welcome index
+$ bin/rails generate controller Welcome index
```
Rails will create several files and a route for you.
@@ -263,6 +263,7 @@ invoke test_unit
create test/controllers/welcome_controller_test.rb
invoke helper
create app/helpers/welcome_helper.rb
+invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/welcome.coffee
@@ -327,7 +328,7 @@ end
application to the welcome controller's index action and `get 'welcome/index'`
tells Rails to map requests to <http://localhost:3000/welcome/index> to the
welcome controller's index action. This was created earlier when you ran the
-controller generator (`bin/rails generate controller welcome index`).
+controller generator (`bin/rails generate controller Welcome index`).
Launch the web server again if you stopped it to generate the controller (`bin/rails
server`) and navigate to <http://localhost:3000> in your browser. You'll see the
@@ -390,7 +391,7 @@ create and read. The form for doing this will look like this:
It will look a little basic for now, but that's ok. We'll look at improving the
styling for it afterwards.
-### Laying down the ground work
+### Laying down the groundwork
Firstly, you need a place within the application to create a new article. A
great place for that would be at `/articles/new`. With the route already
@@ -406,7 +407,7 @@ a controller called `ArticlesController`. You can do this by running this
command:
```bash
-$ bin/rails generate controller articles
+$ bin/rails generate controller Articles
```
If you open up the newly generated `app/controllers/articles_controller.rb`
@@ -474,7 +475,7 @@ one here because the `ArticlesController` inherits from `ApplicationController`.
The next part of the message contains a hash. The `:locale` key in this hash
simply indicates which spoken language template should be retrieved. By default,
this is the English - or "en" - template. The next key, `:formats` specifies the
-format of template to be served in response. The default format is `:html`, and
+format of the template to be served in response. The default format is `:html`, and
so Rails is looking for an HTML template. The final key, `:handlers`, is telling
us what _template handlers_ could be used to render our template. `:erb` is most
commonly used for HTML templates, `:builder` is used for XML templates, and
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index b712965b7f..d67702f52e 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -104,7 +104,7 @@ For a freshly generated Rails application, this might produce something like:
```ruby
use Rack::Sendfile
use ActionDispatch::Static
-use ActionDispatch::LoadInterlock
+use ActionDispatch::Executor
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x000000029a0838>
use Rack::Runtime
use Rack::MethodOverride
@@ -219,7 +219,7 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol
* Sets `env["rack.multithread"]` flag to `false` and wraps the application within a Mutex.
-**`ActionDispatch::LoadInterlock`**
+**`ActionDispatch::Executor`**
* Used for thread safe code reloading during development.
diff --git a/guides/source/security.md b/guides/source/security.md
index 16c5291037..ca985134e6 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -41,24 +41,24 @@ NOTE: _HTTP is a stateless protocol. Sessions make it stateful._
Most applications need to keep track of certain state of a particular user. This could be the contents of a shopping basket or the user id of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request.
Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application.
-A session usually consists of a hash of values and a session id, usually a 32-character string, to identify the hash. Every cookie sent to the client's browser includes the session id. And the other way round: the browser will send it to the server on every request from the client. In Rails you can save and retrieve values using the session method:
+A session usually consists of a hash of values and a session ID, usually a 32-character string, to identify the hash. Every cookie sent to the client's browser includes the session ID. And the other way round: the browser will send it to the server on every request from the client. In Rails you can save and retrieve values using the session method:
```ruby
session[:user_id] = @current_user.id
User.find(session[:user_id])
```
-### Session id
+### Session ID
-NOTE: _The session id is a 32 byte long MD5 hash value._
+NOTE: _The session ID is a 32-character random hex string._
-A session id consists of the hash value of a random string. The random string is the current time, a random number between 0 and 1, the process id number of the Ruby interpreter (also basically a random number) and a constant string. Currently it is not feasible to brute-force Rails' session ids. To date MD5 is uncompromised, but there have been collisions, so it is theoretically possible to create another input text with the same hash value. But this has had no security impact to date.
+The session ID is generated using `SecureRandom.hex` which generates a random hex string using platform specific methods (such as OpenSSL, /dev/urandom or Win32) for generating cryptographically secure random numbers. Currently it is not feasible to brute-force Rails' session IDs.
### Session Hijacking
-WARNING: _Stealing a user's session id lets an attacker use the web application in the victim's name._
+WARNING: _Stealing a user's session ID lets an attacker use the web application in the victim's name._
-Many web applications have an authentication system: a user provides a user name and password, the web application checks them and stores the corresponding user id in the session hash. From now on, the session is valid. On every request the application will load the user, identified by the user id in the session, without the need for new authentication. The session id in the cookie identifies the session.
+Many web applications have an authentication system: a user provides a user name and password, the web application checks them and stores the corresponding user id in the session hash. From now on, the session is valid. On every request the application will load the user, identified by the user id in the session, without the need for new authentication. The session ID in the cookie identifies the session.
Hence, the cookie serves as temporary authentication for the web application. Anyone who seizes a cookie from someone else, may use the web application as this user - with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures:
@@ -89,7 +89,7 @@ This will also be a good idea, if you modify the structure of an object and old
NOTE: _Rails provides several storage mechanisms for the session hashes. The most important is `ActionDispatch::Session::CookieStore`._
-Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session id. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it:
+Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session ID. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it:
* Cookies imply a strict size limit of 4kB. This is fine as you should not store large amounts of data in a session anyway, as described before. _Storing the current user's database id in a session is usually ok_.
@@ -137,16 +137,16 @@ The best _solution against it is not to store this kind of data in a session, bu
### Session Fixation
-NOTE: _Apart from stealing a user's session id, the attacker may fix a session id known to them. This is called session fixation._
+NOTE: _Apart from stealing a user's session ID, the attacker may fix a session ID known to them. This is called session fixation._
![Session fixation](images/session_fixation.png)
-This attack focuses on fixing a user's session id known to the attacker, and forcing the user's browser into using this id. It is therefore not necessary for the attacker to steal the session id afterwards. Here is how this attack works:
+This attack focuses on fixing a user's session ID known to the attacker, and forcing the user's browser into using this ID. It is therefore not necessary for the attacker to steal the session ID afterwards. Here is how this attack works:
-* The attacker creates a valid session id: They load the login page of the web application where they want to fix the session, and take the session id in the cookie from the response (see number 1 and 2 in the image).
+* The attacker creates a valid session ID: They load the login page of the web application where they want to fix the session, and take the session ID in the cookie from the response (see number 1 and 2 in the image).
* They maintain the session by accessing the web application periodically in order to keep an expiring session alive.
-* The attacker forces the user's browser into using this session id (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: `<script>document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";</script>`. Read more about XSS and injection later on.
-* The attacker lures the victim to the infected page with the JavaScript code. By viewing the page, the victim's browser will change the session id to the trap session id.
+* The attacker forces the user's browser into using this session ID (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: `<script>document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";</script>`. Read more about XSS and injection later on.
+* The attacker lures the victim to the infected page with the JavaScript code. By viewing the page, the victim's browser will change the session ID to the trap session ID.
* As the new trap session is unused, the web application will require the user to authenticate.
* From now on, the victim and the attacker will co-use the web application with the same session: The session became valid and the victim didn't notice the attack.
@@ -168,7 +168,7 @@ Another countermeasure is to _save user-specific properties in the session_, ver
NOTE: _Sessions that never expire extend the time-frame for attacks such as cross-site request forgery (CSRF), session hijacking and session fixation._
-One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to _expire sessions in a database table_. Call `Session.sweep("20 minutes")` to expire sessions that were used longer than 20 minutes ago.
+One possibility is to set the expiry time-stamp of the cookie with the session ID. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to _expire sessions in a database table_. Call `Session.sweep("20 minutes")` to expire sessions that were used longer than 20 minutes ago.
```ruby
class Session < ApplicationRecord
@@ -196,11 +196,11 @@ This attack method works by including malicious code or a link in a page that ac
![](images/csrf.png)
-In the [session chapter](#sessions) you have learned that most Rails applications use cookie-based sessions. Either they store the session id in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is that if the request comes from a site of a different domain, it will also send the cookie. Let's start with an example:
+In the [session chapter](#sessions) you have learned that most Rails applications use cookie-based sessions. Either they store the session ID in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is that if the request comes from a site of a different domain, it will also send the cookie. Let's start with an example:
* Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob's project management application, rather than an image file: `<img src="http://www.webapp.com/project/1/destroy">`
* Bob's session at `www.webapp.com` is still alive, because he didn't log out a few minutes ago.
-* By viewing the post, the browser finds an image tag. It tries to load the suspected image from `www.webapp.com`. As explained before, it will also send along the cookie with the valid session id.
+* By viewing the post, the browser finds an image tag. It tries to load the suspected image from `www.webapp.com`. As explained before, it will also send along the cookie with the valid session ID.
* The web application at `www.webapp.com` verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image.
* Bob doesn't notice the attack - but a few days later he finds out that project number one is gone.
@@ -567,7 +567,7 @@ This is alright for some web applications, but certainly not if the user is not
Depending on your web application, there will be many more parameters the user can tamper with. As a rule of thumb, _no user input data is secure, until proven otherwise, and every parameter from the user is potentially manipulated_.
-Don't be fooled by security by obfuscation and JavaScript security. The Web Developer Toolbar for Mozilla Firefox lets you review and change every form's hidden fields. _JavaScript can be used to validate user input data, but certainly not to prevent attackers from sending malicious requests with unexpected values_. The Live Http Headers plugin for Mozilla Firefox logs every request and may repeat and change them. That is an easy way to bypass any JavaScript validations. And there are even client-side proxies that allow you to intercept any request and response from and to the Internet.
+Don't be fooled by security by obfuscation and JavaScript security. Developer tools let you review and change every form's hidden fields. _JavaScript can be used to validate user input data, but certainly not to prevent attackers from sending malicious requests with unexpected values_. The Firebug addon for Mozilla Firefox logs every request and may repeat and change them. That is an easy way to bypass any JavaScript validations. And there are even client-side proxies that allow you to intercept any request and response from and to the Internet.
Injection
---------
@@ -677,14 +677,12 @@ INFO: _The most widespread, and one of the most devastating security vulnerabili
An entry point is a vulnerable URL and its parameters where an attacker can start an attack.
-The most common entry points are message posts, user comments, and guest books, but project titles, document names and search result pages have also been vulnerable - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter - obvious, hidden or internal. Remember that the user may intercept any traffic. Applications, such as the [Live HTTP Headers Firefox plugin](http://livehttpheaders.mozdev.org/), or client-site proxies make it easy to change requests.
+The most common entry points are message posts, user comments, and guest books, but project titles, document names and search result pages have also been vulnerable - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter - obvious, hidden or internal. Remember that the user may intercept any traffic. Applications or client-site proxies make it easy to change requests. There are also other attack vectors like banner advertisements.
XSS attacks work like this: An attacker injects some code, the web application saves it and displays it on a page, later presented to a victim. Most XSS examples simply display an alert box, but it is more powerful than that. XSS can steal the cookie, hijack the session, redirect the victim to a fake website, display advertisements for the benefit of the attacker, change elements on the web site to get confidential information or install malicious software through security holes in the web browser.
During the second half of 2007, there were 88 vulnerabilities reported in Mozilla browsers, 22 in Safari, 18 in IE, and 12 in Opera. The [Symantec Global Internet Security threat report](http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf) also documented 239 browser plug-in vulnerabilities in the last six months of 2007. [Mpack](http://pandalabs.pandasecurity.com/mpack-uncovered/) is a very active and up-to-date attack framework which exploits these vulnerabilities. For criminal hackers, it is very attractive to exploit an SQL-Injection vulnerability in a web application framework and insert malicious code in every textual table column. In April 2008 more than 510,000 sites were hacked like this, among them the British government, United Nations, and many more high targets.
-A relatively new, and unusual, form of entry points are banner advertisements. In earlier 2008, malicious code appeared in banner ads on popular sites, such as MySpace and Excite, according to [Trend Micro](http://blog.trendmicro.com/myspace-excite-and-blick-serve-up-malicious-banner-ads/).
-
#### HTML/JavaScript Injection
The most common XSS language is of course the most popular client-side scripting language JavaScript, often in combination with HTML. _Escaping user input is essential_.
@@ -722,7 +720,7 @@ The log files on www.attacker.com will read like this:
GET http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2
```
-You can mitigate these attacks (in the obvious way) by adding the **httpOnly** flag to cookies, so that document.cookie may not be read by JavaScript. Http only cookies can be used from IE v6.SP1, Firefox v2.0.0.5 and Opera 9.5. Safari is still considering, it ignores the option. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies [will still be visible using Ajax](https://www.owasp.org/index.php/HTTPOnly#Browsers_Supporting_HttpOnly), though.
+You can mitigate these attacks (in the obvious way) by adding the **httpOnly** flag to cookies, so that document.cookie may not be read by JavaScript. HTTP only cookies can be used from IE v6.SP1, Firefox v2.0.0.5, Opera 9.5, Safari 4 and Chrome 1.0.154 onwards. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies [will still be visible using Ajax](https://www.owasp.org/index.php/HTTPOnly#Browsers_Supporting_HttpOnly), though.
##### Defacement
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 34c831c802..050bdda9e3 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -1289,5 +1289,5 @@ end
assert_equal Date.new(2004, 10, 24), user.activation_date # The change was visible only inside the `travel_to` block.
```
-Please see [`ActiveSupport::TimeHelpers` API Documentation](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html)
+Please see [`ActiveSupport::Testing::TimeHelpers` API Documentation](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html)
for in-depth information about the available time helpers.
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 34cfb742a4..82080c4def 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -22,10 +22,10 @@ When changing Rails versions, it's best to move slowly, one minor version at a t
The process should go as follows:
-1. Write tests and make sure they pass
-2. Move to the latest patch version after your current version
-3. Fix tests and deprecated features
-4. Move to the latest patch version of the next minor version
+1. Write tests and make sure they pass.
+2. Move to the latest patch version after your current version.
+3. Fix tests and deprecated features.
+4. Move to the latest patch version of the next minor version.
Repeat this process until you reach your target Rails version. Each time you move versions, you will need to change the Rails version number in the Gemfile (and possibly other gem versions) and run `bundle update`. Then run the Update task mentioned below to update configuration files, then run your tests.
@@ -42,7 +42,7 @@ Rails generally stays close to the latest released Ruby version when it's releas
TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing.
-### The Task
+### The Update Task
Rails provides the `app:update` task (`rails:update` on 4.2 and earlier). After updating the Rails version
in the Gemfile, run this task.
@@ -881,7 +881,7 @@ Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must rep
* Rails 4.0 has removed the identity map from Active Record, due to [some inconsistencies with associations](https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6). If you have manually enabled it in your application, you will have to remove the following config that has no effect anymore: `config.active_record.identity_map`.
-* The `delete` method in collection associations can now receive `Fixnum` or `String` arguments as record ids, besides records, pretty much like the `destroy` method does. Previously it raised `ActiveRecord::AssociationTypeMismatch` for such arguments. From Rails 4.0 on `delete` automatically tries to find the records matching the given ids before deleting them.
+* The `delete` method in collection associations can now receive `Integer` or `String` arguments as record ids, besides records, pretty much like the `destroy` method does. Previously it raised `ActiveRecord::AssociationTypeMismatch` for such arguments. From Rails 4.0 on `delete` automatically tries to find the records matching the given ids before deleting them.
* In Rails 4.0 when a column or a table is renamed the related indexes are also renamed. If you have migrations which rename the indexes, they are no longer needed.
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index c58aee96db..c1dfcab6f3 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -328,7 +328,7 @@ this:
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
- format.js {}
+ format.js
format.json { render json: @user, status: :created, location: @user }
else
format.html { render action: "new" }
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 3a4a77724f..b8f1cda329 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,522 +1,5 @@
-* Add `config/initializers/to_time_preserves_timezone.rb`, which tells
- Active Support to preserve the receiver's timezone when calling `to_time`.
- This matches the new behavior that will be part of Ruby 2.4.
+* Added a shared section to `config/secrets.yml` that will be loaded for all environments.
- Fixes #24617.
+ *DHH*
- *Andrew White*
-
-* Make `rails restart` command work with Puma by passing the restart command
- which Puma can use to restart rails server.
-
- *Prathamesh Sonpatki*
-
-* The application generator writes a new file `config/spring.rb`, which tells
- Spring to watch additional common files.
-
- *Xavier Noria*
-
-* The tasks in the rails task namespace is deprecated in favor of app namespace.
- (e.g. `rails:update` and `rails:template` tasks is renamed to `app:update` and `app:template`.)
-
- *Ryo Hashimoto*
-
-* Enable HSTS with IncludeSudomains header for new applications.
-
- *Egor Homakov*, *Prathamesh Sonpatki*
-
-## Rails 5.0.0.beta3 (February 24, 2016) ##
-
-* Alias `rake` with `rails_command` in the Rails Application Templates API
- following Rails 5 convention of preferring "rails" to "rake" to run tasks.
-
- *claudiob*
-
-* Generate applications with an option to log to STDOUT in production
- using the environment variable `RAILS_LOG_TO_STDOUT`.
-
- *Richard Schneeman*
-
-* Change fail fast of `bin/rails test` interrupts run on error.
-
- *Yuji Yaginuma*
-
-* The application generator supports `--skip-listen` to opt-out of features
- that depend on the listen gem. As of this writing they are the evented file
- system monitor and the async plugin for spring.
-
-* The Gemfiles of new applications include spring-watcher-listen on Linux and
- Mac OS X (unless `--skip-spring`).
-
- *Xavier Noria*
-
-* New applications are generated with the evented file system monitor enabled
- on Linux and Mac OS X.
-
- *Xavier Noria*
-
-* Add dummy files for apple-touch-icon.png and apple-touch-icon.png.
-
- See #23427.
-
- *Alexey Zabelin*
-
-## Rails 5.0.0.beta2 (February 01, 2016) ##
-
-* Add `after_bundle` callbacks in Rails plugin templates. Useful for allowing
- templates to perform actions that are dependent upon `bundle install`.
-
- *Ryan Manuel*
-
-* Bring back `TEST=` env for `rake test` task.
-
- *Yves Senn*
-
-* Specify log file names or all logs to clear `rake log:clear`
-
- Specify which logs to clear when using the `rake log:clear` task, e.g. `rake log:clear LOGS=test,staging`
-
- Clear all logs from log/*.log e.g. `rake log:clear LOGS=all`
-
- By default `rake log:clear` clears standard environment log files i.e. 'development,test,production'
-
- *Pramod Shinde*
-
-* Fix using `add_source` with a block after using `gem` in a custom generator.
-
- *Will Fisher*
-
-
-## Rails 5.0.0.beta1 (December 18, 2015) ##
-
-* Newly generated plugins get a `README.md` in Markdown.
-
- *Yuji Yaginuma*
-
-* The generated config file for the development environment includes a new
- config line, commented out, showing how to enable the evented file watcher.
-
- *Xavier Noria*
-
-* `config.debug_exception_response_format` configures the format used
- in responses when errors occur in development mode.
-
- Set `config.debug_exception_response_format` to render an HTML page with
- debug info (using the value `:default`) or render debug info preserving
- the response format (using the value `:api`).
-
- *Jorge Bejar*
-
-* Fix setting exit status code for rake test tasks. The exit status code
- was not set when tests were fired with `rake`. Now, it is being set and it matches
- behavior of running tests via `rails` command (`rails test`), so no matter if
- `rake test` or `rails test` command is used the exit code will be set.
-
- *Arkadiusz Fal*
-
-* Add Command infrastructure to replace rake.
-
- Also move `rake dev:cache` to new infrastructure. You'll need to use
- `rails dev:cache` to toggle development caching from now on.
-
- *Chuck Callebs*
-
-* Allow use of `minitest-rails` gem with Rails test runner.
-
- Fixes #22455.
-
- *Chris Kottom*
-
-* Add `bin/test` script to rails plugin.
-
- `bin/test` can use the same API as `bin/rails test`.
-
- *Yuji Yaginuma*
-
-* Make `static_index` part of the `config.public_file_server` config and
- call it `config.public_file_server.index_name`.
-
- *Yuki Nishijima*
-
-* Deprecate `config.serve_static_files` in favor of `config.public_file_server.enabled`.
-
- Unifies the static asset options under `config.public_file_server`.
-
- To upgrade, replace occurrences of:
-
- ```
- config.serve_static_files = # false or true
- ```
-
- in your environment files, with:
-
- ```
- config.public_file_server.enabled = # false or true
- ```
-
- *Kasper Timm Hansen*
-
-* Deprecate `config.static_cache_control` in favor of
- `config.public_file_server.headers`.
-
- To upgrade, replace occurrences of:
-
- ```
- config.static_cache_control = 'public, max-age=60'
- ```
-
- in your environment files, with:
-
- ```
- config.public_file_server.headers = {
- 'Cache-Control' => 'public, max-age=60'
- }
- ```
-
- `config.public_file_server.headers` can set arbitrary headers, sent along when
- a response is delivered.
-
- *Yuki Nishijima*
-
-* Route generators are now idempotent.
- Running generators several times no longer require you to cleanup routes.rb.
-
- *Thiago Pinto*
-
-* Allow passing an environment to `config_for`.
-
- *Simon Eskildsen*
-
-* Allow `rake stats` to account for rake tasks in lib/tasks.
-
- *Kevin Deisz*
-
-* Added javascript to update the URL on mailer previews with the currently
- selected email format. Reloading the page now keeps you on your selected
- format rather than going back to the default html version.
-
- *James Kerr*
-
-* Add fail fast to `bin/rails test`.
-
- Adding `--fail-fast` or `-f` when running tests will interrupt the run on
- the first failure:
-
- ```
- # Running:
-
- ................................................S......E
-
- ArgumentError: Wups! Bet you didn't expect this!
- test/models/bunny_test.rb:19:in `block in <class:BunnyTest>'
-
- bin/rails test test/models/bunny_test.rb:18
-
- ....................................F
-
- This failed
-
- bin/rails test test/models/bunny_test.rb:14
-
- Interrupted. Exiting...
-
-
- Finished in 0.051427s, 1808.3872 runs/s, 1769.4972 assertions/s.
-
- ```
-
- Note that any unexpected errors don't abort the run.
-
- *Kasper Timm Hansen*
-
-* Add inline output to `bin/rails test`.
-
- Any failures or errors (and skips if running in verbose mode) are output
- during a test run:
-
- ```
- # Running:
-
- .....S..........................................F
-
- This failed
-
- bin/rails test test/models/bunny_test.rb:14
-
- .................................E
-
- ArgumentError: Wups! Bet you didn't expect this!
- test/models/bunny_test.rb:19:in `block in <class:BunnyTest>'
-
- bin/rails test test/models/bunny_test.rb:18
-
- ....................
-
- Finished in 0.069708s, 1477.6019 runs/s, 1448.9106 assertions/s.
- ```
-
- Output can be deferred to after a run with the `--defer-output` option.
-
- *Kasper Timm Hansen*
-
-* Fix displaying mailer previews on non local requests when config
- `config.action_mailer.show_previews` is set.
-
- *Wojciech Wnętrzak*
-
-* `rails server` will now honour the `PORT` environment variable
-
- *David Cornu*
-
-* Plugins generated using `rails plugin new` are now generated with the
- version number set to 0.1.0.
-
- *Daniel Morris*
-
-* `I18n.load_path` is now reloaded under development so there's no need to
- restart the server to make new locale files available. Also, I18n will no
- longer raise for deleted locale files.
-
- *Kir Shatrov*
-
-* Add `bin/update` script to update development environment automatically.
-
- *Mehmet Emin İNAÇ*
-
-* Fix STATS_DIRECTORIES already defined warning when running rake from within
- the top level directory of an engine that has a test app.
-
- Fixes #20510.
-
- *Ersin Akinci*
-
-* Make enabling or disabling caching in development mode possible with
- `rake dev:cache`.
-
- Running `rake dev:cache` will create or remove tmp/caching-dev.txt. When this
- file exists `config.action_controller.perform_caching` will be set to true in
- config/environments/development.rb.
-
- Additionally, a server can be started with either `--dev-caching` or
- `--no-dev-caching` included to toggle caching on startup.
-
- *Jussi Mertanen*, *Chuck Callebs*
-
-* Add a `--api` option in order to generate plugins that can be added
- inside an API application.
-
- *Robin Dupret*
-
-* Fix `NoMethodError` when generating a scaffold inside a full engine.
-
- *Yuji Yaginuma*
-
-* Adding support for passing a block to the `add_source` action of a custom generator.
-
- *Mike Dalton*, *Hirofumi Wakasugi*
-
-* `assert_file` now understands paths with special characters
- (eg. `v0.1.4~alpha+nightly`).
-
- *Diego Carrion*
-
-* Remove ContentLength middleware from the defaults. If you want it, just
- add it as a middleware in your config.
-
- *Egg McMuffin*
-
-* Make it possible to customize the executable inside rerun snippets.
-
- *Yves Senn*
-
-* Add support for API only apps.
- Middleware stack was slimmed down and it has only the needed
- middleware for API apps & generators generates the right files,
- folders and configurations.
-
- *Santiago Pastorino*, *Jorge Bejar*
-
-* Make generated scaffold functional tests work inside engines.
-
- *Yuji Yaginuma*
-
-* Generate a `.keep` file in the `tmp` folder by default as many scripts
- assume the existence of this folder and most would fail if it is absent.
-
- See #20299.
-
- *Yoong Kang Lim*, *Sunny Juneja*
-
-* `config.static_index` configures directory `index.html` filename
-
- Set `config.static_index` to serve a static directory index file not named
- `index`. E.g. to serve `main.html` instead of `index.html` for directory
- requests, set `config.static_index` to `"main"`.
-
- *Eliot Sykes*
-
-* `bin/setup` uses built-in rake tasks (`log:clear`, `tmp:clear`).
-
- *Mohnish Thallavajhula*
-
-* Fix mailer previews with attachments by using the mail gem's own API to
- locate the first part of the correct mime type.
-
- Fixes #14435.
-
- *Andrew White*
-
-* Remove sqlite support from `rails dbconsole`.
-
- *Andrew White*
-
-* Rename `railties/bin` to `railties/exe` to match the new Bundler executables
- convention.
-
- *Islam Wazery*
-
-* Print `bundle install` output in `rails new` as soon as it's available.
-
- Running `rails new` will now print the output of `bundle install` as
- it is available, instead of waiting until all gems finish installing.
-
- *Max Holder*
-
-* Respect `pluralize_table_names` when generating fixture file.
-
- Fixes #19519.
-
- *Yuji Yaginuma*
-
-* Add a new-line to the end of route method generated code.
-
- We need to add a `\n`, because we cannot have two routes
- in the same line.
-
- *arthurnn*
-
-* Add `rake initializers`.
-
- This task prints out all defined initializers in the order they are invoked
- by Rails. This is helpful for debugging issues related to the initialization
- process.
-
- *Naoto Kaneko*
-
-* Created rake restart task. Restarts your Rails app by touching the
- `tmp/restart.txt`.
-
- See #18876.
-
- *Hyonjee Joo*
-
-* Add `config/initializers/active_record_belongs_to_required_by_default.rb`.
-
- Newly generated Rails apps have a new initializer called
- `active_record_belongs_to_required_by_default.rb` which sets the value of
- the configuration option `config.active_record.belongs_to_required_by_default`
- to `true` when ActiveRecord is not skipped.
-
- As a result, new Rails apps require `belongs_to` association on model
- to be valid.
-
- This initializer is *not* added when running `rake rails:update`, so
- old apps ported to Rails 5 will work without any change.
-
- *Josef Šimánek*
-
-* `delete` operations in configurations are run last in order to eliminate
- 'No such middleware' errors when `insert_before` or `insert_after` are added
- after the `delete` operation for the middleware being deleted.
-
- Fixes #16433.
-
- *Guo Xiang Tan*
-
-* Newly generated applications get a `README.md` in Markdown.
-
- *Xavier Noria*
-
-* Remove the documentation tasks `doc:app`, `doc:rails`, and `doc:guides`.
-
- *Xavier Noria*
-
-* Force generated routes to be inserted into `config/routes.rb`.
-
- *Andrew White*
-
-* Don't remove all line endings from `config/routes.rb` when revoking scaffold.
-
- Fixes #15913.
-
- *Andrew White*
-
-* Rename `--skip-test-unit` option to `--skip-test` in app generator
-
- *Melanie Gilman*
-
-* Add the `method_source` gem to the default Gemfile for apps.
-
- *Sean Griffin*
-
-* Drop old test locations from `rake stats`:
-
- - test/functional
- - test/unit
-
- *Ravil Bayramgalin*
-
-* Update `rake stats` to correctly count declarative tests
- as methods in `_test.rb` files.
-
- *Ravil Bayramgalin*
-
-* Remove deprecated `test:all` and `test:all:db` tasks.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `Rails::Rack::LogTailer`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `RAILS_CACHE` constant.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `serve_static_assets` configuration.
-
- *Rafael Mendonça França*
-
-* Use local variables in `_form.html.erb` partial generated by scaffold.
-
- *Andrew Kozlov*
-
-* Add `config/initializers/callback_terminator.rb`.
-
- Newly generated Rails apps have a new initializer called
- `callback_terminator.rb` which sets the value of the configuration option
- `ActiveSupport.halt_callback_chains_on_return_false` to `false`.
-
- As a result, new Rails apps do not halt Active Record and Active Model
- callback chains when a callback returns `false`; only when they are
- explicitly halted with `throw(:abort)`.
-
- The terminator is *not* added when running `rake rails:update`, so returning
- `false` will still work on old apps ported to Rails 5, displaying a
- deprecation warning to prompt users to update their code to the new syntax.
-
- *claudiob*
-
-* Generated fixtures won't use the id when generated with references attributes.
-
- *Pablo Olmos de Aguilera Corradini*
-
-* Add `--skip-action-mailer` option to the app generator.
-
- *claudiob*
-
-* Autoload any second level directories called `app/*/concerns`.
-
- *Alex Robbin*
-
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/railties/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/railties/CHANGELOG.md) for previous changes.
diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc
index 26a25ee9dc..ef9bbf3d7e 100644
--- a/railties/RDOC_MAIN.rdoc
+++ b/railties/RDOC_MAIN.rdoc
@@ -35,17 +35,17 @@ can read more about Action Pack in its {README}[link:files/actionpack/README_rdo
1. Install \Rails at the command prompt if you haven't yet:
- gem install rails
+ $ gem install rails
2. At the command prompt, create a new \Rails application:
- rails new myapp
+ $ rails new myapp
where "myapp" is the application name.
3. Change directory to +myapp+ and start the web server:
- cd myapp; rails server
+ $ cd myapp; rails server
Run with <tt>--help</tt> or <tt>-h</tt> for options.
diff --git a/railties/Rakefile b/railties/Rakefile
index 3421d9b464..db23bbabaa 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -3,7 +3,6 @@ require 'rake/testtask'
task :default => :test
task :package
-task "package:clean"
desc "Run all unit tests"
task :test => 'test:isolated'
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index ed106c9918..c383de3e06 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -385,11 +385,16 @@ module Rails
def secrets
@secrets ||= begin
secrets = ActiveSupport::OrderedOptions.new
- yaml = config.paths["config/secrets"].first
+ yaml = config.paths["config/secrets"].first
+
if File.exist?(yaml)
require "erb"
- all_secrets = YAML.load(ERB.new(IO.read(yaml)).result) || {}
- env_secrets = all_secrets[Rails.env]
+
+ all_secrets = YAML.load(ERB.new(IO.read(yaml)).result) || {}
+ shared_secrets = all_secrets['shared']
+ env_secrets = all_secrets[Rails.env]
+
+ secrets.merge!(shared_secrets.symbolize_keys) if shared_secrets
secrets.merge!(env_secrets.symbolize_keys) if env_secrets
end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 5757d235d2..9701409755 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -219,7 +219,7 @@ module Rails
# The next thing that changes in isolated engines is the behavior of routes.
# Normally, when you namespace your controllers, you also need to namespace
# the related routes. With an isolated engine, the engine's namespace is
- # automatically applied, so you don't need to specify it explicity in your
+ # automatically applied, so you don't need to specify it explicitly in your
# routes:
#
# MyEngine::Engine.routes.draw do
diff --git a/railties/lib/rails/gem_version.rb b/railties/lib/rails/gem_version.rb
index 081222425c..9c49e0655a 100644
--- a/railties/lib/rails/gem_version.rb
+++ b/railties/lib/rails/gem_version.rb
@@ -6,9 +6,9 @@ module Rails
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 151bf9a879..7aee28c74a 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -296,7 +296,7 @@ module Rails
return [] if options[:skip_sprockets]
gems = []
- gems << GemfileEntry.version('sass-rails', '~> 5.0',
+ gems << GemfileEntry.github('sass-rails', 'rails/sass-rails', nil,
'Use SCSS for stylesheets')
gems << GemfileEntry.version('uglifier',
@@ -312,7 +312,7 @@ module Rails
end
def coffee_gemfile_entry
- GemfileEntry.version 'coffee-rails', '~> 4.1.0', 'Use CoffeeScript for .coffee assets and views'
+ GemfileEntry.github 'coffee-rails', 'rails/coffee-rails', nil, 'Use CoffeeScript for .coffee assets and views'
end
def javascript_gemfile_entry
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index efbf51ddfb..ee076eb711 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -26,6 +26,10 @@ module Rails
super
end
end
+
+ def js_template(source, destination)
+ template(source + '.js', destination + '.js')
+ end
end
protected
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 4d5bb364b2..3bc66f55fb 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -90,11 +90,11 @@ module Rails
def config_when_updating
cookie_serializer_config_exist = File.exist?('config/initializers/cookies_serializer.rb')
- callback_terminator_config_exist = File.exist?('config/initializers/callback_terminator.rb')
- active_record_belongs_to_required_by_default_config_exist = File.exist?('config/initializers/active_record_belongs_to_required_by_default.rb')
- to_time_preserves_timezone_config_exist = File.exist?('config/initializers/to_time_preserves_timezone.rb')
+ callback_terminator_config_exist = File.exist?('config/initializers/new_framework_defaults/callback_terminator.rb')
+ active_record_belongs_to_required_by_default_config_exist = File.exist?('config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb')
+ to_time_preserves_timezone_config_exist = File.exist?('config/initializers/new_framework_defaults/to_time_preserves_timezone.rb')
action_cable_config_exist = File.exist?('config/cable.yml')
- ssl_options_exist = File.exist?('config/initializers/ssl_options.rb')
+ ssl_options_exist = File.exist?('config/initializers/new_framework_defaults/ssl_options.rb')
rack_cors_config_exist = File.exist?('config/initializers/cors.rb')
config
@@ -102,7 +102,7 @@ module Rails
gsub_file 'config/environments/development.rb', /^(\s+)config\.file_watcher/, '\1# config.file_watcher'
unless callback_terminator_config_exist
- remove_file 'config/initializers/callback_terminator.rb'
+ remove_file 'config/initializers/new_framework_defaults/callback_terminator.rb'
end
unless cookie_serializer_config_exist
@@ -110,11 +110,11 @@ module Rails
end
unless active_record_belongs_to_required_by_default_config_exist
- remove_file 'config/initializers/active_record_belongs_to_required_by_default.rb'
+ remove_file 'config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb'
end
unless to_time_preserves_timezone_config_exist
- remove_file 'config/initializers/to_time_preserves_timezone.rb'
+ remove_file 'config/initializers/new_framework_defaults/to_time_preserves_timezone.rb'
end
unless action_cable_config_exist
@@ -122,7 +122,7 @@ module Rails
end
unless ssl_options_exist
- remove_file 'config/initializers/ssl_options.rb'
+ remove_file 'config/initializers/new_framework_defaults/ssl_options.rb'
end
unless rack_cors_config_exist
@@ -305,6 +305,17 @@ module Rails
end
end
+ def delete_public_files_if_api_option
+ if options[:api]
+ remove_file 'public/404.html'
+ remove_file 'public/422.html'
+ remove_file 'public/500.html'
+ remove_file 'public/apple-touch-icon-precomposed.png'
+ remove_file 'public/apple-touch-icon.png'
+ remove_file 'public/favicon.ico'
+ end
+ end
+
def delete_js_folder_skipping_javascript
if options[:skip_javascript]
remove_dir 'app/assets/javascripts'
@@ -333,7 +344,7 @@ module Rails
def delete_active_record_initializers_skipping_active_record
if options[:skip_active_record]
- remove_file 'config/initializers/active_record_belongs_to_required_by_default.rb'
+ remove_file 'config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb'
end
end
@@ -349,8 +360,8 @@ module Rails
if options[:api]
remove_file 'config/initializers/session_store.rb'
remove_file 'config/initializers/cookies_serializer.rb'
- remove_file 'config/initializers/request_forgery_protection.rb'
- remove_file 'config/initializers/per_form_csrf_tokens.rb'
+ remove_file 'config/initializers/new_framework_defaults/request_forgery_protection.rb'
+ remove_file 'config/initializers/new_framework_defaults/per_form_csrf_tokens.rb'
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
index f726fd6305..413354186d 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
@@ -1,7 +1,5 @@
class ApplicationController < ActionController::<%= options[:api] ? "API" : "Base" %>
<%- unless options[:api] -%>
- # Prevent CSRF attacks by raising an exception.
- # For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
<%- end -%>
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/cable.yml b/railties/lib/rails/generators/rails/app/templates/config/cable.yml
index aa4e832748..0bbde6f74f 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/cable.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/cable.yml
@@ -1,10 +1,9 @@
-# Action Cable uses Redis by default to administer connections, channels, and sending/receiving messages over the WebSocket.
-production:
- adapter: redis
- url: redis://localhost:6379/1
-
development:
adapter: async
test:
adapter: async
+
+production:
+ adapter: redis
+ url: redis://localhost:6379/1
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml
index 34fc0e3465..917b52e535 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml
@@ -8,6 +8,7 @@
#
default: &default
adapter: frontbase
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: localhost
username: <%= app_name %>
password: ''
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml
index 187ff01bac..d40117a27f 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml
@@ -34,6 +34,7 @@
#
default: &default
adapter: ibm_db
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: db2inst1
password:
#schema: db2inst1
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml
index db0a429753..563be77710 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml
@@ -38,6 +38,7 @@
default: &default
adapter: jdbc
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: <%= app_name %>
password:
driver:
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml
index f2c4922e7d..a2b2a64ba6 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml
@@ -11,6 +11,7 @@
#
default: &default
adapter: mysql
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password:
host: localhost
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml
index 80ceb9df92..70df04079d 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml
@@ -6,6 +6,7 @@
default: &default
adapter: postgresql
encoding: unicode
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml
index 28c36eb82f..371415e6a8 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml
@@ -6,6 +6,7 @@
#
default: &default
adapter: sqlite3
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
index 193423e84a..d987cf303b 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
@@ -12,7 +12,7 @@
default: &default
adapter: mysql2
encoding: utf8
- pool: 5
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password:
<% if mysql_socket -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
index 9aedcc15cb..d2499ea4fb 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
@@ -18,6 +18,7 @@
#
default: &default
adapter: oracle
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: <%= app_name %>
password:
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml
index 1c1a37ca8d..9510568124 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml
@@ -6,7 +6,7 @@
#
default: &default
adapter: sqlite3
- pool: 5
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
development:
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
index 30b0df34a8..c223d6bc62 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
@@ -25,6 +25,7 @@
default: &default
adapter: sqlserver
encoding: utf8
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
reconnect: false
username: <%= app_name %>
password:
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 6bd5e42251..363af05459 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -37,12 +37,10 @@ Rails.application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
<%- unless options[:skip_action_cable] -%>
- # Action Cable endpoint configuration
+ # Mount Action Cable outside main process or domain
+ # config.action_cable.mount_path = nil
# config.action_cable.url = 'wss://example.com/cable'
# config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
-
- # Don't mount Action Cable in the main server process.
- # config.action_cable.mount_path = nil
<%- end -%>
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/active_record_belongs_to_required_by_default.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb
index f613b40f80..f613b40f80 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/active_record_belongs_to_required_by_default.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/callback_terminator.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/callback_terminator.rb
index 649e82280e..649e82280e 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/callback_terminator.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/callback_terminator.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/per_form_csrf_tokens.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/per_form_csrf_tokens.rb
index 1f569dedfd..1f569dedfd 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/per_form_csrf_tokens.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/per_form_csrf_tokens.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/request_forgery_protection.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/request_forgery_protection.rb
index 3eab78a885..3eab78a885 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/request_forgery_protection.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/request_forgery_protection.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/ssl_options.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/ssl_options.rb
new file mode 100644
index 0000000000..53dfcd7466
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/ssl_options.rb
@@ -0,0 +1,6 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure SSL options to enable HSTS with subdomains. This is a new
+# Rails 5.0 default, so it is introduced as a configuration option to ensure
+# that apps made on earlier versions of Rails are not affected when upgrading.
+Rails.application.config.ssl_options = { hsts: { subdomains: true } }
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/to_time_preserves_timezone.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/to_time_preserves_timezone.rb
index 8674be3227..8674be3227 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/to_time_preserves_timezone.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults/to_time_preserves_timezone.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/ssl_options.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/ssl_options.rb
deleted file mode 100644
index 1775dea1e7..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/ssl_options.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Configure SSL options to enable HSTS with subdomains.
-Rails.application.config.ssl_options = { hsts: { subdomains: true } }
diff --git a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml
index cdea2fd060..8e995a5df1 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml
@@ -10,6 +10,13 @@
# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.
+# Shared secrets are available across all environments.
+
+shared:
+ api_key: 123
+
+# Environmental secrets are only available for that specific environment.
+
development:
secret_key_base: <%= app_secret %>
@@ -18,5 +25,6 @@ test:
# Do not keep production secrets in the repository,
# instead read values from the environment.
+
production:
secret_key_base: <%%= ENV["SECRET_KEY_BASE"] %>
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index 0a4c509a31..6c583e5811 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -19,8 +19,7 @@ module Rails
end
end
- hook_for :template_engine, :test_framework
- hook_for :helper, :assets, hide: true
+ hook_for :template_engine, :test_framework, :helper, :assets
private
diff --git a/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
index 79f8b7f96f..cd4f3de38d 100644
--- a/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
+++ b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
@@ -1,13 +1,13 @@
body {
background-color: #fff;
color: #333;
+ margin: 33px;
}
body, p, ol, ul, td {
font-family: verdana, arial, helvetica, sans-serif;
font-size: 13px;
line-height: 18px;
- margin: 33px;
}
pre {
@@ -34,9 +34,7 @@ th {
}
td {
- padding-bottom: 7px;
- padding-left: 5px;
- padding-right: 5px;
+ padding: 0 5px 7px;
}
div.field,
@@ -57,8 +55,7 @@ div.actions {
#error_explanation {
width: 450px;
border: 2px solid red;
- padding: 7px;
- padding-bottom: 0;
+ padding: 7px 7px 0;
margin-bottom: 20px;
background-color: #f0f0f0;
}
@@ -68,8 +65,7 @@ div.actions {
font-weight: bold;
padding: 5px 5px 5px 15px;
font-size: 12px;
- margin: -7px;
- margin-bottom: 0;
+ margin: -7px -7px 0;
background-color: #c00;
color: #fff;
}
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
index 0e6bef12fc..c33375b7b4 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
@@ -25,7 +25,7 @@ class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTe
post <%= index_helper %>_url, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
end
- assert_redirected_to <%= singular_table_name %>_path(<%= class_name %>.last)
+ assert_redirected_to <%= singular_table_name %>_url(<%= class_name %>.last)
end
test "should show <%= singular_table_name %>" do
@@ -40,7 +40,7 @@ class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTe
test "should update <%= singular_table_name %>" do
patch <%= show_helper %>, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
- assert_redirected_to <%= singular_table_name %>_path(<%= "@#{singular_table_name}" %>)
+ assert_redirected_to <%= singular_table_name %>_url(<%= "@#{singular_table_name}" %>)
end
test "should destroy <%= singular_table_name %>" do
@@ -48,7 +48,7 @@ class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTe
delete <%= show_helper %>
end
- assert_redirected_to <%= index_helper %>_path
+ assert_redirected_to <%= index_helper %>_url
end
end
<% end -%>
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index e32eea42b7..9e8531b482 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -307,7 +307,7 @@ module ApplicationTests
assert_not_equal asset_path, assets["assets"]["application.css"]
end
- test "precompile appends the md5 hash to files referenced with asset_path and run in production with digest true" do
+ test "precompile appends the MD5 hash to files referenced with asset_path and run in production with digest true" do
app_file "app/assets/images/rails.png", "notactuallyapng"
app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 1ca6bbcecf..7ec25aeca1 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -555,6 +555,31 @@ module ApplicationTests
assert_equal 'myamazonsecretaccesskey', app.secrets.aws_secret_access_key
end
+ test "shared secrets saved in config/secrets.yml are loaded in app secrets" do
+ app_file 'config/secrets.yml', <<-YAML
+ shared:
+ api_key: 3b7cd727
+ YAML
+
+ app 'development'
+
+ assert_equal '3b7cd727', app.secrets.api_key
+ end
+
+ test "shared secrets will yield to environment specific secrets" do
+ app_file 'config/secrets.yml', <<-YAML
+ shared:
+ api_key: 3b7cd727
+
+ development:
+ api_key: abc12345
+ YAML
+
+ app 'development'
+
+ assert_equal 'abc12345', app.secrets.api_key
+ end
+
test "blank config/secrets.yml does not crash the loading process" do
app_file 'config/secrets.yml', <<-YAML
YAML
@@ -686,7 +711,7 @@ module ApplicationTests
private
- def form_authenticity_token(*args); token; end # stub the authenticy token
+ def form_authenticity_token(*args); token; end # stub the authenticity token
end
RUBY
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 5869ff64bc..7a86a96e19 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -235,7 +235,7 @@ module ApplicationTests
end
end
- etag = "W/" + "5af83e3196bf99f440f31f2e1a6c9afe".inspect
+ etag = "W/" + "c00862d1c6c1cf7c1b49388306e7b3c1".inspect
get "/"
assert_equal 200, last_response.status
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index acdb4e7d79..badb9ecdd6 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -287,7 +287,7 @@ module ApplicationTests
RAILS_ENV=test bin/rails db:migrate test`
end
- assert_match(/7 runs, 12 assertions, 0 failures, 0 errors/, output)
+ assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output)
assert_no_match(/Errors running/, output)
end
@@ -318,7 +318,7 @@ module ApplicationTests
RAILS_ENV=test bin/rails db:migrate test`
end
- assert_match(/7 runs, 12 assertions, 0 failures, 0 errors/, output)
+ assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output)
assert_no_match(/Errors running/, output)
end
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index 8e1cd0891a..505c1be9fc 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -100,11 +100,17 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
config/initializers/assets.rb
config/initializers/cookies_serializer.rb
config/initializers/session_store.rb
- config/initializers/request_forgery_protection.rb
- config/initializers/per_form_csrf_tokens.rb
+ config/initializers/new_framework_defaults/request_forgery_protection.rb
+ config/initializers/new_framework_defaults/per_form_csrf_tokens.rb
lib/assets
vendor/assets
test/helpers
- tmp/cache/assets)
+ tmp/cache/assets
+ public/404.html
+ public/422.html
+ public/500.html
+ public/apple-touch-icon-precomposed.png
+ public/apple-touch-icon.png
+ public/favicon.ico)
end
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 25a8635e7d..cb656cc94c 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -176,13 +176,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, 'myapp')
run_generator [app_root]
- FileUtils.rm("#{app_root}/config/initializers/callback_terminator.rb")
+ FileUtils.rm("#{app_root}/config/initializers/new_framework_defaults/callback_terminator.rb")
stub_rails_application(app_root) do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
- assert_no_file "#{app_root}/config/initializers/callback_terminator.rb"
+ assert_no_file "#{app_root}/config/initializers/new_framework_defaults/callback_terminator.rb"
end
end
@@ -190,13 +190,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, 'myapp')
run_generator [app_root]
- FileUtils.touch("#{app_root}/config/initializers/callback_terminator.rb")
+ FileUtils.touch("#{app_root}/config/initializers/new_framework_defaults/callback_terminator.rb")
stub_rails_application(app_root) do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
- assert_file "#{app_root}/config/initializers/callback_terminator.rb"
+ assert_file "#{app_root}/config/initializers/new_framework_defaults/callback_terminator.rb"
end
end
@@ -233,13 +233,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, 'myapp')
run_generator [app_root]
- FileUtils.rm("#{app_root}/config/initializers/active_record_belongs_to_required_by_default.rb")
+ FileUtils.rm("#{app_root}/config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb")
stub_rails_application(app_root) do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
- assert_no_file "#{app_root}/config/initializers/active_record_belongs_to_required_by_default.rb"
+ assert_no_file "#{app_root}/config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb"
end
end
@@ -247,13 +247,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, 'myapp')
run_generator [app_root]
- FileUtils.touch("#{app_root}/config/initializers/active_record_belongs_to_required_by_default.rb")
+ FileUtils.touch("#{app_root}/config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb")
stub_rails_application(app_root) do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
- assert_file "#{app_root}/config/initializers/active_record_belongs_to_required_by_default.rb"
+ assert_file "#{app_root}/config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb"
end
end
@@ -261,13 +261,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, 'myapp')
run_generator [app_root]
- FileUtils.rm("#{app_root}/config/initializers/to_time_preserves_timezone.rb")
+ FileUtils.rm("#{app_root}/config/initializers/new_framework_defaults/to_time_preserves_timezone.rb")
stub_rails_application(app_root) do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
- assert_no_file "#{app_root}/config/initializers/to_time_preserves_timezone.rb"
+ assert_no_file "#{app_root}/config/initializers/new_framework_defaults/to_time_preserves_timezone.rb"
end
end
@@ -275,13 +275,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, 'myapp')
run_generator [app_root]
- FileUtils.touch("#{app_root}/config/initializers/to_time_preserves_timezone.rb")
+ FileUtils.touch("#{app_root}/config/initializers/new_framework_defaults/to_time_preserves_timezone.rb")
stub_rails_application(app_root) do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
- assert_file "#{app_root}/config/initializers/to_time_preserves_timezone.rb"
+ assert_file "#{app_root}/config/initializers/new_framework_defaults/to_time_preserves_timezone.rb"
end
end
@@ -289,13 +289,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, 'myapp')
run_generator [app_root]
- FileUtils.rm("#{app_root}/config/initializers/ssl_options.rb")
+ FileUtils.rm("#{app_root}/config/initializers/new_framework_defaults/ssl_options.rb")
stub_rails_application(app_root) do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
- assert_no_file "#{app_root}/config/initializers/ssl_options.rb"
+ assert_no_file "#{app_root}/config/initializers/new_framework_defaults/ssl_options.rb"
end
end
@@ -303,13 +303,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
app_root = File.join(destination_root, 'myapp')
run_generator [app_root]
- FileUtils.touch("#{app_root}/config/initializers/ssl_options.rb")
+ FileUtils.touch("#{app_root}/config/initializers/new_framework_defaults/ssl_options.rb")
stub_rails_application(app_root) do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
- assert_file "#{app_root}/config/initializers/ssl_options.rb"
+ assert_file "#{app_root}/config/initializers/new_framework_defaults/ssl_options.rb"
end
end
@@ -452,7 +452,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_generator_if_skip_active_record_is_given
run_generator [destination_root, "--skip-active-record"]
assert_no_file "config/database.yml"
- assert_no_file "config/initializers/active_record_belongs_to_required_by_default.rb"
+ assert_no_file "config/initializers/new_framework_defaults/active_record_belongs_to_required_by_default.rb"
assert_no_file "app/models/application_record.rb"
assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
assert_file "test/test_helper.rb" do |helper_content|
diff --git a/railties/test/generators/channel_generator_test.rb b/railties/test/generators/channel_generator_test.rb
index d58b54ac24..e3edde681f 100644
--- a/railties/test/generators/channel_generator_test.rb
+++ b/railties/test/generators/channel_generator_test.rb
@@ -24,8 +24,24 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
end
- assert_file "app/assets/javascripts/channels/chat.coffee" do |channel|
- assert_match(/App.cable.subscriptions.create "ChatChannel"/, channel)
+ assert_file "app/assets/javascripts/channels/chat.js" do |channel|
+ assert_match(/App.chat = App.cable.subscriptions.create\("ChatChannel/, channel)
+ end
+ end
+
+ def test_channel_with_multiple_actions_is_created
+ run_generator ['chat', 'speak', 'mute']
+
+ assert_file "app/channels/chat_channel.rb" do |channel|
+ assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
+ assert_match(/def speak/, channel)
+ assert_match(/def mute/, channel)
+ end
+
+ assert_file "app/assets/javascripts/channels/chat.js" do |channel|
+ assert_match(/App.chat = App.cable.subscriptions.create\("ChatChannel/, channel)
+ assert_match(/,\n\n speak/, channel)
+ assert_match(/,\n\n mute: function\(\) \{\n return this\.perform\('mute'\);\n \}\n\}\);/, channel)
end
end
@@ -36,7 +52,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
end
- assert_no_file "app/assets/javascripts/channels/chat.coffee"
+ assert_no_file "app/assets/javascripts/channels/chat.js"
end
def test_cable_js_is_created_if_not_present_already
@@ -52,7 +68,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
run_generator ['chat'], behavior: :revoke
assert_no_file "app/channels/chat_channel.rb"
- assert_no_file "app/assets/javascripts/channels/chat.coffee"
+ assert_no_file "app/assets/javascripts/channels/chat.js"
assert_file "app/channels/application_cable/channel.rb"
assert_file "app/channels/application_cable/connection.rb"
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 7d07d0d8d5..bd69906b9d 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -488,7 +488,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
`bin/rails g scaffold User name:string age:integer;
bin/rails db:migrate`
end
- assert_match(/8 runs, 13 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
+ assert_match(/8 runs, 10 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
end
end
@@ -502,7 +502,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
`bin/rails g scaffold User name:string age:integer;
bin/rails db:migrate`
end
- assert_match(/8 runs, 13 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
+ assert_match(/8 runs, 10 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
end
end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 52e0277633..9dac1008fa 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -111,7 +111,7 @@ module TestHelpers
# Delete the initializers unless requested
unless options[:initializers]
- Dir["#{app_path}/config/initializers/*.rb"].each do |initializer|
+ Dir["#{app_path}/config/initializers/**/*.rb"].each do |initializer|
File.delete(initializer)
end
end
diff --git a/tasks/release.rb b/tasks/release.rb
index de9c51a140..4a1ed04478 100644
--- a/tasks/release.rb
+++ b/tasks/release.rb
@@ -13,7 +13,6 @@ directory "pkg"
task :clean do
rm_f gem
- sh "cd #{framework} && bundle exec rake package:clean" unless framework == "rails"
end
task :update_versions do
@@ -44,6 +43,36 @@ directory "pkg"
raise "Could not insert PRE in #{file}" unless $1
File.open(file, 'w') { |f| f.write ruby }
+
+ if File.exist?("#{framework}/package.json")
+ Dir.chdir("#{framework}") do
+ # This "npm-ifies" the current version
+ # With npm, versions such as "5.0.0.rc1" or "5.0.0.beta1.1" are not compliant with its
+ # versioning system, so they must be transformed to "5.0.0-rc1" and "5.0.0-beta1-1" respectively.
+
+ # In essence, the code below runs through all "."s that appear in the version,
+ # and checks to see if their index in the version string is greater than or equal to 2,
+ # and if so, it will change the "." to a "-".
+
+ # Sample version transformations:
+ # irb(main):001:0> version = "5.0.1.1"
+ # => "5.0.1.1"
+ # irb(main):002:0> version.gsub(/\./).with_index { |s, i| i >= 2 ? '-' : s }
+ # => "5.0.1-1"
+ # irb(main):003:0> version = "5.0.0.rc1"
+ # => "5.0.0.rc1"
+ # irb(main):004:0> version.gsub(/\./).with_index { |s, i| i >= 2 ? '-' : s }
+ # => "5.0.0-rc1"
+ version = version.gsub(/\./).with_index { |s, i| i >= 2 ? '-' : s }
+
+ # Check if npm is installed, and raise an error if not
+ if sh 'which npm'
+ sh "npm version #{version} --no-git-tag-version"
+ else
+ raise 'You must have npm installed to release Rails.'
+ end
+ end
+ end
end
task gem => %w(update_versions pkg) do
@@ -56,11 +85,12 @@ directory "pkg"
task :build => [:clean, gem]
task :install => :build do
- sh "gem install #{gem}"
+ sh "gem install --pre #{gem}"
end
task :push => :build do
sh "gem push #{gem}"
+ sh "npm publish" if File.exist?("#{framework}/package.json")
end
end
end
@@ -135,7 +165,7 @@ namespace :all do
end
task :tag do
- sh "git tag -m '#{tag} release' #{tag}"
+ sh "git tag -s -m '#{tag} release' #{tag}"
sh "git push --tags"
end
diff --git a/version.rb b/version.rb
index 081222425c..9c49e0655a 100644
--- a/version.rb
+++ b/version.rb
@@ -6,9 +6,9 @@ module Rails
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end