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.
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