padrino-gen 0.18.0 → 0.18.2
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/lib/padrino-gen/generators/actions.rb +45 -2
- data/lib/padrino-gen/generators/app/app.rb.tt +10 -1
- data/lib/padrino-gen/generators/cli.rb +2 -0
- data/lib/padrino-gen/generators/components/tests/bacon.rb +4 -2
- data/lib/padrino-gen/generators/components/tests/minitest.rb +2 -0
- data/lib/padrino-gen/generators/components/tests/shoulda.rb +2 -0
- data/lib/padrino-gen/generators/components/tests/testunit.rb +2 -0
- data/lib/padrino-gen/generators/project/config/apps.rb.tt +8 -0
- data/lib/padrino-gen/generators/project.rb +2 -1
- data/lib/padrino-gen/padrino-tasks/activerecord.rb +5 -1
- data/lib/padrino-gen.rb +5 -0
- data/padrino-gen.gemspec +8 -3
- data/test/test_activerecord_tasks.rb +16 -0
- data/test/test_app_generator.rb +190 -0
- data/test/test_cli.rb +14 -0
- data/test/test_project_generator.rb +35 -0
- metadata +15 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 86f7ed8ab8be9172114afb80f101283607ef08a843c4914c062722ff0c1daf29
|
|
4
|
+
data.tar.gz: b09bcbe9c28537c856b700181370a3a75b0d2529b97f0b21de9b638c100d8585
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e42eba95dfc30a9687be99f7ad6ab40e5a77a2c07f24a07c565fe693e1914ac87baa6069fa586257d6d053f1f270d114bf370958e2ded3d8e31597e47ef234be
|
|
7
|
+
data.tar.gz: '06467684e1041ed80502f5463faf6c68163f174d3328534f7855d40701dc2f05ee0f5179ab73b6879133076db22ad0c63ab3deba71a151b23b386ed3140f6c6a'
|
|
@@ -666,6 +666,47 @@ module Padrino
|
|
|
666
666
|
exit 1
|
|
667
667
|
end
|
|
668
668
|
|
|
669
|
+
##
|
|
670
|
+
# Returns true for an --api project: no sessions or CSRF protection. The Gemfile is the
|
|
671
|
+
# only record of that: padrino-core, without the padrino gem or the full set of its parts.
|
|
672
|
+
#
|
|
673
|
+
# @return [Boolean] true for an --api project.
|
|
674
|
+
#
|
|
675
|
+
def api_project?
|
|
676
|
+
return true if options[:api]
|
|
677
|
+
|
|
678
|
+
gemfile_gems.include?('padrino-core') && !(padrino_gem?('padrino-helpers') && padrino_gem?('padrino-mailer'))
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
##
|
|
682
|
+
# Returns true when the project loads the named part of padrino, so a generated app can
|
|
683
|
+
# register it. A Gemfile that lists no padrino-core by name keeps the default, every part.
|
|
684
|
+
#
|
|
685
|
+
# @param [String] name
|
|
686
|
+
# The gem name, such as 'padrino-mailer'.
|
|
687
|
+
#
|
|
688
|
+
# @return [Boolean] true when the project loads the gem.
|
|
689
|
+
#
|
|
690
|
+
def padrino_gem?(name)
|
|
691
|
+
return false if options[:api]
|
|
692
|
+
|
|
693
|
+
gems = gemfile_gems
|
|
694
|
+
!gems.include?('padrino-core') || gems.include?('padrino') || gems.include?(name)
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
##
|
|
698
|
+
# Returns the names of the gems the project's Gemfile lists literally, or an empty
|
|
699
|
+
# array without a readable Gemfile. The padrino-#{g} loop of a --dev Gemfile lists none.
|
|
700
|
+
#
|
|
701
|
+
# @return [Array<String>] The gem names.
|
|
702
|
+
#
|
|
703
|
+
def gemfile_gems
|
|
704
|
+
gemfile = destination_root('Gemfile')
|
|
705
|
+
return [] unless File.file?(gemfile) && File.readable?(gemfile)
|
|
706
|
+
|
|
707
|
+
File.binread(gemfile).scan(/^\s*gem[\s(]+['"]([^'"]+)['"]/).flatten
|
|
708
|
+
end
|
|
709
|
+
|
|
669
710
|
##
|
|
670
711
|
# Generates standard and tiny applications within a project.
|
|
671
712
|
#
|
|
@@ -684,8 +725,10 @@ module Padrino
|
|
|
684
725
|
template 'templates/controller.rb.tt', destination_root(app, 'controllers.rb')
|
|
685
726
|
@helper_name = DEFAULT_HELPER_NAME
|
|
686
727
|
template 'templates/helper.rb.tt', destination_root(app, 'helpers.rb')
|
|
687
|
-
|
|
688
|
-
|
|
728
|
+
if padrino_gem?('padrino-mailer')
|
|
729
|
+
@short_name = 'notifier'
|
|
730
|
+
template 'templates/mailer.rb.tt', destination_root(app, 'mailers.rb')
|
|
731
|
+
end
|
|
689
732
|
else
|
|
690
733
|
empty_directory destination_root(app, 'controllers')
|
|
691
734
|
empty_directory destination_root(app, 'helpers')
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
module <%= @project_name %>
|
|
2
2
|
class <%= @app_name %> < Padrino::Application
|
|
3
|
-
<%
|
|
3
|
+
<% if padrino_gem?('padrino-mailer') -%>
|
|
4
4
|
register Padrino::Mailer
|
|
5
|
+
<% end -%>
|
|
6
|
+
<% if padrino_gem?('padrino-helpers') -%>
|
|
5
7
|
register Padrino::Helpers
|
|
6
8
|
<% end -%>
|
|
9
|
+
<% if api_project? -%>
|
|
10
|
+
# CSRF protection follows sessions, because without a session it rejects every request.
|
|
11
|
+
# `enable :sessions` here turns on both, except in the test environment. For another session
|
|
12
|
+
# store, give the :sessions setting `use:` and `config:` options rather than a `use` line.
|
|
13
|
+
set :protect_from_csrf, proc { sessions? && Padrino.env != :test }
|
|
14
|
+
<% else -%>
|
|
7
15
|
# The session cookie is Secure (sent over HTTPS only) in production. Over plain HTTP
|
|
8
16
|
# the app then sends no session cookie, so logins and CSRF-protected forms fail. Behind
|
|
9
17
|
# a proxy that terminates TLS, have it send X-Forwarded-Proto: https, or set secure: false.
|
|
10
18
|
set :sessions, same_site: :lax, secure: Padrino.env == :production
|
|
19
|
+
<% end -%>
|
|
11
20
|
|
|
12
21
|
##
|
|
13
22
|
# Caching support.
|
|
@@ -17,6 +17,7 @@ module Padrino
|
|
|
17
17
|
# custom generators, so is necessary know who are.
|
|
18
18
|
#
|
|
19
19
|
def load_boot
|
|
20
|
+
Padrino::Generators.boot_error = nil
|
|
20
21
|
saved_env = { 'BUNDLE_GEMFILE' => ENV['BUNDLE_GEMFILE'], 'PADRINO_LOG_LEVEL' => ENV['PADRINO_LOG_LEVEL'] }
|
|
21
22
|
ENV['PADRINO_LOG_LEVEL'] ||= 'test'
|
|
22
23
|
root = File.expand_path(options[:root] || '.')
|
|
@@ -33,6 +34,7 @@ module Padrino
|
|
|
33
34
|
require 'padrino-support'
|
|
34
35
|
end
|
|
35
36
|
rescue StandardError => e
|
|
37
|
+
Padrino::Generators.boot_error = e
|
|
36
38
|
puts "=> Problem loading #{boot}"
|
|
37
39
|
puts ["=> #{e.message}", *e.backtrace].join("\n ")
|
|
38
40
|
ensure
|
|
@@ -30,7 +30,8 @@ BACON_CONTROLLER_TEST = <<~TEST.gsub(/^ {10}/, '') unless defined?(BACON_CONTROL
|
|
|
30
30
|
describe "!PATH!" do
|
|
31
31
|
it 'returns text at root' do
|
|
32
32
|
get "!EXPANDED_PATH!"
|
|
33
|
-
|
|
33
|
+
# Bacon reports a spec without requirements as missing. Add some, such as:
|
|
34
|
+
# last_response.body.should == "some text"
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
TEST
|
|
@@ -74,7 +75,8 @@ BACON_HELPER_TEST = <<~TEST unless defined?(BACON_HELPER_TEST)
|
|
|
74
75
|
end
|
|
75
76
|
|
|
76
77
|
it "should return nil" do
|
|
77
|
-
|
|
78
|
+
# Bacon reports a spec without requirements as missing. Add some, or delete this file:
|
|
79
|
+
# @helpers.foo.should.equal nil
|
|
78
80
|
end
|
|
79
81
|
end
|
|
80
82
|
TEST
|
|
@@ -49,6 +49,7 @@ MINITEST_CONTROLLER_TEST = <<~TEST.gsub(/^ {10}/, '') unless defined?(MINITEST_C
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
it "should return hello world text" do
|
|
52
|
+
skip "add some examples to \#{__FILE__}"
|
|
52
53
|
assert_equal "Hello World", last_response.body
|
|
53
54
|
end
|
|
54
55
|
end
|
|
@@ -79,6 +80,7 @@ MINITEST_HELPER_TEST = <<~TEST unless defined?(MINITEST_HELPER_TEST)
|
|
|
79
80
|
end
|
|
80
81
|
|
|
81
82
|
it "should return nil" do
|
|
83
|
+
skip "add some examples to (or delete) \#{__FILE__}"
|
|
82
84
|
assert_nil helpers.foo
|
|
83
85
|
end
|
|
84
86
|
end
|
|
@@ -34,6 +34,7 @@ SHOULDA_CONTROLLER_TEST = <<~TEST.gsub(/^ {10}/, '') unless defined?(SHOULDA_CON
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
should "return hello world text" do
|
|
37
|
+
pend "add some examples to \#{__FILE__}"
|
|
37
38
|
assert_equal "Hello World", last_response.body
|
|
38
39
|
end
|
|
39
40
|
end
|
|
@@ -82,6 +83,7 @@ SHOULDA_HELPER_TEST = <<~TEST unless defined?(SHOULDA_HELPER_TEST)
|
|
|
82
83
|
end
|
|
83
84
|
|
|
84
85
|
should "return nil" do
|
|
86
|
+
pend "add some examples to (or delete) \#{__FILE__}"
|
|
85
87
|
assert_equal nil, @helpers.foo
|
|
86
88
|
end
|
|
87
89
|
end
|
|
@@ -49,6 +49,7 @@ TESTUNIT_CONTROLLER_TEST = <<~TEST.gsub(/^ {10}/, '') unless defined?(TESTUNIT_C
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def test_returns_hello_world_text
|
|
52
|
+
pend "add some examples to \#{__FILE__}"
|
|
52
53
|
assert_equal "Hello World", last_response.body
|
|
53
54
|
end
|
|
54
55
|
end
|
|
@@ -79,6 +80,7 @@ TESTUNIT_HELPER_TEST = <<~TEST unless defined?(TESTUNIT_HELPER_TEST)
|
|
|
79
80
|
end
|
|
80
81
|
|
|
81
82
|
def test_foo_helper
|
|
83
|
+
pend "add some examples to (or delete) \#{__FILE__}"
|
|
82
84
|
assert_equal nil, helpers.foo
|
|
83
85
|
end
|
|
84
86
|
end
|
|
@@ -32,9 +32,17 @@ Padrino.configure_apps do
|
|
|
32
32
|
# a fallback, so set it in production.
|
|
33
33
|
set :session_secret, ENV.fetch('SESSION_SECRET') { '<%= SecureRandom.hex(32) %>' }
|
|
34
34
|
set :protection, except: :path_traversal
|
|
35
|
+
<% if options.api? -%>
|
|
36
|
+
# CSRF protection follows sessions, because without a session it rejects every request.
|
|
37
|
+
# `enable :sessions` above or in an app turns on both, except in the test environment.
|
|
38
|
+
# For another session store, give the :sessions setting `use:` and `config:` options, here
|
|
39
|
+
# or in an app, rather than adding a session middleware in config.ru.
|
|
40
|
+
set :protect_from_csrf, proc { sessions? && Padrino.env != :test }
|
|
41
|
+
<% else -%>
|
|
35
42
|
# CSRF protection is off in the test environment so test requests don't need a
|
|
36
43
|
# token. To test it, `enable :protect_from_csrf` in an app.
|
|
37
44
|
set :protect_from_csrf, Padrino.env != :test
|
|
45
|
+
<% end -%>
|
|
38
46
|
end
|
|
39
47
|
|
|
40
48
|
# Mounts the core application for this project
|
|
@@ -116,7 +116,8 @@ module Padrino
|
|
|
116
116
|
return unless defined?(controller_template_name)
|
|
117
117
|
|
|
118
118
|
controller_content = instance_eval(controller_template_name).gsub(/!PATH!/, 'Controller').gsub(/!NAME!/, '').gsub(/!EXPANDED_PATH!/, '/')
|
|
119
|
-
|
|
119
|
+
helper_name = "#{@project_name}::#{@app_name}::#{DEFAULT_HELPER_NAME}"
|
|
120
|
+
helper_content = instance_eval(helper_template_name).gsub(/!NAME!/, helper_name).gsub(/!CONSTANT_NAME!/, helper_name)
|
|
120
121
|
|
|
121
122
|
proc { |*args| args.map { |str| str.gsub!(/!PATH!/, recognize_path) } }.call(controller_content, helper_content)
|
|
122
123
|
|
|
@@ -242,7 +242,10 @@ if PadrinoTasks.load?(:activerecord, defined?(ActiveRecord))
|
|
|
242
242
|
desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR.'
|
|
243
243
|
task dump: :skeleton do
|
|
244
244
|
require 'active_record/schema_dumper'
|
|
245
|
-
|
|
245
|
+
# A project on a database server has no db/ until its first migration is generated.
|
|
246
|
+
schema_file = ENV['SCHEMA'] || Padrino.root('db', 'schema.rb')
|
|
247
|
+
FileUtils.mkdir_p(File.dirname(schema_file))
|
|
248
|
+
File.open(schema_file, 'w') do |file|
|
|
246
249
|
ActiveRecord::SchemaDumper.dump(schema_dumper_source, file)
|
|
247
250
|
end
|
|
248
251
|
Rake::Task['ar:schema:dump'].reenable
|
|
@@ -261,6 +264,7 @@ if PadrinoTasks.load?(:activerecord, defined?(ActiveRecord))
|
|
|
261
264
|
namespace :structure do
|
|
262
265
|
desc 'Dump the database structure to a SQL file.'
|
|
263
266
|
task dump: :skeleton do
|
|
267
|
+
FileUtils.mkdir_p(File.dirname(resolve_structure_sql))
|
|
264
268
|
with_database(Padrino.env) do |config|
|
|
265
269
|
case config[:adapter]
|
|
266
270
|
when 'mysql', 'mysql2', 'em_mysql2', 'oci', 'oracle', 'jdbcmysql'
|
data/lib/padrino-gen.rb
CHANGED
|
@@ -23,6 +23,11 @@ module Padrino
|
|
|
23
23
|
DEV_PATH = File.expand_path('../../', __dir__)
|
|
24
24
|
|
|
25
25
|
class << self
|
|
26
|
+
##
|
|
27
|
+
# The error the project's config/boot.rb raised when the generator CLI loaded it, or nil.
|
|
28
|
+
#
|
|
29
|
+
attr_accessor :boot_error
|
|
30
|
+
|
|
26
31
|
##
|
|
27
32
|
# Store our generators paths.
|
|
28
33
|
#
|
data/padrino-gen.gemspec
CHANGED
|
@@ -8,16 +8,21 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
s.authors = ['Padrino Team', 'Nathan Esquenazi', "Davide D'Agostino", 'Arthur Chiu']
|
|
9
9
|
s.email = 'padrinorb@gmail.com'
|
|
10
10
|
s.summary = 'Generators for easily creating and building padrino applications'
|
|
11
|
-
s.homepage = '
|
|
11
|
+
s.homepage = 'https://padrinorb.com'
|
|
12
12
|
s.description = 'Generators for easily creating and building padrino applications from the console'
|
|
13
|
+
s.required_ruby_version = '>= 2.7.8'
|
|
13
14
|
s.required_rubygems_version = '>= 1.3.6'
|
|
14
15
|
s.version = Padrino.version
|
|
15
|
-
s.date = Time.now.strftime('%Y-%m-%d')
|
|
16
16
|
s.license = 'MIT'
|
|
17
|
+
s.metadata = {
|
|
18
|
+
'bug_tracker_uri' => 'https://github.com/padrino/padrino-framework/issues',
|
|
19
|
+
'changelog_uri' => 'https://github.com/padrino/padrino-framework/blob/master/CHANGES.rdoc',
|
|
20
|
+
'documentation_uri' => 'https://padrinorb.com/guides/',
|
|
21
|
+
'source_code_uri' => 'https://github.com/padrino/padrino-framework'
|
|
22
|
+
}
|
|
17
23
|
|
|
18
24
|
s.extra_rdoc_files = Dir['*.rdoc']
|
|
19
25
|
s.files = `git ls-files`.split("\n") | Dir.glob('{lib}/**/*')
|
|
20
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
21
26
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
|
22
27
|
s.require_paths = ['lib']
|
|
23
28
|
s.rdoc_options = ['--charset=UTF-8']
|
|
@@ -142,6 +142,13 @@ describe 'ActiveRecord tasks' do
|
|
|
142
142
|
assert_match(/create_table "widgets"/, schema)
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
+
it 'should migrate a new project on a server database, which has no db directory yet' do
|
|
146
|
+
FileUtils.rm_rf(File.join(@root, 'db'))
|
|
147
|
+
File.write(File.join(@root, 'config', 'boot.rb'), File.read(File.join(@root, 'config', 'boot.rb')).sub("Padrino.root('db', 'development.db')", "Padrino.root('development.db')"))
|
|
148
|
+
rake('ar:migrate')
|
|
149
|
+
assert_includes schema, 'ActiveRecord::Schema'
|
|
150
|
+
end
|
|
151
|
+
|
|
145
152
|
it 'should dump the schema after ar:migrate:down and ar:migrate:up' do
|
|
146
153
|
rake 'ar:migrate'
|
|
147
154
|
rake 'ar:migrate:down', 'MIGRATION_VERSION=1'
|
|
@@ -163,6 +170,15 @@ describe 'ActiveRecord tasks' do
|
|
|
163
170
|
assert_match(/INSERT INTO "schema_migrations" \(version\) VALUES\s*\('1'\)/, structure)
|
|
164
171
|
end
|
|
165
172
|
|
|
173
|
+
it 'should dump the structure when there is no db directory yet' do
|
|
174
|
+
skip_without_sqlite_cli
|
|
175
|
+
File.write(File.join(@root, 'config', 'boot.rb'), File.read(File.join(@root, 'config', 'boot.rb')).sub("Padrino.root('db', 'development.db')", "Padrino.root('development.db')"))
|
|
176
|
+
rake 'ar:migrate'
|
|
177
|
+
FileUtils.rm_rf(File.join(@root, 'db'))
|
|
178
|
+
rake 'ar:structure:dump'
|
|
179
|
+
assert_file_exists File.join(@root, 'db', 'development_structure.sql')
|
|
180
|
+
end
|
|
181
|
+
|
|
166
182
|
def rake_failing(*args)
|
|
167
183
|
libs = %w[padrino-core padrino-gen].flat_map { |name| ['-I', File.expand_path("../../#{name}/lib", __dir__)] }
|
|
168
184
|
env = { 'RACK_ENV' => 'development', 'BUNDLE_GEMFILE' => File.expand_path('../../Gemfile', __dir__) }
|
data/test/test_app_generator.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative 'helper'
|
|
2
|
+
require 'open3'
|
|
2
3
|
|
|
3
4
|
describe 'AppGenerator' do
|
|
4
5
|
def setup
|
|
@@ -66,6 +67,195 @@ describe 'AppGenerator' do
|
|
|
66
67
|
assert_match_in_file(/^ set :session_secret, ENV\.fetch\('SESSION_SECRET'\) \{ '\h{64}' \}$/, "#{@apptmp}/sample_project/config/apps.rb")
|
|
67
68
|
end
|
|
68
69
|
|
|
70
|
+
it 'should write an app without mailer, helpers, sessions or CSRF in an api project' do
|
|
71
|
+
capture_io { generate(:project, 'sample_project', '--api', "--root=#{@apptmp}") }
|
|
72
|
+
capture_io { generate(:app, 'demo', "--root=#{@apptmp}/sample_project") }
|
|
73
|
+
app_file = "#{@apptmp}/sample_project/demo/app.rb"
|
|
74
|
+
assert_no_match_in_file(/Padrino::(Mailer|Helpers)/, app_file)
|
|
75
|
+
assert_no_match_in_file(/set :sessions/, app_file)
|
|
76
|
+
# An older api project turns CSRF on for every app, so the app settles it itself.
|
|
77
|
+
assert_match_in_file(/^\s*set :protect_from_csrf, proc \{ sessions\? && Padrino\.env != :test \}$/, app_file)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'should write a tiny app without mailers in an api project' do
|
|
81
|
+
capture_io { generate(:project, 'sample_project', '--api', '--lean', "--root=#{@apptmp}") }
|
|
82
|
+
capture_io { generate(:app, 'demo', '--tiny', "--root=#{@apptmp}/sample_project") }
|
|
83
|
+
assert_no_match_in_file(/Padrino::(Mailer|Helpers)/, "#{@apptmp}/sample_project/demo/app.rb")
|
|
84
|
+
assert_no_file_exists("#{@apptmp}/sample_project/demo/mailers.rb")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'should keep mailer, helpers and sessions in a project with the padrino gem' do
|
|
88
|
+
capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}") }
|
|
89
|
+
capture_io { generate(:app, 'demo', '--tiny', "--root=#{@apptmp}/sample_project") }
|
|
90
|
+
app_file = "#{@apptmp}/sample_project/demo/app.rb"
|
|
91
|
+
assert_match_in_file(/register Padrino::Mailer/, app_file)
|
|
92
|
+
assert_match_in_file(/register Padrino::Helpers/, app_file)
|
|
93
|
+
assert_match_in_file(/set :sessions/, app_file)
|
|
94
|
+
assert_no_match_in_file(/protect_from_csrf/, app_file)
|
|
95
|
+
assert_file_exists("#{@apptmp}/sample_project/demo/mailers.rb")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'should keep the default app in a project without a Gemfile' do
|
|
99
|
+
capture_io { generate(:project, 'sample_project', '--api', "--root=#{@apptmp}") }
|
|
100
|
+
File.delete("#{@apptmp}/sample_project/Gemfile")
|
|
101
|
+
capture_io { generate(:app, 'demo', "--root=#{@apptmp}/sample_project") }
|
|
102
|
+
app_file = "#{@apptmp}/sample_project/demo/app.rb"
|
|
103
|
+
assert_match_in_file(/register Padrino::Mailer/, app_file)
|
|
104
|
+
assert_match_in_file(/register Padrino::Helpers/, app_file)
|
|
105
|
+
assert_match_in_file(/set :sessions/, app_file)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'should keep the default app in a --dev project, whose Gemfile lists padrino in a loop' do
|
|
109
|
+
capture_io { generate(:project, 'sample_project', '--dev', "--root=#{@apptmp}") }
|
|
110
|
+
capture_io { generate(:app, 'demo', '--tiny', "--root=#{@apptmp}/sample_project") }
|
|
111
|
+
app_file = "#{@apptmp}/sample_project/demo/app.rb"
|
|
112
|
+
assert_match_in_file(/register Padrino::Mailer/, app_file)
|
|
113
|
+
assert_match_in_file(/register Padrino::Helpers/, app_file)
|
|
114
|
+
assert_match_in_file(/set :sessions/, app_file)
|
|
115
|
+
assert_file_exists("#{@apptmp}/sample_project/demo/mailers.rb")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'should keep the default app in a project that lists every padrino gem by name' do
|
|
119
|
+
capture_io { generate(:project, 'sample_project', '--api', "--root=#{@apptmp}") }
|
|
120
|
+
File.write("#{@apptmp}/sample_project/Gemfile", "gem 'padrino-helpers'\ngem 'padrino-mailer'\n", mode: 'a')
|
|
121
|
+
capture_io { generate(:app, 'demo', "--root=#{@apptmp}/sample_project") }
|
|
122
|
+
assert_match_in_file(/set :sessions/, "#{@apptmp}/sample_project/demo/app.rb")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Boots the generated project in a process that loads only the gems its Gemfile names, as
|
|
126
|
+
# config/boot.rb would. Returns the output, ending in the status of a POST without a token.
|
|
127
|
+
def boot_project(project, *gems, env: 'development', path: '/demo/missing')
|
|
128
|
+
script = <<~RUBY
|
|
129
|
+
PADRINO_ROOT = #{project.inspect}
|
|
130
|
+
RACK_ENV = #{env.inspect}
|
|
131
|
+
require 'padrino-core'
|
|
132
|
+
#{gems.map { |gem| "require '#{gem}'" }.join("\n")}
|
|
133
|
+
require 'rack/mock'
|
|
134
|
+
require File.join(PADRINO_ROOT, 'config/apps.rb')
|
|
135
|
+
Padrino.load!
|
|
136
|
+
puts "status=\#{Rack::MockRequest.new(Padrino.application).post(#{path.inspect}).status}"
|
|
137
|
+
RUBY
|
|
138
|
+
out, = Open3.capture2e({ 'RACK_ENV' => env }, RbConfig.ruby, '-rbundler/setup', '-e', script)
|
|
139
|
+
out
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
{ 'nothing' => [], 'padrino-helpers' => %w[padrino-helpers], 'padrino-mailer' => %w[padrino-mailer] }.each do |added, gems|
|
|
143
|
+
[false, true].each do |tiny|
|
|
144
|
+
it "should boot #{tiny ? 'a tiny' : 'an'} app in an api project with #{added} added to its Gemfile" do
|
|
145
|
+
project = "#{@apptmp}/sample_project"
|
|
146
|
+
capture_io { generate(:project, 'sample_project', '--api', "--root=#{@apptmp}") }
|
|
147
|
+
File.write("#{project}/Gemfile", gems.map { |gem| "gem '#{gem}'\n" }.join, mode: 'a')
|
|
148
|
+
capture_io { generate(:app, 'demo', *(tiny ? ['--tiny'] : []), "--root=#{project}") }
|
|
149
|
+
assert_match(/status=404$/, boot_project(project, *gems))
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe 'CSRF protection in an api project' do
|
|
155
|
+
def setup
|
|
156
|
+
super
|
|
157
|
+
@project = "#{@apptmp}/sample_project"
|
|
158
|
+
capture_io { generate(:project, 'sample_project', '--api', "--root=#{@apptmp}") }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def generate_app_with_route(name = 'demo')
|
|
162
|
+
capture_io { generate(:app, name, "--root=#{@project}") }
|
|
163
|
+
File.write("#{@project}/#{name}/controllers/ping.rb", "SampleProject::#{name.capitalize}.controllers { post('/ping') { 'posted' } }\n")
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def edit(file, pattern, replacement)
|
|
167
|
+
File.write(file, File.read(file).sub(pattern, replacement))
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'should accept a POST without a token while sessions are off' do
|
|
171
|
+
generate_app_with_route
|
|
172
|
+
assert_match(/status=200$/, boot_project(@project, path: '/demo/ping'))
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it 'should reject a POST without a token after enabling sessions in the app' do
|
|
176
|
+
generate_app_with_route
|
|
177
|
+
edit("#{@project}/demo/app.rb", /^.*set :protect_from_csrf.*$/, "\\0\n enable :sessions")
|
|
178
|
+
assert_match(/status=403$/, boot_project(@project, path: '/demo/ping'))
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it 'should reject a POST without a token after enabling sessions in config/apps.rb' do
|
|
182
|
+
generate_app_with_route
|
|
183
|
+
edit("#{@project}/config/apps.rb", '# enable :sessions', 'enable :sessions')
|
|
184
|
+
assert_match(/status=403$/, boot_project(@project, path: '/demo/ping'))
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it 'should keep the test environment exemption with sessions enabled' do
|
|
188
|
+
generate_app_with_route
|
|
189
|
+
edit("#{@project}/config/apps.rb", '# enable :sessions', 'enable :sessions')
|
|
190
|
+
assert_match(/status=200$/, boot_project(@project, env: 'test', path: '/demo/ping'))
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
{ "gem 'padrino'" => %w[padrino], "gem 'padrino-helpers'\ngem 'padrino-mailer'" => %w[padrino-helpers padrino-mailer] }.each do |lines, gems|
|
|
194
|
+
it "should protect an app with sessions after adding #{gems.join(' and ')} to the Gemfile" do
|
|
195
|
+
generate_app_with_route
|
|
196
|
+
File.write("#{@project}/Gemfile", "#{lines}\n", mode: 'a')
|
|
197
|
+
generate_app_with_route('web')
|
|
198
|
+
assert_match_in_file(/set :sessions/, "#{@project}/web/app.rb")
|
|
199
|
+
assert_match(/status=403$/, boot_project(@project, *gems, path: '/web/ping'))
|
|
200
|
+
assert_match(/status=200$/, boot_project(@project, *gems, path: '/demo/ping'))
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it 'should accept a POST to a new app in an older project, which turns CSRF on for every app' do
|
|
205
|
+
edit("#{@project}/config/apps.rb", /^.*set :protect_from_csrf.*$/, ' set :protect_from_csrf, Padrino.env != :test')
|
|
206
|
+
generate_app_with_route
|
|
207
|
+
assert_match(/status=200$/, boot_project(@project, path: '/demo/ping'))
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
describe 'reading the Gemfile of an api project' do
|
|
212
|
+
def setup
|
|
213
|
+
super
|
|
214
|
+
@project = "#{@apptmp}/sample_project"
|
|
215
|
+
capture_io { generate(:project, 'sample_project', '--api', "--root=#{@apptmp}") }
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def assert_api_app
|
|
219
|
+
app_file = "#{@project}/demo/app.rb"
|
|
220
|
+
assert_no_match_in_file(/Padrino::(Mailer|Helpers)/, app_file)
|
|
221
|
+
assert_no_match_in_file(/set :sessions/, app_file)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def prepend_to_gemfile(bytes)
|
|
225
|
+
gemfile = "#{@project}/Gemfile"
|
|
226
|
+
File.binwrite(gemfile, bytes.b + File.binread(gemfile))
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it 'should read a Latin-1 Gemfile' do
|
|
230
|
+
prepend_to_gemfile("# Maintainer: Jos\xE9\n")
|
|
231
|
+
capture_io { generate(:app, 'demo', "--root=#{@project}") }
|
|
232
|
+
assert_api_app
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'should read a UTF-8 Gemfile under an ASCII locale' do
|
|
236
|
+
prepend_to_gemfile("# Maintainer: Jos\u00E9\n")
|
|
237
|
+
external = Encoding.default_external
|
|
238
|
+
begin
|
|
239
|
+
capture_io do
|
|
240
|
+
Encoding.default_external = Encoding::US_ASCII
|
|
241
|
+
generate(:app, 'demo', "--root=#{@project}")
|
|
242
|
+
end
|
|
243
|
+
ensure
|
|
244
|
+
Encoding.default_external = external
|
|
245
|
+
end
|
|
246
|
+
assert_api_app
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
it 'should read a parenthesised gem declaration' do
|
|
250
|
+
gemfile = "#{@project}/Gemfile"
|
|
251
|
+
File.write(gemfile, File.read(gemfile).gsub(/^gem 'padrino-(\w+)'(.*)$/, "gem('padrino-\\1'\\2)"))
|
|
252
|
+
assert_match_in_file(/^gem\('padrino-core'/, gemfile)
|
|
253
|
+
capture_io { generate(:app, 'demo', "--root=#{@project}") }
|
|
254
|
+
assert_api_app
|
|
255
|
+
assert_match(/status=404$/, boot_project(@project))
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
69
259
|
it 'should generate tiny app skeleton' do
|
|
70
260
|
capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}") }
|
|
71
261
|
capture_io { generate(:app, 'demo', '--tiny', "--root=#{@apptmp}/sample_project") }
|
data/test/test_cli.rb
CHANGED
|
@@ -77,6 +77,20 @@ describe 'Cli' do
|
|
|
77
77
|
$padrino_test_boot = nil
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
it 'should keep the error of a project boot that fails for the generators' do
|
|
81
|
+
FileUtils.mkdir_p(File.join(@apptmp, 'config'))
|
|
82
|
+
File.write(File.join(@apptmp, 'config/boot.rb'), "MissingBootConstant\n")
|
|
83
|
+
out, = capture_io { Padrino::Generators::Cli.new([], root: @apptmp).load_boot }
|
|
84
|
+
assert_match(/Problem loading/, out)
|
|
85
|
+
assert_match(/MissingBootConstant/, Padrino::Generators.boot_error.message)
|
|
86
|
+
|
|
87
|
+
File.write(File.join(@apptmp, 'config/boot.rb'), "\n")
|
|
88
|
+
capture_io { Padrino::Generators::Cli.new([], root: @apptmp).load_boot }
|
|
89
|
+
assert_nil Padrino::Generators.boot_error
|
|
90
|
+
ensure
|
|
91
|
+
Padrino::Generators.boot_error = nil
|
|
92
|
+
end
|
|
93
|
+
|
|
80
94
|
it 'should give back the environment variables it sets while loading the project boot' do
|
|
81
95
|
saved = { 'BUNDLE_GEMFILE' => ENV['BUNDLE_GEMFILE'], 'PADRINO_LOG_LEVEL' => ENV['PADRINO_LOG_LEVEL'] }
|
|
82
96
|
ENV['BUNDLE_GEMFILE'] = '/caller/Gemfile'
|
|
@@ -164,6 +164,17 @@ describe 'ProjectGenerator' do
|
|
|
164
164
|
assert_match('', err)
|
|
165
165
|
end
|
|
166
166
|
|
|
167
|
+
it 'should generate an api project without sessions or CSRF protection' do
|
|
168
|
+
capture_io { generate(:project, 'sample_project', '--api', "--root=#{@apptmp}") }
|
|
169
|
+
assert_no_match_in_file(/set :sessions/, "#{@apptmp}/sample_project/app/app.rb")
|
|
170
|
+
assert_match_in_file(/^\s*set :protect_from_csrf, proc \{ sessions\? && Padrino\.env != :test \}$/, "#{@apptmp}/sample_project/config/apps.rb")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it 'should generate a tiny api project without mailers' do
|
|
174
|
+
capture_io { generate(:project, 'sample_project', '--api', '--tiny', "--root=#{@apptmp}") }
|
|
175
|
+
assert_no_file_exists("#{@apptmp}/sample_project/app/mailers.rb")
|
|
176
|
+
end
|
|
177
|
+
|
|
167
178
|
it 'should generate tiny skeleton' do
|
|
168
179
|
capture_io { generate(:project, 'sample_project', '--tiny', "--root=#{@apptmp}") }
|
|
169
180
|
assert_dir_exists("#{@apptmp}/sample_project")
|
|
@@ -878,6 +889,30 @@ describe 'ProjectGenerator' do
|
|
|
878
889
|
end
|
|
879
890
|
|
|
880
891
|
describe 'tiny app skeleton generator for test component' do
|
|
892
|
+
{ 'minitest' => /skip "add some examples/, 'testunit' => /pend "add some examples/, 'shoulda' => /pend "add some examples/ }.each do |test, pending|
|
|
893
|
+
it "should mark the tiny skeleton's #{test} placeholders pending, so a new project passes its own tests" do
|
|
894
|
+
capture_io { generate(:project, 'sample_project', '--tiny', "--root=#{@apptmp}", "--test=#{test}") }
|
|
895
|
+
%w[controllers/controllers_test.rb helpers/helpers_test.rb].each do |file|
|
|
896
|
+
assert_match_in_file(pending, "#{@apptmp}/sample_project/test/app/#{file}")
|
|
897
|
+
end
|
|
898
|
+
end
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
it "should leave the tiny skeleton's bacon placeholders without requirements, which bacon reports as missing" do
|
|
902
|
+
capture_io { generate(:project, 'sample_project', '--tiny', "--root=#{@apptmp}", '--test=bacon') }
|
|
903
|
+
%w[controllers/controllers_test.rb helpers/helpers_test.rb].each do |file|
|
|
904
|
+
assert_no_match_in_file(/^\s*[^#\s].*\.should/, "#{@apptmp}/sample_project/test/app/#{file}")
|
|
905
|
+
end
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
%w[bacon minitest rspec shoulda testunit].each do |test|
|
|
909
|
+
it "should substitute every marker in the tiny skeleton's #{test} tests" do
|
|
910
|
+
capture_io { generate(:project, 'sample_project', '--tiny', "--root=#{@apptmp}", "--test=#{test}") }
|
|
911
|
+
dir = test == 'rspec' ? 'spec' : 'test'
|
|
912
|
+
Dir["#{@apptmp}/sample_project/#{dir}/app/**/*.rb"].each { |file| assert_no_match_in_file(/![A-Z_]+!/, file) }
|
|
913
|
+
end
|
|
914
|
+
end
|
|
915
|
+
|
|
881
916
|
it 'should properly generate for rspec' do
|
|
882
917
|
capture_io { generate(:project, 'sample_project', '--tiny', "--root=#{@apptmp}", '--test=rspec') }
|
|
883
918
|
assert_dir_exists("#{@apptmp}/sample_project/spec/")
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: padrino-gen
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.18.
|
|
4
|
+
version: 0.18.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Padrino Team
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date: 2026-
|
|
14
|
+
date: 2026-11-01 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: padrino-core
|
|
@@ -19,14 +19,14 @@ dependencies:
|
|
|
19
19
|
requirements:
|
|
20
20
|
- - '='
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
|
-
version: 0.18.
|
|
22
|
+
version: 0.18.2
|
|
23
23
|
type: :runtime
|
|
24
24
|
prerelease: false
|
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
26
|
requirements:
|
|
27
27
|
- - '='
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: 0.18.
|
|
29
|
+
version: 0.18.2
|
|
30
30
|
- !ruby/object:Gem::Dependency
|
|
31
31
|
name: bundler
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,28 +53,28 @@ dependencies:
|
|
|
53
53
|
requirements:
|
|
54
54
|
- - '='
|
|
55
55
|
- !ruby/object:Gem::Version
|
|
56
|
-
version: 0.18.
|
|
56
|
+
version: 0.18.2
|
|
57
57
|
type: :development
|
|
58
58
|
prerelease: false
|
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
|
60
60
|
requirements:
|
|
61
61
|
- - '='
|
|
62
62
|
- !ruby/object:Gem::Version
|
|
63
|
-
version: 0.18.
|
|
63
|
+
version: 0.18.2
|
|
64
64
|
- !ruby/object:Gem::Dependency
|
|
65
65
|
name: padrino-mailer
|
|
66
66
|
requirement: !ruby/object:Gem::Requirement
|
|
67
67
|
requirements:
|
|
68
68
|
- - '='
|
|
69
69
|
- !ruby/object:Gem::Version
|
|
70
|
-
version: 0.18.
|
|
70
|
+
version: 0.18.2
|
|
71
71
|
type: :development
|
|
72
72
|
prerelease: false
|
|
73
73
|
version_requirements: !ruby/object:Gem::Requirement
|
|
74
74
|
requirements:
|
|
75
75
|
- - '='
|
|
76
76
|
- !ruby/object:Gem::Version
|
|
77
|
-
version: 0.18.
|
|
77
|
+
version: 0.18.2
|
|
78
78
|
description: Generators for easily creating and building padrino applications from
|
|
79
79
|
the console
|
|
80
80
|
email: padrinorb@gmail.com
|
|
@@ -193,10 +193,14 @@ files:
|
|
|
193
193
|
- test/test_sql_helpers.rb
|
|
194
194
|
- test/test_sql_helpers_dump.rb
|
|
195
195
|
- test/test_task_generator.rb
|
|
196
|
-
homepage:
|
|
196
|
+
homepage: https://padrinorb.com
|
|
197
197
|
licenses:
|
|
198
198
|
- MIT
|
|
199
|
-
metadata:
|
|
199
|
+
metadata:
|
|
200
|
+
bug_tracker_uri: https://github.com/padrino/padrino-framework/issues
|
|
201
|
+
changelog_uri: https://github.com/padrino/padrino-framework/blob/master/CHANGES.rdoc
|
|
202
|
+
documentation_uri: https://padrinorb.com/guides/
|
|
203
|
+
source_code_uri: https://github.com/padrino/padrino-framework
|
|
200
204
|
post_install_message:
|
|
201
205
|
rdoc_options:
|
|
202
206
|
- "--charset=UTF-8"
|
|
@@ -206,7 +210,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
206
210
|
requirements:
|
|
207
211
|
- - ">="
|
|
208
212
|
- !ruby/object:Gem::Version
|
|
209
|
-
version:
|
|
213
|
+
version: 2.7.8
|
|
210
214
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
215
|
requirements:
|
|
212
216
|
- - ">="
|