merb-core 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -402,7 +402,7 @@ class Merb::BootLoader::Dependencies < Merb::BootLoader
402
402
  #
403
403
  # :api: private
404
404
  def self.load_dependencies
405
- dependencies.each { |dependency| Kernel.load_dependency(dependency) }
405
+ dependencies.each { |dependency| Kernel.load_dependency(dependency, nil) }
406
406
  nil
407
407
  end
408
408
 
@@ -450,7 +450,10 @@ class Merb::BootLoader::Dependencies < Merb::BootLoader
450
450
  #
451
451
  # :api: private
452
452
  def self.set_encoding
453
- $KCODE = 'UTF8' if $KCODE == 'NONE' || $KCODE.blank?
453
+ unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("1.9")
454
+ $KCODE = 'UTF8' if $KCODE == 'NONE' || $KCODE.blank?
455
+ end
456
+
454
457
  nil
455
458
  end
456
459
 
@@ -593,6 +596,7 @@ end
593
596
  class Merb::BootLoader::LoadClasses < Merb::BootLoader
594
597
  LOADED_CLASSES = {}
595
598
  MTIMES = {}
599
+ FILES_LOADED = {}
596
600
 
597
601
  class << self
598
602
 
@@ -831,10 +835,26 @@ class Merb::BootLoader::LoadClasses < Merb::BootLoader
831
835
  # nil
832
836
  #
833
837
  # :api: private
834
- def load_file(file)
835
- # Don't do this expensive operation unless we need to
836
- unless Merb::Config[:fork_for_class_load]
838
+ def load_file(file, reload = false)
839
+ Merb.logger.verbose! "#{reload ? "re" : ""}loading #{file}"
840
+
841
+ # If we're going to be reloading via constant remove,
842
+ # keep track of what constants were loaded and what files
843
+ # have been added, so that the constants can be removed
844
+ # and the files can be removed from $LOADED_FEAUTRES
845
+ if !Merb::Config[:fork_for_class_load]
846
+ if FILES_LOADED[file]
847
+ FILES_LOADED[file].each {|lf| $LOADED_FEATURES.delete(lf)}
848
+ end
849
+
837
850
  klasses = ObjectSpace.classes.dup
851
+ files_loaded = $LOADED_FEATURES.dup
852
+ end
853
+
854
+ # If we're in the midst of a reload, remove the file
855
+ # itself from $LOADED_FEATURES so it will get reloaded
856
+ if reload
857
+ $LOADED_FEATURES.delete(file) if reload
838
858
  end
839
859
 
840
860
  # Ignore the file for syntax errors. The next time
@@ -849,9 +869,11 @@ class Merb::BootLoader::LoadClasses < Merb::BootLoader
849
869
  end
850
870
  end
851
871
 
852
- # Don't do this expensive operation unless we need to
872
+ # If we're reloading via constant remove, store off the details
873
+ # after the file has been loaded
853
874
  unless Merb::Config[:fork_for_class_load]
854
875
  LOADED_CLASSES[file] = ObjectSpace.classes - klasses
876
+ FILES_LOADED[file] = $LOADED_FEATURES - files_loaded
855
877
  end
856
878
 
857
879
  nil
@@ -900,7 +922,7 @@ class Merb::BootLoader::LoadClasses < Merb::BootLoader
900
922
  if Merb::Config[:fork_for_class_load]
901
923
  reap_workers(128)
902
924
  else
903
- remove_classes_in_file(file) { |f| load_file(f) }
925
+ remove_classes_in_file(file) { |f| load_file(f, true) }
904
926
  end
905
927
  end
906
928
 
@@ -26,6 +26,9 @@ module Merb
26
26
  COOKIE_SPLIT = /[;,] */n.freeze
27
27
  COOKIE_REGEXP = /\s*(.+)=(.*)\s*/.freeze
28
28
  COOKIE_EXPIRED_TIME = Time.at(0).freeze
29
+ ACCEPT_SPLIT = /,/.freeze
30
+ SLASH_SPLIT = %r{/}.freeze
31
+ MEDIA_RANGE = /\s*([^;\s]*)\s*(;\s*q=\s*(.*))?/.freeze
29
32
  HOUR = 60 * 60
30
33
  DAY = HOUR * 24
31
34
  WEEK = DAY * 7
@@ -414,8 +414,8 @@ class Merb::AbstractController
414
414
  # :api: private
415
415
  def _evaluate_condition(condition)
416
416
  case condition
417
- when Symbol : self.send(condition)
418
- when Proc : self.instance_eval(&condition)
417
+ when Symbol then self.send(condition)
418
+ when Proc then self.instance_eval(&condition)
419
419
  else
420
420
  raise ArgumentError,
421
421
  'Filter condtions need to be either a Symbol or a Proc'
@@ -170,7 +170,7 @@ class Merb::Controller < Merb::AbstractController
170
170
  callables << (klass.public_instance_methods(false) + klass._shown_actions) - klass._hidden_actions
171
171
  klass = klass.superclass
172
172
  end until klass == Merb::AbstractController || klass == Object
173
- callables.flatten.reject{|action| action =~ /^_.*/}
173
+ callables.flatten.reject{|action| action =~ /^_.*/}.map {|x| x.to_s}
174
174
  end
175
175
 
176
176
  # The location to look for a template for a particular controller, context,
@@ -246,7 +246,7 @@ class Merb::Controller < Merb::AbstractController
246
246
  #
247
247
  # :api: plugin
248
248
  def _dispatch(action=:index)
249
- Merb.logger.info("Params: #{self.class._filter_params(request.params).inspect}")
249
+ Merb.logger.info { "Params: #{self.class._filter_params(request.params).inspect}" }
250
250
  start = Time.now
251
251
  if self.class.callable_actions.include?(action.to_s)
252
252
  super(action)
@@ -63,6 +63,8 @@ module Merb::ConditionalGetMixin
63
63
  #
64
64
  # :api: public
65
65
  def last_modified=(time)
66
+ time = time.to_time if time.is_a?(DateTime)
67
+ # time.utc.strftime("%a, %d %b %Y %X") if we could rely on locale being American
66
68
  headers[Merb::Const::LAST_MODIFIED] = time.httpdate
67
69
  end
68
70
 
@@ -72,7 +74,8 @@ module Merb::ConditionalGetMixin
72
74
  #
73
75
  # :api: public
74
76
  def last_modified
75
- Time.rfc2822(headers[Merb::Const::LAST_MODIFIED])
77
+ last_mod = headers[Merb::Const::LAST_MODIFIED]
78
+ Time.rfc2822(last_mod) if last_mod
76
79
  end
77
80
 
78
81
  # ==== Returns
@@ -513,7 +513,7 @@ module Merb::RenderMixin
513
513
  unless string || block_given?
514
514
  raise ArgumentError, "You must pass a block or a string into append_content"
515
515
  end
516
- @_caught_content[obj] = [] if @_caught_content[obj].nil?
516
+ @_caught_content[obj] = "" if @_caught_content[obj].nil?
517
517
  @_caught_content[obj] << string.to_s << (block_given? ? capture(&block) : "")
518
518
  end
519
519
 
@@ -99,6 +99,8 @@ module Merb
99
99
 
100
100
  TYPES = Dictionary.new
101
101
  MIMES = {}
102
+ MIME_MUTEX = Mutex.new
103
+ ACCEPT_RESULTS = {}
102
104
 
103
105
  class ContentTypeAlreadySet < StandardError; end
104
106
 
@@ -257,6 +259,35 @@ module Merb
257
259
  @_provided_formats -= formats.flatten
258
260
  end
259
261
 
262
+ def _accept_types
263
+ accept = request.accept
264
+
265
+ MIME_MUTEX.synchronize do
266
+ return ACCEPT_RESULTS[accept] if ACCEPT_RESULTS[accept]
267
+ end
268
+
269
+ types = request.accept.split(Merb::Const::ACCEPT_SPLIT).map do |entry|
270
+ entry =~ Merb::Const::MEDIA_RANGE
271
+ media_range, quality = $1, $3
272
+
273
+ kind, sub_type = media_range.split(Merb::Const::SLASH_SPLIT)
274
+ mime_sym = Merb.available_accepts[media_range]
275
+ mime = Merb.available_mime_types[mime_sym]
276
+ (quality ||= 0.0) if media_range == "*/*"
277
+ quality = quality ? (quality.to_f * 100).to_i : 100
278
+ quality *= (mime && mime[:default_quality] || 1)
279
+ [quality, mime_sym, media_range, kind, sub_type, mime]
280
+ end
281
+
282
+ accepts = types.sort_by {|x| x.first }.reverse!.map! {|x| x[1]}
283
+
284
+ MIME_MUTEX.synchronize do
285
+ ACCEPT_RESULTS[accept] = accepts.freeze
286
+ end
287
+
288
+ accepts
289
+ end
290
+
260
291
  # Do the content negotiation:
261
292
  # 1. if params[:format] is there, and provided, use it
262
293
  # 2. Parse the Accept header
@@ -266,16 +297,12 @@ module Merb
266
297
  #
267
298
  # :api: private
268
299
  def _perform_content_negotiation
269
- if (fmt = params[:format]) && !fmt.empty?
300
+ if fmt = params[:format]
270
301
  accepts = [fmt.to_sym]
271
- elsif request.accept =~ %r{^(text/html|\*/\*)} && _provided_formats.first == :html
272
- # Handle the common case of text/html and :html provided after checking :format
273
- return :html
274
302
  else
275
- accepts = Responder.parse(request.accept).map {|t| t.to_sym}.compact
303
+ accepts = _accept_types
276
304
  end
277
-
278
- # no need to make a bunch of method calls to _provided_formats
305
+
279
306
  provided_formats = _provided_formats
280
307
 
281
308
  specifics = accepts & provided_formats
@@ -289,6 +316,8 @@ module Merb
289
316
  raise Merb::ControllerExceptions::NotAcceptable,
290
317
  (message % [accepts.join(', '), provided_formats.join(', ')])
291
318
  end
319
+
320
+
292
321
 
293
322
  # Returns the output format for this request, based on the
294
323
  # provided formats, <tt>params[:format]</tt> and the client's HTTP
@@ -51,7 +51,8 @@ module Merb::Template
51
51
  # :api: plugin
52
52
  # @overridable
53
53
  def load_template_io(path)
54
- File.open(path, "r")
54
+ file = Dir["#{path}.{#{template_extensions.join(',')}}"].first
55
+ File.open(file, "r") if file
55
56
  end
56
57
 
57
58
  # Get the name of the template method for a particular path.
@@ -69,8 +70,8 @@ module Merb::Template
69
70
  path = File.expand_path(path)
70
71
 
71
72
  if needs_compilation?(path, locals)
72
- file = Dir["#{path}.{#{template_extensions.join(',')}}"].first
73
- METHOD_LIST[path] = file ? inline_template(load_template_io(file), locals) : nil
73
+ template_io = load_template_io(path)
74
+ METHOD_LIST[path] = inline_template(template_io, locals) if template_io
74
75
  end
75
76
 
76
77
  METHOD_LIST[path]
@@ -3,7 +3,7 @@ require 'rubygems/dependency'
3
3
  module Gem
4
4
  class Dependency
5
5
  # :api: private
6
- attr_accessor :require_block, :require_as
6
+ attr_accessor :require_block, :require_as, :original_caller
7
7
  end
8
8
  end
9
9
 
@@ -18,11 +18,12 @@ module Kernel
18
18
  # @return <Gem::Dependency> Dependency information
19
19
  #
20
20
  # :api: private
21
- def track_dependency(name, *ver, &blk)
21
+ def track_dependency(name, clr, *ver, &blk)
22
22
  options = ver.pop if ver.last.is_a?(Hash)
23
23
  new_dep = Gem::Dependency.new(name, ver.empty? ? nil : ver)
24
24
  new_dep.require_block = blk
25
25
  new_dep.require_as = (options && options[:require_as]) || name
26
+ new_dep.original_caller = clr
26
27
 
27
28
  deps = Merb::BootLoader::Dependencies.dependencies
28
29
 
@@ -110,9 +111,9 @@ module Kernel
110
111
  def dependency(name, *opts, &blk)
111
112
  immediate = opts.last.delete(:immediate) if opts.last.is_a?(Hash)
112
113
  if immediate || Merb::BootLoader.finished?(Merb::BootLoader::Dependencies)
113
- load_dependency(name, *opts, &blk)
114
+ load_dependency(name, caller, *opts, &blk)
114
115
  else
115
- track_dependency(name, *opts, &blk)
116
+ track_dependency(name, caller, *opts, &blk)
116
117
  end
117
118
  end
118
119
 
@@ -134,15 +135,19 @@ module Kernel
134
135
  # @return <Gem::Dependency> The dependency information.
135
136
  #
136
137
  # :api: private
137
- def load_dependency(name, *ver, &blk)
138
- dep = name.is_a?(Gem::Dependency) ? name : track_dependency(name, *ver, &blk)
139
- gem(dep)
140
- rescue Gem::LoadError => e
141
- Merb.fatal! "The gem #{name}, #{ver.inspect} was not found", e
142
- ensure
138
+ def load_dependency(name, clr, *ver, &blk)
139
+ begin
140
+ dep = name.is_a?(Gem::Dependency) ? name : track_dependency(name, clr, *ver, &blk)
141
+ Gem.activate(dep)
142
+ rescue Gem::LoadError => e
143
+ e.set_backtrace dep.original_caller
144
+ Merb.fatal! "The gem #{name}, #{ver.inspect} was not found", e
145
+ end
146
+
143
147
  begin
144
148
  require dep.require_as
145
149
  rescue LoadError => e
150
+ e.set_backtrace dep.original_caller
146
151
  Merb.fatal! "The file #{dep.require_as} was not found", e
147
152
  end
148
153
 
@@ -20,21 +20,27 @@ module Merb
20
20
 
21
21
  # :api: private
22
22
  def frame_details(line)
23
- filename, lineno, location = line.split(":")
24
- if filename.index(Merb.framework_root)
25
- type = "framework"
26
- shortname = Pathname.new(filename).relative_path_from(Pathname.new(Merb.framework_root))
27
- elsif filename.index(Merb.root)
28
- type = "app"
29
- shortname = Pathname.new(filename).relative_path_from(Pathname.new(Merb.root))
30
- elsif path = Gem.path.find {|p| filename.index(p)}
31
- type = "gem"
32
- shortname = Pathname.new(filename).relative_path_from(Pathname.new(path))
23
+ if (match = line.match(/^(.+):(\d+):(.+)$/))
24
+ filename = match[1]
25
+ lineno = match[2]
26
+ location = match[3]
27
+ if filename.index(Merb.framework_root) == 0
28
+ type = "framework"
29
+ shortname = Pathname.new(filename).relative_path_from(Pathname.new(Merb.framework_root))
30
+ elsif filename.index(Merb.root) == 0
31
+ type = "app"
32
+ shortname = Pathname.new(filename).relative_path_from(Pathname.new(Merb.root))
33
+ elsif path = Gem.path.find {|p| filename.index(p) == 0}
34
+ type = "gem"
35
+ shortname = Pathname.new(filename).relative_path_from(Pathname.new(path))
36
+ else
37
+ type = "other"
38
+ shortname = filename
39
+ end
40
+ [type, shortname, filename, lineno, location]
33
41
  else
34
- type = "other"
35
- shortname = filename
42
+ ['', '', '', nil, nil]
36
43
  end
37
- [type, shortname, filename, lineno, location]
38
44
  end
39
45
 
40
46
  # :api: private
@@ -57,6 +63,10 @@ module Merb
57
63
  ret.join("\n")
58
64
  end
59
65
 
66
+ def jar?(filename)
67
+ filename.match(/jar\!/)
68
+ end
69
+
60
70
  # :api: private
61
71
  def textmate_url(filename, line)
62
72
  "<a href='txmt://open?url=file://#{filename}&amp;line=#{line}'>#{line}</a>"
@@ -82,6 +92,10 @@ module Merb
82
92
  ret << "</tr>"
83
93
  ret.join("\n")
84
94
  end
95
+
96
+ def jar?(filename)
97
+ filename.match(/jar\!/)
98
+ end
85
99
  end
86
100
 
87
101
  # :api: private
@@ -53,6 +53,8 @@
53
53
  <sup class="error_<%= exception.class.status %>"><%= exception.class.status %></sup>
54
54
  </h1>
55
55
  <%= error_codes(exception) %>
56
+
57
+ <% if @show_details %>
56
58
  <p class="options">
57
59
  <label class="all">All<input type="checkbox" autocomplete="off" /></label>
58
60
  <span class="all">
@@ -63,7 +65,6 @@
63
65
  </span>
64
66
  </p>
65
67
 
66
- <% if @show_details %>
67
68
  <table class="trace">
68
69
  <% exception.backtrace.each_with_index do |line, index| %>
69
70
  <% type, shortname, filename, lineno, location = frame_details(line) %>
@@ -79,10 +80,10 @@
79
80
  <% end %>
80
81
  </td>
81
82
  <td class="line">
82
- <%= textmate_url(filename, lineno) %>
83
+ <%= textmate_url(filename, lineno) unless jar?(filename) %>
83
84
  </td>
84
85
  </tr>
85
- <%= render_source(filename, lineno) %>
86
+ <%= render_source(filename, lineno) unless jar?(filename) %>
86
87
  </tbody>
87
88
  <% end %>
88
89
  </table>
@@ -51,13 +51,13 @@ module Merb
51
51
  # :api: private
52
52
  def handle
53
53
  start = Time.now
54
- Merb.logger.info "Started request handling: #{start.to_s}"
54
+ Merb.logger.info { "Started request handling: #{start.to_s}" }
55
55
 
56
56
  find_route!
57
57
  return rack_response if handled?
58
58
 
59
59
  klass = controller
60
- Merb.logger.debug("Routed to: #{params.inspect}")
60
+ Merb.logger.debug { "Routed to: #{params.inspect}" }
61
61
 
62
62
  unless klass < Controller
63
63
  raise NotFound,
@@ -73,7 +73,7 @@ module Merb
73
73
 
74
74
  controller = dispatch_action(klass, params[:action])
75
75
  controller._benchmarks[:dispatch_time] = Time.now - start
76
- Merb.logger.info controller._benchmarks.inspect
76
+ Merb.logger.info { controller._benchmarks.inspect }
77
77
  Merb.logger.flush
78
78
  controller.rack_response
79
79
  rescue Object => exception
@@ -318,7 +318,12 @@ module Merb
318
318
  # :api: private
319
319
  def compile
320
320
  if routes.any?
321
- eval(compiled_statement, binding, "Generated Code for Router", 1)
321
+ begin
322
+ eval(compiled_statement, binding, "Generated Code for Router", 1)
323
+ rescue SyntaxError => e
324
+ puts "\nGenerated code failed:\n #{compiled_statement}"
325
+ raise e
326
+ end
322
327
  else
323
328
  reset!
324
329
  end
@@ -163,7 +163,10 @@ module Merb
163
163
  # ==== Returns
164
164
  # self:: The logger object for chaining.
165
165
  def #{name}(message = nil)
166
- self << message if #{number} >= level
166
+ if #{number} >= level
167
+ message = block_given? ? yield : message
168
+ self << message if #{number} >= level
169
+ end
167
170
  self
168
171
  end
169
172
 
@@ -177,8 +180,11 @@ module Merb
177
180
  # ==== Returns
178
181
  # self:: The logger object for chaining.
179
182
  def #{name}!(message = nil)
180
- self << message if #{number} >= level
181
- flush if #{number} >= level
183
+ if #{number} >= level
184
+ message = block_given? ? yield : message
185
+ self << message if #{number} >= level
186
+ flush if #{number} >= level
187
+ end
182
188
  self
183
189
  end
184
190