correole 0.0.1 → 0.0.6
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 +5 -5
- data/bin/correole +3 -0
- data/config/configuration.rb +75 -0
- data/config/database.yml +10 -0
- data/config/dependencies.rb +13 -0
- data/config/example.config.yml +21 -0
- data/config/production.html.erb +21 -0
- data/config/production.txt.erb +13 -0
- data/config/test.config.yml +18 -0
- data/config/test.html.erb +21 -0
- data/config/test.txt.erb +13 -0
- data/db/migrate/0001_create_database.rb +9 -0
- data/db/migrate/0002_create_items.rb +12 -0
- data/lib/correole/api.rb +122 -0
- data/lib/correole/feed.rb +37 -0
- data/lib/correole/item.rb +17 -0
- data/lib/correole/purge.rb +18 -0
- data/lib/correole/qputs.rb +3 -0
- data/lib/correole/send.rb +91 -0
- data/lib/correole/subscriber.rb +3 -0
- data/lib/correole/version.rb +4 -0
- metadata +40 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f8f2fe3a7de80d7791152fd8a3763666393e9126af594aa3f67b4cba3e468342
|
4
|
+
data.tar.gz: beb208a8c5f5798cd60f8ace8a62b7b949f1c363a2c85e8e2bc85267fb22de6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3b3d63f74a0be04f81a0ccee3cb48d467f3920701e6cc3cd0bf8a1e516effc8a0c8cecb3c3725e8e0d410d987811a38267dd26702abf06d2d412311a6deddb2
|
7
|
+
data.tar.gz: 32bdbf7124e7aed3474111af567c7cb8b1390d92807764285af43efe19be0c562c2dc8079667edaf07a7a3dd2857a1559ecbd5276dbb52a1d5b0086957604691
|
data/bin/correole
CHANGED
@@ -0,0 +1,75 @@
|
|
1
|
+
DEFAULT_ENV = 'production'
|
2
|
+
DEFAULT_CONFIG_FILE = 'config.yml'
|
3
|
+
|
4
|
+
ENV['RACK_ENV'] ||= DEFAULT_ENV
|
5
|
+
ENV['CONFIG_FILE'] ||= File.expand_path "../#{DEFAULT_CONFIG_FILE}", __FILE__
|
6
|
+
|
7
|
+
class Configuration
|
8
|
+
|
9
|
+
BOOLEAN_KEYS = [
|
10
|
+
'QUIET',
|
11
|
+
'DRY_RUN',
|
12
|
+
'SMTP_TTLS'
|
13
|
+
]
|
14
|
+
CONFIG_KEYS = [
|
15
|
+
'DRY_RUN_EMAIL',
|
16
|
+
'FEED',
|
17
|
+
'UNSUBSCRIBE_URI',
|
18
|
+
'CONFIRMATION_URI',
|
19
|
+
'BASE_URI',
|
20
|
+
'SUBJECT',
|
21
|
+
'FROM',
|
22
|
+
'HTML_TEMPLATE',
|
23
|
+
'PLAIN_TEMPLATE',
|
24
|
+
'SMTP_HOST',
|
25
|
+
'SMTP_PORT',
|
26
|
+
'SMTP_USER',
|
27
|
+
'SMTP_PASS',
|
28
|
+
'SMTP_AUTH',
|
29
|
+
] + BOOLEAN_KEYS
|
30
|
+
|
31
|
+
class << self
|
32
|
+
CONFIG_KEYS.each do |k|
|
33
|
+
attr_accessor k.downcase.to_sym
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.load!
|
38
|
+
|
39
|
+
YAML.load_file(ENV['CONFIG_FILE'])[ENV['RACK_ENV']].each_pair do |k, v|
|
40
|
+
ENV[k.upcase] ||= v.to_s rescue abort("Cannot load configuration key #{k}.")
|
41
|
+
end rescue qputs "Cannot load configuration file #{ENV['CONFIG_FILE']}. Using configuration given by environment."
|
42
|
+
|
43
|
+
CONFIG_KEYS.each do |k|
|
44
|
+
case k
|
45
|
+
when *BOOLEAN_KEYS
|
46
|
+
# Cannot store boolean values in ENV, thus this.
|
47
|
+
self.send("#{k.downcase}=".to_sym, ENV[k] == 'true')
|
48
|
+
when 'HTML_TEMPLATE', 'PLAIN_TEMPLATE'
|
49
|
+
file = File.expand_path "../#{ENV[k]}", __FILE__
|
50
|
+
template = File.read file rescue abort "Cannot load template #{ENV[k]}."
|
51
|
+
self.send("#{k.downcase}=".to_sym, template)
|
52
|
+
when 'SMTP_USER', 'SMTP_PASS'
|
53
|
+
# For user name and password, Mail interprets '' as an input.
|
54
|
+
# It doesn't do the same with nil.
|
55
|
+
self.send("#{k.downcase}=".to_sym, ENV[k] == '' ? nil : ENV[k])
|
56
|
+
else
|
57
|
+
self.send("#{k.downcase}=".to_sym, ENV[k])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Mail.defaults do
|
62
|
+
delivery_method :smtp,
|
63
|
+
address: Configuration.smtp_host,
|
64
|
+
port: Configuration.smtp_port,
|
65
|
+
user_name: Configuration.smtp_user,
|
66
|
+
password: Configuration.smtp_pass,
|
67
|
+
authentication: Configuration.smtp_auth,
|
68
|
+
enable_starttls_auto: Configuration.smtp_ttls
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
Configuration.load!
|
data/config/database.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'sinatra/activerecord'
|
3
|
+
require 'active_support/core_ext/hash'
|
4
|
+
require 'correole/qputs'
|
5
|
+
require 'correole/subscriber'
|
6
|
+
require 'correole/item'
|
7
|
+
require 'correole/feed'
|
8
|
+
require 'correole/api'
|
9
|
+
require 'correole/send'
|
10
|
+
require 'correole/purge'
|
11
|
+
require 'net/http'
|
12
|
+
require 'mail'
|
13
|
+
require 'configuration'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
production: &prod
|
2
|
+
quiet: false
|
3
|
+
dry_run: false
|
4
|
+
dry_run_email: your.email@mail.com
|
5
|
+
base_uri: http://newsletter.ruslanledesma.com
|
6
|
+
feed: http://ruslanledesma.com/feed.xml
|
7
|
+
unsubscribe_uri: 'http://ruslanledesma.com/unsubscribe/?email=<%= recipient %>'
|
8
|
+
confirmation_uri: http://ruslanledesma.com/unsubscribed/
|
9
|
+
subject: '<%= title %>: newsletter for <%= date %>'
|
10
|
+
from: '<%= title %> <no-reply@ruslanledesma.com>'
|
11
|
+
html_template: production.html.erb
|
12
|
+
plain_template: production.txt.erb
|
13
|
+
smtp_host:
|
14
|
+
smtp_port:
|
15
|
+
smtp_user:
|
16
|
+
smtp_pass:
|
17
|
+
smtp_auth:
|
18
|
+
smtp_ttls:
|
19
|
+
|
20
|
+
development:
|
21
|
+
<<: *prod
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<html>
|
2
|
+
<body>
|
3
|
+
<h1><%= title %></h1>
|
4
|
+
<h2>New posts</h2>
|
5
|
+
<ul>
|
6
|
+
<% for item in unsent_items %>
|
7
|
+
<li>
|
8
|
+
<h3>
|
9
|
+
<a href="<%= item.link %>"><%= item.title %></a>
|
10
|
+
</h3>
|
11
|
+
<% if item.pub_date -%>
|
12
|
+
<p><i><%= item.pub_date.to_date %></i></p>
|
13
|
+
<% end -%>
|
14
|
+
<p><%= item.description %></p>
|
15
|
+
</li>
|
16
|
+
<% end %>
|
17
|
+
</ul>
|
18
|
+
|
19
|
+
<a href="<%= unsubscribe_uri %>">Unsubscribe here.</a>
|
20
|
+
</body>
|
21
|
+
</html>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<%= title %>
|
2
|
+
|
3
|
+
New posts
|
4
|
+
<% for item in unsent_items %>
|
5
|
+
- <%= item.title %>
|
6
|
+
<% if item.pub_date -%>
|
7
|
+
<%= item.pub_date.to_date %>
|
8
|
+
<% end -%>
|
9
|
+
<%= item.link %>
|
10
|
+
|
11
|
+
<%= item.description %>
|
12
|
+
<% end %>
|
13
|
+
Unsubscribe here: <%= unsubscribe_uri %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
test:
|
2
|
+
quiet: true
|
3
|
+
dry_run: false
|
4
|
+
dry_run_email: test@mail.com
|
5
|
+
base_uri: http://test.ruslanledesma.com
|
6
|
+
feed: http://ruslanledesma.com/feed.xml # reset by env for end-to-end test
|
7
|
+
unsubscribe_uri: 'http://newsletter.ruslanledesma.com/unsubscribe/?email=<%= recipient %>'
|
8
|
+
confirmation_uri: http://newsletter.ruslanledesma.com/unsubscribed/
|
9
|
+
subject: 'Test <%= title %> - <%= date %>'
|
10
|
+
from: '<%= title %> <no-reply@ruslanledesma.com>'
|
11
|
+
html_template: test.html.erb
|
12
|
+
plain_template: test.txt.erb
|
13
|
+
smtp_host: localhost # reset by env for end-to-end test
|
14
|
+
smtp_port: 25 # reset by env for end-to-end test
|
15
|
+
smtp_user:
|
16
|
+
smtp_pass:
|
17
|
+
smtp_auth:
|
18
|
+
smtp_ttls: false
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<html>
|
2
|
+
<body>
|
3
|
+
<h1><%= title %></h1>
|
4
|
+
<h2>Items</h2>
|
5
|
+
<ul>
|
6
|
+
<% for item in unsent_items %>
|
7
|
+
<li>
|
8
|
+
<% if item.pub_date -%>
|
9
|
+
<i><%= item.pub_date.to_date %></i>
|
10
|
+
<% end -%>
|
11
|
+
<h3>
|
12
|
+
<a href="<%= item.link %>"><%= item.title %></a>
|
13
|
+
</h3>
|
14
|
+
<p><%= item.description %></p>
|
15
|
+
</li>
|
16
|
+
<% end %>
|
17
|
+
</ul>
|
18
|
+
|
19
|
+
<a href="<%= unsubscribe_uri %>">Unsubscribe here.</a>
|
20
|
+
</body>
|
21
|
+
</html>
|
data/config/test.txt.erb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateItems < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :items do |t|
|
4
|
+
t.string :title, null: false
|
5
|
+
t.string :description, null: false
|
6
|
+
t.string :link, null: false
|
7
|
+
t.timestamp :pub_date, null: true
|
8
|
+
t.index :link, unique: true
|
9
|
+
t.timestamps null: false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/correole/api.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
class Api < Sinatra::Base
|
2
|
+
|
3
|
+
UNSUBSCRIBE_PATH = '/unsubscribe'
|
4
|
+
SUBSCRIBERS_ALLOWED_METHODS = 'PUT, DELETE, OPTIONS'
|
5
|
+
SUBSCRIBERS_ALLOWED_ORIGIN = '*'
|
6
|
+
UNSUBSCRIBE_ALLOWED_METHODS = 'GET, OPTIONS'
|
7
|
+
UNSUBSCRIBE_ALLOWED_ORIGIN = '*'
|
8
|
+
|
9
|
+
set :server, :thin
|
10
|
+
enable :logging
|
11
|
+
disable :show_exceptions
|
12
|
+
# use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
13
|
+
|
14
|
+
before do
|
15
|
+
content_type 'text/plain'
|
16
|
+
end
|
17
|
+
|
18
|
+
def subscribe(params)
|
19
|
+
response.headers['Access-Control-Allow-Origin'] = SUBSCRIBERS_ALLOWED_ORIGIN
|
20
|
+
s = Subscriber.new(email: params[:email])
|
21
|
+
return 400 if not s.valid?
|
22
|
+
begin
|
23
|
+
s.save
|
24
|
+
logger.info("Subscribed #{params[:email]}.")
|
25
|
+
rescue ActiveRecord::RecordNotUnique
|
26
|
+
logger.info("Already subscribed #{params[:email]}.")
|
27
|
+
Subscriber.find_by_email(params[:email]).touch
|
28
|
+
end
|
29
|
+
[201, "#{params[:email]}\n"]
|
30
|
+
end
|
31
|
+
|
32
|
+
def unsubscribe(params)
|
33
|
+
response.headers['Access-Control-Allow-Origin'] = UNSUBSCRIBE_ALLOWED_ORIGIN
|
34
|
+
s = Subscriber.new(email: params[:email])
|
35
|
+
return 400 if not s.valid?
|
36
|
+
s = Subscriber.find_by_email(params[:email])
|
37
|
+
if s != nil
|
38
|
+
s.delete
|
39
|
+
logger.info("Unsubscribed #{params[:email]}.")
|
40
|
+
else
|
41
|
+
logger.info("Tried to unsubscribe #{params[:email]} but address is not subscribed.")
|
42
|
+
end
|
43
|
+
"#{params[:email]}\n"
|
44
|
+
end
|
45
|
+
|
46
|
+
def subscribers_method_not_allowed
|
47
|
+
response.headers['Allow'] = SUBSCRIBERS_ALLOWED_METHODS
|
48
|
+
response.headers['Access-Control-Allow-Methods'] = SUBSCRIBERS_ALLOWED_METHODS
|
49
|
+
405
|
50
|
+
end
|
51
|
+
|
52
|
+
def unsubscribe_method_not_allowed
|
53
|
+
response.headers['Allow'] = UNSUBSCRIBE_ALLOWED_METHODS
|
54
|
+
response.headers['Access-Control-Allow-Methods'] = UNSUBSCRIBE_ALLOWED_METHODS
|
55
|
+
405
|
56
|
+
end
|
57
|
+
|
58
|
+
options '/subscribers/:email' do
|
59
|
+
response.headers['Allow'] = SUBSCRIBERS_ALLOWED_METHODS
|
60
|
+
response.headers['Access-Control-Allow-Methods'] = SUBSCRIBERS_ALLOWED_METHODS
|
61
|
+
response.headers['Access-Control-Allow-Origin'] = SUBSCRIBERS_ALLOWED_ORIGIN
|
62
|
+
200
|
63
|
+
end
|
64
|
+
|
65
|
+
put '/subscribers/:email' do
|
66
|
+
subscribe(params)
|
67
|
+
end
|
68
|
+
|
69
|
+
delete '/subscribers/:email' do
|
70
|
+
unsubscribe(params)
|
71
|
+
end
|
72
|
+
|
73
|
+
[ :get,
|
74
|
+
:post,
|
75
|
+
:patch
|
76
|
+
].each do |verb|
|
77
|
+
send verb, '/subscribers/:email' do
|
78
|
+
subscribers_method_not_allowed
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
options "#{UNSUBSCRIBE_PATH}/:email" do
|
83
|
+
response.headers['Allow'] = UNSUBSCRIBE_ALLOWED_METHODS
|
84
|
+
response.headers['Access-Control-Allow-Methods'] = UNSUBSCRIBE_ALLOWED_METHODS
|
85
|
+
response.headers['Access-Control-Allow-Origin'] = UNSUBSCRIBE_ALLOWED_ORIGIN
|
86
|
+
200
|
87
|
+
end
|
88
|
+
|
89
|
+
get "#{UNSUBSCRIBE_PATH}/:email" do
|
90
|
+
r = unsubscribe(params)
|
91
|
+
return r if r.is_a? Integer
|
92
|
+
response.headers['Location'] = Configuration.confirmation_uri
|
93
|
+
[302, r]
|
94
|
+
end
|
95
|
+
|
96
|
+
[ :put,
|
97
|
+
:delete,
|
98
|
+
:post,
|
99
|
+
:patch
|
100
|
+
].each do |verb|
|
101
|
+
send verb, "#{UNSUBSCRIBE_PATH}/:email" do
|
102
|
+
unsubscribe_method_not_allowed
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
not_found do
|
107
|
+
[404, "Not found\n"]
|
108
|
+
end
|
109
|
+
|
110
|
+
error 400 do
|
111
|
+
[400, "Bad request\n"]
|
112
|
+
end
|
113
|
+
|
114
|
+
error 405 do
|
115
|
+
[405, "Method not allowed\n"]
|
116
|
+
end
|
117
|
+
|
118
|
+
error 500 do
|
119
|
+
[500, "Internal server error\n"]
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Feed
|
2
|
+
|
3
|
+
def self.get
|
4
|
+
uri = URI Configuration.feed
|
5
|
+
xml = Net::HTTP.get uri
|
6
|
+
hash = Hash.from_xml xml
|
7
|
+
return {
|
8
|
+
:title => hash['rss']['channel']['title'],
|
9
|
+
:item => hash['rss']['channel']['item'].map { |i|
|
10
|
+
pub_date = nil
|
11
|
+
pub_date = Time.parse(i['pubDate']) if i.has_key? 'pubDate'
|
12
|
+
Item.new(title: i['title'],
|
13
|
+
description: i['description'],
|
14
|
+
link: i['link'],
|
15
|
+
pub_date: pub_date)
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.split_items(feed)
|
21
|
+
split_feed = {
|
22
|
+
:title => feed[:title],
|
23
|
+
:unsent_item => [],
|
24
|
+
:sent_item => []
|
25
|
+
}
|
26
|
+
feed[:item].each do |i|
|
27
|
+
if Item.where(:link => i.link).any?
|
28
|
+
split_feed[:sent_item] << i
|
29
|
+
else
|
30
|
+
split_feed[:unsent_item] << i
|
31
|
+
end
|
32
|
+
end
|
33
|
+
return split_feed
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Item < ActiveRecord::Base
|
2
|
+
validates :title, presence: true
|
3
|
+
validates :description, presence: true
|
4
|
+
validates :link, presence: true, format: /http.+/
|
5
|
+
|
6
|
+
def ==(o)
|
7
|
+
return o.class == self.class &&
|
8
|
+
o.title == self.title &&
|
9
|
+
o.description == self.description &&
|
10
|
+
o.link == self.link &&
|
11
|
+
o.pub_date == self.pub_date &&
|
12
|
+
o.id == self.id &&
|
13
|
+
o.created_at == self.created_at &&
|
14
|
+
o.updated_at == self.updated_at
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Purge
|
2
|
+
|
3
|
+
def self.run!
|
4
|
+
qputs "Fetch feed from #{Configuration.feed}."
|
5
|
+
feed = Feed.get
|
6
|
+
unsent_items = Feed.split_items(feed)[:unsent_item]
|
7
|
+
if unsent_items.empty?
|
8
|
+
qputs 'There are no new items, exiting.'
|
9
|
+
return
|
10
|
+
end
|
11
|
+
qputs "There are #{unsent_items.length} new items. The items are the following."
|
12
|
+
unsent_items.each_with_index { |i, j| qputs "[#{j+1}] #{i.link}" }
|
13
|
+
qputs 'Purge the new items by remembering them.'
|
14
|
+
unsent_items.each { |i| i.save }
|
15
|
+
qputs 'Done.'
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Send
|
2
|
+
|
3
|
+
def self.run!
|
4
|
+
qputs "Fetch feed from #{Configuration.feed}."
|
5
|
+
feed = Feed.get
|
6
|
+
split_feed = Feed.split_items feed
|
7
|
+
if split_feed[:unsent_item].empty?
|
8
|
+
qputs 'There are no new items, exiting.'
|
9
|
+
return
|
10
|
+
end
|
11
|
+
qputs "There are #{split_feed[:unsent_item].length} new items. The items are the following."
|
12
|
+
split_feed[:unsent_item].each_with_index { |i, j| qputs "[#{j+1}] #{i.link}" }
|
13
|
+
html = compose_html split_feed
|
14
|
+
plain = compose_plain split_feed
|
15
|
+
rr = recipients
|
16
|
+
rr.each_with_index do |r, i|
|
17
|
+
html_r = personalize html, r.email
|
18
|
+
plain_r = personalize plain, r.email
|
19
|
+
qputs "[#{i+1}/#{rr.size}] Send newsletter to #{r.email}."
|
20
|
+
begin
|
21
|
+
send_out feed[:title], html_r, plain_r, r.email
|
22
|
+
rescue => exc
|
23
|
+
qputs "Could not send newsletter to #{r.email} for the following reason."
|
24
|
+
qputs exc.message
|
25
|
+
end
|
26
|
+
end
|
27
|
+
if not Configuration.dry_run
|
28
|
+
qputs 'Remember new items.'
|
29
|
+
split_feed[:unsent_item].each { |i| i.save }
|
30
|
+
end
|
31
|
+
qputs 'Done.'
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def self.recipients
|
37
|
+
if Configuration.dry_run
|
38
|
+
s = Subscriber.new(email: Configuration.dry_run_email)
|
39
|
+
return [s].each
|
40
|
+
end
|
41
|
+
return Subscriber.find_each
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.template_bindings(split_feed)
|
45
|
+
title = split_feed[:title]
|
46
|
+
title = '' if !title.is_a?(String)
|
47
|
+
unsent_items = split_feed[:unsent_item]
|
48
|
+
unsent_items = [] if !unsent_items.is_a?(Array)
|
49
|
+
sent_items = split_feed[:sent_item]
|
50
|
+
sent_items = [] if !sent_items.is_a?(Array)
|
51
|
+
unsubscribe_uri = Configuration.unsubscribe_uri
|
52
|
+
if false then unsubscribe_uri end # suppress unused variable warning
|
53
|
+
return binding
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.compose_html(split_feed)
|
57
|
+
template = Configuration.html_template
|
58
|
+
bindings = template_bindings(split_feed)
|
59
|
+
return ERB.new(template, trim_mode: '-').result(bindings)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.compose_plain(split_feed)
|
63
|
+
template = Configuration.plain_template
|
64
|
+
bindings = template_bindings(split_feed)
|
65
|
+
return ERB.new(template, trim_mode: '-').result(bindings)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.personalize(message, recipient)
|
69
|
+
return ERB.new(message).result(binding)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.send_out(title, html, plain, recipient)
|
73
|
+
date = Date.today.strftime('%a, %d %b %Y')
|
74
|
+
if false then date end # suppress unused variable warning
|
75
|
+
Mail.deliver do
|
76
|
+
to recipient
|
77
|
+
from ERB.new(Configuration.from).result(binding)
|
78
|
+
subject ERB.new(Configuration.subject).result(binding)
|
79
|
+
|
80
|
+
text_part do
|
81
|
+
body plain
|
82
|
+
end
|
83
|
+
|
84
|
+
html_part do
|
85
|
+
content_type 'text/html; charset=UTF-8'
|
86
|
+
body html
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: correole
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ruslan Ledesma Garza
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: thin
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,42 +58,42 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '6.0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '6.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: activesupport
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '6.0'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '6.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: mail
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '2.
|
89
|
+
version: '2.7'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '2.
|
96
|
+
version: '2.7'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: pg
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,42 +114,42 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '1.
|
117
|
+
version: '1.4'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '1.
|
124
|
+
version: '1.4'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: minitest
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '5.
|
131
|
+
version: '5.14'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '5.
|
138
|
+
version: '5.14'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: rack-test
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: '
|
145
|
+
version: '1.1'
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: '
|
152
|
+
version: '1.1'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: mini-smtp-server
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,14 +184,14 @@ dependencies:
|
|
184
184
|
requirements:
|
185
185
|
- - "~>"
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version: '
|
187
|
+
version: '13.0'
|
188
188
|
type: :development
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: '
|
194
|
+
version: '13.0'
|
195
195
|
description: |
|
196
196
|
Correole is a webservice that subscribes and unsubscribes readers from
|
197
197
|
a newsletter.
|
@@ -202,6 +202,25 @@ extensions: []
|
|
202
202
|
extra_rdoc_files: []
|
203
203
|
files:
|
204
204
|
- bin/correole
|
205
|
+
- config/configuration.rb
|
206
|
+
- config/database.yml
|
207
|
+
- config/dependencies.rb
|
208
|
+
- config/example.config.yml
|
209
|
+
- config/production.html.erb
|
210
|
+
- config/production.txt.erb
|
211
|
+
- config/test.config.yml
|
212
|
+
- config/test.html.erb
|
213
|
+
- config/test.txt.erb
|
214
|
+
- db/migrate/0001_create_database.rb
|
215
|
+
- db/migrate/0002_create_items.rb
|
216
|
+
- lib/correole/api.rb
|
217
|
+
- lib/correole/feed.rb
|
218
|
+
- lib/correole/item.rb
|
219
|
+
- lib/correole/purge.rb
|
220
|
+
- lib/correole/qputs.rb
|
221
|
+
- lib/correole/send.rb
|
222
|
+
- lib/correole/subscriber.rb
|
223
|
+
- lib/correole/version.rb
|
205
224
|
homepage: http://ruslanledesma.com/
|
206
225
|
licenses:
|
207
226
|
- MIT
|
@@ -209,20 +228,20 @@ metadata: {}
|
|
209
228
|
post_install_message:
|
210
229
|
rdoc_options: []
|
211
230
|
require_paths:
|
231
|
+
- config
|
212
232
|
- lib
|
213
233
|
required_ruby_version: !ruby/object:Gem::Requirement
|
214
234
|
requirements:
|
215
235
|
- - "~>"
|
216
236
|
- !ruby/object:Gem::Version
|
217
|
-
version: 2.
|
237
|
+
version: 2.7.1
|
218
238
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
239
|
requirements:
|
220
240
|
- - ">="
|
221
241
|
- !ruby/object:Gem::Version
|
222
242
|
version: '0'
|
223
243
|
requirements: []
|
224
|
-
|
225
|
-
rubygems_version: 2.5.1
|
244
|
+
rubygems_version: 3.1.2
|
226
245
|
signing_key:
|
227
246
|
specification_version: 4
|
228
247
|
summary: A newsletter webservice
|