padrino-gen 0.16.2 → 0.17.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.rdoc +6 -6
  3. data/lib/padrino-gen/command.rb +1 -1
  4. data/lib/padrino-gen/generators/actions.rb +90 -6
  5. data/lib/padrino-gen/generators/app/app.rb.tt +2 -1
  6. data/lib/padrino-gen/generators/app.rb +2 -2
  7. data/lib/padrino-gen/generators/cli.rb +8 -4
  8. data/lib/padrino-gen/generators/component.rb +4 -1
  9. data/lib/padrino-gen/generators/components/actions.rb +5 -5
  10. data/lib/padrino-gen/generators/components/mocks/mocha.rb +12 -3
  11. data/lib/padrino-gen/generators/components/orms/activerecord.rb +37 -6
  12. data/lib/padrino-gen/generators/components/orms/minirecord.rb +12 -3
  13. data/lib/padrino-gen/generators/components/orms/mongoid.rb +10 -1
  14. data/lib/padrino-gen/generators/components/orms/sequel.rb +11 -3
  15. data/lib/padrino-gen/generators/components/scripts/dojo.rb +2 -2
  16. data/lib/padrino-gen/generators/components/scripts/jquery.rb +2 -2
  17. data/lib/padrino-gen/generators/components/scripts/mootools.rb +2 -2
  18. data/lib/padrino-gen/generators/components/scripts/prototype.rb +3 -3
  19. data/lib/padrino-gen/generators/components/scripts/vite.rb +91 -0
  20. data/lib/padrino-gen/generators/components/stylesheets/tailwind.rb +78 -0
  21. data/lib/padrino-gen/generators/components/tests/bacon.rb +1 -1
  22. data/lib/padrino-gen/generators/components/tests/cucumber.rb +1 -1
  23. data/lib/padrino-gen/generators/components/tests/minitest.rb +2 -2
  24. data/lib/padrino-gen/generators/components/tests/rspec.rb +13 -15
  25. data/lib/padrino-gen/generators/components/tests/shoulda.rb +4 -4
  26. data/lib/padrino-gen/generators/components/tests/testunit.rb +4 -4
  27. data/lib/padrino-gen/generators/controller.rb +6 -1
  28. data/lib/padrino-gen/generators/helper.rb +1 -1
  29. data/lib/padrino-gen/generators/mailer.rb +1 -1
  30. data/lib/padrino-gen/generators/migration.rb +2 -2
  31. data/lib/padrino-gen/generators/model.rb +4 -13
  32. data/lib/padrino-gen/generators/plugin.rb +1 -2
  33. data/lib/padrino-gen/generators/project/config/apps.rb.tt +3 -1
  34. data/lib/padrino-gen/generators/project/config/boot.rb +7 -0
  35. data/lib/padrino-gen/generators/project.rb +6 -4
  36. data/lib/padrino-gen/generators/runner.rb +34 -9
  37. data/lib/padrino-gen/generators/task.rb +1 -1
  38. data/lib/padrino-gen/generators/templates/Gemfile.tt +3 -1
  39. data/lib/padrino-gen/generators/templates/static/js/jquery.js +2 -4
  40. data/lib/padrino-gen/generators/templates/static/ujs/jquery.js +6 -7
  41. data/lib/padrino-gen/padrino-tasks/activerecord.rb +17 -8
  42. data/lib/padrino-gen/padrino-tasks/mongoid.rb +5 -1
  43. data/lib/padrino-gen/padrino-tasks/sequel.rb +0 -2
  44. data/test/helper.rb +2 -3
  45. data/test/test_activerecord_tasks.rb +71 -0
  46. data/test/test_app_generator.rb +3 -3
  47. data/test/test_cli.rb +95 -2
  48. data/test/test_component_generator.rb +243 -2
  49. data/test/test_controller_generator.rb +76 -19
  50. data/test/test_helper_generator.rb +1 -1
  51. data/test/test_mailer_generator.rb +1 -1
  52. data/test/test_migration_generator.rb +27 -40
  53. data/test/test_model_generator.rb +25 -146
  54. data/test/test_mongoid_tasks.rb +74 -0
  55. data/test/test_plugin_generator.rb +161 -8
  56. data/test/test_project_generator.rb +222 -103
  57. data/test/test_task_generator.rb +5 -2
  58. metadata +13 -18
  59. data/lib/padrino-gen/generators/components/orms/couchrest.rb +0 -55
  60. data/lib/padrino-gen/generators/components/orms/datamapper.rb +0 -170
  61. data/lib/padrino-gen/generators/components/orms/mongomapper.rb +0 -44
  62. data/lib/padrino-gen/generators/components/orms/mongomatic.rb +0 -88
  63. data/lib/padrino-gen/generators/components/orms/ripple.rb +0 -75
  64. data/lib/padrino-gen/generators/components/scripts/extcore.rb +0 -10
  65. data/lib/padrino-gen/generators/components/stylesheets/compass/application.scss +0 -43
  66. data/lib/padrino-gen/generators/components/stylesheets/compass/partials/_base.scss +0 -12
  67. data/lib/padrino-gen/generators/components/stylesheets/compass.rb +0 -28
  68. data/lib/padrino-gen/padrino-tasks/datamapper.rb +0 -96
  69. data/lib/padrino-gen/padrino-tasks/mongomapper.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f23d1c3fcf2f3fb73ac268383f3ad934b9cb3a19044fb26e5a1768fb0057034
4
- data.tar.gz: 3f8c4eed5f141cdcff42e93236666fe67765ed906a1eeee3e4d77181b4b6a472
3
+ metadata.gz: e4f2cbd5e5e7dbfaa0374bb83fad39536738fa404938ba59aa1296c65797a20c
4
+ data.tar.gz: b10595d8cc86921a9c6682cfa633ecacbf9df607925903270ee57c67ca4e2d4e
5
5
  SHA512:
6
- metadata.gz: 123166a6d9ef6c9c27d0b12d464380a53ec631e1ac06379f7a6685d6710d834f56f63cb5f044747186d82359aea9370caa45b29e52fd514d6eb82d5794c91270
7
- data.tar.gz: 0c9b6c6c90a123b81afdd15311ceba558afd10519506aa416abcddb52c83e0146338946d79a8ba5651f93e23d2d59c593e50d25cdf4e696fd73e3809e165aba4
6
+ metadata.gz: 513c6bfd2e9d9b8dde54280fd54fe33fb9f675fb213d14d3a14367f93ceaaf3395d060be2f42655fedeee875b0e0de2721caadcbba0ffffa7ede97e2f7bf50a5
7
+ data.tar.gz: ddef4dd75dbe398a17faec64813e8d5f798969770148d9a9595aa327748db9ab603b2a49633ee7d74040d5d96b86acaddc69630fd561b39a9a8ede6cf302643c
data/README.rdoc CHANGED
@@ -30,7 +30,7 @@ setup performed for the default components.
30
30
 
31
31
  You can define specific components to be used:
32
32
 
33
- $ padrino-gen project demo_project -t rspec -r haml -m rr -s jquery -d datamapper
33
+ $ padrino-gen project demo_project -t rspec -r haml -m rr -s jquery -d sequel
34
34
 
35
35
  You can also instruct the generator to skip a certain component to avoid using one at all (or to use your own):
36
36
 
@@ -40,10 +40,10 @@ The available components and their default options are listed below:
40
40
 
41
41
  test:: none (default), bacon, shoulda, cucumber, rspec, minitest, testunit
42
42
  renderer:: none (default), slim, erb, erubis, liquid, haml
43
- stylesheet:: none (default), less, compass, sass, scss
43
+ stylesheet:: none (default), tailwind, less, sass, scss
44
44
  mock:: none (default), mocha, rr
45
- script:: none (default), jquery, prototype, mootools, extcore, dojo
46
- orm:: none (default), datamapper, mongomapper, mongoid, activerecord, minirecord, sequel, couchrest, ohm, mongomatic, ripple, dynamoid
45
+ script:: none (default), jquery, prototype, mootools, dojo, vite
46
+ orm:: none (default), mongoid, activerecord, minirecord, sequel, ohm, dynamoid
47
47
 
48
48
  In addition, you can generate projects based on existing templates:
49
49
 
@@ -56,9 +56,9 @@ To learn more about the project generator, check out the guide to
56
56
 
57
57
  Padrino provides support for plugins to be executed within your application. For example:
58
58
 
59
- $ padrino-gen plugin hoptoad
59
+ $ padrino-gen plugin bulma
60
60
 
61
- would install the hoptoad middleware into your application automatically.
61
+ would download the Bulma stylesheet into your application's public/stylesheets.
62
62
 
63
63
  To learn more about the plugin system, check out the guide to
64
64
  {Padrino Generators}[http://padrinorb.com/guides/generators/plugins/].
@@ -13,6 +13,6 @@ module Padrino
13
13
  #
14
14
  def self.bin_gen(*args)
15
15
  @_padrino_gen_bin ||= [Padrino.ruby_command, File.expand_path('../../bin/padrino-gen', __dir__)]
16
- system(*@_padrino_gen_bin, *args)
16
+ system(*@_padrino_gen_bin, *args.map { |arg| arg.is_a?(Symbol) ? arg.to_s : arg })
17
17
  end
18
18
  end
@@ -61,7 +61,7 @@ module Padrino
61
61
  path = File.expand_path(__dir__ + "/components/#{component.to_s.pluralize}/#{choice}.rb")
62
62
  say_status :apply, "#{component.to_s.pluralize}/#{choice}"
63
63
  shell.padding += 1
64
- instance_eval(File.read(path))
64
+ instance_eval(File.read(path, encoding: 'UTF-8'))
65
65
  shell.padding -= 1
66
66
  end
67
67
 
@@ -152,11 +152,35 @@ module Padrino
152
152
  choice = options[component]
153
153
  until valid_choice?(component, choice)
154
154
  say("Option for --#{component} '#{choice}' is not available.", :red)
155
- choice = ask("Please enter a valid option for #{component}:", limited_to: choices)
155
+ choice = ask_valid_option(component, choices, "Please enter a valid option for #{component}:")
156
156
  end
157
157
  choice
158
158
  end
159
159
 
160
+ ##
161
+ # Asks once for one of the valid choices of an option; callers re-ask while the answer is invalid.
162
+ # Thor's limited_to: would repeat the prompt forever at the end of input (no terminal, empty pipe),
163
+ # so that case raises instead.
164
+ #
165
+ # @param [Symbol] option
166
+ # The name of the option.
167
+ # @param [Array<String>] choices
168
+ # The valid choices for the option.
169
+ # @param [String] statement
170
+ # The prompt to show.
171
+ #
172
+ # @return [String] The answer entered.
173
+ #
174
+ # @example
175
+ # ask_valid_option(:adapter, %w[mysql sqlite], 'Please, choose a proper adapter:')
176
+ #
177
+ def ask_valid_option(option, choices, statement)
178
+ answer = ask("#{statement} [#{choices.join(', ')}]")
179
+ raise "No input left to choose a valid --#{option}; pass one of: #{choices.join(', ')}" if answer.nil?
180
+
181
+ answer
182
+ end
183
+
160
184
  ##
161
185
  # Returns true if the option passed is a valid choice for component.
162
186
  #
@@ -215,6 +239,14 @@ module Padrino
215
239
  File.exist?(destination_root('config/boot.rb'))
216
240
  end
217
241
 
242
+ ##
243
+ # Says that the generator must run at the root of a Padrino application and exits with status 1.
244
+ #
245
+ def exit_outside_app_root!
246
+ say 'You are not at the root of a Padrino application! (config/boot.rb not found)'
247
+ exit 1
248
+ end
249
+
218
250
  ##
219
251
  # Returns true if constant name already exists.
220
252
  #
@@ -315,6 +347,18 @@ module Padrino
315
347
  gem_names.reverse_each { |lib| insert_into_gemfile(lib, options) }
316
348
  end
317
349
 
350
+ ##
351
+ # Returns true if the project runs on Falcon, which serves each request in
352
+ # a fiber. Uses the --server option when generating a project, and the
353
+ # project's Gemfile when adding a component later.
354
+ #
355
+ def falcon_server?
356
+ return options[:server] == 'falcon' if options[:server]
357
+
358
+ gemfile = destination_root('Gemfile')
359
+ File.exist?(gemfile) && File.read(gemfile).match?(/^\s*gem ['"]falcon['"]/)
360
+ end
361
+
318
362
  ##
319
363
  # Inserts a required gem into the Gemfile to add the bundler dependency.
320
364
  #
@@ -347,7 +391,7 @@ module Padrino
347
391
  # method hook to call from Padrino, i.e :after_load, :before_load.
348
392
  #
349
393
  # @example
350
- # insert_hook("DataMapper.finalize", :after_load)
394
+ # insert_hook("SomeORM.finalize", :after_load)
351
395
  #
352
396
  def insert_hook(include_text, where)
353
397
  inject_into_file('config/boot.rb', " #{include_text}\n", after: "Padrino.#{where} do\n")
@@ -436,11 +480,44 @@ module Padrino
436
480
  #
437
481
  def run_bundler
438
482
  say 'Bundling application dependencies using bundler...', :yellow
439
- in_root { run 'bundle install --binstubs' }
483
+ in_root do
484
+ with_project_bundle_env do
485
+ run 'bundle install'
486
+ run 'bundle binstubs --all'
487
+ end
488
+ end
440
489
  end
441
490
 
491
+ # The variables Bundler sets up for its children (Bundler::EnvironmentPreserver::BUNDLER_KEYS).
492
+ BUNDLER_ENV_KEYS = %w[BUNDLE_BIN_PATH BUNDLE_GEMFILE BUNDLE_LOCKFILE BUNDLER_VERSION BUNDLER_SETUP GEM_HOME
493
+ GEM_PATH MANPATH PATH RB_USER_INSTALL RUBYLIB RUBYOPT].freeze
494
+
442
495
  ##
443
- # Raise SystemExit if the app does not exist.
496
+ # Runs the block with the environment for a command run on the project: the project's Gemfile
497
+ # and lockfile, and none of the Bundler setup (RUBYOPT=-rbundler/setup, BUNDLE_GEMFILE...) that a
498
+ # caller's `bundle exec` or binstub exported. Other variables are passed on as they are. With
499
+ # +setup+, the command loads the project's bundle first, as `bundle exec` would in the project.
500
+ #
501
+ # @param [Boolean] setup
502
+ # Whether the command requires bundler/setup.
503
+ #
504
+ def with_project_bundle_env(setup: false)
505
+ saved = ENV.to_h
506
+ if defined?(Bundler) && Bundler.respond_to?(:original_env)
507
+ original = Bundler.original_env
508
+ BUNDLER_ENV_KEYS.each { |key| original.key?(key) ? ENV[key] = original[key] : ENV.delete(key) }
509
+ ENV.keys.grep(/\ABUNDLER_ORIG_/).each { |key| ENV.delete(key) }
510
+ end
511
+ ENV['BUNDLE_GEMFILE'] = destination_root('Gemfile')
512
+ ENV['BUNDLE_LOCKFILE'] = destination_root('Gemfile.lock')
513
+ ENV['RUBYOPT'] = ['-rbundler/setup', ENV['RUBYOPT']].compact.join(' ') if setup
514
+ yield
515
+ ensure
516
+ ENV.replace(saved) if saved
517
+ end
518
+
519
+ ##
520
+ # Exits with status 1 if the app does not exist.
444
521
  #
445
522
  # @param [String] app
446
523
  # Directory name of application.
@@ -456,7 +533,7 @@ module Padrino
456
533
  say "Unable to locate '#{app.underscore.camelize}' application "
457
534
  say '================================================================='
458
535
  say
459
- raise SystemExit
536
+ exit 1
460
537
  end
461
538
 
462
539
  ##
@@ -598,6 +675,13 @@ module Padrino
598
675
  @require_arguments
599
676
  end
600
677
 
678
+ ##
679
+ # Tells Thor to exit with status 1 when it rejects the arguments, e.g. a missing required argument.
680
+ #
681
+ def exit_on_failure?
682
+ true
683
+ end
684
+
601
685
  ##
602
686
  # Returns the compiled list of component types which can be specified.
603
687
  #
@@ -4,7 +4,8 @@ module <%= @project_name %>
4
4
  register Padrino::Mailer
5
5
  register Padrino::Helpers
6
6
  <% end -%>
7
- enable :sessions
7
+ set :sessions, same_site: :lax
8
+ # set :sessions, same_site: :lax, secure: true # Send the session cookie over HTTPS only
8
9
 
9
10
  ##
10
11
  # Caching support.
@@ -43,7 +43,7 @@ module Padrino
43
43
  elsif !options[:force]
44
44
  say "#{@app_name} already exists."
45
45
  say 'Please, change the name.'
46
- return
46
+ exit 1
47
47
  end
48
48
  end
49
49
 
@@ -67,7 +67,7 @@ module Padrino
67
67
  say "This application has been mounted to /#{@app_name.downcase}"
68
68
  say "You can configure a different path by editing 'config/apps.rb'"
69
69
  else
70
- say 'You are not at the root of a Padrino application! (config/boot.rb not found)'
70
+ exit_outside_app_root!
71
71
  end
72
72
  end
73
73
  end
@@ -17,6 +17,7 @@ module Padrino
17
17
  # custom generators, so is necessary know who are.
18
18
  #
19
19
  def load_boot
20
+ saved_env = { 'BUNDLE_GEMFILE' => ENV['BUNDLE_GEMFILE'], 'PADRINO_LOG_LEVEL' => ENV['PADRINO_LOG_LEVEL'] }
20
21
  ENV['PADRINO_LOG_LEVEL'] ||= 'test'
21
22
  ENV['BUNDLE_GEMFILE'] = File.join(options[:root], 'Gemfile') if options[:root]
22
23
  boot = options[:root] ? File.join(options[:root], 'config/boot.rb') : 'config/boot.rb'
@@ -29,8 +30,7 @@ module Padrino
29
30
  puts "=> Problem loading #{boot}"
30
31
  puts ["=> #{e.message}", *e.backtrace].join("\n ")
31
32
  ensure
32
- ENV.delete('BUNDLE_GEMFILE')
33
- ENV.delete('PADRINO_LOG_LEVEL')
33
+ ENV.update(saved_env) # restores the caller's values, or removes the ones it didn't have
34
34
  end
35
35
 
36
36
  ##
@@ -43,10 +43,14 @@ module Padrino
43
43
  generator_class = Padrino::Generators.mappings[generator_kind]
44
44
 
45
45
  if generator_class
46
- args = ARGV.empty? && generator_class.require_arguments? ? ['-h'] : ARGV
47
- generator_class.start(args)
46
+ if ARGV.empty? && generator_class.require_arguments?
47
+ generator_class.start(['-h'])
48
+ exit 1
49
+ end
50
+ generator_class.start(ARGV)
48
51
  else
49
52
  puts "Please specify generator to use (#{Padrino::Generators.mappings.keys.join(', ')})"
53
+ exit 1 unless generator_kind == :help
50
54
  end
51
55
  end
52
56
  end
@@ -41,6 +41,7 @@ module Padrino
41
41
  exit
42
42
  end
43
43
 
44
+ missing_app = false
44
45
  self.class.component_types.each do |comp|
45
46
  next if @_components[comp].empty?
46
47
 
@@ -53,6 +54,7 @@ module Padrino
53
54
  if comp.to_s == 'test' && !already_exists?(@app_name, @project_name)
54
55
  say "#{@project_name}::#{@app_name} does not exist."
55
56
  say 'Please, change app name.'
57
+ missing_app = true
56
58
  next
57
59
  end
58
60
 
@@ -63,8 +65,9 @@ module Padrino
63
65
  inject_into_file destination_root('Rakefile'), "PadrinoTasks.use(#{choice.to_sym.inspect})\n", before: 'PadrinoTasks.init'
64
66
  end
65
67
  end
68
+ exit 1 if missing_app
66
69
  else
67
- say 'You are not at the root of a Padrino application! (config/boot.rb not found)'
70
+ exit_outside_app_root!
68
71
  end
69
72
  end
70
73
  end
@@ -28,8 +28,8 @@ module Padrino
28
28
  return if migration_exist?(filename)
29
29
  model_name = name.to_s.pluralize
30
30
  field_tuples = columns.map { |value| value.split(':') }
31
- field_tuples.map! { |field, kind| kind =~ /datetime/i ? [field, 'DateTime'] : [field, kind] }
32
- column_declarations = field_tuples.map(&options[:column_format]).join("\n ")
31
+ field_tuples.map! { |field, kind, index| [field, kind =~ /datetime/i ? 'DateTime' : kind, index] }
32
+ column_declarations = field_tuples.flat_map(&options[:column_format]).join("\n ")
33
33
  contents = options[:base].dup.gsub(/\s{4}!UP!\n/m, options[:up]).gsub(/!DOWN!\n/m, options[:down])
34
34
  contents = contents.gsub(/!NAME!/, model_name.underscore.camelize).gsub(/!TABLE!/, model_name.underscore)
35
35
  contents = contents.gsub(/!FILENAME!/, filename.underscore).gsub(/!FILECLASS!/, filename.underscore.camelize)
@@ -70,9 +70,9 @@ module Padrino
70
70
  migration_scan = filename.underscore.camelize.scan(/(Add|Remove).*?(?:(?:To|From).+?)*(?:To|From)((?:To|From)?.*?)$/).flatten
71
71
  direction, table_name = migration_scan[0].downcase, migration_scan[1].downcase.pluralize if migration_scan.any?
72
72
  tuples = direction ? columns.map { |value| value.split(':') } : []
73
- tuples.map! { |field, kind| kind =~ /datetime/i ? [field, 'DateTime'] : [field, kind] }
74
- add_columns = tuples.map(&options[:add]).join("\n ")
75
- remove_columns = tuples.map(&options[:remove]).join("\n ")
73
+ tuples.map! { |field, kind, index| [field, kind =~ /datetime/i ? 'DateTime' : kind, index] }
74
+ add_columns = tuples.flat_map(&options[:add]).join("\n ")
75
+ remove_columns = tuples.flat_map(&options[:remove]).join("\n ")
76
76
  forward_text = change_format.gsub(/!TABLE!/, table_name).gsub(/!COLUMNS!/, add_columns) if tuples.any?
77
77
  back_text = change_format.gsub(/!TABLE!/, table_name).gsub(/!COLUMNS!/, remove_columns) if tuples.any?
78
78
  contents = options[:base].dup.gsub(/\s{4}!UP!\n/m, (direction == 'add' ? forward_text.to_s : back_text.to_s))
@@ -1,10 +1,19 @@
1
1
  def setup_mock
2
2
  require_dependencies 'mocha', group: 'test', require: false
3
3
 
4
- if options[:test].to_s == 'rspec'
4
+ test = options[:test].to_s
5
+ test = fetch_component_choice(:test).to_s if test.empty?
6
+ case test
7
+ when 'rspec'
5
8
  inject_into_file 'spec/spec_helper.rb', " conf.mock_with :mocha\n", after: "RSpec.configure do |conf|\n"
9
+ when 'minitest'
10
+ inject_into_file 'test/test_config.rb', "require 'mocha/minitest'\n", after: "require_relative '../config/boot'\n"
11
+ when 'testunit'
12
+ inject_into_file 'test/test_config.rb', "require 'mocha/test_unit'\n", after: "require_relative '../config/boot'\n"
13
+ when 'shoulda'
14
+ inject_into_file 'test/test_config.rb', "require 'mocha/test_unit'\n", after: "require \"test/unit\"\n"
6
15
  else
7
- inject_into_file 'test/test_config.rb', "require 'mocha/api'\n", after: "require_relative '../config/boot')\n"
8
- insert_mocking_include 'Mocha::API'
16
+ say "Mocha is in the Gemfile but not set up for the '#{test}' test component. To use it, require mocha/api " \
17
+ 'and call mocha_setup, mocha_verify and mocha_teardown around each test: https://github.com/freerange/mocha#readme', :yellow
9
18
  end
10
19
  end
@@ -112,7 +112,7 @@ CONNECTION_POOL_MIDDLEWARE = <<~MIDDLEWARE
112
112
  MIDDLEWARE
113
113
 
114
114
  def setup_orm
115
- ar = AR
115
+ ar = AR.dup
116
116
  db = @project_name.underscore
117
117
 
118
118
  begin
@@ -138,15 +138,24 @@ def setup_orm
138
138
  ar.sub!(/!DB_TEST!/, SQLITE.sub(/!DB_NAME!/, "Padrino.root('db', '#{db}_test.db')"))
139
139
  require_dependencies 'sqlite3'
140
140
  else
141
- say "Failed to generate `config/database.rb` for ORM adapter `#{options[:adapter]}`", :red
141
+ say "Failed to generate `config/database.rb` for ORM adapter `#{adapter}`", :red
142
142
  raise ArgumentError
143
143
  end
144
144
  rescue ArgumentError
145
- adapter = ask('Please, choose a proper adapter:', limited_to: %w[mysql mysql2 mysql-gem postgres sqlite])
145
+ adapter = ask_valid_option(:adapter, %w[mysql mysql2 mysql-gem postgres sqlite], 'Please, choose a proper adapter:')
146
146
  retry
147
147
  end
148
148
 
149
149
  require_dependencies 'activerecord', require: 'active_record', version: '>= 3.1'
150
+
151
+ if falcon_server?
152
+ ar = <<~FIBER + ar
153
+ # Falcon serves each request in a fiber, so keep Active Record state per fiber.
154
+ ActiveSupport::IsolatedExecutionState.isolation_level = :fiber
155
+
156
+ FIBER
157
+ end
158
+
150
159
  create_file('config/database.rb', ar)
151
160
  middleware :connection_pool_management, CONNECTION_POOL_MIDDLEWARE
152
161
  end
@@ -201,11 +210,33 @@ AR_MODEL_DOWN_MG = <<~MIGRATION unless defined?(AR_MODEL_DOWN_MG)
201
210
  drop_table :!TABLE!
202
211
  MIGRATION
203
212
 
213
+ def ar_reference?(kind)
214
+ %w[references belongs_to].include?(kind.underscore)
215
+ end
216
+
217
+ # A third field segment, as in user_id:integer:index or email:string:uniq, adds an index.
218
+ def ar_add_column(field, kind, index)
219
+ if ar_reference?(kind)
220
+ index_option = { 'index' => ', index: true', 'uniq' => ', index: { unique: true }' }[index]
221
+ return "t.#{kind.underscore} :#{field}#{index_option}"
222
+ end
223
+
224
+ columns = ["t.#{kind.underscore.gsub(/_/, '')} :#{field}"]
225
+ columns << "t.index :#{field}" if index == 'index'
226
+ columns << "t.index :#{field}, unique: true" if index == 'uniq'
227
+ columns
228
+ end
229
+
230
+ # Removing a column also removes its indexes.
231
+ def ar_remove_column(field, kind)
232
+ ar_reference?(kind) ? "t.remove_references :#{field}" : "t.remove :#{field}"
233
+ end
234
+
204
235
  def create_model_migration(migration_name, name, columns)
205
236
  output_model_migration(
206
237
  migration_name, name, columns,
207
238
  base: AR_MIGRATION,
208
- column_format: proc { |field, kind| "t.#{kind.underscore.gsub(/_/, '')} :#{field}" },
239
+ column_format: proc { |field, kind, index| ar_add_column(field, kind, index) },
209
240
  up: AR_MODEL_UP_MG,
210
241
  down: AR_MODEL_DOWN_MG
211
242
  )
@@ -222,7 +253,7 @@ def create_migration_file(migration_name, name, columns)
222
253
  migration_name, name, columns,
223
254
  base: AR_MIGRATION,
224
255
  change_format: AR_CHANGE_MG,
225
- add: proc { |field, kind| "t.#{kind.underscore.gsub(/_/, '')} :#{field}" },
226
- remove: proc { |field, _kind| "t.remove :#{field}" }
256
+ add: proc { |field, kind, index| ar_add_column(field, kind, index) },
257
+ remove: proc { |field, kind| ar_remove_column(field, kind) }
227
258
  )
228
259
  end
@@ -109,7 +109,7 @@ CONNECTION_POOL_MIDDLEWARE = <<~MIDDLEWARE
109
109
  MIDDLEWARE
110
110
 
111
111
  def setup_orm
112
- ar = MR
112
+ ar = MR.dup
113
113
  db = @project_name.underscore
114
114
 
115
115
  begin
@@ -135,15 +135,24 @@ def setup_orm
135
135
  ar.gsub!(/!DB_TEST!/, SQLITE.gsub(/!DB_NAME!/, "Padrino.root('db', '#{db}_test.db')"))
136
136
  require_dependencies 'sqlite3'
137
137
  else
138
- say "Failed to generate `config/database.rb` for ORM adapter `#{options[:adapter]}`", :red
138
+ say "Failed to generate `config/database.rb` for ORM adapter `#{adapter}`", :red
139
139
  raise ArgumentError
140
140
  end
141
141
  rescue ArgumentError
142
- adapter = ask('Please, choose a proper adapter:', limited_to: %w[mysql mysql2 mysql-gem postgres sqlite])
142
+ adapter = ask_valid_option(:adapter, %w[mysql mysql2 mysql-gem postgres sqlite], 'Please, choose a proper adapter:')
143
143
  retry
144
144
  end
145
145
 
146
146
  require_dependencies 'mini_record'
147
+
148
+ if falcon_server?
149
+ ar = <<~FIBER + ar
150
+ # Falcon serves each request in a fiber, so keep Active Record state per fiber.
151
+ ActiveSupport::IsolatedExecutionState.isolation_level = :fiber
152
+
153
+ FIBER
154
+ end
155
+
147
156
  create_file('config/database.rb', ar)
148
157
  insert_hook('ActiveRecord::Base.auto_upgrade!', :after_load)
149
158
  middleware :connection_pool_management, CONNECTION_POOL_MIDDLEWARE
@@ -56,7 +56,16 @@ MONGO
56
56
 
57
57
  def setup_orm
58
58
  require_dependencies 'mongoid', version: '>= 3.0.0'
59
- create_file('config/database.rb', MONGOID.gsub(/!NAME!/, @project_name.underscore))
59
+ mongoid = MONGOID.gsub(/!NAME!/, @project_name.underscore)
60
+ if falcon_server?
61
+ mongoid = <<~FIBER + mongoid
62
+ # Falcon serves each request in a fiber, so keep Mongoid state per fiber
63
+ # (supported by Mongoid 9.1+ on Ruby 3.2+).
64
+ Mongoid::Config.isolation_level = :fiber if RUBY_VERSION >= '3.2' && Mongoid::Config.respond_to?(:isolation_level=)
65
+
66
+ FIBER
67
+ end
68
+ create_file('config/database.rb', mongoid)
60
69
  end
61
70
 
62
71
  MONGOID_MODEL = <<~MODEL unless defined?(MONGOID_MODEL)
@@ -8,7 +8,7 @@ SEQUEL = <<~SEQUEL unless defined?(SEQUEL)
8
8
  SEQUEL
9
9
 
10
10
  def setup_orm
11
- sequel = SEQUEL
11
+ sequel = SEQUEL.dup
12
12
  db = @project_name.underscore
13
13
  require_dependencies 'sequel'
14
14
 
@@ -35,14 +35,22 @@ def setup_orm
35
35
  sequel.gsub!(/!DB_TEST!/, "\"sqlite://db/#{db}_test.db\"")
36
36
  require_dependencies 'sqlite3'
37
37
  else
38
- say "Failed to generate `config/database.rb` for ORM adapter `#{options[:adapter]}`", :red
38
+ say "Failed to generate `config/database.rb` for ORM adapter `#{adapter}`", :red
39
39
  raise ArgumentError
40
40
  end
41
41
  rescue ArgumentError
42
- adapter = ask('Please, choose a proper adapter:', limited_to: %w[mysql mysql2 mysql-gem postgres sqlite])
42
+ adapter = ask_valid_option(:adapter, %w[mysql mysql2 mysql-gem postgres sqlite], 'Please, choose a proper adapter:')
43
43
  retry
44
44
  end
45
45
 
46
+ if falcon_server?
47
+ sequel = <<~FIBER + sequel
48
+ # Falcon serves each request in a fiber, so give each fiber its own connection.
49
+ Sequel.extension :fiber_concurrency
50
+
51
+ FIBER
52
+ end
53
+
46
54
  create_file('config/database.rb', sequel)
47
55
  empty_directory('db/migrate')
48
56
  end
@@ -1,7 +1,7 @@
1
1
  def setup_script
2
2
  begin
3
- get('https://raw.github.com/padrino/padrino-static/master/js/dojo.js', destination_root('/public/javascripts/dojo.js'))
4
- get('https://raw.github.com/padrino/padrino-static/master/ujs/dojo.js', destination_root('/public/javascripts/dojo-ujs.js'))
3
+ get('https://raw.githubusercontent.com/padrino/padrino-static/master/js/dojo.js', destination_root('/public/javascripts/dojo.js'))
4
+ get('https://raw.githubusercontent.com/padrino/padrino-static/master/ujs/dojo.js', destination_root('/public/javascripts/dojo-ujs.js'))
5
5
  rescue StandardError
6
6
  copy_file('templates/static/js/dojo.js', destination_root('/public/javascripts/dojo.js'))
7
7
  copy_file('templates/static/ujs/dojo.js', destination_root('/public/javascripts/dojo-ujs.js'))
@@ -1,7 +1,7 @@
1
1
  def setup_script
2
2
  begin
3
- get('https://raw.github.com/padrino/padrino-static/master/js/jquery.js', destination_root('/public/javascripts/jquery.js'))
4
- get('https://raw.github.com/padrino/padrino-static/master/ujs/jquery.js', destination_root('/public/javascripts/jquery-ujs.js'))
3
+ get('https://raw.githubusercontent.com/padrino/padrino-static/master/js/jquery.js', destination_root('/public/javascripts/jquery.js'))
4
+ get('https://raw.githubusercontent.com/padrino/padrino-static/master/ujs/jquery.js', destination_root('/public/javascripts/jquery-ujs.js'))
5
5
  rescue StandardError
6
6
  copy_file('templates/static/js/jquery.js', destination_root('/public/javascripts/jquery.js'))
7
7
  copy_file('templates/static/ujs/jquery.js', destination_root('/public/javascripts/jquery-ujs.js'))
@@ -1,7 +1,7 @@
1
1
  def setup_script
2
2
  begin
3
- get('https://raw.github.com/padrino/padrino-static/master/js/mootools.js', destination_root('/public/javascripts/mootools.js'))
4
- get('https://raw.github.com/padrino/padrino-static/master/ujs/mootools.js', destination_root('/public/javascripts/mootools-ujs.js'))
3
+ get('https://raw.githubusercontent.com/padrino/padrino-static/master/js/mootools.js', destination_root('/public/javascripts/mootools.js'))
4
+ get('https://raw.githubusercontent.com/padrino/padrino-static/master/ujs/mootools.js', destination_root('/public/javascripts/mootools-ujs.js'))
5
5
  rescue StandardError
6
6
  copy_file('templates/static/js/mootools.js', destination_root('/public/javascripts/mootools.js'))
7
7
  copy_file('templates/static/ujs/mootools.js', destination_root('/public/javascripts/mootools-ujs.js'))
@@ -1,8 +1,8 @@
1
1
  def setup_script
2
2
  begin
3
- get('https://raw.github.com/padrino/padrino-static/master/js/protopak.js', destination_root('/public/javascripts/protopak.js'))
4
- get('https://raw.github.com/padrino/padrino-static/master/js/lowpro.js', destination_root('/public/javascripts/lowpro.js'))
5
- get('https://raw.github.com/padrino/padrino-static/master/ujs/prototype.js', destination_root('/public/javascripts/prototype-ujs.js'))
3
+ get('https://raw.githubusercontent.com/padrino/padrino-static/master/js/protopak.js', destination_root('/public/javascripts/protopak.js'))
4
+ get('https://raw.githubusercontent.com/padrino/padrino-static/master/js/lowpro.js', destination_root('/public/javascripts/lowpro.js'))
5
+ get('https://raw.githubusercontent.com/padrino/padrino-static/master/ujs/prototype.js', destination_root('/public/javascripts/prototype-ujs.js'))
6
6
  rescue StandardError
7
7
  copy_file('templates/static/js/protopak.js', destination_root('/public/javascripts/protopak.js'))
8
8
  copy_file('templates/static/js/lowpro.js', destination_root('/public/javascripts/lowpro.js'))