padrino-core 0.9.29 → 0.10.0

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