padrino-mailer 0.18.2 → 0.18.4
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/lib/padrino-mailer/ext.rb +63 -5
- data/test/helper.rb +5 -0
- data/test/test_email.rb +78 -0
- data/test/test_message.rb +29 -0
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b11b32ce2d59a91d317eaa40ad82491aad78b7717d269846ff009ff8b1faec13
|
|
4
|
+
data.tar.gz: 5bcc8d002ca343172c0706f834ecfc8d7e559d516c1a8fdab94c77d5c82519fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e2999d5468663f308a0f3ec49ba3f53e793a8bd9f79e3dd741465c3ceedcad4cf94e0ef8906eba3d78a18c81387b7e5d70648eb8a396e08bc3d609e2fe6b782c
|
|
7
|
+
data.tar.gz: '01877f03e82419e0adc3f50e2c9ff719db6ea4d4f774714faae403a09f2f6a2cf2955dece2d2f415c9bdeceb3e32de21ef42e894cf042ef2f0f55dd50ed18ba1'
|
data/lib/padrino-mailer/ext.rb
CHANGED
|
@@ -9,16 +9,57 @@ module Mail # @private
|
|
|
9
9
|
attr_reader :template_cache
|
|
10
10
|
attr_accessor :mailer_name, :message_name
|
|
11
11
|
|
|
12
|
+
# The template cache of a message whose app doesn't reload templates. Templates given by name
|
|
13
|
+
# (a file or a named template) are compiled once for the class and shared by its messages;
|
|
14
|
+
# templates given as a string stay with the message, so they don't pile up.
|
|
15
|
+
class SharedTemplateCache < Sinatra::TemplateCache # @private
|
|
16
|
+
def initialize(owner)
|
|
17
|
+
super()
|
|
18
|
+
@owner = owner
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def fetch(engine, data, *rest, &block)
|
|
22
|
+
return super unless data.is_a?(Symbol)
|
|
23
|
+
|
|
24
|
+
@shared ||= @owner.shared_template_cache
|
|
25
|
+
@shared.fetch(engine, data, *rest, &block)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Message#to_yaml dumps every instance variable; keep the class's templates and lock out of it.
|
|
29
|
+
def encode_with(coder); end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The cache a class's messages share. Messages are often delivered from several threads, and
|
|
33
|
+
# on JRuby writing to a Hash from two threads at once can raise, so it takes a lock. It compiles
|
|
34
|
+
# outside the lock, so two threads may compile one template; the first one stored is kept.
|
|
35
|
+
class LockedTemplateCache # @private
|
|
36
|
+
def initialize
|
|
37
|
+
@cache = {}
|
|
38
|
+
@lock = Mutex.new
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def fetch(*key)
|
|
42
|
+
@lock.synchronize { return @cache[key] if @cache.key?(key) }
|
|
43
|
+
template = yield
|
|
44
|
+
@lock.synchronize { @cache.fetch(key) { @cache[key] = template } }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
SHARED_TEMPLATE_CACHE_LOCK = Mutex.new # @private
|
|
49
|
+
|
|
50
|
+
def self.shared_template_cache # @private
|
|
51
|
+
SHARED_TEMPLATE_CACHE_LOCK.synchronize { @_shared_template_cache ||= LockedTemplateCache.new }
|
|
52
|
+
end
|
|
53
|
+
|
|
12
54
|
def initialize_with_app(*args, &block)
|
|
13
|
-
@template_cache = Sinatra::TemplateCache.new
|
|
14
55
|
# Check if we have an app passed into initialize
|
|
15
56
|
if args[0].respond_to?(:views) && args[0].respond_to?(:reload_templates?)
|
|
16
57
|
app = args.shift
|
|
17
58
|
@_views = File.join(app.views, 'mailers')
|
|
18
|
-
settings.reload_templates = app.reload_templates?
|
|
59
|
+
settings.reload_templates = self.reload_templates = app.reload_templates?
|
|
19
60
|
else
|
|
20
61
|
@_views = File.expand_path('./mailers')
|
|
21
|
-
settings.reload_templates = true
|
|
62
|
+
settings.reload_templates = self.reload_templates = true
|
|
22
63
|
end
|
|
23
64
|
|
|
24
65
|
initialize_template_settings!
|
|
@@ -109,7 +150,7 @@ module Mail # @private
|
|
|
109
150
|
part_block = params.delete(:part_block)
|
|
110
151
|
new_part = Mail::Part.new(params)
|
|
111
152
|
new_part.views(@_views)
|
|
112
|
-
new_part.settings.reload_templates =
|
|
153
|
+
new_part.settings.reload_templates = new_part.reload_templates = reload_templates?
|
|
113
154
|
new_part.instance_eval(&part_block) if part_block
|
|
114
155
|
yield new_part if block_given?
|
|
115
156
|
add_part(new_part)
|
|
@@ -151,6 +192,23 @@ module Mail # @private
|
|
|
151
192
|
@_mailer_views = false
|
|
152
193
|
end
|
|
153
194
|
|
|
195
|
+
##
|
|
196
|
+
# Returns true if this message reads its templates again on every render, as its app's
|
|
197
|
+
# reload_templates setting said when the message was built.
|
|
198
|
+
#
|
|
199
|
+
def reload_templates?
|
|
200
|
+
@_reload_templates
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
##
|
|
204
|
+
# Sets whether this message reads its templates again on every render. A message that does
|
|
205
|
+
# keeps every template to itself; one that doesn't shares the templates it renders by name.
|
|
206
|
+
#
|
|
207
|
+
def reload_templates=(value)
|
|
208
|
+
@_reload_templates = value
|
|
209
|
+
@template_cache = value ? Sinatra::TemplateCache.new : SharedTemplateCache.new(self.class)
|
|
210
|
+
end
|
|
211
|
+
|
|
154
212
|
##
|
|
155
213
|
# Sets the local variables available within the message template.
|
|
156
214
|
#
|
|
@@ -276,7 +334,7 @@ module Mail # @private
|
|
|
276
334
|
#
|
|
277
335
|
def render(engine = nil, data = nil, options = {}, locals = {}, &block)
|
|
278
336
|
locals = @_locals || {} if !options[:locals] && locals.empty?
|
|
279
|
-
@template_cache.clear if
|
|
337
|
+
@template_cache.clear if reload_templates?
|
|
280
338
|
|
|
281
339
|
# render(nil, 'template', ...) as RenderHelpers#partial calls it: shift the arguments
|
|
282
340
|
# the way Padrino::Rendering#render does, before defaulting to the message name.
|
data/test/helper.rb
CHANGED
|
@@ -2,6 +2,11 @@ ENV['RACK_ENV'] = 'test'
|
|
|
2
2
|
PADRINO_ROOT = __dir__ unless defined? PADRINO_ROOT
|
|
3
3
|
|
|
4
4
|
require 'minitest/autorun'
|
|
5
|
+
begin
|
|
6
|
+
require 'minitest/mock'
|
|
7
|
+
rescue LoadError
|
|
8
|
+
# minitest/mock is built-in for older minitest versions
|
|
9
|
+
end
|
|
5
10
|
require 'minitest/pride'
|
|
6
11
|
require 'rack/test'
|
|
7
12
|
require 'padrino-core'
|
data/test/test_email.rb
CHANGED
|
@@ -259,6 +259,84 @@ describe 'Email' do
|
|
|
259
259
|
assert_equal 'B[beta hello]', @app.deliver(:beta, :boxed).body.to_s.strip
|
|
260
260
|
end
|
|
261
261
|
|
|
262
|
+
it 'should compile a mailer template once for all its deliveries' do
|
|
263
|
+
mailer_app(mailer_views('site'))
|
|
264
|
+
@app.set :reload_templates, false
|
|
265
|
+
compiled = 0
|
|
266
|
+
engine = Tilt[:erb]
|
|
267
|
+
new_template = engine.method(:new)
|
|
268
|
+
engine.stub(:new, lambda { |*args, &block|
|
|
269
|
+
compiled += 1
|
|
270
|
+
new_template.call(*args, &block)
|
|
271
|
+
}) do
|
|
272
|
+
2.times { assert_equal 'alpha hello', @app.deliver(:alpha, :hello).body.to_s }
|
|
273
|
+
end
|
|
274
|
+
assert_equal 1, compiled
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
it 'should read an edited mailer template when templates reload' do
|
|
278
|
+
views = mailer_views('site')
|
|
279
|
+
mailer_app(views)
|
|
280
|
+
@app.set :reload_templates, false
|
|
281
|
+
assert_equal 'alpha hello', @app.deliver(:alpha, :hello).body.to_s
|
|
282
|
+
File.write(File.join(views, 'mailers', 'alpha', 'hello.erb'), 'alpha edited')
|
|
283
|
+
@app.set :reload_templates, true
|
|
284
|
+
assert_equal 'alpha edited', @app.deliver(:alpha, :hello).body.to_s
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
it 'should read an edited part template when templates reload' do
|
|
288
|
+
views = mailer_views('site')
|
|
289
|
+
mailer_app(views)
|
|
290
|
+
@app.set :reload_templates, true
|
|
291
|
+
assert_equal 'gamma plain', @app.deliver(:gamma, :hello).parts.first.body.to_s
|
|
292
|
+
File.write(File.join(views, 'mailers', 'gamma', 'hello.plain.erb'), 'gamma plain edited')
|
|
293
|
+
assert_equal 'gamma plain edited', @app.deliver(:gamma, :hello).parts.first.body.to_s
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
it 'should compile part templates once for all their deliveries' do
|
|
297
|
+
mailer_app(mailer_views('site'))
|
|
298
|
+
@app.set :reload_templates, false
|
|
299
|
+
compiled = 0
|
|
300
|
+
engine = Tilt[:erb]
|
|
301
|
+
new_template = engine.method(:new)
|
|
302
|
+
engine.stub(:new, lambda { |*args, &block|
|
|
303
|
+
compiled += 1
|
|
304
|
+
new_template.call(*args, &block)
|
|
305
|
+
}) do
|
|
306
|
+
2.times do
|
|
307
|
+
assert_equal(['gamma plain', 'gamma html'], @app.deliver(:gamma, :hello).parts.map { |part| part.body.to_s })
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
assert_equal 2, compiled
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
it 'should not dump the templates other messages share' do
|
|
314
|
+
mailer_app(mailer_views('site'))
|
|
315
|
+
@app.set :reload_templates, false
|
|
316
|
+
@app.deliver(:beta, :hello)
|
|
317
|
+
message = build_message(:alpha)
|
|
318
|
+
assert_equal 'alpha hello', message.send(:render, :hello)
|
|
319
|
+
yaml = message.to_yaml
|
|
320
|
+
refute_includes yaml, 'beta hello'
|
|
321
|
+
refute_includes yaml, 'Mutex'
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
it 'should keep the shared templates when a message of another app reloads them' do
|
|
325
|
+
views = mailer_views('site')
|
|
326
|
+
cached = mailer_app(views)
|
|
327
|
+
cached.set :reload_templates, false
|
|
328
|
+
reloading = mailer_app(views)
|
|
329
|
+
reloading.set :reload_templates, true
|
|
330
|
+
assert_equal 'alpha hello', cached.deliver(:alpha, :hello).body.to_s
|
|
331
|
+
File.write(File.join(views, 'mailers', 'alpha', 'hello.erb'), 'alpha edited')
|
|
332
|
+
@app = cached
|
|
333
|
+
waiting = build_message(:alpha)
|
|
334
|
+
@app = reloading
|
|
335
|
+
assert_equal 'alpha edited', build_message(:alpha).send(:render, :hello)
|
|
336
|
+
assert_equal 'alpha hello', waiting.send(:render, :hello)
|
|
337
|
+
assert_equal 'alpha hello', cached.deliver(:alpha, :hello).body.to_s
|
|
338
|
+
end
|
|
339
|
+
|
|
262
340
|
MailerFormUser = Struct.new(:name) unless defined?(MailerFormUser)
|
|
263
341
|
|
|
264
342
|
it 'should build forms with form_for in a mailer template' do
|
data/test/test_message.rb
CHANGED
|
@@ -212,4 +212,33 @@ describe 'Message' do
|
|
|
212
212
|
end
|
|
213
213
|
end
|
|
214
214
|
end
|
|
215
|
+
|
|
216
|
+
describe 'the shared template cache' do
|
|
217
|
+
before { @cache = Mail::Message::LockedTemplateCache.new }
|
|
218
|
+
|
|
219
|
+
it 'should give threads that compile a template at the same time the one it keeps' do
|
|
220
|
+
threads = Array.new(2) do
|
|
221
|
+
Thread.new do
|
|
222
|
+
@cache.fetch(:erb, :same) do
|
|
223
|
+
sleep 0.05
|
|
224
|
+
Object.new
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
kept = threads.map(&:value)
|
|
229
|
+
assert_same kept[0], kept[1]
|
|
230
|
+
assert_same(kept[0], @cache.fetch(:erb, :same) { flunk 'compiled a stored template again' })
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
it 'should serve other templates while one compiles' do
|
|
234
|
+
release = Queue.new
|
|
235
|
+
slow = Thread.new { @cache.fetch(:erb, :slow) { release.pop && :slow } }
|
|
236
|
+
Thread.pass while slow.status == 'run'
|
|
237
|
+
fast = Thread.new { @cache.fetch(:erb, :fast) { :fast } }
|
|
238
|
+
assert_equal :fast, fast.join(2) && fast.value
|
|
239
|
+
ensure
|
|
240
|
+
release << true
|
|
241
|
+
assert_equal :slow, slow.value
|
|
242
|
+
end
|
|
243
|
+
end
|
|
215
244
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: padrino-mailer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.18.
|
|
4
|
+
version: 0.18.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Padrino Team
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date: 2026-11-
|
|
14
|
+
date: 2026-11-04 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: padrino-core
|
|
@@ -19,14 +19,14 @@ dependencies:
|
|
|
19
19
|
requirements:
|
|
20
20
|
- - '='
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
|
-
version: 0.18.
|
|
22
|
+
version: 0.18.4
|
|
23
23
|
type: :runtime
|
|
24
24
|
prerelease: false
|
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
26
|
requirements:
|
|
27
27
|
- - '='
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: 0.18.
|
|
29
|
+
version: 0.18.4
|
|
30
30
|
- !ruby/object:Gem::Dependency
|
|
31
31
|
name: mime-types
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|