merb 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/README +21 -14
  2. data/Rakefile +157 -108
  3. data/SVN_REVISION +1 -0
  4. data/app_generators/merb/templates/Rakefile +20 -4
  5. data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +1 -1
  6. data/app_generators/merb/templates/config/boot.rb +1 -1
  7. data/app_generators/merb/templates/config/dependencies.rb +3 -3
  8. data/app_generators/merb/templates/config/merb.yml +5 -0
  9. data/app_generators/merb/templates/config/merb_init.rb +3 -3
  10. data/app_generators/merb/templates/script/destroy +3 -0
  11. data/app_generators/merb/templates/script/generate +1 -1
  12. data/app_generators/merb/templates/spec/spec_helper.rb +2 -2
  13. data/app_generators/merb/templates/test/test_helper.rb +1 -1
  14. data/app_generators/merb_plugin/merb_plugin_generator.rb +4 -0
  15. data/bin/merb +1 -3
  16. data/lib/merb.rb +144 -76
  17. data/lib/merb/abstract_controller.rb +6 -5
  18. data/lib/merb/assets.rb +119 -0
  19. data/lib/merb/boot_loader.rb +217 -0
  20. data/lib/merb/caching.rb +1 -1
  21. data/lib/merb/caching/action_cache.rb +1 -1
  22. data/lib/merb/caching/fragment_cache.rb +1 -1
  23. data/lib/merb/caching/store/file_cache.rb +1 -1
  24. data/lib/merb/config.rb +290 -0
  25. data/lib/merb/controller.rb +5 -5
  26. data/lib/merb/core_ext/get_args.rb +1 -0
  27. data/lib/merb/core_ext/hash.rb +182 -169
  28. data/lib/merb/core_ext/kernel.rb +57 -26
  29. data/lib/merb/dispatcher.rb +6 -6
  30. data/lib/merb/drb_server.rb +1 -1
  31. data/lib/merb/generators/merb_generator_helpers.rb +7 -6
  32. data/lib/merb/logger.rb +1 -1
  33. data/lib/merb/mail_controller.rb +3 -4
  34. data/lib/merb/mailer.rb +2 -2
  35. data/lib/merb/mixins/basic_authentication.rb +2 -2
  36. data/lib/merb/mixins/controller.rb +1 -1
  37. data/lib/merb/mixins/general_controller.rb +13 -20
  38. data/lib/merb/mixins/inline_partial.rb +32 -0
  39. data/lib/merb/mixins/render.rb +3 -3
  40. data/lib/merb/mixins/responder.rb +1 -1
  41. data/lib/merb/mixins/view_context.rb +159 -33
  42. data/lib/merb/mongrel_handler.rb +9 -9
  43. data/lib/merb/plugins.rb +1 -1
  44. data/lib/merb/request.rb +25 -1
  45. data/lib/merb/router.rb +264 -226
  46. data/lib/merb/server.rb +66 -560
  47. data/lib/merb/session/cookie_store.rb +14 -13
  48. data/lib/merb/session/mem_cache_session.rb +20 -10
  49. data/lib/merb/session/memory_session.rb +21 -11
  50. data/lib/merb/template.rb +2 -2
  51. data/lib/merb/template/erubis.rb +3 -33
  52. data/lib/merb/template/haml.rb +8 -3
  53. data/lib/merb/test/fake_request.rb +8 -3
  54. data/lib/merb/test/helper.rb +66 -22
  55. data/lib/merb/test/rspec.rb +9 -155
  56. data/lib/merb/test/rspec_matchers/controller_matchers.rb +117 -0
  57. data/lib/merb/test/rspec_matchers/markup_matchers.rb +98 -0
  58. data/lib/merb/upload_handler.rb +2 -1
  59. data/lib/merb/version.rb +38 -3
  60. data/lib/merb/view_context.rb +1 -2
  61. data/lib/tasks/merb.rake +11 -11
  62. data/merb_generators/part_controller/USAGE +5 -0
  63. data/merb_generators/part_controller/part_controller_generator.rb +27 -0
  64. data/merb_generators/part_controller/templates/controller.rb +8 -0
  65. data/merb_generators/part_controller/templates/helper.rb +5 -0
  66. data/merb_generators/part_controller/templates/index.html.erb +3 -0
  67. data/rspec_generators/merb_controller_test/merb_controller_test_generator.rb +1 -1
  68. data/script/destroy +14 -0
  69. data/script/generate +14 -0
  70. data/spec/fixtures/controllers/dispatch_spec_controllers.rb +9 -1
  71. data/spec/fixtures/controllers/render_spec_controllers.rb +5 -5
  72. data/spec/fixtures/models/router_spec_models.rb +10 -0
  73. data/spec/merb/abstract_controller_spec.rb +2 -2
  74. data/spec/merb/assets_spec.rb +207 -0
  75. data/spec/merb/caching_spec.rb +2 -2
  76. data/spec/merb/controller_spec.rb +7 -2
  77. data/spec/merb/cookie_store_spec.rb +1 -1
  78. data/spec/merb/core_ext/class_spec.rb +97 -0
  79. data/spec/merb/core_ext/enumerable_spec.rb +27 -0
  80. data/spec/merb/core_ext/hash_spec.rb +251 -0
  81. data/spec/merb/core_ext/inflector_spec.rb +34 -0
  82. data/spec/merb/core_ext/kernel_spec.rb +25 -0
  83. data/spec/merb/core_ext/numeric_spec.rb +26 -0
  84. data/spec/merb/core_ext/object_spec.rb +47 -0
  85. data/spec/merb/core_ext/string_spec.rb +22 -0
  86. data/spec/merb/core_ext/symbol_spec.rb +7 -0
  87. data/spec/merb/dependency_spec.rb +22 -0
  88. data/spec/merb/dispatch_spec.rb +23 -12
  89. data/spec/merb/fake_request_spec.rb +8 -0
  90. data/spec/merb/generator_spec.rb +140 -21
  91. data/spec/merb/handler_spec.rb +5 -5
  92. data/spec/merb/mail_controller_spec.rb +3 -3
  93. data/spec/merb/render_spec.rb +1 -1
  94. data/spec/merb/responder_spec.rb +3 -3
  95. data/spec/merb/router_spec.rb +260 -191
  96. data/spec/merb/server_spec.rb +5 -5
  97. data/spec/merb/upload_handler_spec.rb +7 -0
  98. data/spec/merb/version_spec.rb +33 -0
  99. data/spec/merb/view_context_spec.rb +217 -59
  100. data/spec/spec_generator_helper.rb +15 -0
  101. data/spec/spec_helper.rb +5 -3
  102. data/spec/spec_helpers/url_shared_behaviour.rb +5 -7
  103. metadata +32 -7
  104. data/lib/merb/caching/store/memcache.rb +0 -20
  105. data/lib/merb/mixins/form_control.rb +0 -332
  106. data/lib/patch +0 -69
  107. data/spec/merb/core_ext_spec.rb +0 -464
  108. data/spec/merb/form_control_mixin_spec.rb +0 -431
@@ -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
@@ -1,4 +1,4 @@
1
- corelib = __DIR__+'/merb/caching'
1
+ corelib = Merb.framework_root + '/merb/caching'
2
2
 
3
3
  %w[ action_cache
4
4
  fragment_cache
@@ -62,7 +62,7 @@ module Merb
62
62
  private
63
63
 
64
64
  def _caching_enabled?
65
- ::Merb::Server.config[:cache_templates]
65
+ ::Merb::Config[:cache_templates]
66
66
  end
67
67
 
68
68
  def _get_action_fragment
@@ -25,7 +25,7 @@ module Merb
25
25
  end
26
26
 
27
27
  def determine_cache_store
28
- if ::Merb::Server.config[:cache_store].to_s == "file"
28
+ if ::Merb::Config[:cache_store].to_s == "file"
29
29
  require 'merb/caching/store/file_cache'
30
30
  ::Merb::Caching::Store::FileCache.new
31
31
  else
@@ -9,7 +9,7 @@ module Merb
9
9
  class FileCache
10
10
 
11
11
  def initialize(name = "cache", keepalive = nil)
12
- @path = File.join(MERB_ROOT, name)
12
+ @path = File.join(Merb.root, name)
13
13
  @keepalive = keepalive
14
14
  end
15
15
 
@@ -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