merb-core 0.9.8 → 0.9.9

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 (57) hide show
  1. data/CONTRIBUTORS +33 -0
  2. data/README +7 -3
  3. data/Rakefile +3 -3
  4. data/lib/merb-core.rb +165 -94
  5. data/lib/merb-core/bootloader.rb +469 -100
  6. data/lib/merb-core/config.rb +79 -3
  7. data/lib/merb-core/constants.rb +24 -2
  8. data/lib/merb-core/controller/abstract_controller.rb +172 -67
  9. data/lib/merb-core/controller/exceptions.rb +50 -6
  10. data/lib/merb-core/controller/merb_controller.rb +215 -108
  11. data/lib/merb-core/controller/mime.rb +36 -12
  12. data/lib/merb-core/controller/mixins/authentication.rb +52 -7
  13. data/lib/merb-core/controller/mixins/conditional_get.rb +14 -0
  14. data/lib/merb-core/controller/mixins/controller.rb +90 -58
  15. data/lib/merb-core/controller/mixins/render.rb +34 -10
  16. data/lib/merb-core/controller/mixins/responder.rb +40 -16
  17. data/lib/merb-core/controller/template.rb +37 -16
  18. data/lib/merb-core/core_ext/hash.rb +9 -0
  19. data/lib/merb-core/core_ext/kernel.rb +92 -41
  20. data/lib/merb-core/dispatch/dispatcher.rb +29 -45
  21. data/lib/merb-core/dispatch/request.rb +186 -82
  22. data/lib/merb-core/dispatch/router.rb +141 -53
  23. data/lib/merb-core/dispatch/router/behavior.rb +296 -139
  24. data/lib/merb-core/dispatch/router/resources.rb +51 -19
  25. data/lib/merb-core/dispatch/router/route.rb +76 -23
  26. data/lib/merb-core/dispatch/session.rb +80 -36
  27. data/lib/merb-core/dispatch/session/container.rb +31 -15
  28. data/lib/merb-core/dispatch/session/cookie.rb +51 -22
  29. data/lib/merb-core/dispatch/session/memcached.rb +10 -6
  30. data/lib/merb-core/dispatch/session/memory.rb +17 -5
  31. data/lib/merb-core/dispatch/session/store_container.rb +21 -9
  32. data/lib/merb-core/dispatch/worker.rb +16 -2
  33. data/lib/merb-core/gem_ext/erubis.rb +4 -0
  34. data/lib/merb-core/plugins.rb +13 -0
  35. data/lib/merb-core/rack.rb +1 -0
  36. data/lib/merb-core/rack/adapter.rb +1 -0
  37. data/lib/merb-core/rack/adapter/abstract.rb +95 -17
  38. data/lib/merb-core/rack/adapter/irb.rb +50 -5
  39. data/lib/merb-core/rack/application.rb +27 -5
  40. data/lib/merb-core/rack/handler/mongrel.rb +6 -6
  41. data/lib/merb-core/rack/helpers.rb +33 -0
  42. data/lib/merb-core/rack/middleware/conditional_get.rb +1 -1
  43. data/lib/merb-core/rack/middleware/path_prefix.rb +3 -3
  44. data/lib/merb-core/rack/middleware/static.rb +11 -7
  45. data/lib/merb-core/server.rb +134 -69
  46. data/lib/merb-core/tasks/gem_management.rb +153 -80
  47. data/lib/merb-core/tasks/merb_rake_helper.rb +12 -4
  48. data/lib/merb-core/tasks/stats.rake +1 -1
  49. data/lib/merb-core/test/helpers/mock_request_helper.rb +29 -22
  50. data/lib/merb-core/test/helpers/request_helper.rb +1 -1
  51. data/lib/merb-core/test/helpers/route_helper.rb +50 -4
  52. data/lib/merb-core/test/matchers/request_matchers.rb +2 -36
  53. data/lib/merb-core/test/matchers/view_matchers.rb +32 -22
  54. data/lib/merb-core/test/run_specs.rb +6 -5
  55. data/lib/merb-core/test/test_ext/rspec.rb +6 -19
  56. data/lib/merb-core/version.rb +1 -1
  57. metadata +5 -4
@@ -1,4 +1,13 @@
1
1
  class Hash
2
+ # Returns the value of self for each argument and deletes those entries.
3
+ #
4
+ # ==== Parameters
5
+ # *args:: the keys whose values should be extracted and deleted.
6
+ #
7
+ # ==== Returns
8
+ # Array[Object]:: The values of the provided arguments in corresponding order.
9
+ #
10
+ # @api public
2
11
  def extract!(*args)
3
12
  args.map do |arg|
4
13
  self.delete(arg)
@@ -1,5 +1,11 @@
1
1
  require 'rubygems/dependency'
2
2
 
3
+ module Gem
4
+ class Dependency
5
+ attr_accessor :require_block
6
+ end
7
+ end
8
+
3
9
  module Kernel
4
10
 
5
11
  # Keep track of all required dependencies.
@@ -11,8 +17,10 @@ module Kernel
11
17
  # @return <Gem::Dependency> Dependency information
12
18
  #
13
19
  # @api private
14
- def track_dependency(name, *ver)
15
- dep = Gem::Dependency.new(name, ver)
20
+ def track_dependency(name, *ver, &blk)
21
+ dep = Gem::Dependency.new(name, ver.empty? ? nil : ver)
22
+ dep.require_block = blk
23
+
16
24
  existing = Merb::BootLoader::Dependencies.dependencies.find { |d| d.name == dep.name }
17
25
  if existing
18
26
  index = Merb::BootLoader::Dependencies.dependencies.index(existing)
@@ -31,19 +39,23 @@ module Kernel
31
39
  # If that has already happened, the gem will be activated
32
40
  # immediately, but it will still be registered.
33
41
  #
34
- # @param name<String> The name of the gem to load.
35
- # @param *ver<Gem::Requirement, Gem::Version, Array, #to_str>
42
+ # ==== Parameters
43
+ # name<String> The name of the gem to load.
44
+ # *ver<Gem::Requirement, Gem::Version, Array, #to_str>
36
45
  # Version requirements to be passed to Gem::Dependency.new.
37
46
  # If the last argument is a Hash, extract the :immediate option,
38
47
  # forcing a dependency to load immediately.
39
48
  #
40
- # @return <Gem::Dependency> The dependency information.
41
- def dependency(name, *ver)
49
+ # ==== Returns
50
+ # Gem::Dependency:: The dependency information.
51
+ #
52
+ # @api public
53
+ def dependency(name, *ver, &blk)
42
54
  immediate = ver.last.is_a?(Hash) && ver.pop[:immediate]
43
55
  if immediate || Merb::BootLoader.finished?(Merb::BootLoader::Dependencies)
44
- load_dependency(name, *ver)
56
+ load_dependency(name, *ver, &blk)
45
57
  else
46
- track_dependency(name, *ver)
58
+ track_dependency(name, *ver, &blk)
47
59
  end
48
60
  end
49
61
 
@@ -63,20 +75,38 @@ module Kernel
63
75
  # as a library.
64
76
  #
65
77
  # @return <Gem::Dependency> The dependency information.
66
- def load_dependency(name, *ver)
78
+ #
79
+ # @api private
80
+ def load_dependency(name, *ver, &blk)
67
81
  dep = name.is_a?(Gem::Dependency) ? name : track_dependency(name, *ver)
68
82
  gem(dep)
69
- rescue Gem::LoadError
83
+ rescue Gem::LoadError => e
84
+ Merb.fatal! "The gem #{name}, #{ver.inspect} was not found", e
70
85
  ensure
71
- require dep.name
72
- Merb.logger.info!("loading gem '#{dep.name}' ...")
86
+ if block = blk || dep.require_block
87
+ block.call
88
+ else
89
+ begin
90
+ require dep.name
91
+ rescue LoadError => e
92
+ Merb.fatal! "The file #{dep.name} was not found", e
93
+ end
94
+ end
95
+ Merb.logger.verbose!("loading gem '#{dep.name}' ...")
73
96
  return dep # ensure needs explicit return
74
97
  end
75
98
 
76
99
  # Loads both gem and library dependencies that are passed in as arguments.
77
100
  # Execution is deferred to the Merb::BootLoader::Dependencies.run during bootup.
78
101
  #
79
- # @param *args<String, Hash, Array> The dependencies to load.
102
+ # ==== Parameters
103
+ # *args<String, Hash, Array> The dependencies to load.
104
+ #
105
+ # ==== Returns
106
+ # Array[(Gem::Dependency, Array[Gem::Dependency])]:: Gem::Dependencies for the
107
+ # dependencies specified in args.
108
+ #
109
+ # @api public
80
110
  def dependencies(*args)
81
111
  args.map do |arg|
82
112
  case arg
@@ -101,6 +131,8 @@ module Kernel
101
131
  # @example dependencies "RedCloth" # Loads the the RedCloth gem
102
132
  # @example dependencies "RedCloth", "merb_helpers" # Loads RedCloth and merb_helpers
103
133
  # @example dependencies "RedCloth" => "3.0" # Loads RedCloth 3.0
134
+ #
135
+ # @api private
104
136
  def load_dependencies(*args)
105
137
  args.map do |arg|
106
138
  case arg
@@ -115,7 +147,12 @@ module Kernel
115
147
  #
116
148
  # @param library<to_s> The library to attempt to include.
117
149
  # @param message<String> The error to add to the log upon failure. Defaults to nil.
150
+ #
151
+ # @api private
152
+ # @deprecated
118
153
  def rescue_require(library, message = nil)
154
+ Merb.logger.warn("Deprecation warning: rescue_require is deprecated")
155
+ sleep 2.0
119
156
  require library
120
157
  rescue LoadError, RuntimeError
121
158
  Merb.logger.error!(message) if message
@@ -125,17 +162,22 @@ module Kernel
125
162
  # Mapper) you wish to use. Currently Merb has plugins to support
126
163
  # ActiveRecord, DataMapper, and Sequel.
127
164
  #
128
- # @param orm<Symbol> The ORM to use.
165
+ # ==== Parameters
166
+ # orm<Symbol>:: The ORM to use.
129
167
  #
130
- # @example
168
+ # ==== Returns
169
+ # nil
170
+ #
171
+ # ==== Example
131
172
  # use_orm :datamapper
132
173
  #
133
174
  # # This will use the DataMapper generator for your ORM
134
175
  # $ merb-gen model ActivityEvent
135
176
  #
136
- # @note
177
+ # ==== Notes
137
178
  # If for some reason this is called more than once, latter
138
179
  # call takes over other.
180
+ #
139
181
  # @api public
140
182
  def use_orm(orm)
141
183
  begin
@@ -146,37 +188,53 @@ module Kernel
146
188
  Merb.logger.warn!("The #{orm_plugin} gem was not found. You may need to install it.")
147
189
  raise e
148
190
  end
191
+ nil
149
192
  end
150
193
 
151
194
  # Used in Merb.root/config/init.rb to tell Merb which testing framework to
152
195
  # use. Currently Merb has plugins to support RSpec and Test::Unit.
153
196
  #
154
- # @param test_framework<Symbol>
197
+ # ==== Parameters
198
+ # test_framework<Symbol>::
155
199
  # The test framework to use. Currently only supports :rspec and :test_unit.
156
200
  #
157
- # @example
201
+ # ==== Returns
202
+ # nil
203
+ #
204
+ # ==== Example
158
205
  # use_test :rspec
159
206
  #
160
207
  # # This will now use the RSpec generator for tests
161
208
  # $ merb-gen model ActivityEvent
209
+ #
162
210
  # @api public
163
- def use_test(test_framework, *test_dependencies)
211
+ def use_testing_framework(test_framework, *test_dependencies)
164
212
  Merb.test_framework = test_framework
165
213
 
166
214
  Kernel.dependencies test_dependencies if Merb.env == "test" || Merb.env.nil?
215
+ nil
216
+ end
217
+
218
+ def use_test(*args)
219
+ use_testing_framework(*args)
167
220
  end
168
221
 
169
222
  # Used in Merb.root/config/init.rb to tell Merb which template engine to
170
223
  # prefer.
171
224
  #
172
- # @param template_engine<Symbol>
225
+ # ==== Parameters
226
+ # template_engine<Symbol>
173
227
  # The template engine to use.
174
228
  #
175
- # @example
229
+ # ==== Returns
230
+ # nil
231
+ #
232
+ # ==== Example
176
233
  # use_template_engine :haml
177
234
  #
178
235
  # # This will now use haml templates in generators where available.
179
236
  # $ merb-gen resource_controller Project
237
+ #
180
238
  # @api public
181
239
  def use_template_engine(template_engine)
182
240
  Merb.template_engine = template_engine
@@ -189,11 +247,14 @@ module Kernel
189
247
  end
190
248
  Kernel.dependency(template_engine_plugin)
191
249
  end
250
+
251
+ nil
192
252
  rescue LoadError => e
193
253
  Merb.logger.warn!("The #{template_engine_plugin} gem was not found. You may need to install it.")
194
254
  raise e
195
255
  end
196
256
 
257
+
197
258
  # @param i<Fixnum> The caller number. Defaults to 1.
198
259
  #
199
260
  # @return <Array[Array]> The file, line and method of the caller.
@@ -201,6 +262,8 @@ module Kernel
201
262
  # @example
202
263
  # __caller_info__(1)
203
264
  # # => ['/usr/lib/ruby/1.8/irb/workspace.rb', '52', 'irb_binding']
265
+ #
266
+ # @api private
204
267
  def __caller_info__(i = 1)
205
268
  file, line, meth = caller[i].scan(/(.*?):(\d+):in `(.*?)'/).first
206
269
  end
@@ -224,6 +287,8 @@ module Kernel
224
287
  # [ 123, " DEBUGGER__.waiting.push Thread.current", false ],
225
288
  # [ 124, " @suspend_next = false", false ]
226
289
  # ]
290
+ #
291
+ # @api private
227
292
  def __caller_lines__(file, line, size = 4)
228
293
  line = line.to_i
229
294
  if file =~ /\(erubis\)/
@@ -241,26 +306,6 @@ module Kernel
241
306
  yield index + line - size, str.chomp
242
307
  end
243
308
  end
244
- #
245
- # lines = File.readlines(file)
246
- # current = line.to_i - 1
247
- #
248
- # first = current - size
249
- # first = first < 0 ? 0 : first
250
- #
251
- # last = current + size
252
- # last = last > lines.size ? lines.size : last
253
- #
254
- # log = lines[first..last]
255
- #
256
- # area = []
257
- #
258
- # log.each_with_index do |line, index|
259
- # index = index + first + 1
260
- # area << [index, line.chomp, index == current + 1]
261
- # end
262
- #
263
- # area
264
309
  end
265
310
 
266
311
  # Takes a block, profiles the results of running the block
@@ -288,6 +333,8 @@ module Kernel
288
333
  # Assuming that the total time taken for #puts calls was less than 5% of the
289
334
  # total time to run, #puts won't appear in the profile report.
290
335
  # The code block will be run 30 times in the example above.
336
+ #
337
+ # @api private
291
338
  def __profile__(name, min=1, iter=100)
292
339
  require 'ruby-prof' unless defined?(RubyProf)
293
340
  return_result = ''
@@ -313,6 +360,8 @@ module Kernel
313
360
  # opts = extract_options_from_args!(args) || {}
314
361
  # # [...]
315
362
  # end
363
+ #
364
+ # @api public
316
365
  def extract_options_from_args!(args)
317
366
  args.pop if Hash === args.last
318
367
  end
@@ -325,6 +374,8 @@ module Kernel
325
374
  #
326
375
  # @raise <ArgumentError>
327
376
  # An object failed to quack like a condition.
377
+ #
378
+ # @api public
328
379
  def enforce!(opts = {})
329
380
  opts.each do |k,v|
330
381
  raise ArgumentError, "#{k.inspect} doesn't quack like #{v.inspect}" unless k.quacks_like?(v)
@@ -4,15 +4,19 @@ module Merb
4
4
  class Dispatcher
5
5
  class << self
6
6
  include Merb::ControllerExceptions
7
-
7
+
8
8
  attr_accessor :use_mutex
9
9
 
10
10
  @@work_queue = Queue.new
11
-
11
+
12
+ # ==== Returns
13
+ # Queue:: the current queue of dispatch jobs.
14
+ #
15
+ # @api private
12
16
  def work_queue
13
17
  @@work_queue
14
18
  end
15
-
19
+
16
20
  Merb::Dispatcher.use_mutex = ::Merb::Config[:use_mutex]
17
21
 
18
22
  # Dispatch the rack environment. ControllerExceptions are rescued here
@@ -25,9 +29,11 @@ module Merb
25
29
  # ==== Returns
26
30
  # Merb::Controller::
27
31
  # The Merb::Controller that was dispatched to
32
+ #
33
+ # @api private
28
34
  def handle(request)
29
35
  request.handle
30
- end
36
+ end
31
37
  end
32
38
  end
33
39
 
@@ -36,12 +42,18 @@ module Merb
36
42
 
37
43
  @@mutex = Mutex.new
38
44
 
45
+ # Handles request routing and action dispatch.
46
+ #
47
+ # ==== Returns
48
+ # Merb::Controller:: the controller that handled the action dispatch.
49
+ #
50
+ # @api private
39
51
  def handle
40
52
  start = Time.now
41
53
  Merb.logger.info "Started request handling: #{start.to_s}"
42
-
54
+
43
55
  find_route!
44
- return redirect if redirects?
56
+ return rack_response if handled?
45
57
 
46
58
  klass = controller
47
59
  Merb.logger.debug("Routed to: #{params.inspect}")
@@ -57,42 +69,14 @@ module Merb
57
69
  if klass.abstract?
58
70
  raise NotFound, "The '#{klass}' controller has no public actions"
59
71
  end
60
-
72
+
61
73
  controller = dispatch_action(klass, params[:action])
62
74
  controller._benchmarks[:dispatch_time] = Time.now - start
63
75
  Merb.logger.info controller._benchmarks.inspect
64
76
  Merb.logger.flush
65
- controller
77
+ controller.rack_response
66
78
  rescue Object => exception
67
- dispatch_exception(exception)
68
- end
69
-
70
- # Set up a faux controller to do redirection from the router
71
- #
72
- # ==== Parameters
73
- # request<Merb::Request>::
74
- # The Merb::Request object that was created in #handle
75
- # status<Integer>::
76
- # The status code to return with the controller
77
- # url<String>::
78
- # The URL to return
79
- #
80
- # ==== Example
81
- # r.match("/my/old/crusty/url").redirect("http://example.com/index.html")
82
- #
83
- # ==== Returns
84
- # Merb::Controller::
85
- # Merb::Controller set with redirect headers and a 301/302 status
86
- def redirect
87
- status, url = redirect_status, redirect_url
88
- controller = Merb::Controller.new(self, status)
89
-
90
- Merb.logger.info("Dispatcher redirecting to: #{url} (#{status})")
91
- Merb.logger.flush
92
-
93
- controller.headers['Location'] = url
94
- controller.body = "<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>"
95
- controller
79
+ dispatch_exception(exception).rack_response
96
80
  end
97
81
 
98
82
  private
@@ -101,13 +85,13 @@ module Merb
101
85
  # ==== Parameters
102
86
  # klass<Merb::Controller>:: The controller class to dispatch to.
103
87
  # action<Symbol>:: The action to dispatch.
104
- # request<Merb::Request>::
105
- # The Merb::Request object that was created in #handle
106
88
  # status<Integer>:: The status code to respond with.
107
89
  #
108
90
  # ==== Returns
109
91
  # Merb::Controller::
110
92
  # The Merb::Controller that was dispatched to.
93
+ #
94
+ # @api private
111
95
  def dispatch_action(klass, action, status=200)
112
96
  # build controller
113
97
  controller = klass.new(self, status)
@@ -129,8 +113,6 @@ module Merb
129
113
  # StandardError is caught in standard_error).
130
114
  #
131
115
  # ==== Parameters
132
- # request<Merb::Request>::
133
- # The request object associated with the failed request.
134
116
  # exception<Object>::
135
117
  # The exception object that was created when trying to dispatch the
136
118
  # original controller.
@@ -138,6 +120,8 @@ module Merb
138
120
  # ==== Returns
139
121
  # Exceptions::
140
122
  # The Merb::Controller that was dispatched to.
123
+ #
124
+ # @api private
141
125
  def dispatch_exception(exception)
142
126
  if(exception.is_a?(Merb::ControllerExceptions::Base) &&
143
127
  !exception.is_a?(Merb::ControllerExceptions::ServerError))
@@ -145,12 +129,12 @@ module Merb
145
129
  else
146
130
  Merb.logger.error(Merb.exception(exception))
147
131
  end
148
-
132
+
149
133
  self.exceptions = [exception]
150
-
134
+
151
135
  begin
152
136
  e = exceptions.first
153
-
137
+
154
138
  if action_name = e.action_name
155
139
  dispatch_action(Exceptions, action_name, e.class.status)
156
140
  else
@@ -162,7 +146,7 @@ module Merb
162
146
  else
163
147
  Merb.logger.error("Dispatching #{e.class} raised another error.")
164
148
  Merb.logger.error(Merb.exception(dispatch_issue))
165
-
149
+
166
150
  exceptions.unshift dispatch_issue
167
151
  retry
168
152
  end