merb 0.3.4 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README +206 -197
- data/Rakefile +12 -21
- data/bin/merb +1 -1
- data/examples/skeleton/Rakefile +6 -20
- data/examples/skeleton/dist/app/mailers/layout/application.erb +1 -0
- data/examples/skeleton/dist/conf/database.yml +23 -0
- data/examples/skeleton/dist/conf/environments/development.rb +1 -0
- data/examples/skeleton/dist/conf/environments/production.rb +1 -0
- data/examples/skeleton/dist/conf/environments/test.rb +1 -0
- data/examples/skeleton/dist/conf/merb.yml +32 -28
- data/examples/skeleton/dist/conf/merb_init.rb +16 -13
- data/examples/skeleton/dist/conf/router.rb +9 -9
- data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +2 -2
- data/lib/merb.rb +23 -18
- data/lib/merb/caching/fragment_cache.rb +3 -7
- data/lib/merb/caching/store/memcache.rb +20 -0
- data/lib/merb/core_ext/merb_array.rb +0 -0
- data/lib/merb/core_ext/merb_class.rb +44 -4
- data/lib/merb/core_ext/merb_enumerable.rb +43 -1
- data/lib/merb/core_ext/merb_hash.rb +200 -122
- data/lib/merb/core_ext/merb_kernel.rb +2 -0
- data/lib/merb/core_ext/merb_module.rb +41 -0
- data/lib/merb/core_ext/merb_numeric.rb +57 -5
- data/lib/merb/core_ext/merb_object.rb +172 -6
- data/lib/merb/generators/merb_app/merb_app.rb +15 -9
- data/lib/merb/merb_abstract_controller.rb +193 -0
- data/lib/merb/merb_constants.rb +26 -1
- data/lib/merb/merb_controller.rb +143 -234
- data/lib/merb/merb_dispatcher.rb +28 -20
- data/lib/merb/merb_drb_server.rb +2 -3
- data/lib/merb/merb_exceptions.rb +194 -49
- data/lib/merb/merb_handler.rb +34 -26
- data/lib/merb/merb_mail_controller.rb +200 -0
- data/lib/merb/merb_mailer.rb +33 -13
- data/lib/merb/merb_part_controller.rb +42 -0
- data/lib/merb/merb_plugins.rb +293 -0
- data/lib/merb/merb_request.rb +6 -4
- data/lib/merb/merb_router.rb +99 -65
- data/lib/merb/merb_server.rb +65 -21
- data/lib/merb/merb_upload_handler.rb +2 -1
- data/lib/merb/merb_view_context.rb +36 -15
- data/lib/merb/mixins/basic_authentication_mixin.rb +5 -5
- data/lib/merb/mixins/controller_mixin.rb +67 -28
- data/lib/merb/mixins/erubis_capture_mixin.rb +1 -8
- data/lib/merb/mixins/form_control_mixin.rb +280 -42
- data/lib/merb/mixins/render_mixin.rb +127 -45
- data/lib/merb/mixins/responder_mixin.rb +5 -7
- data/lib/merb/mixins/view_context_mixin.rb +260 -94
- data/lib/merb/session.rb +23 -0
- data/lib/merb/session/merb_ar_session.rb +28 -16
- data/lib/merb/session/merb_mem_cache_session.rb +108 -0
- data/lib/merb/session/merb_memory_session.rb +65 -20
- data/lib/merb/template/erubis.rb +22 -13
- data/lib/merb/template/haml.rb +5 -16
- data/lib/merb/template/markaby.rb +5 -3
- data/lib/merb/template/xml_builder.rb +17 -5
- data/lib/merb/test/merb_fake_request.rb +63 -0
- data/lib/merb/test/merb_multipart.rb +58 -0
- data/lib/tasks/db.rake +2 -0
- data/lib/tasks/merb.rake +20 -8
- metadata +24 -25
- 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
|
data/lib/merb/merb_mailer.rb
CHANGED
@@ -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].
|
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
|
-
|
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.
|
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
|