email_templator 1.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 833f300e05c8f85170c52c44b08374e95cbaf8ed
4
+ data.tar.gz: 0498995a7808a044da9656b337f95cb25b041f31
5
+ SHA512:
6
+ metadata.gz: 70c9f08231f09140b7772327c70f11b7ca1c0d96a670a7dd217ed598f053fdeaabde93accd2d2727c2c1dceef4889aa2c828aaf4995ced0c2416e13f68a620dd
7
+ data.tar.gz: ff2097a92343ac9d222745280f45fe6bc7230dd28008983161861415c2f283eb718cbb84cb4249d6c3c9d49a89f60f73d32788f3c9238670fc948bfb11c66ce5
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.1
6
+ notifications:
7
+ email:
8
+ on_success: always
9
+ on_failure: always
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in email_templator.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Bucky Box Limited
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.
@@ -0,0 +1,37 @@
1
+ # EmailTemplator
2
+
3
+ Sanitize and parse an user-generated email template for sending.
4
+
5
+ ## Usage
6
+
7
+ ```ruby
8
+ # 1. Define an email template class which can be personalized for a given resource (e.g. a customer)
9
+ class CustomerEmailTemplate < EmailTemplator
10
+
11
+ # white-list mapping of keywords to be replaced
12
+ KEYWORDS = {
13
+ first_name: :first_name,
14
+ account_balance: :account_balance_with_currency,
15
+ email_address: :email,
16
+ }
17
+
18
+ end
19
+
20
+ # 2. Create the template
21
+ template = CustomerEmailTemplate.new "Hi {first_name}", <<-BODY
22
+ Hey {first_name}!
23
+
24
+ Here's your email: {email_address}
25
+ BODY
26
+
27
+ template.valid? #=> true
28
+
29
+ # 3. Create a personalized email from the template
30
+ customer = OpenStruct.new(first_name: "Joe", email: "joe@example.net") # typically a model
31
+ personalized_email = template.personalize(customer)
32
+ personalized_email.subject #=> "Hi Joe"
33
+ personalized_email.body #=> "Hey Joe!\n\nHere's your email: joe@example.net\n"
34
+
35
+ # 4. Send emails!
36
+ ```
37
+
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.ruby_opts = "-w"
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "email_templator"
7
+ spec.version = "1.0.0"
8
+ spec.authors = ["Cédric Félizard"]
9
+ spec.email = ["cedric@felizard.fr"]
10
+ spec.summary = %q{Sanitize and parse an user-generated email template for sending.}
11
+ spec.description = spec.summary
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.5"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rspec"
23
+ spec.add_development_dependency "simplecov"
24
+ end
@@ -0,0 +1,90 @@
1
+ # An email template which can be personalized for a given resource
2
+ class EmailTemplator
3
+
4
+ # white-list of special keywords to be replaced
5
+ KEYWORDS = {}
6
+
7
+ # 2-array with left and right keyword delimiters
8
+ DELIMITERS = %w({ })
9
+
10
+ ATTRIBUTES = [:subject, :body]
11
+
12
+ attr_reader(*ATTRIBUTES)
13
+ attr_reader :errors
14
+
15
+ def initialize(subject, body)
16
+ @subject, @body = subject, body
17
+
18
+ @errors = []
19
+ end
20
+
21
+ def valid?
22
+ ATTRIBUTES.each do |attribute|
23
+ value = public_send(attribute)
24
+ if value.nil? || value.empty?
25
+ @errors << "#{attribute.to_s.capitalize} can't be blank"
26
+ end
27
+ end
28
+
29
+ unless unknown_keywords.empty?
30
+ @errors << "Unknown keywords found: #{unknown_keywords.join(', ')}"
31
+ end
32
+
33
+ @errors.empty?
34
+ end
35
+
36
+ def personalize resource
37
+ raise ArgumentError, @errors unless valid?
38
+
39
+ resource = pre_personalize_hook resource
40
+
41
+ replace_map = self.class::KEYWORDS.inject({}) do |hash, (keyword,method)|
42
+ replace = resource.public_send(method)
43
+ hash.merge!(keyword => replace.to_s)
44
+ end.freeze
45
+
46
+ personalized = {}
47
+
48
+ ATTRIBUTES.each do |attribute|
49
+ attribute_value = public_send(attribute).dup
50
+
51
+ replace_map.each do |key, value|
52
+ attribute_value.gsub!(self.class.keyword_with_delimiters(key), value)
53
+ end
54
+
55
+ personalized[attribute] = attribute_value
56
+ end
57
+
58
+ self.class.new(personalized[:subject], personalized[:body]).freeze
59
+ end
60
+
61
+ def pre_personalize_hook resource
62
+ resource
63
+ end
64
+
65
+ def unknown_keywords
66
+ regexp = /#{Regexp.escape(self.class::DELIMITERS.first)}(.*?)#{Regexp.escape(self.class::DELIMITERS.last)}/
67
+
68
+ present_keywords = ATTRIBUTES.map do |attribute|
69
+ public_send(attribute).to_s.scan(regexp).map(&:first)
70
+ end.flatten
71
+
72
+ present_keywords - self.class.keywords
73
+ end
74
+
75
+ def self.keywords_with_delimiters
76
+ keywords.map do |keyword|
77
+ keyword_with_delimiters keyword
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def self.keyword_with_delimiters keyword
84
+ "#{self::DELIMITERS.first}#{keyword}#{self::DELIMITERS.last}"
85
+ end
86
+
87
+ def self.keywords
88
+ self::KEYWORDS.keys.map(&:to_s).sort
89
+ end
90
+ end
@@ -0,0 +1,84 @@
1
+ require "simplecov"
2
+ SimpleCov.start
3
+ SimpleCov.minimum_coverage 100
4
+
5
+ require "email_templator"
6
+
7
+ class EmailTemplatorTest < EmailTemplator
8
+ KEYWORDS = {
9
+ first_name: :name,
10
+ email: :email,
11
+ }
12
+ end
13
+
14
+ class EmailTemplatorTestWithCustomDelimiters < EmailTemplatorTest
15
+ DELIMITERS = %w(# #)
16
+ end
17
+
18
+ describe EmailTemplatorTest do
19
+ subject { EmailTemplatorTest }
20
+
21
+ describe "#valid?" do
22
+ it "validates presence of required attributes" do
23
+ template = subject.new "", ""
24
+
25
+ template.should_not be_valid
26
+ template.errors.join.should include "Subject", "Body", "blank"
27
+ end
28
+
29
+ it "validates absence of unknown attributes" do
30
+ template = subject.new "Hey", "{nope}"
31
+
32
+ template.should_not be_valid
33
+ expect(template.errors).to eq ["Unknown keywords found: nope"]
34
+ end
35
+ end
36
+
37
+ describe "#unknown_keywords" do
38
+ it "returns unknown keywords" do
39
+ template = subject.new "Hey {you}", <<-BODY
40
+ Hi {first_name},
41
+
42
+ Your are {age} years old!
43
+ BODY
44
+
45
+ template.unknown_keywords.should eq %w(you age)
46
+ end
47
+ end
48
+
49
+ describe "#personalize" do
50
+ it "replaces keywords" do
51
+ customer = double(:customer,
52
+ name: "Joe",
53
+ email: "joe@example.net",
54
+ )
55
+
56
+ template = subject.new "Hi {first_name}", <<-BODY
57
+ Hey {first_name}!
58
+
59
+ Here's your email: {email}
60
+ BODY
61
+
62
+ personalized_email = template.personalize(customer)
63
+ personalized_email.subject.should eq "Hi Joe"
64
+ personalized_email.body.should eq <<-BODY
65
+ Hey Joe!
66
+
67
+ Here's your email: joe@example.net
68
+ BODY
69
+ end
70
+ end
71
+
72
+ describe ".keywords_with_delimiters" do
73
+ it "returns a list of keywords for use in views" do
74
+ expect(subject.keywords_with_delimiters).to eq %w({email} {first_name})
75
+ end
76
+ end
77
+ end
78
+
79
+ describe EmailTemplatorTestWithCustomDelimiters do
80
+ subject { EmailTemplatorTestWithCustomDelimiters }
81
+
82
+ specify { expect(subject.keywords_with_delimiters).to eq %w(#email# #first_name#) }
83
+ end
84
+
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: email_templator
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Cédric Félizard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-10 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.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '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: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Sanitize and parse an user-generated email template for sending.
70
+ email:
71
+ - cedric@felizard.fr
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - email_templator.gemspec
83
+ - lib/email_templator.rb
84
+ - spec/email_templator_spec.rb
85
+ homepage: ''
86
+ licenses:
87
+ - MIT
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.2.2
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Sanitize and parse an user-generated email template for sending.
109
+ test_files:
110
+ - spec/email_templator_spec.rb