pah 0.0.15 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -1
  4. data/CHANGELOG.md +19 -1
  5. data/README.md +9 -9
  6. data/Rakefile +1 -1
  7. data/bin/pah +1 -2
  8. data/features/bourbon.feature +1 -1
  9. data/features/env.feature +1 -1
  10. data/features/gemfile.feature +1 -1
  11. data/features/inflections.feature +14 -0
  12. data/features/jumpup.feature +12 -2
  13. data/features/layout.feature +4 -0
  14. data/features/locale.feature +1 -1
  15. data/features/public.feature +9 -0
  16. data/features/readme.feature +1 -1
  17. data/features/rspec.feature +12 -1
  18. data/features/runner.feature +1 -1
  19. data/features/step_definitions/create_simple_app.rb +4 -4
  20. data/features/step_definitions/file_content.rb +1 -1
  21. data/features/support/bin/heroku +1 -1
  22. data/features/support/env.rb +1 -1
  23. data/features/support/file_helpers.rb +0 -1
  24. data/lib/pah.rb +12 -0
  25. data/lib/pah/configuration.rb +18 -0
  26. data/lib/pah/files/Gemfile +22 -22
  27. data/lib/pah/files/README.md +3 -9
  28. data/lib/pah/files/app/views/layouts/application.html.slim +1 -0
  29. data/lib/pah/files/config/initializers/bullet.rb +1 -1
  30. data/lib/pah/files/config/initializers/jumpup_heroku.rb +1 -1
  31. data/lib/pah/files/config/puma.rb +2 -2
  32. data/lib/pah/files/lib/tasks/jumpup.rake +1 -2
  33. data/lib/pah/files/public/humans.txt +5 -0
  34. data/lib/pah/files/spec/acceptance/dummy_spec.rb +3 -4
  35. data/lib/pah/files/spec/rails_helper.rb +5 -4
  36. data/lib/pah/files/spec/support/acceptance_helpers.rb +6 -4
  37. data/lib/pah/files/spec/support/acceptance_macros.rb +0 -1
  38. data/lib/pah/files/spec/support/http_basic_auth.rb +2 -2
  39. data/lib/pah/files/spec/support/matchers.rb +1 -1
  40. data/lib/pah/files/spec/support/random_timezone.rb +6 -0
  41. data/lib/pah/files/spec/support/shared_connection.rb +10 -8
  42. data/lib/pah/files/spec/support/uploaded_file.rb +9 -10
  43. data/lib/pah/files/spec/support/vcr.rb +8 -0
  44. data/lib/pah/rails_template.rb +45 -0
  45. data/lib/pah/runner.rb +20 -0
  46. data/lib/pah/template.rb +30 -37
  47. data/lib/pah/templates/bin.rb +12 -0
  48. data/lib/pah/templates/bourbon.rb +32 -0
  49. data/lib/pah/templates/bullet.rb +14 -0
  50. data/lib/pah/templates/canonical_host.rb +19 -0
  51. data/lib/pah/templates/capybara.rb +16 -0
  52. data/lib/pah/templates/cleanup.rb +12 -0
  53. data/lib/pah/templates/config.rb +42 -0
  54. data/lib/pah/templates/database.rb +17 -0
  55. data/lib/pah/templates/gems.rb +31 -0
  56. data/lib/pah/{partials/_generators.rb → templates/generators.rb} +12 -4
  57. data/lib/pah/templates/git.rb +13 -0
  58. data/lib/pah/{partials/_heroku.rb → templates/heroku.rb} +34 -26
  59. data/lib/pah/templates/inflection.rb +19 -0
  60. data/lib/pah/templates/jumpup.rb +16 -0
  61. data/lib/pah/templates/layout.rb +15 -0
  62. data/lib/pah/templates/letter_opener.rb +19 -0
  63. data/lib/pah/templates/locale.rb +32 -0
  64. data/lib/pah/templates/newrelic.rb +14 -0
  65. data/lib/pah/templates/public.rb +27 -0
  66. data/lib/pah/templates/puma.rb +14 -0
  67. data/lib/pah/templates/rack_deflater.rb +16 -0
  68. data/lib/pah/templates/rack_timeout.rb +12 -0
  69. data/lib/pah/templates/readme.rb +14 -0
  70. data/lib/pah/templates/rollbar.rb +23 -0
  71. data/lib/pah/templates/rspec.rb +26 -0
  72. data/lib/pah/templates/ruby_env.rb +24 -0
  73. data/lib/pah/templates/secret_token.rb +15 -0
  74. data/lib/pah/templates/secure_headers.rb +29 -0
  75. data/lib/pah/{partials/_sendgrid.rb → templates/sendgrid.rb} +12 -4
  76. data/lib/pah/templates/simple_form.rb +17 -0
  77. data/lib/pah/version.rb +3 -2
  78. data/pah.gemspec +14 -15
  79. metadata +43 -35
  80. data/lib/pah/pah.rb +0 -64
  81. data/lib/pah/partials/_bin.rb +0 -4
  82. data/lib/pah/partials/_bourbon.rb +0 -25
  83. data/lib/pah/partials/_bullet.rb +0 -6
  84. data/lib/pah/partials/_canonical_host.rb +0 -11
  85. data/lib/pah/partials/_capybara.rb +0 -8
  86. data/lib/pah/partials/_cleanup.rb +0 -4
  87. data/lib/pah/partials/_config.rb +0 -36
  88. data/lib/pah/partials/_database.rb +0 -9
  89. data/lib/pah/partials/_gems.rb +0 -22
  90. data/lib/pah/partials/_git.rb +0 -5
  91. data/lib/pah/partials/_jumpup.rb +0 -8
  92. data/lib/pah/partials/_layout.rb +0 -7
  93. data/lib/pah/partials/_letter_opener.rb +0 -11
  94. data/lib/pah/partials/_locale.rb +0 -24
  95. data/lib/pah/partials/_newrelic.rb +0 -6
  96. data/lib/pah/partials/_public.rb +0 -16
  97. data/lib/pah/partials/_puma.rb +0 -6
  98. data/lib/pah/partials/_rack_deflater.rb +0 -8
  99. data/lib/pah/partials/_rack_timeout.rb +0 -4
  100. data/lib/pah/partials/_readme.rb +0 -6
  101. data/lib/pah/partials/_rollbar.rb +0 -15
  102. data/lib/pah/partials/_rspec.rb +0 -17
  103. data/lib/pah/partials/_ruby_env.rb +0 -16
  104. data/lib/pah/partials/_secret_token.rb +0 -7
  105. data/lib/pah/partials/_secure_headers.rb +0 -21
  106. data/lib/pah/partials/_simple_form.rb +0 -9
@@ -9,6 +9,7 @@ html lang="pt"
9
9
  = page_title(app_name: 'example')
10
10
 
11
11
  link href="/favicon.ico" rel=("shortcut icon")
12
+ link rel="author" href="/humans.txt"
12
13
 
13
14
  = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
14
15
  = javascript_include_tag 'application', 'data-turbolinks-track' => true
@@ -11,4 +11,4 @@ if defined? Bullet
11
11
  # :receiver => 'your_account@jabber.org',
12
12
  # :show_online_status => true }
13
13
  # Bullet.airbrake = true
14
- end
14
+ end
@@ -1,3 +1,3 @@
1
1
  Jumpup::Heroku.configure do |config|
2
2
  config.app = 'PROJECT'
3
- end if Rails.env.development?
3
+ end if Rails.env.development?
@@ -5,8 +5,8 @@ threads Integer(ENV['MIN_THREADS'] || 1), Integer(ENV['MAX_THREADS'] || 16)
5
5
 
6
6
  preload_app!
7
7
 
8
- rackup DefaultRackup
9
- port ENV['PORT'] || 3000
8
+ rackup DefaultRackup
9
+ port ENV['PORT'] || 3000
10
10
  environment ENV['RACK_ENV'] || 'development'
11
11
 
12
12
  on_worker_boot do
@@ -4,7 +4,6 @@ INTEGRATION_TASKS = %w(
4
4
  jumpup:bundle_install
5
5
  db:migrate
6
6
  spec
7
- jumpup:coverage_verify
8
- jumpup:finish
9
7
  jumpup:heroku:finish
8
+ jumpup:finish
10
9
  )
@@ -0,0 +1,5 @@
1
+ /* TEAM */
2
+ Developer: HE:labs team
3
+ Contact: tech [at] helabs.com.br
4
+ Twitter: @helabs
5
+ From: Rio de Janeiro, Brazil
@@ -1,9 +1,8 @@
1
- # -*- encoding : utf-8 -*-
2
1
  require 'rails_helper'
3
2
 
4
- RSpec.describe "Homepage", type: :request do
5
- xit {
3
+ RSpec.describe 'Homepage', type: :request do
4
+ xit do
6
5
  visit root_path
7
6
  page.should have_content('Copyright © 2012 App')
8
- }
7
+ end
9
8
  end
@@ -1,11 +1,11 @@
1
- ENV["RAILS_ENV"] ||= 'test'
1
+ ENV['RAILS_ENV'] ||= 'test'
2
2
  require 'spec_helper'
3
- require File.expand_path("../../config/environment", __FILE__)
3
+ require File.expand_path('../../config/environment', __FILE__)
4
4
  require 'rspec/rails'
5
5
  require 'shoulda/matchers'
6
- require "email_spec"
6
+ require 'email_spec'
7
7
 
8
- Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
8
+ Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
9
9
 
10
10
  ActiveRecord::Migration.maintain_test_schema!
11
11
 
@@ -16,4 +16,5 @@ RSpec.configure do |config|
16
16
  config.include FactoryGirl::Syntax::Methods
17
17
  config.include EmailSpec::Helpers
18
18
  config.include EmailSpec::Matchers
19
+ config.include ActiveSupport::Testing::TimeHelpers
19
20
  end
@@ -1,20 +1,22 @@
1
- # -*- encoding : utf-8 -*-
2
1
  module AcceptanceHelpers
3
2
  def saop
4
3
  save_and_open_page
5
4
  end
5
+
6
6
  def handle_js_confirm(message = nil, accept = true)
7
- page.execute_script("window.original_confirm_function = window.confirm")
7
+ page.execute_script('window.original_confirm_function = window.confirm')
8
8
  page.execute_script("window.confirm = function(msg) { $.cookie('confirm_message', msg); return #{!!accept}; }")
9
9
  yield
10
10
  page.evaluate_script("$.cookie('confirm_message')").should == message unless message.nil?
11
11
  ensure
12
- page.evaluate_script "window.confirm = window.original_confirm_function"
12
+ page.evaluate_script 'window.confirm = window.original_confirm_function'
13
13
  end
14
+
14
15
  def fill_in_autocomplete(capybara_selector, jquery_selector, value)
15
16
  fill_in capybara_selector, with: value
16
- page.execute_script %Q{$('#{jquery_selector}').keydown()}
17
+ page.execute_script "$('#{jquery_selector}').keydown()"
17
18
  end
19
+
18
20
  def choose_autocomplete(text)
19
21
  find('ul.ui-autocomplete').should have_content(text)
20
22
  page.execute_script("$('.ui-menu-item:contains(\"#{text}\")').find('a').trigger('mouseenter').click()")
@@ -1,4 +1,3 @@
1
- # -*- encoding : utf-8 -*-
2
1
  module AcceptanceMacros
3
2
  def login!
4
3
  before(:each) do
@@ -1,6 +1,6 @@
1
1
  module AuthSpec
2
2
  def http_basic_login!
3
- request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('usuario','senha')
3
+ request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('usuario', 'senha')
4
4
  end
5
5
  end
6
6
 
@@ -15,7 +15,7 @@ module CapybaraAuthSpec
15
15
  elsif page.driver.respond_to?(:browser) && page.driver.browser.respond_to?(:basic_authorize)
16
16
  page.driver.browser.basic_authorize(name, password)
17
17
  else
18
- raise "I don't know how to log in!"
18
+ fail "I don't know how to log in!"
19
19
  end
20
20
  end
21
21
  end
@@ -2,4 +2,4 @@ RSpec::Matchers.define :be_bad_request do
2
2
  match do |response|
3
3
  response.code.to_s =~ /^4/
4
4
  end
5
- end
5
+ end
@@ -0,0 +1,6 @@
1
+ RSpec.configure do |config|
2
+ config.before(:suite) do
3
+ Time.zone = ActiveSupport::TimeZone.all.sample
4
+ puts "Randomized timezone: #{Time.zone}"
5
+ end
6
+ end
@@ -1,12 +1,14 @@
1
- class ActiveRecord::Base
2
- mattr_accessor :shared_connection
3
- @@shared_connection = nil
4
-
5
- def self.connection
6
- @@shared_connection || retrieve_connection
1
+ module ActiveRecord
2
+ class Base
3
+ mattr_accessor :shared_connection
4
+ @@shared_connection = nil
5
+
6
+ def self.connection
7
+ @@shared_connection || retrieve_connection
8
+ end
7
9
  end
8
10
  end
9
-
11
+
10
12
  # Forces all threads to share the same connection. This works on
11
13
  # Capybara because it starts the web server in a thread.
12
- ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
14
+ ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
@@ -1,16 +1,15 @@
1
- # -*- encoding : utf-8 -*-
2
1
  module UploadFileTestHelper
3
- def uploaded_file(filename, content_type = "text/csv")
4
- t = Tempfile.new([filename.split("/").last, filename.split(".").last])
2
+ def uploaded_file(filename, content_type = 'text/csv')
3
+ t = Tempfile.new([filename.split('/').last, filename.split('.').last])
5
4
  t.binmode
6
- path = File.join(Rails.root, "spec", "fixtures", filename)
5
+ path = File.join(Rails.root, 'spec', 'fixtures', filename)
7
6
  FileUtils.copy_file(path, t.path)
8
- ActionDispatch::Http::UploadedFile.new({
9
- filename: filename,
10
- head: "Content-Disposition: form-data; name=\"file\"; filename=\"#{filename}\"\r\nContent-Type: #{content_type}\r\n",
11
- type: content_type,
12
- tempfile: t
13
- })
7
+ ActionDispatch::Http::UploadedFile.new(
8
+ filename: filename,
9
+ head: "Content-Disposition: form-data; name=\"file\"; filename=\"#{filename}\"\r\nContent-Type: #{content_type}\r\n",
10
+ type: content_type,
11
+ tempfile: t
12
+ )
14
13
  end
15
14
  end
16
15
 
@@ -4,4 +4,12 @@ VCR.configure do |c|
4
4
  c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
5
5
  c.hook_into :webmock
6
6
  c.configure_rspec_metadata!
7
+
8
+ # Filter Rails secrets that are strings or numbers
9
+ secrets_to_filter = Rails.application.secrets.select do |key, value|
10
+ value.is_a?(String) || value.is_a?(Numeric)
11
+ end
12
+ secrets_to_filter.each do |key, value|
13
+ c.filter_sensitive_data("<#{key.upcase}>") { value }
14
+ end
7
15
  end
@@ -0,0 +1,45 @@
1
+ require 'pah'
2
+
3
+ puts "\n========================================================="
4
+ puts ' Pah'.yellow.bold
5
+ puts "=========================================================\n"
6
+
7
+ Pah.configure do |config|
8
+ config.app_name = @app_name
9
+ end
10
+
11
+ runner = Pah::Runner.new
12
+ runner.apply_n :config
13
+ runner.apply_n :git, 'Initializing new Git repo...'
14
+ runner.apply_n :ruby_env, 'Setting up ruby env...'
15
+ runner.apply_n :cleanup, 'Removing unnecessary files...'
16
+ runner.apply_n :gems, 'Setting up bundler and installing bundled gems (may take a while)...'
17
+ runner.apply_n :database, 'Initializing databases...'
18
+ runner.apply_n :rspec, 'Setting up RSpec...'
19
+ runner.apply_n :layout, 'Adding layout files...'
20
+ runner.apply_n :bourbon, 'Setting up Bourbon files...'
21
+ runner.apply_n :public, 'Adding public files...'
22
+ runner.apply_n :inflection, 'Adding API acronym to inflection file...'
23
+ runner.apply_n :secure_headers, 'Adding secure headers...'
24
+ runner.apply_n :secret_token, 'Replacing secret token with environment variable...'
25
+ runner.apply_n :capybara, 'Adding capybara helpers...'
26
+ runner.apply_n :generators, 'Adding generators...'
27
+ runner.apply_n :simple_form, 'Configuring simple_form...'
28
+ runner.apply_n :letter_opener, 'Adding letter_opener...'
29
+ runner.apply_n :sendgrid, 'Adding sendgrid...'
30
+ runner.apply_n :bullet, 'Setting up bullet...'
31
+ runner.apply_n :locale, 'Adding locale...'
32
+ runner.apply_n :canonical_host, 'Configuring canonical hosts...'
33
+ runner.apply_n :puma, 'Configuring Puma...'
34
+ runner.apply_n :rack_timeout, 'Setting up Rack::Timeout...'
35
+ runner.apply_n :rack_deflater, 'Setting up Rack::Deflater...'
36
+ runner.apply_n :jumpup, 'Setting up Jumpup...'
37
+ runner.apply_n :newrelic, 'Setting up New Relic...'
38
+ runner.apply_n :readme, 'Adding default README...'
39
+ runner.apply_n :rollbar, 'Setting up Rollbar...'
40
+ runner.apply_n :heroku, 'Configuring Heroku application...'
41
+ runner.apply_n :bin, 'Configuring the binstub...'
42
+
43
+ puts "\n========================================================="
44
+ puts ' CONGRATS! INSTALLATION COMPLETE!'.yellow.bold
45
+ puts "=========================================================\n\n\n"
data/lib/pah/runner.rb ADDED
@@ -0,0 +1,20 @@
1
+ module Pah
2
+ class Runner < Rails::Generators::Base
3
+ def partials
4
+ File.join(TEMPLATE_ROOT, 'templates')
5
+ end
6
+
7
+ def apply_n(partial_name, message = '')
8
+ puts message.magenta
9
+
10
+ in_root do
11
+ Bundler.with_clean_env do
12
+ require "#{partials}/#{partial_name}"
13
+ "::Pah::Templates::#{partial_name.to_s.classify}".constantize.new.call
14
+ end
15
+ end
16
+
17
+ puts "\n"
18
+ end
19
+ end
20
+ end
data/lib/pah/template.rb CHANGED
@@ -1,40 +1,33 @@
1
- require File.join(File.dirname(__FILE__), 'pah.rb')
1
+ module Pah
2
+ class Template < Rails::Generators::Base
3
+ def static_files
4
+ File.join(TEMPLATE_ROOT, 'files')
5
+ end
2
6
 
3
- puts "\n========================================================="
4
- puts " Pah".yellow.bold
5
- puts "=========================================================\n"
7
+ def copy_static_file(path)
8
+ remove_file path
9
+ create_file path, File.read(File.join(static_files, path))
10
+ end
6
11
 
7
- apply_n :config
8
- apply_n :git, 'Initializing new Git repo...'
9
- apply_n :ruby_env, 'Setting up ruby env...'
10
- apply_n :cleanup, 'Removing unnecessary files...'
11
- apply_n :gems, 'Setting up bundler and installing bundled gems (may take a while)...'
12
- apply_n :database, 'Initializing databases...'
13
- apply_n :rspec, 'Setting up RSpec...'
14
- apply_n :layout, 'Adding layout files...'
15
- apply_n :bourbon, 'Setting up Bourbon files...'
16
- apply_n :public, 'Adding public files...'
17
- apply_n :secure_headers, 'Adding secure headers...'
18
- apply_n :secret_token, 'Replacing secret token with environment variable...'
19
- apply_n :capybara, 'Adding capybara helpers...'
20
- apply_n :generators, 'Adding generators...'
21
- apply_n :simple_form, 'Configuring simple_form...'
22
- apply_n :letter_opener, 'Adding letter_opener...'
23
- apply_n :sendgrid, 'Adding sendgrid...'
24
- apply_n :bullet, 'Setting up bullet...'
25
- apply_n :locale, 'Adding locale...'
26
- apply_n :canonical_host, 'Configuring canonical hosts...'
27
- apply_n :puma, 'Configuring Puma...'
28
- apply_n :rack_timeout, 'Setting up Rack::Timeout...'
29
- apply_n :rack_deflater, 'Setting up Rack::Deflater...'
30
- apply_n :jumpup, 'Setting up Jumpup...'
31
- apply_n :newrelic, 'Setting up New Relic...'
32
- apply_n :readme, 'Adding default README...'
33
- apply_n :rollbar, 'Setting up Rollbar...'
34
- apply_n :heroku, 'Configuring Heroku application...'
35
- apply_n :bin, 'Configuring the binstub...'
12
+ def will_you_like_to?(question)
13
+ answer = ask("Will you like to #{question} [y,n]".red)
14
+ case answer.downcase
15
+ when 'yes', 'y'
16
+ true
17
+ when 'no', 'n'
18
+ false
19
+ else
20
+ will_you_like_to?(question)
21
+ end
22
+ end
36
23
 
37
- puts "\n========================================================="
38
- puts " CONGRATS! INSTALLATION COMPLETE!".yellow.bold
39
- puts "=========================================================\n\n\n"
40
- def run_bundle; end
24
+ def ask_unless_test(*params)
25
+ ask(*params)
26
+ end
27
+
28
+ def git_commit(message)
29
+ message = "#{message}\n\nGenerated by pah version #{Pah::VERSION}"
30
+ git commit: "-qm '#{message}'"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ module Pah
2
+ module Templates
3
+ class Bin < Pah::Template
4
+ def call
5
+ system 'bundle exec spring binstub --all'
6
+
7
+ git add: 'bin/rake bin/rails bin/spring'
8
+ git_commit 'Add spring bin files'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,32 @@
1
+ module Pah
2
+ module Templates
3
+ class Bourbon < Pah::Template
4
+ def call
5
+ css_path = 'app/assets/stylesheets/'
6
+ application_css_path = "#{css_path}application.css"
7
+
8
+ prepend_to_file application_css_path do
9
+ <<IMPORTS
10
+ @import 'bourbon';
11
+ @import 'base/base';
12
+ @import 'neat';
13
+ IMPORTS
14
+ end
15
+
16
+ gsub_file application_css_path, /\*= require_tree \./, '*'
17
+
18
+ inside(css_path) do
19
+ system 'bundle exec bitters install'
20
+ end
21
+
22
+ gsub_file "#{css_path}base/_base.scss", %r{// @import 'grid-settings';}, "@import 'grid-settings';"
23
+
24
+ system "mv #{application_css_path} #{css_path}application.css.scss"
25
+
26
+ git rm: application_css_path
27
+ git add: css_path
28
+ git_commit 'Install bourbon.'
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ module Pah
2
+ module Templates
3
+ class Bullet < Pah::Template
4
+ def call
5
+ puts 'Adding bullet... '.magenta
6
+
7
+ copy_static_file 'config/initializers/bullet.rb'
8
+
9
+ git add: 'config/initializers/bullet.rb'
10
+ git_commit 'Adding bullet config.'
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ module Pah
2
+ module Templates
3
+ class CanonicalHost < Pah::Template
4
+ def call
5
+ rack_canonical = <<CANONICAL
6
+
7
+ #Run heroku config:add CANONICAL_HOST=yourdomain.com
8
+ #For more information, see: https://github.com/tylerhunt/rack-canonical-host#usage
9
+ use Rack::CanonicalHost, ENV['CANONICAL_HOST'] if ENV['CANONICAL_HOST']
10
+ CANONICAL
11
+
12
+ inject_into_file 'config.ru', rack_canonical, after: "require ::File.expand_path('../config/environment', __FILE__)", verbose: false
13
+
14
+ git add: 'config.ru'
15
+ git_commit 'Add rack-canonical-host.'
16
+ end
17
+ end
18
+ end
19
+ end