merb-core 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +22 -13
- data/lib/merb-core/bootloader.rb +3 -2
- data/lib/merb-core/config.rb +1 -1
- data/lib/merb-core/constants.rb +3 -1
- data/lib/merb-core/controller/abstract_controller.rb +25 -39
- data/lib/merb-core/controller/exceptions.rb +14 -16
- data/lib/merb-core/controller/merb_controller.rb +2 -2
- data/lib/merb-core/controller/mime.rb +2 -1
- data/lib/merb-core/controller/mixins/render.rb +12 -0
- data/lib/merb-core/controller/mixins/responder.rb +31 -7
- data/lib/merb-core/core_ext/hash.rb +7 -0
- data/lib/merb-core/core_ext/kernel.rb +31 -34
- data/lib/merb-core/core_ext.rb +1 -0
- data/lib/merb-core/dispatch/default_exception/default_exception.rb +3 -3
- data/lib/merb-core/dispatch/dispatcher.rb +128 -135
- data/lib/merb-core/dispatch/request.rb +11 -11
- data/lib/merb-core/dispatch/router/behavior.rb +6 -6
- data/lib/merb-core/dispatch/router.rb +5 -5
- data/lib/merb-core/logger.rb +203 -202
- data/lib/merb-core/rack/application.rb +19 -5
- data/lib/merb-core/rack/middleware/conditional_get.rb +23 -0
- data/lib/merb-core/rack/middleware/content_length.rb +18 -0
- data/lib/merb-core/rack/middleware/tracer.rb +20 -0
- data/lib/merb-core/rack/middleware.rb +1 -7
- data/lib/merb-core/rack.rb +19 -16
- data/lib/merb-core/test/matchers/route_matchers.rb +1 -0
- data/lib/merb-core/test/matchers/view_matchers.rb +36 -0
- data/lib/merb-core/test/run_specs.rb +2 -1
- data/lib/merb-core/version.rb +1 -1
- data/lib/merb-core.rb +39 -33
- data/spec/private/config/merb_spec.rb +34 -0
- data/spec/private/dispatch/fixture/log/merb_test.log +372 -0
- data/spec/private/router/fixture/log/merb_test.log +42 -0
- data/spec/public/abstract_controller/controllers/filters.rb +50 -1
- data/spec/public/abstract_controller/filter_spec.rb +25 -0
- data/spec/public/controller/base_spec.rb +41 -1
- data/spec/public/controller/controllers/base.rb +16 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.html.erb +1 -1
- data/spec/public/controller/dispatcher_spec.rb +24 -25
- data/spec/public/controller/responder_spec.rb +6 -0
- data/spec/public/core_ext/kernel_spec.rb +79 -0
- data/spec/public/directory_structure/directory/log/merb_test.log +245 -0
- data/spec/public/rack/conditinal_get_middleware_spec.rb +139 -0
- data/spec/public/rack/rack_middleware_spec.rb +99 -0
- data/spec/public/rack/shared_example_groups.rb +35 -0
- data/spec/public/reloading/directory/log/merb_test.log +40 -0
- data/spec/public/request/request_spec.rb +0 -5
- data/spec/public/router/fixture/log/merb_test.log +348 -0
- data/spec/public/test/route_matchers_spec.rb +4 -0
- data/spec/spec_helper.rb +7 -1
- metadata +42 -5
- data/spec/private/plugins/plugin_spec.rb +0 -166
data/lib/merb-core/logger.rb
CHANGED
@@ -1,202 +1,203 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# Merb.logger.
|
13
|
-
# Merb.logger.
|
14
|
-
# Merb.logger.
|
15
|
-
# Merb.logger.
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# Merb.logger.
|
20
|
-
# Merb.logger.
|
21
|
-
# Merb.logger.
|
22
|
-
# Merb.logger.
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
1
|
+
Merb::Logger = Extlib::Logger
|
2
|
+
# require "time" # httpdate
|
3
|
+
# # ==== Public Merb Logger API
|
4
|
+
# #
|
5
|
+
# # To replace an existing logger with a new one:
|
6
|
+
# # Merb::Logger.set_log(log{String, IO},level{Symbol, String})
|
7
|
+
# #
|
8
|
+
# # Available logging levels are
|
9
|
+
# # Merb::Logger::{ Fatal, Error, Warn, Info, Debug }
|
10
|
+
# #
|
11
|
+
# # Logging via:
|
12
|
+
# # Merb.logger.fatal(message<String>,&block)
|
13
|
+
# # Merb.logger.error(message<String>,&block)
|
14
|
+
# # Merb.logger.warn(message<String>,&block)
|
15
|
+
# # Merb.logger.info(message<String>,&block)
|
16
|
+
# # Merb.logger.debug(message<String>,&block)
|
17
|
+
# #
|
18
|
+
# # Logging with autoflush:
|
19
|
+
# # Merb.logger.fatal!(message<String>,&block)
|
20
|
+
# # Merb.logger.error!(message<String>,&block)
|
21
|
+
# # Merb.logger.warn!(message<String>,&block)
|
22
|
+
# # Merb.logger.info!(message<String>,&block)
|
23
|
+
# # Merb.logger.debug!(message<String>,&block)
|
24
|
+
# #
|
25
|
+
# # Flush the buffer to
|
26
|
+
# # Merb.logger.flush
|
27
|
+
# #
|
28
|
+
# # Remove the current log object
|
29
|
+
# # Merb.logger.close
|
30
|
+
# #
|
31
|
+
# # ==== Private Merb Logger API
|
32
|
+
# #
|
33
|
+
# # To initialize the logger you create a new object, proxies to set_log.
|
34
|
+
# # Merb::Logger.new(log{String, IO},level{Symbol, String})
|
35
|
+
# module Merb
|
36
|
+
#
|
37
|
+
# class << self
|
38
|
+
# attr_accessor :logger
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# class Logger
|
42
|
+
#
|
43
|
+
# attr_accessor :level
|
44
|
+
# attr_accessor :delimiter
|
45
|
+
# attr_accessor :auto_flush
|
46
|
+
# attr_reader :buffer
|
47
|
+
# attr_reader :log
|
48
|
+
# attr_reader :init_args
|
49
|
+
#
|
50
|
+
# # ==== Notes
|
51
|
+
# # Ruby (standard) logger levels:
|
52
|
+
# # :fatal:: An unhandleable error that results in a program crash
|
53
|
+
# # :error:: A handleable error condition
|
54
|
+
# # :warn:: A warning
|
55
|
+
# # :info:: generic (useful) information about system operation
|
56
|
+
# # :debug:: low-level information for developers
|
57
|
+
# Levels =
|
58
|
+
# {
|
59
|
+
# :fatal => 7,
|
60
|
+
# :error => 6,
|
61
|
+
# :warn => 4,
|
62
|
+
# :info => 3,
|
63
|
+
# :debug => 0
|
64
|
+
# } unless const_defined?(:Levels)
|
65
|
+
#
|
66
|
+
# private
|
67
|
+
#
|
68
|
+
# # Readies a log for writing.
|
69
|
+
# #
|
70
|
+
# # ==== Parameters
|
71
|
+
# # log<IO, String>:: Either an IO object or a name of a logfile.
|
72
|
+
# def initialize_log(log)
|
73
|
+
# close if @log # be sure that we don't leave open files laying around.
|
74
|
+
#
|
75
|
+
# if log.respond_to?(:write)
|
76
|
+
# @log = log
|
77
|
+
# elsif File.exist?(log)
|
78
|
+
# @log = open(log, (File::WRONLY | File::APPEND))
|
79
|
+
# @log.sync = true
|
80
|
+
# else
|
81
|
+
# FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
|
82
|
+
# @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
|
83
|
+
# @log.sync = true
|
84
|
+
# @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# public
|
89
|
+
#
|
90
|
+
# # To initialize the logger you create a new object, proxies to set_log.
|
91
|
+
# #
|
92
|
+
# # ==== Parameters
|
93
|
+
# # *args:: Arguments to create the log from. See set_logs for specifics.
|
94
|
+
# def initialize(*args)
|
95
|
+
# @init_args = args
|
96
|
+
# set_log(*args)
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# # Replaces an existing logger with a new one.
|
100
|
+
# #
|
101
|
+
# # ==== Parameters
|
102
|
+
# # log<IO, String>:: Either an IO object or a name of a logfile.
|
103
|
+
# # log_level<~to_sym>::
|
104
|
+
# # The log level from, e.g. :fatal or :info. Defaults to :error in the
|
105
|
+
# # production environment and :debug otherwise.
|
106
|
+
# # delimiter<String>::
|
107
|
+
# # Delimiter to use between message sections. Defaults to " ~ ".
|
108
|
+
# # auto_flush<Boolean>::
|
109
|
+
# # Whether the log should automatically flush after new messages are
|
110
|
+
# # added. Defaults to false.
|
111
|
+
# def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false)
|
112
|
+
# if log_level && Levels[log_level.to_sym]
|
113
|
+
# @level = Levels[log_level.to_sym]
|
114
|
+
# elsif Merb.environment == "production"
|
115
|
+
# @level = Levels[:warn]
|
116
|
+
# else
|
117
|
+
# @level = Levels[:debug]
|
118
|
+
# end
|
119
|
+
# @buffer = []
|
120
|
+
# @delimiter = delimiter
|
121
|
+
# @auto_flush = auto_flush
|
122
|
+
#
|
123
|
+
# initialize_log(log)
|
124
|
+
#
|
125
|
+
# Merb.logger = self
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
# # Flush the entire buffer to the log object.
|
129
|
+
# def flush
|
130
|
+
# return unless @buffer.size > 0
|
131
|
+
# @log.write(@buffer.slice!(0..-1).to_s)
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# # Close and remove the current log object.
|
135
|
+
# def close
|
136
|
+
# flush
|
137
|
+
# @log.close if @log.respond_to?(:close) && !@log.tty?
|
138
|
+
# @log = nil
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# # Appends a message to the log. The methods yield to an optional block and
|
142
|
+
# # the output of this block will be appended to the message.
|
143
|
+
# #
|
144
|
+
# # ==== Parameters
|
145
|
+
# # string<String>:: The message to be logged. Defaults to nil.
|
146
|
+
# #
|
147
|
+
# # ==== Returns
|
148
|
+
# # String:: The resulting message added to the log file.
|
149
|
+
# def <<(string = nil)
|
150
|
+
# message = ""
|
151
|
+
# message << delimiter
|
152
|
+
# message << string if string
|
153
|
+
# message << "\n" unless message[-1] == ?\n
|
154
|
+
# @buffer << message
|
155
|
+
# flush if @auto_flush
|
156
|
+
#
|
157
|
+
# message
|
158
|
+
# end
|
159
|
+
# alias :push :<<
|
160
|
+
#
|
161
|
+
# # Generate the logging methods for Merb.logger for each log level.
|
162
|
+
# Levels.each_pair do |name, number|
|
163
|
+
# class_eval <<-LEVELMETHODS, __FILE__, __LINE__
|
164
|
+
#
|
165
|
+
# # Appends a message to the log if the log level is at least as high as
|
166
|
+
# # the log level of the logger.
|
167
|
+
# #
|
168
|
+
# # ==== Parameters
|
169
|
+
# # string<String>:: The message to be logged. Defaults to nil.
|
170
|
+
# #
|
171
|
+
# # ==== Returns
|
172
|
+
# # self:: The logger object for chaining.
|
173
|
+
# def #{name}(message = nil)
|
174
|
+
# self << message if #{number} >= level
|
175
|
+
# self
|
176
|
+
# end
|
177
|
+
#
|
178
|
+
# # Appends a message to the log if the log level is at least as high as
|
179
|
+
# # the log level of the logger. The bang! version of the method also auto
|
180
|
+
# # flushes the log buffer to disk.
|
181
|
+
# #
|
182
|
+
# # ==== Parameters
|
183
|
+
# # string<String>:: The message to be logged. Defaults to nil.
|
184
|
+
# #
|
185
|
+
# # ==== Returns
|
186
|
+
# # self:: The logger object for chaining.
|
187
|
+
# def #{name}!(message = nil)
|
188
|
+
# self << message if #{number} >= level
|
189
|
+
# flush if #{number} >= level
|
190
|
+
# self
|
191
|
+
# end
|
192
|
+
#
|
193
|
+
# # ==== Returns
|
194
|
+
# # Boolean:: True if this level will be logged by this logger.
|
195
|
+
# def #{name}?
|
196
|
+
# #{number} >= level
|
197
|
+
# end
|
198
|
+
# LEVELMETHODS
|
199
|
+
# end
|
200
|
+
#
|
201
|
+
# end
|
202
|
+
#
|
203
|
+
# end
|
@@ -6,13 +6,27 @@ module Merb
|
|
6
6
|
begin
|
7
7
|
controller = ::Merb::Dispatcher.handle(Merb::Request.new(env))
|
8
8
|
rescue Object => e
|
9
|
-
return [500, {
|
9
|
+
return [500, {Merb::Const::CONTENT_TYPE => "text/html"}, e.message + "<br/>" + e.backtrace.join("<br/>")]
|
10
10
|
end
|
11
11
|
Merb.logger.info "\n\n"
|
12
12
|
Merb.logger.flush
|
13
|
+
|
14
|
+
unless controller.headers[Merb::Const::DATE]
|
15
|
+
require "time"
|
16
|
+
controller.headers[Merb::Const::DATE] = Time.now.rfc2822.to_s
|
17
|
+
end
|
13
18
|
controller.rack_response
|
14
19
|
end
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
20
|
+
|
21
|
+
def deferred?(env)
|
22
|
+
path = env[Merb::Const::PATH_INFO] ? env[Merb::Const::PATH_INFO].chomp('/') : ""
|
23
|
+
if path =~ Merb.deferred_actions
|
24
|
+
Merb.logger.info! "Deferring Request: #{path}"
|
25
|
+
true
|
26
|
+
else
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end # deferred?(env)
|
30
|
+
end # Application
|
31
|
+
end # Rack
|
32
|
+
end # Merb
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Merb
|
2
|
+
module Rack
|
3
|
+
|
4
|
+
class ConditionalGet < Merb::Rack::Middleware
|
5
|
+
def call(env)
|
6
|
+
status, headers, body = @app.call(env)
|
7
|
+
|
8
|
+
# set Date header using RFC1123 date format as specified by HTTP
|
9
|
+
# RFC2616 section 3.3.1.
|
10
|
+
if etag = headers['ETag']
|
11
|
+
status = 304 if etag == env[Merb::Const::HTTP_IF_NONE_MATCH]
|
12
|
+
end
|
13
|
+
|
14
|
+
if last_modified = headers[Merb::Const::LAST_MODIFIED]
|
15
|
+
status = 304 if last_modified == env[Merb::Const::HTTP_IF_MODIFIED_SINCE]
|
16
|
+
end
|
17
|
+
|
18
|
+
[status, headers, body]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Merb
|
2
|
+
module Rack
|
3
|
+
|
4
|
+
class ContentLength < Merb::Rack::Middleware
|
5
|
+
def call(env)
|
6
|
+
status, headers, body = @app.call(env)
|
7
|
+
|
8
|
+
# to_s is because Rack spec expects header
|
9
|
+
# values to be iterable and yield strings
|
10
|
+
header = 'Content-Length'.freeze
|
11
|
+
headers[header] = body.size.to_s unless headers.has_key?(header)
|
12
|
+
|
13
|
+
[status, headers, body]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Merb
|
2
|
+
module Rack
|
3
|
+
class Tracer < Merb::Rack::Middleware
|
4
|
+
|
5
|
+
def call(env)
|
6
|
+
|
7
|
+
Merb.logger.debug!("Rack environment:\n" + env.inspect + "\n\n")
|
8
|
+
|
9
|
+
status, headers, body = @app.call(env)
|
10
|
+
|
11
|
+
Merb.logger.debug!("Status: #{status.inspect}")
|
12
|
+
Merb.logger.debug!("Headers: #{headers.inspect}")
|
13
|
+
Merb.logger.debug!("Body: #{body.inspect}")
|
14
|
+
|
15
|
+
[status, headers, body]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -7,13 +7,7 @@ module Merb
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def deferred?(env)
|
10
|
-
|
11
|
-
if path =~ Merb.deferred_actions
|
12
|
-
Merb.logger.info! "Deferring Request: #{path}"
|
13
|
-
true
|
14
|
-
else
|
15
|
-
false
|
16
|
-
end
|
10
|
+
@app.deferred?(env)
|
17
11
|
end
|
18
12
|
|
19
13
|
def call(env)
|
data/lib/merb-core/rack.rb
CHANGED
@@ -1,21 +1,24 @@
|
|
1
1
|
require 'rack'
|
2
2
|
module Merb
|
3
3
|
module Rack
|
4
|
-
autoload :Application, 'merb-core
|
5
|
-
autoload :Adapter, 'merb-core
|
6
|
-
autoload :Ebb, 'merb-core
|
7
|
-
autoload :EventedMongrel, 'merb-core
|
8
|
-
autoload :FastCGI, 'merb-core
|
9
|
-
autoload :Irb, 'merb-core
|
10
|
-
autoload :Middleware, 'merb-core
|
11
|
-
autoload :Mongrel, 'merb-core
|
12
|
-
autoload :Runner, 'merb-core
|
13
|
-
autoload :SwiftipliedMongrel, 'merb-core
|
14
|
-
autoload :Thin, 'merb-core
|
15
|
-
autoload :ThinTurbo, 'merb-core
|
16
|
-
autoload :WEBrick, 'merb-core
|
17
|
-
autoload :PathPrefix, 'merb-core
|
18
|
-
autoload :Static, 'merb-core
|
19
|
-
autoload :Profiler, 'merb-core
|
4
|
+
autoload :Application, 'merb-core/rack/application'
|
5
|
+
autoload :Adapter, 'merb-core/rack/adapter'
|
6
|
+
autoload :Ebb, 'merb-core/rack/adapter/ebb'
|
7
|
+
autoload :EventedMongrel, 'merb-core/rack/adapter/evented_mongrel'
|
8
|
+
autoload :FastCGI, 'merb-core/rack/adapter/fcgi'
|
9
|
+
autoload :Irb, 'merb-core/rack/adapter/irb'
|
10
|
+
autoload :Middleware, 'merb-core/rack/middleware'
|
11
|
+
autoload :Mongrel, 'merb-core/rack/adapter/mongrel'
|
12
|
+
autoload :Runner, 'merb-core/rack/adapter/runner'
|
13
|
+
autoload :SwiftipliedMongrel, 'merb-core/rack/adapter/swiftiplied_mongrel'
|
14
|
+
autoload :Thin, 'merb-core/rack/adapter/thin'
|
15
|
+
autoload :ThinTurbo, 'merb-core/rack/adapter/thin_turbo'
|
16
|
+
autoload :WEBrick, 'merb-core/rack/adapter/webrick'
|
17
|
+
autoload :PathPrefix, 'merb-core/rack/middleware/path_prefix'
|
18
|
+
autoload :Static, 'merb-core/rack/middleware/static'
|
19
|
+
autoload :Profiler, 'merb-core/rack/middleware/profiler'
|
20
|
+
autoload :Tracer, 'merb-core/rack/middleware/tracer'
|
21
|
+
autoload :ContentLength, 'merb-core/rack/middleware/content_length'
|
22
|
+
autoload :ConditionalGet, 'merb-core/rack/middleware/conditional_get'
|
20
23
|
end # Rack
|
21
24
|
end # Merb
|
@@ -99,6 +99,7 @@ module Merb::Test::Rspec::RouteMatchers
|
|
99
99
|
def matches?(parameter_hash)
|
100
100
|
@actual = parameter_hash.dup.except(:controller, :action)
|
101
101
|
|
102
|
+
return @actual.empty? if @expected.empty?
|
102
103
|
@expected.all? {|(k, v)| @actual.has_key?(k) && @actual[k] == v}
|
103
104
|
end
|
104
105
|
|
@@ -1,4 +1,34 @@
|
|
1
1
|
module Merb::Test::Rspec::ViewMatchers
|
2
|
+
class HaveXpath
|
3
|
+
def initialize(expected)
|
4
|
+
@expected = expected
|
5
|
+
end
|
6
|
+
|
7
|
+
def matches?(stringlike)
|
8
|
+
@document = case stringlike
|
9
|
+
when LibXML::XML::Document, LibXML::XML::Node
|
10
|
+
stringlike
|
11
|
+
when StringIO
|
12
|
+
LibXML::XML::HTMLParser.string(stringlike.string).parse
|
13
|
+
else
|
14
|
+
LibXML::XML::HTMLParser.string(stringlike).parse
|
15
|
+
end
|
16
|
+
!@document.find(@expected).empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
# ==== Returns
|
20
|
+
# String:: The failure message.
|
21
|
+
def failure_message
|
22
|
+
"expected following text to match xpath #{@expected}:\n#{@document}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# ==== Returns
|
26
|
+
# String:: The failure message to be displayed in negative matches.
|
27
|
+
def negative_failure_message
|
28
|
+
"expected following text to not match xpath #{@expected}:\n#{@document}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
2
32
|
class HaveSelector
|
3
33
|
|
4
34
|
# ==== Parameters
|
@@ -302,6 +332,12 @@ module Merb::Test::Rspec::ViewMatchers
|
|
302
332
|
end
|
303
333
|
alias_method :match_selector, :have_selector
|
304
334
|
|
335
|
+
def have_xpath(expected)
|
336
|
+
require "libxml"
|
337
|
+
HaveXpath.new(expected)
|
338
|
+
end
|
339
|
+
alias_method :match_xpath, :have_xpath
|
340
|
+
|
305
341
|
# RSpec matcher to test for the presence of tags.
|
306
342
|
#
|
307
343
|
# ==== Parameters
|
@@ -9,7 +9,7 @@ require 'benchmark'
|
|
9
9
|
# spec_cmd<~to_s>:: The spec command. Defaults to "spec".
|
10
10
|
# run_opts<String>:: Options to pass to spec commands, for instance,
|
11
11
|
# if you want to use profiling formatter.
|
12
|
-
def run_specs(globs, spec_cmd='spec', run_opts = "-c
|
12
|
+
def run_specs(globs, spec_cmd='spec', run_opts = "-c")
|
13
13
|
require "optparse"
|
14
14
|
require "spec"
|
15
15
|
globs = globs.is_a?(Array) ? globs : [globs]
|
@@ -18,6 +18,7 @@ def run_specs(globs, spec_cmd='spec', run_opts = "-c -f s")
|
|
18
18
|
time = Benchmark.measure do
|
19
19
|
globs.each do |glob|
|
20
20
|
Dir[glob].each do |spec|
|
21
|
+
STDOUT.puts "\n\nRunning #{spec}...\n"
|
21
22
|
response = Open3.popen3("#{spec_cmd} #{File.expand_path(spec)} #{run_opts}") do |i,o,e|
|
22
23
|
while out = o.gets
|
23
24
|
STDOUT.puts out
|
data/lib/merb-core/version.rb
CHANGED