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