has_web_fallback 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,43 @@
1
+ HasWebFallback
2
+ =============
3
+
4
+ Extracts the common behaviour of wanting online versions of html emails.
5
+
6
+ If a mailer function is described as has_web_fallback two things will happen.
7
+
8
+ 1. The template will get access to an @web_fallback_url, a url (relative to /) to the web version of the email
9
+ 2. After sending, if the email has an html part, that part will be written out to the public directory.
10
+
11
+ Future Features
12
+ ===============
13
+ Customizable urls/paths
14
+
15
+ Installation
16
+ ============
17
+ Right now, HasWebFallback requires UUIDTools to generate the names for the emails.
18
+ config.gem 'uuidtools'
19
+
20
+
21
+ Example
22
+ =======
23
+
24
+ class ExampleMailer < ActionMailer::Base
25
+
26
+ has_web_fallback :welcome, :keep_for=>7.days
27
+ has_web_fallback :goodbye, :keep_for=>10.minutes
28
+
29
+ def welcome
30
+ #
31
+ end
32
+
33
+ def goodbye
34
+ #
35
+ end
36
+ end
37
+
38
+ ... and in the template
39
+
40
+ <%= link_to("View this email online", @web_fallback_url, :path_only=>false) %>
41
+
42
+
43
+ Copyright (c) 2009 Gavin Montague, released under the MIT license
@@ -0,0 +1 @@
1
+ require "leftbrained/has_web_fallback"
@@ -0,0 +1,139 @@
1
+ require 'find'
2
+ require 'ftools'
3
+ require 'uuidtools'
4
+
5
+ module Leftbrained
6
+
7
+ module HasWebFallback #:nodoc:
8
+
9
+ WEB_FALLBACK_PATH = File.join(File.expand_path(RAILS_ROOT), "public")
10
+
11
+ def self.included(base)
12
+ base.extend(ClassMethods)
13
+ end
14
+
15
+
16
+ # mattr_accessor :write_web_fallbacks
17
+
18
+ module ClassMethods
19
+
20
+ def has_web_fallback(name, options={})
21
+ write_inheritable_attribute(:methods_with_web_fallback, {}) if methods_with_web_fallback.nil?
22
+ methods_with_web_fallback[name] = { :keep_for=>7.days }.merge(options)
23
+
24
+ class_eval <<-EOV
25
+ include Leftbrained::HasWebFallback::InstanceMethods
26
+ EOV
27
+ end
28
+
29
+ def has_web_fallback?(method_name)
30
+ methods_with_web_fallback.include? method_name.to_sym
31
+ end
32
+
33
+ def web_fallback_uuid_for(method_name)
34
+ UUIDTools::UUID.random_create.to_s<<".html"
35
+ end
36
+
37
+ def methods_with_web_fallback
38
+ read_inheritable_attribute(:methods_with_web_fallback)
39
+ end
40
+
41
+ def cache_deadline_for_web_fallback(method_name)
42
+ Time.now - cache_period_for_method_with_web_fallback(method_name)
43
+ end
44
+
45
+ def clear_web_fallback_caches
46
+ methods_with_web_fallback.keys.each do |meth_name|
47
+ clear_web_fallback_cache meth_name
48
+ end
49
+ end
50
+
51
+ def clear_web_fallback_cache(method_name)
52
+ web_fallback_caches_to_be_cleared(method_name).each do |file|
53
+ File.unlink(file)
54
+ end
55
+ end
56
+
57
+ def web_fallback_caches_to_be_cleared(method_name)
58
+ return [] unless has_web_fallback? method_name
59
+ cut_off_time = cache_deadline_for_web_fallback(method_name)
60
+ path = path_for_web_fallback(method_name)
61
+ deletable = []
62
+ if File.exists? path
63
+ Find.find(path) do |path|
64
+ deletable << path if (File.mtime(path) <= cut_off_time) && File.file?(path)
65
+ end
66
+ end
67
+ deletable
68
+ end
69
+
70
+ def cache_period_for_method_with_web_fallback(method_name)
71
+ methods_with_web_fallback[method_name.to_sym][:keep_for]
72
+ end
73
+
74
+ def url_for_web_fallback(method_name)
75
+ "/system/web_fallback/#{self.to_s.underscore.gsub("_mailer", '')}/#{method_name}"
76
+ end
77
+
78
+ def path_for_web_fallback(method_name)
79
+ File.join(WEB_FALLBACK_PATH, url_for_web_fallback(method_name))
80
+ end
81
+
82
+ end # ClassMethods
83
+
84
+ module InstanceMethods
85
+
86
+ def web_fallback_uuid
87
+ @web_fallback_uuid ||= self.class.web_fallback_uuid_for(@action_name)
88
+ end
89
+
90
+ def web_fallback_url
91
+ File.join(self.class.url_for_web_fallback(@action_name), web_fallback_uuid)
92
+ end
93
+
94
+ def web_fallback_path
95
+ File.join(self.class.path_for_web_fallback(@action_name), web_fallback_uuid)
96
+ end
97
+
98
+ def has_web_fallback?
99
+ self.class.has_web_fallback? @action_name
100
+ end
101
+
102
+ # If this method needs a fallback version set an instance variable in the template
103
+ # so we can actually tell the user what the URL will be.
104
+ def render_message(method_name, body)
105
+ body[:web_fallback_url] = web_fallback_url if has_web_fallback?
106
+ super
107
+ end
108
+
109
+ def write_web_fallback
110
+ return unless has_html_part?
111
+ FileUtils.mkdir_p(File.dirname(web_fallback_path))
112
+ f = File.new(web_fallback_path, 'w')
113
+ f.puts(html_part)
114
+ f.close
115
+ end
116
+
117
+ def has_html_part?
118
+ !!mail.parts.find{ |part| 'text/html' == part.content_type }
119
+ end
120
+
121
+ def html_part
122
+ mail.parts.find{ |part| 'text/html' == part.content_type }.body
123
+ end
124
+
125
+
126
+ # After delivering any mail, pop a version out to the file system
127
+ def deliver!(mail=@mail)
128
+ super(mail)
129
+ write_web_fallback if has_web_fallback?
130
+ mail
131
+ end
132
+
133
+ end # InstanceMethods
134
+
135
+ end
136
+
137
+ end
138
+
139
+ ActionMailer::Base.class_eval { include Leftbrained::HasWebFallback }
@@ -0,0 +1,124 @@
1
+ require 'test_helper'
2
+
3
+
4
+ require "#{File.dirname(__FILE__)}/test_helper"
5
+
6
+ class HasWebFallbackTest < ActionMailer::TestCase
7
+
8
+ class ExampleMailer < ActionMailer::Base
9
+ has_web_fallback :welcome
10
+ has_web_fallback :goodbye
11
+
12
+ def welcome
13
+ recipients "no-one@example.com"
14
+ body[:name] = "Joe"
15
+ subject "welcome"
16
+ end
17
+
18
+ def goodbye
19
+ body[:name] = "Joe"
20
+ subject "goodbye"
21
+ end
22
+
23
+ def recover_password
24
+ end
25
+ end
26
+ #
27
+
28
+ context "When included, ExampleMailer" do
29
+
30
+ should "have a class attribute for tracking methods with fallbacks" do
31
+ assert ExampleMailer.inheritable_attributes.include? :methods_with_web_fallback
32
+ end
33
+
34
+ should "be able to identify if a method has a web fallback" do
35
+ assert ExampleMailer.has_web_fallback?(:welcome)
36
+ assert ExampleMailer.has_web_fallback?(:goodbye)
37
+ end
38
+
39
+ should "be able to identify a method without a web fallback" do
40
+ assert !ExampleMailer.has_web_fallback?(:recover_password)
41
+ end
42
+
43
+ should "be able to generate UUIDs" do # I know this is a lousy test, but how the hell would one test a UUID?
44
+ uuid_1 = ExampleMailer.web_fallback_uuid_for(:welcome)
45
+ uuid_2 = ExampleMailer.web_fallback_uuid_for(:welcome)
46
+ assert uuid_1 != uuid_2, "UUID clash - #{uuid_1} vs #{uuid_2}"
47
+ end
48
+
49
+ should_eventually "be able to list cached files for a given function that should be deleted" do
50
+ # I have quite literally no idea how to test this without
51
+ # mocking the living shit out File, or writing out to the file system.
52
+ end
53
+
54
+ end
55
+
56
+ context "A method with no fallback" do
57
+ setup do
58
+ @recover_password_mail = ExampleMailer.send(:new, 'recover_password')
59
+ end
60
+
61
+ should "know it doesn't have a fallback" do
62
+ assert !@recover_password_mail.has_web_fallback?
63
+ end
64
+
65
+ should "not write out an html version" do
66
+ @recover_password_mail.deliver!
67
+ assert !File.exists?(@recover_password_mail.web_fallback_path)
68
+ end
69
+ end
70
+
71
+ context "A method with fallback" do
72
+ setup do
73
+ @welcome_mail = ExampleMailer.send(:new, 'welcome')
74
+ end
75
+
76
+ should "know if it has a web fallback " do
77
+ assert @welcome_mail.has_web_fallback?
78
+ end
79
+
80
+ context "and an html part" do
81
+ should "have a url to the email " do
82
+ assert_match /^\/system\/web_fallback\/has_web_fallback_test\/example\/welcome\/[a-z0-9\-]+\.html$/, @welcome_mail.web_fallback_url
83
+ end
84
+
85
+ should "have an absolute_path to the file's location" do
86
+ assert @welcome_mail.web_fallback_path
87
+ end
88
+
89
+ should "know it has an html part" do
90
+ assert @welcome_mail.has_html_part?
91
+ end
92
+
93
+ should "be able to include a link in any of the body parts." do
94
+ @welcome_mail.parts.each do |part|
95
+ assert_match @welcome_mail.web_fallback_url, part.body
96
+ end
97
+ end
98
+
99
+ should "write out an html version" do
100
+ @welcome_mail.deliver!
101
+ assert File.exists?(@welcome_mail.web_fallback_path)
102
+ end
103
+ end
104
+
105
+ context "but no html part" do
106
+ setup do
107
+ @goodbye_mail = ExampleMailer.send(:new, 'goodbye')
108
+ end
109
+
110
+ should "know it doesn't have an html part" do
111
+ assert !@goodbye_mail.has_html_part?
112
+ end
113
+
114
+ should "not write out an html version" do
115
+ @goodbye_mail.deliver!
116
+ assert !File.exists?(@goodbye_mail.web_fallback_path)
117
+ end
118
+
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+
@@ -0,0 +1,6 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/has_web_fallback'
4
+
5
+ # Reset out template path for AM to work with tests, apologies to any one else running tests....
6
+ ActionMailer::Base.template_root = File.join(File.dirname(__FILE__), 'app', 'views')
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_web_fallback
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gavin Montague
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-22 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thoughtbot-shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: actionmailer
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: uuidtools
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ description: Adds the commonly required behaviour of caching web accessible versions of HTML emails to ActionMailer
46
+ email: gavin@leftbrained.co.uk
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - README
53
+ files:
54
+ - lib/has_web_fallback.rb
55
+ - lib/leftbrained/has_web_fallback.rb
56
+ - README
57
+ has_rdoc: true
58
+ homepage: http://github.com/Govan/has_web_fallback
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --charset=UTF-8
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.5
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Adds the commonly required behaviour of caching web accessible versions of HTML emails to ActionMailer
85
+ test_files:
86
+ - test/has_web_fallback_test.rb
87
+ - test/test_helper.rb