merb-core 0.9.4 → 0.9.5

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