padrino-core 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60c8ed53139a3e9558b1e665205eeb798960a6049ac0788e3454f8f6f4280de0
4
- data.tar.gz: '08dc99355571157c9113a0083c8acacd9970c7f64e407008265fdc177e590572'
3
+ metadata.gz: bf357f68006699d9e80fa024da18fbf0b64bd7d0365ea2ecfd90c12b0b8a6cfa
4
+ data.tar.gz: f702562744c51a4dee46da71739b7864ba3ebc1c9c937ddb8d5af3a07efc391c
5
5
  SHA512:
6
- metadata.gz: 50d10641e73dd6b858b2a6490cdc77372055ace5232683d7727e44b45a85267d8bd56fa2c5ce49852c51e5c017169a7449ecb0d297b6b5d67a6896b8f7396b93
7
- data.tar.gz: 79af9aed08ab7c2189d162b75772e9d40f37ca45189abf26d18c9c42f0ca62d149d30756c77088fdaa119683192c76c94b8e9acf07b6b2abb8d0f72cd214155e
6
+ metadata.gz: 140423832026779eb7731e1145ab1d6d7b7197bf77e85d83c40077f639bcbc3fc0f7ee4ed2bedaee35a6b9b1188ae8ce9437e09fb605c17c605abfbf413c7383
7
+ data.tar.gz: ea73969e005298405dd22072d18a211634d038a68e53b7c3b9cfead7d29b2471eb06f30b723702f50fa00ab331cdb1a5bc29cbdc98449be874629e91dab50e05
@@ -417,25 +417,25 @@ module Padrino
417
417
 
418
418
  # Saves controller options, yields the block, restores controller options.
419
419
  def with_new_options(*args)
420
+ original = {}
420
421
  options = args.last.is_a?(Hash) ? args.pop : {}
421
422
 
422
- CONTROLLER_OPTIONS.each { |key| replace_instance_variable("@_#{key}", options.delete(key)) }
423
- replace_instance_variable(:@_controller, args)
424
- replace_instance_variable(:@_defaults, options)
425
- replace_instance_variable(:@filters, before: @filters[:before].dup, after: @filters[:after].dup)
426
- replace_instance_variable(:@layout, @layout)
423
+ CONTROLLER_OPTIONS.each { |key| replace_instance_variable(original, "@_#{key}", options.delete(key)) }
424
+ replace_instance_variable(original, :@_controller, args)
425
+ replace_instance_variable(original, :@_defaults, options)
426
+ replace_instance_variable(original, :@filters, before: @filters[:before].dup, after: @filters[:after].dup)
427
+ replace_instance_variable(original, :@layout, @layout)
427
428
 
428
429
  yield
429
-
430
- @original_instance.each do |key, value|
430
+ ensure
431
+ original.each do |key, value|
431
432
  instance_variable_set(key, value)
432
433
  end
433
434
  end
434
435
 
435
- # Sets instance variable by name and saves the original value in @original_instance hash
436
- def replace_instance_variable(name, value)
437
- @original_instance ||= {}
438
- @original_instance[name] = instance_variable_get(name)
436
+ # Sets instance variable by name and saves the original value in the given hash
437
+ def replace_instance_variable(original, name, value)
438
+ original[name] = instance_variable_get(name)
439
439
  instance_variable_set(name, value)
440
440
  end
441
441
 
@@ -1019,6 +1019,7 @@ module Padrino
1019
1019
  (route.before_filters - settings.filters[:before]).each { |block| instance_eval(&block) }
1020
1020
  @layout = route.use_layout if route.use_layout
1021
1021
  route.custom_conditions.each { |block| pass if block.bind(self).call == false }
1022
+ env['sinatra.route'] = "#{@request.request_method} #{route.sinatra_pattern}"
1022
1023
  route_response = route.block[self, captured_params]
1023
1024
  @_response_buffer = route_response.instance_of?(Array) ? route_response.last : route_response
1024
1025
  halt(route_response)
@@ -4,19 +4,22 @@ module Padrino
4
4
  PADRINO_IGNORE_CALLERS = [
5
5
  *Sinatra::Base.callers_to_ignore, # Inherit Sinatra's default ignore patterns
6
6
  Regexp.new(Regexp.escape(RbConfig::CONFIG['rubylibdir'])), # Ignore the Ruby standard lib path
7
- %r{lib/padrino-.*$},
8
- %r{/padrino-.*/(lib|bin)},
7
+ %r{/lib/padrino-(admin|cache|core|gen|helpers|mailer|performance|support)/},
8
+ %r{/lib/padrino/(core_ext/|rendering/|rendering\.rb$|safe_buffer\.rb$)},
9
+ %r{/lib/padrino(-[^/]+)?\.rb$},
10
+ %r{/padrino-[^/]+/(lib|bin)/},
9
11
  %r{/bin/padrino$},
10
- %r{lib/rack.*\.rb$},
11
- %r{lib/mongrel.*\.rb$},
12
- %r{lib/shotgun.*\.rb$},
12
+ %r{/lib/rack(up)?(\.rb$|/)},
13
+ %r{/lib/mongrel(\.rb$|/)},
14
+ %r{/lib/shotgun(\.rb$|/)},
13
15
  %r{bin/shotgun$},
14
16
  %r{shoulda/context\.rb$},
15
- %r{mocha/integration},
16
- %r{test/unit},
17
+ %r{/lib/mocha/integration(\.rb$|/)},
18
+ %r{/lib/test/unit(\.rb$|/)},
17
19
  /rake_test_loader\.rb/,
18
20
  /custom_require\.rb$/,
19
- %r{/thor}
21
+ %r{/lib/thor(\.rb$|/)},
22
+ %r{/bin/thor$}
20
23
  ]
21
24
 
22
25
  ##
@@ -62,8 +62,10 @@ module Padrino
62
62
  ARGV.shift
63
63
  ARGV << 'help' if ARGV.empty?
64
64
  exit 1 unless Padrino.bin_gen(*ARGV)
65
- rescue StandardError
66
- puts '<= You need padrino-gen! Run: gem install padrino-gen'
65
+ rescue LoadError => e
66
+ raise unless (e.path || e.message[/-- (\S+)\z/, 1]) == 'padrino-gen/command'
67
+
68
+ abort "<= padrino g needs padrino-gen. Add gem 'padrino-gen' to your Gemfile and run bundle install, or run: gem install padrino-gen"
67
69
  end
68
70
 
69
71
  desc 'version', 'Show current Padrino version.'
@@ -55,6 +55,20 @@ module Padrino
55
55
  @path
56
56
  end
57
57
 
58
+ ##
59
+ # Returns the pattern as Sinatra writes it in env['sinatra.route']. Sinatra
60
+ # stringifies a Regexp through Mustermann, which drops the (?-mix:) wrapper.
61
+ # The root route is stored as an empty path; Sinatra names it "/".
62
+ #
63
+ def sinatra_pattern
64
+ @sinatra_pattern ||=
65
+ if @path.is_a?(Regexp)
66
+ Mustermann.new(@path, check_anchors: false).to_s
67
+ else
68
+ @path.to_s.empty? ? '/' : @path.to_s
69
+ end
70
+ end
71
+
58
72
  SIGNIFICANT_VARIABLES_REGEX = /(^|[^\\])[:*]([a-zA-Z0-9_]+)/
59
73
 
60
74
  ##
@@ -111,6 +125,7 @@ module Padrino
111
125
  def path=(pattern)
112
126
  @path = pattern
113
127
  @matcher = nil
128
+ @sinatra_pattern = nil
114
129
  @significant_variable_names = nil
115
130
  end
116
131
 
@@ -6,7 +6,7 @@
6
6
  #
7
7
  module Padrino
8
8
  # The version constant for the current version of Padrino.
9
- VERSION = '0.18.0' unless defined?(Padrino::VERSION)
9
+ VERSION = '0.18.2' unless defined?(Padrino::VERSION)
10
10
 
11
11
  #
12
12
  # The current Padrino version.
data/padrino-core.gemspec CHANGED
@@ -6,17 +6,21 @@ Gem::Specification.new do |s|
6
6
  s.authors = ['Padrino Team', 'Nathan Esquenazi', "Davide D'Agostino", 'Arthur Chiu']
7
7
  s.email = 'padrinorb@gmail.com'
8
8
  s.summary = 'The required Padrino core gem'
9
- s.homepage = 'http://www.padrinorb.com'
9
+ s.homepage = 'https://padrinorb.com'
10
10
  s.description = 'The Padrino core gem required for use of this framework'
11
11
  s.required_ruby_version = '>= 2.7.8'
12
12
  s.required_rubygems_version = '>= 1.3.6'
13
13
  s.version = Padrino.version
14
- s.date = Time.now.strftime('%Y-%m-%d')
15
14
  s.license = 'MIT'
15
+ s.metadata = {
16
+ 'bug_tracker_uri' => 'https://github.com/padrino/padrino-framework/issues',
17
+ 'changelog_uri' => 'https://github.com/padrino/padrino-framework/blob/master/CHANGES.rdoc',
18
+ 'documentation_uri' => 'https://padrinorb.com/guides/',
19
+ 'source_code_uri' => 'https://github.com/padrino/padrino-framework'
20
+ }
16
21
 
17
22
  s.extra_rdoc_files = Dir['*.rdoc']
18
23
  s.files = `git ls-files`.split("\n")
19
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
24
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
21
25
  s.require_paths = ['lib']
22
26
  s.rdoc_options = ['--charset=UTF-8']
data/test/helper.rb CHANGED
@@ -15,6 +15,9 @@ require 'rack/test'
15
15
  require 'yaml'
16
16
  require 'padrino-core'
17
17
 
18
+ # Reloading outside development needs tracking enabled before the apps load, and any test file can load them first.
19
+ Padrino::Reloader.enabled = true
20
+
18
21
  require 'ext/minitest-spec'
19
22
  require 'ext/rack-test-methods'
20
23
 
@@ -0,0 +1,104 @@
1
+ require_relative 'helper'
2
+ require 'tmpdir'
3
+
4
+ describe 'PADRINO_IGNORE_CALLERS' do
5
+ def ignored?(file)
6
+ Padrino::PADRINO_IGNORE_CALLERS.any? { |pattern| file =~ pattern }
7
+ end
8
+
9
+ gems = '/usr/lib/ruby/gems/4.0.0/gems'
10
+
11
+ [
12
+ "#{gems}/padrino-0.18.1/lib/padrino.rb",
13
+ "#{gems}/padrino-core-0.18.1/lib/padrino-core/application.rb",
14
+ "#{gems}/padrino-helpers-0.18.1/lib/padrino/rendering.rb",
15
+ "#{gems}/padrino-helpers-0.18.1/lib/padrino/rendering/erb_template.rb",
16
+ "#{gems}/padrino-helpers-0.18.1/lib/padrino/safe_buffer.rb",
17
+ "#{gems}/padrino-helpers-0.18.1/lib/padrino/core_ext/output_safety.rb",
18
+ "#{gems}/padrino-gen-0.18.1/bin/padrino-gen",
19
+ "#{gems}/padrino-contrib-0.3.0/lib/padrino-contrib/helpers/flash.rb",
20
+ '/home/me/.bundle/gems/padrino-framework-abc123/padrino-core/lib/padrino-core/mounter.rb',
21
+ '/home/me/.gem/bin/padrino',
22
+ '/srv/vendor/core/lib/padrino-core/application/application_setup.rb',
23
+ '/srv/vendor/core/lib/padrino-core.rb',
24
+ "#{gems}/acme-padrino-core-1.0/lib/padrino-core/mounter.rb",
25
+ '/home/me/.bundle/gems/fork-abc123/padrino-helpers/lib/padrino/rendering.rb',
26
+ "#{gems}/padrino-performance-0.18.1/lib/suites/json.rb",
27
+ "#{gems}/acme/lib/padrino-acme.rb",
28
+ "#{gems}/padrino-foo-1.0/lib/foo.rb",
29
+ "#{gems}/puma-6.4/lib/rack/handler/puma.rb",
30
+ "#{gems}/thor-1.5.0/lib/thor.rb",
31
+ "#{gems}/thor-1.5.0/lib/thor/base.rb",
32
+ "#{gems}/thor-1.5.0/bin/thor",
33
+ "#{gems}/rack-3.2.8/lib/rack.rb",
34
+ "#{gems}/rack-3.2.8/lib/rack/builder.rb",
35
+ "#{gems}/rack-protection-4.2.1/lib/rack/protection/base.rb",
36
+ "#{gems}/rack-session-2.1.2/lib/rack/session/cookie.rb",
37
+ "#{gems}/rackup-2.3.1/lib/rackup/server.rb",
38
+ "#{gems}/shotgun-0.9/lib/shotgun/loader.rb",
39
+ "#{gems}/shotgun-0.9/bin/shotgun",
40
+ "#{gems}/mongrel-1.1/lib/mongrel/http_response.rb",
41
+ "#{gems}/mocha-3.1.0/lib/mocha/integration/mini_test.rb",
42
+ "#{gems}/test-unit-3.6/lib/test/unit/testcase.rb",
43
+ "#{gems}/test-unit-3.6/lib/test/unit.rb",
44
+ "#{gems}/mocha-3.1.0/lib/mocha/integration.rb"
45
+ ].each do |file|
46
+ it "should ignore the library file #{file}" do
47
+ assert ignored?(file), "#{file} should be ignored"
48
+ end
49
+ end
50
+
51
+ %w[
52
+ /home/thor/demo/app/app.rb
53
+ /srv/thor-apps/demo/app/app.rb
54
+ /srv/lib/padrino-blog/demo/app/app.rb
55
+ /srv/demo/bin/padrino-worker
56
+ /srv/padrino-demo/app/app.rb
57
+ /srv/lib/rack-app/demo/app/app.rb
58
+ /srv/lib/rack_demo/demo/app/app.rb
59
+ /srv/lib/mongrel-demo/app/app.rb
60
+ /srv/lib/shotgun-demo/app/app.rb
61
+ /srv/test/unit/demo/app/app.rb
62
+ /srv/mocha/integration/demo/app/app.rb
63
+ /srv/demo/app/thor_helpers.rb
64
+ /srv/lib/padrino/rendering-blog/app/app.rb
65
+ /srv/lib/padrino/safe_buffer_demo.rb
66
+ /srv/lib/padrino/core_extras/app/app.rb
67
+ /srv/lib/test/unit-app/app/app.rb
68
+ /srv/lib/mocha/integration-demo/app/app.rb
69
+ ].each do |file|
70
+ it "should keep the application file #{file}" do
71
+ refute ignored?(file), "#{file} should not be ignored"
72
+ end
73
+ end
74
+
75
+ %w[thor-apps lib/padrino-blog lib/rack-app test/unit].each do |parent|
76
+ it "should find the application file of an app under #{parent}" do
77
+ Dir.mktmpdir do |dir|
78
+ app_file = File.join(dir, parent, 'demo', 'app', 'app.rb')
79
+ FileUtils.mkdir_p(File.dirname(app_file))
80
+ File.write(app_file, "Class.new(Padrino::Application) { $caller_test_app = self }\n")
81
+ begin
82
+ load app_file
83
+ assert_equal File.realpath(app_file), File.realpath($caller_test_app.app_file)
84
+ ensure
85
+ $caller_test_app = nil
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ it 'should keep the first caller for a file under a matching directory' do
92
+ Dir.mktmpdir do |dir|
93
+ file = File.join(dir, 'thor-apps', 'boot.rb')
94
+ FileUtils.mkdir_p(File.dirname(file))
95
+ File.write(file, "$caller_test_file = Padrino.first_caller\n")
96
+ begin
97
+ load file
98
+ assert_equal File.realpath(file), File.realpath($caller_test_file)
99
+ ensure
100
+ $caller_test_file = nil
101
+ end
102
+ end
103
+ end
104
+ end
data/test/test_cli.rb CHANGED
@@ -81,6 +81,65 @@ describe 'Padrino::Cli' do
81
81
  refute_includes err, "add gem 'irb'"
82
82
  end
83
83
 
84
+ it 'should say how to add padrino-gen when padrino g cannot load it' do
85
+ _, err, status = run_in_project(<<~RUBY)
86
+ # A bundle without padrino-gen
87
+ module Kernel
88
+ alias_method :require_before_test, :require
89
+ def require(name)
90
+ if name == 'padrino-gen/command'
91
+ error = LoadError.new('cannot load such file -- padrino-gen/command')
92
+ error.define_singleton_method(:path) { 'padrino-gen/command' }
93
+ raise error
94
+ end
95
+
96
+ require_before_test(name)
97
+ end
98
+ end
99
+ require 'padrino-core/cli/base'
100
+ Padrino::Cli::Base.start(['g', 'controller', 'demo'])
101
+ RUBY
102
+ assert_equal 1, status.exitstatus
103
+ assert_includes err, "Add gem 'padrino-gen' to your Gemfile"
104
+ end
105
+
106
+ it 'should say how to add padrino-gen when the LoadError has no path' do
107
+ _, err, status = run_in_project(<<~RUBY)
108
+ module Kernel
109
+ alias_method :require_before_test, :require
110
+ def require(name)
111
+ if name == 'padrino-gen/command'
112
+ error = LoadError.new('cannot load such file -- padrino-gen/command')
113
+ error.define_singleton_method(:path) { nil }
114
+ raise error
115
+ end
116
+
117
+ require_before_test(name)
118
+ end
119
+ end
120
+ require 'padrino-core/cli/base'
121
+ Padrino::Cli::Base.start(['g', 'controller', 'demo'])
122
+ RUBY
123
+ assert_equal 1, status.exitstatus
124
+ assert_includes err, "Add gem 'padrino-gen' to your Gemfile"
125
+ end
126
+
127
+ it 'should not hide a LoadError from inside padrino-gen in padrino g' do
128
+ _, err, status = run_in_project(<<~RUBY)
129
+ module Kernel
130
+ alias_method :require_before_test, :require
131
+ def require(name)
132
+ require_before_test(name == 'padrino-gen/command' ? 'padrino_missing_gen_dependency' : name)
133
+ end
134
+ end
135
+ require 'padrino-core/cli/base'
136
+ Padrino::Cli::Base.start(['g', 'controller', 'demo'])
137
+ RUBY
138
+ refute status.success?
139
+ assert_includes err, 'padrino_missing_gen_dependency'
140
+ refute_includes err, "Add gem 'padrino-gen'"
141
+ end
142
+
84
143
  it 'should load the models in the rake environment task after a skeleton task' do
85
144
  out, err, status = run_in_project(<<~RUBY)
86
145
  require 'rake'
@@ -1,7 +1,5 @@
1
1
  require_relative 'helper'
2
2
 
3
- # Reloading outside development needs tracking enabled before the apps load.
4
- Padrino::Reloader.enabled = true
5
3
  require_relative 'fixtures/apps/complex'
6
4
 
7
5
  describe 'ComplexReloader' do
@@ -1,7 +1,5 @@
1
1
  require_relative 'helper'
2
2
 
3
- # Reloading outside development needs tracking enabled before the apps load.
4
- Padrino::Reloader.enabled = true
5
3
  require_relative 'fixtures/reloadable_apps/main/app'
6
4
 
7
5
  describe 'ExternalReloader' do
@@ -1,7 +1,5 @@
1
1
  require_relative 'helper'
2
2
 
3
- # Reloading outside development needs tracking enabled before the apps load.
4
- Padrino::Reloader.enabled = true
5
3
  require_relative 'fixtures/apps/simple'
6
4
 
7
5
  describe 'SimpleReloader' do
@@ -1,8 +1,6 @@
1
1
  require_relative 'helper'
2
2
  require 'tmpdir'
3
3
 
4
- # Reloading outside development needs tracking enabled before the apps load.
5
- Padrino::Reloader.enabled = true
6
4
  require_relative 'fixtures/apps/kiq'
7
5
  require_relative 'fixtures/apps/system'
8
6
  require_relative 'fixtures/apps/static'
@@ -201,6 +199,7 @@ describe 'SystemReloader' do
201
199
  concern_file = "#{__dir__}/fixtures/apps/concerned/models/mixins/filterable.rb"
202
200
  includer_file = "#{__dir__}/fixtures/apps/concerned/models/filtered_item.rb"
203
201
  original = File.read(concern_file)
202
+ includer_mtime = File.mtime(includer_file)
204
203
  Padrino.clear!
205
204
  require File.expand_path("#{__dir__}/fixtures/apps/concerned/app.rb")
206
205
  @app = SystemConcernedClassDemo
@@ -223,6 +222,8 @@ describe 'SystemReloader' do
223
222
  assert_equal :kept, Filterable.instance_variable_get(:@config)
224
223
  ensure
225
224
  File.write(concern_file, original) if original
225
+ # A future mtime would hide the next run's touch from Reloader.
226
+ File.utime(includer_mtime, includer_mtime, includer_file) if includer_mtime
226
227
  end
227
228
 
228
229
  it 'should redefine an ActiveSupport::Concern that another file requires, also after Padrino.clear!' do
@@ -234,6 +235,7 @@ describe 'SystemReloader' do
234
235
  concern_file = "#{__dir__}/fixtures/apps/concerned/models/mixins/sortable.rb"
235
236
  includer_file = "#{__dir__}/fixtures/apps/concerned/models/catalog_item.rb"
236
237
  original = File.read(concern_file)
238
+ includer_mtime = File.mtime(includer_file)
237
239
  # Sortable stays defined after Padrino.clear!, so the second load finds it already defined.
238
240
  2.times do
239
241
  Padrino.clear!
@@ -256,6 +258,7 @@ describe 'SystemReloader' do
256
258
  ensure
257
259
  if original
258
260
  File.write(concern_file, original)
261
+ File.utime(includer_mtime, includer_mtime, includer_file)
259
262
  # The next Padrino.clear! keeps Sortable, and CatalogItem then requires the restored file without a reset.
260
263
  Padrino::Reloader.reset_concern(Sortable) if defined?(Sortable)
261
264
  end
data/test/test_routing.rb CHANGED
@@ -980,6 +980,137 @@ describe 'Routing' do
980
980
  assert_equal 'foo_bar_index', body
981
981
  end
982
982
 
983
+ it 'should not give routes defined after a nested controller block the outer controller options' do
984
+ mock_app do
985
+ get(:early) { 'early' }
986
+ controller :admin, provides: :json, conditions: { agent: /Admin/ } do
987
+ before { response['x-outer'] = 'admin' }
988
+ after { response['x-outer-after'] = 'admin' }
989
+ controller :posts do
990
+ before { response['x-inner'] = 'posts' }
991
+ get(:index) { 'posts' }
992
+ end
993
+ get(:show) { 'show' }
994
+ end
995
+ get(:top) { 'top' }
996
+ end
997
+ get '/posts'
998
+ assert_equal 'posts', body
999
+ assert_equal 'admin', response['x-outer']
1000
+ assert_equal 'posts', response['x-inner']
1001
+ get '/admin/show', {}, 'HTTP_USER_AGENT' => 'Admin'
1002
+ assert_equal 'show', body
1003
+ assert_equal 'admin', response['x-outer']
1004
+ assert_nil response['x-inner']
1005
+ assert_match %r{^application/json}, response['Content-Type']
1006
+ get '/admin/show'
1007
+ assert_equal 404, status
1008
+ get '/top'
1009
+ assert_equal 200, status
1010
+ assert_equal 'top', body
1011
+ assert_nil response['x-outer']
1012
+ assert_nil response['x-outer-after']
1013
+ assert_match %r{^text/html}, response['Content-Type']
1014
+ assert_equal '/top', @app.url(:top)
1015
+ get '/early'
1016
+ assert_equal 'early', body
1017
+ assert_nil response['x-outer']
1018
+ assert_nil response['x-outer-after']
1019
+ get '/admin/top', {}, 'HTTP_USER_AGENT' => 'Admin'
1020
+ assert_equal 404, status
1021
+ end
1022
+
1023
+ it 'should not give routes defined after a nested controller block the outer map, layout or default values' do
1024
+ mock_app do
1025
+ controller :admin, map: '/manage', lang: 'en' do
1026
+ @layout = :admin_layout # what padrino-helpers' `layout :admin_layout` sets
1027
+ controller(:posts) { get(:index) { 'posts' } }
1028
+ get(:show) { 'show' }
1029
+ end
1030
+ get(:top) { 'top' }
1031
+ controller(:other) { get(:x) { 'x' } }
1032
+ end
1033
+ show = @app.routes.find { |route| route.action == :show }
1034
+ top = @app.routes.find { |route| route.action == :top }
1035
+ other = @app.routes.find { |route| route.action == :x }
1036
+ assert_equal :admin_layout, show.use_layout
1037
+ assert_equal({ lang: 'en' }, show.default_values)
1038
+ assert_nil top.use_layout
1039
+ assert_nil top.default_values
1040
+ assert_nil other.use_layout
1041
+ get '/manage/show'
1042
+ assert_equal 'show', body
1043
+ get '/top'
1044
+ assert_equal 'top', body
1045
+ end
1046
+
1047
+ it 'should restore each level of controllers nested three deep' do
1048
+ mock_app do
1049
+ controller :one, conditions: { agent: /One/ } do
1050
+ before { response['x-one'] = 'one' }
1051
+ controller :two do
1052
+ before { response['x-two'] = 'two' }
1053
+ controller :three do
1054
+ get(:index) { 'three' }
1055
+ end
1056
+ get(:in_two) { 'in two' }
1057
+ end
1058
+ get(:in_one) { 'in one' }
1059
+ end
1060
+ get(:top) { 'top' }
1061
+ end
1062
+ get '/three'
1063
+ assert_equal 'three', body
1064
+ assert_equal 'two', response['x-two']
1065
+ get '/two/in_two'
1066
+ assert_equal 'in two', body
1067
+ assert_equal 'one', response['x-one']
1068
+ assert_equal 'two', response['x-two']
1069
+ get '/one/in_one', {}, 'HTTP_USER_AGENT' => 'One'
1070
+ assert_equal 'in one', body
1071
+ assert_equal 'one', response['x-one']
1072
+ assert_nil response['x-two']
1073
+ get '/one/in_one'
1074
+ assert_equal 404, status
1075
+ get '/top'
1076
+ assert_equal 'top', body
1077
+ assert_nil response['x-one']
1078
+ assert_nil response['x-two']
1079
+ end
1080
+
1081
+ it 'should restore the outer options after sibling nested controller blocks' do
1082
+ mock_app do
1083
+ controller :admin, conditions: { agent: /Admin/ } do
1084
+ controller(:posts) { get(:index) { 'posts' } }
1085
+ controller(:users) { get(:index) { 'users' } }
1086
+ get(:show) { 'show' }
1087
+ end
1088
+ get(:top) { 'top' }
1089
+ end
1090
+ get '/posts'
1091
+ assert_equal 'posts', body
1092
+ get '/users'
1093
+ assert_equal 'users', body
1094
+ get '/admin/show', {}, 'HTTP_USER_AGENT' => 'Admin'
1095
+ assert_equal 'show', body
1096
+ get '/top'
1097
+ assert_equal 'top', body
1098
+ end
1099
+
1100
+ it 'should restore the options when a controller block raises' do
1101
+ mock_app
1102
+ assert_raises(RuntimeError) do
1103
+ @app.controller :broken, conditions: { agent: /Broken/ } do
1104
+ before { response['x-broken'] = 'broken' }
1105
+ raise 'broken controller'
1106
+ end
1107
+ end
1108
+ @app.get(:top) { 'top' }
1109
+ get '/top'
1110
+ assert_equal 'top', body
1111
+ assert_nil response['x-broken']
1112
+ end
1113
+
983
1114
  it 'should use map and with' do
984
1115
  mock_app do
985
1116
  get :index, map: '/bugs', with: :id do
@@ -2599,4 +2730,224 @@ describe 'Routing' do
2599
2730
  assert_equal 'static', body
2600
2731
  end
2601
2732
  end
2733
+
2734
+ describe "env['sinatra.route']" do
2735
+ it 'names the root route /' do
2736
+ mock_app do
2737
+ get(:index) { env['sinatra.route'] }
2738
+ controller(:admin) { get(:index) { env['sinatra.route'] } }
2739
+ end
2740
+ get '/'
2741
+ assert_equal 'GET /', body
2742
+ get '/admin'
2743
+ assert_equal 'GET /admin', body
2744
+ end
2745
+
2746
+ it 'holds the verb and the pattern, as Sinatra writes it' do
2747
+ mock_app do
2748
+ get(:index, map: '/posts') { env['sinatra.route'] }
2749
+ get('/posts/:id') { env['sinatra.route'] }
2750
+ post('/posts/:id') { env['sinatra.route'] }
2751
+ end
2752
+ get '/posts'
2753
+ assert_equal 'GET /posts', body
2754
+ get '/posts/1'
2755
+ assert_equal 'GET /posts/:id', body
2756
+ post '/posts/1'
2757
+ assert_equal 'POST /posts/:id', body
2758
+ end
2759
+
2760
+ it 'writes a regexp the way Sinatra does' do
2761
+ mock_app do
2762
+ get(%r{/plain/(\d+)}) { env['sinatra.route'] }
2763
+ get(%r{/flagged/(\d+)}i) { env['sinatra.route'] }
2764
+ get(%r{/spaced/ (\d+)}x) { env['sinatra.route'] }
2765
+ end
2766
+ get '/plain/1'
2767
+ assert_equal 'GET \/plain\/(\d+)', body
2768
+ get '/flagged/1'
2769
+ assert_equal 'GET (?i-mx:\/flagged\/(\d+))', body
2770
+ get '/spaced/1'
2771
+ assert_equal 'GET /spaced/(\d+)', body
2772
+ end
2773
+
2774
+ it 'names a regexp with anchors, which Sinatra refuses' do
2775
+ mock_app do
2776
+ get(%r{^/anchored$}) { env['sinatra.route'] }
2777
+ end
2778
+ get '/anchored'
2779
+ assert_equal 'GET ^\/anchored$', body
2780
+ end
2781
+
2782
+ it 'includes the controller prefix, the parent and the with params' do
2783
+ mock_app do
2784
+ controller :admin do
2785
+ get(:index) { env['sinatra.route'] }
2786
+ get(:show, with: :id) { env['sinatra.route'] }
2787
+ end
2788
+ controller :items, parent: :user do
2789
+ get(:index) { env['sinatra.route'] }
2790
+ end
2791
+ end
2792
+ get '/admin'
2793
+ assert_equal 'GET /admin', body
2794
+ get '/admin/show/1'
2795
+ assert_equal 'GET /admin/show/:id', body
2796
+ get '/user/1/items'
2797
+ assert_equal 'GET /user/:user_id/items', body
2798
+ end
2799
+
2800
+ it 'keeps the format of a route with provides in the pattern' do
2801
+ mock_app do
2802
+ get(:index, map: '/feed', provides: %i[html json]) { env['sinatra.route'] }
2803
+ end
2804
+ get '/feed.json'
2805
+ assert_equal 'GET /feed(.:format)?', body
2806
+ get '/feed'
2807
+ assert_equal 'GET /feed(.:format)?', body
2808
+ end
2809
+
2810
+ it 'is set for after filters, and not yet for before filters, as in Sinatra' do
2811
+ seen = []
2812
+ mock_app do
2813
+ before { seen << [:before, env['sinatra.route']] }
2814
+ after { seen << [:after, env['sinatra.route']] }
2815
+ controller :admin do
2816
+ before { seen << [:controller_before, env['sinatra.route']] }
2817
+ after { seen << [:controller_after, env['sinatra.route']] }
2818
+ get(:show, with: :id) { 'ok' }
2819
+ end
2820
+ end
2821
+ get '/admin/show/1'
2822
+ assert_equal 200, status
2823
+ assert_equal [
2824
+ [:before, nil],
2825
+ [:controller_before, nil],
2826
+ [:controller_after, 'GET /admin/show/:id'],
2827
+ [:after, 'GET /admin/show/:id']
2828
+ ], seen
2829
+ end
2830
+
2831
+ it 'stays unset when a before filter halts' do
2832
+ mock_app do
2833
+ before { halt 403, env.key?('sinatra.route').to_s }
2834
+ get('/secret') { 'never' }
2835
+ end
2836
+ get '/secret'
2837
+ assert_equal 403, status
2838
+ assert_equal 'false', body
2839
+ end
2840
+
2841
+ it 'reaches an error handler reached from a route' do
2842
+ mock_app do
2843
+ set :raise_errors, false
2844
+ set :show_exceptions, false
2845
+ error(FooError) { env['sinatra.route'] }
2846
+ get('/boom/:id') { raise FooError }
2847
+ end
2848
+ get '/boom/1'
2849
+ assert_equal 500, status
2850
+ assert_equal 'GET /boom/:id', body
2851
+ end
2852
+
2853
+ it 'names the route that answers after a pass' do
2854
+ seen = []
2855
+ mock_app do
2856
+ after { seen << env['sinatra.route'] }
2857
+ get('/posts/:id') { pass }
2858
+ get('/posts/*rest') { env['sinatra.route'] }
2859
+ end
2860
+ get '/posts/1'
2861
+ assert_equal 'GET /posts/*rest', body
2862
+ assert_equal ['GET /posts/*rest'], seen
2863
+ end
2864
+
2865
+ it 'names a HEAD request as HEAD' do
2866
+ seen = []
2867
+ mock_app do
2868
+ after { seen << env['sinatra.route'] }
2869
+ get('/posts/:id') { 'ok' }
2870
+ end
2871
+ head '/posts/1'
2872
+ assert_equal 200, status
2873
+ assert_equal ['HEAD /posts/:id'], seen
2874
+ end
2875
+
2876
+ it 'stays unset for a request that matches no route' do
2877
+ seen = []
2878
+ mock_app do
2879
+ before { seen << env.key?('sinatra.route') }
2880
+ not_found { env.key?('sinatra.route').to_s }
2881
+ get('/posts') { 'ok' }
2882
+ end
2883
+ get '/nothing'
2884
+ assert_equal 404, status
2885
+ assert_equal 'false', body
2886
+ assert_equal [false], seen
2887
+ end
2888
+
2889
+ it 'stays unset when only the verb differs' do
2890
+ mock_app do
2891
+ error(405) { env.key?('sinatra.route').to_s }
2892
+ get('/posts') { 'ok' }
2893
+ end
2894
+ post '/posts'
2895
+ assert_equal 405, status
2896
+ assert_equal 'false', body
2897
+ end
2898
+
2899
+ it 'stays unset when the only matching route fails its condition' do
2900
+ mock_app do
2901
+ not_found { env.key?('sinatra.route').to_s }
2902
+ get('/feed', provides: :json) { 'ok' }
2903
+ condition { false }
2904
+ get('/hidden') { 'ok' }
2905
+ get('/bots', agent: /bot/) { 'ok' }
2906
+ end
2907
+ ['/feed.xml', '/hidden', '/bots'].each do |path|
2908
+ get path
2909
+ assert_equal 404, status
2910
+ assert_equal 'false', body
2911
+ end
2912
+ end
2913
+
2914
+ it 'stays unset when a condition calls pass' do
2915
+ mock_app do
2916
+ not_found { env.key?('sinatra.route').to_s }
2917
+ condition { pass }
2918
+ get('/cp') { 'never' }
2919
+ end
2920
+ get '/cp'
2921
+ assert_equal 404, status
2922
+ assert_equal 'false', body
2923
+ end
2924
+
2925
+ it 'keeps the passed route when the next route fails its condition' do
2926
+ mock_app do
2927
+ not_found { env['sinatra.route'] }
2928
+ get('/posts/:id') { pass }
2929
+ condition { false }
2930
+ get('/posts/*rest') { 'never' }
2931
+ end
2932
+ get '/posts/1'
2933
+ assert_equal 404, status
2934
+ assert_equal 'GET /posts/:id', body
2935
+ end
2936
+
2937
+ it 'leaves out the mount path, at the root and under a sub-path' do
2938
+ mock_app do
2939
+ get('/posts/:id') { env['sinatra.route'] }
2940
+ end
2941
+ map = Padrino::Router.new({ path: '/', to: @app }, { path: '/blog', to: @app })
2942
+ outer_env = nil
2943
+ outer = lambda { |env|
2944
+ outer_env = env
2945
+ map.call(env)
2946
+ }
2947
+
2948
+ assert_equal 'GET /posts/:id', Rack::MockRequest.new(outer).get('/posts/1').body
2949
+ assert_equal 'GET /posts/:id', Rack::MockRequest.new(outer).get('/blog/posts/1').body
2950
+ assert_equal 'GET /posts/:id', outer_env['sinatra.route']
2951
+ end
2952
+ end
2602
2953
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: padrino-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.0
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-10-31 00:00:00.000000000 Z
14
+ date: 2026-11-01 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: ostruct
@@ -33,14 +33,14 @@ dependencies:
33
33
  requirements:
34
34
  - - '='
35
35
  - !ruby/object:Gem::Version
36
- version: 0.18.0
36
+ version: 0.18.2
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - '='
42
42
  - !ruby/object:Gem::Version
43
- version: 0.18.0
43
+ version: 0.18.2
44
44
  - !ruby/object:Gem::Dependency
45
45
  name: rackup
46
46
  requirement: !ruby/object:Gem::Requirement
@@ -223,6 +223,7 @@ files:
223
223
  - test/helper.rb
224
224
  - test/test_application.rb
225
225
  - test/test_binstub.rb
226
+ - test/test_caller.rb
226
227
  - test/test_cli.rb
227
228
  - test/test_configuration.rb
228
229
  - test/test_core.rb
@@ -246,10 +247,14 @@ files:
246
247
  - test/test_router.rb
247
248
  - test/test_routing.rb
248
249
  - test/test_server.rb
249
- homepage: http://www.padrinorb.com
250
+ homepage: https://padrinorb.com
250
251
  licenses:
251
252
  - MIT
252
- metadata: {}
253
+ metadata:
254
+ bug_tracker_uri: https://github.com/padrino/padrino-framework/issues
255
+ changelog_uri: https://github.com/padrino/padrino-framework/blob/master/CHANGES.rdoc
256
+ documentation_uri: https://padrinorb.com/guides/
257
+ source_code_uri: https://github.com/padrino/padrino-framework
253
258
  post_install_message:
254
259
  rdoc_options:
255
260
  - "--charset=UTF-8"