merb 0.3.4 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +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
|