padrino-core 0.12.9 → 0.13.0.beta1
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 +5 -13
- data/bin/padrino +1 -2
- data/lib/padrino-core/application/application_setup.rb +10 -6
- data/lib/padrino-core/application/params_protection.rb +7 -1
- data/lib/padrino-core/application/routing.rb +88 -50
- data/lib/padrino-core/application/show_exceptions.rb +29 -0
- data/lib/padrino-core/application.rb +0 -19
- data/lib/padrino-core/caller.rb +1 -2
- data/lib/padrino-core/cli/base.rb +7 -11
- data/lib/padrino-core/cli/rake_tasks.rb +8 -21
- data/lib/padrino-core/loader.rb +13 -12
- data/lib/padrino-core/logger.rb +4 -32
- data/lib/padrino-core/mounter.rb +62 -21
- data/lib/padrino-core/path_router/compiler.rb +110 -0
- data/lib/padrino-core/path_router/error_handler.rb +8 -0
- data/lib/padrino-core/path_router/matcher.rb +123 -0
- data/lib/padrino-core/path_router/route.rb +169 -0
- data/lib/padrino-core/path_router.rb +119 -0
- data/lib/padrino-core/reloader/rack.rb +1 -4
- data/lib/padrino-core/reloader/storage.rb +3 -31
- data/lib/padrino-core/reloader.rb +12 -17
- data/lib/padrino-core/version.rb +1 -1
- data/lib/padrino-core.rb +0 -2
- data/padrino-core.gemspec +8 -2
- data/test/fixtures/apps/helpers/support.rb +1 -0
- data/test/fixtures/apps/{rack_apps.rb → mountable_apps/rack_apps.rb} +0 -4
- data/test/fixtures/apps/{static.html → mountable_apps/static.html} +0 -0
- data/test/fixtures/apps/precompiled_app.rb +19 -0
- data/test/fixtures/apps/system.rb +0 -2
- data/test/helper.rb +1 -1
- data/test/test_application.rb +18 -6
- data/test/test_csrf_protection.rb +6 -7
- data/test/test_filters.rb +18 -1
- data/test/test_logger.rb +1 -49
- data/test/test_mounter.rb +2 -4
- data/test/test_params_protection.rb +15 -15
- data/test/test_reloader_simple.rb +2 -2
- data/test/test_reloader_system.rb +0 -30
- data/test/test_restful_routing.rb +4 -4
- data/test/test_routing.rb +176 -54
- metadata +109 -49
- data/lib/padrino-core/cli/binstub.rb +0 -27
- data/lib/padrino-core/configuration.rb +0 -40
- data/lib/padrino-core/ext/http_router.rb +0 -201
- data/lib/padrino-core/mounter/application_extension.rb +0 -55
- data/test/fixtures/apps/custom_dependencies/custom_dependencies.rb +0 -11
- data/test/fixtures/apps/custom_dependencies/my_dependencies/my_dependency.rb +0 -0
- data/test/fixtures/apps/stealthy/app.rb +0 -7
- data/test/fixtures/apps/stealthy/helpers/stealthy_class_helpers.rb +0 -13
- data/test/test_configuration.rb +0 -29
- data/test/test_reloader_storage.rb +0 -51
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'mustermann/sinatra'
|
2
|
+
|
3
|
+
module Padrino
|
4
|
+
module PathRouter
|
5
|
+
class Matcher
|
6
|
+
# To count group of regexp
|
7
|
+
GROUP_REGEXP = %r{\((?!\?:|\?!|\?<=|\?<!|\?=).+?\)}.freeze
|
8
|
+
|
9
|
+
##
|
10
|
+
# Constructs an instance of PathRouter::Matcher.
|
11
|
+
#
|
12
|
+
def initialize(path, options = {})
|
13
|
+
@path = path.is_a?(String) && path.empty? ? "/" : path
|
14
|
+
@capture = options[:capture]
|
15
|
+
@default_values = options[:default_values]
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Matches a pattern with the route matcher.
|
20
|
+
#
|
21
|
+
def match(pattern)
|
22
|
+
if match_data = handler.match(pattern)
|
23
|
+
match_data
|
24
|
+
elsif mustermann? && pattern != "/" && pattern.end_with?("/")
|
25
|
+
handler.match(pattern[0..-2])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Returns a regexp from handler.
|
31
|
+
#
|
32
|
+
def to_regexp
|
33
|
+
mustermann? ? handler.to_regexp : handler
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Expands the path by using parameters.
|
38
|
+
#
|
39
|
+
def expand(params)
|
40
|
+
params = params.merge(@default_values) if @default_values.is_a?(Hash)
|
41
|
+
expanded_path = handler.expand(:append, params)
|
42
|
+
expanded_path
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Returns true if handler is an instance of Mustermann.
|
47
|
+
#
|
48
|
+
def mustermann?
|
49
|
+
handler.instance_of?(Mustermann::Sinatra)
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Builds a parameters, and returns them.
|
54
|
+
#
|
55
|
+
def params_for(pattern, others)
|
56
|
+
data = match(pattern)
|
57
|
+
params = indifferent_hash
|
58
|
+
if data.names.empty?
|
59
|
+
params.merge!(:captures => data.captures) unless data.captures.empty?
|
60
|
+
else
|
61
|
+
if mustermann?
|
62
|
+
new_params = handler.params(pattern, :captures => data)
|
63
|
+
params.merge!(new_params) if new_params
|
64
|
+
elsif data
|
65
|
+
params.merge!(Hash[names.zip(data.captures)])
|
66
|
+
end
|
67
|
+
params.merge!(others){ |_, old, new| old || new }
|
68
|
+
end
|
69
|
+
params
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Returns the handler which is an instance of Mustermann or Regexp.
|
74
|
+
#
|
75
|
+
def handler
|
76
|
+
@handler ||=
|
77
|
+
case @path
|
78
|
+
when String
|
79
|
+
Mustermann.new(@path, :capture => @capture)
|
80
|
+
when Regexp
|
81
|
+
/^(?:#{@path})$/
|
82
|
+
else
|
83
|
+
@path
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Converts the handler into string.
|
89
|
+
#
|
90
|
+
def to_s
|
91
|
+
handler.to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Returns names of the handler.
|
96
|
+
# @see Regexp#names
|
97
|
+
#
|
98
|
+
def names
|
99
|
+
handler.names
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Returns captures parameter length.
|
104
|
+
#
|
105
|
+
def capture_length
|
106
|
+
if mustermann?
|
107
|
+
handler.named_captures.inject(0) { |count, (_, capture)| count += capture.length }
|
108
|
+
else
|
109
|
+
handler.inspect.scan(GROUP_REGEXP).length
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
##
|
116
|
+
# Creates a hash with indifferent access.
|
117
|
+
#
|
118
|
+
def indifferent_hash
|
119
|
+
Hash.new{ |hash, key| hash[key.to_s] if key.instance_of?(Symbol) }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module Padrino
|
2
|
+
module PathRouter
|
3
|
+
class Route
|
4
|
+
##
|
5
|
+
# The accessors are useful to access from PathRouter::Router
|
6
|
+
#
|
7
|
+
attr_accessor :name, :capture, :order, :options, :index
|
8
|
+
|
9
|
+
##
|
10
|
+
# A reader for compile option
|
11
|
+
#
|
12
|
+
attr_reader :verb, :block
|
13
|
+
|
14
|
+
##
|
15
|
+
# The router will be treated in this class
|
16
|
+
#
|
17
|
+
attr_writer :router
|
18
|
+
|
19
|
+
##
|
20
|
+
# The accessors will be used in other classes
|
21
|
+
#
|
22
|
+
attr_accessor :action, :cache, :cache_key, :cache_expires,
|
23
|
+
:parent, :use_layout, :controller, :user_agent, :path_for_generation, :default_values
|
24
|
+
|
25
|
+
##
|
26
|
+
# Constructs an instance of PathRouter::Route.
|
27
|
+
#
|
28
|
+
def initialize(path, verb, options = {}, &block)
|
29
|
+
@path = path
|
30
|
+
@verb = verb
|
31
|
+
@capture = {}
|
32
|
+
@order = 0
|
33
|
+
@block = block if block_given?
|
34
|
+
merge_with_options!(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Calls the route block with arguments.
|
39
|
+
#
|
40
|
+
def call(app, *args)
|
41
|
+
@block.call(app, *args)
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Returns the route's verb as an array.
|
46
|
+
#
|
47
|
+
def request_methods
|
48
|
+
[verb.to_s.upcase]
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Returns the original path.
|
53
|
+
#
|
54
|
+
def original_path
|
55
|
+
@path
|
56
|
+
end
|
57
|
+
|
58
|
+
SIGNIFICANT_VARIABLES_REGEX = /(^|[^\\])[:\*]([a-zA-Z0-9_]+)/.freeze
|
59
|
+
|
60
|
+
##
|
61
|
+
# Returns signficant variable names.
|
62
|
+
#
|
63
|
+
def significant_variable_names
|
64
|
+
@significant_variable_names ||=
|
65
|
+
if @path.is_a?(String)
|
66
|
+
@path.scan(SIGNIFICANT_VARIABLES_REGEX).map{ |p| p.last.to_sym }
|
67
|
+
elsif @path.is_a?(Regexp) and @path.respond_to?(:named_captures)
|
68
|
+
@path.named_captures.keys.map(&:to_sym)
|
69
|
+
else
|
70
|
+
[]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Returns an instance of PathRouter::Matcher that is associated with the route.
|
76
|
+
#
|
77
|
+
def matcher
|
78
|
+
@matcher ||= Matcher.new(@path, :capture => @capture, :default_values => default_values)
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# @see PathRouter::Matcher#match
|
83
|
+
#
|
84
|
+
def match(pattern)
|
85
|
+
matcher.match(pattern)
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Associates a block with the route, and increments current order of the router.
|
90
|
+
#
|
91
|
+
def to(&block)
|
92
|
+
@block = block if block_given?
|
93
|
+
@order = @router.current_order
|
94
|
+
@router.increment_order
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Expands the path by using parameters.
|
99
|
+
# @see PathRouter::Matcher#expand
|
100
|
+
#
|
101
|
+
def path(*args)
|
102
|
+
return @path if args.empty?
|
103
|
+
params = args[0].dup
|
104
|
+
params.delete(:captures)
|
105
|
+
matcher.expand(params) if matcher.mustermann?
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Returns parameters which is created by the matcher.
|
110
|
+
#
|
111
|
+
def params_for(pattern, others = {})
|
112
|
+
matcher.params_for(pattern, others)
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Returns before_filters as an array.
|
117
|
+
#
|
118
|
+
def before_filters(&block)
|
119
|
+
@_before_filters ||= []
|
120
|
+
@_before_filters << block if block_given?
|
121
|
+
@_before_filters
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Returns after_filters as an array.
|
126
|
+
#
|
127
|
+
def after_filters(&block)
|
128
|
+
@_after_filters ||= []
|
129
|
+
@_after_filters << block if block_given?
|
130
|
+
@_after_filters
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Returns custom_conditions as an array.
|
135
|
+
#
|
136
|
+
def custom_conditions(&block)
|
137
|
+
@_custom_conditions ||= []
|
138
|
+
@_custom_conditions << block if block_given?
|
139
|
+
@_custom_conditions
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Returns block parameter length.
|
144
|
+
#
|
145
|
+
def block_parameter_length
|
146
|
+
matcher.capture_length
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
##
|
152
|
+
# Set value to accessor if option name has been defined as an accessora.
|
153
|
+
#
|
154
|
+
def merge_with_options!(options)
|
155
|
+
@options ||= {}
|
156
|
+
options.each_pair do |key, value|
|
157
|
+
accessor?(key) ? __send__("#{key}=", value) : (@options[key] = value)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# Returns true if name has been defined as an accessor.
|
163
|
+
#
|
164
|
+
def accessor?(key)
|
165
|
+
respond_to?("#{key}=") && respond_to?(key)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'padrino-core/path_router/error_handler'
|
2
|
+
require 'padrino-core/path_router/route'
|
3
|
+
require 'padrino-core/path_router/matcher'
|
4
|
+
require 'padrino-core/path_router/compiler'
|
5
|
+
|
6
|
+
module Padrino
|
7
|
+
##
|
8
|
+
# Provides an HTTP router for use in path routing.
|
9
|
+
#
|
10
|
+
module PathRouter
|
11
|
+
##
|
12
|
+
# Constructs an instance of PathRouter::Router.
|
13
|
+
#
|
14
|
+
def self.new
|
15
|
+
Router.new
|
16
|
+
end
|
17
|
+
|
18
|
+
class Router
|
19
|
+
attr_reader :current_order, :routes, :engine
|
20
|
+
|
21
|
+
##
|
22
|
+
# Constructs an instance of PathRouter::Router.
|
23
|
+
#
|
24
|
+
def initialize
|
25
|
+
reset!
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Adds a new route to routes.
|
30
|
+
#
|
31
|
+
def add(verb, path, options = {}, &block)
|
32
|
+
route = Route.new(path, verb, options, &block)
|
33
|
+
route.router = self
|
34
|
+
@routes << route
|
35
|
+
route
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Returns all routes which are matched with the condition
|
40
|
+
#
|
41
|
+
def call(request, &block)
|
42
|
+
prepare! unless prepared?
|
43
|
+
@engine.call_by_request(request, &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Returns all routes which are matched with the condition without block
|
48
|
+
#
|
49
|
+
def recognize(request_or_env)
|
50
|
+
prepare! unless prepared?
|
51
|
+
@engine.find_by(request_or_env)
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Finds a path which is matched with conditions from arguments
|
56
|
+
#
|
57
|
+
def path(name, *args)
|
58
|
+
params = args.extract_options!
|
59
|
+
@routes.each do |route|
|
60
|
+
next unless route.name == name
|
61
|
+
matcher = route.matcher
|
62
|
+
params_for_expand = params.dup
|
63
|
+
if !args.empty? && matcher.mustermann?
|
64
|
+
matcher.names.each_with_index do |matcher_name, index|
|
65
|
+
params_for_expand[matcher_name.to_sym] ||= args[index]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
return matcher.mustermann? ? matcher.expand(params_for_expand) : route.path_for_generation
|
69
|
+
end
|
70
|
+
fail InvalidRouteException
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Recognizes route and expanded params from a path.
|
75
|
+
#
|
76
|
+
def recognize_path(path_info)
|
77
|
+
prepare! unless prepared?
|
78
|
+
route = @engine.find_by_pattern(path_info).first
|
79
|
+
[route.name, route.params_for(path_info, {})]
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Resets all routes, current order and preparation.
|
84
|
+
#
|
85
|
+
def reset!
|
86
|
+
@routes = []
|
87
|
+
@current_order = 0
|
88
|
+
@prepared = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Increments the order.
|
93
|
+
#
|
94
|
+
def increment_order
|
95
|
+
@current_order += 1
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Constructs an instance of PathRouter::Compiler,
|
100
|
+
# and sorts all routes by using the order.
|
101
|
+
#
|
102
|
+
def prepare!
|
103
|
+
@engine = Compiler.new(@routes)
|
104
|
+
@prepared = true
|
105
|
+
return if @current_order.zero?
|
106
|
+
@routes.sort_by!(&:order)
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
##
|
112
|
+
# Returns true if the router has been prepared.
|
113
|
+
#
|
114
|
+
def prepared?
|
115
|
+
!!@prepared
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -11,15 +11,12 @@ module Padrino
|
|
11
11
|
@app = app
|
12
12
|
@cooldown = cooldown
|
13
13
|
@last = (Time.now - cooldown)
|
14
|
-
@mutex = Mutex.new
|
15
14
|
end
|
16
15
|
|
17
16
|
# Invoked in order to perform the reload as part of the request stack.
|
18
17
|
def call(env)
|
19
18
|
if @cooldown && Time.now > @last + @cooldown
|
20
|
-
|
21
|
-
Padrino.reload!
|
22
|
-
end
|
19
|
+
Thread.list.size > 1 ? Thread.exclusive { Padrino.reload! } : Padrino.reload!
|
23
20
|
@last = Time.now
|
24
21
|
end
|
25
22
|
@app.call(env)
|
@@ -22,7 +22,7 @@ module Padrino
|
|
22
22
|
file = remove(name)
|
23
23
|
@old_entries ||= {}
|
24
24
|
@old_entries[name] = {
|
25
|
-
:constants =>
|
25
|
+
:constants => ObjectSpace.classes,
|
26
26
|
:features => old_features = Set.new($LOADED_FEATURES.dup)
|
27
27
|
}
|
28
28
|
features = file && file[:features] || []
|
@@ -32,7 +32,7 @@ module Padrino
|
|
32
32
|
|
33
33
|
def commit(name)
|
34
34
|
entry = {
|
35
|
-
:constants => new_classes(@old_entries[name][:constants]),
|
35
|
+
:constants => ObjectSpace.new_classes(@old_entries[name][:constants]),
|
36
36
|
:features => Set.new($LOADED_FEATURES) - @old_entries[name][:features] - [name]
|
37
37
|
}
|
38
38
|
files[name] = entry
|
@@ -40,7 +40,7 @@ module Padrino
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def rollback(name)
|
43
|
-
new_constants = new_classes(@old_entries[name][:constants])
|
43
|
+
new_constants = ObjectSpace.new_classes(@old_entries[name][:constants])
|
44
44
|
new_constants.each{ |klass| Reloader.remove_constant(klass) }
|
45
45
|
@old_entries.delete(name)
|
46
46
|
end
|
@@ -50,34 +50,6 @@ module Padrino
|
|
50
50
|
def files
|
51
51
|
@files ||= {}
|
52
52
|
end
|
53
|
-
|
54
|
-
##
|
55
|
-
# Returns all the classes in the object space.
|
56
|
-
#
|
57
|
-
def object_classes
|
58
|
-
klasses = Set.new
|
59
|
-
|
60
|
-
ObjectSpace.each_object(Class).each do |klass|
|
61
|
-
if block_given?
|
62
|
-
if filtered_class = yield(klass)
|
63
|
-
klasses << filtered_class
|
64
|
-
end
|
65
|
-
else
|
66
|
-
klasses << klass
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
klasses
|
71
|
-
end
|
72
|
-
|
73
|
-
##
|
74
|
-
# Returns a list of object space classes that are not included in "snapshot".
|
75
|
-
#
|
76
|
-
def new_classes(snapshot)
|
77
|
-
object_classes do |klass|
|
78
|
-
snapshot.include?(klass) ? nil : klass
|
79
|
-
end
|
80
|
-
end
|
81
53
|
end
|
82
54
|
end
|
83
55
|
end
|