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.
- data/lib/merb-core.rb +5 -2
- data/lib/merb-core.rbc +8941 -0
- data/lib/merb-core/bootloader.rb +29 -7
- data/lib/merb-core/constants.rb +3 -0
- data/lib/merb-core/controller/abstract_controller.rb +2 -2
- data/lib/merb-core/controller/merb_controller.rb +2 -2
- data/lib/merb-core/controller/mixins/conditional_get.rb +4 -1
- data/lib/merb-core/controller/mixins/render.rb +1 -1
- data/lib/merb-core/controller/mixins/responder.rb +36 -7
- data/lib/merb-core/controller/template.rb +4 -3
- data/lib/merb-core/core_ext/kernel.rb +15 -10
- data/lib/merb-core/dispatch/default_exception/default_exception.rb +27 -13
- data/lib/merb-core/dispatch/default_exception/views/index.html.erb +4 -3
- data/lib/merb-core/dispatch/dispatcher.rb +3 -3
- data/lib/merb-core/dispatch/router.rb +6 -1
- data/lib/merb-core/logger.rb +9 -3
- data/lib/merb-core/rack/adapter/irb.rb +1 -0
- data/lib/merb-core/rack/adapter/thin.rb +2 -1
- data/lib/merb-core/test/matchers.rb +6 -4
- data/lib/merb-core/test/test_ext/rspec.rb +4 -1
- data/lib/merb-core/two-oh.rb +50 -0
- data/lib/merb-core/version.rb +2 -2
- metadata +4 -2
data/lib/merb-core/bootloader.rb
CHANGED
@@ -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
|
-
|
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
|
-
#
|
836
|
-
|
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
|
-
#
|
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
|
|
data/lib/merb-core/constants.rb
CHANGED
@@ -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
|
418
|
-
when Proc
|
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
|
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
|
-
|
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] =
|
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
|
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 =
|
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
|
-
|
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
|
-
|
73
|
-
METHOD_LIST[path] =
|
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
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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}&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
|
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
|
-
|
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
|
data/lib/merb-core/logger.rb
CHANGED
@@ -163,7 +163,10 @@ module Merb
|
|
163
163
|
# ==== Returns
|
164
164
|
# self:: The logger object for chaining.
|
165
165
|
def #{name}(message = nil)
|
166
|
-
|
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
|
-
|
181
|
-
|
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
|
|