padrino-core 0.12.9 → 0.13.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|