talktome 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 868359e1f73e9fcf52180d2217179b7ea15ad990
4
+ data.tar.gz: 49a82192411f5ca01f129e3ece14712ce1eee0be
5
+ SHA512:
6
+ metadata.gz: b485f9ec89169ba868237e9007cf9d2f46cb42fbffb5ed28e1565a5193b3e4d2047b48284343a1ca395b370cd046156479fea710784614a404268d31382ad0f6
7
+ data.tar.gz: ccb84f63e28059def408fa31365913f993cde2335b8ec62874e8de180bfa493a37fec596cddaca1765456c6ce8cf0d44eb0da39ea49daa6cb4439232226cd05e
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ # The MIT Licence
2
+
3
+ Copyright (c) 2017 - Enspirit SPRL (Bernard Lambeau)
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,3 @@
1
+ # Talktome - Talk to users easily
2
+
3
+ ...
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'path'
2
+
3
+ #
4
+ # Install all tasks found in tasks folder
5
+ #
6
+ # See .rake files there for complete documentation.
7
+ #
8
+ Dir["tasks/*.rake"].each do |taskfile|
9
+ load taskfile
10
+ end
11
+
12
+ task :default => :test
data/lib/talktome.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'yaml'
2
+ require 'path'
3
+ require 'mustache'
4
+ require 'redcarpet'
5
+ module Talktome
6
+
7
+ def redcarpet
8
+ @redcarpet ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, extensions = {})
9
+ end
10
+ module_function :redcarpet
11
+
12
+ end
13
+ require 'talktome/version'
14
+ require 'talktome/error'
15
+ require 'talktome/strategy'
16
+ require 'talktome/message'
17
+ require 'talktome/client'
@@ -0,0 +1,21 @@
1
+ module Talktome
2
+ class Client
3
+
4
+ def initialize
5
+ @strategies = {}
6
+ yield self if block_given?
7
+ end
8
+
9
+ def strategy(name, stragegy)
10
+ @strategies[name] = stragegy
11
+ end
12
+
13
+ protected
14
+
15
+ def get_handler(strategy)
16
+ @strategies[strategy]
17
+ end
18
+
19
+ end # class Client
20
+ end # module Talktome
21
+ require 'talktome/client/local'
@@ -0,0 +1,54 @@
1
+ module Talktome
2
+ class Client
3
+ class Local < Client
4
+
5
+ def initialize(folder, options = {})
6
+ raise ArgumentError, "Missing messages folder" unless folder
7
+ raise ArgumentError, "Invalid messages folder" unless Path(folder).directory?
8
+ @folder = folder
9
+ @options = options
10
+ super()
11
+ end
12
+ attr_reader :folder, :options
13
+
14
+ def talktome(message, user, tpldata, strategies)
15
+ message, handler = load_message!(message, strategies)
16
+ message = message.instantiate(tpldata)
17
+ options[:debugger].call(message, user, handler) if options[:debugger]
18
+ handler.send_message message, user
19
+ end
20
+
21
+ protected
22
+
23
+ def load_message!(identifier, strategies)
24
+ folder = self.folder/identifier
25
+ raise InvalidMessageError, "No such message `#{identifier}`" unless folder.exists?
26
+ raise InvalidMessageError, "Message `#{identifier}` should be a folder" unless folder.directory?
27
+ strategies.each do |s|
28
+ if (file = folder.glob("#{s}.*").first) && file.file?
29
+ handler = get_handler(s)
30
+ options = {}
31
+ options[:templater] = templater(s)
32
+ message = Message.new(file, options)
33
+ return [ message, handler ]
34
+ end
35
+ end
36
+ files = folder.glob("*").map{|f| f.basename.to_s }
37
+ raise InvalidMessageError, "No available strategy for `#{identifier}`\n#{files.inspect} vs. #{strategies.inspect}"
38
+ end
39
+
40
+ def templater(strategy)
41
+ return nil unless tpl_folder = options[:templates]
42
+ ->(message, src, ctype) {
43
+ if (file = tpl_folder/"#{strategy}.#{ctype}").file?
44
+ data = { metadata: message.metadata, yield: src }
45
+ Mustache.render(file.read, data)
46
+ else
47
+ src
48
+ end
49
+ }
50
+ end
51
+
52
+ end # class Client
53
+ end # class Client
54
+ end # module Talktome
@@ -0,0 +1,4 @@
1
+ module Talktome
2
+ class Error < StandardError; end
3
+ class InvalidMessageError < Error; end
4
+ end
@@ -0,0 +1,89 @@
1
+ module Talktome
2
+ class Message
3
+
4
+ def initialize(path, options = {})
5
+ @path = path
6
+ @options = options
7
+ compile
8
+ end
9
+ attr_accessor :path
10
+ attr_accessor :metadata
11
+ attr_accessor :data
12
+ attr_accessor :template_content
13
+ protected :path=, :metadata=, :data=, :template_content=
14
+
15
+ def instantiate(tpldata)
16
+ self.dup do |m|
17
+ m.metadata = {}
18
+ self.metadata.each_pair do |k, v|
19
+ m.metadata[k] = i(v, tpldata)
20
+ end
21
+ m.template_content = i(m.template_content, tpldata.merge(metadata: m.metadata))
22
+ m.data = tpldata
23
+ end
24
+ end
25
+
26
+ def dup
27
+ d = super
28
+ yield(d) if block_given?
29
+ d
30
+ end
31
+
32
+ def extension
33
+ path.ext.to_s
34
+ end
35
+
36
+ def to_text
37
+ template_it(self.template_content, :text)
38
+ end
39
+
40
+ def to_html
41
+ case extension
42
+ when 'md'
43
+ template_it(Talktome.redcarpet.render(self.template_content), :html)
44
+ else
45
+ template_it(self.template_content, :html)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ class Template < Mustache
52
+
53
+ def partial(name)
54
+ path = "#{@options[:template_path]}/#{name}.#{template_extension}"
55
+ File.read(path).force_encoding(Encoding::UTF_8)
56
+ end
57
+
58
+ def raise_on_context_miss?
59
+ true
60
+ end
61
+
62
+ end
63
+
64
+ def template_it(src, ctype)
65
+ if @options[:templater]
66
+ @options[:templater].call(self, src, ctype)
67
+ else
68
+ src
69
+ end
70
+ end
71
+
72
+ def i(tpl, tpldata)
73
+ Template.new({
74
+ template_file: self.path,
75
+ template_path: self.path.parent
76
+ }).render(tpl, tpldata)
77
+ end
78
+
79
+ def compile
80
+ raw = path.read.force_encoding(Encoding::UTF_8)
81
+ if raw =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
82
+ @metadata, @template_content = YAML::load($1), $'
83
+ else
84
+ @metadata, @template_content = {}, raw
85
+ end
86
+ end
87
+
88
+ end # module Message
89
+ end # module Talktome
@@ -0,0 +1,7 @@
1
+ module Talktome
2
+ class Strategy
3
+
4
+ end # class Strategy
5
+ end # module Talktome
6
+ require 'talktome/strategy/debug'
7
+ require 'talktome/strategy/email'
@@ -0,0 +1,21 @@
1
+ module Talktome
2
+ class Strategy
3
+ class Debug
4
+
5
+ attr_accessor :last
6
+
7
+ def clear!
8
+ @last = nil
9
+ end
10
+
11
+ def send_message(message, user)
12
+ require 'ostruct'
13
+ @last = OpenStruct.new({
14
+ message: message,
15
+ user: user
16
+ })
17
+ end
18
+
19
+ end # class Debug
20
+ end # class Strategy
21
+ end # module Talktome
@@ -0,0 +1,46 @@
1
+ require 'mail'
2
+ module Talktome
3
+ class Strategy
4
+ class Email < Strategy
5
+
6
+ def initialize(options = {}, &defaulter)
7
+ @options = options
8
+ @defaulter = defaulter
9
+ end
10
+
11
+ def send_message(message, user)
12
+ mail = base_email
13
+ mail.to = user[:email]
14
+ mail.reply_to = message.metadata["reply_to"] if message.metadata.has_key?("reply_to")
15
+ mail.subject = message.metadata["subject"]
16
+
17
+ case message.extension
18
+ when 'md', 'html', 'htm'
19
+ mail.text_part do
20
+ content_type 'text/plain; charset=UTF-8'
21
+ body message.to_text
22
+ end
23
+ mail.html_part do
24
+ content_type 'text/html; charset=UTF-8'
25
+ body message.to_html
26
+ end
27
+ when 'text'
28
+ mail.body message.to_text
29
+ else
30
+ raise "Unsupported extension `#{message.extension}`"
31
+ end
32
+
33
+ mail.deliver!
34
+ end
35
+
36
+ private
37
+
38
+ def base_email
39
+ default_email = Mail.new
40
+ @defaulter.call(default_email) if @defaulter
41
+ default_email
42
+ end
43
+
44
+ end # class Email
45
+ end # class Strategy
46
+ end # module Talktome
@@ -0,0 +1,8 @@
1
+ module Talktome
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+ end
7
+ VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ module Talktome
3
+ class Client
4
+ describe Local do
5
+
6
+ let(:strategy) {
7
+ Strategy::Debug.new
8
+ }
9
+
10
+ let(:client){
11
+ Local.new(folder, options) do |c|
12
+ c.strategy :email, strategy
13
+ end
14
+ }
15
+
16
+ let(:folder) {
17
+ Path.dir/"../fixtures"
18
+ }
19
+
20
+ let(:user) {
21
+ { email: "user@test.com" }
22
+ }
23
+
24
+ let(:tpldata) {
25
+ { who: "Test user" }
26
+ }
27
+
28
+ before(:each) {
29
+ strategy.clear!
30
+ }
31
+
32
+ context "without templates" do
33
+ let(:options) {
34
+ {}
35
+ }
36
+
37
+ it 'sends email when requested' do
38
+ client.talktome("welcome", user, tpldata, [:email])
39
+ expect(strategy.last.message).not_to be_nil
40
+ expect(strategy.last.message.to_html).to eql("<h1>Hello Test user</h1>\n\n<p>Welcome to this email example!</p>\n\n<h3>Test user</h3>\n")
41
+ end
42
+ end
43
+
44
+ context "with templates" do
45
+ let(:options) {
46
+ {
47
+ templates: Path.dir/"../fixtures/templates"
48
+ }
49
+ }
50
+
51
+ it 'sends email when requested' do
52
+ client.talktome("welcome", user, tpldata, [:email])
53
+ expect(strategy.last.message).not_to be_nil
54
+ expect(strategy.last.message.to_html).to eql("<html><title>Hello Test user</title><body><h1>Hello Test user</h1>\n\n<p>Welcome to this email example!</p>\n\n<h3>Test user</h3>\n</body></html>\n")
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1 @@
1
+ <html><title>{{metadata.subject}}</title><body>{{{yield}}}</body></html>
@@ -0,0 +1,8 @@
1
+ ---
2
+ subject: Hello {{who}}
3
+ ---
4
+ # {{metadata.subject}}
5
+
6
+ Welcome to this email example!
7
+
8
+ {{> footer}}
@@ -0,0 +1 @@
1
+ ### {{who}}
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ module Talktome
3
+ describe Message, 'initialize' do
4
+
5
+ subject{
6
+ Message.new path
7
+ }
8
+
9
+ context 'on a file with yaml frontmatter' do
10
+ let(:path) { Path.dir/'with-frontmatter.md' }
11
+
12
+ it 'works as expected' do
13
+ expect(subject.template_content).to eql("Subject: {{metadata.subject}}\n\nHello {{who}} [test]({{url}})\n")
14
+ expect(subject.metadata).to eql("hello" => "World", "subject" => "Hello {{who}}")
15
+ end
16
+ end
17
+
18
+ context 'on a file without yaml frontmatter' do
19
+ let(:path) { Path.dir/'without-frontmatter.md' }
20
+
21
+ it 'works as expected' do
22
+ expect(subject.template_content).to eql("Hello {{who}}\n")
23
+ expect(subject.metadata).to eql({})
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ module Talktome
3
+ describe Message, 'instantiate' do
4
+
5
+ subject{
6
+ Message.new(path).instantiate(data)
7
+ }
8
+
9
+ let(:path) { Path.dir/'with-frontmatter.md' }
10
+ let(:data) { { url: "http://foo", who: "bar"} }
11
+
12
+ it 'works as expected' do
13
+ expect(subject.template_content).to eql("Subject: Hello bar\n\nHello bar [test](http://foo)\n")
14
+ expect(subject.metadata).to eql("hello" => "World", "subject" => "Hello bar")
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ ---
2
+ hello: World
3
+ subject: Hello {{who}}
4
+ ---
5
+ Subject: {{metadata.subject}}
6
+
7
+ Hello {{who}} [test]({{url}})
@@ -0,0 +1 @@
1
+ Hello {{who}}
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'talktome'
3
+
4
+ module SpecHelpers
5
+ end
6
+
7
+ RSpec.configure do |c|
8
+ c.include SpecHelpers
9
+ end
data/tasks/gem.rake ADDED
@@ -0,0 +1,39 @@
1
+ require 'rubygems/package_task'
2
+
3
+ # Dynamically load the gem spec
4
+ gemspec_file = File.expand_path('../../talktome.gemspec', __FILE__)
5
+ gemspec = Kernel.eval(File.read(gemspec_file))
6
+
7
+ Gem::PackageTask.new(gemspec) do |t|
8
+
9
+ # Name of the package
10
+ t.name = gemspec.name
11
+
12
+ # Version of the package
13
+ t.version = gemspec.version
14
+
15
+ # Directory used to store the package files
16
+ t.package_dir = "pkg"
17
+
18
+ # True if a gzipped tar file (tgz) should be produced
19
+ t.need_tar = false
20
+
21
+ # True if a gzipped tar file (tar.gz) should be produced
22
+ t.need_tar_gz = false
23
+
24
+ # True if a bzip2'd tar file (tar.bz2) should be produced
25
+ t.need_tar_bz2 = false
26
+
27
+ # True if a zip file should be produced (default is false)
28
+ t.need_zip = false
29
+
30
+ # List of files to be included in the package.
31
+ t.package_files = gemspec.files
32
+
33
+ # Tar command for gzipped or bzip2ed archives.
34
+ t.tar_command = "tar"
35
+
36
+ # Zip command for zipped archives.
37
+ t.zip_command = "zip"
38
+
39
+ end
data/tasks/test.rake ADDED
@@ -0,0 +1,11 @@
1
+ namespace :test do
2
+ require "rspec/core/rake_task"
3
+
4
+ desc "Run RSpec code examples"
5
+ RSpec::Core::RakeTask.new(:unit) do |t|
6
+ t.pattern = "spec/**/test_*.rb"
7
+ t.rspec_opts = ["--color", "--backtrace"]
8
+ end
9
+
10
+ end
11
+ task :test => [:'test:unit']
metadata ADDED
@@ -0,0 +1,170 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: talktome
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Bernard Lambeau
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: path
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mail
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.6.6
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '2'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.6.6
75
+ - !ruby/object:Gem::Dependency
76
+ name: mustache
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1'
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 1.0.5
85
+ type: :runtime
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '1'
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 1.0.5
95
+ - !ruby/object:Gem::Dependency
96
+ name: redcarpet
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '3'
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '3.4'
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '3'
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '3.4'
115
+ description: Talktome helps you talk to users by email, messaging, sms, etc. It abstracts
116
+ the messaging mechanisms and lets you manage message templates easily.
117
+ email: blambeau@gmail.com
118
+ executables: []
119
+ extensions: []
120
+ extra_rdoc_files: []
121
+ files:
122
+ - Gemfile
123
+ - LICENSE.md
124
+ - README.md
125
+ - Rakefile
126
+ - lib/talktome.rb
127
+ - lib/talktome/client.rb
128
+ - lib/talktome/client/local.rb
129
+ - lib/talktome/error.rb
130
+ - lib/talktome/message.rb
131
+ - lib/talktome/strategy.rb
132
+ - lib/talktome/strategy/debug.rb
133
+ - lib/talktome/strategy/email.rb
134
+ - lib/talktome/version.rb
135
+ - spec/client/test_local.rb
136
+ - spec/fixtures/templates/email.html
137
+ - spec/fixtures/welcome/email.md
138
+ - spec/fixtures/welcome/footer.mustache
139
+ - spec/message/test_initialize.rb
140
+ - spec/message/test_instantiate.rb
141
+ - spec/message/with-frontmatter.md
142
+ - spec/message/without-frontmatter.md
143
+ - spec/spec_helper.rb
144
+ - tasks/gem.rake
145
+ - tasks/test.rake
146
+ homepage: http://github.com/enspirit/talktome
147
+ licenses:
148
+ - MIT
149
+ metadata: {}
150
+ post_install_message:
151
+ rdoc_options: []
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubyforge_project:
166
+ rubygems_version: 2.5.2.3
167
+ signing_key:
168
+ specification_version: 4
169
+ summary: Talktome helps you talk to users by email, messaging, sms, etc.
170
+ test_files: []