wycats-merb-core 0.9.8

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 (98) hide show
  1. data/CHANGELOG +992 -0
  2. data/CONTRIBUTORS +94 -0
  3. data/LICENSE +20 -0
  4. data/PUBLIC_CHANGELOG +142 -0
  5. data/README +21 -0
  6. data/Rakefile +458 -0
  7. data/TODO +0 -0
  8. data/bin/merb +11 -0
  9. data/bin/merb-specs +5 -0
  10. data/lib/merb-core.rb +598 -0
  11. data/lib/merb-core/autoload.rb +31 -0
  12. data/lib/merb-core/bootloader.rb +717 -0
  13. data/lib/merb-core/config.rb +305 -0
  14. data/lib/merb-core/constants.rb +45 -0
  15. data/lib/merb-core/controller/abstract_controller.rb +568 -0
  16. data/lib/merb-core/controller/exceptions.rb +315 -0
  17. data/lib/merb-core/controller/merb_controller.rb +256 -0
  18. data/lib/merb-core/controller/mime.rb +107 -0
  19. data/lib/merb-core/controller/mixins/authentication.rb +123 -0
  20. data/lib/merb-core/controller/mixins/conditional_get.rb +83 -0
  21. data/lib/merb-core/controller/mixins/controller.rb +319 -0
  22. data/lib/merb-core/controller/mixins/render.rb +513 -0
  23. data/lib/merb-core/controller/mixins/responder.rb +469 -0
  24. data/lib/merb-core/controller/template.rb +254 -0
  25. data/lib/merb-core/core_ext.rb +9 -0
  26. data/lib/merb-core/core_ext/hash.rb +7 -0
  27. data/lib/merb-core/core_ext/kernel.rb +340 -0
  28. data/lib/merb-core/dispatch/cookies.rb +130 -0
  29. data/lib/merb-core/dispatch/default_exception/default_exception.rb +93 -0
  30. data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +198 -0
  31. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +73 -0
  32. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +94 -0
  33. data/lib/merb-core/dispatch/dispatcher.rb +176 -0
  34. data/lib/merb-core/dispatch/request.rb +729 -0
  35. data/lib/merb-core/dispatch/router.rb +151 -0
  36. data/lib/merb-core/dispatch/router/behavior.rb +566 -0
  37. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  38. data/lib/merb-core/dispatch/router/resources.rb +191 -0
  39. data/lib/merb-core/dispatch/router/route.rb +511 -0
  40. data/lib/merb-core/dispatch/session.rb +222 -0
  41. data/lib/merb-core/dispatch/session/container.rb +74 -0
  42. data/lib/merb-core/dispatch/session/cookie.rb +173 -0
  43. data/lib/merb-core/dispatch/session/memcached.rb +68 -0
  44. data/lib/merb-core/dispatch/session/memory.rb +99 -0
  45. data/lib/merb-core/dispatch/session/store_container.rb +150 -0
  46. data/lib/merb-core/dispatch/worker.rb +28 -0
  47. data/lib/merb-core/gem_ext/erubis.rb +77 -0
  48. data/lib/merb-core/logger.rb +203 -0
  49. data/lib/merb-core/plugins.rb +67 -0
  50. data/lib/merb-core/rack.rb +25 -0
  51. data/lib/merb-core/rack/adapter.rb +44 -0
  52. data/lib/merb-core/rack/adapter/ebb.rb +25 -0
  53. data/lib/merb-core/rack/adapter/evented_mongrel.rb +26 -0
  54. data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
  55. data/lib/merb-core/rack/adapter/irb.rb +118 -0
  56. data/lib/merb-core/rack/adapter/mongrel.rb +26 -0
  57. data/lib/merb-core/rack/adapter/runner.rb +28 -0
  58. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +26 -0
  59. data/lib/merb-core/rack/adapter/thin.rb +39 -0
  60. data/lib/merb-core/rack/adapter/thin_turbo.rb +24 -0
  61. data/lib/merb-core/rack/adapter/webrick.rb +36 -0
  62. data/lib/merb-core/rack/application.rb +32 -0
  63. data/lib/merb-core/rack/handler/mongrel.rb +97 -0
  64. data/lib/merb-core/rack/middleware.rb +20 -0
  65. data/lib/merb-core/rack/middleware/conditional_get.rb +29 -0
  66. data/lib/merb-core/rack/middleware/content_length.rb +18 -0
  67. data/lib/merb-core/rack/middleware/csrf.rb +73 -0
  68. data/lib/merb-core/rack/middleware/path_prefix.rb +31 -0
  69. data/lib/merb-core/rack/middleware/profiler.rb +19 -0
  70. data/lib/merb-core/rack/middleware/static.rb +45 -0
  71. data/lib/merb-core/rack/middleware/tracer.rb +20 -0
  72. data/lib/merb-core/server.rb +284 -0
  73. data/lib/merb-core/tasks/audit.rake +68 -0
  74. data/lib/merb-core/tasks/gem_management.rb +229 -0
  75. data/lib/merb-core/tasks/merb.rb +1 -0
  76. data/lib/merb-core/tasks/merb_rake_helper.rb +80 -0
  77. data/lib/merb-core/tasks/stats.rake +71 -0
  78. data/lib/merb-core/test.rb +11 -0
  79. data/lib/merb-core/test/helpers.rb +9 -0
  80. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  81. data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
  82. data/lib/merb-core/test/helpers/request_helper.rb +393 -0
  83. data/lib/merb-core/test/helpers/route_helper.rb +39 -0
  84. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  85. data/lib/merb-core/test/matchers.rb +9 -0
  86. data/lib/merb-core/test/matchers/controller_matchers.rb +351 -0
  87. data/lib/merb-core/test/matchers/route_matchers.rb +137 -0
  88. data/lib/merb-core/test/matchers/view_matchers.rb +375 -0
  89. data/lib/merb-core/test/run_specs.rb +49 -0
  90. data/lib/merb-core/test/tasks/spectasks.rb +68 -0
  91. data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
  92. data/lib/merb-core/test/test_ext/object.rb +14 -0
  93. data/lib/merb-core/test/test_ext/string.rb +14 -0
  94. data/lib/merb-core/vendor/facets.rb +2 -0
  95. data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
  96. data/lib/merb-core/vendor/facets/inflect.rb +342 -0
  97. data/lib/merb-core/version.rb +3 -0
  98. metadata +253 -0
@@ -0,0 +1,31 @@
1
+ module Merb
2
+ autoload :AbstractController, "merb-core/controller/abstract_controller"
3
+ autoload :BootLoader, "merb-core/bootloader"
4
+ autoload :Config, "merb-core/config"
5
+ autoload :Const, "merb-core/constants"
6
+ autoload :ConditionalGetMixin, "merb-core/controller/mixins/conditional_get"
7
+ autoload :ControllerMixin, "merb-core/controller/mixins/controller"
8
+ autoload :ControllerExceptions, "merb-core/controller/exceptions"
9
+ autoload :Dispatcher, "merb-core/dispatch/dispatcher"
10
+ autoload :AuthenticationMixin, "merb-core/controller/mixins/authentication"
11
+ autoload :BasicAuthenticationMixin, "merb-core/controller/mixins/authentication/basic"
12
+ autoload :ErubisCaptureMixin, "merb-core/controller/mixins/erubis_capture"
13
+ autoload :Plugins, "merb-core/plugins"
14
+ autoload :Rack, "merb-core/rack"
15
+ autoload :RenderMixin, "merb-core/controller/mixins/render"
16
+ autoload :Request, "merb-core/dispatch/request"
17
+ autoload :ResponderMixin, "merb-core/controller/mixins/responder"
18
+ autoload :Router, "merb-core/dispatch/router"
19
+ autoload :Test, "merb-core/test"
20
+ autoload :Worker, "merb-core/dispatch/worker"
21
+ end
22
+
23
+ # Require this rather than autoloading it so we can be sure the default template
24
+ # gets registered
25
+ require 'merb-core/core_ext'
26
+ require "merb-core/controller/template"
27
+ require "merb-core/controller/merb_controller"
28
+
29
+ module Merb
30
+ module InlineTemplates; end
31
+ end
@@ -0,0 +1,717 @@
1
+ module Merb
2
+
3
+ class BootLoader
4
+
5
+ # def self.subclasses
6
+ #---
7
+ # @semipublic
8
+ cattr_accessor :subclasses, :after_load_callbacks, :before_load_callbacks, :finished
9
+ self.subclasses, self.after_load_callbacks, self.before_load_callbacks, self.finished = [], [], [], []
10
+
11
+ class << self
12
+
13
+ # Adds the inheriting class to the list of subclasses in a position
14
+ # specified by the before and after methods.
15
+ #
16
+ # ==== Parameters
17
+ # klass<Class>:: The class inheriting from Merb::BootLoader.
18
+ def inherited(klass)
19
+ subclasses << klass.to_s
20
+ super
21
+ end
22
+
23
+ # ==== Parameters
24
+ # klass<~to_s>::
25
+ # The boot loader class after which this boot loader should be run.
26
+ #
27
+ #---
28
+ # @public
29
+ def after(klass)
30
+ move_klass(klass, 1)
31
+ end
32
+
33
+ # ==== Parameters
34
+ # klass<~to_s>::
35
+ # The boot loader class before which this boot loader should be run.
36
+ #
37
+ #---
38
+ # @public
39
+ def before(klass)
40
+ move_klass(klass, 0)
41
+ end
42
+
43
+ # Move a class that is inside the bootloader to some place in the Array,
44
+ # relative to another class.
45
+ #
46
+ # ==== Parameters
47
+ # klass<~to_s>::
48
+ # The klass to move the bootloader relative to
49
+ # where<Integer>::
50
+ # 0 means insert it before; 1 means insert it after
51
+ def move_klass(klass, where)
52
+ index = Merb::BootLoader.subclasses.index(klass.to_s)
53
+ if index
54
+ Merb::BootLoader.subclasses.delete(self.to_s)
55
+ Merb::BootLoader.subclasses.insert(index + where, self.to_s)
56
+ end
57
+ end
58
+
59
+ # Runs all boot loader classes by calling their run methods.
60
+ def run
61
+ Merb.started = true
62
+ subklasses = subclasses.dup
63
+ until subclasses.empty?
64
+ time = Time.now.to_i
65
+ bootloader = subclasses.shift
66
+ if (ENV['DEBUG'] || $DEBUG || Merb::Config[:verbose]) && Merb.logger
67
+ Merb.logger.debug!("Loading: #{bootloader}")
68
+ end
69
+ Object.full_const_get(bootloader).run
70
+ if (ENV['DEBUG'] || $DEBUG || Merb::Config[:verbose]) && Merb.logger
71
+ Merb.logger.debug!("It took: #{Time.now.to_i - time}")
72
+ end
73
+ self.finished << bootloader
74
+ end
75
+ self.subclasses = subklasses
76
+ end
77
+
78
+ # Determines whether or not a specific bootloader has finished yet.
79
+ #
80
+ # ==== Parameters
81
+ # bootloader<String, Class>:: The name of the bootloader to check.
82
+ #
83
+ # ==== Returns
84
+ # Boolean:: Whether or not the bootloader has finished.
85
+ def finished?(bootloader)
86
+ self.finished.include?(bootloader.to_s)
87
+ end
88
+
89
+ # Set up the default framework
90
+ #
91
+ # ==== Returns
92
+ # nil
93
+ #
94
+ #---
95
+ # @public
96
+ def default_framework
97
+ %w[view model helper controller mailer part].each do |component|
98
+ Merb.push_path(component.to_sym, Merb.root_path("app/#{component}s"))
99
+ end
100
+ Merb.push_path(:application, Merb.root_path("app" / "controllers" / "application.rb"))
101
+ Merb.push_path(:config, Merb.root_path("config"), nil)
102
+ Merb.push_path(:router, Merb.dir_for(:config), (Merb::Config[:router_file] || "router.rb"))
103
+ Merb.push_path(:lib, Merb.root_path("lib"), nil)
104
+ Merb.push_path(:log, Merb.log_path, nil)
105
+ Merb.push_path(:public, Merb.root_path("public"), nil)
106
+ Merb.push_path(:stylesheet, Merb.dir_for(:public) / "stylesheets", nil)
107
+ Merb.push_path(:javascript, Merb.dir_for(:public) / "javascripts", nil)
108
+ Merb.push_path(:image, Merb.dir_for(:public) / "images", nil)
109
+ nil
110
+ end
111
+
112
+ # ==== Parameters
113
+ # &block::
114
+ # A block to be added to the callbacks that will be executed after the
115
+ # app loads.
116
+ #
117
+ #---
118
+ # @public
119
+ def after_app_loads(&block)
120
+ after_load_callbacks << block
121
+ end
122
+
123
+ # ==== Parameters
124
+ # &block::
125
+ # A block to be added to the callbacks that will be executed before the
126
+ # app loads.
127
+ #
128
+ #---
129
+ # @public
130
+ def before_app_loads(&block)
131
+ before_load_callbacks << block
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+
139
+ # Set up the logger.
140
+ #
141
+ # Place the logger inside of the Merb log directory (set up in
142
+ # Merb::BootLoader::BuildFramework)
143
+ class Merb::BootLoader::Logger < Merb::BootLoader
144
+
145
+ # Sets Merb.logger to a new logger created based on the config settings.
146
+ def self.run
147
+ Merb.logger = Merb::Logger.new(Merb.log_file, Merb::Config[:log_level], Merb::Config[:log_delimiter], Merb::Config[:log_auto_flush])
148
+ print_warnings
149
+ end
150
+
151
+ def self.print_warnings
152
+ if Gem::Version.new(Gem::RubyGemsVersion) < Gem::Version.new("1.1")
153
+ Merb.logger.warn! "Merb requires Rubygems 1.1 and later. Please upgrade RubyGems with gem update --system."
154
+ end
155
+ end
156
+ end
157
+
158
+ # Stores pid file.
159
+ #
160
+ # Only run if daemonization or clustering options specified on start.
161
+ # Port is taken from Merb::Config and must be already set at this point.
162
+ class Merb::BootLoader::DropPidFile < Merb::BootLoader
163
+ class << self
164
+
165
+ # Stores a PID file if Merb is running daemonized or clustered.
166
+ def run
167
+ Merb::Server.store_pid(Merb::Config[:port]) if Merb::Config[:daemonize] || Merb::Config[:cluster]
168
+ end
169
+ end
170
+ end
171
+
172
+ # Setup some useful defaults
173
+ class Merb::BootLoader::Defaults < Merb::BootLoader
174
+ def self.run
175
+ Merb::Request.http_method_overrides.concat([
176
+ proc { |c| c.params[:_method] },
177
+ proc { |c| c.env['HTTP_X_HTTP_METHOD_OVERRIDE'] }
178
+ ])
179
+ end
180
+ end
181
+
182
+
183
+ # Build the framework paths.
184
+ #
185
+ # By default, the following paths will be used:
186
+ # application:: Merb.root/app/controller/application.rb
187
+ # config:: Merb.root/config
188
+ # lib:: Merb.root/lib
189
+ # log:: Merb.root/log
190
+ # view:: Merb.root/app/views
191
+ # model:: Merb.root/app/models
192
+ # controller:: Merb.root/app/controllers
193
+ # helper:: Merb.root/app/helpers
194
+ # mailer:: Merb.root/app/mailers
195
+ # part:: Merb.root/app/parts
196
+ #
197
+ # To override the default, set Merb::Config[:framework] in your initialization
198
+ # file. Merb::Config[:framework] takes a Hash whose key is the name of the
199
+ # path, and whose values can be passed into Merb.push_path (see Merb.push_path
200
+ # for full details).
201
+ #
202
+ # ==== Notes
203
+ # All paths will default to Merb.root, so you can get a flat-file structure by
204
+ # doing Merb::Config[:framework] = {}.
205
+ #
206
+ # ==== Example
207
+ # Merb::Config[:framework] = {
208
+ # :view => Merb.root / "views",
209
+ # :model => Merb.root / "models",
210
+ # :lib => Merb.root / "lib",
211
+ # :public => [Merb.root / "public", nil]
212
+ # :router => [Merb.root / "config", "router.rb"]
213
+ # }
214
+ #
215
+ # That will set up a flat directory structure with the config files and
216
+ # controller files under Merb.root, but with models, views, and lib with their
217
+ # own folders off of Merb.root.
218
+ class Merb::BootLoader::BuildFramework < Merb::BootLoader
219
+ class << self
220
+
221
+ # Builds the framework directory structure.
222
+ def run
223
+ build_framework
224
+ end
225
+
226
+ # This method should be overridden in init.rb before Merb.start to set up
227
+ # a different framework structure.
228
+ def build_framework
229
+ if File.exists?(Merb.root / "config" / "framework.rb")
230
+ require Merb.root / "config" / "framework"
231
+ elsif File.exists?(Merb.root / "framework.rb")
232
+ require Merb.root / "framework"
233
+ else
234
+ Merb::BootLoader.default_framework
235
+ end
236
+ (Merb::Config[:framework] || {}).each do |name, path|
237
+ path = Array(path)
238
+ Merb.push_path(name, path.first, path.length == 2 ? path[1] : "**/*.rb")
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ class Merb::BootLoader::Dependencies < Merb::BootLoader
245
+
246
+ cattr_accessor :dependencies
247
+ self.dependencies = []
248
+
249
+ # Load the init_file specified in Merb::Config or if not specified, the
250
+ # init.rb file from the Merb configuration directory, and any environment
251
+ # files, which register the list of necessary dependencies and any
252
+ # after_app_loads hooks.
253
+ #
254
+ # Dependencies can hook into the bootloader process itself by using
255
+ # before or after insertion methods. Since these are loaded from this
256
+ # bootloader (Dependencies), they can only adapt the bootloaders that
257
+ # haven't been loaded up until this point.
258
+
259
+ def self.run
260
+ load_initfile
261
+ load_env_config
262
+ enable_json_gem unless Merb::disabled?(:json)
263
+ load_dependencies
264
+ update_logger
265
+ end
266
+
267
+ def self.load_dependencies
268
+ dependencies.each { |dependency| Kernel.load_dependency(dependency) }
269
+ end
270
+
271
+ def self.enable_json_gem
272
+ require "json/ext"
273
+ rescue LoadError
274
+ require "json/pure"
275
+ end
276
+
277
+ def self.update_logger
278
+ updated_logger_options = [ Merb.log_file, Merb::Config[:log_level], Merb::Config[:log_delimiter], Merb::Config[:log_auto_flush] ]
279
+ Merb::BootLoader::Logger.run if updated_logger_options != Merb.logger.init_args
280
+ end
281
+
282
+ private
283
+
284
+ # Determines the path for the environment configuration file
285
+ def self.env_config
286
+ Merb.dir_for(:config) / "environments" / (Merb.environment + ".rb")
287
+ end
288
+
289
+ # Checks to see whether or not an environment configuration exists
290
+ def self.env_config?
291
+ Merb.environment && File.exist?(env_config)
292
+ end
293
+
294
+ # Loads the environment configuration file, if any
295
+ def self.load_env_config
296
+ load(env_config) if env_config?
297
+ end
298
+
299
+ # Determines the init file to use, if any.
300
+ # By default Merb uses init.rb from application config directory.
301
+ def self.initfile
302
+ if Merb::Config[:init_file]
303
+ Merb::Config[:init_file].chomp(".rb") + ".rb"
304
+ else
305
+ Merb.dir_for(:config) / "init.rb"
306
+ end
307
+ end
308
+
309
+ # Loads the init file, should one exist
310
+ def self.load_initfile
311
+ load(initfile) if File.exists?(initfile)
312
+ end
313
+
314
+ end
315
+
316
+ class Merb::BootLoader::MixinSession < Merb::BootLoader
317
+
318
+ # Mixin the session functionality; this is done before BeforeAppLoads
319
+ # so that SessionContainer and SessionStoreContainer can be subclassed by
320
+ # plugin session stores for example - these need to be loaded in a
321
+ # before_app_loads block or a BootLoader that runs after MixinSession.
322
+ #
323
+ # Note: access to Merb::Config is needed, so it needs to run after
324
+ # Merb::BootLoader::Dependencies is done.
325
+ def self.run
326
+ require 'merb-core/dispatch/session'
327
+ Merb::Controller.send(:include, ::Merb::SessionMixin)
328
+ Merb::Request.send(:include, ::Merb::SessionMixin::RequestMixin)
329
+ end
330
+
331
+ end
332
+
333
+ class Merb::BootLoader::BeforeAppLoads < Merb::BootLoader
334
+
335
+ # Call any before_app_loads hooks that were registered via before_app_loads
336
+ # in any plugins.
337
+ def self.run
338
+ Merb::BootLoader.before_load_callbacks.each { |x| x.call }
339
+ end
340
+ end
341
+
342
+ # Load all classes inside the load paths.
343
+ #
344
+ # This is used in conjunction with Merb::BootLoader::ReloadClasses to track
345
+ # files that need to be reloaded, and which constants need to be removed in
346
+ # order to reload a file.
347
+ #
348
+ # This also adds the model, controller, and lib directories to the load path,
349
+ # so they can be required in order to avoid load-order issues.
350
+ class Merb::BootLoader::LoadClasses < Merb::BootLoader
351
+ LOADED_CLASSES = {}
352
+ MTIMES = {}
353
+
354
+ class << self
355
+
356
+ # Load all classes from Merb's native load paths.
357
+ def run
358
+ # Add models, controllers, helpers and lib to the load path
359
+ $LOAD_PATH.unshift Merb.dir_for(:model)
360
+ $LOAD_PATH.unshift Merb.dir_for(:controller)
361
+ $LOAD_PATH.unshift Merb.dir_for(:lib)
362
+ $LOAD_PATH.unshift Merb.dir_for(:helper)
363
+
364
+ # Load application file if it exists - for flat applications
365
+ load_file Merb.dir_for(:application) if File.file?(Merb.dir_for(:application))
366
+
367
+ # Load classes and their requirements
368
+ Merb.load_paths.each do |component, path|
369
+ next unless path.last && component != :application
370
+ load_classes(path.first / path.last)
371
+ end
372
+
373
+ Merb::Controller.send :include, Merb::GlobalHelpers
374
+ end
375
+
376
+ # ==== Parameters
377
+ # file<String>:: The file to load.
378
+ def load_file(file)
379
+ klasses = ObjectSpace.classes.dup
380
+ load file
381
+ LOADED_CLASSES[file] = ObjectSpace.classes - klasses
382
+ MTIMES[file] = File.mtime(file)
383
+ end
384
+
385
+ # Load classes from given paths - using path/glob pattern.
386
+ #
387
+ # *paths<Array>::
388
+ # Array of paths to load classes from - may contain glob pattern
389
+ def load_classes(*paths)
390
+ orphaned_classes = []
391
+ paths.flatten.each do |path|
392
+ Dir[path].each do |file|
393
+ begin
394
+ load_file file
395
+ rescue NameError => ne
396
+ orphaned_classes.unshift(file)
397
+ end
398
+ end
399
+ end
400
+ load_classes_with_requirements(orphaned_classes)
401
+ end
402
+
403
+ # ==== Parameters
404
+ # file<String>:: The file to reload.
405
+ def reload(file)
406
+ remove_classes_in_file(file) { |f| load_file(f) }
407
+ end
408
+
409
+ # Reload the router to regenerate all routes.
410
+ def reload_router!
411
+ if File.file?(router_file = Merb.dir_for(:router) / Merb.glob_for(:router))
412
+ Merb::Router.reset!
413
+ reload router_file
414
+ end
415
+ end
416
+
417
+ # ==== Parameters
418
+ # file<String>:: The file to remove classes for.
419
+ # &block:: A block to call with the file that has been removed.
420
+ def remove_classes_in_file(file, &block)
421
+ Merb.klass_hashes.each {|x| x.protect_keys!}
422
+ if klasses = LOADED_CLASSES.delete(file)
423
+ klasses.each { |klass| remove_constant(klass) unless klass.to_s =~ /Router/ }
424
+ end
425
+ yield file if block_given?
426
+ Merb.klass_hashes.each {|x| x.unprotect_keys!}
427
+ end
428
+
429
+ # ==== Parameters
430
+ # const<Class>:: The class to remove.
431
+ def remove_constant(const)
432
+ # This is to support superclasses (like AbstractController) that track
433
+ # their subclasses in a class variable. Classes that wish to use this
434
+ # functionality are required to alias it to _subclasses_list. Plugins
435
+ # for ORMs and other libraries should keep this in mind.
436
+ superklass = const
437
+ until (superklass = superklass.superclass).nil?
438
+ if superklass.respond_to?(:_subclasses_list)
439
+ superklass.send(:_subclasses_list).delete(klass)
440
+ superklass.send(:_subclasses_list).delete(klass.to_s)
441
+ end
442
+ end
443
+
444
+ parts = const.to_s.split("::")
445
+ base = parts.size == 1 ? Object : Object.full_const_get(parts[0..-2].join("::"))
446
+ object = parts[-1].to_s
447
+ begin
448
+ base.send(:remove_const, object)
449
+ Merb.logger.debug("Removed constant #{object} from #{base}")
450
+ rescue NameError
451
+ Merb.logger.debug("Failed to remove constant #{object} from #{base}")
452
+ end
453
+ end
454
+
455
+ private
456
+
457
+ # "Better loading" of classes. If a class fails to load due to a NameError
458
+ # it will be added to the failed_classes and load cycle will be repeated unless
459
+ # no classes load.
460
+ #
461
+ # ==== Parameters
462
+ # klasses<Array[Class]>:: Classes to load.
463
+ def load_classes_with_requirements(klasses)
464
+ klasses.uniq!
465
+
466
+ while klasses.size > 0
467
+ # Note size to make sure things are loading
468
+ size_at_start = klasses.size
469
+
470
+ # List of failed classes
471
+ failed_classes = []
472
+ # Map classes to exceptions
473
+ error_map = {}
474
+
475
+ klasses.each do |klass|
476
+ klasses.delete(klass)
477
+ begin
478
+ load_file klass
479
+ rescue NameError => ne
480
+ error_map[klass] = ne
481
+ failed_classes.push(klass)
482
+ end
483
+ end
484
+
485
+ # Keep list of classes unique
486
+ failed_classes.each { |k| klasses.push(k) unless klasses.include?(k) }
487
+
488
+ # Stop processing if nothing loads or if everything has loaded
489
+ if klasses.size == size_at_start && klasses.size != 0
490
+ # Write all remaining failed classes and their exceptions to the log
491
+ messages = error_map.only(*failed_classes).map do |klass, e|
492
+ ["Could not load #{klass}:\n\n#{e.message} - (#{e.class})", "#{(e.backtrace || []).join("\n")}"]
493
+ end
494
+ messages.each { |msg, trace| Merb.logger.fatal!("#{msg}\n\n#{trace}") }
495
+ raise LoadError, "#{messages[0][0]} (see log for details)"
496
+ end
497
+ break if(klasses.size == size_at_start || klasses.size == 0)
498
+ end
499
+ end
500
+
501
+ end
502
+
503
+ end
504
+
505
+ class Merb::BootLoader::Templates < Merb::BootLoader
506
+ class << self
507
+
508
+ # Loads the templates into the Merb::InlineTemplates module.
509
+ def run
510
+ template_paths.each do |path|
511
+ Merb::Template.inline_template(File.open(path))
512
+ end
513
+ end
514
+
515
+ # ==== Returns
516
+ # Array[String]:: Template files found.
517
+ def template_paths
518
+ extension_glob = "{#{Merb::Template.template_extensions.join(',')}}"
519
+
520
+ # This gets all templates set in the controllers template roots
521
+ # We separate the two maps because most of controllers will have
522
+ # the same _template_root, so it's silly to be globbing the same
523
+ # path over and over.
524
+ controller_view_paths = []
525
+ Merb::AbstractController._abstract_subclasses.each do |klass|
526
+ next if (const = Object.full_const_get(klass))._template_root.blank?
527
+ controller_view_paths += const._template_roots.map { |pair| pair.first }
528
+ end
529
+ template_paths = controller_view_paths.uniq.compact.map { |path| Dir["#{path}/**/*.#{extension_glob}"] }
530
+
531
+ # This gets the templates that might be created outside controllers
532
+ # template roots. eg app/views/shared/*
533
+ template_paths << Dir["#{Merb.dir_for(:view)}/**/*.#{extension_glob}"] if Merb.dir_for(:view)
534
+
535
+ template_paths.flatten.compact.uniq
536
+ end
537
+ end
538
+ end
539
+
540
+ # Register the default MIME types:
541
+ #
542
+ # By default, the mime-types include:
543
+ # :all:: no transform, */*
544
+ # :yaml:: to_yaml, application/x-yaml or text/yaml
545
+ # :text:: to_text, text/plain
546
+ # :html:: to_html, text/html or application/xhtml+xml or application/html
547
+ # :xml:: to_xml, application/xml or text/xml or application/x-xml
548
+ # :js:: to_json, text/javascript ot application/javascript or application/x-javascript
549
+ # :json:: to_json, application/json or text/x-json
550
+ class Merb::BootLoader::MimeTypes < Merb::BootLoader
551
+
552
+ # Registers the default MIME types.
553
+ def self.run
554
+ Merb.add_mime_type(:all, nil, %w[*/*])
555
+ Merb.add_mime_type(:yaml, :to_yaml, %w[application/x-yaml text/yaml], :charset => "utf-8")
556
+ Merb.add_mime_type(:text, :to_text, %w[text/plain], :charset => "utf-8")
557
+ Merb.add_mime_type(:html, :to_html, %w[text/html application/xhtml+xml application/html], :charset => "utf-8")
558
+ Merb.add_mime_type(:xml, :to_xml, %w[application/xml text/xml application/x-xml], {:charset => "utf-8"}, 0.9998)
559
+ Merb.add_mime_type(:js, :to_json, %w[text/javascript application/javascript application/x-javascript], :charset => "utf-8")
560
+ Merb.add_mime_type(:json, :to_json, %w[application/json text/x-json], :charset => "utf-8")
561
+ end
562
+ end
563
+
564
+ class Merb::BootLoader::Cookies < Merb::BootLoader
565
+
566
+ def self.run
567
+ require 'merb-core/dispatch/cookies'
568
+ Merb::Controller.send(:include, Merb::CookiesMixin)
569
+ Merb::Request.send(:include, Merb::CookiesMixin::RequestMixin)
570
+ end
571
+
572
+ end
573
+
574
+ class Merb::BootLoader::SetupSession < Merb::BootLoader
575
+
576
+ # Enable the configured session container(s); any class that inherits from
577
+ # SessionContainer will be considered by its session_store_type attribute.
578
+ def self.run
579
+ # Require all standard session containers.
580
+ Dir[Merb.framework_root / "merb-core" / "dispatch" / "session" / "*.rb"].each do |file|
581
+ base_name = File.basename(file, ".rb")
582
+ require file unless base_name == "container" || base_name == "store_container"
583
+ end
584
+
585
+ # Set some defaults.
586
+ Merb::Config[:session_id_key] ||= "_session_id"
587
+
588
+ # List of all session_stores from :session_stores and :session_store config options.
589
+ config_stores = Merb::Config.session_stores
590
+
591
+ # Register all configured session stores - any loaded session container class
592
+ # (subclassed from Merb::SessionContainer) will be available for registration.
593
+ Merb::SessionContainer.subclasses.each do |class_name|
594
+ if(store = Object.full_const_get(class_name)) &&
595
+ config_stores.include?(store.session_store_type)
596
+ Merb::Request.register_session_type(store.session_store_type, class_name)
597
+ end
598
+ end
599
+
600
+ # Mixin the Merb::Session module to add app-level functionality to sessions
601
+ Merb::SessionContainer.send(:include, Merb::Session)
602
+ end
603
+
604
+ end
605
+
606
+ class Merb::BootLoader::AfterAppLoads < Merb::BootLoader
607
+
608
+ # Call any after_app_loads hooks that were registered via after_app_loads in
609
+ # init.rb.
610
+ def self.run
611
+ Merb::BootLoader.after_load_callbacks.each {|x| x.call }
612
+ end
613
+ end
614
+
615
+ # In case someone's running a sparse app, the default exceptions require the
616
+ # Exceptions class.
617
+ class Merb::BootLoader::SetupStubClasses < Merb::BootLoader
618
+ def self.run
619
+ unless defined?(Exceptions)
620
+ Object.class_eval <<-RUBY
621
+ class Application < Merb::Controller
622
+ abstract!
623
+ end
624
+
625
+ class Exceptions < Application
626
+ end
627
+ RUBY
628
+ end
629
+ end
630
+ end
631
+
632
+ class Merb::BootLoader::ChooseAdapter < Merb::BootLoader
633
+
634
+ # Choose the Rack adapter/server to use and set Merb.adapter.
635
+ def self.run
636
+ Merb.adapter = Merb::Rack::Adapter.get(Merb::Config[:adapter])
637
+ end
638
+ end
639
+
640
+ class Merb::BootLoader::StartWorkerThread < Merb::BootLoader
641
+
642
+ # Choose the Rack adapter/server to use and set Merb.adapter.
643
+ def self.run
644
+ Merb::Worker.new
645
+ end
646
+ end
647
+
648
+ class Merb::BootLoader::RackUpApplication < Merb::BootLoader
649
+ # Setup the Merb Rack App or read a rackup file located at
650
+ # Merb::Config[:rackup] with the same syntax as the
651
+ # rackup tool that comes with rack. Automatically evals the file in
652
+ # the context of a Rack::Builder.new { } block. Allows for mounting
653
+ # additional apps or middleware.
654
+ def self.run
655
+ require 'rack'
656
+ if File.exists?(Merb.dir_for(:config) / "rack.rb")
657
+ Merb::Config[:rackup] ||= Merb.dir_for(:config) / "rack.rb"
658
+ end
659
+
660
+ if Merb::Config[:rackup]
661
+ rackup_code = File.read(Merb::Config[:rackup])
662
+ Merb::Config[:app] = eval("::Rack::Builder.new {( #{rackup_code}\n )}.to_app", TOPLEVEL_BINDING, Merb::Config[:rackup])
663
+ else
664
+ Merb::Config[:app] = ::Rack::Builder.new {
665
+ if prefix = ::Merb::Config[:path_prefix]
666
+ use Merb::Rack::PathPrefix, prefix
667
+ end
668
+ use Merb::Rack::Static, Merb.dir_for(:public)
669
+ run Merb::Rack::Application.new
670
+ }.to_app
671
+ end
672
+
673
+ end
674
+ end
675
+
676
+ class Merb::BootLoader::ReloadClasses < Merb::BootLoader
677
+
678
+ class TimedExecutor
679
+ def self.every(seconds, &block)
680
+ Thread.abort_on_exception = true
681
+ Thread.new do
682
+ loop do
683
+ sleep( seconds )
684
+ block.call
685
+ end
686
+ Thread.exit
687
+ end
688
+ end
689
+ end
690
+
691
+ # Setup the class reloader if it's been specified in config.
692
+ def self.run
693
+ return unless Merb::Config[:reload_classes]
694
+
695
+ TimedExecutor.every(Merb::Config[:reload_time] || 0.5) do
696
+ reload
697
+ end
698
+
699
+ end
700
+
701
+ # Reloads all files.
702
+ def self.reload
703
+ paths = []
704
+ Merb.load_paths.each do |path_name, file_info|
705
+ path, glob = file_info
706
+ next unless glob
707
+ paths << Dir[path / glob]
708
+ end
709
+
710
+ paths << Merb.dir_for(:application) if Merb.dir_for(:application) && File.file?(Merb.dir_for(:application))
711
+
712
+ paths.flatten.each do |file|
713
+ next if Merb::BootLoader::LoadClasses::MTIMES[file] && Merb::BootLoader::LoadClasses::MTIMES[file] == File.mtime(file)
714
+ Merb::BootLoader::LoadClasses.reload(file)
715
+ end
716
+ end
717
+ end