padrino-gen 0.16.2 → 0.18.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.
- checksums.yaml +4 -4
- data/README.rdoc +8 -8
- data/lib/padrino-gen/command.rb +1 -1
- data/lib/padrino-gen/generators/actions.rb +233 -18
- data/lib/padrino-gen/generators/app/app.rb.tt +4 -1
- data/lib/padrino-gen/generators/app.rb +2 -2
- data/lib/padrino-gen/generators/cli.rb +17 -7
- data/lib/padrino-gen/generators/component.rb +20 -13
- data/lib/padrino-gen/generators/components/actions.rb +7 -6
- data/lib/padrino-gen/generators/components/mocks/mocha.rb +19 -4
- data/lib/padrino-gen/generators/components/mocks/rr.rb +24 -6
- data/lib/padrino-gen/generators/components/orms/activerecord.rb +73 -35
- data/lib/padrino-gen/generators/components/orms/mongoid.rb +37 -2
- data/lib/padrino-gen/generators/components/orms/sequel.rb +88 -37
- data/lib/padrino-gen/generators/components/renderers/liquid.rb +1 -2
- data/lib/padrino-gen/generators/components/scripts/dojo.rb +3 -3
- data/lib/padrino-gen/generators/components/scripts/jquery.rb +3 -3
- data/lib/padrino-gen/generators/components/scripts/mootools.rb +3 -3
- data/lib/padrino-gen/generators/components/scripts/prototype.rb +4 -4
- data/lib/padrino-gen/generators/components/scripts/vite.rb +91 -0
- data/lib/padrino-gen/generators/components/stylesheets/sass.rb +1 -1
- data/lib/padrino-gen/generators/components/stylesheets/scss.rb +1 -1
- data/lib/padrino-gen/generators/components/stylesheets/tailwind.rb +78 -0
- data/lib/padrino-gen/generators/components/tests/bacon.rb +4 -2
- data/lib/padrino-gen/generators/components/tests/cucumber.rb +14 -9
- data/lib/padrino-gen/generators/components/tests/minitest.rb +3 -3
- data/lib/padrino-gen/generators/components/tests/rspec.rb +14 -16
- data/lib/padrino-gen/generators/components/tests/shoulda.rb +5 -5
- data/lib/padrino-gen/generators/components/tests/testunit.rb +5 -5
- data/lib/padrino-gen/generators/controller.rb +6 -1
- data/lib/padrino-gen/generators/helper.rb +1 -1
- data/lib/padrino-gen/generators/mailer.rb +1 -1
- data/lib/padrino-gen/generators/migration.rb +2 -2
- data/lib/padrino-gen/generators/model.rb +4 -13
- data/lib/padrino-gen/generators/plugin.rb +1 -2
- data/lib/padrino-gen/generators/project/config/apps.rb.tt +7 -2
- data/lib/padrino-gen/generators/project/config/boot.rb +7 -0
- data/lib/padrino-gen/generators/project.rb +24 -8
- data/lib/padrino-gen/generators/runner.rb +37 -10
- data/lib/padrino-gen/generators/task.rb +1 -1
- data/lib/padrino-gen/generators/templates/Gemfile.tt +3 -1
- data/lib/padrino-gen/generators/templates/static/js/jquery.js +2 -4
- data/lib/padrino-gen/generators/templates/static/ujs/jquery.js +6 -7
- data/lib/padrino-gen/padrino-tasks/activerecord.rb +79 -26
- data/lib/padrino-gen/padrino-tasks/mongoid.rb +5 -1
- data/lib/padrino-gen/padrino-tasks/sequel.rb +37 -16
- data/lib/padrino-gen/padrino-tasks/sql-helpers.rb +172 -44
- data/test/helper.rb +2 -3
- data/test/test_activerecord_tasks.rb +241 -0
- data/test/test_app_generator.rb +18 -5
- data/test/test_cli.rb +152 -2
- data/test/test_component_generator.rb +547 -2
- data/test/test_controller_generator.rb +79 -21
- data/test/test_generator.rb +24 -0
- data/test/test_helper_generator.rb +4 -3
- data/test/test_mailer_generator.rb +5 -4
- data/test/test_migration_generator.rb +146 -39
- data/test/test_model_generator.rb +124 -203
- data/test/test_mongoid_tasks.rb +74 -0
- data/test/test_plugin_generator.rb +199 -8
- data/test/test_project_generator.rb +348 -132
- data/test/test_sequel_tasks.rb +156 -0
- data/test/test_sql_helpers.rb +139 -64
- data/test/test_sql_helpers_dump.rb +328 -0
- data/test/test_task_generator.rb +9 -5
- metadata +16 -46
- data/lib/padrino-gen/generators/components/orms/couchrest.rb +0 -55
- data/lib/padrino-gen/generators/components/orms/datamapper.rb +0 -170
- data/lib/padrino-gen/generators/components/orms/dynamoid.rb +0 -67
- data/lib/padrino-gen/generators/components/orms/minirecord.rb +0 -175
- data/lib/padrino-gen/generators/components/orms/mongomapper.rb +0 -44
- data/lib/padrino-gen/generators/components/orms/mongomatic.rb +0 -88
- data/lib/padrino-gen/generators/components/orms/ohm.rb +0 -67
- data/lib/padrino-gen/generators/components/orms/ripple.rb +0 -75
- data/lib/padrino-gen/generators/components/scripts/extcore.rb +0 -10
- data/lib/padrino-gen/generators/components/stylesheets/compass/application.scss +0 -43
- data/lib/padrino-gen/generators/components/stylesheets/compass/partials/_base.scss +0 -12
- data/lib/padrino-gen/generators/components/stylesheets/compass.rb +0 -28
- data/lib/padrino-gen/generators/components/stylesheets/less.rb +0 -25
- data/lib/padrino-gen/padrino-tasks/datamapper.rb +0 -96
- data/lib/padrino-gen/padrino-tasks/minirecord.rb +0 -19
- 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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '039917b81dc2bfbb846774da7452682d6f29af8bc1f4d76609b2ec10d8392912'
|
|
4
|
+
data.tar.gz: 451d8a83d39747bf6648d0b434f211a4571e5694f5cc70b45e9f9844ef7bfa7c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c0769c35ccfe2cff70e99aa0c137b825e285e831649945d18e07e5ea72b44efcc1214f3fb3ebc63661d0b0c8371e84c78dcb5fff6b7472471c83176e3dd1b40e
|
|
7
|
+
data.tar.gz: 45b9f905e803023d7932ceb487ade75bbcd5df395754f820cc86626176de272bb422ef5696d3665027b80f7a8f1d34436f266b02c8bb27d9938fbbcce410baf1
|
data/README.rdoc
CHANGED
|
@@ -24,13 +24,13 @@ The simplest possible command to generate a base application would be:
|
|
|
24
24
|
|
|
25
25
|
$ padrino-gen project demo_project
|
|
26
26
|
|
|
27
|
-
This would construct a Padrino application
|
|
27
|
+
This would construct a Padrino application DemoProject::App (which extends from Padrino::Application)
|
|
28
28
|
inside the folder 'demo_project' at our current path. Inside the application there would be configuration and
|
|
29
29
|
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 -
|
|
33
|
+
$ padrino-gen project demo_project -t rspec -e 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
|
|
|
@@ -39,11 +39,11 @@ You can also instruct the generator to skip a certain component to avoid using o
|
|
|
39
39
|
The available components and their default options are listed below:
|
|
40
40
|
|
|
41
41
|
test:: none (default), bacon, shoulda, cucumber, rspec, minitest, testunit
|
|
42
|
-
renderer:: none (default), slim, erb,
|
|
43
|
-
stylesheet:: none (default),
|
|
42
|
+
renderer:: none (default), slim, erb, liquid, haml
|
|
43
|
+
stylesheet:: none (default), tailwind, sass, scss
|
|
44
44
|
mock:: none (default), mocha, rr
|
|
45
|
-
script:: none (default), jquery, prototype, mootools,
|
|
46
|
-
orm:: none (default),
|
|
45
|
+
script:: none (default), jquery, prototype, mootools, dojo, vite
|
|
46
|
+
orm:: none (default), mongoid, activerecord, sequel
|
|
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
|
|
59
|
+
$ padrino-gen plugin bulma
|
|
60
60
|
|
|
61
|
-
would
|
|
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/].
|
data/lib/padrino-gen/command.rb
CHANGED
|
@@ -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
|
|
@@ -10,6 +10,12 @@ module Padrino
|
|
|
10
10
|
#
|
|
11
11
|
DEFAULT_HELPER_NAME = 'Helper'.freeze
|
|
12
12
|
|
|
13
|
+
##
|
|
14
|
+
# The SQL adapters for --adapter, and the ORM components that set up their database with one.
|
|
15
|
+
#
|
|
16
|
+
SQL_ADAPTERS = %w[mysql mysql2 mysql-gem postgres sqlite].freeze
|
|
17
|
+
SQL_ADAPTER_ORMS = %w[activerecord sequel].freeze
|
|
18
|
+
|
|
13
19
|
##
|
|
14
20
|
# Common actions needed to support project and component generation.
|
|
15
21
|
#
|
|
@@ -59,9 +65,19 @@ module Padrino
|
|
|
59
65
|
def apply_component_for(choice, component)
|
|
60
66
|
# I need to override Thor#apply because for unknown reason verbose: false break tasks.
|
|
61
67
|
path = File.expand_path(__dir__ + "/components/#{component.to_s.pluralize}/#{choice}.rb")
|
|
68
|
+
unless File.exist?(path)
|
|
69
|
+
# An existing project's .components can name a component that has since been removed.
|
|
70
|
+
available = Dir["#{__dir__}/components/#{component.to_s.pluralize}/*.rb"].map { |lib| File.basename(lib, '.rb') }.sort
|
|
71
|
+
if choice.to_s.empty?
|
|
72
|
+
say "The project's .components sets no #{component} component (available: #{available.join(', ')})."
|
|
73
|
+
else
|
|
74
|
+
say "The #{choice} #{component} component is no longer available in Padrino (available: #{available.join(', ')})."
|
|
75
|
+
end
|
|
76
|
+
exit 1
|
|
77
|
+
end
|
|
62
78
|
say_status :apply, "#{component.to_s.pluralize}/#{choice}"
|
|
63
79
|
shell.padding += 1
|
|
64
|
-
instance_eval(File.read(path))
|
|
80
|
+
instance_eval(File.read(path, encoding: 'UTF-8'))
|
|
65
81
|
shell.padding -= 1
|
|
66
82
|
end
|
|
67
83
|
|
|
@@ -152,11 +168,130 @@ module Padrino
|
|
|
152
168
|
choice = options[component]
|
|
153
169
|
until valid_choice?(component, choice)
|
|
154
170
|
say("Option for --#{component} '#{choice}' is not available.", :red)
|
|
155
|
-
choice =
|
|
171
|
+
choice = ask_valid_option(component, choices, "Please enter a valid option for #{component}:")
|
|
156
172
|
end
|
|
157
173
|
choice
|
|
158
174
|
end
|
|
159
175
|
|
|
176
|
+
##
|
|
177
|
+
# Prompts the user if necessary until options[:adapter] or the answer is a valid SQL adapter, and keeps it.
|
|
178
|
+
#
|
|
179
|
+
# @return [String] Name of the SQL adapter.
|
|
180
|
+
#
|
|
181
|
+
# @example
|
|
182
|
+
# resolve_valid_adapter # => 'sqlite'
|
|
183
|
+
#
|
|
184
|
+
def resolve_valid_adapter
|
|
185
|
+
@_adapter ||= begin
|
|
186
|
+
adapter = options[:adapter] || stored_adapter || 'sqlite'
|
|
187
|
+
until SQL_ADAPTERS.include?(adapter)
|
|
188
|
+
say("Option for --adapter '#{adapter}' is not available.", :red)
|
|
189
|
+
adapter = ask_valid_option(:adapter, SQL_ADAPTERS, 'Please enter a valid option for adapter:')
|
|
190
|
+
end
|
|
191
|
+
adapter
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# Returns whether `padrino g component` is applying a component the project already has,
|
|
197
|
+
# rather than adding it or switching to it. Files the project may have edited stay as they are then.
|
|
198
|
+
#
|
|
199
|
+
# @param [Symbol] component
|
|
200
|
+
# The type of component module.
|
|
201
|
+
#
|
|
202
|
+
# @return [Boolean] Whether the chosen component is the one the project has.
|
|
203
|
+
#
|
|
204
|
+
def reapplying_component?(component)
|
|
205
|
+
!@_stored_components.nil? && @_stored_components[component].to_s == @_components[component].to_s
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
##
|
|
209
|
+
# Removes the lines the project's mock component added to the test helper, when `padrino g component`
|
|
210
|
+
# switches to another mock. Each mock file defines mock_setup_lines(test), the lines its setup_mock adds;
|
|
211
|
+
# the previous mock's file is evaluated on a separate object, so its methods don't replace the new mock's.
|
|
212
|
+
# Only lines that are still exactly as written (indentation aside) are removed; other edits stay.
|
|
213
|
+
#
|
|
214
|
+
# @param [String] test
|
|
215
|
+
# The test component the new mock is set up for.
|
|
216
|
+
#
|
|
217
|
+
def remove_previous_mock_setup(test)
|
|
218
|
+
previous = @_stored_components && @_stored_components[:mock].to_s
|
|
219
|
+
return if previous.nil? || previous.empty? || previous == 'none' || reapplying_component?(:mock)
|
|
220
|
+
source = File.expand_path("components/mocks/#{previous}.rb", __dir__)
|
|
221
|
+
return unless File.exist?(source)
|
|
222
|
+
|
|
223
|
+
probe = Object.new
|
|
224
|
+
probe.instance_eval(File.read(source, encoding: 'UTF-8'), source)
|
|
225
|
+
setup = probe.respond_to?(:mock_setup_lines) && probe.mock_setup_lines(test)
|
|
226
|
+
return unless setup && File.exist?(destination_root(setup[:path]))
|
|
227
|
+
|
|
228
|
+
content = File.binread(destination_root(setup[:path]))
|
|
229
|
+
# The mocks insert after the same line; if the project edited it, the new mock can't be added, so keep the old one.
|
|
230
|
+
return unless content.include?(setup[:after].b)
|
|
231
|
+
|
|
232
|
+
lines = setup[:text].each_line.map(&:strip).reject(&:empty?).uniq
|
|
233
|
+
pattern = /^[ \t]*(?:#{lines.map { |line| Regexp.escape(line) }.join('|')})[ \t]*(?:\n|\z)/
|
|
234
|
+
gsub_file(setup[:path], pattern, '') if content.match?(pattern)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
##
|
|
238
|
+
# Returns whether config/database.rb stays: the orm is the project's, and so is any --adapter given.
|
|
239
|
+
#
|
|
240
|
+
# @return [Boolean] Whether to keep an existing database config.
|
|
241
|
+
#
|
|
242
|
+
def keep_database_config?
|
|
243
|
+
# An --adapter corrected at the prompt counts as the corrected one, which resolve_valid_adapter keeps in @_adapter.
|
|
244
|
+
given = options[:adapter] && (@_adapter || options[:adapter])
|
|
245
|
+
reapplying_component?(:orm) && [nil, stored_adapter].include?(given)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
##
|
|
249
|
+
# Returns whether the SQL adapter of the project is unknown: it is applying the orm the project
|
|
250
|
+
# has, without --adapter, in a project that has a database config but no stored adapter
|
|
251
|
+
# (generated by an older version). The config stays and no driver is added then.
|
|
252
|
+
#
|
|
253
|
+
# @return [Boolean] Whether the adapter is unknown.
|
|
254
|
+
#
|
|
255
|
+
def adapter_unknown?
|
|
256
|
+
return @_adapter_unknown if defined?(@_adapter_unknown)
|
|
257
|
+
|
|
258
|
+
@_adapter_unknown = reapplying_component?(:orm) && options[:adapter].nil? && stored_adapter.nil? &&
|
|
259
|
+
File.exist?(destination_root('config/database.rb'))
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
##
|
|
263
|
+
# Returns the SQL adapter stored for the project, if any. Projects generated by older versions have none.
|
|
264
|
+
#
|
|
265
|
+
# @return [String, nil] Name of the SQL adapter.
|
|
266
|
+
#
|
|
267
|
+
def stored_adapter
|
|
268
|
+
@_stored_components && @_stored_components[:adapter]
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
##
|
|
272
|
+
# Asks once for one of the valid choices of an option; callers re-ask while the answer is invalid.
|
|
273
|
+
# Thor's limited_to: would repeat the prompt forever at the end of input (no terminal, empty pipe),
|
|
274
|
+
# so that case raises a Thor::Error instead: the generator's start prints its message and exits with status 1.
|
|
275
|
+
#
|
|
276
|
+
# @param [Symbol] option
|
|
277
|
+
# The name of the option.
|
|
278
|
+
# @param [Array<String>] choices
|
|
279
|
+
# The valid choices for the option.
|
|
280
|
+
# @param [String] statement
|
|
281
|
+
# The prompt to show.
|
|
282
|
+
#
|
|
283
|
+
# @return [String] The answer entered.
|
|
284
|
+
#
|
|
285
|
+
# @example
|
|
286
|
+
# ask_valid_option(:adapter, %w[mysql sqlite], 'Please, choose a proper adapter:')
|
|
287
|
+
#
|
|
288
|
+
def ask_valid_option(option, choices, statement)
|
|
289
|
+
answer = ask("#{statement} [#{choices.join(', ')}]")
|
|
290
|
+
raise Thor::Error, "No input left to choose a valid --#{option}; pass one of: #{choices.join(', ')}" if answer.nil?
|
|
291
|
+
|
|
292
|
+
answer
|
|
293
|
+
end
|
|
294
|
+
|
|
160
295
|
##
|
|
161
296
|
# Returns true if the option passed is a valid choice for component.
|
|
162
297
|
#
|
|
@@ -215,12 +350,24 @@ module Padrino
|
|
|
215
350
|
File.exist?(destination_root('config/boot.rb'))
|
|
216
351
|
end
|
|
217
352
|
|
|
353
|
+
##
|
|
354
|
+
# Says that the generator must run at the root of a Padrino application and exits with status 1.
|
|
355
|
+
#
|
|
356
|
+
def exit_outside_app_root!
|
|
357
|
+
say 'You are not at the root of a Padrino application! (config/boot.rb not found)'
|
|
358
|
+
exit 1
|
|
359
|
+
end
|
|
360
|
+
|
|
218
361
|
##
|
|
219
362
|
# Returns true if constant name already exists.
|
|
220
363
|
#
|
|
221
364
|
def already_exists?(name, project_name = nil)
|
|
222
365
|
project_name = project_name ? (Object.const_get(project_name) rescue nil) : nil
|
|
223
366
|
Object.const_defined?(name) || project_name&.const_defined?(name)
|
|
367
|
+
rescue ::NameError => e
|
|
368
|
+
# Only Ruby's rejection of the name itself; a NameError from code that autoloading runs is a real error.
|
|
369
|
+
raise unless e.instance_of?(::NameError) && e.message.start_with?('wrong constant name')
|
|
370
|
+
raise Thor::Error, "#{name} is not a valid constant name"
|
|
224
371
|
end
|
|
225
372
|
|
|
226
373
|
##
|
|
@@ -315,6 +462,18 @@ module Padrino
|
|
|
315
462
|
gem_names.reverse_each { |lib| insert_into_gemfile(lib, options) }
|
|
316
463
|
end
|
|
317
464
|
|
|
465
|
+
##
|
|
466
|
+
# Returns true if the project runs on Falcon, which serves each request in
|
|
467
|
+
# a fiber. Uses the --server option when generating a project, and the
|
|
468
|
+
# project's Gemfile when adding a component later.
|
|
469
|
+
#
|
|
470
|
+
def falcon_server?
|
|
471
|
+
return options[:server] == 'falcon' if options[:server]
|
|
472
|
+
|
|
473
|
+
gemfile = destination_root('Gemfile')
|
|
474
|
+
File.exist?(gemfile) && File.read(gemfile).match?(/^\s*gem ['"]falcon['"]/)
|
|
475
|
+
end
|
|
476
|
+
|
|
318
477
|
##
|
|
319
478
|
# Inserts a required gem into the Gemfile to add the bundler dependency.
|
|
320
479
|
#
|
|
@@ -329,9 +488,18 @@ module Padrino
|
|
|
329
488
|
# insert_into_gemfile(name, group: 'test', version: ">1.2.3")
|
|
330
489
|
#
|
|
331
490
|
def insert_into_gemfile(name, options = {})
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
491
|
+
# Generated Gemfiles only have a "Test requirements" section; other groups go with the components.
|
|
492
|
+
group_pattern = "#{options[:group].to_s.capitalize} requirements\n" if options[:group]
|
|
493
|
+
gemfile = destination_root('Gemfile')
|
|
494
|
+
after_pattern = group_pattern if group_pattern && File.exist?(gemfile) && File.read(gemfile).include?(group_pattern)
|
|
495
|
+
after_pattern ||= "Component requirements\n"
|
|
496
|
+
version = options[:version]
|
|
497
|
+
gem_options = options.reject { |k, _| k == :version }.map do |k, v|
|
|
498
|
+
if v.is_a?(Array) then "#{k}: #{v.inspect}"
|
|
499
|
+
elsif k.to_s == 'require' && [true, false].include?(v) then "#{k}: #{v}"
|
|
500
|
+
else "#{k}: '#{v}'"
|
|
501
|
+
end
|
|
502
|
+
end.join(', ')
|
|
335
503
|
write_option = gem_options.empty? ? '' : ", #{gem_options}"
|
|
336
504
|
write_version = version ? ", '#{version}'" : ''
|
|
337
505
|
include_text = "gem '#{name}'" << write_version << write_option << "\n"
|
|
@@ -347,7 +515,7 @@ module Padrino
|
|
|
347
515
|
# method hook to call from Padrino, i.e :after_load, :before_load.
|
|
348
516
|
#
|
|
349
517
|
# @example
|
|
350
|
-
# insert_hook("
|
|
518
|
+
# insert_hook("SomeORM.finalize", :after_load)
|
|
351
519
|
#
|
|
352
520
|
def insert_hook(include_text, where)
|
|
353
521
|
inject_into_file('config/boot.rb', " #{include_text}\n", after: "Padrino.#{where} do\n")
|
|
@@ -374,16 +542,18 @@ module Padrino
|
|
|
374
542
|
# Name of the initializer.
|
|
375
543
|
# @param [String] data
|
|
376
544
|
# Text to generate into the initializer file.
|
|
545
|
+
# @param [Boolean] skip
|
|
546
|
+
# Whether to keep an existing initializer file.
|
|
377
547
|
#
|
|
378
548
|
# @example
|
|
379
549
|
# initializer(:test, "some stuff here")
|
|
380
550
|
# #=> generates 'lib/test_init.rb'
|
|
381
551
|
#
|
|
382
|
-
def initializer(name, data = nil)
|
|
552
|
+
def initializer(name, data = nil, skip: false)
|
|
383
553
|
@_init_name, @_init_data = name, data
|
|
384
554
|
register = data ? " register #{name.to_s.underscore.camelize}Initializer\n" : " register #{name}\n"
|
|
385
555
|
inject_into_file destination_root('/app/app.rb'), register, after: "Padrino::Application\n"
|
|
386
|
-
template 'templates/initializer.rb.tt', destination_root("/config/initializers/#{name}.rb") if data
|
|
556
|
+
template 'templates/initializer.rb.tt', destination_root("/config/initializers/#{name}.rb"), skip: skip if data
|
|
387
557
|
end
|
|
388
558
|
|
|
389
559
|
##
|
|
@@ -392,13 +562,15 @@ module Padrino
|
|
|
392
562
|
# Name of the middleware.
|
|
393
563
|
# @param [String] source
|
|
394
564
|
# Text to generate into the middleware file.
|
|
565
|
+
# @param [Boolean] skip
|
|
566
|
+
# Whether to keep an existing middleware file.
|
|
395
567
|
#
|
|
396
568
|
# @example
|
|
397
569
|
# middleware(:hello, "class Hello\nend")
|
|
398
570
|
# #=> generates 'lib/hello_middleware.rb'
|
|
399
571
|
#
|
|
400
|
-
def middleware(name, source)
|
|
401
|
-
create_file destination_root("lib/#{name}_middleware.rb"), source
|
|
572
|
+
def middleware(name, source, skip: false)
|
|
573
|
+
create_file destination_root("lib/#{name}_middleware.rb"), source, skip: skip
|
|
402
574
|
insert_middleware name.to_s.underscore.camelize
|
|
403
575
|
end
|
|
404
576
|
|
|
@@ -436,11 +608,46 @@ module Padrino
|
|
|
436
608
|
#
|
|
437
609
|
def run_bundler
|
|
438
610
|
say 'Bundling application dependencies using bundler...', :yellow
|
|
439
|
-
in_root
|
|
611
|
+
in_root do
|
|
612
|
+
with_project_bundle_env do
|
|
613
|
+
run 'bundle install'
|
|
614
|
+
run 'bundle binstubs --all'
|
|
615
|
+
end
|
|
616
|
+
end
|
|
440
617
|
end
|
|
441
618
|
|
|
619
|
+
# The variables Bundler sets up for its children (Bundler::EnvironmentPreserver::BUNDLER_KEYS).
|
|
620
|
+
BUNDLER_ENV_KEYS = %w[BUNDLE_BIN_PATH BUNDLE_GEMFILE BUNDLE_LOCKFILE BUNDLER_VERSION BUNDLER_SETUP GEM_HOME
|
|
621
|
+
GEM_PATH MANPATH PATH RB_USER_INSTALL RUBYLIB RUBYOPT].freeze
|
|
622
|
+
|
|
442
623
|
##
|
|
443
|
-
#
|
|
624
|
+
# Runs the block with the environment for a command run on the project: the project's Gemfile
|
|
625
|
+
# and lockfile, and none of the Bundler setup (RUBYOPT=-rbundler/setup, BUNDLE_GEMFILE...) that a
|
|
626
|
+
# caller's `bundle exec` or binstub exported. Other variables are passed on as they are. With
|
|
627
|
+
# +setup+, the command loads the project's bundle first, as `bundle exec` would in the project.
|
|
628
|
+
#
|
|
629
|
+
# @param [Boolean] setup
|
|
630
|
+
# Whether the command requires bundler/setup.
|
|
631
|
+
#
|
|
632
|
+
def with_project_bundle_env(setup: false)
|
|
633
|
+
saved = ENV.to_h
|
|
634
|
+
if defined?(Bundler) && Bundler.respond_to?(:original_env)
|
|
635
|
+
original = Bundler.original_env
|
|
636
|
+
# ceiling: a template's own changes to Bundler's keys (PATH, RUBYOPT, GEM_PATH...) made after Bundler loaded
|
|
637
|
+
# don't reach its steps; upgrade: re-apply the diff from a snapshot taken when the runner starts.
|
|
638
|
+
BUNDLER_ENV_KEYS.each { |key| original.key?(key) ? ENV[key] = original[key] : ENV.delete(key) }
|
|
639
|
+
ENV.keys.grep(/\ABUNDLER_ORIG_/).each { |key| ENV.delete(key) }
|
|
640
|
+
end
|
|
641
|
+
ENV['BUNDLE_GEMFILE'] = destination_root('Gemfile')
|
|
642
|
+
ENV['BUNDLE_LOCKFILE'] = destination_root('Gemfile.lock')
|
|
643
|
+
ENV['RUBYOPT'] = ['-rbundler/setup', ENV['RUBYOPT']].compact.join(' ') if setup
|
|
644
|
+
yield
|
|
645
|
+
ensure
|
|
646
|
+
ENV.replace(saved) if saved
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
##
|
|
650
|
+
# Exits with status 1 if the app does not exist.
|
|
444
651
|
#
|
|
445
652
|
# @param [String] app
|
|
446
653
|
# Directory name of application.
|
|
@@ -456,7 +663,7 @@ module Padrino
|
|
|
456
663
|
say "Unable to locate '#{app.underscore.camelize}' application "
|
|
457
664
|
say '================================================================='
|
|
458
665
|
say
|
|
459
|
-
|
|
666
|
+
exit 1
|
|
460
667
|
end
|
|
461
668
|
|
|
462
669
|
##
|
|
@@ -488,7 +695,8 @@ module Padrino
|
|
|
488
695
|
end
|
|
489
696
|
|
|
490
697
|
##
|
|
491
|
-
# Ensures that project name is valid, else
|
|
698
|
+
# Ensures that project name is valid, else raises a Thor::Error: the generator's start prints its
|
|
699
|
+
# message and exits with status 1.
|
|
492
700
|
#
|
|
493
701
|
# @param [String] name
|
|
494
702
|
# Name of project.
|
|
@@ -501,9 +709,9 @@ module Padrino
|
|
|
501
709
|
#
|
|
502
710
|
def valid_constant?(name)
|
|
503
711
|
if name =~ /^\d/
|
|
504
|
-
raise ::
|
|
712
|
+
raise Thor::Error, "Constant name #{name} cannot start with numbers"
|
|
505
713
|
elsif name =~ /^\W/
|
|
506
|
-
raise ::
|
|
714
|
+
raise Thor::Error, "Constant name #{name} cannot start with non-word character"
|
|
507
715
|
end
|
|
508
716
|
end
|
|
509
717
|
|
|
@@ -519,7 +727,7 @@ module Padrino
|
|
|
519
727
|
#
|
|
520
728
|
def validate_namespace(name)
|
|
521
729
|
valid_constant? name
|
|
522
|
-
name.match(/^[[:alnum:]_]+$/) || raise(::
|
|
730
|
+
name.match(/^[[:alnum:]_]+$/) || raise(Thor::Error, "Namespace '#{name}' must consist only of alphanumeric characters or '_'")
|
|
523
731
|
end
|
|
524
732
|
|
|
525
733
|
##
|
|
@@ -580,7 +788,7 @@ module Padrino
|
|
|
580
788
|
[:stylesheet, 'stylesheet engine', { aliases: '-c', default: :none }]
|
|
581
789
|
].each do |name, caption, opts|
|
|
582
790
|
opts[:default] = '' if options[:default] == false
|
|
583
|
-
component_option name, caption, opts.merge(choices: Dir["#{__dir__}/components/#{name.to_s.pluralize}/*.rb"].map { |lib| File.basename(lib, '.rb').to_sym
|
|
791
|
+
component_option name, caption, opts.merge(choices: Dir["#{__dir__}/components/#{name.to_s.pluralize}/*.rb"].map { |lib| File.basename(lib, '.rb') }.sort.map(&:to_sym))
|
|
584
792
|
end
|
|
585
793
|
end
|
|
586
794
|
|
|
@@ -598,6 +806,13 @@ module Padrino
|
|
|
598
806
|
@require_arguments
|
|
599
807
|
end
|
|
600
808
|
|
|
809
|
+
##
|
|
810
|
+
# Tells Thor to exit with status 1 when it rejects the arguments, e.g. a missing required argument.
|
|
811
|
+
#
|
|
812
|
+
def exit_on_failure?
|
|
813
|
+
true
|
|
814
|
+
end
|
|
815
|
+
|
|
601
816
|
##
|
|
602
817
|
# Returns the compiled list of component types which can be specified.
|
|
603
818
|
#
|
|
@@ -4,7 +4,10 @@ module <%= @project_name %>
|
|
|
4
4
|
register Padrino::Mailer
|
|
5
5
|
register Padrino::Helpers
|
|
6
6
|
<% end -%>
|
|
7
|
-
|
|
7
|
+
# The session cookie is Secure (sent over HTTPS only) in production. Over plain HTTP
|
|
8
|
+
# the app then sends no session cookie, so logins and CSRF-protected forms fail. Behind
|
|
9
|
+
# a proxy that terminates TLS, have it send X-Forwarded-Proto: https, or set secure: false.
|
|
10
|
+
set :sessions, same_site: :lax, secure: Padrino.env == :production
|
|
8
11
|
|
|
9
12
|
##
|
|
10
13
|
# 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
|
-
|
|
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
|
-
|
|
70
|
+
exit_outside_app_root!
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
end
|
|
@@ -17,11 +17,18 @@ 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
|
-
|
|
22
|
+
root = File.expand_path(options[:root] || '.')
|
|
23
|
+
ENV['BUNDLE_GEMFILE'] = File.join(root, 'Gemfile') if options[:root]
|
|
24
|
+
boot = File.join(root, 'config/boot.rb')
|
|
23
25
|
if File.exist?(boot)
|
|
24
|
-
|
|
26
|
+
# Boot from the project root, as `padrino start`, rake and the binstubs do, so that its relative paths
|
|
27
|
+
# (such as a sqlite database in config/database.rb) don't resolve against the caller's directory.
|
|
28
|
+
# ceiling: only the boot runs there; a path the project opens lazily after boot (an ActiveRecord connection
|
|
29
|
+
# to a relative path) still resolves against the caller's directory. Running the whole generator from the
|
|
30
|
+
# root would cover it.
|
|
31
|
+
Dir.chdir(root) { require boot }
|
|
25
32
|
else
|
|
26
33
|
require 'padrino-support'
|
|
27
34
|
end
|
|
@@ -29,8 +36,7 @@ module Padrino
|
|
|
29
36
|
puts "=> Problem loading #{boot}"
|
|
30
37
|
puts ["=> #{e.message}", *e.backtrace].join("\n ")
|
|
31
38
|
ensure
|
|
32
|
-
ENV.
|
|
33
|
-
ENV.delete('PADRINO_LOG_LEVEL')
|
|
39
|
+
ENV.update(saved_env) # restores the caller's values, or removes the ones it didn't have
|
|
34
40
|
end
|
|
35
41
|
|
|
36
42
|
##
|
|
@@ -43,10 +49,14 @@ module Padrino
|
|
|
43
49
|
generator_class = Padrino::Generators.mappings[generator_kind]
|
|
44
50
|
|
|
45
51
|
if generator_class
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
if ARGV.empty? && generator_class.require_arguments?
|
|
53
|
+
generator_class.start(['-h'])
|
|
54
|
+
exit 1
|
|
55
|
+
end
|
|
56
|
+
generator_class.start(ARGV)
|
|
48
57
|
else
|
|
49
58
|
puts "Please specify generator to use (#{Padrino::Generators.mappings.keys.join(', ')})"
|
|
59
|
+
exit 1 unless generator_kind == :help
|
|
50
60
|
end
|
|
51
61
|
end
|
|
52
62
|
end
|
|
@@ -17,7 +17,7 @@ module Padrino
|
|
|
17
17
|
|
|
18
18
|
class_option :app, desc: 'The application name', aliases: '-n', default: nil, type: :string
|
|
19
19
|
class_option :root, desc: 'The root destination', aliases: '-r', default: '.', type: :string
|
|
20
|
-
class_option :adapter, desc: 'SQL adapter for ORM (sqlite, mysql, mysql2, mysql-gem, postgres)', aliases: '-a', default:
|
|
20
|
+
class_option :adapter, desc: 'SQL adapter for ORM (sqlite, mysql, mysql2, mysql-gem, postgres)', aliases: '-a', default: nil, type: :string
|
|
21
21
|
|
|
22
22
|
defines_component_options default: false
|
|
23
23
|
|
|
@@ -28,6 +28,7 @@ module Padrino
|
|
|
28
28
|
self.destination_root = options[:root]
|
|
29
29
|
if in_app_root?
|
|
30
30
|
@_components = options.class.new(options.slice(*self.class.component_types))
|
|
31
|
+
@_stored_components = retrieve_component_config(destination_root('.components'))
|
|
31
32
|
@app_name = (options[:app] || 'App').gsub(/\W/, '_').camelize
|
|
32
33
|
if @_components.values.delete_if(&:empty?).empty?
|
|
33
34
|
self.class.start(['-h'])
|
|
@@ -41,30 +42,36 @@ module Padrino
|
|
|
41
42
|
exit
|
|
42
43
|
end
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
# Settle every choice, switch and check before applying any, so a failure leaves the project unchanged.
|
|
46
|
+
chosen = self.class.component_types.reject { |comp| @_components[comp].empty? }
|
|
47
|
+
chosen.each { |comp| @_components[comp] = resolve_valid_choice(comp) }
|
|
48
|
+
chosen.select! do |comp|
|
|
49
|
+
choice = @_components[comp]
|
|
48
50
|
existing = fetch_component_choice(comp)
|
|
49
51
|
ask = existing != 'none' && existing != choice
|
|
50
|
-
|
|
52
|
+
!ask || yes?("Switch #{comp} to '#{choice}' from '#{existing}' ?[yes/no]:")
|
|
53
|
+
end
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
@project_name = fetch_component_choice(:namespace)
|
|
56
|
+
if chosen.include?(:test) && !already_exists?(@app_name, @project_name)
|
|
57
|
+
say "#{@project_name}::#{@app_name} does not exist."
|
|
58
|
+
say 'Please, change app name.'
|
|
59
|
+
exit 1
|
|
60
|
+
end
|
|
61
|
+
resolve_valid_adapter if chosen.include?(:orm) && SQL_ADAPTER_ORMS.include?(@_components[:orm])
|
|
58
62
|
|
|
63
|
+
chosen.each do |comp|
|
|
64
|
+
choice = @_components[comp]
|
|
59
65
|
execute_component_setup(comp, choice)
|
|
60
66
|
store_component_choice(comp, choice)
|
|
67
|
+
store_component_choice(:adapter, resolve_valid_adapter) if comp.to_s == 'orm' && SQL_ADAPTER_ORMS.include?(choice) && !adapter_unknown?
|
|
61
68
|
if comp.to_s == 'orm' && choice.to_s != 'none'
|
|
62
69
|
inject_into_file destination_root('Rakefile'), "PadrinoTasks.use(:database)\n", before: 'PadrinoTasks.init'
|
|
63
70
|
inject_into_file destination_root('Rakefile'), "PadrinoTasks.use(#{choice.to_sym.inspect})\n", before: 'PadrinoTasks.init'
|
|
64
71
|
end
|
|
65
72
|
end
|
|
66
73
|
else
|
|
67
|
-
|
|
74
|
+
exit_outside_app_root!
|
|
68
75
|
end
|
|
69
76
|
end
|
|
70
77
|
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 ?
|
|
32
|
-
column_declarations = field_tuples.
|
|
31
|
+
field_tuples.map! { |field, kind, *modifiers| [field, kind =~ /datetime/i ? 'DateTime' : kind, *modifiers] }
|
|
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 ?
|
|
74
|
-
add_columns = tuples.
|
|
75
|
-
remove_columns = tuples.
|
|
73
|
+
tuples.map! { |field, kind, *modifiers| [field, kind =~ /datetime/i ? 'DateTime' : kind, *modifiers] }
|
|
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))
|
|
@@ -145,7 +145,8 @@ module Padrino
|
|
|
145
145
|
#
|
|
146
146
|
def insert_test_suite_setup(suite_text, options = {})
|
|
147
147
|
options = { path: 'test/test_config.rb' }.update(options)
|
|
148
|
-
|
|
148
|
+
# --app points the setup at another app, so that run rewrites it.
|
|
149
|
+
create_file(options[:path], suite_text.gsub(/CLASS_NAME/, "#{@project_name}::#{@app_name}"), skip: reapplying_component?(:test) && !self.options[:app])
|
|
149
150
|
end
|
|
150
151
|
|
|
151
152
|
##
|