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.
Files changed (51) hide show
  1. checksums.yaml +5 -13
  2. data/bin/padrino +1 -2
  3. data/lib/padrino-core/application/application_setup.rb +10 -6
  4. data/lib/padrino-core/application/params_protection.rb +7 -1
  5. data/lib/padrino-core/application/routing.rb +88 -50
  6. data/lib/padrino-core/application/show_exceptions.rb +29 -0
  7. data/lib/padrino-core/application.rb +0 -19
  8. data/lib/padrino-core/caller.rb +1 -2
  9. data/lib/padrino-core/cli/base.rb +7 -11
  10. data/lib/padrino-core/cli/rake_tasks.rb +8 -21
  11. data/lib/padrino-core/loader.rb +13 -12
  12. data/lib/padrino-core/logger.rb +4 -32
  13. data/lib/padrino-core/mounter.rb +62 -21
  14. data/lib/padrino-core/path_router/compiler.rb +110 -0
  15. data/lib/padrino-core/path_router/error_handler.rb +8 -0
  16. data/lib/padrino-core/path_router/matcher.rb +123 -0
  17. data/lib/padrino-core/path_router/route.rb +169 -0
  18. data/lib/padrino-core/path_router.rb +119 -0
  19. data/lib/padrino-core/reloader/rack.rb +1 -4
  20. data/lib/padrino-core/reloader/storage.rb +3 -31
  21. data/lib/padrino-core/reloader.rb +12 -17
  22. data/lib/padrino-core/version.rb +1 -1
  23. data/lib/padrino-core.rb +0 -2
  24. data/padrino-core.gemspec +8 -2
  25. data/test/fixtures/apps/helpers/support.rb +1 -0
  26. data/test/fixtures/apps/{rack_apps.rb → mountable_apps/rack_apps.rb} +0 -4
  27. data/test/fixtures/apps/{static.html → mountable_apps/static.html} +0 -0
  28. data/test/fixtures/apps/precompiled_app.rb +19 -0
  29. data/test/fixtures/apps/system.rb +0 -2
  30. data/test/helper.rb +1 -1
  31. data/test/test_application.rb +18 -6
  32. data/test/test_csrf_protection.rb +6 -7
  33. data/test/test_filters.rb +18 -1
  34. data/test/test_logger.rb +1 -49
  35. data/test/test_mounter.rb +2 -4
  36. data/test/test_params_protection.rb +15 -15
  37. data/test/test_reloader_simple.rb +2 -2
  38. data/test/test_reloader_system.rb +0 -30
  39. data/test/test_restful_routing.rb +4 -4
  40. data/test/test_routing.rb +176 -54
  41. metadata +109 -49
  42. data/lib/padrino-core/cli/binstub.rb +0 -27
  43. data/lib/padrino-core/configuration.rb +0 -40
  44. data/lib/padrino-core/ext/http_router.rb +0 -201
  45. data/lib/padrino-core/mounter/application_extension.rb +0 -55
  46. data/test/fixtures/apps/custom_dependencies/custom_dependencies.rb +0 -11
  47. data/test/fixtures/apps/custom_dependencies/my_dependencies/my_dependency.rb +0 -0
  48. data/test/fixtures/apps/stealthy/app.rb +0 -7
  49. data/test/fixtures/apps/stealthy/helpers/stealthy_class_helpers.rb +0 -13
  50. data/test/test_configuration.rb +0 -29
  51. 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
- @mutex.synchronize do
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 => object_classes,
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