merb 0.4.2 → 0.5.0
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/README +21 -14
- data/Rakefile +157 -108
- data/SVN_REVISION +1 -0
- data/app_generators/merb/templates/Rakefile +20 -4
- data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +1 -1
- data/app_generators/merb/templates/config/boot.rb +1 -1
- data/app_generators/merb/templates/config/dependencies.rb +3 -3
- data/app_generators/merb/templates/config/merb.yml +5 -0
- data/app_generators/merb/templates/config/merb_init.rb +3 -3
- data/app_generators/merb/templates/script/destroy +3 -0
- data/app_generators/merb/templates/script/generate +1 -1
- data/app_generators/merb/templates/spec/spec_helper.rb +2 -2
- data/app_generators/merb/templates/test/test_helper.rb +1 -1
- data/app_generators/merb_plugin/merb_plugin_generator.rb +4 -0
- data/bin/merb +1 -3
- data/lib/merb.rb +144 -76
- data/lib/merb/abstract_controller.rb +6 -5
- data/lib/merb/assets.rb +119 -0
- data/lib/merb/boot_loader.rb +217 -0
- data/lib/merb/caching.rb +1 -1
- data/lib/merb/caching/action_cache.rb +1 -1
- data/lib/merb/caching/fragment_cache.rb +1 -1
- data/lib/merb/caching/store/file_cache.rb +1 -1
- data/lib/merb/config.rb +290 -0
- data/lib/merb/controller.rb +5 -5
- data/lib/merb/core_ext/get_args.rb +1 -0
- data/lib/merb/core_ext/hash.rb +182 -169
- data/lib/merb/core_ext/kernel.rb +57 -26
- data/lib/merb/dispatcher.rb +6 -6
- data/lib/merb/drb_server.rb +1 -1
- data/lib/merb/generators/merb_generator_helpers.rb +7 -6
- data/lib/merb/logger.rb +1 -1
- data/lib/merb/mail_controller.rb +3 -4
- data/lib/merb/mailer.rb +2 -2
- data/lib/merb/mixins/basic_authentication.rb +2 -2
- data/lib/merb/mixins/controller.rb +1 -1
- data/lib/merb/mixins/general_controller.rb +13 -20
- data/lib/merb/mixins/inline_partial.rb +32 -0
- data/lib/merb/mixins/render.rb +3 -3
- data/lib/merb/mixins/responder.rb +1 -1
- data/lib/merb/mixins/view_context.rb +159 -33
- data/lib/merb/mongrel_handler.rb +9 -9
- data/lib/merb/plugins.rb +1 -1
- data/lib/merb/request.rb +25 -1
- data/lib/merb/router.rb +264 -226
- data/lib/merb/server.rb +66 -560
- data/lib/merb/session/cookie_store.rb +14 -13
- data/lib/merb/session/mem_cache_session.rb +20 -10
- data/lib/merb/session/memory_session.rb +21 -11
- data/lib/merb/template.rb +2 -2
- data/lib/merb/template/erubis.rb +3 -33
- data/lib/merb/template/haml.rb +8 -3
- data/lib/merb/test/fake_request.rb +8 -3
- data/lib/merb/test/helper.rb +66 -22
- data/lib/merb/test/rspec.rb +9 -155
- data/lib/merb/test/rspec_matchers/controller_matchers.rb +117 -0
- data/lib/merb/test/rspec_matchers/markup_matchers.rb +98 -0
- data/lib/merb/upload_handler.rb +2 -1
- data/lib/merb/version.rb +38 -3
- data/lib/merb/view_context.rb +1 -2
- data/lib/tasks/merb.rake +11 -11
- data/merb_generators/part_controller/USAGE +5 -0
- data/merb_generators/part_controller/part_controller_generator.rb +27 -0
- data/merb_generators/part_controller/templates/controller.rb +8 -0
- data/merb_generators/part_controller/templates/helper.rb +5 -0
- data/merb_generators/part_controller/templates/index.html.erb +3 -0
- data/rspec_generators/merb_controller_test/merb_controller_test_generator.rb +1 -1
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/fixtures/controllers/dispatch_spec_controllers.rb +9 -1
- data/spec/fixtures/controllers/render_spec_controllers.rb +5 -5
- data/spec/fixtures/models/router_spec_models.rb +10 -0
- data/spec/merb/abstract_controller_spec.rb +2 -2
- data/spec/merb/assets_spec.rb +207 -0
- data/spec/merb/caching_spec.rb +2 -2
- data/spec/merb/controller_spec.rb +7 -2
- data/spec/merb/cookie_store_spec.rb +1 -1
- data/spec/merb/core_ext/class_spec.rb +97 -0
- data/spec/merb/core_ext/enumerable_spec.rb +27 -0
- data/spec/merb/core_ext/hash_spec.rb +251 -0
- data/spec/merb/core_ext/inflector_spec.rb +34 -0
- data/spec/merb/core_ext/kernel_spec.rb +25 -0
- data/spec/merb/core_ext/numeric_spec.rb +26 -0
- data/spec/merb/core_ext/object_spec.rb +47 -0
- data/spec/merb/core_ext/string_spec.rb +22 -0
- data/spec/merb/core_ext/symbol_spec.rb +7 -0
- data/spec/merb/dependency_spec.rb +22 -0
- data/spec/merb/dispatch_spec.rb +23 -12
- data/spec/merb/fake_request_spec.rb +8 -0
- data/spec/merb/generator_spec.rb +140 -21
- data/spec/merb/handler_spec.rb +5 -5
- data/spec/merb/mail_controller_spec.rb +3 -3
- data/spec/merb/render_spec.rb +1 -1
- data/spec/merb/responder_spec.rb +3 -3
- data/spec/merb/router_spec.rb +260 -191
- data/spec/merb/server_spec.rb +5 -5
- data/spec/merb/upload_handler_spec.rb +7 -0
- data/spec/merb/version_spec.rb +33 -0
- data/spec/merb/view_context_spec.rb +217 -59
- data/spec/spec_generator_helper.rb +15 -0
- data/spec/spec_helper.rb +5 -3
- data/spec/spec_helpers/url_shared_behaviour.rb +5 -7
- metadata +32 -7
- data/lib/merb/caching/store/memcache.rb +0 -20
- data/lib/merb/mixins/form_control.rb +0 -332
- data/lib/patch +0 -69
- data/spec/merb/core_ext_spec.rb +0 -464
- data/spec/merb/form_control_mixin_spec.rb +0 -431
data/lib/merb/assets.rb
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
module Merb
|
|
2
|
+
module Assets
|
|
3
|
+
|
|
4
|
+
# Returns true if assets should be bundled (e.g., production mode or
|
|
5
|
+
# :bundle_assets is explicitly enabled), false if not.
|
|
6
|
+
def self.bundle?
|
|
7
|
+
(Merb::Config[:environment] == :production) ||
|
|
8
|
+
(!!Merb::Config[:bundle_assets])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Helpers for handling asset files.
|
|
12
|
+
module AssetHelpers
|
|
13
|
+
# :nodoc:
|
|
14
|
+
ASSET_FILE_EXTENSIONS = {
|
|
15
|
+
:javascript => ".js",
|
|
16
|
+
:stylesheet => ".css"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Returns the URI path to a particular asset file. If +local_path+ is
|
|
20
|
+
# true, returns the path relative to the Merb.root, not the public
|
|
21
|
+
# directory. Uses the path_prefix, if any is configured.
|
|
22
|
+
#
|
|
23
|
+
# asset_path(:javascript, :dingo) #=> "/javascripts/dingo.js"
|
|
24
|
+
# asset_path(:javascript, :dingo, true) #=> "public/javascripts/dingo.js"
|
|
25
|
+
def asset_path(asset_type, filename, local_path = false)
|
|
26
|
+
filename = filename.to_s
|
|
27
|
+
if filename !~ /#{'\\' + ASSET_FILE_EXTENSIONS[asset_type]}\Z/
|
|
28
|
+
filename << ASSET_FILE_EXTENSIONS[asset_type]
|
|
29
|
+
end
|
|
30
|
+
filename = "/#{asset_type}s/#{filename}"
|
|
31
|
+
if local_path
|
|
32
|
+
return "public#{filename}"
|
|
33
|
+
else
|
|
34
|
+
return "#{Merb::Config[:path_prefix]}#{filename}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# An abstract class for bundling text assets into single files.
|
|
40
|
+
class AbstractAssetBundler
|
|
41
|
+
class << self
|
|
42
|
+
|
|
43
|
+
# Add a post-bundle callback, for example to run a Javascript or CSS
|
|
44
|
+
# compressor on the bundled file:
|
|
45
|
+
#
|
|
46
|
+
# Merb::Assets::JavascriptAssetBundler.add_callback do |filename|
|
|
47
|
+
# `yuicompressor #{filename}`
|
|
48
|
+
# end
|
|
49
|
+
def add_callback(&block)
|
|
50
|
+
callbacks << block
|
|
51
|
+
end
|
|
52
|
+
alias_method :after_bundling, :add_callback
|
|
53
|
+
|
|
54
|
+
# An array of any existing callbacks.
|
|
55
|
+
def callbacks
|
|
56
|
+
@callbacks ||= []
|
|
57
|
+
return @callbacks
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The type of asset for which the bundler is responsible.
|
|
61
|
+
def asset_type
|
|
62
|
+
raise NotImplementedError, "should return a symbol for the first argument to be passed to asset_path"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Create a new asset bundler, which will produce a bundled file containing
|
|
67
|
+
# the contents of +files+. If +name+ is +true+ (as in, is an instance of
|
|
68
|
+
# +TrueClass+), the filename is written out as "all", otherwise +name+
|
|
69
|
+
# is coerced into a string.
|
|
70
|
+
def initialize(name, *files)
|
|
71
|
+
@bundle_name = name == true ? :all : name
|
|
72
|
+
@bundle_filename = asset_path(self.class.asset_type, @bundle_name, true)
|
|
73
|
+
@files = files.map { |f| asset_path(self.class.asset_type, f, true) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Creates the new bundled file, executes all the callbacks, and returns
|
|
77
|
+
# the name of the bundled file.
|
|
78
|
+
def bundle!
|
|
79
|
+
# TODO: Move this file check out into an in-memory cache. Also, push it out to the helper level so we don't have to create the helper object.
|
|
80
|
+
unless File.exist?(@bundle_filename)
|
|
81
|
+
bundle_files(@bundle_filename, *@files)
|
|
82
|
+
self.class.callbacks.each { |c| c.call(@bundle_filename) }
|
|
83
|
+
end
|
|
84
|
+
return @bundle_name
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
protected
|
|
88
|
+
|
|
89
|
+
include Merb::Assets::AssetHelpers # for asset_path
|
|
90
|
+
|
|
91
|
+
# Bundle all the filenames in +files+ into +filename+.
|
|
92
|
+
def bundle_files(filename, *files)
|
|
93
|
+
File.open(filename, "w") do |f|
|
|
94
|
+
files.each { |file| f.puts(File.read(file)) }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Bundles javascripts into a single file:
|
|
101
|
+
#
|
|
102
|
+
# javascripts/#{name}.js
|
|
103
|
+
class JavascriptAssetBundler < AbstractAssetBundler
|
|
104
|
+
def self.asset_type
|
|
105
|
+
:javascript
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Bundles stylesheets into a single file:
|
|
110
|
+
#
|
|
111
|
+
# stylesheets/#{name}.css
|
|
112
|
+
class StylesheetAssetBundler < AbstractAssetBundler
|
|
113
|
+
def self.asset_type
|
|
114
|
+
:stylesheet
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
module Merb
|
|
2
|
+
module GlobalHelper;end
|
|
3
|
+
|
|
4
|
+
module BootLoader
|
|
5
|
+
class << self
|
|
6
|
+
|
|
7
|
+
def initialize_merb
|
|
8
|
+
require 'merb'
|
|
9
|
+
@mtime = Time.now if Merb::Config[:reloader] == true
|
|
10
|
+
# Register session types before merb_init.rb so that any additional
|
|
11
|
+
# session stores will be added to the end of the list and become the
|
|
12
|
+
# default.
|
|
13
|
+
register_session_type('memory',
|
|
14
|
+
Merb.framework_root / "merb" / "session" / "memory_session",
|
|
15
|
+
"Using in-memory sessions; sessions will be lost whenever the server stops.")
|
|
16
|
+
register_session_type('mem_cache',
|
|
17
|
+
Merb.framework_root / "merb" / "session" / "mem_cache_session",
|
|
18
|
+
"Using MemCache distributed memory sessions")
|
|
19
|
+
register_session_type('cookie', # Last session type becomes the default
|
|
20
|
+
Merb.framework_root / "merb" / "session" / "cookie_store",
|
|
21
|
+
"Using 'share-nothing' cookie sessions (4kb limit per client)")
|
|
22
|
+
require Merb.root / 'config/merb_init.rb'
|
|
23
|
+
add_controller_mixins
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def max_mtime( files = [] )
|
|
27
|
+
files.map{ |file| File.mtime(file) rescue @mtime }.max
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def register_session_type(name, file, description = nil)
|
|
31
|
+
@registered_session_types ||= YAML::Omap.new
|
|
32
|
+
@registered_session_types[name] = {
|
|
33
|
+
:file => file,
|
|
34
|
+
:description => (description || "Using #{name} sessions")
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_controller_mixins
|
|
39
|
+
types = @registered_session_types
|
|
40
|
+
Merb::Controller.class_eval do
|
|
41
|
+
lib = File.join(Merb.framework_root, 'merb')
|
|
42
|
+
session_store = Merb::Config[:session_store].to_s
|
|
43
|
+
if ["", "false"].include?(session_store)
|
|
44
|
+
puts "Not Using Sessions"
|
|
45
|
+
elsif reg = types[session_store]
|
|
46
|
+
if session_store == "cookie"
|
|
47
|
+
unless Merb::Config[:session_secret_key] && (Merb::Config[:session_secret_key].length >= 16)
|
|
48
|
+
puts("You must specify a session_secret_key in your merb.yml, and it must be at least 16 characters\nbailing out...")
|
|
49
|
+
exit!
|
|
50
|
+
end
|
|
51
|
+
Merb::Controller.session_secret_key = Merb::Config[:session_secret_key]
|
|
52
|
+
end
|
|
53
|
+
require reg[:file]
|
|
54
|
+
include ::Merb::SessionMixin
|
|
55
|
+
puts reg[:description]
|
|
56
|
+
else
|
|
57
|
+
puts "Session store not found, '#{Merb::Config[:session_store]}'."
|
|
58
|
+
puts "Defaulting to CookieStore Sessions"
|
|
59
|
+
unless Merb::Config[:session_secret_key] && (Merb::Config[:session_secret_key].length >= 16)
|
|
60
|
+
puts("You must specify a session_secret_key in your merb.yml, and it must be at least 16 characters\nbailing out...")
|
|
61
|
+
exit!
|
|
62
|
+
end
|
|
63
|
+
Merb::Controller.session_secret_key = Merb::Config[:session_secret_key]
|
|
64
|
+
require types['cookie'][:file]
|
|
65
|
+
include ::Merb::SessionMixin
|
|
66
|
+
puts "(plugin not installed?)"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if Merb::Config[:basic_auth]
|
|
70
|
+
require lib + "/mixins/basic_authentication"
|
|
71
|
+
include ::Merb::AuthenticationMixin
|
|
72
|
+
puts "Basic Authentication mixed in"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def after_app_loads(&block)
|
|
78
|
+
@after_app_blocks ||= []
|
|
79
|
+
@after_app_blocks << block
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def app_loaded?
|
|
83
|
+
@app_loaded
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def load_action_arguments(klasses = Merb::Controller._subclasses)
|
|
87
|
+
begin
|
|
88
|
+
klasses.each do |controller|
|
|
89
|
+
controller = Object.full_const_get(controller)
|
|
90
|
+
controller.action_argument_list = {}
|
|
91
|
+
controller.callable_actions.each do |action, bool|
|
|
92
|
+
controller.action_argument_list[action.to_sym] = ParseTreeArray.translate(controller, action).get_args
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
rescue
|
|
96
|
+
klasses.each { |controller| Object.full_const_get(controller).action_argument_list = {} }
|
|
97
|
+
end if defined?(ParseTreeArray)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def template_paths(type = "*[a-zA-Z]")
|
|
101
|
+
# This gets all templates set in the controllers template roots
|
|
102
|
+
template_paths = Merb::AbstractController._abstract_subclasses.map do |klass|
|
|
103
|
+
Object.full_const_get(klass)._template_root
|
|
104
|
+
end.uniq.map do |path|
|
|
105
|
+
Dir["#{path}/**/#{type}"]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# This gets the templates that might be created outside controllers
|
|
109
|
+
# template roots. eg app/views/shared/*
|
|
110
|
+
template_paths << Dir["#{Merb.root}/app/views/**/*[a-zA-Z]"] if type == "*"
|
|
111
|
+
|
|
112
|
+
template_paths.flatten.compact.uniq || []
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def load_controller_template_path_cache
|
|
116
|
+
Merb::AbstractController.reset_template_path_cache!
|
|
117
|
+
|
|
118
|
+
template_paths.each do |template|
|
|
119
|
+
Merb::AbstractController.add_path_to_template_cache(template)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def load_inline_helpers
|
|
124
|
+
partials = template_paths("_*.{erb,haml}")
|
|
125
|
+
|
|
126
|
+
partials.each do |partial|
|
|
127
|
+
case partial
|
|
128
|
+
when /erb$/
|
|
129
|
+
template = Erubis::Eruby.new(File.read(partial))
|
|
130
|
+
template.def_method(Merb::GlobalHelper, partial.gsub(/[^\.a-zA-Z0-9]/, "__").gsub(/\./, "_"), partial)
|
|
131
|
+
when /haml$/
|
|
132
|
+
if Object.const_defined?(:Haml) and Haml::Engine.instance_methods.include?('def_method')
|
|
133
|
+
template = Haml::Engine.new(File.read(partial), :filename => partial)
|
|
134
|
+
template.def_method(Merb::GlobalHelper, partial.gsub(/[^\.a-zA-Z0-9]/, "__").gsub(/\./, "_"))
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def load_application
|
|
141
|
+
$LOAD_PATH.unshift( File.join(Merb.root , '/app/models') )
|
|
142
|
+
$LOAD_PATH.unshift( File.join(Merb.root , '/app/controllers') )
|
|
143
|
+
$LOAD_PATH.unshift( File.join(Merb.root , '/lib') )
|
|
144
|
+
Merb.load_paths.each do |glob|
|
|
145
|
+
Dir[Merb.root + glob].each { |m| require m }
|
|
146
|
+
end
|
|
147
|
+
load_action_arguments
|
|
148
|
+
load_controller_template_path_cache
|
|
149
|
+
load_inline_helpers
|
|
150
|
+
@app_loaded = true
|
|
151
|
+
(@after_app_blocks || []).each { |b| b.call }
|
|
152
|
+
require "sass/plugin" if File.directory?(Merb.root / "public" / "stylesheets" / "sass")
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def remove_constant(const)
|
|
156
|
+
parts = const.to_s.split("::")
|
|
157
|
+
base = parts.size == 1 ? Object : Object.full_const_get(parts[0..-2].join("::"))
|
|
158
|
+
object = parts[-1].intern
|
|
159
|
+
Merb.logger.info("Removing constant #{object} from #{base}")
|
|
160
|
+
base.send(:remove_const, object) if object
|
|
161
|
+
Merb::Controller._subclasses.delete(const)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def reload
|
|
165
|
+
return if !Merb::Config[:reloader] || !Object.const_defined?(:MERB_PATHS)
|
|
166
|
+
|
|
167
|
+
# First we collect all files in the project (this will also grab newly added files)
|
|
168
|
+
project_files = MERB_PATHS.map { |path| Dir[Merb.root + path] }.flatten.uniq
|
|
169
|
+
partials = template_paths("_*.*").map { |path| Dir[path] }.flatten.uniq
|
|
170
|
+
project_mtime = max_mtime(project_files + partials) # Latest changed time of all project files
|
|
171
|
+
|
|
172
|
+
return if @mtime.nil? || @mtime >= project_mtime # Only continue if a file has changed
|
|
173
|
+
|
|
174
|
+
project_files.each do |file|
|
|
175
|
+
if File.mtime(file) >= @mtime
|
|
176
|
+
# If the file has changed or been added since the last project reload time
|
|
177
|
+
# remove any cannonical constants, based on what type of project file it is
|
|
178
|
+
# and then reload the file
|
|
179
|
+
begin
|
|
180
|
+
constant = case file
|
|
181
|
+
when %r[/app/(models|controllers|parts|mailers)/(.+)\.rb$]
|
|
182
|
+
$2.to_const_string
|
|
183
|
+
when %r[/app/(helpers)/(.+)\.rb$]
|
|
184
|
+
"Merb::" + $2.to_const_string
|
|
185
|
+
end
|
|
186
|
+
remove_constant(constant)
|
|
187
|
+
rescue NameError => e
|
|
188
|
+
Merb.logger.warn "Couldn't remove constant #{constant}"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
begin
|
|
192
|
+
Merb.logger.info("Reloading file #{file}")
|
|
193
|
+
old_subclasses = Merb::Controller._subclasses.dup
|
|
194
|
+
load(file)
|
|
195
|
+
loaded_classes = Merb::Controller._subclasses - old_subclasses
|
|
196
|
+
load_action_arguments(loaded_classes)
|
|
197
|
+
rescue Exception => e
|
|
198
|
+
puts "Error reloading file #{file}: #{e}"
|
|
199
|
+
Merb.logger.warn " Error: #{e}"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# constant = file =~ /\/(controllers|models|mailers|helpers|parts)\/(.*).rb/ ? $2.to_const_string : nil
|
|
203
|
+
# remove_constant($2.to_const_string, ($1 == "helpers") ? Merb : nil)
|
|
204
|
+
# load file and puts "loaded file: #{file}"
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Rebuild the glob cache and erubis inline helpers
|
|
209
|
+
load_controller_template_path_cache
|
|
210
|
+
load_inline_helpers
|
|
211
|
+
|
|
212
|
+
@mtime = project_mtime # As the last action, update the current @mtime
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
end # class << self
|
|
216
|
+
end # BootLoader
|
|
217
|
+
end # Merb
|
data/lib/merb/caching.rb
CHANGED
data/lib/merb/config.rb
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'erubis_ext')
|
|
2
|
+
require File.join(File.dirname(__FILE__), 'version')
|
|
3
|
+
require File.join(File.dirname(__FILE__), 'boot_loader')
|
|
4
|
+
module Merb
|
|
5
|
+
class Config
|
|
6
|
+
class << self
|
|
7
|
+
|
|
8
|
+
def defaults
|
|
9
|
+
@defaults ||= {
|
|
10
|
+
:host => "0.0.0.0",
|
|
11
|
+
:port => "4000",
|
|
12
|
+
:reloader => true,
|
|
13
|
+
:cache_templates => false,
|
|
14
|
+
:merb_root => Dir.pwd,
|
|
15
|
+
:use_mutex => true,
|
|
16
|
+
:session_id_cookie_only => true,
|
|
17
|
+
:query_string_whitelist => [],
|
|
18
|
+
:mongrel_x_sendfile => true
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def [](key)
|
|
23
|
+
(@configuration||{})[key]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def []=(key,val)
|
|
27
|
+
@configuration[key] = val
|
|
28
|
+
end
|
|
29
|
+
def delete(key)
|
|
30
|
+
@configuration.delete key
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fetch(key, default)
|
|
34
|
+
@configuration.fetch key, default
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_yaml
|
|
38
|
+
@configuration.to_yaml
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def setup(global_merb_yml = nil)
|
|
42
|
+
@configuration ||= {}
|
|
43
|
+
if FileTest.exist? "#{defaults[:merb_root]}/framework"
|
|
44
|
+
$LOAD_PATH.unshift( "#{defaults[:merb_root]}/framework" )
|
|
45
|
+
end
|
|
46
|
+
global_merb_yml ||= "#{defaults[:merb_root]}/config/merb.yml"
|
|
47
|
+
apply_configuration_from_file defaults, global_merb_yml
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def apply_configuration_from_file(configuration, file)
|
|
51
|
+
if File.exists?(file)
|
|
52
|
+
configuration.merge(Erubis.load_yaml_file(file))
|
|
53
|
+
else
|
|
54
|
+
configuration
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def parse_args(argv = ARGV)
|
|
59
|
+
@configuration ||= {}
|
|
60
|
+
# Our primary configuration hash for the length of this method
|
|
61
|
+
options = {}
|
|
62
|
+
|
|
63
|
+
# Environment variables always win
|
|
64
|
+
options[:environment] = ENV['MERB_ENV'] if ENV['MERB_ENV']
|
|
65
|
+
|
|
66
|
+
# Build a parser for the command line arguments
|
|
67
|
+
opts = OptionParser.new do |opts|
|
|
68
|
+
opts.version = Merb::VERSION
|
|
69
|
+
opts.release = Merb::RELEASE
|
|
70
|
+
|
|
71
|
+
opts.banner = "Usage: merb [fdcepghmisluMG] [argument]"
|
|
72
|
+
opts.define_head "Merb Mongrel+ Erb. Lightweight replacement for ActionPack."
|
|
73
|
+
opts.separator '*'*80
|
|
74
|
+
opts.separator 'If no flags are given, Merb starts in the foreground on port 4000.'
|
|
75
|
+
opts.separator '*'*80
|
|
76
|
+
|
|
77
|
+
opts.on("-u", "--user USER", "This flag is for having merb run as a user other than the one currently logged in. Note: if you set this you must also provide a --group option for it to take effect.") do |config|
|
|
78
|
+
options[:user] = config
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
opts.on("-G", "--group GROUP", "This flag is for having merb run as a group other than the one currently logged in. Note: if you set this you must also provide a --user option for it to take effect.") do |config|
|
|
82
|
+
options[:group] = config
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
opts.on("-f", "--config-file FILENAME", "This flag is for adding extra config files for things like the upload progress module.") do |config|
|
|
86
|
+
options[:config] = config
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
opts.on("-d", "--daemonize", "This will run a single merb in the background.") do |config|
|
|
90
|
+
options[:daemonize] = true
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
opts.on("-c", "--cluster-nodes NUM_MERBS", "Number of merb daemons to run.") do |nodes|
|
|
94
|
+
options[:cluster] = nodes
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
opts.on("-p", "--port PORTNUM", "Port to run merb on, defaults to 4000.") do |port|
|
|
98
|
+
options[:port] = port
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
opts.on("-h", "--host HOSTNAME", "Host to bind to (default is all IP's).") do |host|
|
|
102
|
+
options[:host] = host
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
opts.on("-m", "--merb-root Merb.root", "The path to the Merb.root for the app you want to run (default is current working dir).") do |Merb.root|
|
|
106
|
+
options[:merb_root] = File.expand_path(Merb.root)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
opts.on("-i", "--irb-console", "This flag will start merb in irb console mode. All your models and other classes will be available for you in an irb session.") do |console|
|
|
110
|
+
::Merb::BootLoader.initialize_merb
|
|
111
|
+
_merb = Class.new do
|
|
112
|
+
class << self
|
|
113
|
+
include Merb::GeneralControllerMixin
|
|
114
|
+
def params() {} end
|
|
115
|
+
end
|
|
116
|
+
def self.show_routes(all_opts = false)
|
|
117
|
+
seen = []
|
|
118
|
+
unless Merb::Router.named_routes.empty?
|
|
119
|
+
puts "Named Routes"
|
|
120
|
+
Merb::Router.named_routes.each do |name,route|
|
|
121
|
+
puts " #{name}: #{route}"
|
|
122
|
+
seen << route
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
puts "Anonymous Routes"
|
|
126
|
+
(Merb::Router.routes - seen).each do |route|
|
|
127
|
+
puts " #{route}"
|
|
128
|
+
end
|
|
129
|
+
nil
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
Object.send(:define_method, :merb) {
|
|
134
|
+
_merb
|
|
135
|
+
}
|
|
136
|
+
ARGV.clear # Avoid passing args to IRB
|
|
137
|
+
require 'irb'
|
|
138
|
+
require 'irb/completion'
|
|
139
|
+
def exit
|
|
140
|
+
exit!
|
|
141
|
+
end
|
|
142
|
+
if File.exists? ".irbrc"
|
|
143
|
+
ENV['IRBRC'] = ".irbrc"
|
|
144
|
+
end
|
|
145
|
+
IRB.start
|
|
146
|
+
exit!
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
opts.on("-s", "--start-drb PORTNUM", "This is the port number to run the drb daemon on for sessions and upload progress monitoring.") do |drb_port|
|
|
150
|
+
puts "Starting merb drb server on port: #{Merb::Config[:drb_server_port]}"
|
|
151
|
+
Merb::Server.start(drb_port, :drbserver_start)
|
|
152
|
+
exit if Merb::Config[:only_drb]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
opts.on("-l", "--log-level LEVEL", "Log levels can be set to any of these options: DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN") do |loglevel|
|
|
156
|
+
options[:log_level] = loglevel
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
opts.on("-e", "--environment STRING", "Run merb in the correct mode(development, production, testing)") do |env|
|
|
160
|
+
options[:environment] ||= env
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
opts.on("-r", "--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]",
|
|
164
|
+
"Command-line option to run scripts and/or code in the merb app.") do |code_or_file|
|
|
165
|
+
::Merb::BootLoader.initialize_merb
|
|
166
|
+
if File.exists?(code_or_file)
|
|
167
|
+
eval(File.read(code_or_file))
|
|
168
|
+
else
|
|
169
|
+
eval(code_or_file)
|
|
170
|
+
end
|
|
171
|
+
exit!
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
opts.on("-P","--generate-plugin PATH", "Generate a fresh merb plugin at PATH.") do |path|
|
|
175
|
+
require 'merb/generators/merb_plugin'
|
|
176
|
+
::Merb::PluginGenerator.run path || Dir.pwd
|
|
177
|
+
exit
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
opts.on("-K", "--graceful PORT or all", "Gracefully kill one merb proceses by port number. Use merb -K all to gracefully kill all merbs.") do |ports|
|
|
181
|
+
@configuration = defaults.merge(options)
|
|
182
|
+
Merb::Server.kill(ports, 1)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
opts.on("-k", "--kill PORT or all", "Kill one merb proceses by port number. Use merb -k all to kill all merbs.") do |ports|
|
|
186
|
+
@configuration = defaults.merge(options)
|
|
187
|
+
Merb::Server.kill(ports, 9)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
opts.on("-M", "--merb-config FILENAME", "This flag is for explicitly declaring the merb app's config file.") do |config|
|
|
191
|
+
options[:merb_config] = config
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
opts.on("-w", "--webrick", "Run merb using Webrick Rack Adapter instead of mongrel.") do |webport|
|
|
195
|
+
puts "Starting merb webrick server on port: #{Merb::Config[:port]}"
|
|
196
|
+
trap('TERM') { exit }
|
|
197
|
+
Merb::Server.webrick_start(Merb::Config[:port])
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
opts.on("-F", "--fastcgi", "Run merb using FastCGI Rack Adapter instead of mongrel.") do
|
|
201
|
+
trap('TERM') { exit }
|
|
202
|
+
Merb::Server.fastcgi_start
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
opts.on("-X", "--mutex on/off", "This flag is for turning the mutex lock on and off.") do |mutex|
|
|
206
|
+
if mutex == 'off'
|
|
207
|
+
options[:use_mutex] = false
|
|
208
|
+
else
|
|
209
|
+
options[:use_mutex] = true
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
opts.on("-D", "--debugger", "Run merb using rDebug.") do
|
|
214
|
+
begin
|
|
215
|
+
require 'ruby-debug'
|
|
216
|
+
Debugger.start
|
|
217
|
+
Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
|
|
218
|
+
puts "Debugger enabled"
|
|
219
|
+
rescue LoadError
|
|
220
|
+
puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
|
|
221
|
+
exit
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
opts.on("-?", "-H", "--help", "Show this help message") do
|
|
226
|
+
puts opts
|
|
227
|
+
exit
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# If we run merb with no arguments and we are not inside a merb root
|
|
232
|
+
# show the help message
|
|
233
|
+
if !defined?(Merb.framework_root) && (argv.size == 0) && !File.exists?("#{options[:merb_root] || Merb::Config.defaults[:merb_root]}/config/merb_init.rb")
|
|
234
|
+
puts "You are not in the root of a merb application...\n"
|
|
235
|
+
puts opts
|
|
236
|
+
exit
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Parse what we have on the command line
|
|
240
|
+
opts.parse!(argv)
|
|
241
|
+
|
|
242
|
+
# merb <argument> is same as merb -g <argument>
|
|
243
|
+
if argv.size == 1
|
|
244
|
+
require 'merb/generators/merb_app/merb_app'
|
|
245
|
+
::Merb::AppGenerator.run File.expand_path(argv.last)
|
|
246
|
+
exit!
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Load up the configuration from file, but keep the command line
|
|
250
|
+
# options that may have been chosen. Also, pass-through if we have
|
|
251
|
+
# a new merb_config path.
|
|
252
|
+
options = Merb::Config.setup(options[:merb_config]).merge(options)
|
|
253
|
+
|
|
254
|
+
# Finally, if all else fails... set the environment to 'development'
|
|
255
|
+
options[:environment] ||= 'development'
|
|
256
|
+
|
|
257
|
+
environment_merb_yml = "#{options[:merb_root]}/config/environments/#{options[:environment]}.yml"
|
|
258
|
+
|
|
259
|
+
@configuration = Merb::Config.apply_configuration_from_file options, environment_merb_yml
|
|
260
|
+
|
|
261
|
+
case Merb::Config[:environment].to_s
|
|
262
|
+
when 'production'
|
|
263
|
+
Merb::Config[:reloader] = Merb::Config.fetch(:reloader, false)
|
|
264
|
+
Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, false)
|
|
265
|
+
Merb::Config[:cache_templates] = true
|
|
266
|
+
else
|
|
267
|
+
Merb::Config[:reloader] = Merb::Config.fetch(:reloader, true)
|
|
268
|
+
Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, true)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
Merb::Config[:reloader_time] ||= 0.5 if Merb::Config[:reloader] == true
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
if Merb::Config[:reloader]
|
|
275
|
+
Thread.abort_on_exception = true
|
|
276
|
+
Thread.new do
|
|
277
|
+
loop do
|
|
278
|
+
sleep( Merb::Config[:reloader_time] )
|
|
279
|
+
::Merb::BootLoader.reload if ::Merb::BootLoader.app_loaded?
|
|
280
|
+
end
|
|
281
|
+
Thread.exit
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
@configuration
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
end # class << self
|
|
288
|
+
end # Config
|
|
289
|
+
|
|
290
|
+
end
|