has_web_fallback 0.1.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.
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