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.
Files changed (52) hide show
  1. data/Rakefile +22 -13
  2. data/lib/merb-core/bootloader.rb +3 -2
  3. data/lib/merb-core/config.rb +1 -1
  4. data/lib/merb-core/constants.rb +3 -1
  5. data/lib/merb-core/controller/abstract_controller.rb +25 -39
  6. data/lib/merb-core/controller/exceptions.rb +14 -16
  7. data/lib/merb-core/controller/merb_controller.rb +2 -2
  8. data/lib/merb-core/controller/mime.rb +2 -1
  9. data/lib/merb-core/controller/mixins/render.rb +12 -0
  10. data/lib/merb-core/controller/mixins/responder.rb +31 -7
  11. data/lib/merb-core/core_ext/hash.rb +7 -0
  12. data/lib/merb-core/core_ext/kernel.rb +31 -34
  13. data/lib/merb-core/core_ext.rb +1 -0
  14. data/lib/merb-core/dispatch/default_exception/default_exception.rb +3 -3
  15. data/lib/merb-core/dispatch/dispatcher.rb +128 -135
  16. data/lib/merb-core/dispatch/request.rb +11 -11
  17. data/lib/merb-core/dispatch/router/behavior.rb +6 -6
  18. data/lib/merb-core/dispatch/router.rb +5 -5
  19. data/lib/merb-core/logger.rb +203 -202
  20. data/lib/merb-core/rack/application.rb +19 -5
  21. data/lib/merb-core/rack/middleware/conditional_get.rb +23 -0
  22. data/lib/merb-core/rack/middleware/content_length.rb +18 -0
  23. data/lib/merb-core/rack/middleware/tracer.rb +20 -0
  24. data/lib/merb-core/rack/middleware.rb +1 -7
  25. data/lib/merb-core/rack.rb +19 -16
  26. data/lib/merb-core/test/matchers/route_matchers.rb +1 -0
  27. data/lib/merb-core/test/matchers/view_matchers.rb +36 -0
  28. data/lib/merb-core/test/run_specs.rb +2 -1
  29. data/lib/merb-core/version.rb +1 -1
  30. data/lib/merb-core.rb +39 -33
  31. data/spec/private/config/merb_spec.rb +34 -0
  32. data/spec/private/dispatch/fixture/log/merb_test.log +372 -0
  33. data/spec/private/router/fixture/log/merb_test.log +42 -0
  34. data/spec/public/abstract_controller/controllers/filters.rb +50 -1
  35. data/spec/public/abstract_controller/filter_spec.rb +25 -0
  36. data/spec/public/controller/base_spec.rb +41 -1
  37. data/spec/public/controller/controllers/base.rb +16 -0
  38. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.html.erb +1 -1
  39. data/spec/public/controller/dispatcher_spec.rb +24 -25
  40. data/spec/public/controller/responder_spec.rb +6 -0
  41. data/spec/public/core_ext/kernel_spec.rb +79 -0
  42. data/spec/public/directory_structure/directory/log/merb_test.log +245 -0
  43. data/spec/public/rack/conditinal_get_middleware_spec.rb +139 -0
  44. data/spec/public/rack/rack_middleware_spec.rb +99 -0
  45. data/spec/public/rack/shared_example_groups.rb +35 -0
  46. data/spec/public/reloading/directory/log/merb_test.log +40 -0
  47. data/spec/public/request/request_spec.rb +0 -5
  48. data/spec/public/router/fixture/log/merb_test.log +348 -0
  49. data/spec/public/test/route_matchers_spec.rb +4 -0
  50. data/spec/spec_helper.rb +7 -1
  51. metadata +42 -5
  52. data/spec/private/plugins/plugin_spec.rb +0 -166
@@ -1,202 +1,203 @@
1
- require "time" # httpdate
2
- # ==== Public Merb Logger API
3
- #
4
- # To replace an existing logger with a new one:
5
- # Merb::Logger.set_log(log{String, IO},level{Symbol, String})
6
- #
7
- # Available logging levels are
8
- # Merb::Logger::{ Fatal, Error, Warn, Info, Debug }
9
- #
10
- # Logging via:
11
- # Merb.logger.fatal(message<String>,&block)
12
- # Merb.logger.error(message<String>,&block)
13
- # Merb.logger.warn(message<String>,&block)
14
- # Merb.logger.info(message<String>,&block)
15
- # Merb.logger.debug(message<String>,&block)
16
- #
17
- # Logging with autoflush:
18
- # Merb.logger.fatal!(message<String>,&block)
19
- # Merb.logger.error!(message<String>,&block)
20
- # Merb.logger.warn!(message<String>,&block)
21
- # Merb.logger.info!(message<String>,&block)
22
- # Merb.logger.debug!(message<String>,&block)
23
- #
24
- # Flush the buffer to
25
- # Merb.logger.flush
26
- #
27
- # Remove the current log object
28
- # Merb.logger.close
29
- #
30
- # ==== Private Merb Logger API
31
- #
32
- # To initialize the logger you create a new object, proxies to set_log.
33
- # Merb::Logger.new(log{String, IO},level{Symbol, String})
34
- module Merb
35
-
36
- class << self
37
- attr_accessor :logger
38
- end
39
-
40
- class Logger
41
-
42
- attr_accessor :level
43
- attr_accessor :delimiter
44
- attr_accessor :auto_flush
45
- attr_reader :buffer
46
- attr_reader :log
47
- attr_reader :init_args
48
-
49
- # ==== Notes
50
- # Ruby (standard) logger levels:
51
- # :fatal:: An unhandleable error that results in a program crash
52
- # :error:: A handleable error condition
53
- # :warn:: A warning
54
- # :info:: generic (useful) information about system operation
55
- # :debug:: low-level information for developers
56
- Levels =
57
- {
58
- :fatal => 7,
59
- :error => 6,
60
- :warn => 4,
61
- :info => 3,
62
- :debug => 0
63
- } unless const_defined?(:Levels)
64
-
65
- private
66
-
67
- # Readies a log for writing.
68
- #
69
- # ==== Parameters
70
- # log<IO, String>:: Either an IO object or a name of a logfile.
71
- def initialize_log(log)
72
- close if @log # be sure that we don't leave open files laying around.
73
-
74
- if log.respond_to?(:write)
75
- @log = log
76
- elsif File.exist?(log)
77
- @log = open(log, (File::WRONLY | File::APPEND))
78
- @log.sync = true
79
- else
80
- FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
81
- @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
82
- @log.sync = true
83
- @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
84
- end
85
- end
86
-
87
- public
88
-
89
- # To initialize the logger you create a new object, proxies to set_log.
90
- #
91
- # ==== Parameters
92
- # *args:: Arguments to create the log from. See set_logs for specifics.
93
- def initialize(*args)
94
- @init_args = args
95
- set_log(*args)
96
- end
97
-
98
- # Replaces an existing logger with a new one.
99
- #
100
- # ==== Parameters
101
- # log<IO, String>:: Either an IO object or a name of a logfile.
102
- # log_level<~to_sym>::
103
- # The log level from, e.g. :fatal or :info. Defaults to :error in the
104
- # production environment and :debug otherwise.
105
- # delimiter<String>::
106
- # Delimiter to use between message sections. Defaults to " ~ ".
107
- # auto_flush<Boolean>::
108
- # Whether the log should automatically flush after new messages are
109
- # added. Defaults to false.
110
- def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false)
111
- if log_level && Levels[log_level.to_sym]
112
- @level = Levels[log_level.to_sym]
113
- elsif Merb.environment == "production"
114
- @level = Levels[:warn]
115
- else
116
- @level = Levels[:debug]
117
- end
118
- @buffer = []
119
- @delimiter = delimiter
120
- @auto_flush = auto_flush
121
-
122
- initialize_log(log)
123
-
124
- Merb.logger = self
125
- end
126
-
127
- # Flush the entire buffer to the log object.
128
- def flush
129
- return unless @buffer.size > 0
130
- @log.write(@buffer.slice!(0..-1).to_s)
131
- end
132
-
133
- # Close and remove the current log object.
134
- def close
135
- flush
136
- @log.close if @log.respond_to?(:close) && !@log.tty?
137
- @log = nil
138
- end
139
-
140
- # Appends a message to the log. The methods yield to an optional block and
141
- # the output of this block will be appended to the message.
142
- #
143
- # ==== Parameters
144
- # string<String>:: The message to be logged. Defaults to nil.
145
- #
146
- # ==== Returns
147
- # String:: The resulting message added to the log file.
148
- def <<(string = nil)
149
- message = ""
150
- message << delimiter
151
- message << string if string
152
- message << "\n" unless message[-1] == ?\n
153
- @buffer << message
154
- flush if @auto_flush
155
-
156
- message
157
- end
158
- alias :push :<<
159
-
160
- # Generate the logging methods for Merb.logger for each log level.
161
- Levels.each_pair do |name, number|
162
- class_eval <<-LEVELMETHODS, __FILE__, __LINE__
163
-
164
- # Appends a message to the log if the log level is at least as high as
165
- # the log level of the logger.
166
- #
167
- # ==== Parameters
168
- # string<String>:: The message to be logged. Defaults to nil.
169
- #
170
- # ==== Returns
171
- # self:: The logger object for chaining.
172
- def #{name}(message = nil)
173
- self << message if #{number} >= level
174
- self
175
- end
176
-
177
- # Appends a message to the log if the log level is at least as high as
178
- # the log level of the logger. The bang! version of the method also auto
179
- # flushes the log buffer to disk.
180
- #
181
- # ==== Parameters
182
- # string<String>:: The message to be logged. Defaults to nil.
183
- #
184
- # ==== Returns
185
- # self:: The logger object for chaining.
186
- def #{name}!(message = nil)
187
- self << message if #{number} >= level
188
- flush if #{number} >= level
189
- self
190
- end
191
-
192
- # ==== Returns
193
- # Boolean:: True if this level will be logged by this logger.
194
- def #{name}?
195
- #{number} >= level
196
- end
197
- LEVELMETHODS
198
- end
199
-
200
- end
201
-
202
- end
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, {"Content-Type"=>"text/html"}, e.message + "<br/>" + e.backtrace.join("<br/>")]
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
- end
17
- end
18
- end
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
- path = env['PATH_INFO'] ? env['PATH_INFO'].chomp('/') : ""
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)
@@ -1,21 +1,24 @@
1
1
  require 'rack'
2
2
  module Merb
3
3
  module Rack
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'
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 -f s")
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
@@ -1,5 +1,5 @@
1
1
  module Merb
2
- VERSION = '0.9.4' unless defined?(Merb::VERSION)
2
+ VERSION = '0.9.5' unless defined?(Merb::VERSION)
3
3
 
4
4
  # Merb::RELEASE meanings:
5
5
  # 'dev' : unreleased