bullet_train-super_scaffolding 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,543 @@
1
+ require "active_support/inflector"
2
+ require "fileutils"
3
+ require "pry"
4
+ require "colorize"
5
+
6
+ require_relative "#{APP_ROOT}/config/initializers/inflections"
7
+
8
+ # TODO these methods were removed from the global scope in super scaffolding and moved to `Scaffolding::Transformer`,
9
+ # but oauth provider scaffolding hasn't been updated yet.
10
+
11
+ def legacy_replace_in_file(file, before, after)
12
+ puts "Replacing in '#{file}'."
13
+ target_file_content = File.read(file)
14
+ target_file_content.gsub!(before, after)
15
+ File.write(file, target_file_content)
16
+ end
17
+
18
+ def legacy_add_line_to_file(file, content, hook, child, parent, options = {})
19
+ increase_indent = options[:increase_indent]
20
+ add_before = options[:add_before]
21
+ add_after = options[:add_after]
22
+
23
+ transformed_file_name = file
24
+ transformed_content = content
25
+ transform_hook = hook
26
+
27
+ target_file_content = File.read(transformed_file_name)
28
+
29
+ if target_file_content.include?(transformed_content)
30
+ puts "No need to update '#{transformed_file_name}'. It already has '#{transformed_content}'."
31
+ else
32
+ new_target_file_content = []
33
+ target_file_content.split("\n").each do |line|
34
+ if /#{Regexp.escape(transform_hook)}\s*$/.match?(line)
35
+
36
+ if add_before
37
+ new_target_file_content << "#{line} #{add_before}"
38
+ else
39
+ unless options[:prepend]
40
+ new_target_file_content << line
41
+ end
42
+ end
43
+
44
+ # get leading whitespace.
45
+ line =~ /^(\s*).*#{Regexp.escape(transform_hook)}.*/
46
+ leading_whitespace = $1
47
+ new_target_file_content << "#{leading_whitespace}#{" " if increase_indent}#{transformed_content}"
48
+
49
+ new_target_file_content << "#{leading_whitespace}#{add_after}" if add_after
50
+
51
+ if options[:prepend]
52
+ new_target_file_content << line
53
+ end
54
+ else
55
+ new_target_file_content << line
56
+ end
57
+ end
58
+
59
+ puts "Updating '#{transformed_file_name}'."
60
+
61
+ File.write(transformed_file_name, new_target_file_content.join("\n") + "\n")
62
+ end
63
+ end
64
+
65
+ # filter out options.
66
+ argv = []
67
+ @options = {}
68
+ ARGV.each do |arg|
69
+ if arg[0..1] == "--"
70
+ arg = arg[2..-1]
71
+ if arg.split("=").count > 1
72
+ @options[arg.split("=")[0]] = arg.split("=")[1]
73
+ else
74
+ @options[arg] = true
75
+ end
76
+ else
77
+ argv << arg
78
+ end
79
+ end
80
+
81
+ def check_required_options_for_attributes(scaffolding_type, attributes, child, parent = nil)
82
+ attributes.each do |attribute|
83
+ parts = attribute.split(":")
84
+ name = parts.shift
85
+ type = parts.join(":")
86
+
87
+ # extract any options they passed in with the field.
88
+ type, attribute_options = type.scan(/^(.*)\[(.*)\]/).first || type
89
+
90
+ # create a hash of the options.
91
+ attribute_options = if attribute_options
92
+ attribute_options.split(",").map do |s|
93
+ option_name, option_value = s.split("=")
94
+ [option_name.to_sym, option_value || true]
95
+ end.to_h
96
+ else
97
+ {}
98
+ end
99
+
100
+ if name.match?(/_id$/) || name.match?(/_ids$/)
101
+ attribute_options ||= {}
102
+ unless attribute_options[:vanilla]
103
+ name_without_id = if name.match?(/_id$/)
104
+ name.gsub(/_id$/, "")
105
+ elsif name.match?(/_ids$/)
106
+ name.gsub(/_ids$/, "")
107
+ end
108
+
109
+ attribute_options[:class_name] ||= name_without_id.classify
110
+
111
+ file_name = "app/models/#{attribute_options[:class_name].underscore}.rb"
112
+ unless File.exist?(file_name)
113
+ puts ""
114
+ puts "Attributes that end with `_id` or `_ids` trigger awesome, powerful magic in Super Scaffolding. However, because no `#{attribute_options[:class_name]}` class was found defined in `#{file_name}`, you'll need to specify a `class_name` that exists to let us know what model class is on the other side of the association, like so:".red
115
+ puts ""
116
+ puts " bin/super-scaffold #{scaffolding_type} #{child}#{" " + parent if parent.present?} #{name}:#{type}[class_name=#{name.gsub(/_ids?$/, "").classify}]".red
117
+ puts ""
118
+ puts "If `#{name}` is just a regular field and isn't backed by an ActiveRecord association, you can skip all this with the `[vanilla]` option, e.g.:".red
119
+ puts ""
120
+ puts " bin/super-scaffold #{scaffolding_type} #{child}#{" " + parent if parent.present?} #{name}:#{type}[vanilla]".red
121
+ puts ""
122
+ exit
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def show_usage
130
+ puts ""
131
+ puts "🚅 usage: bin/super-scaffold [type] (... | --help)"
132
+ puts ""
133
+ puts "Supported types of scaffolding:"
134
+ puts ""
135
+ puts " crud"
136
+ puts " crud-field"
137
+ puts " join-model"
138
+ puts " oauth-provider"
139
+ puts " breadcrumbs"
140
+ puts ""
141
+ puts "Try \`bin/super-scaffold [type]` for usage examples.".blue
142
+ puts ""
143
+ end
144
+
145
+ # grab the _type_ of scaffold we're doing.
146
+ scaffolding_type = argv.shift
147
+
148
+ # if we're doing the classic super scaffolding ..
149
+ if scaffolding_type == "crud"
150
+
151
+ unless argv.count >= 3
152
+ puts ""
153
+ puts "🚅 usage: bin/super-scaffold crud <Model> <ParentModel[s]> <attribute:type> <attribute:type> ..."
154
+ puts ""
155
+ puts "E.g. a Team has many Sites with some attributes:"
156
+ puts " rails g model Site team:references name:string url:text"
157
+ puts " bin/super-scaffold crud Site Team name:string url:text"
158
+ puts ""
159
+ puts "E.g. a Section belongs to a Page, which belongs to a Site, which belongs to a Team:"
160
+ puts " rails g model Section page:references title:text body:text"
161
+ puts " bin/super-scaffold crud Section Page,Site,Team title:text body:text"
162
+ puts ""
163
+ puts "E.g. an Image belongs to either a Page or a Site:"
164
+ puts " Doable! See https://bit.ly/2NvO8El for a step by step guide."
165
+ puts ""
166
+ puts "E.g. Pages belong to a Site and are sortable via drag-and-drop:"
167
+ puts " rails g model Page site:references name:string path:text"
168
+ puts " bin/super-scaffold crud Page Site,Team name:text path:text --sortable"
169
+ puts ""
170
+ puts "🏆 Protip: Commit your other changes before running Super Scaffolding so it's easy to undo if you (or we) make any mistakes."
171
+ puts "If you do that, you can reset to your last commit state by using `git checkout .` and `git clean -d -f` ."
172
+ puts ""
173
+ puts "Give it a shot! Let us know if you have any trouble with it! ✌️"
174
+ puts ""
175
+ exit
176
+ end
177
+
178
+ child = argv[0]
179
+ parents = argv[1] ? argv[1].split(",") : []
180
+ parents = parents.map(&:classify).uniq
181
+ parent = parents.first
182
+
183
+ unless parents.include?("Team")
184
+ raise "Parents for #{child} should trace back to the Team model, but Team wasn't provided. Please confirm that all of the parents tracing back to the Team model are present and try again.\n" +
185
+ "E.g.:\n" +
186
+ "rails g model Section page:references title:text body:text\n" +
187
+ "bin/super-scaffold crud Section Page,Site,Team title:text body:text\n"
188
+ end
189
+
190
+ # get all the attributes.
191
+ attributes = argv[2..-1]
192
+
193
+ check_required_options_for_attributes(scaffolding_type, attributes, child, parent)
194
+
195
+ transformer = Scaffolding::Transformer.new(child, parents, @options)
196
+ transformer.scaffold_crud(attributes)
197
+
198
+ transformer.additional_steps.each_with_index do |additional_step, index|
199
+ color, message = additional_step
200
+ puts ""
201
+ puts "#{index + 1}. #{message}".send(color)
202
+ end
203
+ puts ""
204
+
205
+ elsif scaffolding_type == "crud-field"
206
+
207
+ unless argv.count >= 2
208
+ puts ""
209
+ puts "🚅 usage: bin/super-scaffold crud-field <Model> <attribute:type> <attribute:type> ... [options]"
210
+ puts ""
211
+ puts "E.g. add a description and body to Pages:"
212
+ puts " rails g migration add_description_etc_to_pages description:text body:text"
213
+ puts " bin/super-scaffold crud-field Page description:text body:text"
214
+ puts ""
215
+ puts "Options:"
216
+ puts ""
217
+ puts " --skip-table: Only add to the new/edit form and show view."
218
+ puts ""
219
+ exit
220
+ end
221
+
222
+ # We pass this value to parents to create a new Scaffolding::Transformer because
223
+ # we don't actually need knowledge of the parent to add the new field.
224
+ parents = [""]
225
+ child = argv[0]
226
+
227
+ # get all the attributes.
228
+ attributes = argv[1..-1]
229
+
230
+ check_required_options_for_attributes(scaffolding_type, attributes, child)
231
+
232
+ transformer = Scaffolding::Transformer.new(child, parents, @options)
233
+ transformer.add_attributes_to_various_views(attributes, type: :crud_field)
234
+
235
+ transformer.additional_steps.uniq.each_with_index do |additional_step, index|
236
+ color, message = additional_step
237
+ puts ""
238
+ puts "#{index + 1}. #{message}".send(color)
239
+ end
240
+ puts ""
241
+
242
+ elsif scaffolding_type == "join-model"
243
+
244
+ unless argv.count >= 3
245
+ puts ""
246
+ puts "🚅 usage: bin/super-scaffold join-model <JoinModel> <left_association> <right_association>"
247
+ puts ""
248
+ puts "E.g. Add project-specific tags to a project:"
249
+ puts ""
250
+ puts " Given the following example models:".blue
251
+ puts ""
252
+ puts " rails g model Project team:references name:string description:text"
253
+ puts " bin/super-scaffold crud Project Team name:text_field description:trix_editor"
254
+ puts ""
255
+ puts " rails g model Projects::Tag team:references name:string"
256
+ puts " bin/super-scaffold crud Projects::Tag Team name:text_field"
257
+ puts ""
258
+ puts " 1️⃣ Use the standard Rails model generator to generate the join model:".blue
259
+ puts ""
260
+ puts " rails g model Projects::AppliedTag project:references tag:references"
261
+ puts ""
262
+ puts " 👋 Don't run migrations yet! Sometimes Super Scaffolding updates them for you.".yellow
263
+ puts ""
264
+ puts " 2️⃣ Use `join-model` scaffolding to prepare the join model for use in `crud-field` scaffolding:".blue
265
+ puts ""
266
+ puts " bin/super-scaffold join-model Projects::AppliedTag project_id[class_name=Project] tag_id[class_name=Projects::Tag]"
267
+ puts ""
268
+ puts " 3️⃣ Now you can use `crud-field` scaffolding to actually add the field to the form of the parent model:".blue
269
+ puts ""
270
+ puts " bin/super-scaffold crud-field Project tag_ids:super_select[class_name=Projects::Tag]"
271
+ puts ""
272
+ puts " 👋 Heads up! There will be one follow-up step output by this command that you need to take action on."
273
+ puts ""
274
+ puts " 4️⃣ Now you can run your migrations.".blue
275
+ exit
276
+ end
277
+
278
+ child = argv[0]
279
+ primary_parent = argv[1].split("class_name=").last.split(",").first.split("]").first
280
+ secondary_parent = argv[2].split("class_name=").last.split(",").first.split("]").first
281
+
282
+ # There should only be two attributes.
283
+ attributes = [argv[1], argv[2]]
284
+
285
+ # Pretend we're doing a `super_select` scaffolding because it will do the correct thing.
286
+ attributes = attributes.map { |attribute| attribute.gsub("\[", ":super_select\[") }
287
+ attributes = attributes.map { |attribute| attribute.gsub("\]", ",required\]") }
288
+
289
+ transformer = Scaffolding::Transformer.new(child, [primary_parent], @options)
290
+
291
+ # We need this transformer to reflect on the class names _just_ between e.g. `Project` and `Projects::Tag`, without the join model.
292
+ has_many_through_transformer = Scaffolding::Transformer.new(secondary_parent, [primary_parent], @options)
293
+
294
+ # We need this transformer to reflect on the association between `Projects::Tag` and `Projects::AppliedTag` backwards.
295
+ inverse_transformer = Scaffolding::Transformer.new(child, [secondary_parent], @options)
296
+
297
+ # We need this transformer to reflect on the class names _just_ between e.g. `Projects::Tag` and `Project`, without the join model.
298
+ inverse_has_many_through_transformer = Scaffolding::Transformer.new(primary_parent, [secondary_parent], @options)
299
+
300
+ # However, for the first attribute, we actually don't need the scope validator (and can't really implement it).
301
+ attributes[0] = attributes[0].gsub("\]", ",unscoped\]")
302
+
303
+ has_many_through_association = has_many_through_transformer.transform_string("completely_concrete_tangible_things")
304
+ source = transformer.transform_string("absolutely_abstract_creative_concept.valid_$HAS_MANY_THROUGH_ASSOCIATION")
305
+ source.gsub!("$HAS_MANY_THROUGH_ASSOCIATION", has_many_through_association)
306
+
307
+ # For the second one, we don't want users to have to define the list of valid options in the join model, so we do this:
308
+ attributes[1] = attributes[1].gsub("\]", ",source=#{source}\]")
309
+
310
+ # This model hasn't been crud scaffolded, so a bunch of views are skipped here, but that's OK!
311
+ # It does what we need on the files that exist.
312
+ transformer.add_scaffolding_hooks_to_model
313
+
314
+ transformer.suppress_could_not_find = true
315
+ transformer.add_attributes_to_various_views(attributes, type: :crud_field)
316
+ transformer.suppress_could_not_find = false
317
+
318
+ # Add the `has_many ... through:` association in both directions.
319
+ transformer.add_has_many_through_associations(has_many_through_transformer)
320
+ inverse_transformer.add_has_many_through_associations(inverse_has_many_through_transformer)
321
+
322
+ additional_steps = (transformer.additional_steps + has_many_through_transformer.additional_steps + inverse_transformer.additional_steps + inverse_has_many_through_transformer.additional_steps).uniq
323
+
324
+ additional_steps.each_with_index do |additional_step, index|
325
+ color, message = additional_step
326
+ puts ""
327
+ puts "#{index + 1}. #{message}".send(color)
328
+ end
329
+ puts ""
330
+
331
+ elsif scaffolding_type == "breadcrumbs"
332
+
333
+ unless argv.count == 2
334
+ puts ""
335
+ puts "🚅 usage: bin/super-scaffold breadcrumbs <Model> <ParentModel[s]>"
336
+ puts ""
337
+ puts "Heads up! You only need to use this if you generated your views before the new Bullet Train breadcrumbs implementation came into effect.".green
338
+ puts ""
339
+ puts "E.g. create updated breadcrumbs for Pages:"
340
+ puts " bin/super-scaffold breadcrumbs Page Site,Team"
341
+ puts ""
342
+ puts "When Super Scaffolding breadcrumbs, you have to specify the entire path of the immediate parent back to Team."
343
+ puts ""
344
+ exit
345
+ end
346
+
347
+ child = argv[0]
348
+ parents = argv[1] ? argv[1].split(",") : []
349
+ parents = parents.map(&:classify).uniq
350
+ parent = parents.first
351
+
352
+ unless parents.include?("Team")
353
+ raise "Parents for #{child} should trace back to the Team model, but Team wasn't provided. Please confirm that all of the parents tracing back to the Team model are present and try again.\n" +
354
+ "E.g.:\n" +
355
+ "bin/super-scaffold breadcrumbs Page Site,Team\n"
356
+ end
357
+
358
+ # get all the attributes.
359
+ transformer = Scaffolding::Transformer.new(child, parents, @options)
360
+ transformer.scaffold_new_breadcrumbs(child, parents)
361
+
362
+ elsif scaffolding_type == "oauth-provider"
363
+
364
+ unless argv.count >= 5
365
+ puts ""
366
+ puts "🚅 usage: bin/super-scaffold oauth-provider <omniauth_gem> <gems_provider_name> <our_provider_name> <PROVIDER_API_KEY_IN_ENV> <PROVIDER_API_SECRET_IN_ENV> [options]"
367
+ puts ""
368
+ puts "E.g. what we'd do to start Stripe off (if we didn't already do it):"
369
+ puts " bin/super-scaffold oauth-provider omniauth-stripe-connect stripe_connect Oauth::StripeAccount STRIPE_CLIENT_ID STRIPE_SECRET_KEY --icon=ti-money"
370
+ puts ""
371
+ puts "E.g. what we actually did to start Shopify off:"
372
+ puts " bin/super-scaffold oauth-provider omniauth-shopify-oauth2 shopify Oauth::ShopifyAccount SHOPIFY_API_KEY SHOPIFY_API_SECRET_KEY --icon=ti-shopping-cart"
373
+ puts ""
374
+ puts "Options:"
375
+ puts ""
376
+ puts " --icon={ti-*}: Specify an icon."
377
+ puts ""
378
+ puts "For a list of readily available provider strategies, see https://github.com/omniauth/omniauth/wiki/List-of-Strategies ."
379
+ puts ""
380
+ exit
381
+ end
382
+
383
+ _, omniauth_gem, gems_provider_name, our_provider_name, api_key, api_secret = *ARGV
384
+
385
+ unless match = our_provider_name.match(/Oauth::(.*)Account/)
386
+ puts "\n🚨 Your provider name must match the pattern of `Oauth::{Name}Account`, e.g. `Oauth::StripeAccount`\n".red
387
+ return
388
+ end
389
+
390
+ options = {
391
+ omniauth_gem: omniauth_gem,
392
+ gems_provider_name: gems_provider_name,
393
+ our_provider_name: match[1],
394
+ api_key: api_key,
395
+ api_secret: api_secret
396
+ }
397
+
398
+ unless File.exist?(oauth_transform_string("./app/models/oauth/stripe_account.rb", options)) &&
399
+ File.exist?(oauth_transform_string("./app/models/integrations/stripe_installation.rb", options)) &&
400
+ File.exist?(oauth_transform_string("./app/models/webhooks/incoming/oauth/stripe_account_webhook.rb", options))
401
+ puts ""
402
+ puts oauth_transform_string("🚨 Before doing the actual Super Scaffolding, you'll need to generate the models like so:", options).red
403
+ puts ""
404
+ puts oauth_transform_string(" rails g model Oauth::StripeAccount uid:string data:jsonb user:references", options).red
405
+ puts oauth_transform_string(" rails g model Integrations::StripeInstallation team:references oauth_stripe_account:references name:string", options).red
406
+ puts oauth_transform_string(" rails g model Webhooks::Incoming::Oauth::StripeAccountWebhook data:jsonb processed_at:datetime verified_at:datetime oauth_stripe_account:references", options).red
407
+ puts ""
408
+ puts "However, don't do the `rake db:migrate` until after you re-run Super Scaffolding, as it will need to update some settings in those migrations.".red
409
+ puts ""
410
+ return
411
+ end
412
+
413
+ icon_name = nil
414
+ if @options["icon"].present?
415
+ icon_name = @options["icon"]
416
+ else
417
+ puts "OK, great! Let's do this! By default providers will appear with a dollar symbol,"
418
+ puts "but after you hit enter I'll open a page where you can view other icon options."
419
+ puts "When you find one you like, hover your mouse over it and then come back here and"
420
+ puts "and enter the name of the icon you want to use."
421
+ response = STDIN.gets.chomp
422
+ `open http://light.pinsupreme.com/icon_fonts_themefy.html`
423
+ puts ""
424
+ puts "Did you find an icon you wanted to use? Enter the name here or hit enter to just"
425
+ puts "use the dollar symbol:"
426
+ icon_name = STDIN.gets.chomp
427
+ puts ""
428
+ unless icon_name.length > 0 || icon_name.downcase == "y"
429
+ icon_name = "icon-puzzle"
430
+ end
431
+ end
432
+
433
+ options[:icon] = icon_name
434
+
435
+ [
436
+
437
+ # User OAuth.
438
+ "./app/models/oauth/stripe_account.rb",
439
+ "./app/models/webhooks/incoming/oauth/stripe_account_webhook.rb",
440
+ "./app/controllers/account/oauth/stripe_accounts_controller.rb",
441
+ "./app/controllers/webhooks/incoming/oauth/stripe_account_webhooks_controller.rb",
442
+ "./app/views/account/oauth/stripe_accounts",
443
+ "./test/models/oauth/stripe_account_test.rb",
444
+ "./test/factories/oauth/stripe_accounts.rb",
445
+ "./config/locales/en/oauth/stripe_accounts.en.yml",
446
+ "./app/views/devise/shared/oauth/_stripe.html.erb",
447
+
448
+ # Team Integration.
449
+ "./app/models/integrations/stripe_installation.rb",
450
+ # './app/serializers/api/v1/integrations/stripe_installation_serializer.rb',
451
+ "./app/controllers/account/integrations/stripe_installations_controller.rb",
452
+ "./app/views/account/integrations/stripe_installations",
453
+ "./test/models/integrations/stripe_installation_test.rb",
454
+ "./test/factories/integrations/stripe_installations.rb",
455
+ "./config/locales/en/integrations/stripe_installations.en.yml",
456
+
457
+ # Webhook.
458
+ "./app/models/webhooks/incoming/oauth/stripe_account_webhook.rb",
459
+ "./app/controllers/webhooks/incoming/oauth/stripe_account_webhooks_controller.rb"
460
+
461
+ ].each do |name|
462
+ if File.directory?(name)
463
+ oauth_scaffold_directory(name, options)
464
+ else
465
+ oauth_scaffold_file(name, options)
466
+ end
467
+ end
468
+
469
+ oauth_scaffold_add_line_to_file("./app/views/devise/shared/_oauth.html.erb", "<%= render 'devise/shared/oauth/stripe', verb: verb if stripe_enabled? %>", "<%# 🚅 super scaffolding will insert new oauth providers above this line. %>", options, prepend: true)
470
+ oauth_scaffold_add_line_to_file("./app/views/account/users/edit.html.erb", "<%= render 'account/oauth/stripe_accounts/index', context: @user, stripe_accounts: @user.oauth_stripe_accounts if stripe_enabled? %>", "<% # 🚅 super scaffolding will insert new oauth providers above this line. %>", options, prepend: true)
471
+ oauth_scaffold_add_line_to_file("./config/initializers/devise.rb", "config.omniauth :stripe_connect, ENV['STRIPE_CLIENT_ID'], ENV['STRIPE_SECRET_KEY'], {\n ## specify options for your oauth provider here, e.g.:\n # scope: 'read_products,read_orders,write_content',\n }\n", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
472
+ oauth_scaffold_add_line_to_file("./app/controllers/account/oauth/omniauth_callbacks_controller.rb", "def stripe_connect\n callback(\"Stripe\", team_id_from_env)\n end\n", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
473
+ oauth_scaffold_add_line_to_file("./app/models/team.rb", "has_many :integrations_stripe_installations, class_name: 'Integrations::StripeInstallation', dependent: :destroy if stripe_enabled?", "# 🚅 add oauth providers above.", options, prepend: true)
474
+ oauth_scaffold_add_line_to_file("./app/models/user.rb", "has_many :oauth_stripe_accounts, class_name: 'Oauth::StripeAccount' if stripe_enabled?", "# 🚅 add oauth providers above.", options, prepend: true)
475
+ oauth_scaffold_add_line_to_file("./config/locales/en/oauth.en.yml", "stripe_connect: Stripe", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
476
+ oauth_scaffold_add_line_to_file("./app/views/account/shared/_menu.html.erb", "<%= render 'account/integrations/stripe_installations/menu_item' if stripe_enabled? %>", "<%# 🚅 super scaffolding will insert new oauth providers above this line. %>", options, prepend: true)
477
+ oauth_scaffold_add_line_to_file("./config/routes.rb", "resources :stripe_account_webhooks if stripe_enabled?", "# 🚅 super scaffolding will insert new oauth provider webhooks above this line.", options, prepend: true)
478
+ oauth_scaffold_add_line_to_file("./config/routes.rb", "resources :stripe_accounts if stripe_enabled?", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
479
+ oauth_scaffold_add_line_to_file("./config/routes.rb", "resources :stripe_installations if stripe_enabled?", "# 🚅 super scaffolding will insert new integration installations above this line.", options, prepend: true)
480
+ oauth_scaffold_add_line_to_file("./Gemfile", "gem 'omniauth-stripe-connect'", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
481
+ oauth_scaffold_add_line_to_file("./lib/bullet_train.rb", "def stripe_enabled?\n ENV['STRIPE_CLIENT_ID'].present? && ENV['STRIPE_SECRET_KEY'].present?\nend\n", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
482
+ oauth_scaffold_add_line_to_file("./lib/bullet_train.rb", "stripe_enabled?,", "# 🚅 super scaffolding will insert new oauth provider checks above this line.", options, prepend: true)
483
+ oauth_scaffold_add_line_to_file("./app/models/ability.rb", "if stripe_enabled?
484
+ can [:read, :create, :destroy], Oauth::StripeAccount, user_id: user.id
485
+ can :manage, Integrations::StripeInstallation, team_id: user.team_ids
486
+ can :destroy, Integrations::StripeInstallation, oauth_stripe_account: {user_id: user.id}
487
+ end
488
+ ", "# 🚅 super scaffolding will insert any new oauth providers above.", options, prepend: true)
489
+
490
+ # find the database migration that defines this relationship.
491
+ migration_file_name = `grep "create_table #{oauth_transform_string(":oauth_stripe_accounts", options)}" db/migrate/*`.split(":").first
492
+ legacy_replace_in_file(migration_file_name, "null: false", "null: true")
493
+
494
+ migration_file_name = `grep "create_table #{oauth_transform_string(":integrations_stripe_installations", options)}" db/migrate/*`.split(":").first
495
+ legacy_replace_in_file(migration_file_name,
496
+ oauth_transform_string("t.references :oauth_stripe_account, null: false, foreign_key: true", options),
497
+ oauth_transform_string('t.references :oauth_stripe_account, null: false, foreign_key: true, index: {name: "index_stripe_installations_on_oauth_stripe_account_id"}', options))
498
+
499
+ migration_file_name = `grep "create_table #{oauth_transform_string(":webhooks_incoming_oauth_stripe_account_webhooks", options)}" db/migrate/*`.split(":").first
500
+ legacy_replace_in_file(migration_file_name, "null: false", "null: true")
501
+ legacy_replace_in_file(migration_file_name, "foreign_key: true", 'foreign_key: true, index: {name: "index_stripe_webhooks_on_oauth_stripe_account_id"}')
502
+
503
+ puts ""
504
+ puts "🎉"
505
+ puts ""
506
+ puts "You'll probably need to `bundle install`.".green
507
+ puts ""
508
+ puts "If the OAuth provider asks you for some whitelisted callback URLs, the URL structure for those is as so:"
509
+ puts ""
510
+ path = "users/auth/stripe_connect/callback"
511
+ puts oauth_transform_string(" https://yourdomain.co/#{path}", options)
512
+ puts oauth_transform_string(" https://yourtunnel.ngrok.io/#{path}", options)
513
+ puts oauth_transform_string(" http://localhost:3000/#{path}", options)
514
+ puts ""
515
+ puts "If you're able to specify an endpoint to receive webhooks from this provider, use this URL:"
516
+ puts ""
517
+ path = "webhooks/incoming/oauth/stripe_account_webhooks"
518
+ puts oauth_transform_string(" https://yourdomain.co/#{path}", options)
519
+ puts oauth_transform_string(" https://yourtunnel.ngrok.io/#{path}", options)
520
+ puts oauth_transform_string(" http://localhost:3000/#{path}", options)
521
+ puts ""
522
+ puts ""
523
+ puts "If you'd like to edit how your Bullet Train application refers to this provider, just edit the locale file at `config/locales/en/oauth.en.yml`."
524
+ puts ""
525
+ puts "And finally, if you need to specify any custom authorizations or options for your OAuth integration with this provider, you can configure those in `config/initializers/devise.rb`."
526
+ puts ""
527
+
528
+ elsif argv.count > 1
529
+
530
+ puts ""
531
+ puts "👋"
532
+ puts "The command line options for Super Scaffolding have changed slightly:".yellow
533
+ puts "To use the original Super Scaffolding that you know and love, use the `crud` option.".yellow
534
+ show_usage
535
+
536
+ else
537
+ if ARGV.first.present?
538
+ puts ""
539
+ puts "Invalid scaffolding type \"#{ARGV.first}\".".red
540
+ end
541
+
542
+ show_usage
543
+ end