merb 0.3.4 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/README +206 -197
  2. data/Rakefile +12 -21
  3. data/bin/merb +1 -1
  4. data/examples/skeleton/Rakefile +6 -20
  5. data/examples/skeleton/dist/app/mailers/layout/application.erb +1 -0
  6. data/examples/skeleton/dist/conf/database.yml +23 -0
  7. data/examples/skeleton/dist/conf/environments/development.rb +1 -0
  8. data/examples/skeleton/dist/conf/environments/production.rb +1 -0
  9. data/examples/skeleton/dist/conf/environments/test.rb +1 -0
  10. data/examples/skeleton/dist/conf/merb.yml +32 -28
  11. data/examples/skeleton/dist/conf/merb_init.rb +16 -13
  12. data/examples/skeleton/dist/conf/router.rb +9 -9
  13. data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +2 -2
  14. data/lib/merb.rb +23 -18
  15. data/lib/merb/caching/fragment_cache.rb +3 -7
  16. data/lib/merb/caching/store/memcache.rb +20 -0
  17. data/lib/merb/core_ext/merb_array.rb +0 -0
  18. data/lib/merb/core_ext/merb_class.rb +44 -4
  19. data/lib/merb/core_ext/merb_enumerable.rb +43 -1
  20. data/lib/merb/core_ext/merb_hash.rb +200 -122
  21. data/lib/merb/core_ext/merb_kernel.rb +2 -0
  22. data/lib/merb/core_ext/merb_module.rb +41 -0
  23. data/lib/merb/core_ext/merb_numeric.rb +57 -5
  24. data/lib/merb/core_ext/merb_object.rb +172 -6
  25. data/lib/merb/generators/merb_app/merb_app.rb +15 -9
  26. data/lib/merb/merb_abstract_controller.rb +193 -0
  27. data/lib/merb/merb_constants.rb +26 -1
  28. data/lib/merb/merb_controller.rb +143 -234
  29. data/lib/merb/merb_dispatcher.rb +28 -20
  30. data/lib/merb/merb_drb_server.rb +2 -3
  31. data/lib/merb/merb_exceptions.rb +194 -49
  32. data/lib/merb/merb_handler.rb +34 -26
  33. data/lib/merb/merb_mail_controller.rb +200 -0
  34. data/lib/merb/merb_mailer.rb +33 -13
  35. data/lib/merb/merb_part_controller.rb +42 -0
  36. data/lib/merb/merb_plugins.rb +293 -0
  37. data/lib/merb/merb_request.rb +6 -4
  38. data/lib/merb/merb_router.rb +99 -65
  39. data/lib/merb/merb_server.rb +65 -21
  40. data/lib/merb/merb_upload_handler.rb +2 -1
  41. data/lib/merb/merb_view_context.rb +36 -15
  42. data/lib/merb/mixins/basic_authentication_mixin.rb +5 -5
  43. data/lib/merb/mixins/controller_mixin.rb +67 -28
  44. data/lib/merb/mixins/erubis_capture_mixin.rb +1 -8
  45. data/lib/merb/mixins/form_control_mixin.rb +280 -42
  46. data/lib/merb/mixins/render_mixin.rb +127 -45
  47. data/lib/merb/mixins/responder_mixin.rb +5 -7
  48. data/lib/merb/mixins/view_context_mixin.rb +260 -94
  49. data/lib/merb/session.rb +23 -0
  50. data/lib/merb/session/merb_ar_session.rb +28 -16
  51. data/lib/merb/session/merb_mem_cache_session.rb +108 -0
  52. data/lib/merb/session/merb_memory_session.rb +65 -20
  53. data/lib/merb/template/erubis.rb +22 -13
  54. data/lib/merb/template/haml.rb +5 -16
  55. data/lib/merb/template/markaby.rb +5 -3
  56. data/lib/merb/template/xml_builder.rb +17 -5
  57. data/lib/merb/test/merb_fake_request.rb +63 -0
  58. data/lib/merb/test/merb_multipart.rb +58 -0
  59. data/lib/tasks/db.rake +2 -0
  60. data/lib/tasks/merb.rake +20 -8
  61. metadata +24 -25
  62. data/examples/skeleton.tar +0 -0
@@ -0,0 +1,200 @@
1
+ require File.dirname(__FILE__)+'/merb_mailer'
2
+
3
+ module Merb
4
+ class MailController < AbstractController
5
+
6
+ self._template_root = File.expand_path(DIST_ROOT / "app/mailers/views")
7
+ class_inheritable_accessor :_mailer_klass
8
+ self._mailer_klass = Merb::Mailer
9
+
10
+ attr_accessor :params, :mailer, :mail
11
+ attr_reader :session, :base_controller
12
+
13
+ # You can initialize a MailController with a series of parameters that can
14
+ # be used by methods in the class. You can also pass in a controller
15
+ # object, which will be available to the MailController methods as
16
+ # base_controller.
17
+ def initialize(params = {}, controller = nil)
18
+ @params = params
19
+ @base_controller = controller
20
+ @session = (controller && controller.session) || {}
21
+ end
22
+
23
+ def filters_halted
24
+ end
25
+
26
+ # Allows you to render various types of things into the text and HTML parts
27
+ # of an email If you include just text, the email will be sent as
28
+ # plain-text. If you include HTML, the email will be sent as a multi-part
29
+ # email.
30
+ #
31
+ # There are a lot of ways to use render_mail, but it works similarly to the
32
+ # default Merb render method.
33
+ #
34
+ # First of all, you'll need to store email files in your
35
+ # dist/app/mailers/views directory. They should be under a directory that
36
+ # matches the name of your mailer (e.g. TestMailer's views would be stored
37
+ # under test_mailer).
38
+ #
39
+ # The files themselves should be named action_name.mime_type.extension. For
40
+ # example, a herb template that should be the HTML part of the email, and
41
+ # rendered from the "foo" action would be named foo.html.herb.
42
+ #
43
+ # The only mime-types currently supported are "html" and "text", which
44
+ # correspond to text/html and text/plain respectively. All template systems
45
+ # supported by your app are available to MailController, and the extensions
46
+ # are the same as they are throughout the rest of Merb.
47
+ #
48
+ # render_mail can take any of the following option patterns:
49
+ #
50
+ # render_mail
51
+ #
52
+ # will attempt to render the current action. If the current action is
53
+ # "foo", this is identical to render_mail :foo.
54
+ #
55
+ # render_mail :foo
56
+ #
57
+ # checks for foo.html.ext and foo.text.ext and applies them as appropriate.
58
+ #
59
+ # render_mail :action => {:html => :foo, :text => :bar}
60
+ #
61
+ # checks for foo.html.ext and bar.text.ext in the view directory of the
62
+ # current controller and adds them to the mail object if found
63
+ #
64
+ # render_mail :template => {:html => "foo/bar", :text => "foo/baz"}
65
+ #
66
+ # checks for bar.html.ext and baz.text.ext in the foo directory and adds
67
+ # them to the mail object if found.
68
+ #
69
+ # render_mail :html => :foo, :text => :bar
70
+ #
71
+ # the same as render_mail :action => {html => :foo, :text => :bar }
72
+ #
73
+ # render_mail :html => "FOO", :text => "BAR"
74
+ #
75
+ # adds the text "FOO" as the html part of the email and the text "BAR" as
76
+ # the text part of the email. The difference between the last two examples
77
+ # is that symbols represent actions to render, while string represent the
78
+ # literal text to render. Note that you can use regular render methods
79
+ # instead of literal strings here, like:
80
+ #
81
+ # render_mail :html => render(:action => :foo)
82
+ #
83
+ # but you're probably better off just using render_mail :action at that
84
+ # point.
85
+ #
86
+ # You can also mix and match:
87
+ #
88
+ # render_mail :action => {:html => :foo}, :text => "BAR"
89
+ #
90
+ # which would be identical to:
91
+ #
92
+ # render_mail :html => :foo, :text => "BAR"
93
+ def render_mail(options = @method)
94
+ # If the options are not a hash, normalize to an action hash
95
+ options = {:action => {:html => options, :text => options}} if !options.is_a?(Hash)
96
+ # Normalize :html => :sym and :text => :sym to an action hash
97
+ options[:action] ||= {}
98
+ options[:action][:html] = options.delete(:html) if options[:html].is_a?(Symbol)
99
+ options[:action][:text] = options.delete(:text) if options[:text].is_a?(Symbol)
100
+ options.delete(:action) if options[:action].empty?
101
+
102
+ # set type to action or template, if the hash includes :action or :template
103
+ type = options.keys.find {|x| x.to_s =~ /(action|template)/}
104
+ # normalize :action/:template => ... to :action/template => {:html => ..., :text => ...}
105
+ options[type] = {:html => options[type], :text => options[type]} if type && !options[type].is_a?(Hash)
106
+ for mime_type in [:html, :text]
107
+ # if action or template, get the template to render. If not, get the string to render. If none
108
+ # are available, use the default params[:action]
109
+ renderer = (type && options[type][mime_type] ? options[type][mime_type] : options[mime_type]).to_s
110
+ # action or template
111
+ if type && options[type][mime_type]
112
+ # build the template to render
113
+ mime_renderer = renderer + ".#{mime_type}" if !(renderer =~ /.#{mime_type}$/)
114
+ if find_template(type => mime_renderer)
115
+ @mail.send((mime_type == :html ? "rawhtml=" : "text="),
116
+ render(type => mime_renderer, :layout => mimed_layout(mime_type, options[:layout])))
117
+ elsif find_template(type => renderer) && mime_type == :text
118
+ @mail.text = render(type => renderer, :layout => options[:layout])
119
+ end
120
+ # string to render
121
+ else
122
+ @mail.send((mime_type == :html ? "rawhtml=" : "text="), renderer)
123
+ end
124
+ end
125
+ end
126
+
127
+ # Attaches a file or multiple files to an email. You call this from a
128
+ # method in your MailController (including a before filter).
129
+ #
130
+ # attach File.open("foo")
131
+ # attach [File.open("foo"), File.open("bar")]
132
+ #
133
+ # You can also include the filename, mime-type, or headers in the
134
+ # subsequent parameters.
135
+ #
136
+ # If you are passing an array of files, you should use an array of the
137
+ # allowed parameters:
138
+ #
139
+ # attach [[File.open("foo"), "bar", "text/html"], [File.open("baz"),
140
+ # "bat", "text/css"]
141
+ #
142
+ # which would attach two files ("foo" and "baz" in the filesystem) as
143
+ # "bar" and "bat" respectively. It would also set the mime-type as
144
+ # "text/html" and "text/css" respectively.
145
+ def attach( file_or_files, filename = file_or_files.is_a?(File) ? File.basename(file_or_files.path) : nil,
146
+ type = nil, headers = nil)
147
+ @mail.attach(file_or_files, filename, type, headers)
148
+ end
149
+
150
+ # take a method name to dispatch to and mail parameters for the MailFactory object.
151
+ #
152
+ # Available mail parameters include:
153
+ # to
154
+ # from
155
+ # replyto
156
+ # subject
157
+ # body
158
+ # cc
159
+ #
160
+ # Other parameters passed in will be interpreted as email headers, with _'s converted
161
+ # to -'s.
162
+ def dispatch_and_deliver(method, mail_params)
163
+ @mailer = self.class._mailer_klass.new(mail_params)
164
+ @mail = @mailer.mail
165
+ @method = method
166
+ params[:action] = method
167
+ body = dispatch method
168
+ if !@mail.html.blank? || !@mail.text.blank?
169
+ @mailer.deliver!
170
+ MERB_LOGGER.info "#{method} sent to #{@mail.to} about #{@mail.subject}"
171
+ else
172
+ MERB_LOGGER.info "#{method} was not sent because nothing was rendered for it"
173
+ end
174
+ end
175
+
176
+ # A convenience method that creates a blank copy of the MailController and runs
177
+ # dispatch_and_deliver on it.
178
+ def self.dispatch_and_deliver(method, mail_params)
179
+ new({}).dispatch_and_deliver method, mail_params
180
+ end
181
+
182
+ private
183
+ def mimed_layout(mime_type, layout = nil)
184
+ if layout && layout != :application
185
+ layout_choice = find_template(:layout => layout + ".#{mime_type}") ||
186
+ find_template(:layout => layout)
187
+ elsif name = find_template(:layout => self.class.name.snake_case + ".#{mime_type}")
188
+ layout_choice = name
189
+ elsif name = find_template(:layout => self.class.name.snake_case)
190
+ layout_choice = name
191
+ elsif name = find_template(:layout => "application.#{mime_type}")
192
+ layout_choice = name
193
+ else
194
+ layout_choice = find_template(:layout => "application")
195
+ end
196
+ layout_choice = layout_choice.split("/").last.split(".")[0...-1].join(".")
197
+ end
198
+
199
+ end
200
+ end
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  begin
4
2
  require 'mailfactory'
5
3
  require 'net/smtp'
@@ -8,10 +6,16 @@ rescue LoadError
8
6
  MERB_LOGGER.warn "You need to install the mailfactory gem to use Merb::Mailer"
9
7
  end
10
8
 
9
+ class MailFactory
10
+ attr_reader :html, :text
11
+ end
12
+
11
13
  module Merb
12
14
  class Mailer
13
15
 
14
- class_inheritable_accessor :config
16
+ class_inheritable_accessor :config, :delivery_method, :deliveries
17
+ attr_accessor :mail
18
+ self.deliveries = []
15
19
 
16
20
  def sendmail
17
21
  sendmail = IO.popen("sendmail #{@mail.to}", 'w+')
@@ -22,28 +26,40 @@ module Merb
22
26
  # :plain, :login, or :cram_md5
23
27
  def net_smtp
24
28
  Net::SMTP.start(config[:host], config[:port].to_i, config[:domain],
25
- config[:user], config[:pass], (config[:auth].intern||:plain)) { |smtp|
26
- smtp.send_message(@mail.to_s, @mail.from, @mail.to)
27
- }
29
+ config[:user], config[:pass], (config[:auth].to_sym||:plain)) { |smtp|
30
+ smtp.send_message(@mail.to_s, @mail.from.first, @mail.to)
31
+ }
32
+ end
33
+
34
+ def test_send
35
+ deliveries << @mail
28
36
  end
29
37
 
30
38
  def deliver!
31
- config == :sendmail ? sendmail : net_smtp
39
+ send(delivery_method || :net_smtp)
40
+ end
41
+
42
+ def attach(file_or_files, filename = file_or_files.is_a?(File) ? File.basename(file_or_files.path) : nil,
43
+ type = nil, headers = nil)
44
+ if file_or_files.is_a?(Array)
45
+ file_or_files.each {|k,v| @mail.add_attachment_as k, *v}
46
+ else
47
+ raise ArgumentError, "You did not pass in a file. Instead, you sent a #{file_or_files.class}" if !file_or_files.is_a?(File)
48
+ @mail.add_attachment_as(file_or_files, filename, type, headers)
49
+ end
32
50
  end
33
51
 
34
52
  def initialize(o={})
35
53
  self.config = :sendmail if config.nil?
54
+ o[:rawhtml] = o.delete(:html)
36
55
  @mail = returning MailFactory.new() do |m|
37
- m.to = o[:to]
38
- m.from = o[:from]
39
- m.subject = o[:subject]
40
- m.text = o[:body]
41
- m.html = o[:html] if o[:html]
56
+ o.each { |k,v| m.send "#{k}=", v }
42
57
  end
43
- end
58
+ end
44
59
 
45
60
  end
46
61
  end
62
+
47
63
  =begin
48
64
 
49
65
 
@@ -54,10 +70,14 @@ Merb::Mailer.config = {
54
70
  :pass=>'pass',
55
71
  :auth=>:plain # :plain, :login, or :cram_md5, default :plain
56
72
  }
73
+
74
+ Merb::Mailer.delivery_method = :sendmail
75
+
57
76
  m = Merb::Mailer.new :to => 'foo@bar.com',
58
77
  :from => 'bar@foo.com',
59
78
  :subject => 'Welcome to whatever!',
60
79
  :body => partial(:sometemplate)
61
80
  m.deliver!
62
81
 
82
+
63
83
  =end
@@ -0,0 +1,42 @@
1
+ module Merb
2
+ class PartController < AbstractController
3
+ self._template_root = File.expand_path(DIST_ROOT / "app/parts/views")
4
+
5
+ def initialize(web_controller)
6
+ @_benchmarks = {}
7
+ @web_controller = web_controller
8
+ end
9
+
10
+ def dispatch(action=:to_s)
11
+ old_action = params[:action]
12
+ params[:action] = action
13
+ super(action)
14
+ params[:action] = old_action
15
+ @_body
16
+ end
17
+
18
+ def request
19
+ @web_controller.request
20
+ end
21
+
22
+ def params
23
+ @web_controller.params
24
+ end
25
+
26
+ def cookies
27
+ @web_controller.cookies
28
+ end
29
+
30
+ def headers
31
+ @web_controller.headers
32
+ end
33
+
34
+ def session
35
+ @web_controller.session
36
+ end
37
+
38
+ def response
39
+ @web_controller.response
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,293 @@
1
+ # TODO: Find Merb app root so we aren't wantonly copying Rakefiles
2
+ # TODO: Figure out a good way to handle the lack of install/uninstall script
3
+ # TODO: Minimum versions for local gems in install
4
+
5
+ require 'yaml'
6
+ require 'rubygems'
7
+
8
+ module Merb
9
+ class PluginManager
10
+
11
+ def self.gem_name
12
+ @@gem_name
13
+ end
14
+
15
+ def self.gem_spec
16
+ @@gem_spec
17
+ end
18
+
19
+ def self.options
20
+ @@options
21
+ end
22
+
23
+ def self.gem_name=(val)
24
+ @@gem_name = val
25
+ end
26
+
27
+ def self.gem_spec=(val)
28
+ @@gem_spec = val
29
+ end
30
+
31
+ def self.options=(val)
32
+ @@options = val
33
+ end
34
+
35
+
36
+ # Run a particular plugin action
37
+ def self.action(action, gem_name = nil, opts = [])
38
+ @@gem_name = gem_name
39
+ @@options = opts.flatten
40
+
41
+ case action
42
+ when 'install':
43
+ install
44
+ when 'uninstall':
45
+ uninstall
46
+ when 'remove':
47
+ remove_from_manifest
48
+ when 'list':
49
+ list
50
+ when 'help'
51
+ help
52
+ when 'migrate'
53
+ migrate
54
+ when 'update'
55
+ raise "This hasn't been implemented yet!"
56
+ when 'upgrade'
57
+ raise "This hasn't been implemented yet!"
58
+ else
59
+ raise "Invalid action: #{action}"
60
+ end
61
+ end
62
+
63
+ # Install a new plugin named +gem_name+.
64
+ def self.install
65
+ raise "Plugin already installed!" if plugin_installed?
66
+
67
+ self.gem_spec = find_locally or install_gem
68
+
69
+ run_install_script
70
+ copy_rakefile
71
+ add_to_manifest
72
+ rescue Exception => e
73
+ puts "\n!!! Plugin installation FAILED: #{e.message}\n\n"
74
+ end
75
+
76
+ # Check the manifest to see if the plugin is already installed in this application.
77
+ def self.plugin_installed?
78
+ with_manifest do |manifest|
79
+ return manifest.has_key?(gem_name)
80
+ end
81
+ rescue
82
+ false
83
+ end
84
+
85
+ # Search the local Rubygems installation for the gem.
86
+ def self.find_locally
87
+ all_installed_gems = Gem::SourceIndex.from_installed_gems
88
+ latest_gem_version = all_installed_gems.search(gem_name).sort_by {|x| x.version.version }.last
89
+ require 'pp'
90
+
91
+ if latest_gem_version
92
+ puts "Local gem found."
93
+ return latest_gem_version
94
+ else
95
+ puts "Can't find gem...installing."
96
+ return false
97
+ end
98
+ end
99
+
100
+ # Run the installation code in the gem.
101
+ def self.run_install_script
102
+ require "#{gem_name}/install"
103
+ end
104
+
105
+ # Copy the Rakefile from the gem to a directory in our application.
106
+ def self.copy_rakefile
107
+ path = gem_spec.loaded_from.split(/specifications/)[0] + "gems/#{gem_name}-#{gem_spec.version.version}/"
108
+
109
+ merb_rake_file = path + "Rakefile.merb"
110
+
111
+ require 'fileutils'
112
+ Dir.mkdir 'plugins' if !File.exists?('plugins/')
113
+ FileUtils.cp merb_rake_file, "plugins/#{gem_name}.rake"
114
+
115
+ File.open('Rakefile', 'a') do |rakefile|
116
+ rakefile.write("\nrequire 'plugins/#{gem_name}.rake\n")
117
+ end
118
+ end
119
+
120
+ def self.remove_rakefile
121
+ FileUtils.rm "plugins/#{gem_name}.rake"
122
+
123
+ File.open('Rakefile', 'r+') do |rakefile|
124
+ contents = rakefile.read
125
+ contents.gsub!("\nrequire 'plugins/#{gem_name}.rake\n", "")
126
+
127
+ rakefile.pos = 0
128
+ rakefile.write contents
129
+ rakefile.truncate rakefile.pos
130
+ end
131
+ end
132
+
133
+ # Do a gem installation since it wasn't found in the locally installed gems.
134
+ def self.install_gem
135
+ require 'rubygems/installer'
136
+
137
+ options = {
138
+ :domain => :both,
139
+ :generate_rdoc => true,
140
+ :generate_ri => true,
141
+ :force => false,
142
+ :test => false,
143
+ :wrappers => true,
144
+ :version => "> 0",
145
+ :install_dir => Gem.dir,
146
+ :security_policy => nil }
147
+
148
+
149
+
150
+ puts "Attempting local install..."
151
+ local_install = Gem::Installer.new(gem_name, options).install(options[:force], options[:install_dir]) rescue false
152
+
153
+ if !local_install
154
+ # Do remote installation
155
+ require 'rubygems/remote_installer'
156
+
157
+ Gem.configuration = ConfigStub.new
158
+
159
+ puts "Attempting remote installation..."
160
+ installer = Gem::RemoteInstaller.new(options)
161
+
162
+ @@options.each do |opt|
163
+ installer.sources << opt.gsub(/source=/, '') if opt.scan(/^source=/) != []
164
+ options[:version] = "> #{opt.gsub(/version=/, '')}" if opt.scan(/^version=/) != []
165
+ end
166
+
167
+ remote_install = installer.install(gem_name, options[:version], options[:force], options[:install_dir]) rescue false
168
+ end
169
+
170
+ if !remote_install && !local_install
171
+ raise "That plugin can't be found in locally or in a remote repository."
172
+ else
173
+ find_locally
174
+ end
175
+ end
176
+
177
+ # Method to open and load our manifest; yields the manifest data to whatever method calls it.
178
+ def self.with_manifest
179
+ File.open('plugins.manifest', 'r+') do |manifest_file|
180
+ manifest_data = YAML::load(manifest_file.read)
181
+ manifest_data = {} if !manifest_data.is_a?(Hash)
182
+
183
+ yield manifest_data
184
+
185
+ manifest_file.pos = 0
186
+ manifest_file.write manifest_data.to_yaml
187
+ manifest_file.truncate manifest_file.pos
188
+ end
189
+ end
190
+
191
+ # Write our newly installed plugin to the application's manifest.
192
+ def self.add_to_manifest
193
+ with_manifest do |manifest_data|
194
+ manifest_data[gem_name] = {'version' => gem_spec.version.version}
195
+ end
196
+ end
197
+
198
+ def self.uninstall
199
+ raise "That plugin isn't installed!" if !plugin_installed?
200
+
201
+ require "#{gem_name}/uninstall" rescue nil
202
+
203
+ remove_rakefile
204
+ remove_from_manifest
205
+ uninstall_gem
206
+ end
207
+
208
+ def self.remove_from_manifest
209
+ with_manifest do |manifest|
210
+ manifest.delete(gem_name)
211
+ end
212
+ end
213
+
214
+ def self.uninstall_gem
215
+ require 'rubygems/installer'
216
+ require 'rubygems/doc_manager'
217
+
218
+ options = {
219
+ :domain => :both,
220
+ :generate_rdoc => true,
221
+ :generate_ri => true,
222
+ :force => false,
223
+ :test => false,
224
+ :wrappers => true,
225
+ :version => "> 0",
226
+ :install_dir => Gem.dir,
227
+ :security_policy => nil }
228
+
229
+ Gem::Uninstaller.new(gem_name, options).uninstall
230
+ end
231
+
232
+ def self.list
233
+ with_manifest do |manifest_data|
234
+ manifest_data.each do |key, value|
235
+ gem_name = key
236
+ gem_spec = find_locally
237
+ puts "#{key} (#{gem_spec.version.version})"
238
+ puts "\t#{gem_spec.description}\n"
239
+ end
240
+ end
241
+ end
242
+
243
+ def self.migrate
244
+ raise "This hasn't been implemented yet!"
245
+ end
246
+
247
+ def self.help
248
+ puts """
249
+ Merb Plugins
250
+ The -P (--plugin) option fed to Merb allows you to manage plugins.
251
+
252
+ Commands
253
+ --------
254
+ install Installs a new plugin.
255
+ uninstall Uninstalls a plugin, removing its entry from the manifest and uninstalling the gem.
256
+ remove Uninstalls a plugin, removing its entry from the manifest but leaving the gem installed.
257
+ list Lists all installed plugins for this Merb application.
258
+ upgrade Upgrades a plugin to the specified version.
259
+ update Updates a plugin to the latest available version.
260
+ migrate Reads the manifest for this application, installing and updating gems as needed.
261
+ help Shows this help message.
262
+
263
+ """
264
+ end
265
+ end
266
+
267
+ # Class to stub out the verbose flag in Rubygems user interaction lib
268
+ class ConfigStub
269
+ attr_accessor :verbose
270
+
271
+ def initialize
272
+ @verbose = false
273
+ end
274
+ end
275
+ end
276
+
277
+ module Gem
278
+ # Fixes a bug in RubyGems where the SilentProgressReporter class is
279
+ # inaccurately named SilentReporter. Maybe one day we'll monkeypatch this for
280
+ # our own purposes.
281
+ class SilentProgressReporter
282
+ attr_reader :count
283
+
284
+ def initialize(out_stream, size, initial_message)
285
+ end
286
+
287
+ def updated(message)
288
+ end
289
+
290
+ def done
291
+ end
292
+ end
293
+ end