padrino-core 0.9.29 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems' unless defined?(Gem) # Useful only on --dev mode
2
+ require 'rubygems' unless defined?(Gem)
3
+ require 'bundler/setup' if %w(Gemfile .components).all? { |f| File.exist?(f) }
3
4
 
4
5
  padrino_core_path = File.expand_path('../../lib', __FILE__)
5
6
  $:.unshift(padrino_core_path) if File.directory?(padrino_core_path) && !$:.include?(padrino_core_path)
@@ -80,7 +80,7 @@ module Padrino
80
80
  $KCODE='u'
81
81
  else
82
82
  Encoding.default_external = Encoding::UTF_8
83
- Encoding.default_internal = Encoding::UTF_8
83
+ Encoding.default_internal = nil # Encoding::UTF_8
84
84
  end
85
85
  nil
86
86
  end
@@ -8,9 +8,6 @@ module Padrino
8
8
  #
9
9
  class Application < Sinatra::Base
10
10
  register Padrino::Routing # Support for advanced routing, controllers, url_for
11
- unless defined?(SKIP_PADRINO_RENDERING) && SKIP_PADRINO_RENDERING
12
- register Padrino::Rendering # Support for enhanced rendering with template detection
13
- end
14
11
 
15
12
  class << self
16
13
 
@@ -18,10 +15,13 @@ module Padrino
18
15
  logger.devel "Setup #{base}"
19
16
  CALLERS_TO_IGNORE.concat(PADRINO_IGNORE_CALLERS)
20
17
  base.default_configuration!
21
- Padrino.require_dependencies File.join(base.root, "/models.rb")
22
- Padrino.require_dependencies File.join(base.root, "/models/**/*.rb")
23
- Padrino.require_dependencies File.join(base.root, "/lib.rb")
24
- Padrino.require_dependencies File.join(base.root, "/lib/**/*.rb")
18
+ base.prerequisites.concat([
19
+ File.join(base.root, "/models.rb"),
20
+ File.join(base.root, "/models/**/*.rb"),
21
+ File.join(base.root, "/lib.rb"),
22
+ File.join(base.root, "/lib/**/*.rb")
23
+ ]).uniq!
24
+ Padrino.require_dependencies(base.prerequisites)
25
25
  super(base) # Loading the subclass inherited method
26
26
  end
27
27
 
@@ -112,7 +112,7 @@ module Padrino
112
112
  def run!(options={})
113
113
  return unless Padrino.load!
114
114
  Padrino.mount(self.to_s).to("/")
115
- Padrino.run!
115
+ Padrino.run!(options)
116
116
  end
117
117
 
118
118
  ##
@@ -137,6 +137,23 @@ module Padrino
137
137
  ].map { |file| Dir[File.join(self.root, file)] }.flatten
138
138
  end
139
139
 
140
+ ##
141
+ # An array of file to load before your app.rb, basically are files wich our app depends on.
142
+ #
143
+ # By default we look for files:
144
+ #
145
+ # yourapp/models.rb
146
+ # yourapp/models/**/*.rb
147
+ # yourapp/lib.rb
148
+ # yourapp/lib/**/*.rb
149
+ #
150
+ # ==== Examples
151
+ # MyApp.prerequisites << Padrino.root('my_app', 'custom_model.rb')
152
+ #
153
+ def prerequisites
154
+ @_prerequisites ||= []
155
+ end
156
+
140
157
  protected
141
158
  ##
142
159
  # Defines default settings for Padrino application
@@ -182,8 +199,10 @@ module Padrino
182
199
  #
183
200
  def default_filters!
184
201
  before do
185
- @_content_type = :html
186
- response['Content-Type'] = 'text/html;charset=utf-8'
202
+ unless @_content_type
203
+ @_content_type = :html
204
+ response['Content-Type'] = 'text/html;charset=utf-8'
205
+ end
187
206
  end
188
207
  end
189
208
 
@@ -220,5 +239,21 @@ module Padrino
220
239
  Padrino.require_dependencies(dependencies, :force => true)
221
240
  end
222
241
  end # self
242
+
243
+ # TODO Remove deprecated render inclusion in a few versions
244
+ # Detects if a user is incorrectly using 'render' and warns them about the fix
245
+ # In 0.10.0, Padrino::Rendering now has to be explicitly included in the application
246
+ def render(*args)
247
+ if !defined?(DEFAULT_RENDERING_OPTIONS) && !@_render_included &&
248
+ (args.size == 1 || (args.size == 2 && args[0].is_a?(String) && args[1].is_a?(Hash)))
249
+ logger.warn "[Deprecation] Please 'register Padrino::Rendering' for each application as shown here:
250
+ https://gist.github.com/1d36a35794dbbd664ea4 for 'render' to function as expected"
251
+ self.class.instance_eval { register Padrino::Rendering }
252
+ @_render_included = true
253
+ render(*args)
254
+ else # pass through, rendering is valid
255
+ super(*args)
256
+ end
257
+ end # render method
223
258
  end # Application
224
259
  end # Padrino
@@ -1,10 +1,13 @@
1
- $:.unshift '/Users/joshbuddy/Development/http_router/lib'
2
-
3
1
  require 'http_router' unless defined?(HttpRouter)
4
2
  require 'padrino-core/support_lite' unless defined?(SupportLite)
5
3
 
6
4
  class Sinatra::Request #:nodoc:
7
- attr_accessor :route_obj
5
+ attr_accessor :route_obj, :runner
6
+
7
+ def runner=(runner)
8
+ @runner = runner
9
+ env['padrino.instance'] = runner
10
+ end
8
11
 
9
12
  def controller
10
13
  route_obj && route_obj.controller
@@ -12,7 +15,49 @@ class Sinatra::Request #:nodoc:
12
15
  end
13
16
 
14
17
  class HttpRouter #:nodoc:
15
- attr_accessor :runner
18
+ def rewrite_partial_path_info(env, request); end
19
+ def rewrite_path_info(env, request); end
20
+
21
+ def process_destination_path(path, env)
22
+ env['padrino.instance'].instance_eval do
23
+ request.route_obj = path.route
24
+ @_response_buffer = nil
25
+ @params ||= {}
26
+ @params.update(env['router.params'])
27
+ @block_params = if path.route.is_a?(HttpRouter::RegexRoute)
28
+ params_list = env['router.request'].extra_env['router.regex_match'].to_a
29
+ params_list.shift
30
+ @params[:captures] = params_list
31
+ params_list
32
+ else
33
+ env['router.request'].params
34
+ end
35
+ # Provide access to the current controller to the request
36
+ # Now we can eval route, but because we have "throw halt" we need to be
37
+ # (en)sure to reset old layout and run controller after filters.
38
+ old_params = @params
39
+ parent_layout = @layout
40
+ successful = false
41
+ begin
42
+ filter! :before
43
+ (path.route.before_filters - self.class.filters[:before]).each { |filter| instance_eval(&filter)} if path.route.before_filters
44
+ # If present set current controller layout
45
+ @layout = path.route.use_layout if path.route.use_layout
46
+ @route = path.route
47
+ @route.custom_conditions.each { |blk| pass if instance_eval(&blk) == false } if @route.custom_conditions
48
+ @block_params = @block_params.slice(0, path.route.dest.arity) if path.route.dest.arity > 0
49
+ halt_response = catch(:halt) { route_eval(&path.route.dest) }
50
+ @_response_buffer = halt_response.is_a?(Array) ? halt_response.last : halt_response
51
+ successful = true
52
+ halt @_response_buffer
53
+ ensure
54
+ (@_pending_after_filters ||= []).concat(path.route.after_filters) if path.route.after_filters && successful
55
+ @layout = parent_layout
56
+ @params = old_params
57
+ end
58
+ end
59
+ end
60
+
16
61
  class Route #:nodoc:
17
62
  attr_reader :before_filters, :after_filters
18
63
  attr_accessor :custom_conditions, :use_layout, :controller, :cache
@@ -36,13 +81,43 @@ class HttpRouter #:nodoc:
36
81
  end
37
82
 
38
83
  def custom_conditions=(custom_conditions)
39
- custom_conditions.each { |blk| arbitrary { |req, params| router.runner.instance_eval(&blk) != false } } if custom_conditions
40
84
  @custom_conditions = custom_conditions
41
85
  end
42
86
  end
43
87
  end
44
88
 
45
89
  module Padrino
90
+ class Filter
91
+ attr_reader :block
92
+
93
+ def initialize(mode, scoped_controller, options, args, &block)
94
+ @mode, @scoped_controller, @options, @args, @block = mode, scoped_controller, options, args, block
95
+ end
96
+
97
+ def apply?(request)
98
+ return true if @args.empty? && @options.empty?
99
+ detect = @args.any? do |arg|
100
+ case arg
101
+ when Symbol then request.route_obj.named == arg or request.route_obj.named == [@scoped_controller, arg].flatten.join("_").to_sym
102
+ else arg === request.path_info
103
+ end
104
+ end || @options.any? { |name, val|
105
+ case name
106
+ when :agent then val === request.user_agent
107
+ else val === request.send(name)
108
+ end
109
+ }
110
+ detect ^ !@mode
111
+ end
112
+
113
+ def to_proc
114
+ filter = self
115
+ proc {
116
+ instance_eval(&filter.block) if filter.apply?(request)
117
+ }
118
+ end
119
+ end
120
+
46
121
  ##
47
122
  # Padrino provides advanced routing definition support to make routes and url generation much easier.
48
123
  # This routing system supports named route aliases and easy access to url paths.
@@ -52,6 +127,7 @@ module Padrino
52
127
  #
53
128
  module Routing
54
129
  CONTENT_TYPE_ALIASES = { :htm => :html } unless defined?(CONTENT_TYPE_ALIASES)
130
+ ROUTE_PRIORITY = {:high => 0, :normal => 1, :low => 2}
55
131
 
56
132
  class UnrecognizedException < RuntimeError #:nodoc:
57
133
  end
@@ -132,6 +208,30 @@ module Padrino
132
208
  # end
133
209
  # end
134
210
  #
211
+ # You can specify conditions to run for all routes:
212
+ #
213
+ # controller :conditions => {:protect => true} do
214
+ # def self.protect(protected)
215
+ # condition do
216
+ # halt 403, "No secrets for you!" unless params[:key] == "s3cr3t"
217
+ # end if protected
218
+ # end
219
+ #
220
+ # # This route will only return "secret stuff" if the user goes to
221
+ # # `/private?key=s3cr3t`.
222
+ # get("/private") { "secret stuff" }
223
+ #
224
+ # # And this one, too!
225
+ # get("/also-private") { "secret stuff" }
226
+ #
227
+ # # But you can override the conditions for each route as needed.
228
+ # # This route will be publicly accessible without providing the
229
+ # # secret key.
230
+ # get :index, :protect => false do
231
+ # "Welcome!"
232
+ # end
233
+ # end
234
+ #
135
235
  # You can supply default values:
136
236
  #
137
237
  # controller :lang => :de do
@@ -158,11 +258,12 @@ module Padrino
158
258
  @_use_format, original_use_format = options.delete(:use_format), @_use_format
159
259
  @_cache, original_cache = options.delete(:cache), @_cache
160
260
  @_map, original_map = options.delete(:map), @_map
261
+ @_conditions, original_conditions = options.delete(:conditions), @_conditions
161
262
  @_defaults, original_defaults = options, @_defaults
162
263
 
163
264
  # Application defaults
164
- @filters, original_filters = { :before => [], :after => [] }, @filters
165
- @layout, original_layout = nil, @layout
265
+ @filters, original_filters = { :before => @filters[:before].dup, :after => @filters[:after].dup }, @filters
266
+ @layout, original_layout = nil, @layout
166
267
 
167
268
  instance_eval(&block)
168
269
 
@@ -173,13 +274,29 @@ module Padrino
173
274
  # Controller defaults
174
275
  @_controller, @_parents, @_cache = original_controller, original_parent, original_cache
175
276
  @_defaults, @_provides, @_map = original_defaults, original_provides, original_map
176
- @_use_format = original_use_format
277
+ @_conditions, @_use_format = original_conditions, original_use_format
177
278
  else
178
279
  include(*args) if extensions.any?
179
280
  end
180
281
  end
181
282
  alias :controllers :controller
182
283
 
284
+ def before(*args, &block)
285
+ add_filter :before, &(args.empty? ? block : construct_filter(*args, &block))
286
+ end
287
+
288
+ def after(*args, &block)
289
+ add_filter :after, &(args.empty? ? block : construct_filter(*args, &block))
290
+ end
291
+
292
+ def construct_filter(*args, &block)
293
+ options = args.last.is_a?(Hash) ? args.pop : {}
294
+ except = options.key?(:except) && Array(options.delete(:except))
295
+ raise("You cannot use except with other options specified") if except && (!args.empty? || !options.empty?)
296
+ options = except.last.is_a?(Hash) ? except.pop : {} if except
297
+ Filter.new(!except, @_controller, options, Array(except || args), &block)
298
+ end
299
+
183
300
  ##
184
301
  # Provides many parents with shallowing.
185
302
  #
@@ -220,17 +337,27 @@ module Padrino
220
337
  end
221
338
  alias :urls :router
222
339
 
223
- def recognition_router
224
- @recognition_router ||= HttpRouter.new
340
+ def compiled_router
341
+ if deferred_routes.empty?
342
+ router
343
+ else
344
+ deferred_routes.each { |_, routes| routes.each { |(route, dest)| route.to(dest) } }
345
+ @deferred_routes = nil
346
+ router
347
+ end
348
+ end
349
+
350
+ def deferred_routes
351
+ @deferred_routes ||= Hash[ROUTE_PRIORITY.values.sort.map{|p| [p, []]}]
225
352
  end
226
353
 
227
354
  def reset_router!
355
+ @deferred_routes = nil
228
356
  router.reset!
229
- recognition_router.reset!
230
357
  end
231
358
 
232
359
  def recognize_path(path)
233
- if response = @recognition_router.recognize(Rack::MockRequest.env_for(path))
360
+ if response = @router.recognize(Rack::MockRequest.env_for(path))
234
361
  [response.path.route.named, response.params]
235
362
  end
236
363
  end
@@ -253,15 +380,15 @@ module Padrino
253
380
  params = value_to_param(params)
254
381
  end
255
382
  url = if params_array.empty?
256
- router.url(name, params)
383
+ compiled_router.url(name, params)
257
384
  else
258
- router.url(name, *(params_array << params))
385
+ compiled_router.url(name, *(params_array << params))
259
386
  end
260
387
  url[0,0] = conform_uri(uri_root) if defined?(uri_root)
261
388
  url[0,0] = conform_uri(ENV['RACK_BASE_URI']) if ENV['RACK_BASE_URI']
262
389
  url = "/" if url.blank?
263
390
  url
264
- rescue HttpRouter::UngeneratableRouteException
391
+ rescue HttpRouter::InvalidRouteException
265
392
  route_error = "route mapping for url(#{name.inspect}) could not be found!"
266
393
  raise Padrino::Routing::UnrecognizedException.new(route_error)
267
394
  end
@@ -275,6 +402,10 @@ module Padrino
275
402
  route('HEAD', path, *args, &block)
276
403
  end
277
404
 
405
+ def current_controller
406
+ @_controller && @_controller.last
407
+ end
408
+
278
409
  private
279
410
  # Parse params from the url method
280
411
  def value_to_param(value)
@@ -318,6 +449,7 @@ module Padrino
318
449
  # get :list, :provides => :any # => "/list(.:format)"
319
450
  # get :list, :provides => [:js, :json] # => "/list.{!format,js|json}"
320
451
  # get :list, :provides => [:html, :js, :json] # => "/list(.{!format,js|json})"
452
+ # get :list, :priority => :low # Defers route to be last
321
453
  #
322
454
  def route(verb, path, *args, &block)
323
455
  options = case args.size
@@ -340,21 +472,18 @@ module Padrino
340
472
  route_options[:provides] = @_provides if @_provides
341
473
  path, *route_options[:with] = path if path.is_a?(Array)
342
474
  path, name, options = *parse_route(path, route_options, verb)
475
+ options.reverse_merge!(@_conditions) if @_conditions
343
476
 
344
477
  # Sinatra defaults
345
- define_method "#{verb} #{path}", &block
478
+ method_name = "#{verb} #{path}"
479
+ define_method(method_name, &block)
346
480
  unbound_method = instance_method("#{verb} #{path}")
481
+ remove_method(method_name)
347
482
 
348
- block =
349
- if block.arity != 0
350
- block_arity = block.arity
351
- proc {
352
- @block_params = @block_params.slice(0, block_arity) if block_arity > 0
353
- unbound_method.bind(self).call(*@block_params)
354
- }
355
- else
483
+ block_arity = block.arity
484
+ block = block_arity != 0 ?
485
+ proc { @block_params = @block_params[0, block_arity]; unbound_method.bind(self).call(*@block_params) } :
356
486
  proc { unbound_method.bind(self).call }
357
- end
358
487
 
359
488
  invoke_hook(:route_added, verb, path, block)
360
489
 
@@ -362,6 +491,8 @@ module Padrino
362
491
  route = router.add(path)
363
492
 
364
493
  route.name(name) if name
494
+ priority_name = options.delete(:priority) || :normal
495
+ priority = ROUTE_PRIORITY[priority_name] or raise("Priority #{priority_name} not recognized, try #{ROUTE_PRIORITY.keys.join(', ')}")
365
496
  route.cache = options.key?(:cache) ? options.delete(:cache) : @_cache
366
497
  route.send(verb.downcase.to_sym)
367
498
  route.host(options.delete(:host)) if options.key?(:host)
@@ -377,40 +508,25 @@ module Padrino
377
508
  end
378
509
  end
379
510
 
380
- recognition_router.add(path).name(name).to(name)
381
-
382
511
  # Add Sinatra conditions
383
- options.each { |option, args|
384
- if route.respond_to?(option)
385
- route.send(option, *args)
386
- else
387
- send(option, *args)
388
- end
389
- }
512
+ options.each { |o, a| route.respond_to?(o) ? route.send(o, *a) : send(o, *a) }
390
513
  conditions, @conditions = @conditions, []
391
514
  route.custom_conditions = conditions
392
515
 
393
516
  invoke_hook(:padrino_route_added, route, verb, path, args, options, block)
394
517
 
395
518
  # Add Application defaults
519
+ route.before_filters = @filters[:before]
520
+ route.after_filters = @filters[:after]
396
521
  if @_controller
397
- route.before_filters = @filters[:before]
398
- route.after_filters = @filters[:after]
399
- route.use_layout = @layout
400
- route.controller = Array(@_controller).first.to_s
401
- else
402
- route.before_filters = @filters[:before] || []
403
- route.after_filters = @filters[:after] || []
522
+ route.use_layout = @layout
523
+ route.controller = Array(@_controller).first.to_s
404
524
  end
405
525
 
406
- route.to(block)
526
+ deferred_routes[priority] << [route, block]
407
527
  route
408
528
  end
409
529
 
410
- def current_controller
411
- @_controller && @_controller.last
412
- end
413
-
414
530
  ##
415
531
  # Returns the final parsed route details (modified to reflect all Padrino options)
416
532
  # given the raw route. Raw route passed in could be a named alias or a string and
@@ -555,11 +671,7 @@ module Padrino
555
671
  # per rfc2616-sec14:
556
672
  # Assume */* if no ACCEPT header is given.
557
673
  catch_all = (accepts.delete "*/*" || accepts.empty?)
558
- if accepts.empty?
559
- matching_types = mime_types.slice(0,1)
560
- else
561
- matching_types = (accepts & mime_types)
562
- end
674
+ matching_types = accepts.empty? ? mime_types.slice(0,1) : (accepts & mime_types)
563
675
 
564
676
  if params[:format]
565
677
  accept_format = params[:format]
@@ -668,51 +780,29 @@ module Padrino
668
780
  end
669
781
 
670
782
  private
671
- ##
672
- # Compatibility with http_router
673
- #
783
+ def dispatch!
784
+ static! if settings.static? && (request.get? || request.head?)
785
+ route!
786
+ rescue Sinatra::NotFound => boom
787
+ handle_not_found!(boom)
788
+ rescue ::Exception => boom
789
+ handle_exception!(boom)
790
+ ensure
791
+ @_pending_after_filters.each { |filter| instance_eval(&filter)} if @_pending_after_filters
792
+ end
793
+
674
794
  def route!(base=self.class, pass_block=nil)
675
- base.router.runner = self
676
- if base.router and match = base.router.recognize(@request.env) { |match|
677
- request.route_obj = match.path.route
678
- @_response_buffer = nil
679
- if match.path.route.is_a?(HttpRouter::RegexRoute)
680
- params_list = match.request.extra_env['router.regex_match'].to_a
681
- params_list.shift
682
- @block_params = params_list
683
- @params.update({:captures => params_list}.merge(@params || {}))
684
- else
685
- @block_params = match.param_values
686
- @params.update(match.params.merge(@params || {}))
687
- end
688
- parent_layout = @layout
689
- @params ||= {}
690
- @layout = match.path.route.use_layout if match.path.route.use_layout
691
- # Provide access to the current controller to the request
692
- # Now we can eval route, but because we have "throw halt" we need to be
693
- # (en)sure to reset old layout and run controller after filters.
694
- begin
695
- old_params = @params
696
- match.path.route.before_filters.each { |filter| instance_eval(&filter) } if match.path.route.before_filters
697
- # If present set current controller layout
698
- @route = match.path.route
699
- @block_params = @block_params.slice(0, match.path.route.dest.arity) if match.path.route.dest.arity > 0
700
- match.acceptance_response = catch(:halt) { route_eval(&match.path.route.dest) } || ''
701
- ensure
702
- @layout = parent_layout
703
- (@_pending_after_filters ||= []).concat(match.path.route.after_filters) if match.path.route.after_filters
704
- @params = old_params
705
- end
706
- }
707
- if match.respond_to?(:path)
708
- throw :halt, @_response_buffer = match.acceptance_response
709
- elsif match.respond_to?(:each)
795
+ @request.env['padrino.instance'] = self
796
+ if base.compiled_router and match = base.router.call(@request.env)
797
+ if match.respond_to?(:each)
710
798
  route_eval do
711
799
  match[1].each {|k,v| response[k] = v}
712
800
  status match[0]
713
801
  route_missing if match[0] == 404
714
802
  end
715
803
  end
804
+ else
805
+ filter! :before
716
806
  end
717
807
 
718
808
  # Run routes defined in superclass.
@@ -725,7 +815,6 @@ module Padrino
725
815
 
726
816
  route_missing
727
817
  ensure
728
- @_pending_after_filters.each { |aft| instance_eval(&aft) } if @_pending_after_filters
729
818
  end
730
819
  end # InstanceMethods
731
820
  end # Routing