railties 6.0.0.beta3 → 6.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -7
  3. data/RDOC_MAIN.rdoc +3 -3
  4. data/README.rdoc +1 -1
  5. data/lib/rails/application.rb +3 -1
  6. data/lib/rails/application/configuration.rb +27 -1
  7. data/lib/rails/application/default_middleware_stack.rb +1 -0
  8. data/lib/rails/application/dummy_erb_compiler.rb +19 -0
  9. data/lib/rails/application/finisher.rb +38 -1
  10. data/lib/rails/autoloaders.rb +10 -2
  11. data/lib/rails/command/environment_argument.rb +7 -4
  12. data/lib/rails/commands/console/console_command.rb +6 -0
  13. data/lib/rails/commands/credentials/USAGE +1 -1
  14. data/lib/rails/commands/credentials/credentials_command.rb +17 -3
  15. data/lib/rails/commands/dbconsole/dbconsole_command.rb +19 -7
  16. data/lib/rails/commands/dev/dev_command.rb +4 -2
  17. data/lib/rails/commands/encrypted/USAGE +28 -0
  18. data/lib/rails/commands/encrypted/encrypted_command.rb +1 -0
  19. data/lib/rails/commands/initializers/initializers_command.rb +7 -0
  20. data/lib/rails/commands/notes/notes_command.rb +1 -1
  21. data/lib/rails/commands/runner/runner_command.rb +7 -3
  22. data/lib/rails/commands/server/server_command.rb +8 -6
  23. data/lib/rails/engine.rb +27 -31
  24. data/lib/rails/gem_version.rb +1 -1
  25. data/lib/rails/generators.rb +2 -0
  26. data/lib/rails/generators/app_base.rb +2 -2
  27. data/lib/rails/generators/app_name.rb +2 -2
  28. data/lib/rails/generators/database.rb +1 -1
  29. data/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt +6 -3
  30. data/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt +8 -0
  31. data/lib/rails/generators/generated_attribute.rb +36 -10
  32. data/lib/rails/generators/named_base.rb +1 -0
  33. data/lib/rails/generators/rails/app/templates/Gemfile.tt +3 -3
  34. data/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +8 -0
  35. data/lib/rails/generators/rails/app/templates/bin/setup.tt +3 -2
  36. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +2 -0
  37. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +2 -0
  38. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +10 -5
  39. data/lib/rails/generators/rails/app/templates/ruby-version.tt +1 -1
  40. data/lib/rails/generators/rails/assets/assets_generator.rb +7 -0
  41. data/lib/rails/generators/rails/db/system/change/change_generator.rb +12 -2
  42. data/lib/rails/generators/rails/plugin/plugin_generator.rb +0 -15
  43. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +8 -0
  44. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +1 -1
  45. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +1 -1
  46. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +2 -2
  47. data/lib/rails/mailers_controller.rb +6 -3
  48. data/lib/rails/source_annotation_extractor.rb +14 -1
  49. data/lib/rails/tasks.rb +1 -0
  50. data/lib/rails/tasks/zeitwerk.rake +78 -0
  51. metadata +14 -12
  52. data/lib/rails/generators/rails/app/templates/bin/update.tt +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4b78369d9f271572d3a302573c3074369e2d00f9507ade58f17667c9dda5476
4
- data.tar.gz: b3afe11e12e754306480173cfa772222d9b998d27a232d209a64624604a91341
3
+ metadata.gz: b79bdc57812a1581d826a6c2740a48f6392063b58797ca663c86864391fe2a22
4
+ data.tar.gz: 7f2f54e3166b8ce85e685ca97272a68423fce112b4f66b98c4fbd5e057264d0a
5
5
  SHA512:
6
- metadata.gz: fc7ff12025bd5ae53ee9db3a985bbc6fc661d9f11aef9fdcd243e26a7dcd4c7186c12dd888479ebbd9f1da1204c031c9cdfaf34eab2e6411e81b63130a8167d3
7
- data.tar.gz: 612e651ac16761224f2de33b9ee6829309aaa685935d576a935e782762ef5be87b111e585df6741eefe67c5937819b15c3c5be856eae8c74afb3a3185928c6cb
6
+ metadata.gz: e597f918a385dce73903379a335656b1312ca20fdcaaf6414764a056188cf53f6f53c5966fe9623afb1fbf0e021df87fe6fbcf3d3f4cd45e15960717f77ecea5
7
+ data.tar.gz: 8e9e498390107f6903c372e0e4e1d5c34975c1a1375bc4dbb9ba7ed801d76afb3e35d1b54a3b46b596c7b9919b58b75fb06b60866118440cc008f0ef0c3170b0
@@ -1,6 +1,83 @@
1
+ ## Rails 6.0.0.rc1 (April 24, 2019) ##
2
+
3
+ * Applications upgrading to Rails 6 can run the command
4
+
5
+ ```
6
+ bin/rails zeitwerk:check
7
+ ```
8
+
9
+ to check if the project structure they were using with the classic
10
+ autoloader is compatible with `:zeitwerk` mode.
11
+
12
+ *Matilda Smeds* & *Xavier Noria*
13
+
14
+ * Allow loading seeds without ActiveJob.
15
+
16
+ Fixes #35782
17
+
18
+ *Jeremy Weathers*
19
+
20
+ * `null: false` is set in the migrations by default for column pointed by
21
+ `belongs_to` / `references` association generated by model generator.
22
+
23
+ Also deprecate passing {required} to the model generator.
24
+
25
+ *Prathamesh Sonpatki*
26
+
27
+ * New applications get `config.cache_classes = false` in `config/environments/test.rb`
28
+ unless `--skip-spring`.
29
+
30
+ *Xavier Noria*
31
+
32
+ * Autoloading during initialization is deprecated.
33
+
34
+ *Xavier Noria*
35
+
36
+ * Only force `:async` ActiveJob adapter to `:inline` during seeding.
37
+
38
+ *BatedUrGonnaDie*
39
+
40
+ * The `connection` option of `rails dbconsole` command is deprecated in
41
+ favor of `database` option.
42
+
43
+ *Yuji Yaginuma*
44
+
45
+ * Replace `chromedriver-helper` gem with `webdrivers` in default Gemfile.
46
+ `chromedriver-helper` is deprecated as of March 31, 2019 and won't
47
+ receive any further updates.
48
+
49
+ *Guillermo Iguaran‮*
50
+
51
+ * Applications running in `:zeitwerk` mode that use `bootsnap` need
52
+ to upgrade `bootsnap` to at least 1.4.2.
53
+
54
+ *Xavier Noria*
55
+
56
+ * Add `config.disable_sandbox` option to Rails console.
57
+
58
+ This setting will disable `rails console --sandbox` mode, preventing
59
+ developer from accidentally starting a sandbox console,
60
+ which when left inactive, can cause the database server to run out of memory.
61
+
62
+ *Prem Sichanugrist*
63
+
64
+ * Add `-e/--environment` option to `rails initializers`.
65
+
66
+ *Yuji Yaginuma*
67
+
68
+
1
69
  ## Rails 6.0.0.beta3 (March 11, 2019) ##
2
70
 
3
- * No changes.
71
+ * Generate random development secrets
72
+
73
+ A random development secret is now generated to tmp/development_secret.txt
74
+
75
+ This avoids an issue where development mode servers were vulnerable to
76
+ remote code execution.
77
+
78
+ Fixes CVE-2019-5420
79
+
80
+ *Eileen M. Uchitelle*, *Aaron Patterson*, *John Hawthorn*
4
81
 
5
82
 
6
83
  ## Rails 6.0.0.beta2 (February 25, 2019) ##
@@ -49,7 +126,9 @@
49
126
  gsub Gemfile
50
127
  ```
51
128
 
52
- The change command copies a template `config/database.yml` with the target database adapter into your app, and replaces your database gem with the target database gem.
129
+ The change command copies a template `config/database.yml` with
130
+ the target database adapter into your app, and replaces your database gem
131
+ with the target database gem.
53
132
 
54
133
  *Gannon McGibbon*
55
134
 
@@ -73,9 +152,9 @@
73
152
 
74
153
  *George Claghorn*
75
154
 
76
- * Introduce guard against DNS rebinding attacks
155
+ * Introduce guard against DNS rebinding attacks.
77
156
 
78
- The `ActionDispatch::HostAuthorization` is a new middleware that prevent
157
+ The `ActionDispatch::HostAuthorization` is a new middleware that prevents
79
158
  against DNS rebinding and other `Host` header attacks. It is included in
80
159
  the development environment by default with the following configuration:
81
160
 
@@ -183,7 +262,7 @@
183
262
  The encryption key can be in `ENV["RAILS_MASTER_KEY"]` or `config/credentials/production.key`.
184
263
 
185
264
  Environment credentials overrides can be edited with `rails credentials:edit --environment production`.
186
- If no override is setup for the passed environment, it will be created.
265
+ If no override is set up for the passed environment, it will be created.
187
266
 
188
267
  Additionally, the default lookup paths can be overwritten with these configs:
189
268
 
@@ -273,9 +352,9 @@
273
352
 
274
353
  *Jose Luis Duran*
275
354
 
276
- * Deprecate support for using the `HOST` environment to specify the server IP.
355
+ * Deprecate support for using the `HOST` environment variable to specify the server IP.
277
356
 
278
- The `BINDING` environment should be used instead.
357
+ The `BINDING` environment variable should be used instead.
279
358
 
280
359
  Fixes #29516.
281
360
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  \Rails is a web-application framework that includes everything needed to
6
6
  create database-backed web applications according to the
7
- {Model-View-Controller (MVC)}[http://en.wikipedia.org/wiki/Model-view-controller]
7
+ {Model-View-Controller (MVC)}[https://en.wikipedia.org/wiki/Model-view-controller]
8
8
  pattern.
9
9
 
10
10
  Understanding the MVC pattern is key to understanding \Rails. MVC divides your
@@ -79,7 +79,7 @@ and may also be used independently outside \Rails.
79
79
  * The \README file created within your application.
80
80
  * {Getting Started with \Rails}[https://guides.rubyonrails.org/getting_started.html].
81
81
  * {Ruby on \Rails Guides}[https://guides.rubyonrails.org].
82
- * {The API Documentation}[http://api.rubyonrails.org].
82
+ * {The API Documentation}[https://api.rubyonrails.org].
83
83
  * {Ruby on \Rails Tutorial}[https://www.railstutorial.org/book].
84
84
 
85
85
  == Contributing
@@ -88,7 +88,7 @@ We encourage you to contribute to Ruby on \Rails! Please check out the
88
88
  {Contributing to Ruby on \Rails guide}[https://guides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how to proceed. {Join us!}[http://contributors.rubyonrails.org]
89
89
 
90
90
  Trying to report a possible security vulnerability in \Rails? Please
91
- check out our {security policy}[http://rubyonrails.org/security/] for
91
+ check out our {security policy}[https://rubyonrails.org/security/] for
92
92
  guidelines about how to proceed.
93
93
 
94
94
  Everyone interacting in \Rails and its sub-projects' codebases, issue trackers, chat rooms, and mailing lists is expected to follow the \Rails {code of conduct}[http://rubyonrails.org/conduct/].
@@ -29,7 +29,7 @@ Railties is released under the MIT license:
29
29
 
30
30
  API documentation is at
31
31
 
32
- * http://api.rubyonrails.org
32
+ * https://api.rubyonrails.org
33
33
 
34
34
  Bug reports can be filed for the Ruby on Rails project here:
35
35
 
@@ -409,7 +409,8 @@ module Rails
409
409
  # The secret_key_base is used as the input secret to the application's key generator, which in turn
410
410
  # is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies.
411
411
  #
412
- # In test and development, this is simply derived as a MD5 hash of the application's name.
412
+ # In development and test, this is randomly generated and stored in a
413
+ # temporary file in <tt>tmp/development_secret.txt</tt>.
413
414
  #
414
415
  # In all other environments, we look for it first in ENV["SECRET_KEY_BASE"],
415
416
  # then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications,
@@ -587,6 +588,7 @@ module Rails
587
588
 
588
589
  if !File.exist?(key_file)
589
590
  random_key = SecureRandom.hex(64)
591
+ FileUtils.mkdir_p(key_file.dirname)
590
592
  File.binwrite(key_file, random_key)
591
593
  end
592
594
 
@@ -18,7 +18,8 @@ module Rails
18
18
  :session_options, :time_zone, :reload_classes_only_on_change,
19
19
  :beginning_of_week, :filter_redirect, :x, :enable_dependency_loading,
20
20
  :read_encrypted_secrets, :log_level, :content_security_policy_report_only,
21
- :content_security_policy_nonce_generator, :require_master_key, :credentials
21
+ :content_security_policy_nonce_generator, :require_master_key, :credentials,
22
+ :disable_sandbox
22
23
 
23
24
  attr_reader :encoding, :api_only, :loaded_config_version, :autoloader
24
25
 
@@ -65,6 +66,7 @@ module Rails
65
66
  @credentials.content_path = default_credentials_content_path
66
67
  @credentials.key_path = default_credentials_key_path
67
68
  @autoloader = :classic
69
+ @disable_sandbox = false
68
70
  end
69
71
 
70
72
  def load_defaults(target_version)
@@ -140,6 +142,10 @@ module Rails
140
142
  active_storage.queues.analysis = :active_storage_analysis
141
143
  active_storage.queues.purge = :active_storage_purge
142
144
  end
145
+
146
+ if respond_to?(:active_record)
147
+ active_record.collection_cache_versioning = true
148
+ end
143
149
  else
144
150
  raise "Unknown version #{target_version.to_s.inspect}"
145
151
  end
@@ -184,6 +190,26 @@ module Rails
184
190
  end
185
191
  end
186
192
 
193
+ # Load the database YAML without evaluating ERB. This allows us to
194
+ # create the rake tasks for multiple databases without filling in the
195
+ # configuration values or loading the environment. Do not use this
196
+ # method.
197
+ #
198
+ # This uses a DummyERB custom compiler so YAML can ignore the ERB
199
+ # tags and load the database.yml for the rake tasks.
200
+ def load_database_yaml # :nodoc:
201
+ if path = paths["config/database"].existent.first
202
+ require "rails/application/dummy_erb_compiler"
203
+
204
+ yaml = Pathname.new(path)
205
+ erb = DummyERB.new(yaml.read)
206
+
207
+ YAML.load(erb.result)
208
+ else
209
+ {}
210
+ end
211
+ end
212
+
187
213
  # Loads and returns the entire raw configuration of database from
188
214
  # values stored in <tt>config/database.yml</tt>.
189
215
  def database_configuration
@@ -49,6 +49,7 @@ module Rails
49
49
  middleware.use ::Rails::Rack::Logger, config.log_tags
50
50
  middleware.use ::ActionDispatch::ShowExceptions, show_exceptions_app
51
51
  middleware.use ::ActionDispatch::DebugExceptions, app, config.debug_exception_response_format
52
+ middleware.use ::ActionDispatch::ActionableExceptions
52
53
 
53
54
  unless config.cache_classes
54
55
  middleware.use ::ActionDispatch::Reloader, app.reloader
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # These classes are used to strip out the ERB configuration
4
+ # values so we can evaluate the database.yml without evaluating
5
+ # the ERB values.
6
+ class DummyERB < ERB # :nodoc:
7
+ def make_compiler(trim_mode)
8
+ DummyCompiler.new trim_mode
9
+ end
10
+ end
11
+
12
+ class DummyCompiler < ERB::Compiler # :nodoc:
13
+ def compile_content(stag, out)
14
+ case stag
15
+ when "<%="
16
+ out.push "_erbout << 'dummy_compiler'"
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/string/inflections"
4
+ require "active_support/core_ext/array/conversions"
5
+
3
6
  module Rails
4
7
  class Application
5
8
  module Finisher
@@ -21,10 +24,44 @@ module Rails
21
24
  end
22
25
  end
23
26
 
27
+ # This will become an error if/when we remove classic mode. The plan is
28
+ # autoloaders won't be configured up to this point in the finisher, so
29
+ # constants just won't be found, raising regular NameError exceptions.
30
+ initializer :warn_if_autoloaded, before: :let_zeitwerk_take_over do
31
+ next if config.cache_classes
32
+ next if ActiveSupport::Dependencies.autoloaded_constants.empty?
33
+
34
+ autoloaded = ActiveSupport::Dependencies.autoloaded_constants
35
+ constants = "constant".pluralize(autoloaded.size)
36
+ enum = autoloaded.to_sentence
37
+ have = autoloaded.size == 1 ? "has" : "have"
38
+ these = autoloaded.size == 1 ? "This" : "These"
39
+ example = autoloaded.first
40
+ example_klass = example.constantize.class
41
+
42
+ ActiveSupport::DescendantsTracker.clear
43
+ ActiveSupport::Dependencies.clear
44
+
45
+ ActiveSupport::Deprecation.warn(<<~WARNING)
46
+ Initialization autoloaded the #{constants} #{enum}.
47
+
48
+ Being able to do this is deprecated. Autoloading during initialization is going
49
+ to be an error condition in future versions of Rails.
50
+
51
+ Reloading does not reboot the application, and therefore code executed during
52
+ initialization does not run again. So, if you reload #{example}, for example,
53
+ the expected changes won't be reflected in that stale #{example_klass} object.
54
+
55
+ #{these} autoloaded #{constants} #{have} been unloaded.
56
+
57
+ Please, check the "Autoloading and Reloading Constants" guide for solutions.
58
+ WARNING
59
+ end
60
+
24
61
  initializer :let_zeitwerk_take_over do
25
62
  if config.autoloader == :zeitwerk
26
63
  require "active_support/dependencies/zeitwerk_integration"
27
- ActiveSupport::Dependencies::ZeitwerkIntegration.take_over
64
+ ActiveSupport::Dependencies::ZeitwerkIntegration.take_over(enable_reloading: !config.cache_classes)
28
65
  end
29
66
  end
30
67
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/dependencies/zeitwerk_integration"
4
+
3
5
  module Rails
4
6
  module Autoloaders # :nodoc:
5
7
  class << self
@@ -7,13 +9,19 @@ module Rails
7
9
 
8
10
  def main
9
11
  if zeitwerk_enabled?
10
- @main ||= Zeitwerk::Loader.new.tap { |loader| loader.tag = "rails.main" }
12
+ @main ||= Zeitwerk::Loader.new.tap do |loader|
13
+ loader.tag = "rails.main"
14
+ loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
15
+ end
11
16
  end
12
17
  end
13
18
 
14
19
  def once
15
20
  if zeitwerk_enabled?
16
- @once ||= Zeitwerk::Loader.new.tap { |loader| loader.tag = "rails.once" }
21
+ @once ||= Zeitwerk::Loader.new.tap do |loader|
22
+ loader.tag = "rails.once"
23
+ loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
24
+ end
17
25
  end
18
26
  end
19
27
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
+ require "active_support/core_ext/class/attribute"
4
5
 
5
6
  module Rails
6
7
  module Command
@@ -8,16 +9,18 @@ module Rails
8
9
  extend ActiveSupport::Concern
9
10
 
10
11
  included do
11
- class_option :environment, aliases: "-e", type: :string,
12
- desc: "Specifies the environment to run this console under (test/development/production)."
12
+ no_commands do
13
+ class_attribute :environment_desc, default: "Specifies the environment to run this #{self.command_name} under (test/development/production)."
14
+ end
15
+ class_option :environment, aliases: "-e", type: :string, desc: environment_desc
13
16
  end
14
17
 
15
18
  private
16
- def extract_environment_option_from_argument
19
+ def extract_environment_option_from_argument(default_environment: Rails::Command.environment)
17
20
  if options[:environment]
18
21
  self.options = options.merge(environment: acceptable_environment(options[:environment]))
19
22
  else
20
- self.options = options.merge(environment: Rails::Command.environment)
23
+ self.options = options.merge(environment: default_environment)
21
24
  end
22
25
  end
23
26
 
@@ -26,6 +26,12 @@ module Rails
26
26
  @options = options
27
27
 
28
28
  app.sandbox = sandbox?
29
+
30
+ if sandbox? && app.config.disable_sandbox
31
+ puts "Error: Unable to start console in sandbox mode as sandbox mode is disabled (config.disable_sandbox is true)."
32
+ exit 1
33
+ end
34
+
29
35
  app.load_console
30
36
 
31
37
  @console = app.config.console || IRB
@@ -42,7 +42,7 @@ from leaking.
42
42
  === Environment Specific Credentials
43
43
 
44
44
  The `credentials` command supports passing an `--environment` option to create an
45
- environment specific override. That override will takes precedence over the
45
+ environment specific override. That override will take precedence over the
46
46
  global `config/credentials.yml.enc` file when running in that environment. So:
47
47
 
48
48
  rails credentials:edit --environment development
@@ -2,14 +2,15 @@
2
2
 
3
3
  require "active_support"
4
4
  require "rails/command/helpers/editor"
5
+ require "rails/command/environment_argument"
5
6
 
6
7
  module Rails
7
8
  module Command
8
9
  class CredentialsCommand < Rails::Command::Base # :nodoc:
9
10
  include Helpers::Editor
11
+ include EnvironmentArgument
10
12
 
11
- class_option :environment, aliases: "-e", type: :string,
12
- desc: "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
13
+ self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
13
14
 
14
15
  no_commands do
15
16
  def help
@@ -20,6 +21,7 @@ module Rails
20
21
  end
21
22
 
22
23
  def edit
24
+ extract_environment_option_from_argument(default_environment: nil)
23
25
  require_application!
24
26
 
25
27
  ensure_editor_available(command: "bin/rails credentials:edit") || (return)
@@ -37,6 +39,7 @@ module Rails
37
39
  end
38
40
 
39
41
  def show
42
+ extract_environment_option_from_argument(default_environment: nil)
40
43
  require_application!
41
44
 
42
45
  say credentials.read.presence || missing_credentials_message
@@ -53,7 +56,11 @@ module Rails
53
56
  end
54
57
 
55
58
  def ensure_credentials_have_been_added
56
- encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
59
+ if options[:environment]
60
+ encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
61
+ else
62
+ credentials_generator.add_credentials_file_silently
63
+ end
57
64
  end
58
65
 
59
66
  def change_credentials_in_system_editor
@@ -93,6 +100,13 @@ module Rails
93
100
 
94
101
  Rails::Generators::EncryptedFileGenerator.new
95
102
  end
103
+
104
+ def credentials_generator
105
+ require "rails/generators"
106
+ require "rails/generators/rails/credentials/credentials_generator"
107
+
108
+ Rails::Generators::CredentialsGenerator.new
109
+ end
96
110
  end
97
111
  end
98
112
  end