merb-core 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
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