merb-core 1.0.3 → 1.0.4

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