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