roda 1.1.0 → 1.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG +70 -0
- data/README.rdoc +261 -302
- data/Rakefile +1 -1
- data/doc/release_notes/1.2.0.txt +406 -0
- data/lib/roda.rb +206 -124
- data/lib/roda/plugins/all_verbs.rb +11 -10
- data/lib/roda/plugins/assets.rb +5 -5
- data/lib/roda/plugins/backtracking_array.rb +12 -5
- data/lib/roda/plugins/caching.rb +10 -8
- data/lib/roda/plugins/class_level_routing.rb +94 -0
- data/lib/roda/plugins/content_for.rb +6 -0
- data/lib/roda/plugins/default_headers.rb +4 -11
- data/lib/roda/plugins/delay_build.rb +42 -0
- data/lib/roda/plugins/delegate.rb +64 -0
- data/lib/roda/plugins/drop_body.rb +33 -0
- data/lib/roda/plugins/empty_root.rb +48 -0
- data/lib/roda/plugins/environments.rb +68 -0
- data/lib/roda/plugins/error_email.rb +1 -2
- data/lib/roda/plugins/error_handler.rb +1 -1
- data/lib/roda/plugins/halt.rb +7 -5
- data/lib/roda/plugins/head.rb +4 -2
- data/lib/roda/plugins/header_matchers.rb +17 -9
- data/lib/roda/plugins/hooks.rb +16 -32
- data/lib/roda/plugins/json.rb +4 -10
- data/lib/roda/plugins/mailer.rb +233 -0
- data/lib/roda/plugins/match_affix.rb +48 -0
- data/lib/roda/plugins/multi_route.rb +9 -11
- data/lib/roda/plugins/multi_run.rb +81 -0
- data/lib/roda/plugins/named_templates.rb +93 -0
- data/lib/roda/plugins/not_allowed.rb +43 -48
- data/lib/roda/plugins/path.rb +63 -2
- data/lib/roda/plugins/render.rb +79 -48
- data/lib/roda/plugins/render_each.rb +6 -0
- data/lib/roda/plugins/sinatra_helpers.rb +523 -0
- data/lib/roda/plugins/slash_path_empty.rb +25 -0
- data/lib/roda/plugins/static_path_info.rb +64 -0
- data/lib/roda/plugins/streaming.rb +1 -1
- data/lib/roda/plugins/view_subdirs.rb +12 -8
- data/lib/roda/version.rb +1 -1
- data/spec/integration_spec.rb +33 -0
- data/spec/plugin/backtracking_array_spec.rb +24 -18
- data/spec/plugin/class_level_routing_spec.rb +138 -0
- data/spec/plugin/delay_build_spec.rb +23 -0
- data/spec/plugin/delegate_spec.rb +20 -0
- data/spec/plugin/drop_body_spec.rb +20 -0
- data/spec/plugin/empty_root_spec.rb +14 -0
- data/spec/plugin/environments_spec.rb +31 -0
- data/spec/plugin/h_spec.rb +1 -3
- data/spec/plugin/header_matchers_spec.rb +14 -0
- data/spec/plugin/hooks_spec.rb +3 -5
- data/spec/plugin/mailer_spec.rb +191 -0
- data/spec/plugin/match_affix_spec.rb +22 -0
- data/spec/plugin/multi_run_spec.rb +31 -0
- data/spec/plugin/named_templates_spec.rb +65 -0
- data/spec/plugin/path_spec.rb +66 -2
- data/spec/plugin/render_spec.rb +46 -1
- data/spec/plugin/sinatra_helpers_spec.rb +534 -0
- data/spec/plugin/slash_path_empty_spec.rb +22 -0
- data/spec/plugin/static_path_info_spec.rb +50 -0
- data/spec/request_spec.rb +23 -0
- data/spec/response_spec.rb +12 -1
- metadata +48 -6
@@ -0,0 +1,68 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The environments plugin adds a environment class accessor to get
|
4
|
+
# the environment for the application, 3 predicate class methods
|
5
|
+
# to check for the current environment (development?, test? and
|
6
|
+
# production?), and a class configure method that takes environment(s)
|
7
|
+
# and yields to the block if the given environment(s) match the
|
8
|
+
# current environment.
|
9
|
+
#
|
10
|
+
# The default environment for the application is based on
|
11
|
+
# <tt>ENV['RACK_ENV']</tt>.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# class Roda
|
16
|
+
# plugin :environments
|
17
|
+
#
|
18
|
+
# environment # => :development
|
19
|
+
# development? # => true
|
20
|
+
# test? # => false
|
21
|
+
# production? # => false
|
22
|
+
#
|
23
|
+
# # Set the environment for the application
|
24
|
+
# self.environment = :test
|
25
|
+
# test? # => true
|
26
|
+
#
|
27
|
+
# configure do
|
28
|
+
# # called, as no environments given
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# configure :development, :production do
|
32
|
+
# # not called, as no environments match
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# configure :test do
|
36
|
+
# # called, as environment given matches current environment
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
module Environments
|
40
|
+
# Set the environment to use for the app. Default to ENV['RACK_ENV']
|
41
|
+
# if no environment is given. If ENV['RACK_ENV'] is not set and
|
42
|
+
# no environment is given, assume the development environment.
|
43
|
+
def self.configure(app, env=ENV["RACK_ENV"])
|
44
|
+
app.environment = (env || 'development').to_sym
|
45
|
+
end
|
46
|
+
|
47
|
+
module ClassMethods
|
48
|
+
# The current environment for the application, which should be stored
|
49
|
+
# as a symbol.
|
50
|
+
attr_accessor :environment
|
51
|
+
|
52
|
+
# If no environments are given or one of the given environments
|
53
|
+
# matches the current environment, yield the receiver to the block.
|
54
|
+
def configure(*envs)
|
55
|
+
if envs.empty? || envs.any?{|s| s == environment}
|
56
|
+
yield self
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
[:development, :test, :production].each do |env|
|
61
|
+
define_method("#{env}?"){environment == env}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
register_plugin(:environments, Environments)
|
67
|
+
end
|
68
|
+
end
|
@@ -41,7 +41,7 @@ class Roda
|
|
41
41
|
format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
|
42
42
|
|
43
43
|
message = <<END
|
44
|
-
Path: #{s.request.
|
44
|
+
Path: #{s.request.path}
|
45
45
|
|
46
46
|
Backtrace:
|
47
47
|
|
@@ -88,7 +88,6 @@ END
|
|
88
88
|
# the superclass.
|
89
89
|
def inherited(subclass)
|
90
90
|
super
|
91
|
-
subclass.opts[:error_email] = subclass.opts[:error_email].dup
|
92
91
|
subclass.opts[:error_email][:headers] = subclass.opts[:error_email][:headers].dup
|
93
92
|
end
|
94
93
|
end
|
data/lib/roda/plugins/halt.rb
CHANGED
@@ -53,12 +53,14 @@ class Roda
|
|
53
53
|
raise Roda::RodaError, "singular argument to #halt must be Integer, String, or Array"
|
54
54
|
end
|
55
55
|
when 2
|
56
|
-
|
57
|
-
|
56
|
+
resp = response
|
57
|
+
resp.status = res[0]
|
58
|
+
resp.write res[1]
|
58
59
|
when 3
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
resp = response
|
61
|
+
resp.status = res[0]
|
62
|
+
resp.headers.merge!(res[1])
|
63
|
+
resp.write res[2]
|
62
64
|
else
|
63
65
|
raise Roda::RodaError, "too many arguments given to #halt (accepts 0-3, received #{res.length})"
|
64
66
|
end
|
data/lib/roda/plugins/head.rb
CHANGED
@@ -23,13 +23,15 @@ class Roda
|
|
23
23
|
# HEAD requests for +/+, +/a+, and +/b+ will all return 200 status
|
24
24
|
# with an empty body.
|
25
25
|
module Head
|
26
|
+
EMPTY_ARRAY = [].freeze
|
27
|
+
|
26
28
|
module InstanceMethods
|
27
29
|
# Always use an empty response body for head requests, with a
|
28
30
|
# content length of 0.
|
29
31
|
def call(*)
|
30
32
|
res = super
|
31
|
-
if
|
32
|
-
res[2] =
|
33
|
+
if @_request.head?
|
34
|
+
res[2] = EMPTY_ARRAY
|
33
35
|
end
|
34
36
|
res
|
35
37
|
end
|
@@ -8,23 +8,23 @@ class Roda
|
|
8
8
|
# It adds a +:header+ matcher for matching on arbitrary headers, which matches
|
9
9
|
# if the header is present:
|
10
10
|
#
|
11
|
-
#
|
12
|
-
# r.on :header=>'X-App-Token' do
|
13
|
-
# end
|
11
|
+
# r.on :header=>'X-App-Token' do
|
14
12
|
# end
|
15
13
|
#
|
16
14
|
# It adds a +:host+ matcher for matching by the host of the request:
|
17
15
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
16
|
+
# r.on :host=>'foo.example.com' do
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# It adds a +:user_agent+ matcher for matching on a user agent patterns, which
|
20
|
+
# yields the regexp captures to the block:
|
21
|
+
#
|
22
|
+
# r.on :user_agent=>/Chrome\/([.\d]+)/ do |chrome_version|
|
21
23
|
# end
|
22
24
|
#
|
23
25
|
# It adds an +:accept+ matcher for matching based on the Accept header:
|
24
26
|
#
|
25
|
-
#
|
26
|
-
# r.on :accept=>'text/csv' do
|
27
|
-
# end
|
27
|
+
# r.on :accept=>'text/csv' do
|
28
28
|
# end
|
29
29
|
#
|
30
30
|
# Note that the accept matcher is very simple and cannot handle wildcards,
|
@@ -49,6 +49,14 @@ class Roda
|
|
49
49
|
def match_host(hostname)
|
50
50
|
hostname === host
|
51
51
|
end
|
52
|
+
|
53
|
+
# Match the submitted user agent to the given pattern, capturing any
|
54
|
+
# regexp match groups.
|
55
|
+
def match_user_agent(pattern)
|
56
|
+
if (user_agent = @env["HTTP_USER_AGENT"]) && user_agent.to_s =~ pattern
|
57
|
+
@captures.concat($~[1..-1])
|
58
|
+
end
|
59
|
+
end
|
52
60
|
end
|
53
61
|
end
|
54
62
|
|
data/lib/roda/plugins/hooks.rb
CHANGED
@@ -30,10 +30,8 @@ class Roda
|
|
30
30
|
# handle cases where before hooks are added after the route block.
|
31
31
|
module Hooks
|
32
32
|
def self.configure(app)
|
33
|
-
app.
|
34
|
-
|
35
|
-
@before ||= nil
|
36
|
-
end
|
33
|
+
app.opts[:before_hook] ||= nil
|
34
|
+
app.opts[:after_hook] ||= nil
|
37
35
|
end
|
38
36
|
|
39
37
|
module ClassMethods
|
@@ -42,17 +40,14 @@ class Roda
|
|
42
40
|
# then instance_execs the given after proc, so that the given
|
43
41
|
# after proc always executes after the previous one.
|
44
42
|
def after(&block)
|
45
|
-
if
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
instance_exec(res, &block)
|
50
|
-
end
|
51
|
-
else
|
52
|
-
block
|
43
|
+
opts[:after_hook] = if b = opts[:after_hook]
|
44
|
+
proc do |res|
|
45
|
+
instance_exec(res, &b)
|
46
|
+
instance_exec(res, &block)
|
53
47
|
end
|
48
|
+
else
|
49
|
+
block
|
54
50
|
end
|
55
|
-
@after
|
56
51
|
end
|
57
52
|
|
58
53
|
# Add a before hook. If there is already a before hook defined,
|
@@ -60,25 +55,14 @@ class Roda
|
|
60
55
|
# then instance_execs the existing before proc, so that the given
|
61
56
|
# before proc always executes before the previous one.
|
62
57
|
def before(&block)
|
63
|
-
if
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
instance_exec(&b)
|
68
|
-
end
|
69
|
-
else
|
70
|
-
block
|
58
|
+
opts[:before_hook] = if b = opts[:before_hook]
|
59
|
+
proc do
|
60
|
+
instance_exec(&block)
|
61
|
+
instance_exec(&b)
|
71
62
|
end
|
63
|
+
else
|
64
|
+
block
|
72
65
|
end
|
73
|
-
@before
|
74
|
-
end
|
75
|
-
|
76
|
-
# Copy the before and after hooks into the subclasses
|
77
|
-
# when inheriting
|
78
|
-
def inherited(subclass)
|
79
|
-
super
|
80
|
-
subclass.instance_variable_set(:@before, @before)
|
81
|
-
subclass.instance_variable_set(:@after, @after)
|
82
66
|
end
|
83
67
|
end
|
84
68
|
|
@@ -88,13 +72,13 @@ class Roda
|
|
88
72
|
# Before routing, execute the before hooks, and
|
89
73
|
# execute the after hooks before returning.
|
90
74
|
def _route(*, &block)
|
91
|
-
if b =
|
75
|
+
if b = opts[:before_hook]
|
92
76
|
instance_exec(&b)
|
93
77
|
end
|
94
78
|
|
95
79
|
res = super
|
96
80
|
ensure
|
97
|
-
if b =
|
81
|
+
if b = opts[:after_hook]
|
98
82
|
instance_exec(res, &b)
|
99
83
|
end
|
100
84
|
end
|
data/lib/roda/plugins/json.rb
CHANGED
@@ -36,19 +36,13 @@ class Roda
|
|
36
36
|
module Json
|
37
37
|
# Set the classes to automatically convert to JSON
|
38
38
|
def self.configure(app)
|
39
|
-
app.
|
40
|
-
@json_result_classes ||= [Array, Hash]
|
41
|
-
end
|
39
|
+
app.opts[:json_result_classes] ||= [Array, Hash]
|
42
40
|
end
|
43
41
|
|
44
42
|
module ClassMethods
|
45
43
|
# The classes that should be automatically converted to json
|
46
|
-
|
47
|
-
|
48
|
-
# Copy the json_result_classes into the subclass
|
49
|
-
def inherited(subclass)
|
50
|
-
super
|
51
|
-
subclass.instance_variable_set(:@json_result_classes, json_result_classes.dup)
|
44
|
+
def json_result_classes
|
45
|
+
opts[:json_result_classes]
|
52
46
|
end
|
53
47
|
end
|
54
48
|
|
@@ -63,7 +57,7 @@ class Roda
|
|
63
57
|
# application/json content-type.
|
64
58
|
def block_result_body(result)
|
65
59
|
case result
|
66
|
-
when *
|
60
|
+
when *roda_class.json_result_classes
|
67
61
|
response[CONTENT_TYPE] = APPLICATION_JSON
|
68
62
|
convert_to_json(result)
|
69
63
|
else
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'mail'
|
3
|
+
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The mailer plugin allows your Roda application to send emails easily.
|
7
|
+
#
|
8
|
+
# class App < Roda
|
9
|
+
# plugin :render
|
10
|
+
# plugin :mailer
|
11
|
+
#
|
12
|
+
# route do |r|
|
13
|
+
# r.on "albums" do
|
14
|
+
# r.mail "added" do |album|
|
15
|
+
# @album = album
|
16
|
+
# from 'from@example.com'
|
17
|
+
# to 'to@example.com'
|
18
|
+
# cc 'cc@example.com'
|
19
|
+
# bcc 'bcc@example.com'
|
20
|
+
# subject 'Album Added'
|
21
|
+
# add_file "path/to/album_added_img.jpg"
|
22
|
+
# render(:albums_added_email) # body
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# The default method for sending a mail is +sendmail+:
|
29
|
+
#
|
30
|
+
# App.sendmail("/albums/added", Album[1])
|
31
|
+
#
|
32
|
+
# If you want to return the <tt>Mail::Message</tt> instance for further modification,
|
33
|
+
# you can just use the +mail+ method:
|
34
|
+
#
|
35
|
+
# mail = App.mail("/albums/added", Album[1])
|
36
|
+
# mail.from 'from2@example.com'
|
37
|
+
# mail.deliver
|
38
|
+
#
|
39
|
+
# The mailer plugin uses the mail gem, so if you want to configure how
|
40
|
+
# email is sent, you can use <tt>Mail.defaults</tt> (see the mail gem documentation for
|
41
|
+
# more details):
|
42
|
+
#
|
43
|
+
# Mail.defaults do
|
44
|
+
# delivery_method :smtp, :address=>'smtp.example.com', :port=>587
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# You can support multipart emails using +text_part+ and +html_part+:
|
48
|
+
#
|
49
|
+
# r.mail "added" do |album_added|
|
50
|
+
# from 'from@example.com'
|
51
|
+
# to 'to@example.com'
|
52
|
+
# subject 'Album Added'
|
53
|
+
# text_part render('album_added.txt') # views/album_added.txt.erb
|
54
|
+
# html_part render('album_added.html') # views/album_added.html.erb
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# In addition to allowing you to use Roda's render plugin for rendering
|
58
|
+
# email bodies, you can use all of Roda's usual routing tree features
|
59
|
+
# to DRY up your code:
|
60
|
+
#
|
61
|
+
# r.on "albums/:d" do |album_id|
|
62
|
+
# @album = Album[album_id.to_i]
|
63
|
+
# from 'from@example.com'
|
64
|
+
# to 'to@example.com'
|
65
|
+
#
|
66
|
+
# r.mail "added" do
|
67
|
+
# subject 'Album Added'
|
68
|
+
# render(:albums_added_email)
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# r.mail "deleted" do
|
72
|
+
# subject 'Album Deleted'
|
73
|
+
# render(:albums_deleted_email)
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# When sending a mail via +mail+ or +sendmail+, an Error will be raised
|
78
|
+
# if the mail object does not have a body. This is similar to the 404
|
79
|
+
# status that Roda uses by default for web requests that don't have
|
80
|
+
# a body. If you want to specifically send an email with an empty body, you
|
81
|
+
# can use the explicit empty string:
|
82
|
+
#
|
83
|
+
# r.mail do
|
84
|
+
# from 'from@example.com'
|
85
|
+
# to 'to@example.com'
|
86
|
+
# subject 'No Body Here'
|
87
|
+
# ""
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# By default, the mailer uses text/plain as the Content-Type for emails.
|
91
|
+
# You can override the default by specifying a :content_type option when
|
92
|
+
# loading the plugin:
|
93
|
+
#
|
94
|
+
# plugin :mailer, :content_type=>'text/html'
|
95
|
+
#
|
96
|
+
# The mailer plugin does support being used inside a Roda application
|
97
|
+
# that is handling web requests, where the routing block for mails and
|
98
|
+
# web requests is shared. However, it's recommended that you create a
|
99
|
+
# separate Roda application for emails. This can be a subclass of your main
|
100
|
+
# Roda application if you want your helper methods to automatically be
|
101
|
+
# available in your email views.
|
102
|
+
module Mailer
|
103
|
+
REQUEST_METHOD = "REQUEST_METHOD".freeze
|
104
|
+
PATH_INFO = "PATH_INFO".freeze
|
105
|
+
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
106
|
+
EMPTY_STRING = ''.freeze
|
107
|
+
RACK_INPUT = 'rack.input'.freeze
|
108
|
+
RODA_MAIL = 'roda.mail'.freeze
|
109
|
+
RODA_MAIL_ARGS = 'roda.mail_args'.freeze
|
110
|
+
MAIL = "MAIL".freeze
|
111
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
112
|
+
TEXT_PLAIN = "text/plain".freeze
|
113
|
+
|
114
|
+
# Error raised when the using the mail class method, but the routing
|
115
|
+
# tree doesn't return the mail object.
|
116
|
+
class Error < ::Roda::RodaError; end
|
117
|
+
|
118
|
+
# Set the options for the mailer. Options:
|
119
|
+
# :content_type :: The default content type for emails (default: text/plain)
|
120
|
+
def self.configure(app, opts={})
|
121
|
+
app.opts[:mailer] = (app.opts[:mailer]||{}).merge(opts).freeze
|
122
|
+
end
|
123
|
+
|
124
|
+
module ClassMethods
|
125
|
+
# Return a Mail::Message instance for the email for the given request path
|
126
|
+
# and arguments. You can further manipulate the returned mail object before
|
127
|
+
# calling +deliver+ to send the mail.
|
128
|
+
def mail(path, *args)
|
129
|
+
mail = ::Mail.new
|
130
|
+
unless mail.equal?(allocate.call(PATH_INFO=>path, SCRIPT_NAME=>EMPTY_STRING, REQUEST_METHOD=>MAIL, RACK_INPUT=>StringIO.new, RODA_MAIL=>mail, RODA_MAIL_ARGS=>args, &route_block))
|
131
|
+
raise Error, "route did not return mail instance for #{path.inspect}, #{args.inspect}"
|
132
|
+
end
|
133
|
+
mail
|
134
|
+
end
|
135
|
+
|
136
|
+
# Calls +mail+ and immediately sends the resulting mail.
|
137
|
+
def sendmail(*args)
|
138
|
+
mail(*args).deliver
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
module RequestMethods
|
143
|
+
# Similar to routing tree methods such as +get+ and +post+, this matches
|
144
|
+
# only if the request method is MAIL (only set when using the Roda class
|
145
|
+
# +mail+ or +sendmail+ methods) and the rest of the arguments match
|
146
|
+
# the request. This yields any of the captures to the block, as well as
|
147
|
+
# any arguments passed to the +mail+ or +sendmail+ Roda class methods.
|
148
|
+
def mail(*args)
|
149
|
+
if @env[REQUEST_METHOD] == MAIL
|
150
|
+
if_match(args) do |*vs|
|
151
|
+
yield *(vs + @env[RODA_MAIL_ARGS])
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
module ResponseMethods
|
158
|
+
# The mail object related to the current request.
|
159
|
+
attr_accessor :mail
|
160
|
+
|
161
|
+
# If the related request was an email request, add any response headers
|
162
|
+
# to the email, as well as adding the response body to the email.
|
163
|
+
# Return the email unless no body was set for it, which would indicate
|
164
|
+
# that the routing tree did not handle the request.
|
165
|
+
def finish
|
166
|
+
if m = mail
|
167
|
+
header_content_type = @headers.delete(CONTENT_TYPE)
|
168
|
+
m.headers(@headers)
|
169
|
+
m.body(@body.join) unless @body.empty?
|
170
|
+
|
171
|
+
if content_type = header_content_type || roda_class.opts[:mailer][:content_type]
|
172
|
+
if mail.multipart?
|
173
|
+
if mail.content_type =~ /multipart\/mixed/ &&
|
174
|
+
mail.parts.length >= 2 &&
|
175
|
+
(part = mail.parts.find{|p| !p.attachment && p.content_type == TEXT_PLAIN})
|
176
|
+
part.content_type = content_type
|
177
|
+
end
|
178
|
+
else
|
179
|
+
mail.content_type = content_type
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
unless m.body.to_s.empty? && m.parts.empty? && @body.empty?
|
184
|
+
m
|
185
|
+
end
|
186
|
+
else
|
187
|
+
super
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
module InstanceMethods
|
193
|
+
# Add delegates for common email methods.
|
194
|
+
[:from, :to, :cc, :bcc, :subject, :add_file].each do |meth|
|
195
|
+
define_method(meth) do |*args|
|
196
|
+
env[RODA_MAIL].send(meth, *args)
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
end
|
200
|
+
[:text_part, :html_part].each do |meth|
|
201
|
+
define_method(meth) do |*args|
|
202
|
+
_mail_part(meth, *args)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
|
208
|
+
# If this is an email request, set the mail object in the response, as well
|
209
|
+
# as the default content_type for the email.
|
210
|
+
def _route
|
211
|
+
if mail = env[RODA_MAIL]
|
212
|
+
res = @_response
|
213
|
+
res.mail = mail
|
214
|
+
res.headers.delete(CONTENT_TYPE)
|
215
|
+
end
|
216
|
+
super
|
217
|
+
end
|
218
|
+
|
219
|
+
# Set the text_part or html_part (depending on the method) in the related email,
|
220
|
+
# using the given body and optional headers.
|
221
|
+
def _mail_part(meth, body, headers=nil)
|
222
|
+
env[RODA_MAIL].send(meth) do
|
223
|
+
body(body)
|
224
|
+
headers(headers) if headers
|
225
|
+
end
|
226
|
+
nil
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
register_plugin(:mailer, Mailer)
|
232
|
+
end
|
233
|
+
end
|