padrino-core 0.16.1 → 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.
- checksums.yaml +4 -4
- data/lib/padrino-core/application/application_setup.rb +1 -0
- data/lib/padrino-core/application/routing.rb +10 -6
- data/lib/padrino-core/cli/base.rb +9 -2
- data/lib/padrino-core/cli/binstub.rb +1 -1
- data/lib/padrino-core/cli/console.rb +1 -1
- data/lib/padrino-core/cli/launcher.rb +3 -3
- data/lib/padrino-core/cli/rake.rb +0 -2
- data/lib/padrino-core/cli/rake_tasks.rb +14 -4
- data/lib/padrino-core/loader.rb +2 -2
- data/lib/padrino-core/logger.rb +63 -14
- data/lib/padrino-core/mounter/application_extension.rb +1 -1
- data/lib/padrino-core/mounter.rb +22 -10
- data/lib/padrino-core/path_router/compiler.rb +1 -1
- data/lib/padrino-core/reloader.rb +91 -3
- data/lib/padrino-core/server.rb +3 -3
- data/lib/padrino-core/version.rb +1 -1
- data/padrino-core.gemspec +1 -0
- data/test/fixtures/apps/external_constants/app.rb +7 -0
- data/test/fixtures/apps/external_constants/helpers/external_constants_helpers.rb +4 -0
- data/test/fixtures/dependencies/autoload/a.rb +5 -0
- data/test/fixtures/dependencies/autoload/b.rb +2 -0
- data/test/fixtures/dependencies/autoload/lazy.rb +2 -0
- data/test/fixtures/dependencies/gc/t.rb +5 -0
- data/test/fixtures/dependencies/gc/u.rb +3 -0
- data/test/fixtures/dependencies/gc/v.rb +10 -0
- data/test/fixtures/dependencies/gc/w.rb +2 -0
- data/test/fixtures/dependencies/gc/x.rb +2 -0
- data/test/fixtures/dependencies/gc/y.rb +2 -0
- data/test/fixtures/dependencies/parent/admin.rb +5 -0
- data/test/fixtures/dependencies/parent/trackable.rb +2 -0
- data/test/fixtures/dependencies/parent/user.rb +2 -0
- data/test/fixtures/dependencies/partial/a.rb +3 -0
- data/test/fixtures/dependencies/partial/b.rb +2 -0
- data/test/fixtures/dependencies/reload/forced.rb +1 -0
- data/test/fixtures/dependencies/reload/untracked.rb +3 -0
- data/test/fixtures/dependencies/reload/untracked_forced.rb +3 -0
- data/test/fixtures/dependencies/untracked/a.rb +2 -0
- data/test/fixtures/dependencies/untracked/b.rb +2 -0
- data/test/helper.rb +21 -0
- data/test/test_binstub.rb +21 -0
- data/test/test_cli.rb +110 -0
- data/test/test_dependencies.rb +163 -0
- data/test/test_launcher.rb +51 -0
- data/test/test_logger.rb +136 -5
- data/test/test_mounter.rb +17 -0
- data/test/test_rake_tasks.rb +37 -0
- data/test/test_reloader_complex.rb +3 -0
- data/test/test_reloader_external.rb +3 -0
- data/test/test_reloader_simple.rb +67 -0
- data/test/test_reloader_system.rb +32 -0
- data/test/test_routing.rb +62 -1
- data/test/test_server.rb +32 -0
- metadata +70 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 44a11b5c0a503449fdce5f86ea6ceff3c6fe7f33e7d28223983b8507c1975aca
|
|
4
|
+
data.tar.gz: b3dcecde6843769f9b74336e1b94d1ecbd5cd2d2ae607d5982c2380be1452085
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dbad97028d154c692aa99e3f9d7b68301c69da4843e905cbafbd59bd2b24026186efe0eaadcdb0df0bc2aff14d803952d507efa5153988d5ede8b29a7d2de60e
|
|
7
|
+
data.tar.gz: f9dca7338b15c2276b2546afc826f9e4c39eb2ef8f226dc90fa2666d4ce3b3a2814230aea1f23f4e99985ae45f76bd8c5068e4c5918f8c7aa5fb5b11522aa3d5
|
|
@@ -341,7 +341,7 @@ module Padrino
|
|
|
341
341
|
params = args.last.is_a?(Hash) ? args.pop : {}
|
|
342
342
|
fragment = params.delete(:fragment) || params.delete(:anchor)
|
|
343
343
|
path = make_path_with_params(args, value_to_param(params))
|
|
344
|
-
rebase_url(fragment ? path
|
|
344
|
+
rebase_url(fragment ? "#{path}##{fragment}" : path)
|
|
345
345
|
end
|
|
346
346
|
alias url_for url
|
|
347
347
|
|
|
@@ -372,7 +372,7 @@ module Padrino
|
|
|
372
372
|
|
|
373
373
|
def rebase_url(url)
|
|
374
374
|
if url.start_with?('/')
|
|
375
|
-
new_url =
|
|
375
|
+
new_url = String.new
|
|
376
376
|
new_url << conform_uri(ENV['RACK_BASE_URI']) if ENV['RACK_BASE_URI']
|
|
377
377
|
new_url << conform_uri(uri_root) if defined?(uri_root)
|
|
378
378
|
new_url << url
|
|
@@ -599,12 +599,14 @@ module Padrino
|
|
|
599
599
|
# path i.e :index or :show
|
|
600
600
|
if path.is_a?(Symbol)
|
|
601
601
|
name = path
|
|
602
|
-
path = map&.dup || (path == :index ? '/' : path.to_s)
|
|
602
|
+
path = map&.dup || (path == :index ? '/' : path.to_s.dup)
|
|
603
603
|
end
|
|
604
604
|
|
|
605
605
|
# Build our controller
|
|
606
606
|
controller = Array(@_controller).map(&:to_s)
|
|
607
607
|
|
|
608
|
+
path = path.dup if path.is_a?(String)
|
|
609
|
+
|
|
608
610
|
case path
|
|
609
611
|
when String # path i.e "/index" or "/show"
|
|
610
612
|
# Now we need to parse our 'with' params
|
|
@@ -860,8 +862,10 @@ module Padrino
|
|
|
860
862
|
def static_file?(path_info)
|
|
861
863
|
return unless (public_dir = settings.public_folder)
|
|
862
864
|
public_dir = File.expand_path(public_dir)
|
|
863
|
-
path =
|
|
864
|
-
return unless
|
|
865
|
+
path = "#{public_dir}#{unescape(path_info)}"
|
|
866
|
+
return unless Rack::Utils.valid_path?(path)
|
|
867
|
+
path = File.expand_path(path)
|
|
868
|
+
return unless path.start_with?("#{public_dir}/")
|
|
865
869
|
return unless File.file?(path)
|
|
866
870
|
path
|
|
867
871
|
end
|
|
@@ -963,7 +967,7 @@ module Padrino
|
|
|
963
967
|
verb = request.request_method
|
|
964
968
|
candidacies, allows = routes.partition { |route| route.verb == verb }
|
|
965
969
|
if candidacies.empty?
|
|
966
|
-
response['
|
|
970
|
+
response['Allow'] = allows.map(&:verb).uniq.join(', ')
|
|
967
971
|
halt 405
|
|
968
972
|
end
|
|
969
973
|
end
|
|
@@ -35,7 +35,13 @@ module Padrino
|
|
|
35
35
|
if defined? Pry
|
|
36
36
|
Pry.start
|
|
37
37
|
else
|
|
38
|
-
|
|
38
|
+
begin
|
|
39
|
+
require 'irb'
|
|
40
|
+
rescue LoadError => e
|
|
41
|
+
raise unless e.path == 'irb'
|
|
42
|
+
|
|
43
|
+
abort "<= padrino console needs irb. Since Ruby 4.0 it is a bundled gem: add gem 'irb' to your Gemfile and run bundle install."
|
|
44
|
+
end
|
|
39
45
|
begin
|
|
40
46
|
require 'irb/completion'
|
|
41
47
|
rescue LoadError
|
|
@@ -55,7 +61,7 @@ module Padrino
|
|
|
55
61
|
require 'padrino-gen/command'
|
|
56
62
|
ARGV.shift
|
|
57
63
|
ARGV << 'help' if ARGV.empty?
|
|
58
|
-
Padrino.bin_gen(*ARGV)
|
|
64
|
+
exit 1 unless Padrino.bin_gen(*ARGV)
|
|
59
65
|
rescue StandardError
|
|
60
66
|
puts '<= You need padrino-gen! Run: gem install padrino-gen'
|
|
61
67
|
end
|
|
@@ -76,6 +82,7 @@ module Padrino
|
|
|
76
82
|
abort 'Please specify code or file' if code_or_file.nil?
|
|
77
83
|
|
|
78
84
|
require File.expand_path('config/boot.rb')
|
|
85
|
+
Padrino.mounted_apps.each(&:setup_application!)
|
|
79
86
|
|
|
80
87
|
if File.exist?(code_or_file)
|
|
81
88
|
eval(File.read(code_or_file), nil, code_or_file)
|
|
@@ -21,7 +21,7 @@ module Padrino
|
|
|
21
21
|
if File.file?(binstub)
|
|
22
22
|
exec Gem.ruby, binstub, *ARGV
|
|
23
23
|
else
|
|
24
|
-
puts 'Please run `bundle install --
|
|
24
|
+
puts 'Please run `bundle install && bundle binstubs --all` from your project root to generate bundle-specific executables'
|
|
25
25
|
exit!
|
|
26
26
|
end
|
|
27
27
|
end
|
|
@@ -18,7 +18,7 @@ module Padrino
|
|
|
18
18
|
method_option :pid, type: :string, aliases: '-i', desc: 'File to store pid.'
|
|
19
19
|
method_option :debug, type: :boolean, desc: 'Set debugging flags.'
|
|
20
20
|
method_option :options, type: :array, aliases: '-O', desc: <<~DESC.chomp
|
|
21
|
-
--options NAME=VALUE NAME2=VALUE2
|
|
21
|
+
--options NAME=VALUE NAME2=VALUE2. pass VALUE to the server as option NAME. If no VALUE, sets it to true. Run '#{$PROGRAM_NAME} start --server_options'
|
|
22
22
|
DESC
|
|
23
23
|
method_option :server_options, type: :boolean, desc: "Tells the current server handler's options that can be used with --options"
|
|
24
24
|
|
|
@@ -48,7 +48,7 @@ module Padrino
|
|
|
48
48
|
# https://github.com/rack/rack/blob/master/lib/rack/server.rb\#L100
|
|
49
49
|
def server_options(options)
|
|
50
50
|
info = []
|
|
51
|
-
server =
|
|
51
|
+
server = Rackup::Handler.get(options[:server]) || Rackup::Handler.default
|
|
52
52
|
if server.respond_to?(:valid_options)
|
|
53
53
|
info << ''
|
|
54
54
|
info << "Server-specific options for #{server.name}:"
|
|
@@ -62,7 +62,7 @@ module Padrino
|
|
|
62
62
|
return '' unless has_options
|
|
63
63
|
end
|
|
64
64
|
info.join("\n")
|
|
65
|
-
rescue NameError
|
|
65
|
+
rescue NameError, LoadError
|
|
66
66
|
"Warning: Could not find handler specified (#{options[:server] || 'default'}) to determine handler-specific options"
|
|
67
67
|
end
|
|
68
68
|
|
|
@@ -13,8 +13,6 @@ module PadrinoTasks
|
|
|
13
13
|
def self.init(init = false)
|
|
14
14
|
Padrino::Tasks.files.flatten.uniq.each do |rakefile|
|
|
15
15
|
Rake.application.add_import(rakefile)
|
|
16
|
-
rescue StandardError
|
|
17
|
-
puts "<= Failed load #{ext}"
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
load(File.expand_path('rake_tasks.rb', __dir__))
|
|
@@ -1,17 +1,27 @@
|
|
|
1
|
+
# Model paths that :skeleton left out, loaded by a later :environment in the same run
|
|
2
|
+
# (e.g. `rake ar:migrate db:seed`).
|
|
3
|
+
# ceiling: they load after config/apps.rb and the after_load hooks, so a hook that walks the models
|
|
4
|
+
# doesn't see them in that case; loading them before the hooks would mean re-running Padrino.load!.
|
|
5
|
+
skipped_model_paths = []
|
|
6
|
+
|
|
1
7
|
# Loads the Padrino applications mounted within the project.
|
|
2
8
|
# Setting up the required environment for Padrino.
|
|
3
9
|
task :environment do
|
|
4
10
|
require File.expand_path('config/boot.rb', Rake.application.original_dir)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
unless skipped_model_paths.empty?
|
|
12
|
+
Padrino.dependency_paths.concat(skipped_model_paths)
|
|
13
|
+
Padrino.require_dependencies(*skipped_model_paths)
|
|
14
|
+
skipped_model_paths.clear
|
|
8
15
|
end
|
|
16
|
+
|
|
17
|
+
Padrino.mounted_apps.each(&:setup_application!)
|
|
9
18
|
end
|
|
10
19
|
|
|
11
20
|
task :skeleton do
|
|
12
21
|
PADRINO_ROOT ||= Rake.application.original_dir
|
|
13
22
|
require 'padrino-core'
|
|
14
|
-
Padrino.
|
|
23
|
+
skipped_model_paths.concat(Padrino.dependency_paths.select { |path| path.include?('/models/') })
|
|
24
|
+
Padrino.dependency_paths.reject! { |path| path.include?('/models/') }
|
|
15
25
|
require File.expand_path('config/boot.rb', Rake.application.original_dir)
|
|
16
26
|
end
|
|
17
27
|
|
data/lib/padrino-core/loader.rb
CHANGED
|
@@ -31,7 +31,7 @@ module Padrino
|
|
|
31
31
|
#
|
|
32
32
|
# @example
|
|
33
33
|
# after_load do
|
|
34
|
-
#
|
|
34
|
+
# SomeORM.finalize
|
|
35
35
|
# end
|
|
36
36
|
#
|
|
37
37
|
def after_load(&block)
|
|
@@ -150,7 +150,7 @@ module Padrino
|
|
|
150
150
|
error = fatal = loaded = nil
|
|
151
151
|
|
|
152
152
|
files.dup.each do |file|
|
|
153
|
-
Reloader.safe_load(file, options)
|
|
153
|
+
Reloader.enabled? || options[:force] ? Reloader.safe_load(file, options) : Reloader.untracked_load(file)
|
|
154
154
|
files.delete(file)
|
|
155
155
|
loaded = true
|
|
156
156
|
rescue NameError, LoadError => error
|
data/lib/padrino-core/logger.rb
CHANGED
|
@@ -30,6 +30,10 @@ module Padrino
|
|
|
30
30
|
#
|
|
31
31
|
# @example using ruby default logger
|
|
32
32
|
# require 'logger'
|
|
33
|
+
# Padrino.logger = ::Logger.new(STDOUT)
|
|
34
|
+
#
|
|
35
|
+
# @example using ruby default logger with Padrino formatting and #colorize!
|
|
36
|
+
# require 'logger'
|
|
33
37
|
# new_logger = ::Logger.new(STDOUT)
|
|
34
38
|
# new_logger.extend(Padrino::Logger::Extensions)
|
|
35
39
|
# Padrino.logger = new_logger
|
|
@@ -85,8 +89,8 @@ module Padrino
|
|
|
85
89
|
if args.size > 1
|
|
86
90
|
bench(args[0], args[1], args[2], name)
|
|
87
91
|
else
|
|
88
|
-
location = resolve_source_location(caller(1).shift)
|
|
89
|
-
args.unshift(location) if location
|
|
92
|
+
location = resolve_source_location(caller(1).shift) if enable_source_location?
|
|
93
|
+
args.unshift(location) if location
|
|
90
94
|
push(args * '', name)
|
|
91
95
|
end
|
|
92
96
|
end
|
|
@@ -94,7 +98,7 @@ module Padrino
|
|
|
94
98
|
define_method(:"#{name}?") { number >= level }
|
|
95
99
|
end
|
|
96
100
|
|
|
97
|
-
SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in
|
|
101
|
+
SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in [`'].+?')?$/
|
|
98
102
|
|
|
99
103
|
##
|
|
100
104
|
# Returns true if :source_location is set to true.
|
|
@@ -111,7 +115,11 @@ module Padrino
|
|
|
111
115
|
return unless path && line
|
|
112
116
|
|
|
113
117
|
root = Padrino.root
|
|
114
|
-
|
|
118
|
+
if Pathname.new(path).relative?
|
|
119
|
+
return unless File.exist?(path) # code from irb, eval or ruby -e has no file
|
|
120
|
+
|
|
121
|
+
path = File.realpath(path)
|
|
122
|
+
end
|
|
115
123
|
return unless path.start_with?(root) && !path.start_with?(Padrino.root('vendor'))
|
|
116
124
|
|
|
117
125
|
"[#{path.gsub("#{root}/", '')}:#{line}] "
|
|
@@ -240,6 +248,33 @@ module Padrino
|
|
|
240
248
|
end
|
|
241
249
|
end
|
|
242
250
|
|
|
251
|
+
##
|
|
252
|
+
# Plain versions of the Padrino-specific methods of {Extensions}. A logger assigned
|
|
253
|
+
# without {Extensions} gets the ones it lacks, so Padrino can log to it while its
|
|
254
|
+
# standard logging methods stay untouched.
|
|
255
|
+
#
|
|
256
|
+
module Fallbacks
|
|
257
|
+
def devel(*); end
|
|
258
|
+
|
|
259
|
+
def devel?
|
|
260
|
+
false
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def bench(action, began_at, message, level = :debug, _color = :yellow)
|
|
264
|
+
return unless Levels.key?(level)
|
|
265
|
+
|
|
266
|
+
public_send level, "#{action.to_s.upcase} (#{Kernel.format('%0.4fs', Time.now - began_at)}) #{message}"
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def exception(boom, verbosity = :long, level = :error)
|
|
270
|
+
Extensions.instance_method(:exception).bind(self).call(boom, verbosity, level)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def colorize(string, *_colors)
|
|
274
|
+
string
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
243
278
|
module Colorize
|
|
244
279
|
# Colors for levels
|
|
245
280
|
unless defined?(ColoredLevels)
|
|
@@ -332,16 +367,25 @@ module Padrino
|
|
|
332
367
|
end
|
|
333
368
|
|
|
334
369
|
def self.logger=(logger)
|
|
335
|
-
unless logger.
|
|
336
|
-
warn <<~MESSAGE
|
|
337
|
-
WARNING! `Padrino.logger = new_logger` no longer extends it with #colorize! and other features.
|
|
338
|
-
To do it with a custom logger you have to manually `new_logger.extend(Padrino::Logger::Extensions)`
|
|
339
|
-
before passing to `Padrino.logger = new_logger`.
|
|
340
|
-
MESSAGE
|
|
341
|
-
end
|
|
370
|
+
add_fallbacks(logger) unless logger.is_a?(Padrino::Logger::Extensions)
|
|
342
371
|
@_logger = logger
|
|
343
372
|
end
|
|
344
373
|
|
|
374
|
+
##
|
|
375
|
+
# Defines the {Fallbacks} methods a logger does not have yet.
|
|
376
|
+
#
|
|
377
|
+
def self.add_fallbacks(logger)
|
|
378
|
+
return if logger.nil?
|
|
379
|
+
|
|
380
|
+
missing = Fallbacks.instance_methods.reject { |name| logger.respond_to?(name) }
|
|
381
|
+
return if missing.empty?
|
|
382
|
+
return warn("WARNING! Padrino.logger is frozen and lacks #{missing.join(', ')}, logging from Padrino will fail.") if logger.frozen?
|
|
383
|
+
|
|
384
|
+
fallbacks = ::Module.new
|
|
385
|
+
missing.each { |name| fallbacks.send(:define_method, name, Fallbacks.instance_method(name)) }
|
|
386
|
+
logger.extend(fallbacks)
|
|
387
|
+
end
|
|
388
|
+
|
|
345
389
|
##
|
|
346
390
|
# Setup a new logger.
|
|
347
391
|
#
|
|
@@ -443,7 +487,11 @@ module Padrino
|
|
|
443
487
|
return unless @buffer.size.positive?
|
|
444
488
|
self.class.mutex.synchronize do
|
|
445
489
|
@buffer.each do |line|
|
|
446
|
-
|
|
490
|
+
if @sanitize_encoding
|
|
491
|
+
line = line.encode(@sanitize_encoding, invalid: :replace, undef: :replace)
|
|
492
|
+
elsif line.encoding != Encoding::UTF_8
|
|
493
|
+
line = line.encode(@log.external_encoding || Encoding::UTF_8, invalid: :replace, undef: :replace)
|
|
494
|
+
end
|
|
447
495
|
@log.write(line)
|
|
448
496
|
end
|
|
449
497
|
@buffer.clear
|
|
@@ -475,7 +523,7 @@ module Padrino
|
|
|
475
523
|
# The message
|
|
476
524
|
#
|
|
477
525
|
def <<(message = nil)
|
|
478
|
-
message
|
|
526
|
+
message += "\n" unless message[-1] == "\n"
|
|
479
527
|
self.class.mutex.synchronize { @buffer << message }
|
|
480
528
|
flush if @auto_flush
|
|
481
529
|
message
|
|
@@ -483,7 +531,8 @@ module Padrino
|
|
|
483
531
|
alias write <<
|
|
484
532
|
|
|
485
533
|
def format(message, level)
|
|
486
|
-
|
|
534
|
+
datetime = @format_datetime.empty? ? '' : Time.now.strftime(@format_datetime)
|
|
535
|
+
Kernel.format(@format_message, stylized_level(level), colorize(datetime, :yellow), message.to_s.strip)
|
|
487
536
|
end
|
|
488
537
|
|
|
489
538
|
##
|
data/lib/padrino-core/mounter.rb
CHANGED
|
@@ -87,17 +87,13 @@ module Padrino
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
##
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
# @param [Padrino::Router]
|
|
94
|
-
#
|
|
95
|
-
# @return [Padrino::Router]
|
|
90
|
+
# Applies the mount settings (uri_root, root, public folder, cascade) to the
|
|
91
|
+
# application and sets it up, so its routes, controllers and mailers are loaded.
|
|
96
92
|
#
|
|
97
93
|
# @example
|
|
98
|
-
# @app.
|
|
94
|
+
# @app.setup_application!
|
|
99
95
|
#
|
|
100
|
-
def
|
|
96
|
+
def setup_application!
|
|
101
97
|
app_data = self
|
|
102
98
|
app_obj = @app_obj
|
|
103
99
|
|
|
@@ -117,7 +113,22 @@ module Padrino
|
|
|
117
113
|
app_obj.public_folder = Padrino.root('public', uri_root) unless public_folder_exists
|
|
118
114
|
end
|
|
119
115
|
app_obj.setup_application! # Initializes the app here with above settings.
|
|
120
|
-
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
##
|
|
119
|
+
# Maps Padrino application onto a Padrino::Router.
|
|
120
|
+
# For use in constructing a Rack application.
|
|
121
|
+
#
|
|
122
|
+
# @param [Padrino::Router]
|
|
123
|
+
#
|
|
124
|
+
# @return [Padrino::Router]
|
|
125
|
+
#
|
|
126
|
+
# @example
|
|
127
|
+
# @app.map_onto(router)
|
|
128
|
+
#
|
|
129
|
+
def map_onto(router)
|
|
130
|
+
setup_application!
|
|
131
|
+
router.map(to: app_obj, path: uri_root, host: app_host)
|
|
121
132
|
end
|
|
122
133
|
|
|
123
134
|
###
|
|
@@ -183,7 +194,8 @@ module Padrino
|
|
|
183
194
|
def locate_app_object
|
|
184
195
|
@_app_object ||= begin
|
|
185
196
|
ensure_app_file!
|
|
186
|
-
|
|
197
|
+
# Always tracked, since Application#reload! must re-run the app file.
|
|
198
|
+
Reloader.safe_load(app_file) if File.file?(app_file)
|
|
187
199
|
app_constant
|
|
188
200
|
end
|
|
189
201
|
end
|
|
@@ -105,7 +105,7 @@ module Padrino
|
|
|
105
105
|
# Decode uri escape sequences
|
|
106
106
|
#
|
|
107
107
|
def decode_uri(string)
|
|
108
|
-
string.split(/%2F|%2f/, -1).map { |part| Rack::Utils.unescape(part) }.join('%2F')
|
|
108
|
+
string.split(/%2F|%2f/, -1).map { |part| Rack::Utils.unescape(part.gsub('+', '%2B')) }.join('%2F')
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
111
|
end
|
|
@@ -27,6 +27,17 @@ module Padrino
|
|
|
27
27
|
@_exclude ||= Set.new(%w[test spec tmp features config public db].map { |path| Padrino.root(path) })
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
##
|
|
31
|
+
# Tracking files for reloading slows down boot, so it's only enabled in development
|
|
32
|
+
# by default. Files loaded while it's disabled are never reloaded. To reload code in
|
|
33
|
+
# other environments, set +Padrino::Reloader.enabled = true+ before +Padrino.load!+.
|
|
34
|
+
#
|
|
35
|
+
attr_writer :enabled
|
|
36
|
+
|
|
37
|
+
def enabled?
|
|
38
|
+
@enabled.nil? ? Padrino.env == :development : @enabled
|
|
39
|
+
end
|
|
40
|
+
|
|
30
41
|
##
|
|
31
42
|
# Specified constants can be excluded from the code unloading process.
|
|
32
43
|
#
|
|
@@ -48,6 +59,7 @@ module Padrino
|
|
|
48
59
|
def reload!
|
|
49
60
|
rotation do |file|
|
|
50
61
|
next unless file_changed?(file)
|
|
62
|
+
next skip_untracked(file) if untracked_files.include?(file)
|
|
51
63
|
reload_special(file) || reload_regular(file)
|
|
52
64
|
end
|
|
53
65
|
end
|
|
@@ -57,6 +69,7 @@ module Padrino
|
|
|
57
69
|
#
|
|
58
70
|
def clear!
|
|
59
71
|
MTIMES.clear
|
|
72
|
+
untracked_files.clear
|
|
60
73
|
Storage.clear!
|
|
61
74
|
end
|
|
62
75
|
|
|
@@ -87,6 +100,7 @@ module Padrino
|
|
|
87
100
|
began_at = Time.now
|
|
88
101
|
file = figure_path(file)
|
|
89
102
|
return unless options[:force] || file_changed?(file)
|
|
103
|
+
return skip_untracked(file) if untracked_files.include?(file)
|
|
90
104
|
return require(file) if feature_excluded?(file)
|
|
91
105
|
|
|
92
106
|
Storage.prepare(file) # might call #safe_load recursively
|
|
@@ -95,6 +109,7 @@ module Padrino
|
|
|
95
109
|
with_silence { require(file) }
|
|
96
110
|
Storage.commit(file)
|
|
97
111
|
update_modification_time(file)
|
|
112
|
+
untracked_files.delete(file)
|
|
98
113
|
rescue Exception => e
|
|
99
114
|
unless options[:cyclic]
|
|
100
115
|
logger.exception e, :short
|
|
@@ -105,13 +120,33 @@ module Padrino
|
|
|
105
120
|
end
|
|
106
121
|
end
|
|
107
122
|
|
|
123
|
+
##
|
|
124
|
+
# A Kernel::require for when tracking is disabled. Only the modification time is
|
|
125
|
+
# recorded, so later changes are detected but the file is not reloaded.
|
|
126
|
+
#
|
|
127
|
+
def untracked_load(file)
|
|
128
|
+
file = figure_path(file)
|
|
129
|
+
features = $LOADED_FEATURES.dup
|
|
130
|
+
begin
|
|
131
|
+
with_silence { require(file) }
|
|
132
|
+
rescue Exception
|
|
133
|
+
rollback_untracked(file, $LOADED_FEATURES - features)
|
|
134
|
+
raise
|
|
135
|
+
end
|
|
136
|
+
return if feature_excluded?(file) || !File.file?(file)
|
|
137
|
+
update_modification_time(file)
|
|
138
|
+
untracked_files << file
|
|
139
|
+
end
|
|
140
|
+
|
|
108
141
|
##
|
|
109
142
|
# Removes the specified class and constant.
|
|
110
143
|
#
|
|
111
144
|
def remove_constant(const)
|
|
112
145
|
return if constant_excluded?(const)
|
|
113
146
|
base, _, object = const.to_s.rpartition('::')
|
|
114
|
-
base = base.empty? ? Object :
|
|
147
|
+
base = base.empty? ? Object : Inflections.constantize(base)
|
|
148
|
+
return if base.autoload?(object, false) || !base.const_defined?(object, false)
|
|
149
|
+
return unless base.const_get(object, false).equal?(const)
|
|
115
150
|
base.send :remove_const, object
|
|
116
151
|
logger.devel "Removed constant #{const} from #{base}"
|
|
117
152
|
rescue NameError
|
|
@@ -153,6 +188,39 @@ module Padrino
|
|
|
153
188
|
file
|
|
154
189
|
end
|
|
155
190
|
|
|
191
|
+
##
|
|
192
|
+
# Files loaded while tracking was disabled.
|
|
193
|
+
#
|
|
194
|
+
def untracked_files
|
|
195
|
+
@untracked_files ||= Set.new
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
##
|
|
199
|
+
# Like Storage.rollback, removes classes defined by a file that failed partway and by
|
|
200
|
+
# the app files it required, so they can be required again. Only the failure pays for
|
|
201
|
+
# walking Object.constants, which keeps boot as fast as a plain require.
|
|
202
|
+
#
|
|
203
|
+
def rollback_untracked(file, features)
|
|
204
|
+
features = features.reject { |feature| feature_excluded?(feature) || untracked_files.include?(feature) }
|
|
205
|
+
features.each { |feature| $LOADED_FEATURES.delete(feature) }
|
|
206
|
+
files = Set.new(features << file)
|
|
207
|
+
Object.constants.each do |name|
|
|
208
|
+
next if Object.autoload?(name)
|
|
209
|
+
next unless files.include?(Object.const_source_location(name)&.first)
|
|
210
|
+
klass = Object.const_get(name)
|
|
211
|
+
remove_constant(klass) if klass.is_a?(Class)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
##
|
|
216
|
+
# Warns that a changed file can't be reloaded because it was loaded without tracking.
|
|
217
|
+
#
|
|
218
|
+
def skip_untracked(file)
|
|
219
|
+
logger.warn "Skipping reload of #{file}, it was loaded with Padrino::Reloader disabled. " \
|
|
220
|
+
'Set Padrino::Reloader.enabled = true before Padrino.load! to reload it'
|
|
221
|
+
update_modification_time(file)
|
|
222
|
+
end
|
|
223
|
+
|
|
156
224
|
##
|
|
157
225
|
# Reloads the file if it's special. For now it's only I18n locale files.
|
|
158
226
|
#
|
|
@@ -244,21 +312,41 @@ module Padrino
|
|
|
244
312
|
# Tells if a constant should be excluded from Reloader routines.
|
|
245
313
|
#
|
|
246
314
|
def constant_excluded?(const)
|
|
247
|
-
|
|
315
|
+
return true if external_constant?(const)
|
|
316
|
+
name = constant_name(const)
|
|
317
|
+
(exclude_constants - include_constants).any? do |excluded|
|
|
318
|
+
excluded = excluded.to_s
|
|
319
|
+
name == excluded || name.start_with?("#{excluded}::")
|
|
320
|
+
end
|
|
248
321
|
end
|
|
249
322
|
|
|
250
323
|
##
|
|
251
324
|
# Tells if a constant is defined only outside of Padrino project path.
|
|
252
325
|
# If a constant has any methods defined inside of the project path it's
|
|
253
326
|
# considered internal and will be included in further testing.
|
|
327
|
+
# A constant without methods (an exception class, say) is judged by
|
|
328
|
+
# where its name was defined instead.
|
|
254
329
|
#
|
|
255
330
|
def external_constant?(const)
|
|
256
331
|
sources = object_sources(const)
|
|
257
|
-
|
|
332
|
+
sources = [constant_source(const)].compact if sources.empty?
|
|
333
|
+
# consider methodless constants of unknown origin not external
|
|
258
334
|
return false if sources.empty?
|
|
259
335
|
sources.none? { |source| source.start_with?(Padrino.root) }
|
|
260
336
|
end
|
|
261
337
|
|
|
338
|
+
##
|
|
339
|
+
# Returns the file in which the constant's name was defined, if known.
|
|
340
|
+
#
|
|
341
|
+
def constant_source(const)
|
|
342
|
+
base, _, object = constant_name(const).to_s.rpartition('::')
|
|
343
|
+
return if object.empty?
|
|
344
|
+
base = base.empty? ? Object : Inflections.constantize(base)
|
|
345
|
+
base.const_source_location(object)&.first
|
|
346
|
+
rescue NameError
|
|
347
|
+
nil
|
|
348
|
+
end
|
|
349
|
+
|
|
262
350
|
##
|
|
263
351
|
# Gets all the sources in which target class is defined.
|
|
264
352
|
#
|
data/lib/padrino-core/server.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Padrino
|
|
2
2
|
##
|
|
3
|
-
# Runs the Padrino apps as a self-hosted server using
|
|
4
|
-
# thin,
|
|
3
|
+
# Runs the Padrino apps as a self-hosted server using the first available
|
|
4
|
+
# handler in Server::Handlers (thin, puma, falcon, ..., WEBrick).
|
|
5
5
|
#
|
|
6
6
|
# @example
|
|
7
7
|
# Padrino.run! # with these defaults => host: '127.0.0.1', port: '3000', adapter: the first found
|
|
@@ -31,7 +31,7 @@ module Padrino
|
|
|
31
31
|
DEFAULT_ADDRESS = { Host: '127.0.0.1', Port: 3000 }
|
|
32
32
|
|
|
33
33
|
# Server Handlers
|
|
34
|
-
Handlers = %i[thin puma spider-gazelle mongrel trinidad webrick]
|
|
34
|
+
Handlers = %i[thin puma falcon spider-gazelle mongrel trinidad webrick]
|
|
35
35
|
|
|
36
36
|
# Starts the application on the available server with specified options.
|
|
37
37
|
def self.start(app, options = {})
|
data/lib/padrino-core/version.rb
CHANGED
data/padrino-core.gemspec
CHANGED
|
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
|
|
|
21
21
|
s.require_paths = ['lib']
|
|
22
22
|
s.rdoc_options = ['--charset=UTF-8']
|
|
23
23
|
|
|
24
|
+
s.add_dependency('ostruct', '>= 0.6')
|
|
24
25
|
s.add_dependency('padrino-support', Padrino.version)
|
|
25
26
|
s.add_dependency('rackup', '~> 2.1')
|
|
26
27
|
s.add_dependency('sinatra', '~> 4')
|