template_mailer 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 83b2af7df86d4e00961e7e7863e1278b3d87cc17
4
+ data.tar.gz: d330298ec3ace4d7975c389ddeb3855ace67b47e
5
+ SHA512:
6
+ metadata.gz: 762b21bb217c393ac8bd12be866503b18d766c33aa2b7d7e8845a0626a900513305b2e7b16e7bc956467721cc3560bdc5366b859057ebbc758d973f542ff5c84
7
+ data.tar.gz: ffd9d59f7f8b6e473eed31f686f43174c7d214df590274a438a195c25f3b861a8031d63b1f51e8e4696a23acd085215098576dd5ccabe78b097e77fa5bf55794
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in template_mailer.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Peter Wood
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # TemplateMailer
2
+
3
+ The Template Mailer library provides functionality to encapsulate the creation
4
+ and dispatch of emails. The emails sent by the library are created from templates
5
+ stored locally and allows for the creation of emails that are both HTML and/or
6
+ textual based. The library makes use of the [Tilt](https://github.com/rtomayko/tilt)
7
+ template library and the [Pony](https://github.com/benprew/pony libraries and
8
+ aspects of these interactions are exposed via configuration settings.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'template_mailer'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install template_mailer
25
+
26
+ ## Usage
27
+
28
+ To make use of the library you first need to require in the Tilt functionality
29
+ for the templating systems that you want to support and then require in the
30
+ template mailer library. So, for example, if you wanted to make use of ERB as
31
+ your templating engine then you would do the following...
32
+
33
+ ```ruby
34
+ require "tilt/erb"
35
+ require "template_mailer"
36
+ ```
37
+
38
+ Next you want to create a ```Mailer``` class instance. To do this you will need
39
+ to specify a number of configuration settings as parameters to the constructor.
40
+ The parameters needed include the path to the directory containing your template
41
+ files as well as the mechanism that you want to use when dispatching emails. The
42
+ default mechanism, inherited from Pony, is ```sendmail``` but throughout the
43
+ rest of this document the assumption will be that you want to send your email via
44
+ an SMTP server. So, assuming your templates are stored in the ```/templates```
45
+ folder the code to create a ```Mailer``` might look as follows...
46
+
47
+ ```ruby
48
+ mailer = TemplateMailer::Mailer.new(directory: "/templates",
49
+ server: {address: 'mymail.smtp.com',
50
+ port: '587',
51
+ enable_starttls_auto: true,
52
+ user_name: 'mymailuser',
53
+ password: 'password',
54
+ authentication: :login,
55
+ domain: "mydomain.com"},
56
+ via: :smtp)
57
+ ```
58
+
59
+ Once you have a ```Mailer``` instance you can use it to create an email. To create
60
+ an email you call the ```generate_mail()``` method on the mailer and give it the
61
+ name of the template that you want to generate the email from. The template name
62
+ equates to the name of the file or files containing the templates to be generated
63
+ minus any extensions. So, for example, if you had a template file in the template
64
+ directory called ```test_message.html.erb``` then the template name to specify to
65
+ the ```Mailer``` would be ```test_message```.
66
+
67
+ Note that if you want your email message to contain details for HTML and text only
68
+ based clients then create to files in the template directory with ```.text``` and
69
+ ```.html``` extension. The ```Mailer``` will find both of these templates and
70
+ generate content from them both for the email message to be sent.
71
+
72
+ Note that when calling ```generate_mail()``` you can pass a Hash of context
73
+ parmaeters as the second parameter to the method call. The values within this
74
+ Hash will be available to populate the message templates with. So, for example,
75
+ if you wanted to pass in a parameter called full name you would make a call that
76
+ looked like this...
77
+
78
+ ```ruby
79
+ mailer.generate_mail("my_template", full_name: "John Smith")
80
+ ```
81
+
82
+ To send the email generated simply call ```send()``` on the value returned from
83
+ the call to the ```generate_mail()``` method. The call to ```send()``` should be
84
+ accompanied by the final set of details needed to send the email such as the list
85
+ of recipients for the message, the title to be given to the email and possibly the
86
+ email address that the message will appear to come from. For example, a send
87
+ might look as follows...
88
+
89
+ ```ruby
90
+ mailer.send(from: "me@mydomain.com",
91
+ recipients: ["first.person@mydomain.com", "second.person@mydomain.com"],
92
+ subject: "Test Message")
93
+ ```
94
+
95
+ ### Advanced Usage
96
+
97
+ Having all potential email templates within a single directory could ultimately
98
+ become difficult to manage. To support a hierarchical directory structure an
99
+ instance of the ```Mailer``` class will support stepping down through a list of
100
+ subdirectories to pick out a particular template. For example, lets say that you
101
+ wanted to keep all emails relating to account creation and maintenance in a
102
+ subdirectory beneath the mail template directory called ```accounts```. To send
103
+ a template called ```create``` from this directory you could create the
104
+ ```Mailer``` as outlined above and then do the following...
105
+
106
+ ```ruby
107
+ message = mailer.accounts.generate_mail("create")
108
+ ```
109
+
110
+ The call to ```mailer.accounts``` automatically creates a new ```Mailer``` instance
111
+ based on the subdirectory. As this works this way you can chain calls like this
112
+ together so, if you had a template called 'verify' that was stored in a subfolder
113
+ of the accounts folder called create you could create a messasge based on it as
114
+ follows...
115
+
116
+ ```ruby
117
+ message = mailer.accounts.create.generate_mail("verify")
118
+ ```
119
+
120
+ Note that the call to ```generate_mail()``` here can be replaced with the name of
121
+ the template as the ```Mailer``` instance will recognise that this relates to an
122
+ existing template and automatically call ```generate_mail()``` for you. This
123
+ means that previous call could be changed to...
124
+
125
+ ```ruby
126
+ message = mailer.account.create.verify
127
+ ```
128
+
129
+ ## Contributing
130
+
131
+ 1. Fork it ( https://github.com/[my-github-username]/template_mailer/fork )
132
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
133
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
134
+ 4. Push to the branch (`git push origin my-new-feature`)
135
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,4 @@
1
+ module TemplateMailer
2
+ class TemplateMailerError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,11 @@
1
+ module TemplateMailer
2
+ module LoggingHelper
3
+ def log
4
+ if !@logger
5
+ @logger = Logger.new(STDOUT)
6
+ @logger.level = Logger::UNKNOWN
7
+ end
8
+ @logger
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,70 @@
1
+ module TemplateMailer
2
+ class MailMessage
3
+ # Constructor for the MailMessage class.
4
+ #
5
+ # ==== Parameters
6
+ # options:: An options Hash. The following keys are recognised within this
7
+ # Hash...
8
+ # * :html - The HTML content for the email message. Both this and
9
+ # text can be specified but at least one of them should be.
10
+ # * :text - The textual content for the email message. Both this
11
+ # and :html can be specified but at least one should be.
12
+ # * :server - When SMTP is the preferred email mecahnism (see the
13
+ # :via option) then this value should be a Hash of the parameters
14
+ # that will be used to talk to the SMTP server.
15
+ # * :via - The email mechanism to use to dispatch email messages.
16
+ # options are :sendmail (the default) or :smtp. If :smtp is
17
+ # specified then the :server option should also be given a
18
+ # value.
19
+ def initialize(options={})
20
+ @html = options[:html]
21
+ @text = options.fetch(:text, options[:txt])
22
+ @server = options[:server]
23
+ @via = options.fetch(:via, :sendmail)
24
+ end
25
+ attr_reader :html, :text
26
+
27
+ # This method attempts to send the contents of a mail message to a specified
28
+ # set of recipients.
29
+ #
30
+ # ==== Parameters
31
+ # options:: A Hash of the options to be used when sending the email.
32
+ # Recognised keys in this Hash are :subject (a String containing
33
+ # the email title), :recipients (either a String or Array list
34
+ # of email addresses that the message should be sent to) and
35
+ # :from (the email address that will be set as the message
36
+ # source).
37
+ def send(options={})
38
+ if !options.include?(:recipients) || [nil, "", []].include?(options[:recipients])
39
+ raise TemplateMailerError, "No recipients specified for email."
40
+ end
41
+ Pony.mail(send_settings(options))
42
+ end
43
+ alias :dispatch :send
44
+
45
+ private
46
+
47
+ # This method assembles a Hash of configuration settings to be given to the
48
+ # Pony library to dispatch an email.
49
+ #
50
+ # ==== Parameters
51
+ # settings:: A Hash of the base server settings to be used when putting\
52
+ # the complete settings together.
53
+ def send_settings(settings)
54
+ output = {to: settings[:recipients]}
55
+ output[:subject] = settings[:subject]
56
+ output[:html_body] = @html if (@html || "") != ""
57
+ output[:body] = @text if (@text || "") != ""
58
+ output[:from] = settings[:from] if settings.include?(:from)
59
+ output.merge(pony_settings(settings))
60
+ end
61
+
62
+ # This method is used internally to assemble configuration settings that are
63
+ # specific to dispatching an email via SMTP through the Pony library.
64
+ def pony_settings(settings)
65
+ output = {via: @via}
66
+ output[:via_options] = @server if @via == :smtp
67
+ output
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,135 @@
1
+ module TemplateMailer
2
+ class Mailer
3
+ include LoggingHelper
4
+
5
+ # Constructor for the Mailer class.
6
+ #
7
+ # ==== Parameters
8
+ # options:: A Hash of the options to be used by the Mailer. Recognised
9
+ # option settings are...
10
+ # * :directory - The root directory that the mailer will use
11
+ # when looking for template files. If not specified or if the
12
+ # value specified does not exist then the current working
13
+ # directory is assumed to have a subdirectory called templates
14
+ # and that will be used.
15
+ # * :logger - The logger to be used by the mailer. Defaults to
16
+ # using a null logger.
17
+ # * :server - Configuration details used to talk to the SMTP
18
+ # server to send emails. See the :via option.
19
+ # * :via - A indication of the mailing method to be used when
20
+ # dispatching emails. Viable options are :sendmail (the
21
+ # default) or :smtp. If :smtp is specified then server
22
+ # details should also be specified.
23
+ def initialize(options={})
24
+ @logger = options[:logger]
25
+ @directory = TemplateDirectory.new(template_directory(options[:directory]))
26
+ @via = options.fetch(:via, :sendmail)
27
+ @server = options[:server]
28
+ end
29
+ attr_reader :server, :via
30
+
31
+ # Fetches the path to the template directory being used by the mailer.
32
+ def directory
33
+ @directory.path
34
+ end
35
+
36
+ # This method creates a new Mailer instance by using the details of the
37
+ # current mailer and extending the directory used with the name passed
38
+ # in.
39
+ #
40
+ # ==== Parameters
41
+ # name:: The name of the folder to be appended to the directory being
42
+ # used by the mailer that the call is being made on.
43
+ def subdirectory(name)
44
+ Mailer.new(directory: File.join(directory, name.to_s),
45
+ logger: log,
46
+ server: @server,
47
+ via: @via)
48
+ end
49
+
50
+ # Generates a MailMessage for a named template in the template directory.
51
+ #
52
+ # ==== Parameters
53
+ # template:: The name of the template to generate the email from.
54
+ # context:: A Hash of the variables to be used when generating the email
55
+ # templates. Defaults to an empty Hash.
56
+ def generate_mail(template, context={})
57
+ log.debug "Constructing a mail message based on the '#{template}' template."
58
+ factory = TemplateFactory.new(@directory.path, log)
59
+ content = factory.manufacture(template, context)
60
+ MailMessage.new({server: @server, via: @via}.merge(content))
61
+ end
62
+
63
+ # This method overrides the default implementation to return true for
64
+ # anything that represents a valid Ruby method name. Everything else
65
+ # is delegated to the parent class.
66
+ def respond_to?(name, all=false)
67
+ method_name?(name) || super
68
+ end
69
+
70
+ # This method checks the template directory for a subdirectory for the
71
+ # given name. If a subdirectory is found then the call becomes an
72
+ # equivalent to a call to the subdirectory() method. If template
73
+ # matching the name passed in is instead found then this is equivalent
74
+ # to a call to the generate_mail() method. If all other options are
75
+ # exhausted then the parent class version of this method is invoked.
76
+ def method_missing(name, *arguments, &block)
77
+ if directory_exists?(@directory.path, name.to_s)
78
+ subdirectory(name)
79
+ elsif @directory.exists?(name.to_s)
80
+ generate_mail(name, arguments.empty? ? {} : arguments[0])
81
+ else
82
+ super
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def method_name?(name)
89
+ return /[@$"]/ !~ name.inspect
90
+ end
91
+
92
+ def settings
93
+ {directory: @directory.path, engines: @engines.values, logger: log}
94
+ end
95
+
96
+ def generate_template(path, context={})
97
+ raise TemplateMailerError, "The '#{path}' template files does not exist." if !File.exist?(path)
98
+ raise TemplateMailerError, "Insufficient permission to read the '#{path}' file." if !File.readable?(path)
99
+
100
+ pathname = Pathname.new(path)
101
+ engine = @engines[Pathname.new(path).extname]
102
+ raise TemplateMailerError, "No template engine configured to process the '#{path}' template file." if engine.nil?
103
+
104
+ log.debug "Processing the '#{path}' template file with an instance of the #{engine.class.name} class."
105
+ engine.process(pathname, context)
106
+ end
107
+
108
+ def template_directory(path)
109
+ output = File.join(Dir.getwd, "templates")
110
+ if path && path.strip != ""
111
+ if directory_exists?(path)
112
+ output = path
113
+ else
114
+ log.warn "The '#{path}' path either does not exist or is not a directory. Default will be used."
115
+ end
116
+ end
117
+ log.debug "Using '#{output}' as the mailer templates directory."
118
+ output
119
+ end
120
+
121
+ def directory_exists?(*components)
122
+ path = File.join(*components)
123
+ File.exist?(path) && File.directory?(path)
124
+ end
125
+
126
+ def file_exists?(*components)
127
+ path = path(*components)
128
+ File.exist?(path) && File.file?(path)
129
+ end
130
+
131
+ def path(*components)
132
+ File.join(*components)
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,65 @@
1
+ module TemplateMailer
2
+ class TemplateDirectory
3
+ include LoggingHelper
4
+
5
+ # Constructor for the TemplateDirectory class.
6
+ #
7
+ # ==== Parameters
8
+ # path:: The path to the template directory.
9
+ # logger:: The logger to be used by the directory object.
10
+ def initialize(path,logger=nil)
11
+ @pathname = Pathname.new(path)
12
+ @logger = logger
13
+ scan_templates
14
+ end
15
+ attr_reader :pathname, :engines, :extensions
16
+
17
+ # Returns a String containing the template directory path.
18
+ def path
19
+ @pathname.to_s
20
+ end
21
+
22
+ # Returns an array of the template files within the directory.
23
+ def template_files
24
+ [].concat(@templates)
25
+ end
26
+
27
+ # Checks whether at least one template file with a given name exists within
28
+ # the template directory.
29
+ #
30
+ # ==== Parameters
31
+ # name:: The name of the template. This should be the file name, not
32
+ # including base path details or extensions.
33
+ def exists?(name)
34
+ !template_paths(name).empty?
35
+ end
36
+
37
+ # Retrieves a list of paths for all template files within a template
38
+ # directory that match a given template name.
39
+ #
40
+ # ==== Parameters
41
+ # name:: The name of the template. This should be the file name, not
42
+ # including base path details or extensions.
43
+ def template_paths(name)
44
+ @templates.inject([]) do |list, path|
45
+ file_name = File.basename(path)
46
+ file_name[0, name.length] == name.to_s ? list << path : list
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # Scans the files in the template directory to generate a list of files
53
+ # recognised as templates based on extensions from the object itself and
54
+ # the engine it possesses.
55
+ def scan_templates
56
+ @templates = Dir.glob(File.join(path, "*")).inject([]) do |list, file_path|
57
+ log.debug "Checking if #{file_path} is a recognised template file."
58
+ file_name = File.basename(file_path)
59
+ log.debug "#{file_path} is a template file." if !(Tilt[file_name]).nil?
60
+ list << file_path if !(Tilt[file_name]).nil?
61
+ list
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,84 @@
1
+ module TemplateMailer
2
+ class TemplateFactory
3
+ include LoggingHelper
4
+
5
+ # Constructor for the template factory class.
6
+ #
7
+ # ==== Parameters
8
+ # directory:: The path to the directory containing the template files.
9
+ # logger:: The logger to be used by the factory. Defaults to nil.
10
+ def initialize(directory, logger=nil)
11
+ @directory = TemplateDirectory.new(directory, logger)
12
+ @logger = logger
13
+ end
14
+
15
+ # Fetches the template directory being used by the factory.
16
+ def directory
17
+ @directory.path
18
+ end
19
+
20
+ # Checks whether one or more template files exist for a given name.
21
+ #
22
+ # ==== Parameters
23
+ # name:: The name of the template to check for. A template name should be
24
+ # the template file name without extension(s).
25
+ def exists?(name)
26
+ @directory.exists?(name)
27
+ end
28
+
29
+ # This method 'manufactures' a set of templates based on the template name.
30
+ # Manufacturing involves finding all instance of a template and then using
31
+ # the template engine to generate the results of the template including the
32
+ # elements of the context passed in. The return value from this method will
33
+ # be a Hash of the templates generated (there may be more than one) keyed
34
+ # on the base file types used to generate the template instance. If a
35
+ # matching template cannot be found then an empty Hash is returned.
36
+ #
37
+ # ==== Parameters
38
+ # name:: The name of the template to manufacture. Template names equate
39
+ # to template file names minus extensions.
40
+ # context:: A Hash of settings that will be made available to the template
41
+ # engine when the template is instantiated. Defaults to {}.
42
+ def manufacture(name, context={})
43
+ paths = @directory.template_paths(name)
44
+ log.debug "Manufacture requested for the '#{name}' template. Context:\n#{context}\nFound #{paths.size} matching template files."
45
+ paths.inject({}) do |store, path|
46
+ key = file_base_type(path)
47
+ engine = Tilt.new(path)
48
+ log.debug "Generating template for the #{path} template file as type '#{key}'."
49
+ store[key] = engine.render(nil, context)
50
+ store
51
+ end
52
+ end
53
+
54
+ # Same as the manufacture() method except this version raises an exception
55
+ # if no matching templates are found.
56
+ #
57
+ # ==== Parameters
58
+ # name:: The name of the template to manufacture. Template names equate
59
+ # to template file names minus extensions.
60
+ # context:: A Hash of settings that will be made available to the template
61
+ # engine when the template is instantiated. Defaults to {}.
62
+ def manufacture!(name, context={})
63
+ output = manufacture(name, context)
64
+ raise TemplateMailerError, "Unable to locate a template with the name '#{name}'." if output.empty?
65
+ output
66
+ end
67
+
68
+ private
69
+
70
+ # Generate a Symbol based on the base type for a file. The base type for
71
+ # template files will be the first extension that the file has rather than
72
+ # the second (e.g. :html for a file called template.html.erb).
73
+ #
74
+ # ==== Parameters
75
+ # path:: The path and name of the template file to generate the base type
76
+ # for.
77
+ def file_base_type(path)
78
+ file_name = File.basename(path)
79
+ extension = File.extname(file_name)
80
+ file_name = file_name[0, file_name.length - extension.length]
81
+ (File.extname(file_name)[1..-1]).to_sym
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,3 @@
1
+ module TemplateMailer
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,14 @@
1
+ require "tilt"
2
+ require "logger"
3
+ require "pathname"
4
+ require "pony"
5
+ require "template_mailer/version"
6
+ require "template_mailer/exceptions"
7
+ require "template_mailer/logging_helper"
8
+ require "template_mailer/template_directory"
9
+ require "template_mailer/template_factory"
10
+ require "template_mailer/mail_message"
11
+ require "template_mailer/mailer"
12
+
13
+ module TemplateMailer
14
+ end
@@ -0,0 +1 @@
1
+ This is another test template.
@@ -0,0 +1 @@
1
+ A simple template.
@@ -0,0 +1 @@
1
+ Template File 2
@@ -0,0 +1 @@
1
+ <h1>Test Template 3</h1>
@@ -0,0 +1 @@
1
+ <h1>T4: <%= one %>, '<%= two %>'</h1>
@@ -0,0 +1 @@
1
+ T4: <%= one %>, '<%= two %>'
@@ -0,0 +1 @@
1
+ T4: <%= one %>, '<%= two %>'
@@ -0,0 +1,66 @@
1
+ describe TemplateMailer::MailMessage do
2
+ let(:recipients) {
3
+ ["one@test.com", "two@test.com"]
4
+ }
5
+ let(:from) {
6
+ "sender@somewhere.com"
7
+ }
8
+ let(:title) {
9
+ "Test Email"
10
+ }
11
+ let(:html_body) {
12
+ "<h1>HTML message.</h1>"
13
+ }
14
+ let(:text_body) {
15
+ "Text message."
16
+ }
17
+ let(:server) {
18
+ {address: "smtp.test.com",
19
+ port: "25",
20
+ user_name: "user",
21
+ password: "password",
22
+ authentication: :plain,
23
+ domain: "localhost.localdomain"}
24
+ }
25
+ subject {
26
+ TemplateMailer::MailMessage.new(html: html_body,
27
+ server: server,
28
+ text: text_body,
29
+ via: :smtp)
30
+ }
31
+
32
+ describe "#send()" do
33
+ let(:server_settings) {
34
+ {body: text_body,
35
+ from: from,
36
+ html_body: html_body,
37
+ subject: title,
38
+ to: recipients,
39
+ via: :smtp,
40
+ via_options: server}
41
+ }
42
+
43
+ it "invokes the Pony.mail() method when called with valid parameters" do
44
+ expect(Pony).to receive(:mail).with(server_settings).once
45
+ subject.send(from: from, recipients: recipients, subject: title)
46
+ end
47
+
48
+ it "raises an exception when invoked without specifying recipients" do
49
+ expect {
50
+ subject.send()
51
+ }.to raise_exception(TemplateMailer::TemplateMailerError, "No recipients specified for email.")
52
+ end
53
+
54
+ it "raises an exception when invoked with an empty recipient list" do
55
+ expect {
56
+ subject.send(recipients: [])
57
+ }.to raise_exception(TemplateMailer::TemplateMailerError, "No recipients specified for email.")
58
+ end
59
+
60
+ it "raises an exception when invoked with a blank recipient string" do
61
+ expect {
62
+ subject.send(recipients: "")
63
+ }.to raise_exception(TemplateMailer::TemplateMailerError, "No recipients specified for email.")
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,62 @@
1
+ describe TemplateMailer::Mailer do
2
+ let(:template_directory) {
3
+ File.join(Dir.getwd, "spec", "data")
4
+ }
5
+ let(:subdirectory) {
6
+ "more_templates"
7
+ }
8
+ let(:server) {
9
+ {address: "smtp.test.com",
10
+ port: "25",
11
+ user_name: "user",
12
+ password: "password",
13
+ authentication: :plain,
14
+ domain: "localhost.localdomain"}
15
+ }
16
+ subject {
17
+ TemplateMailer::Mailer.new(directory: template_directory,
18
+ server: server,
19
+ via: :smtp)
20
+ }
21
+
22
+ describe "#directory()" do
23
+ it "returns a String containing the template directory path" do
24
+ expect(subject.directory).to eq(template_directory)
25
+ end
26
+ end
27
+
28
+ describe "#subdirectory()" do
29
+ let(:subdirectory_path) {
30
+ File.join(template_directory, subdirectory)
31
+ }
32
+
33
+ it "creates a new Mailer instance pointing at the appropriate subdirectory" do
34
+ mailer = subject.subdirectory(subdirectory)
35
+ expect(mailer).not_to be_nil
36
+ expect(mailer.class).to eq(TemplateMailer::Mailer)
37
+ expect(mailer.directory).to eq(subdirectory_path)
38
+ end
39
+ end
40
+
41
+ describe "#generate_mail()" do
42
+ it "generates a MailMessage with appropriate content" do
43
+ message = subject.generate_mail("test_template_4", one: 1, two: "TWO")
44
+ expect(message).not_to be_nil
45
+ expect(message.class).to eq(TemplateMailer::MailMessage)
46
+ expect(message.html).to eq("<h1>T4: 1, 'TWO'</h1>")
47
+ expect(message.text).to eq("T4: 1, 'TWO'")
48
+ end
49
+ end
50
+
51
+ describe "#method_missing()" do
52
+ it "calls the #subdirectory() method if the method invoked matches a subdirectory name" do
53
+ expect(subject).to receive(:subdirectory).with(subdirectory.to_sym).once
54
+ subject.more_templates
55
+ end
56
+
57
+ it "calls the #generate_mail() method of the method invoked matches a template" do
58
+ expect(subject).to receive(:generate_mail).with(:test_template_2, {}).once
59
+ subject.test_template_2
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,99 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+
20
+ require "template_mailer"
21
+
22
+ RSpec.configure do |config|
23
+ # rspec-expectations config goes here. You can use an alternate
24
+ # assertion/expectation library such as wrong or the stdlib/minitest
25
+ # assertions if you prefer.
26
+ config.expect_with :rspec do |expectations|
27
+ # This option will default to `true` in RSpec 4. It makes the `description`
28
+ # and `failure_message` of custom matchers include text for helper methods
29
+ # defined using `chain`, e.g.:
30
+ # be_bigger_than(2).and_smaller_than(4).description
31
+ # # => "be bigger than 2 and smaller than 4"
32
+ # ...rather than:
33
+ # # => "be bigger than 2"
34
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
35
+ end
36
+
37
+ # rspec-mocks config goes here. You can use an alternate test double
38
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
39
+ config.mock_with :rspec do |mocks|
40
+ # Prevents you from mocking or stubbing a method that does not exist on
41
+ # a real object. This is generally recommended, and will default to
42
+ # `true` in RSpec 4.
43
+ mocks.verify_partial_doubles = true
44
+ end
45
+
46
+ # The settings below are suggested to provide a good initial experience
47
+ # with RSpec, but feel free to customize to your heart's content.
48
+ =begin
49
+ # These two settings work together to allow you to limit a spec run
50
+ # to individual examples or groups you care about by tagging them with
51
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
52
+ # get run.
53
+ config.filter_run :focus
54
+ config.run_all_when_everything_filtered = true
55
+
56
+ # Allows RSpec to persist some state between runs in order to support
57
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
58
+ # you configure your source control system to ignore this file.
59
+ config.example_status_persistence_file_path = "spec/examples.txt"
60
+
61
+ # Limits the available syntax to the non-monkey patched syntax that is
62
+ # recommended. For more details, see:
63
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
64
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
65
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
66
+ config.disable_monkey_patching!
67
+
68
+ # This setting enables warnings. It's recommended, but in some cases may
69
+ # be too noisy due to issues in dependencies.
70
+ config.warnings = true
71
+
72
+ # Many RSpec users commonly either run the entire suite or an individual
73
+ # file, and it's useful to allow more verbose output when running an
74
+ # individual spec file.
75
+ if config.files_to_run.one?
76
+ # Use the documentation formatter for detailed output,
77
+ # unless a formatter has already been configured
78
+ # (e.g. via a command-line flag).
79
+ config.default_formatter = 'doc'
80
+ end
81
+
82
+ # Print the 10 slowest examples and example groups at the
83
+ # end of the spec run, to help surface which specs are running
84
+ # particularly slow.
85
+ config.profile_examples = 10
86
+
87
+ # Run specs in random order to surface order dependencies. If you find an
88
+ # order dependency and want to debug it, you can fix the order by providing
89
+ # the seed, which is printed after each run.
90
+ # --seed 1234
91
+ config.order = :random
92
+
93
+ # Seed global randomization in this process using the `--seed` CLI option.
94
+ # Setting this allows you to use `--seed` to deterministically reproduce
95
+ # test failures related to randomization by passing the same `--seed` value
96
+ # as the one that triggered the failure.
97
+ Kernel.srand config.seed
98
+ =end
99
+ end
@@ -0,0 +1,54 @@
1
+ require "tilt/erb"
2
+
3
+ describe TemplateMailer::TemplateDirectory do
4
+ let(:path) {
5
+ File.join(Dir.getwd, "spec", "data")
6
+ }
7
+ subject {
8
+ TemplateMailer::TemplateDirectory.new(path)
9
+ }
10
+
11
+ describe "#path()" do
12
+ it "returns the path to the template directory" do
13
+ expect(subject.path).to eq(path)
14
+ end
15
+ end
16
+
17
+ describe "#template_files()" do
18
+ let(:file_paths) {
19
+ Dir.glob(File.join(path, "*.erb"))
20
+ }
21
+
22
+ it "returns a list of matching template paths for the directory" do
23
+ paths = subject.template_files
24
+ expect(paths.size).to eq(file_paths.size)
25
+ file_paths.each do |file_path|
26
+ expect(paths).to include(file_path)
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "#exists?()" do
32
+ it "returns true for a template that exists" do
33
+ expect(subject.exists?("test_template_2")).to eq(true)
34
+ end
35
+
36
+ it "returns false for a template that does not exist" do
37
+ expect(subject.exists?("does_not_exist")).to eq(false)
38
+ end
39
+ end
40
+
41
+ describe "#template_paths()" do
42
+ let(:paths) {
43
+ [File.join(path, "test_template_3.html.erb")]
44
+ }
45
+
46
+ it "returns a list of template paths given an existing template name" do
47
+ expect(subject.template_paths("test_template_3")).to eq(paths)
48
+ end
49
+
50
+ it "returns an empty array if given the name of a template that does not exist" do
51
+ expect(subject.template_paths("does_not_exist")).to eq([])
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,41 @@
1
+ describe TemplateMailer::TemplateFactory do
2
+ let(:directory) {
3
+ File.join(Dir.getwd, "spec", "data")
4
+ }
5
+ subject {
6
+ TemplateMailer::TemplateFactory.new(directory)
7
+ }
8
+
9
+ describe "#directory()" do
10
+ it "returns the path to the template directory" do
11
+ expect(subject.directory).to eq(directory)
12
+ end
13
+ end
14
+
15
+ describe "#manufacture()" do
16
+ let(:context) {
17
+ {one: 1, two: "Two"}
18
+ }
19
+ let(:templates) {
20
+ {html: "<h1>T4: 1, 'Two'</h1>",
21
+ text: "T4: 1, 'Two'",
22
+ txt: "T4: 1, 'Two'"}
23
+ }
24
+
25
+ it "returns an empty Hash if given the name of a template that does not exist" do
26
+ expect(subject.manufacture("does_not_exist")).to eq({})
27
+ end
28
+
29
+ it "generates template entries for all available template files" do
30
+ expect(subject.manufacture("test_template_4", context)).to eq(templates)
31
+ end
32
+ end
33
+
34
+ describe "#manufacture!()" do
35
+ it "raises an exception if a matching template is not found" do
36
+ expect {
37
+ subject.manufacture!("does_not_exist")
38
+ }.to raise_exception(TemplateMailer::TemplateMailerError, "Unable to locate a template with the name 'does_not_exist'.")
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'template_mailer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "template_mailer"
8
+ spec.version = TemplateMailer::VERSION
9
+ spec.authors = ["Peter Wood"]
10
+ spec.email = ["pwood@blacknorth.com"]
11
+ spec.summary = %q{A simple library that uses the Tilt and Pony libraries to generate and dispatch emails.}
12
+ spec.description = %q{Template Mailer is a library that can be used to send emails that are created from templates. It makes use of the Tilt library to support a wide variety of templating systems and the Pony library for integration with mail servers.}
13
+ spec.homepage = "https://github.com/free-beer/template_mailer"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.4"
24
+
25
+ spec.add_dependency "pony", "~> 1.11"
26
+ spec.add_dependency "tilt", "~> 2.0"
27
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: template_mailer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Peter Wood
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pony
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.11'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: tilt
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
83
+ description: Template Mailer is a library that can be used to send emails that are
84
+ created from templates. It makes use of the Tilt library to support a wide variety
85
+ of templating systems and the Pony library for integration with mail servers.
86
+ email:
87
+ - pwood@blacknorth.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - ".rspec"
94
+ - Gemfile
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - lib/template_mailer.rb
99
+ - lib/template_mailer/exceptions.rb
100
+ - lib/template_mailer/logging_helper.rb
101
+ - lib/template_mailer/mail_message.rb
102
+ - lib/template_mailer/mailer.rb
103
+ - lib/template_mailer/template_directory.rb
104
+ - lib/template_mailer/template_factory.rb
105
+ - lib/template_mailer/version.rb
106
+ - spec/data/more_templates/another_template.text.erb
107
+ - spec/data/test_template_1.txt
108
+ - spec/data/test_template_2.txt.erb
109
+ - spec/data/test_template_3.html.erb
110
+ - spec/data/test_template_4.html.erb
111
+ - spec/data/test_template_4.text.erb
112
+ - spec/data/test_template_4.txt.erb
113
+ - spec/mail_message_spec.rb
114
+ - spec/mailer_spec.rb
115
+ - spec/spec_helper.rb
116
+ - spec/template_directory_spec.rb
117
+ - spec/template_factory_spec.rb
118
+ - template_mailer.gemspec
119
+ homepage: https://github.com/free-beer/template_mailer
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.4.6
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: A simple library that uses the Tilt and Pony libraries to generate and dispatch
143
+ emails.
144
+ test_files:
145
+ - spec/data/more_templates/another_template.text.erb
146
+ - spec/data/test_template_1.txt
147
+ - spec/data/test_template_2.txt.erb
148
+ - spec/data/test_template_3.html.erb
149
+ - spec/data/test_template_4.html.erb
150
+ - spec/data/test_template_4.text.erb
151
+ - spec/data/test_template_4.txt.erb
152
+ - spec/mail_message_spec.rb
153
+ - spec/mailer_spec.rb
154
+ - spec/spec_helper.rb
155
+ - spec/template_directory_spec.rb
156
+ - spec/template_factory_spec.rb