suspenders 1.55.1 → 20230113.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CONTRIBUTING.md +2 -2
  4. data/NEWS.md +41 -1
  5. data/README.md +13 -16
  6. data/bin/suspenders +29 -2
  7. data/docs/rails_7.md +5 -0
  8. data/lib/suspenders/actions/strip_comments_action.rb +254 -0
  9. data/lib/suspenders/actions.rb +22 -1
  10. data/lib/suspenders/app_builder.rb +34 -19
  11. data/lib/suspenders/exit_on_failure.rb +19 -0
  12. data/lib/suspenders/generators/accessibility_generator.rb +12 -0
  13. data/lib/suspenders/generators/advisories_generator.rb +1 -1
  14. data/lib/suspenders/generators/app_generator.rb +27 -21
  15. data/lib/suspenders/generators/base.rb +1 -0
  16. data/lib/suspenders/generators/jobs_generator.rb +7 -8
  17. data/lib/suspenders/generators/json_generator.rb +4 -0
  18. data/lib/suspenders/generators/production/email_generator.rb +2 -2
  19. data/lib/suspenders/generators/production/manifest_generator.rb +1 -1
  20. data/lib/suspenders/generators/stylesheet_base_generator.rb +5 -20
  21. data/lib/suspenders/generators/testing_generator.rb +1 -2
  22. data/lib/suspenders/generators/views_generator.rb +0 -5
  23. data/lib/suspenders/version.rb +2 -2
  24. data/lib/suspenders.rb +3 -1
  25. data/templates/Gemfile.erb +8 -4
  26. data/templates/Procfile +1 -1
  27. data/templates/active_job.rb +1 -0
  28. data/templates/application.postcss.css +1 -0
  29. data/templates/bin_yarn +18 -0
  30. data/templates/descriptions/jobs.md +1 -1
  31. data/templates/descriptions/runner.md +1 -1
  32. data/templates/descriptions/stylesheet_base.md +1 -4
  33. data/templates/descriptions/testing.md +1 -1
  34. data/templates/errors.rb +1 -0
  35. data/templates/oj.rb +3 -0
  36. data/templates/partials/email_smtp.rb +2 -2
  37. data/templates/partials/pull_requests_config.rb +5 -4
  38. data/templates/postcss.config.js +8 -0
  39. data/templates/postgresql_database.yml.erb +1 -0
  40. data/templates/sample_env +1 -0
  41. data/templates/suspenders_layout.html.erb.erb +6 -4
  42. metadata +47 -100
  43. data/.gitignore +0 -5
  44. data/.standard.yml +0 -2
  45. data/.travis.yml +0 -15
  46. data/Gemfile +0 -3
  47. data/Rakefile +0 -9
  48. data/USAGE +0 -13
  49. data/bin/rake +0 -16
  50. data/bin/rspec +0 -16
  51. data/bin/setup +0 -13
  52. data/lib/suspenders/generators/preloader_generator.rb +0 -122
  53. data/spec/adapters/heroku_spec.rb +0 -98
  54. data/spec/expand_json_spec.rb +0 -89
  55. data/spec/fakes/bin/heroku +0 -5
  56. data/spec/fakes/bin/hub +0 -5
  57. data/spec/features/advisories_spec.rb +0 -24
  58. data/spec/features/api_spec.rb +0 -18
  59. data/spec/features/ci_spec.rb +0 -31
  60. data/spec/features/cli_help_spec.rb +0 -36
  61. data/spec/features/db_optimizations_spec.rb +0 -19
  62. data/spec/features/github_spec.rb +0 -16
  63. data/spec/features/heroku_spec.rb +0 -64
  64. data/spec/features/inline_svg_spec.rb +0 -10
  65. data/spec/features/json_spec.rb +0 -15
  66. data/spec/features/lint_spec.rb +0 -26
  67. data/spec/features/new_project_spec.rb +0 -321
  68. data/spec/features/preloader_spec.rb +0 -25
  69. data/spec/features/production/compression_spec.rb +0 -23
  70. data/spec/features/production/deployment_spec.rb +0 -22
  71. data/spec/features/production/email_spec.rb +0 -47
  72. data/spec/features/production/manifest_spec.rb +0 -37
  73. data/spec/features/production/single_redirect_spec.rb +0 -25
  74. data/spec/features/profiler_spec.rb +0 -20
  75. data/spec/features/runner_spec.rb +0 -30
  76. data/spec/features/staging/pull_requests_spec.rb +0 -22
  77. data/spec/features/static_spec.rb +0 -17
  78. data/spec/features/stylelint_spec.rb +0 -60
  79. data/spec/spec_helper.rb +0 -21
  80. data/spec/support/be_executable_matcher.rb +0 -7
  81. data/spec/support/contain_json_matcher.rb +0 -30
  82. data/spec/support/exist_as_a_file_matcher.rb +0 -7
  83. data/spec/support/fake_github.rb +0 -21
  84. data/spec/support/fake_heroku.rb +0 -53
  85. data/spec/support/generators.rb +0 -5
  86. data/spec/support/match_contents_matcher.rb +0 -6
  87. data/spec/support/project_files.rb +0 -25
  88. data/spec/support/rails_template.rb +0 -1
  89. data/spec/support/suspenders.rb +0 -184
  90. data/suspenders.gemspec +0 -35
  91. data/templates/_javascript.html.erb +0 -3
  92. data/templates/application.scss +0 -7
  93. data/templates/descriptions/preloader.md +0 -3
  94. data/templates/spring.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61b9cb4553f3ffae97642ec3aa15c218be90a047c89c3ba0373fad360dd942fc
4
- data.tar.gz: 962cf7626ca047b3a8e414ead6043476778717c5647dd0928efc5fd3b0b65ec6
3
+ metadata.gz: 606737b8d87f3fe7ccfae7c15d214df27de810600e1cb77873025d1af0f17f3d
4
+ data.tar.gz: 10ab96b1b4eb893b86cb9ddd40146fdb49c99d51fe03aecf579148ca7a6cbf46
5
5
  SHA512:
6
- metadata.gz: 8905a5aeafa593a8d81430529f5d7bbbe1a532d87da0c7039834aa84122113f817f1ff180c055929146a40c79b772254a5faf4625d4ec49c30bc48983d4c61a2
7
- data.tar.gz: 39a8250d0f32c5d09990f786625e3864400e8a41ad27fffe44d69bf2d6365c8fe765db2b99eb40d17ecf9ef86daca7a02d851b0ed9f69f6aa927558787ff3894
6
+ metadata.gz: 8d8f0be9c9c48c24cbd459575552dba6f3a93ad669472235e3e2a00bbe26c7cd114a798a527b8b0faeb226aaeb91bad8a98930502e82cf4e2995c90f40070d02
7
+ data.tar.gz: '08f52691df274a0187571d59020ca241a564ce442ac5b60427eb7a93087563fc3bb73b1719042ec957d38ed84071d0c06260a2cd18c2ca0124a7cbfd57e05ad1'
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.6.6
1
+ 3.0.5
data/CONTRIBUTING.md CHANGED
@@ -20,7 +20,7 @@ Make sure the tests pass:
20
20
  Make your change. Write tests. Follow our [style guide][style]. Make the tests
21
21
  pass:
22
22
 
23
- [style]: https://github.com/thoughtbot/guides/tree/master/style
23
+ [style]: https://github.com/thoughtbot/guides/blob/main/ruby/README.md
24
24
 
25
25
  rake
26
26
 
@@ -48,7 +48,7 @@ All new contributions must be within the generator framework, as described in
48
48
 
49
49
  ## Versions
50
50
 
51
- To update the Ruby version, change `.ruby-version` and `.travis.yml`.
51
+ To update the Ruby version, change `.ruby-version`.
52
52
 
53
53
  If you see this error while running tests:
54
54
 
data/NEWS.md CHANGED
@@ -1,4 +1,44 @@
1
- Unreleased
1
+ 20230113.0 (January, 13, 2023)
2
+
3
+ Support Rails 7 and Ruby 3. Introduce CalVer.
4
+
5
+ * Upgraded: Ruby to 3.0.5
6
+ * Upgraded: Supported Rails version to 7.0.0
7
+ * Removed: Bourbon
8
+ * Removed: Bitters
9
+ * Removed: Autoprefixer Rails
10
+ * Added: cssbundling-rails
11
+ * Added: PostCSS Autoprefixer
12
+ * Added: PostCSS Normalize
13
+
14
+ 1.56.1 (July 17, 2022)
15
+
16
+ Fixes a critical error with the previous release
17
+
18
+ * Run database migrations as the last step of bin/suspenders
19
+ * Fix bundler error on bin/suspenders script
20
+
21
+ 1.56.0 (July 4, 2022)
22
+
23
+ Maintenance release
24
+
25
+ * Fixed: Make Suspenders fail if running with an unsupported Rails version
26
+ * Added: Update default configuration to use request specs
27
+ * Added: Add missing Errno::ECONNREFUSED to HTTP_ERRORS
28
+ * Fixed: Drop use of git in gemspec
29
+ * Fixed: Enforce bundler >= 2.1.0
30
+ * Fixed: Make suspenders abort when something goes wrong
31
+ * Fixed: Reliability and aesthetics of the config files comment stripper
32
+ * Fixed: ActionMailer asset host in the production configuration
33
+ * Added: Configure the oj gem (fast JSON parsing) when generating a new application
34
+ * Fixed: Improve error message of the match_contents matcher
35
+ * Fixed: Convert generator tests to unit tests thus speeding up the test suite
36
+ * Removed: Preloader generator / spring
37
+ * Added: Pull in DATABASE_URL env var explicitly in database.yml
38
+ * Removed: Travis CI configuration
39
+ * Upgraded: Ruby to version 2.7.4
40
+ * Added: A GitHub Action for CI
41
+ * Fixed: Run bin/suspenders in both CLI and tests against a fixed Rails version
2
42
 
3
43
  1.55.1 (September 11, 2020)
4
44
 
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
- # Suspenders [![Build Status](https://secure.travis-ci.org/thoughtbot/suspenders.svg?branch=master)](http://travis-ci.org/thoughtbot/suspenders) [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
1
+ # Suspenders
2
+
3
+ [![Build Status](https://github.com/thoughtbot/suspenders/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/thoughtbot/suspenders/actions)
4
+ [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
2
5
 
3
6
  Suspenders is the base Rails application used at
4
7
  [thoughtbot](https://thoughtbot.com/).
@@ -31,14 +34,10 @@ generated projectname/Gemfile.
31
34
 
32
35
  It includes application gems like:
33
36
 
34
- * [Autoprefixer Rails](https://github.com/ai/autoprefixer-rails) for CSS vendor prefixes
35
- * [Bourbon](https://github.com/thoughtbot/bourbon) for Sass mixins
36
- * [Bitters](https://github.com/thoughtbot/bitters) for scaffold application styles
37
- * [Delayed Job](https://github.com/collectiveidea/delayed_job) for background
37
+ * [Sidekiq](https://github.com/mperham/sidekiq) for background
38
38
  processing
39
39
  * [High Voltage](https://github.com/thoughtbot/high_voltage) for static pages
40
40
  * [Honeybadger](https://www.honeybadger.io/?affiliate=A43uwl) for exception notification
41
- * [Normalize](https://necolas.github.io/normalize.css/) for resetting browser styles
42
41
  * [Oj](http://www.ohler.com/oj/)
43
42
  * [Postgres](https://github.com/ged/ruby-pg) for access to the Postgres database
44
43
  * [Rack Canonical Host](https://github.com/tylerhunt/rack-canonical-host) to
@@ -64,8 +63,6 @@ And development gems like:
64
63
  unused eager loading
65
64
  * [Bundler Audit](https://github.com/rubysec/bundler-audit) for scanning the
66
65
  Gemfile for insecure dependencies based on published CVEs
67
- * [Spring](https://github.com/rails/spring) for fast Rails actions via
68
- pre-loading
69
66
  * [Web Console](https://github.com/rails/web-console) for better debugging via
70
67
  in-browser IRB consoles.
71
68
 
@@ -74,6 +71,8 @@ And testing gems like:
74
71
  * [Capybara](https://github.com/jnicklas/capybara) and
75
72
  [Google Chromedriver]
76
73
  integration testing
74
+ * [capybara_accessibility_audit](https://github.com/thoughtbot/capybara_accessibility_audit) and
75
+ [capybara_accessible_selectors](https://github.com/citizensadvice/capybara_accessible_selectors)
77
76
  * [Factory Bot](https://github.com/thoughtbot/factory_bot) for test data
78
77
  * [Formulaic](https://github.com/thoughtbot/formulaic) for integration testing
79
78
  HTML forms
@@ -102,6 +101,8 @@ Suspenders also comes with:
102
101
  * Configuration for [stylelint][stylelint]
103
102
  * The analytics adapter [Segment][segment] (and therefore config for Google
104
103
  Analytics, Intercom, Facebook Ads, Twitter Ads, etc.)
104
+ * [PostCSS Autoprefixer][autoprefixer] for CSS vendor prefixes
105
+ * [PostCSS Normalize][normalize] for resetting browser styles
105
106
 
106
107
  [setup]: https://robots.thoughtbot.com/bin-setup
107
108
  [compress]: https://robots.thoughtbot.com/content-compression-with-rack-deflater
@@ -112,6 +113,8 @@ Suspenders also comes with:
112
113
  [hound]: https://houndci.com
113
114
  [stylelint]: https://stylelint.io/
114
115
  [segment]: https://segment.com
116
+ [autoprefixer]: https://github.com/postcss/autoprefixer
117
+ [normalize]: https://github.com/csstools/postcss-normalize
115
118
 
116
119
  ## Heroku
117
120
 
@@ -162,14 +165,6 @@ This has the same effect as running:
162
165
 
163
166
  hub create organization/project
164
167
 
165
- ## Spring
166
-
167
- Suspenders uses [spring](https://github.com/rails/spring) by default.
168
- It makes Rails applications load faster, but it might introduce confusing issues
169
- around stale code not being refreshed.
170
- If you think your application is running old code, run `spring stop`.
171
- And if you'd rather not use spring, add `DISABLE_SPRING=1` to your login file.
172
-
173
168
  ## Dependencies
174
169
 
175
170
  Suspenders requires the latest version of Ruby.
@@ -190,6 +185,8 @@ requires Google Chrome or Chromium.
190
185
 
191
186
  PostgreSQL needs to be installed and running for the `db:create` rake task.
192
187
 
188
+ Redis needs to be installed and running for Sidekiq
189
+
193
190
  ## Issues
194
191
 
195
192
  If you have problems, please create a
data/bin/suspenders CHANGED
@@ -4,14 +4,41 @@ require 'pathname'
4
4
  source_path = (Pathname.new(__FILE__).dirname + '../lib').expand_path
5
5
  $LOAD_PATH << source_path
6
6
 
7
- require 'suspenders'
7
+ activate_rails_version = ->(rails_version) do
8
+ rails_bin_path = Gem.activate_bin_path("railties", "rails", rails_version)
9
+ rails_path = File.expand_path("../..", rails_bin_path)
10
+ $LOAD_PATH.unshift(rails_path)
11
+ end
12
+
13
+ if str = ARGV.first
14
+ str = str.b[/\A_(.*)_\z/, 1]
15
+
16
+ if str && Gem::Version.correct?(str)
17
+ rails_version = str
18
+ ARGV.shift
19
+
20
+ begin
21
+ activate_rails_version.call(rails_version)
22
+ rescue Gem::GemNotFoundException
23
+ abort "Suspenders error: Unable to find Rails version #{rails_version}"
24
+ end
25
+ else
26
+ require "suspenders/version"
27
+
28
+ spec = Gem::Specification.find_by_name("rails", Suspenders::RAILS_VERSION)
29
+
30
+ activate_rails_version.call(spec.version.to_s)
31
+ end
32
+ end
33
+
34
+ require "suspenders"
8
35
 
9
36
  if ARGV.empty?
10
37
  puts "Please provide a path for the new application"
11
38
  puts
12
39
  puts "See --help for more info"
13
40
  exit 0
14
- elsif ['-v', '--version'].include? ARGV[0]
41
+ elsif ["-v", "--version"].include? ARGV[0]
15
42
  puts Suspenders::VERSION
16
43
  exit 0
17
44
  end
data/docs/rails_7.md ADDED
@@ -0,0 +1,5 @@
1
+ # Rails 7
2
+
3
+ To create a new Rails 7 app with Suspenders, you need to have `node >= 14.6`.
4
+
5
+ After the app is created, run the server with `bin/dev`.
@@ -0,0 +1,254 @@
1
+ require "parser/current"
2
+
3
+ module Suspenders
4
+ module Actions
5
+ class StripCommentsAction
6
+ class << self
7
+ def call(source)
8
+ parser = Parser::CurrentRuby.new
9
+
10
+ source
11
+ .then { |s| strip_comments(s, parser) }
12
+ .then { |s| strip_trailing_whitespace(s) }
13
+ .then { |s| strip_dup_newlines(s) }
14
+ .then { |s| strip_leading_scope_newlines(s, parser) }
15
+ end
16
+
17
+ private
18
+
19
+ def strip_comments(source, parser)
20
+ StripComments.call(source, parser.reset)
21
+ end
22
+
23
+ def strip_trailing_whitespace(source)
24
+ source.gsub(/[[:blank:]]+$/, "")
25
+ end
26
+
27
+ def strip_dup_newlines(source)
28
+ source.gsub(/\n{2,}/, "\n\n")
29
+ end
30
+
31
+ def strip_leading_scope_newlines(source, parser)
32
+ StripLeadingScopeNewlines.call(source, parser.reset)
33
+ end
34
+ end
35
+
36
+ # Strips full-line and inline comments from a buffer but does
37
+ # not remove whitespaces or newlines after the fact. Example
38
+ # input:
39
+ #
40
+ # MyGem.application.configure do |config|
41
+ # # Full-line comment
42
+ # config.option1 = :value # Inline comment
43
+ # end
44
+ #
45
+ # The output is:
46
+ #
47
+ # MyGem.application.configure do |config|
48
+ #
49
+ # config.option1 = :value
50
+ # end
51
+ class StripComments
52
+ class << self
53
+ def call(source, parser)
54
+ buffer = Parser::Source::Buffer.new(nil, source: source)
55
+ rewriter = Parser::Source::TreeRewriter.new(buffer)
56
+
57
+ _, comments = parser.parse_with_comments(buffer)
58
+
59
+ comments.each do |comment|
60
+ strip_comment(comment, buffer, rewriter)
61
+ end
62
+
63
+ rewriter.process
64
+ end
65
+
66
+ private
67
+
68
+ def strip_comment(comment, buffer, rewriter)
69
+ expr = comment.location.expression
70
+
71
+ if full_line_comment?(expr)
72
+ expr = full_line_comment_expr(expr, buffer)
73
+ end
74
+
75
+ rewriter.remove(expr)
76
+ end
77
+
78
+ def full_line_comment_expr(expr, buffer)
79
+ pos = BackwardStringScanner.beginning_of_line_pos(expr, expr.begin_pos)
80
+
81
+ Parser::Source::Range.new(buffer, pos, expr.end_pos + 1)
82
+ end
83
+
84
+ def full_line_comment?(expr)
85
+ expr.source == expr.source_line.lstrip
86
+ end
87
+ end
88
+ end
89
+
90
+ # A tiny, non-stateful backward string scanner somewhat inspired
91
+ # by Ruby's StringScanner. Ruby's StringScanner is unable to
92
+ # seek backward on a string.
93
+ class BackwardStringScanner
94
+ def self.beginning_of_line_pos(expr, initial_pos)
95
+ skip_before(expr, initial_pos) { |char| char == "\n" }
96
+ end
97
+
98
+ def self.skip_before(expr, initial_pos, &block)
99
+ skip_until(expr, initial_pos, -1, &block)
100
+ end
101
+
102
+ def self.skip_until(expr, initial_pos, lookup_inc = 0)
103
+ pos = initial_pos
104
+
105
+ loop do
106
+ break if pos.zero?
107
+ char = expr.source_buffer.source[pos + lookup_inc]
108
+ break if yield(char)
109
+ pos -= 1
110
+ end
111
+
112
+ pos
113
+ end
114
+ end
115
+
116
+ # The intent of this class is purely aesthetic: remove leading
117
+ # newlines inside of code scopes like blocks and begin/end.
118
+ # Example input:
119
+ #
120
+ # module MyGem
121
+ #
122
+ # MyGem.application.configure do |config|
123
+ #
124
+ # config.option1 = true
125
+ #
126
+ # config.option2 = false
127
+ # end
128
+ # end
129
+ #
130
+ # The output is:
131
+ #
132
+ # module MyGem
133
+ # MyGem.application.configure do |config|
134
+ # config.option1 = true
135
+ #
136
+ # config.option2 = false
137
+ # end
138
+ # end
139
+ class StripLeadingScopeNewlines
140
+ def self.call(source, parser)
141
+ buffer = Parser::Source::Buffer.new(nil, source: source)
142
+ ast = parser.parse(buffer)
143
+
144
+ LeadingNewlineStripRewriter.new.rewrite(buffer, ast).lstrip
145
+ end
146
+
147
+ class LeadingNewlineStripRewriter < Parser::TreeRewriter
148
+ def on_module(node)
149
+ strip_newline_before(node.children[1])
150
+ strip_newline_after(node.children.last)
151
+
152
+ super
153
+ end
154
+
155
+ def on_class(node)
156
+ strip_newline_before(node.children[2])
157
+ strip_newline_after(node.children.last)
158
+
159
+ super
160
+ end
161
+
162
+ def on_begin(node)
163
+ handle_begin(node)
164
+
165
+ super
166
+ end
167
+
168
+ def on_kwbegin(node)
169
+ strip_newline_before(node.children[0])
170
+ strip_newline_after(node.children.last)
171
+
172
+ handle_begin(node)
173
+
174
+ super
175
+ end
176
+
177
+ def on_block(node)
178
+ strip_newline_before(node.children[2])
179
+ strip_newline_after(node.children.last)
180
+
181
+ super
182
+ end
183
+
184
+ private
185
+
186
+ def handle_begin(node)
187
+ strip_blank_lines_between_setter_calls(node.children)
188
+
189
+ node.children.each do |child_node|
190
+ send("on_#{child_node.type}", child_node)
191
+ end
192
+ end
193
+
194
+ def strip_blank_lines_between_setter_calls(children)
195
+ pairs = children.each_cons(2).to_a
196
+
197
+ pairs.each do |(node_before, node_after)|
198
+ if setter_call?(node_before) && setter_call?(node_after)
199
+ strip_newline_before(node_after)
200
+ end
201
+ end
202
+ end
203
+
204
+ def setter_call?(node)
205
+ node.children[1].to_s.end_with?("=")
206
+ end
207
+
208
+ def strip_newline_before(node)
209
+ return if node.nil?
210
+
211
+ expr = node.location.expression
212
+ end_pos = find_end_pos(expr, expr.begin_pos)
213
+ begin_pos = find_begin_pos(expr, end_pos)
214
+
215
+ strip_source_range(expr, begin_pos, end_pos)
216
+ end
217
+
218
+ def strip_newline_after(node)
219
+ return if node.nil?
220
+
221
+ expr = node.location.expression
222
+ source = expr.source_buffer.source
223
+
224
+ if source[expr.end_pos + 1] == "\n"
225
+ strip_source_range(expr, expr.end_pos, expr.end_pos + 1)
226
+ end
227
+ end
228
+
229
+ def find_end_pos(expr, begin_pos)
230
+ BackwardStringScanner.skip_until(expr, begin_pos) do |char|
231
+ char == "\n"
232
+ end
233
+ end
234
+
235
+ def find_begin_pos(expr, end_pos)
236
+ BackwardStringScanner.skip_before(expr, end_pos) do |char|
237
+ char != "\n" && char != " "
238
+ end
239
+ end
240
+
241
+ def strip_source_range(expr, begin_pos, end_pos)
242
+ remove(
243
+ Parser::Source::Range.new(
244
+ expr.source_buffer,
245
+ begin_pos,
246
+ end_pos
247
+ )
248
+ )
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
@@ -6,7 +6,7 @@ module Suspenders
6
6
  unless contents.gsub!(find, replace)
7
7
  raise "#{find.inspect} not found in #{relative_path}"
8
8
  end
9
- File.open(path, "w") { |file| file.write(contents) }
9
+ File.write(path, contents)
10
10
  end
11
11
 
12
12
  def action_mailer_host(rails_env, host)
@@ -31,6 +31,27 @@ module Suspenders
31
31
  action ExpandJson.new(destination_root, file, data)
32
32
  end
33
33
 
34
+ def gem(*args)
35
+ options = args.extract_options!
36
+ name, *versions = args
37
+
38
+ parts = [quote(name)]
39
+
40
+ if (versions = versions.any? ? versions : options.delete(:version))
41
+ versions = Array(versions)
42
+ versions.each do |version|
43
+ parts << quote(version)
44
+ end
45
+ end
46
+
47
+ parts << quote(options) unless options.empty?
48
+
49
+ str = []
50
+ str << indentation
51
+ str << "gem #{parts.join(", ")}"
52
+ append_file "Gemfile", %(\n#{str.join}\n), verbose: false
53
+ end
54
+
34
55
  class ExpandJson
35
56
  def initialize(destination_root, file, data)
36
57
  @destination_root = destination_root
@@ -75,13 +75,19 @@ module Suspenders
75
75
  run "chmod a+x bin/setup"
76
76
  end
77
77
 
78
+ def provide_yarn_script
79
+ template "bin_yarn", "bin/yarn", force: true
80
+ run "chmod a+x bin/yarn"
81
+ end
82
+
78
83
  def configure_generators
79
84
  config = <<-RUBY
80
85
 
81
86
  config.generators do |generate|
82
87
  generate.helper false
83
88
  generate.javascripts false
84
- generate.request_specs false
89
+ generate.controller_specs false
90
+ generate.request_specs true
85
91
  generate.routing_specs false
86
92
  generate.stylesheets false
87
93
  generate.test_framework :rspec
@@ -99,13 +105,13 @@ module Suspenders
99
105
 
100
106
  def setup_asset_host
101
107
  replace_in_file "config/environments/production.rb",
102
- "# config.action_controller.asset_host = 'http://assets.example.com'",
103
- 'config.action_controller.asset_host = ENV.fetch("ASSET_HOST", ENV.fetch("APPLICATION_HOST"))'
108
+ %(# config.asset_host = "http://assets.example.com"),
109
+ 'config.asset_host = ENV.fetch("ASSET_HOST", ENV.fetch("APPLICATION_HOST"))'
104
110
 
105
111
  if File.exist?("config/initializers/assets.rb")
106
112
  replace_in_file "config/initializers/assets.rb",
107
- "config.assets.version = '1.0'",
108
- 'config.assets.version = (ENV["ASSETS_VERSION"] || "1.0")'
113
+ %(Rails.application.config.assets.version = "1.0"),
114
+ 'Rails.application.config.assets.version = (ENV["ASSETS_VERSION"] || "1.0")'
109
115
  end
110
116
 
111
117
  config = <<~EOD
@@ -134,10 +140,14 @@ module Suspenders
134
140
  bundle_command "exec rails db:create db:migrate"
135
141
  end
136
142
 
137
- def replace_gemfile(path)
143
+ def run_database_migrations
144
+ bundle_command "exec rails db:migrate"
145
+ end
146
+
147
+ def replace_gemfile
138
148
  template "Gemfile.erb", "Gemfile", force: true do |content|
139
- if path
140
- content.gsub(%r{gem .suspenders.}) { |s| %(#{s}, path: "#{path}") }
149
+ if development_env?
150
+ content.gsub(%r{gem .suspenders.}) { |s| %(#{s}, path: "#{root_path}") }
141
151
  else
142
152
  content
143
153
  end
@@ -168,10 +178,12 @@ module Suspenders
168
178
  action_mailer_host "test", %("www.example.com")
169
179
  action_mailer_asset_host "test", %("http://www.example.com")
170
180
  action_mailer_host "production", %{ENV.fetch("APPLICATION_HOST")}
181
+ # rubocop:disable Lint/InterpolationCheck
171
182
  action_mailer_asset_host(
172
183
  "production",
173
- %{ENV.fetch("ASSET_HOST", ENV.fetch("APPLICATION_HOST"))}
184
+ %q{"https://#{ENV.fetch("ASSET_HOST", ENV.fetch("APPLICATION_HOST"))}"}
174
185
  )
186
+ # rubocop:enable Lint/InterpolationCheck
175
187
  end
176
188
 
177
189
  def create_heroku_apps(flags)
@@ -203,15 +215,10 @@ module Suspenders
203
215
  ]
204
216
 
205
217
  config_files.each do |config_file|
206
- path = File.join(destination_root, "config/#{config_file}")
207
-
208
- accepted_content = File.readlines(path).reject { |line|
209
- line =~ /^.*#.*$/ || line =~ /^$\n/
210
- }
218
+ path = Pathname(destination_root).join("config", config_file)
219
+ source = Actions::StripCommentsAction.call(path.read)
211
220
 
212
- File.open(path, "w") do |file|
213
- accepted_content.each { |line| file.puts line }
214
- end
221
+ path.write(source)
215
222
  end
216
223
  end
217
224
 
@@ -226,7 +233,7 @@ module Suspenders
226
233
  <<~EOS
227
234
  task(:default).clear
228
235
  task default: [:spec]
229
-
236
+
230
237
  if defined? RSpec
231
238
  task(:spec).clear
232
239
  RSpec::Core::RakeTask.new(:spec) do |t|
@@ -239,8 +246,16 @@ module Suspenders
239
246
 
240
247
  private
241
248
 
249
+ def root_path
250
+ @root_path ||= Pathname(__dir__).join("..", "..").expand_path
251
+ end
252
+
253
+ def development_env?
254
+ root_path.join("suspenders.gemspec").exist?
255
+ end
256
+
242
257
  def raise_on_missing_translations_in(environment)
243
- config = "config.action_view.raise_on_missing_translations = true"
258
+ config = "config.i18n.raise_on_missing_translations = true"
244
259
 
245
260
  uncomment_lines("config/environments/#{environment}.rb", config)
246
261
  end
@@ -0,0 +1,19 @@
1
+ require "active_support/concern"
2
+ require "English"
3
+
4
+ module Suspenders
5
+ module ExitOnFailure
6
+ extend ActiveSupport::Concern
7
+
8
+ def bundle_command(*)
9
+ super
10
+ exit(false) if $CHILD_STATUS.exitstatus.nonzero?
11
+ end
12
+
13
+ module ClassMethods
14
+ def exit_on_failure?
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ require_relative "base"
2
+
3
+ module Suspenders
4
+ class AccessibilityGenerator < Generators::Base
5
+ def capybara_gems
6
+ gem "capybara_accessibility_audit", group: [:test]
7
+ gem "capybara_accessible_selectors", group: [:test],
8
+ github: "citizensadvice/capybara_accessible_selectors"
9
+ Bundler.with_unbundled_env { run "bundle install" }
10
+ end
11
+ end
12
+ end
@@ -3,7 +3,7 @@ require_relative "base"
3
3
  module Suspenders
4
4
  class AdvisoriesGenerator < Generators::Base
5
5
  def bundler_audit_gem
6
- gem "bundler-audit", ">= 0.7.0", require: false, group: %i[development test]
6
+ gem "bundler-audit", ">= 0.7.0", require: false, group: [:development, :test]
7
7
  Bundler.with_unbundled_env { run "bundle install" }
8
8
  end
9
9