suspenders 1.53.0 → 1.54.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +4 -3
  4. data/CONTRIBUTING.md +15 -15
  5. data/GOALS.md +65 -0
  6. data/NEWS.md +29 -0
  7. data/README.md +2 -4
  8. data/docs/heroku_deploy.md +2 -5
  9. data/lib/suspenders.rb +6 -0
  10. data/lib/suspenders/actions.rb +2 -2
  11. data/lib/suspenders/adapters/heroku.rb +14 -1
  12. data/lib/suspenders/app_builder.rb +3 -71
  13. data/lib/suspenders/generators/advisories_generator.rb +19 -0
  14. data/lib/suspenders/generators/app_generator.rb +7 -33
  15. data/lib/suspenders/generators/base.rb +39 -0
  16. data/lib/suspenders/generators/ci_generator.rb +19 -9
  17. data/lib/suspenders/generators/db_optimizations_generator.rb +3 -15
  18. data/lib/suspenders/generators/js_driver_generator.rb +1 -2
  19. data/lib/suspenders/generators/preloader_generator.rb +122 -0
  20. data/lib/suspenders/generators/production/compression_generator.rb +14 -0
  21. data/lib/suspenders/generators/production/deployment_generator.rb +1 -12
  22. data/lib/suspenders/generators/production/email_generator.rb +5 -8
  23. data/lib/suspenders/generators/production/manifest_generator.rb +1 -0
  24. data/lib/suspenders/generators/production/single_redirect.rb +15 -0
  25. data/lib/suspenders/generators/production/timeout_generator.rb +1 -0
  26. data/lib/suspenders/generators/profiler_generator.rb +35 -0
  27. data/lib/suspenders/generators/runner_generator.rb +48 -0
  28. data/lib/suspenders/generators/staging/pull_requests_generator.rb +2 -10
  29. data/lib/suspenders/generators/static_generator.rb +4 -0
  30. data/lib/suspenders/generators/stylesheet_base_generator.rb +5 -6
  31. data/lib/suspenders/generators/testing_generator.rb +0 -11
  32. data/lib/suspenders/version.rb +1 -1
  33. data/spec/adapters/heroku_spec.rb +28 -2
  34. data/spec/expand_json_spec.rb +89 -0
  35. data/spec/features/advisories_spec.rb +24 -0
  36. data/spec/features/ci_spec.rb +31 -0
  37. data/spec/features/db_optimizations_spec.rb +19 -0
  38. data/spec/features/heroku_spec.rb +6 -13
  39. data/spec/features/new_project_spec.rb +5 -27
  40. data/spec/features/preloader_spec.rb +25 -0
  41. data/spec/features/production/compression_spec.rb +23 -0
  42. data/spec/features/production/manifest_spec.rb +2 -0
  43. data/spec/features/production/single_redirect_spec.rb +25 -0
  44. data/spec/features/profiler_spec.rb +20 -0
  45. data/spec/features/runner_spec.rb +30 -0
  46. data/spec/features/static_spec.rb +17 -0
  47. data/spec/support/contain_json_matcher.rb +16 -10
  48. data/spec/support/project_files.rb +12 -0
  49. data/spec/support/rails_template.rb +1 -0
  50. data/spec/support/suspenders.rb +16 -13
  51. data/suspenders.gemspec +1 -2
  52. data/templates/Gemfile.erb +1 -6
  53. data/templates/application.scss +0 -1
  54. data/templates/bin_auto_migrate +5 -0
  55. data/templates/bin_deploy +0 -2
  56. data/templates/bin_setup +2 -2
  57. data/templates/bin_setup_review_app.erb +0 -1
  58. data/templates/chromedriver.rb +10 -0
  59. data/templates/descriptions/advisories.md +5 -0
  60. data/templates/descriptions/analytics.md +4 -0
  61. data/templates/descriptions/ci.md +4 -0
  62. data/templates/descriptions/compression.md +4 -0
  63. data/templates/descriptions/db_optimizations.md +2 -0
  64. data/templates/descriptions/deployment.md +5 -0
  65. data/templates/descriptions/email.md +9 -0
  66. data/templates/descriptions/factories.md +12 -0
  67. data/templates/descriptions/force_tls.md +1 -0
  68. data/templates/descriptions/forms.md +1 -0
  69. data/templates/descriptions/inline_svg.md +2 -0
  70. data/templates/descriptions/jobs.md +3 -0
  71. data/templates/descriptions/js_driver.md +4 -0
  72. data/templates/descriptions/json.md +1 -0
  73. data/templates/descriptions/lint.md +3 -0
  74. data/templates/descriptions/manifest.md +2 -0
  75. data/templates/descriptions/preloader.md +3 -0
  76. data/templates/descriptions/profiler.md +7 -0
  77. data/templates/descriptions/pull_requests.md +4 -0
  78. data/templates/descriptions/runner.md +10 -0
  79. data/templates/descriptions/single_redirect.md +1 -0
  80. data/templates/descriptions/static.md +5 -0
  81. data/templates/descriptions/stylelint.md +3 -0
  82. data/templates/descriptions/stylesheet_base.md +4 -0
  83. data/templates/descriptions/testing.md +9 -0
  84. data/templates/descriptions/timeout.md +4 -0
  85. data/templates/descriptions/views.md +8 -0
  86. data/templates/partials/ci_simplecov.rb +16 -0
  87. data/templates/partials/db_optimizations_configuration.rb +7 -0
  88. data/templates/partials/deployment_readme.md +8 -0
  89. data/templates/partials/email_smtp.rb +3 -0
  90. data/templates/partials/profiler_readme.md +8 -0
  91. data/templates/partials/pull_requests_config.rb +5 -0
  92. data/templates/partials/runner_readme.md +31 -0
  93. data/templates/partials/runner_setup.rb +3 -0
  94. data/templates/rack_mini_profiler.rb +2 -0
  95. data/templates/rails_helper.rb +4 -1
  96. data/templates/{dotfiles/.env → sample_env} +0 -1
  97. data/templates/spec_helper.rb +4 -7
  98. data/templates/spring.rb +6 -0
  99. data/templates/suspenders_gitignore +1 -1
  100. metadata +75 -25
  101. data/templates/dotfiles/.ctags +0 -2
  102. data/templates/puma.rb +0 -28
@@ -8,6 +8,7 @@ RSpec.describe "suspenders:production:manifest", type: :generator do
8
8
  name: SuspendersTestHelpers::APP_NAME.dasherize,
9
9
  env: {
10
10
  APPLICATION_HOST: { required: true },
11
+ AUTO_MIGRATE_DB: { value: true },
11
12
  EMAIL_RECIPIENTS: { required: true },
12
13
  HEROKU_APP_NAME: { required: true },
13
14
  HEROKU_PARENT_APP_NAME: { required: true },
@@ -24,6 +25,7 @@ RSpec.describe "suspenders:production:manifest", type: :generator do
24
25
  name: SuspendersTestHelpers::APP_NAME.dasherize,
25
26
  env: {
26
27
  APPLICATION_HOST: { required: true },
28
+ AUTO_MIGRATE_DB: { value: true },
27
29
  EMAIL_RECIPIENTS: { required: true },
28
30
  HEROKU_APP_NAME: { required: true },
29
31
  HEROKU_PARENT_APP_NAME: { required: true },
@@ -0,0 +1,25 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "suspenders:production:single_redirect", type: :generator do
4
+ context "generate" do
5
+ it "adds Rack::CanonicalHost to the production middleware" do
6
+ with_app { generate("suspenders:production:single_redirect") }
7
+ middleware_canonical_host = %r{config.middleware.use Rack::CanonicalHost, ENV.fetch\("APPLICATION_HOST"\)}
8
+
9
+ expect("config/environments/production.rb").to match_contents(
10
+ middleware_canonical_host,
11
+ )
12
+ end
13
+ end
14
+
15
+ context "destroy" do
16
+ it "removes Rack::CanonicalHost from the production middleware" do
17
+ with_app { destroy("suspenders:production:single_redirect") }
18
+ middleware_canonical_host = %r{config.middleware.use Rack::CanonicalHost, ENV.fetch\("APPLICATION_HOST"\)}
19
+
20
+ expect("config/environments/production.rb").not_to match_contents(
21
+ middleware_canonical_host,
22
+ )
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "suspenders:profiler", type: :generator do
4
+ it "sets up rack-min-profiler" do
5
+ with_app { generate("suspenders:profiler") }
6
+
7
+ expect("config/initializers/rack_mini_profiler.rb").to \
8
+ match_contents(/Rack::MiniProfilerRails.initialize/)
9
+ expect("Gemfile").to match_contents(/rack-mini-profiler/)
10
+ expect(".env").to match_contents(/RACK_MINI_PROFILER=0/)
11
+ end
12
+
13
+ it "removes rack-min-profiler" do
14
+ with_app { destroy("suspenders:profiler") }
15
+
16
+ expect("config/initializers/rack_mini_profiler.rb").not_to exist_as_a_file
17
+ expect("Gemfile").not_to match_contents(/rack-mini-profiler/)
18
+ expect(".env").not_to exist_as_a_file
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "suspenders:runner", type: :generator do
4
+ it "configures the app for running" do
5
+ with_app { generate("suspenders:runner") }
6
+
7
+ expect("Procfile").to exist_as_a_file
8
+ expect(".sample.env").to exist_as_a_file
9
+ expect("bin/setup").to match_contents(/\.sample\.env/)
10
+ expect("README.md").to match_contents(/\.sample\.env/)
11
+ end
12
+
13
+ it "removes custom app running configuration" do
14
+ with_app { destroy("suspenders:runner") }
15
+
16
+ expect("README.md").not_to match_contents(/\.sample\.env/)
17
+ expect("bin/setup").not_to match_contents(/\.sample\.env/)
18
+ expect(".sample.env").not_to exist_as_a_file
19
+ expect("Procfile").not_to exist_as_a_file
20
+ end
21
+
22
+ it "configures the app with a shell script bin/setup" do
23
+ with_app do
24
+ copy_file "bin_setup", "bin/setup"
25
+ generate("suspenders:runner")
26
+ end
27
+
28
+ expect("bin/setup").to match_contents(/\.sample\.env/)
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "suspenders:static", type: :generator do
4
+ it "adds the gem and pages directory" do
5
+ with_app { generate("suspenders:static") }
6
+
7
+ expect("Gemfile").to match_contents(/high_voltage/)
8
+ expect("app/views/pages/.keep").to exist_as_a_file
9
+ end
10
+
11
+ it "removes the gem and pages directory" do
12
+ with_app { destroy("suspenders:static") }
13
+
14
+ expect("app/views/pages/.keep").not_to exist_as_a_file
15
+ expect("Gemfile").not_to match_contents(/high_voltage/)
16
+ end
17
+ end
@@ -4,21 +4,27 @@ require "json"
4
4
 
5
5
  RSpec::Matchers.define :contain_json do
6
6
  match do
7
- sub_json = expected
8
- filename = actual
7
+ @sub_json = expected
8
+ @filename = actual
9
9
 
10
- filepath = File.join(project_path, filename)
11
- json = JSON.parse(IO.read(filepath), symbolize_names: true)
12
- sub_json <= json
10
+ @filepath = File.join(project_path, @filename)
11
+ @json = JSON.parse(IO.read(@filepath), symbolize_names: true)
12
+
13
+ subhash?(@sub_json, @json)
13
14
  end
14
15
 
15
16
  failure_message do
16
- sub_json = expected
17
- filename = actual
17
+ "in #{@filename}, expected to find\n#{@sub_json.inspect}\n" \
18
+ "in\n#{@json.inspect}"
19
+ end
18
20
 
19
- filepath = File.join(project_path, filename)
20
- json = JSON.parse(IO.read(filepath), symbolize_names: true)
21
+ private
21
22
 
22
- "in #{filename}, expected to find\n#{sub_json.inspect}\nin\n#{json.inspect}"
23
+ def subhash?(inner, outer)
24
+ if inner.is_a?(Hash) && outer.is_a?(Hash)
25
+ inner.all? { |key, value| subhash?(value, outer[key]) }
26
+ else
27
+ inner == outer
28
+ end
23
29
  end
24
30
  end
@@ -10,4 +10,16 @@ module ProjectFiles
10
10
  path = File.join(project_path, filename)
11
11
  FileUtils.rm_rf(path)
12
12
  end
13
+
14
+ def copy_file(from_in_templates, to_in_project)
15
+ destination = File.join(project_path, to_in_project)
16
+ destination_dirname = File.dirname(destination)
17
+
18
+ FileUtils.mkdir_p(destination_dirname)
19
+
20
+ FileUtils.cp(
21
+ File.join(root_path, "templates", from_in_templates),
22
+ destination,
23
+ )
24
+ end
13
25
  end
@@ -0,0 +1 @@
1
+ gem "suspenders", path: File.expand_path("../..", __dir__)
@@ -19,10 +19,7 @@ module SuspendersTestHelpers
19
19
  end
20
20
 
21
21
  Dir.chdir(APP_NAME) do
22
- with_env("HOME", tmp_path) do
23
- debug `git add .`
24
- debug `git commit -m 'Initial commit'`
25
- end
22
+ commit_all
26
23
  end
27
24
  end
28
25
  end
@@ -41,18 +38,11 @@ module SuspendersTestHelpers
41
38
  add_fakes_to_path
42
39
 
43
40
  with_revision_for_honeybadger do
44
- debug `#{system_rails_bin} new #{APP_NAME}`
41
+ debug `#{system_rails_bin} new #{APP_NAME} -m #{rails_template_path}`
45
42
  end
46
43
 
47
44
  Dir.chdir(APP_NAME) do
48
- File.open("Gemfile", "a") do |file|
49
- file.puts %{gem "suspenders", path: #{root_path.inspect}}
50
- end
51
-
52
- with_env("HOME", tmp_path) do
53
- debug `git add .`
54
- debug `git commit -m 'Initial commit'`
55
- end
45
+ commit_all
56
46
  end
57
47
  end
58
48
  end
@@ -135,6 +125,19 @@ module SuspendersTestHelpers
135
125
  File.expand_path('../../../', __FILE__)
136
126
  end
137
127
 
128
+ def rails_template_path
129
+ File.join(root_path, "spec", "support", "rails_template.rb")
130
+ end
131
+
132
+ def commit_all
133
+ with_env("HOME", tmp_path) do
134
+ debug `git config user.email suspenders@example.com`
135
+ debug `git config user.name "Suspenders Boy"`
136
+ debug `git add .`
137
+ debug `git commit -m 'Initial commit'`
138
+ end
139
+ end
140
+
138
141
  def with_env(name, new_value)
139
142
  had_key = ENV.has_key?(name)
140
143
  prior = ENV[name]
@@ -28,9 +28,8 @@ rush to build something amazing; don't use it if you like missing deadlines.
28
28
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
29
29
  s.version = Suspenders::VERSION
30
30
 
31
- s.add_dependency 'bitters', '~> 1.7'
31
+ s.add_dependency 'bitters', '>= 2.0.4'
32
32
  s.add_dependency 'rails', Suspenders::RAILS_VERSION
33
33
 
34
34
  s.add_development_dependency 'rspec', '~> 3.2'
35
- s.add_development_dependency 'rack-timeout'
36
35
  end
@@ -19,22 +19,18 @@ gem "rails", "<%= Suspenders::RAILS_VERSION %>"
19
19
  gem "recipient_interceptor"
20
20
  gem "sassc-rails"
21
21
  gem "skylight"
22
- gem "sprockets", ">= 3.0.0"
22
+ gem "sprockets", "< 4"
23
23
  gem "title"
24
24
  gem "tzinfo-data", platforms: [:mingw, :x64_mingw, :mswin, :jruby]
25
25
  gem "webpacker"
26
26
 
27
27
  group :development do
28
28
  gem "listen"
29
- gem "rack-mini-profiler", require: false
30
- gem "spring"
31
29
  gem "web-console"
32
30
  end
33
31
 
34
32
  group :development, :test do
35
33
  gem "awesome_print"
36
- gem "bundler-audit", ">= 0.5.0", require: false
37
- gem "dotenv-rails"
38
34
  gem "pry-byebug"
39
35
  gem "pry-rails"
40
36
  end
@@ -42,7 +38,6 @@ end
42
38
  group :test do
43
39
  gem "formulaic"
44
40
  gem "launchy"
45
- gem "simplecov", require: false
46
41
  gem "timecop"
47
42
  gem "webmock"
48
43
  end
@@ -3,6 +3,5 @@
3
3
  @import "normalize.css/normalize";
4
4
 
5
5
  @import "bourbon";
6
- @import "neat";
7
6
 
8
7
  @import "base/base";
@@ -0,0 +1,5 @@
1
+ set -e
2
+
3
+ if [ -n "$AUTO_MIGRATE_DB" ]; then
4
+ bundle exec rake db:migrate
5
+ fi
@@ -8,5 +8,3 @@ branch="$(git symbolic-ref HEAD --short)"
8
8
  target="${1:-staging}"
9
9
 
10
10
  git push "$target" "$branch:master"
11
- heroku run rails db:migrate --exit-code --remote "$target"
12
- heroku restart --remote "$target"
@@ -24,5 +24,5 @@ if [ ! -d .git/safe ] && echo $PATH | grep .git/safe > /dev/null; then
24
24
  fi
25
25
 
26
26
  # Only if this isn't CI
27
- # if [ -z "$CI" ]; then
28
- # fi
27
+ if [ -z "$CI" ]; then
28
+ fi
@@ -17,6 +17,5 @@ heroku pg:backups:capture --app $PARENT_APP_NAME
17
17
  URL=`heroku pg:backups public-url --app $PARENT_APP_NAME`
18
18
 
19
19
  heroku pg:backups restore $URL DATABASE_URL --confirm $APP_NAME --app $APP_NAME
20
- heroku run rails db:migrate --exit-code --app $APP_NAME
21
20
  heroku ps:scale worker=1 --app $APP_NAME
22
21
  heroku restart --app $APP_NAME
@@ -15,3 +15,13 @@ Capybara.register_driver :headless_chrome do |app|
15
15
  end
16
16
 
17
17
  Capybara.javascript_driver = :headless_chrome
18
+
19
+ RSpec.configure do |config|
20
+ config.before(:each, type: :system) do
21
+ driven_by :rack_test
22
+ end
23
+
24
+ config.before(:each, type: :system, js: true) do
25
+ driven_by Capybara.javascript_driver
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ Show security advisories during development.
2
+
3
+ Uses the `bundler-audit` gem and rake task to update the local security
4
+ database and show any relevant issues with the app's dependencies. This happens
5
+ on every test run and interaction with `bin/rake` and `bin/rails`.
@@ -0,0 +1,4 @@
1
+ Work with a user analytics service.
2
+
3
+ Add the JavaScript for connecting to Segment to a partial and, if possible,
4
+ include that partial on every page.
@@ -0,0 +1,4 @@
1
+ Prepare the app for running tests on a continuous integration service.
2
+
3
+ Sets up a CircleCI configuration using `bin/setup` to initialize the app and
4
+ `rake` to run the tests. Use SimpleCov for code coverage metrics on CI.
@@ -0,0 +1,4 @@
1
+ Compress HTTP requests at the Rails layer.
2
+
3
+ This uses `Rack::Deflater`, and is done at the app layer instead of at the Web
4
+ server layer.
@@ -0,0 +1,2 @@
1
+ Get database performance insights as you develop. This installs and configures
2
+ Bullet.
@@ -0,0 +1,5 @@
1
+ Script and document the deployment.
2
+
3
+ This adds a `bin/deploy` script, to be called like `deploy staging` or
4
+ `deploy production`, along with updates to the README.md describing how to use
5
+ it.
@@ -0,0 +1,9 @@
1
+ Configure the app for email.
2
+
3
+ Set the app up with SMTP in production. This expects the following environment
4
+ variables to be set:
5
+
6
+ - `SMTP_ADDRESS`
7
+ - `SMTP_DOMAIN`
8
+ - `SMTP_PASSWORD`
9
+ - `SMTP_USERNAME`
@@ -0,0 +1,12 @@
1
+ Build test data with clarity and ease.
2
+
3
+ This uses FactoryBot to help you define dummy and test data for your test
4
+ suite. The `create`, `build`, and `build_stubbed` class methods are directly
5
+ available to all tests.
6
+
7
+ We recommend putting FactoryBot definitions in one `spec/factories.rb` file, at
8
+ least until it grows unwieldy. This helps reduce confusion around circular
9
+ dependencies and makes it easy to jump between definitions.
10
+
11
+ Outside of the tests, the `dev:prime` rake task can be used to insert initial
12
+ development data into the database. You can use FactoryBot here, too.
@@ -0,0 +1 @@
1
+ Redirect users to the HTTPS URL on the production Web site.
@@ -0,0 +1 @@
1
+ Make forms easier to make with form helpers. This mostly involves SimpleForm.
@@ -0,0 +1,2 @@
1
+ Render SVG images inline, as a potential performance improvement for the
2
+ viewer.
@@ -0,0 +1,3 @@
1
+ Set up our favorite job runner.
2
+
3
+ Currently we like DelayedJob. Run all tests inline by default.
@@ -0,0 +1,4 @@
1
+ Set up the test suite for running JavaScript tests.
2
+
3
+ This uses the latest and greatest JavaScript test runner -- currently,
4
+ chromedriver. It comes with a headless mode and a headful/debugging mode.
@@ -0,0 +1 @@
1
+ Use the fastest JSON parser available.
@@ -0,0 +1,3 @@
1
+ Prepare the app for style linting.
2
+
3
+ This sets up your app to use Hound with a default Hound configuration.
@@ -0,0 +1,2 @@
1
+ Write the AppManifest (`app.json`) document with all the environment variables
2
+ we require.
@@ -0,0 +1,3 @@
1
+ Speed up your Rails development flow with a preloader.
2
+
3
+ This installs Spring, including the Spring binstubs.
@@ -0,0 +1,7 @@
1
+ Show runtime profiling for your Rails app as you develop it.
2
+
3
+ This uses the `rack-mini-profiler` gem to show a speed badge on every page.
4
+ This is controlled by the `RACK_MINI_PROFILER` environment variable which
5
+ defaults to `0` in the `.sample.env`. Set it to `1` to enable profiling.
6
+
7
+ Updates your README.md to explain this environment variable.
@@ -0,0 +1,4 @@
1
+ Create a staging environment for each pull request.
2
+
3
+ This glues Heroku review apps to GitHub pull requests, populating the database
4
+ from the generic staging environment on a low-scale review app.
@@ -0,0 +1,10 @@
1
+ Set up the app to run locally with ease.
2
+
3
+ Use Puma and the Rails jobs runner to run the app. This can be done via either
4
+ `heroku local` or any Foreman-compatible project runner (e.g. `foreman start`).
5
+
6
+ Configure your app using `.env`. Installs a basic `.sample.env` that is meant
7
+ to be checked into git and used as a template for your `.env`. The `bin/setup`
8
+ script is modified to safely copy `.sample.env` to `.env` for you.
9
+
10
+ Document all of this in the README.