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
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- Zjc3MmQyN2MyYjcyOTU2N2YyYTE0YWJmZTExM2MxODAwZTYwNTEyMg==
5
- data.tar.gz: !binary |-
6
- ZGYwNjcyNDY1YTRkMjFlOGU5YTUwNjZiNTUwNzhjNTljNDczMzkwOA==
2
+ SHA1:
3
+ metadata.gz: dd190eb5ae35578b80e7e1f3917864308bf6be3a
4
+ data.tar.gz: 8927013bd7f6c5c190a894780efd38d842e9bc87
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- YTM0ZjYxOThkNGViZjhmYzI1YzdmYjk4MTBjOTIxOGQzYTYyYWM3ODczMjlj
10
- MTkzOTM5ZDQ0YjMzMWQ2Nzg0YjkwN2Q2ZGMyMTE3ZTQ2NzdkMTg5NDA0MTdm
11
- N2Y1Mzk0YTgwMmYyN2Y2MTRhODhlNjliMjUzZDI1MjU0NmExNmM=
12
- data.tar.gz: !binary |-
13
- OTRhOTFhZGYzN2M2MmZjYzBjNTIxNzdhNWE2MWFlM2E5MTY1YjExM2ZmNDUw
14
- ODgyODU1NmZhNjZlMjZmNTNkMjE3ZTQ1OWUxMGNlY2E2NzFkODU0MWUzNGFi
15
- NmZlZjc4NDYyZmY4ODdlNTBkMDZiNGQzNjllYjMzMmM5MDFjYmY=
6
+ metadata.gz: 6b1db6d4f4ebfd97f9a0b1383f94a8f19e8d10f4b9b71d33ee7a8b8be6ba2e5d9a5227dda9b5a57ed28595127646a508fd8cc7bce4dfa1e52400757f1bc5b468
7
+ data.tar.gz: a5ccf5a769094f21b189f30fbdf06914ad977c3c7a7caf794cbf4bf56ec361bcc5514c144b91f9c0e96c5a3de875bac84a863d27b142b23f8745b856cdd47e73
data/bin/padrino CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- require 'padrino-core/cli/binstub'
3
- Padrino.replace_with_binstub('padrino')
2
+ require 'bundler/setup' if %w(Gemfile .components).all? { |f| File.exist?(f) }
4
3
 
5
4
  padrino_core_path = File.expand_path('../../lib', __FILE__)
6
5
  $:.unshift(padrino_core_path) if File.directory?(padrino_core_path) && !$:.include?(padrino_core_path)
@@ -22,11 +22,6 @@ module Padrino
22
22
  set :method_override, true
23
23
  set :default_builder, 'StandardFormBuilder'
24
24
 
25
- # TODO: Remove this hack after getting rid of thread-unsafe http_router:
26
- if RUBY_PLATFORM == "java"
27
- set :init_mutex, Mutex.new
28
- end
29
-
30
25
  default_paths
31
26
  default_security
32
27
  global_configuration
@@ -45,9 +40,19 @@ module Padrino
45
40
  default_routes
46
41
  default_errors
47
42
  setup_locale
43
+ precompile_routes!
48
44
  @_configured = true
49
45
  end
50
46
 
47
+ def precompile_routes?
48
+ settings.respond_to?(:precompile_routes) && settings.precompile_routes?
49
+ end
50
+
51
+ def precompile_routes!
52
+ compiled_router.prepare!
53
+ compiled_router.engine.compile!
54
+ end
55
+
51
56
  private
52
57
 
53
58
  def default_paths
@@ -57,7 +62,6 @@ module Padrino
57
62
  set :uri_root, '/'
58
63
  set :public_folder, proc { Padrino.root('public', uri_root) }
59
64
  set :images_path, proc { File.join(public_folder, 'images') }
60
- set :base_url, 'http://localhost'
61
65
  end
62
66
 
63
67
  def default_security
@@ -1,3 +1,9 @@
1
+ begin
2
+ require 'active_support/core_ext/object/deep_dup' # AS 4.1
3
+ rescue LoadError
4
+ require 'active_support/core_ext/hash/deep_dup' # AS >= 3.1
5
+ end
6
+
1
7
  module Padrino
2
8
  ##
3
9
  # Padrino application module providing means for mass-assignment protection.
@@ -39,7 +45,7 @@ module Padrino
39
45
  def params(*allowed_params)
40
46
  allowed_params = prepare_allowed_params(allowed_params)
41
47
  condition do
42
- @original_params = Utils.deep_dup(params)
48
+ @original_params = params.deep_dup
43
49
  filter_params!(params, allowed_params)
44
50
  end
45
51
  end
@@ -1,6 +1,6 @@
1
1
  require 'padrino-support'
2
+ require 'padrino-core/path_router' unless defined?(PathRouter)
2
3
  require 'padrino-core/ext/sinatra'
3
- require 'padrino-core/ext/http_router'
4
4
  require 'padrino-core/filter'
5
5
 
6
6
  module Padrino
@@ -21,6 +21,14 @@ module Padrino
21
21
  # Raised when a route was invalid or cannot be processed.
22
22
  class UnrecognizedException < RuntimeError; end
23
23
 
24
+ # Raised when block arity was nonzero and was not same with
25
+ # captured parameter length.
26
+ class BlockArityError < ArgumentError
27
+ def initialize(path, block_arity, required_arity)
28
+ super "route block arity does not match path '#{path}' (#{block_arity} for #{required_arity})"
29
+ end
30
+ end
31
+
24
32
  class Parent < String
25
33
  attr_reader :map
26
34
  attr_reader :optional
@@ -250,16 +258,14 @@ module Padrino
250
258
  end
251
259
 
252
260
  ##
253
- # Using HttpRouter, for features and configurations.
261
+ # Using PathRouter, for features and configurations.
254
262
  #
255
263
  # @example
256
264
  # router.add('/greedy/:greed')
257
265
  # router.recognize('/simple')
258
266
  #
259
- # @see http://github.com/joshbuddy/http_router
260
- #
261
267
  def router
262
- @router ||= HttpRouter.new
268
+ @router ||= PathRouter.new
263
269
  block_given? ? yield(@router) : @router
264
270
  end
265
271
  alias :urls :router
@@ -268,13 +274,12 @@ module Padrino
268
274
  if @deferred_routes
269
275
  deferred_routes.each do |routes|
270
276
  routes.each do |(route, dest)|
271
- route.to(dest)
277
+ route.to(&dest)
272
278
  route.before_filters.flatten!
273
279
  route.after_filters.flatten!
274
280
  end
275
281
  end
276
282
  @deferred_routes = nil
277
- router.sort!
278
283
  end
279
284
  router
280
285
  end
@@ -311,9 +316,8 @@ module Padrino
311
316
  # # => [:foo_bar, :id => :mine]
312
317
  #
313
318
  def recognize_path(path)
314
- responses = @router.recognize(Rack::MockRequest.env_for(path))
315
- responses = responses[0] if responses[0].is_a?(Array)
316
- [responses[0].path.route.name, responses[0].params]
319
+ responses = @router.recognize_path(path)
320
+ [responses[0], responses[1]]
317
321
  end
318
322
 
319
323
  ##
@@ -336,18 +340,10 @@ module Padrino
336
340
  params = args.extract_options!
337
341
  fragment = params.delete(:fragment) || params.delete(:anchor)
338
342
  path = make_path_with_params(args, value_to_param(params.symbolize_keys))
339
- rebase_url(fragment ? path << '#' << fragment : path)
343
+ rebase_url(fragment ? path << '#' << fragment.to_s : path)
340
344
  end
341
345
  alias :url_for :url
342
346
 
343
- ##
344
- # Returns absolute url. By default adds 'http://localhost' before generated url.
345
- # To change that `set :base_url, 'http://example.com'` in your app.
346
- #
347
- def absolute_url(*args)
348
- base_url + url(*args)
349
- end
350
-
351
347
  def get(path, *args, &block)
352
348
  conditions = @conditions.dup
353
349
  route('GET', path, *args, &block)
@@ -384,7 +380,7 @@ module Padrino
384
380
  parent_prefix = parent_params.flatten.compact.uniq.map do |param|
385
381
  map = (param.respond_to?(:map) && param.map ? param.map : param.to_s)
386
382
  part = "#{map}/:#{param.to_s.singularize}_id/"
387
- part = "(#{part})" if param.respond_to?(:optional) && param.optional?
383
+ part = "(#{part})?" if param.respond_to?(:optional) && param.optional?
388
384
  part
389
385
  end
390
386
 
@@ -403,7 +399,7 @@ module Padrino
403
399
  replace_instance_variable(:@_controller, args)
404
400
  replace_instance_variable(:@_defaults, options)
405
401
  replace_instance_variable(:@filters, :before => @filters[:before].dup, :after => @filters[:after].dup)
406
- replace_instance_variable(:@layout, @layout)
402
+ replace_instance_variable(:@layout, nil)
407
403
 
408
404
  yield
409
405
 
@@ -424,7 +420,7 @@ module Padrino
424
420
  names, params_array = args.partition{ |arg| arg.is_a?(Symbol) }
425
421
  name = names[0, 2].join(" ").to_sym
426
422
  compiled_router.path(name, *(params_array << params))
427
- rescue HttpRouter::InvalidRouteException
423
+ rescue PathRouter::InvalidRouteException
428
424
  raise Padrino::Routing::UnrecognizedException, "Route mapping for url(#{name.inspect}) could not be found"
429
425
  end
430
426
 
@@ -488,7 +484,6 @@ module Padrino
488
484
  else raise
489
485
  end
490
486
 
491
- # Do padrino parsing. We dup options so we can build HEAD request correctly.
492
487
  route_options = options.dup
493
488
  route_options[:provides] = @_provides if @_provides
494
489
  route_options[:accepts] = @_accepts if @_accepts
@@ -509,7 +504,8 @@ module Padrino
509
504
  method_name = "#{verb} #{path}"
510
505
  unbound_method = generate_method(method_name, &block)
511
506
 
512
- block = if block.arity == 0
507
+ block_arity = block.arity
508
+ block = if block_arity == 0
513
509
  proc{ |request, _| unbound_method.bind(request).call }
514
510
  else
515
511
  proc{ |request, block_params| unbound_method.bind(request).call(*block_params) }
@@ -517,8 +513,8 @@ module Padrino
517
513
 
518
514
  invoke_hook(:route_added, verb, path, block)
519
515
 
520
- # HTTPRouter route construction
521
- route = router.add(path, route_options)
516
+ path[0, 0] = "/" if path == "(.:format)?"
517
+ route = router.add(verb, path, route_options)
522
518
  route.name = name if name
523
519
  route.action = action
524
520
  priority_name = options.delete(:priority) || :normal
@@ -526,16 +522,16 @@ module Padrino
526
522
  route.cache = options.key?(:cache) ? options.delete(:cache) : @_cache
527
523
  route.cache_expires = options.key?(:expires) ? options.delete(:expires) : @_expires
528
524
  route.parent = route_parents ? (route_parents.count == 1 ? route_parents.first : route_parents) : route_parents
529
- route.add_request_method(verb.downcase.to_sym)
530
525
  route.host = options.delete(:host) if options.key?(:host)
531
526
  route.user_agent = options.delete(:agent) if options.key?(:agent)
532
527
  if options.key?(:default_values)
533
528
  defaults = options.delete(:default_values)
534
- route.add_default_values(defaults) if defaults
529
+ #route.options[:default_values] = defaults if defaults
530
+ route.default_values = defaults if defaults
535
531
  end
536
- options.delete_if do |option, _args|
532
+ options.delete_if do |option, captures|
537
533
  if route.significant_variable_names.include?(option)
538
- route.add_match_with(option => Array(_args).first)
534
+ route.capture[option] = Array(captures).first
539
535
  true
540
536
  end
541
537
  end
@@ -550,6 +546,11 @@ module Padrino
550
546
 
551
547
  invoke_hook(:padrino_route_added, route, verb, path, args, options, block)
552
548
 
549
+ block_parameter_length = route.block_parameter_length
550
+ if block_arity > 0 && block_parameter_length != block_arity
551
+ fail BlockArityError.new(route.path, block_arity, block_parameter_length)
552
+ end
553
+
553
554
  # Add Application defaults.
554
555
  route.before_filters << @filters[:before]
555
556
  route.after_filters << @filters[:after]
@@ -570,7 +571,6 @@ module Padrino
570
571
  # controllers, parents, 'with' parameters, and other options.
571
572
  #
572
573
  def parse_route(path, options, verb)
573
- path = path.dup if path.kind_of?(String)
574
574
  route_options = {}
575
575
 
576
576
  if options[:params] == true
@@ -669,7 +669,7 @@ module Padrino
669
669
  # Used for calculating path in route method.
670
670
  #
671
671
  def process_path_for_provides(path)
672
- path << "(.:format)" unless path[-10, 10] == '(.:format)'
672
+ path << "(.:format)?" unless path[-11, 11] == '(.:format)?'
673
673
  end
674
674
 
675
675
  ##
@@ -723,9 +723,10 @@ module Padrino
723
723
 
724
724
  accept_format = CONTENT_TYPE_ALIASES[type] || type
725
725
  if types.include?(accept_format)
726
- content_type(accept_format || :html)
726
+ content_type(accept_format || :html, :charset => 'utf-8')
727
727
  else
728
- catch_all ? true : halt(406)
728
+ halt 406 unless catch_all
729
+ false
729
730
  end
730
731
  end
731
732
  end
@@ -743,7 +744,7 @@ module Padrino
743
744
  mime_types = types.map{ |type| mime_type(CONTENT_TYPE_ALIASES[type] || type) }
744
745
  condition do
745
746
  halt 406 unless mime_types.include?(request.media_type)
746
- content_type(mime_symbol(request.media_type))
747
+ content_type(mime_symbol(request.media_type), :charset => 'utf-8')
747
748
  end
748
749
  end
749
750
 
@@ -886,6 +887,7 @@ module Padrino
886
887
  #
887
888
  def content_type(type=nil, params={})
888
889
  return @_content_type unless type
890
+ params.delete(:charset) if type == :json
889
891
  super(type, params)
890
892
  @_content_type = type
891
893
  end
@@ -895,7 +897,7 @@ module Padrino
895
897
  def provides_any?(formats)
896
898
  accepted_format = formats.first
897
899
  type = accepted_format ? mime_symbol(accepted_format) : :html
898
- content_type(CONTENT_TYPE_ALIASES[type] || type)
900
+ content_type(CONTENT_TYPE_ALIASES[type] || type, :charset => 'utf-8')
899
901
  end
900
902
 
901
903
  def provides_format?(types, format)
@@ -905,7 +907,7 @@ module Padrino
905
907
  halt 406 if settings.respond_to?(:treat_format_as_accept) && settings.treat_format_as_accept
906
908
  false
907
909
  else
908
- content_type(format || :html)
910
+ content_type(format || :html, :charset => 'utf-8')
909
911
  end
910
912
  end
911
913
 
@@ -918,8 +920,6 @@ module Padrino
918
920
  end
919
921
 
920
922
  def dispatch!
921
- @params = defined?(Sinatra::IndifferentHash) ? Sinatra::IndifferentHash[@request.params] : indifferent_params(@request.params)
922
- force_encoding(@params)
923
923
  invoke do
924
924
  static! if settings.static? && (request.get? || request.head?)
925
925
  route!
@@ -935,19 +935,26 @@ module Padrino
935
935
  end
936
936
  end
937
937
 
938
- def route!(base=settings, pass_block=nil)
938
+ def route!(base = settings, pass_block = nil)
939
939
  Thread.current['padrino.instance'] = self
940
+ first_time = true
941
+
942
+ routes = base.compiled_router.call(@request) do |route, params|
943
+ next if route.user_agent && !(route.user_agent =~ @request.user_agent)
944
+ original_params, parent_layout = @params.dup, @layout
945
+ returned_pass_block = invoke_route(route, params, first_time)
946
+ pass_block = returned_pass_block if returned_pass_block
947
+ first_time = false if first_time
948
+ @params, @layout = original_params, parent_layout
949
+ end
940
950
 
941
- if base.compiled_router && result = base.compiled_router.call(@request.env)
942
- if result.respond_to?(:each)
943
- route_eval do
944
- response.headers.merge!(result[1])
945
- route_missing if status(result[0]) == 404
946
- route_missing if (allow = response['Allow']) && allow.include?(request.env['REQUEST_METHOD'])
947
- end
951
+ if routes.present?
952
+ verb = request.request_method
953
+ candidacies, allows = routes.partition{|route| route.verb == verb }
954
+ if candidacies.empty?
955
+ response["Allows"] = allows.map(&:verb).join(", ")
956
+ halt 405
948
957
  end
949
- else
950
- filter! :before
951
958
  end
952
959
 
953
960
  if base.superclass.respond_to?(:router)
@@ -956,9 +963,40 @@ module Padrino
956
963
  end
957
964
 
958
965
  route_eval(&pass_block) if pass_block
959
-
960
966
  route_missing
961
967
  end
968
+
969
+ def invoke_route(route, params, first_time)
970
+ @_response_buffer = nil
971
+ @route = request.route_obj = route
972
+ captured_params = captures_from_params(params)
973
+
974
+ @params.merge!(params) if params.kind_of?(Hash)
975
+ @params.merge!(:captures => captured_params) if !captured_params.empty? && route.path.is_a?(Regexp)
976
+
977
+ filter! :before if first_time
978
+
979
+ catch(:pass) do
980
+ begin
981
+ (route.before_filters - settings.filters[:before]).each{|block| instance_eval(&block) }
982
+ @layout = route.use_layout if route.use_layout
983
+ route.custom_conditions.each {|block| pass if block.bind(self).call == false }
984
+ route_response = route.block[self, captured_params]
985
+ @_response_buffer = route_response.instance_of?(Array) ? route_response.last : route_response
986
+ halt(route_response)
987
+ ensure
988
+ (route.after_filters - settings.filters[:after]).each {|block| instance_eval(&block) }
989
+ end
990
+ end
991
+ end
992
+
993
+ def captures_from_params(params)
994
+ if params[:captures].instance_of?(Array) && params[:captures].present?
995
+ params.delete(:captures)
996
+ else
997
+ params.values_at(*route.matcher.names).flatten
998
+ end
999
+ end
962
1000
  end
963
1001
  end
964
1002
  end
@@ -4,6 +4,35 @@ module Padrino
4
4
  #
5
5
  # @private
6
6
  class ShowExceptions < Sinatra::ShowExceptions
7
+ if Sinatra::VERSION <= '1.4.5'
8
+ def call(env)
9
+ @app.call(env)
10
+ rescue Exception => e
11
+ errors, env["rack.errors"] = env["rack.errors"], @@eats_errors
12
+
13
+ if prefers_plain_text?(env)
14
+ content_type = "text/plain"
15
+ exception_string = dump_exception(e)
16
+ else
17
+ content_type = "text/html"
18
+ exception_string = pretty(env, e)
19
+ end
20
+
21
+ env["rack.errors"] = errors
22
+
23
+ # Post 893a2c50 in rack/rack, the #pretty method above, implemented in
24
+ # Rack::ShowExceptions, returns a String instead of an array.
25
+ body = Array(exception_string)
26
+
27
+ [
28
+ 500,
29
+ {"Content-Type" => content_type,
30
+ "Content-Length" => Rack::Utils.bytesize(body.join).to_s},
31
+ body
32
+ ]
33
+ end
34
+ end
35
+
7
36
  private
8
37
 
9
38
  def frame_class(frame)
@@ -26,19 +26,6 @@ module Padrino
26
26
  Padrino.logger
27
27
  end
28
28
 
29
- # TODO: Remove this hack (issue #863) after getting rid of thread-unsafe http_router:
30
- if RUBY_PLATFORM == "java"
31
- alias_method :original_call, :call
32
- def call(*args)
33
- settings.init_mutex.synchronize do
34
- instance_eval{ undef :call }
35
- class_eval{ alias_method :call, :original_call }
36
- instance_eval{ undef :original_call }
37
- super(*args)
38
- end
39
- end
40
- end
41
-
42
29
  class << self
43
30
  def inherited(base)
44
31
  begun_at = Time.now
@@ -175,12 +162,6 @@ module Padrino
175
162
  set(option, *args, &block) unless respond_to?(option)
176
163
  end
177
164
 
178
- # Deprecated
179
- def load_paths
180
- warn 'Padrino::Application#load_paths is deprecated. Please, use #prerequisites'
181
- []
182
- end
183
-
184
165
  protected
185
166
 
186
167
  ##