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.
- data/CHANGELOG +992 -0
- data/CONTRIBUTORS +94 -0
- data/LICENSE +20 -0
- data/PUBLIC_CHANGELOG +142 -0
- data/README +21 -0
- data/Rakefile +458 -0
- data/TODO +0 -0
- data/bin/merb +11 -0
- data/bin/merb-specs +5 -0
- data/lib/merb-core.rb +598 -0
- data/lib/merb-core/autoload.rb +31 -0
- data/lib/merb-core/bootloader.rb +717 -0
- data/lib/merb-core/config.rb +305 -0
- data/lib/merb-core/constants.rb +45 -0
- data/lib/merb-core/controller/abstract_controller.rb +568 -0
- data/lib/merb-core/controller/exceptions.rb +315 -0
- data/lib/merb-core/controller/merb_controller.rb +256 -0
- data/lib/merb-core/controller/mime.rb +107 -0
- data/lib/merb-core/controller/mixins/authentication.rb +123 -0
- data/lib/merb-core/controller/mixins/conditional_get.rb +83 -0
- data/lib/merb-core/controller/mixins/controller.rb +319 -0
- data/lib/merb-core/controller/mixins/render.rb +513 -0
- data/lib/merb-core/controller/mixins/responder.rb +469 -0
- data/lib/merb-core/controller/template.rb +254 -0
- data/lib/merb-core/core_ext.rb +9 -0
- data/lib/merb-core/core_ext/hash.rb +7 -0
- data/lib/merb-core/core_ext/kernel.rb +340 -0
- data/lib/merb-core/dispatch/cookies.rb +130 -0
- data/lib/merb-core/dispatch/default_exception/default_exception.rb +93 -0
- data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +198 -0
- data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +73 -0
- data/lib/merb-core/dispatch/default_exception/views/index.html.erb +94 -0
- data/lib/merb-core/dispatch/dispatcher.rb +176 -0
- data/lib/merb-core/dispatch/request.rb +729 -0
- data/lib/merb-core/dispatch/router.rb +151 -0
- data/lib/merb-core/dispatch/router/behavior.rb +566 -0
- data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
- data/lib/merb-core/dispatch/router/resources.rb +191 -0
- data/lib/merb-core/dispatch/router/route.rb +511 -0
- data/lib/merb-core/dispatch/session.rb +222 -0
- data/lib/merb-core/dispatch/session/container.rb +74 -0
- data/lib/merb-core/dispatch/session/cookie.rb +173 -0
- data/lib/merb-core/dispatch/session/memcached.rb +68 -0
- data/lib/merb-core/dispatch/session/memory.rb +99 -0
- data/lib/merb-core/dispatch/session/store_container.rb +150 -0
- data/lib/merb-core/dispatch/worker.rb +28 -0
- data/lib/merb-core/gem_ext/erubis.rb +77 -0
- data/lib/merb-core/logger.rb +203 -0
- data/lib/merb-core/plugins.rb +67 -0
- data/lib/merb-core/rack.rb +25 -0
- data/lib/merb-core/rack/adapter.rb +44 -0
- data/lib/merb-core/rack/adapter/ebb.rb +25 -0
- data/lib/merb-core/rack/adapter/evented_mongrel.rb +26 -0
- data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
- data/lib/merb-core/rack/adapter/irb.rb +118 -0
- data/lib/merb-core/rack/adapter/mongrel.rb +26 -0
- data/lib/merb-core/rack/adapter/runner.rb +28 -0
- data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +26 -0
- data/lib/merb-core/rack/adapter/thin.rb +39 -0
- data/lib/merb-core/rack/adapter/thin_turbo.rb +24 -0
- data/lib/merb-core/rack/adapter/webrick.rb +36 -0
- data/lib/merb-core/rack/application.rb +32 -0
- data/lib/merb-core/rack/handler/mongrel.rb +97 -0
- data/lib/merb-core/rack/middleware.rb +20 -0
- data/lib/merb-core/rack/middleware/conditional_get.rb +29 -0
- data/lib/merb-core/rack/middleware/content_length.rb +18 -0
- data/lib/merb-core/rack/middleware/csrf.rb +73 -0
- data/lib/merb-core/rack/middleware/path_prefix.rb +31 -0
- data/lib/merb-core/rack/middleware/profiler.rb +19 -0
- data/lib/merb-core/rack/middleware/static.rb +45 -0
- data/lib/merb-core/rack/middleware/tracer.rb +20 -0
- data/lib/merb-core/server.rb +284 -0
- data/lib/merb-core/tasks/audit.rake +68 -0
- data/lib/merb-core/tasks/gem_management.rb +229 -0
- data/lib/merb-core/tasks/merb.rb +1 -0
- data/lib/merb-core/tasks/merb_rake_helper.rb +80 -0
- data/lib/merb-core/tasks/stats.rake +71 -0
- data/lib/merb-core/test.rb +11 -0
- data/lib/merb-core/test/helpers.rb +9 -0
- data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
- data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
- data/lib/merb-core/test/helpers/request_helper.rb +393 -0
- data/lib/merb-core/test/helpers/route_helper.rb +39 -0
- data/lib/merb-core/test/helpers/view_helper.rb +121 -0
- data/lib/merb-core/test/matchers.rb +9 -0
- data/lib/merb-core/test/matchers/controller_matchers.rb +351 -0
- data/lib/merb-core/test/matchers/route_matchers.rb +137 -0
- data/lib/merb-core/test/matchers/view_matchers.rb +375 -0
- data/lib/merb-core/test/run_specs.rb +49 -0
- data/lib/merb-core/test/tasks/spectasks.rb +68 -0
- data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
- data/lib/merb-core/test/test_ext/object.rb +14 -0
- data/lib/merb-core/test/test_ext/string.rb +14 -0
- data/lib/merb-core/vendor/facets.rb +2 -0
- data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
- data/lib/merb-core/vendor/facets/inflect.rb +342 -0
- data/lib/merb-core/version.rb +3 -0
- 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
|