4
4
require "digest/md5"
5
5
require "rails/version" unless defined? ( Rails ::VERSION )
6
6
require "open-uri"
7
+ require "tsort"
7
8
require "uri"
8
9
require "rails/generators"
9
10
require "active_support/core_ext/array/extract_options"
@@ -33,73 +34,73 @@ def self.add_shared_options_for(name)
33
34
class_option :database , type : :string , aliases : "-d" , default : "sqlite3" ,
34
35
desc : "Preconfigure for selected database (options: #{ DATABASES . join ( '/' ) } )"
35
36
36
- class_option :skip_git , type : :boolean , aliases : "-G" , default : false ,
37
+ class_option :skip_git , type : :boolean , aliases : "-G" , default : nil ,
37
38
desc : "Skip git init, .gitignore and .gitattributes"
38
39
39
- class_option :skip_keeps , type : :boolean , default : false ,
40
+ class_option :skip_keeps , type : :boolean , default : nil ,
40
41
desc : "Skip source control .keep files"
41
42
42
43
class_option :skip_action_mailer , type : :boolean , aliases : "-M" ,
43
- default : false ,
44
+ default : nil ,
44
45
desc : "Skip Action Mailer files"
45
46
46
- class_option :skip_action_mailbox , type : :boolean , default : false ,
47
+ class_option :skip_action_mailbox , type : :boolean , default : nil ,
47
48
desc : "Skip Action Mailbox gem"
48
49
49
- class_option :skip_action_text , type : :boolean , default : false ,
50
+ class_option :skip_action_text , type : :boolean , default : nil ,
50
51
desc : "Skip Action Text gem"
51
52
52
- class_option :skip_active_record , type : :boolean , aliases : "-O" , default : false ,
53
+ class_option :skip_active_record , type : :boolean , aliases : "-O" , default : nil ,
53
54
desc : "Skip Active Record files"
54
55
55
- class_option :skip_active_job , type : :boolean , default : false ,
56
+ class_option :skip_active_job , type : :boolean , default : nil ,
56
57
desc : "Skip Active Job"
57
58
58
- class_option :skip_active_storage , type : :boolean , default : false ,
59
+ class_option :skip_active_storage , type : :boolean , default : nil ,
59
60
desc : "Skip Active Storage files"
60
61
61
- class_option :skip_action_cable , type : :boolean , aliases : "-C" , default : false ,
62
+ class_option :skip_action_cable , type : :boolean , aliases : "-C" , default : nil ,
62
63
desc : "Skip Action Cable files"
63
64
64
- class_option :skip_asset_pipeline , type : :boolean , aliases : "-A" , default : false
65
+ class_option :skip_asset_pipeline , type : :boolean , aliases : "-A" , default : nil
65
66
66
67
class_option :asset_pipeline , type : :string , aliases : "-a" , default : "sprockets" ,
67
68
desc : "Choose your asset pipeline [options: sprockets (default), propshaft]"
68
69
69
- class_option :skip_javascript , type : :boolean , aliases : [ "-J" , "--skip-js" ] , default : name == "plugin" ,
70
+ class_option :skip_javascript , type : :boolean , aliases : [ "-J" , "--skip-js" ] , default : ( true if name == "plugin" ) ,
70
71
desc : "Skip JavaScript files"
71
72
72
- class_option :skip_hotwire , type : :boolean , default : false ,
73
+ class_option :skip_hotwire , type : :boolean , default : nil ,
73
74
desc : "Skip Hotwire integration"
74
75
75
- class_option :skip_jbuilder , type : :boolean , default : false ,
76
+ class_option :skip_jbuilder , type : :boolean , default : nil ,
76
77
desc : "Skip jbuilder gem"
77
78
78
- class_option :skip_test , type : :boolean , aliases : "-T" , default : false ,
79
+ class_option :skip_test , type : :boolean , aliases : "-T" , default : nil ,
79
80
desc : "Skip test files"
80
81
81
- class_option :skip_system_test , type : :boolean , default : false ,
82
+ class_option :skip_system_test , type : :boolean , default : nil ,
82
83
desc : "Skip system test files"
83
84
84
- class_option :skip_bootsnap , type : :boolean , default : false ,
85
+ class_option :skip_bootsnap , type : :boolean , default : nil ,
85
86
desc : "Skip bootsnap gem"
86
87
87
- class_option :skip_dev_gems , type : :boolean , default : false ,
88
+ class_option :skip_dev_gems , type : :boolean , default : nil ,
88
89
desc : "Skip development gems (e.g., web-console)"
89
90
90
- class_option :dev , type : :boolean , default : false ,
91
+ class_option :dev , type : :boolean , default : nil ,
91
92
desc : "Set up the #{ name } with Gemfile pointing to your Rails checkout"
92
93
93
- class_option :edge , type : :boolean , default : false ,
94
+ class_option :edge , type : :boolean , default : nil ,
94
95
desc : "Set up the #{ name } with Gemfile pointing to Rails repository"
95
96
96
- class_option :main , type : :boolean , default : false , aliases : "--master" ,
97
+ class_option :main , type : :boolean , default : nil , aliases : "--master" ,
97
98
desc : "Set up the #{ name } with Gemfile pointing to Rails repository main branch"
98
99
99
100
class_option :rc , type : :string , default : nil ,
100
101
desc : "Path to file containing extra configuration options for rails command"
101
102
102
- class_option :no_rc , type : :boolean , default : false ,
103
+ class_option :no_rc , type : :boolean , default : nil ,
103
104
desc : "Skip loading of extra configuration options from .railsrc file"
104
105
105
106
class_option :help , type : :boolean , aliases : "-h" , group : :rails ,
@@ -140,6 +141,70 @@ def build(meth, *args) # :doc:
140
141
builder . public_send ( meth , *args ) if builder . respond_to? ( meth )
141
142
end
142
143
144
+ def deduce_implied_options ( options , option_reasons , meta_options )
145
+ active = options . transform_values { |value | [ ] if value } . compact
146
+ irrevocable = ( active . keys - meta_options ) . to_set
147
+
148
+ deduction_order = TSort . tsort (
149
+ -> ( &block ) { option_reasons . each_key ( &block ) } ,
150
+ -> ( key , &block ) { option_reasons [ key ] &.each ( &block ) }
151
+ )
152
+
153
+ deduction_order . each do |name |
154
+ reasons = option_reasons [ name ] &.select ( &active ) . presence
155
+ active [ name ] ||= reasons if reasons
156
+ irrevocable << name if reasons &.any? ( irrevocable )
157
+ end
158
+
159
+ revoked = options . select { |name , value | value == false } . keys . to_set - irrevocable
160
+ deduction_order . reverse_each do |name |
161
+ revoked += option_reasons [ name ] . to_a if revoked . include? ( name )
162
+ end
163
+ revoked -= meta_options
164
+
165
+ active . filter_map do |name , reasons |
166
+ reasons -= revoked . to_a
167
+ [ name , reasons ] unless revoked . include? ( name ) || reasons . empty?
168
+ end . to_h
169
+ end
170
+
171
+ OPTION_IMPLICATIONS = { # :nodoc:
172
+ skip_active_job : [ :skip_action_mailer , :skip_active_storage ] ,
173
+ skip_active_record : [ :skip_active_storage ] ,
174
+ skip_active_storage : [ :skip_action_mailbox , :skip_action_text ] ,
175
+ skip_javascript : [ :skip_hotwire ] ,
176
+ }
177
+
178
+ def imply_options ( option_implications = OPTION_IMPLICATIONS , meta_options : [ ] )
179
+ option_reasons = { }
180
+ option_implications . each do |reason , implications |
181
+ implications . each do |implication |
182
+ ( option_reasons [ implication . to_s ] ||= [ ] ) << reason . to_s
183
+ end
184
+ end
185
+
186
+ @implied_options = deduce_implied_options ( options , option_reasons , meta_options . map ( &:to_s ) )
187
+ @implied_options_conflicts = @implied_options . keys . select { |name | options [ name ] == false }
188
+ self . options = options . merge ( @implied_options . transform_values { true } ) . freeze
189
+ end
190
+
191
+ def report_implied_options
192
+ return if @implied_options . blank?
193
+
194
+ say "Based on the specified options, the following options will also be activated:"
195
+ say ""
196
+ @implied_options . each do |name , reasons |
197
+ due_to = reasons . map { |reason | "--#{ reason . dasherize } " } . join ( ", " )
198
+ say " --#{ name . dasherize } [due to #{ due_to } ]"
199
+ if @implied_options_conflicts . include? ( name )
200
+ say " ERROR: Conflicts with --no-#{ name . dasherize } " , :red
201
+ end
202
+ end
203
+ say ""
204
+
205
+ raise "Cannot proceed due to conflicting options" if @implied_options_conflicts . any?
206
+ end
207
+
143
208
def create_root # :doc:
144
209
valid_const?
145
210
@@ -190,18 +255,16 @@ def asset_pipeline_gemfile_entry
190
255
end
191
256
192
257
def include_all_railties? # :doc:
193
- [
194
- options . values_at (
195
- :skip_active_record ,
196
- :skip_test ,
197
- :skip_action_cable ,
198
- :skip_active_job
199
- ) ,
200
- skip_active_storage? ,
201
- skip_action_mailer? ,
202
- skip_action_mailbox? ,
203
- skip_action_text?
204
- ] . flatten . none?
258
+ options . values_at (
259
+ :skip_action_cable ,
260
+ :skip_action_mailbox ,
261
+ :skip_action_mailer ,
262
+ :skip_action_text ,
263
+ :skip_active_job ,
264
+ :skip_active_record ,
265
+ :skip_active_storage ,
266
+ :skip_test ,
267
+ ) . none?
205
268
end
206
269
207
270
def comment_if ( value ) # :doc:
@@ -226,19 +289,19 @@ def sqlite3? # :doc:
226
289
end
227
290
228
291
def skip_active_storage? # :doc:
229
- options [ :skip_active_storage ] || options [ :skip_active_record ] || options [ :skip_active_job ]
292
+ options [ :skip_active_storage ]
230
293
end
231
294
232
295
def skip_action_mailer? # :doc:
233
- options [ :skip_action_mailer ] || options [ :skip_active_job ]
296
+ options [ :skip_action_mailer ]
234
297
end
235
298
236
299
def skip_action_mailbox? # :doc:
237
- options [ :skip_action_mailbox ] || skip_active_storage?
300
+ options [ :skip_action_mailbox ]
238
301
end
239
302
240
303
def skip_action_text? # :doc:
241
- options [ :skip_action_text ] || skip_active_storage?
304
+ options [ :skip_action_text ]
242
305
end
243
306
244
307
def skip_sprockets?
@@ -333,7 +396,7 @@ def javascript_gemfile_entry
333
396
end
334
397
335
398
def hotwire_gemfile_entry
336
- return if options [ :skip_javascript ] || options [ : skip_hotwire]
399
+ return if options [ :skip_hotwire ]
337
400
338
401
turbo_rails_entry =
339
402
GemfileEntry . floats "turbo-rails" , "Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]"
@@ -457,7 +520,7 @@ def run_javascript
457
520
end
458
521
459
522
def run_hotwire
460
- return if options [ :skip_javascript ] || options [ : skip_hotwire] || !bundle_install?
523
+ return if options [ :skip_hotwire ] || !bundle_install?
461
524
462
525
rails_command "turbo:install stimulus:install"
463
526
end
0 commit comments