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