feed2email 0.7.0 → 0.8.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,147 @@
1
+ require 'forwardable'
2
+ require 'thor'
3
+ require 'yaml'
4
+
5
+ module Feed2Email
6
+ class FeedList
7
+ class DuplicateFeedError < StandardError; end
8
+
9
+ extend Forwardable
10
+
11
+ def initialize(path)
12
+ @path = path
13
+ @dirty = false
14
+ end
15
+
16
+ def_delegators :data, :size, :each, :each_with_index, :empty?, :[]
17
+
18
+ def <<(uri)
19
+ if index = find_feed_by_uri(uri)
20
+ raise DuplicateFeedError, "Feed #{uri} already exists at index #{index}"
21
+ end
22
+
23
+ mark_dirty
24
+ data << { uri: uri, enabled: true }
25
+ end
26
+
27
+ def clear_fetch_cache(index)
28
+ mark_dirty
29
+ data[index].delete(:last_modified)
30
+ data[index].delete(:etag)
31
+ end
32
+
33
+ def delete_at(index)
34
+ if deleted = data.delete_at(index)
35
+ mark_dirty
36
+ deleted
37
+ end
38
+ end
39
+
40
+ def include?(uri)
41
+ !find_feed_by_uri(uri).nil?
42
+ end
43
+
44
+ def process
45
+ require 'feed2email/feed'
46
+
47
+ begin
48
+ each do |meta|
49
+ next unless meta[:enabled]
50
+
51
+ feed = Feed.new(meta)
52
+ processed = feed.process
53
+ sync_feed_meta(feed.meta, meta, processed)
54
+ end
55
+ ensure
56
+ smtp_connection.finalize
57
+ end
58
+
59
+ sync
60
+ end
61
+
62
+ def sync
63
+ if dirty?
64
+ open(path, 'w') {|f| f.write(to_yaml) } > 0
65
+ end
66
+ end
67
+
68
+ def to_s
69
+ return 'Empty feed list' if empty?
70
+
71
+ justify = size.to_s.size
72
+ disabled = Thor::Shell::Color.new.set_color('DISABLED', :red)
73
+
74
+ each_with_index.map do |feed, i|
75
+ '%{index}: %{disabled}%{uri}' % {
76
+ index: i.to_s.rjust(justify),
77
+ disabled: feed[:enabled] ? '' : "#{disabled} ",
78
+ uri: feed[:uri]
79
+ }
80
+ end.join("\n")
81
+ end
82
+
83
+ def toggle(index)
84
+ if data[index]
85
+ mark_dirty
86
+ data[index][:enabled] = !data[index][:enabled]
87
+ true
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def data
94
+ return @data if @data
95
+
96
+ if File.exist?(path)
97
+ @data = YAML.load(read_file)
98
+ else
99
+ @data = []
100
+ end
101
+ end
102
+
103
+ def dirty?; @dirty end
104
+
105
+ def find_feed_by_uri(uri)
106
+ data.index {|feed| feed[:uri] == uri }
107
+ end
108
+
109
+ def mark_dirty
110
+ @dirty = true
111
+ end
112
+
113
+ def path; @path end
114
+
115
+ def read_file
116
+ File.read(path)
117
+ end
118
+
119
+ def smtp_connection
120
+ Feed2Email.smtp_connection # delegate
121
+ end
122
+
123
+ def sync_feed_meta(src, dest, processed)
124
+ if processed
125
+ if src[:last_modified]
126
+ mark_dirty
127
+ dest[:last_modified] = src[:last_modified]
128
+ end
129
+
130
+ if src[:etag]
131
+ mark_dirty
132
+ dest[:etag] = src[:etag]
133
+ end
134
+ end
135
+
136
+ # Check for permanent redirection and persist
137
+ if dest[:uri] != src[:uri]
138
+ mark_dirty
139
+ dest[:uri] = src[:uri]
140
+ end
141
+ end
142
+
143
+ def to_yaml
144
+ data.to_yaml # delegate
145
+ end
146
+ end
147
+ end
@@ -1,10 +1,9 @@
1
1
  require 'net/smtp'
2
+ require 'feed2email/configurable'
2
3
 
3
4
  module Feed2Email
4
5
  class LazySMTPConnection
5
- def config
6
- Feed2Email.config # delegate
7
- end
6
+ include Configurable
8
7
 
9
8
  def connect
10
9
  smtp.start('localhost', config['smtp_user'], config['smtp_pass'],
@@ -19,11 +18,13 @@ module Feed2Email
19
18
  smtp.finish if connected?
20
19
  end
21
20
 
22
- def send_message(*args)
21
+ def sendmail(*args, &block)
23
22
  connect unless connected?
24
- smtp.send_message(*args)
23
+ smtp.sendmail(*args, &block) # delegate
25
24
  end
26
25
 
26
+ private
27
+
27
28
  def smtp
28
29
  return @smtp if @smtp
29
30
  @smtp = Net::SMTP.new(config['smtp_host'], config['smtp_port'])
@@ -0,0 +1,7 @@
1
+ module Feed2Email
2
+ module Loggable
3
+ def logger
4
+ Feed2Email.logger # delegate
5
+ end
6
+ end
7
+ end
@@ -1,19 +1,18 @@
1
1
  require 'mail'
2
+ require 'feed2email/configurable'
2
3
  require 'feed2email/version'
3
4
 
4
5
  module Feed2Email
5
6
  class Mail
7
+ include Configurable
8
+
6
9
  def initialize(entry, feed_title)
7
10
  @entry = entry
8
11
  @feed_title = feed_title
9
12
  end
10
13
 
11
14
  def send
12
- if smtp_configured?
13
- send_with_smtp
14
- else
15
- send_with_sendmail
16
- end
15
+ build_mail.deliver!
17
16
  end
18
17
 
19
18
  private
@@ -43,31 +42,37 @@ module Feed2Email
43
42
  body_html.to_markdown
44
43
  end
45
44
 
46
- def config
47
- Feed2Email.config # delegate
48
- end
49
-
50
- def entry; @entry end
51
-
52
- def feed_title; @feed_title end
53
-
54
- def mail
45
+ def build_mail
55
46
  ::Mail.new.tap do |m|
56
47
  m.from = %{"#{feed_title}" <#{config['sender']}>}
57
48
  m.to = config['recipient']
58
49
  m.subject = entry.title.strip_html
59
- m.html_part = mail_part('text/html', body_html)
60
- m.text_part = mail_part('text/plain', body_text)
61
- end.to_s
50
+ m.html_part = build_mail_part('text/html', body_html)
51
+ m.text_part = build_mail_part('text/plain', body_text)
52
+
53
+ m.delivery_method(*delivery_method_params)
54
+ end
62
55
  end
63
56
 
64
- def mail_part(content_type, body)
57
+ def build_mail_part(content_type, body)
65
58
  part = ::Mail::Part.new
66
59
  part.content_type = "#{content_type}; charset=UTF-8"
67
60
  part.body = body
68
61
  part
69
62
  end
70
63
 
64
+ def delivery_method_params
65
+ if config.smtp_configured?
66
+ [:smtp_connection, connection: Feed2Email.smtp_connection]
67
+ else
68
+ [:sendmail, location: config['sendmail_path']]
69
+ end
70
+ end
71
+
72
+ def entry; @entry end
73
+
74
+ def feed_title; @feed_title end
75
+
71
76
  def published
72
77
  return nil unless entry.author || entry.published
73
78
  text = 'Published'
@@ -75,26 +80,5 @@ module Feed2Email
75
80
  text << " at #{entry.published}" if entry.published
76
81
  text
77
82
  end
78
-
79
- def send_with_sendmail
80
- open("|#{config['sendmail_path']} #{config['recipient']}", 'w') do |f|
81
- f.write(mail)
82
- end
83
- end
84
-
85
- def send_with_smtp
86
- smtp_connection.send_message(mail, config['sender'], config['recipient'])
87
- end
88
-
89
- def smtp_configured?
90
- config['smtp_host'] &&
91
- config['smtp_port'] &&
92
- config['smtp_user'] &&
93
- config['smtp_pass']
94
- end
95
-
96
- def smtp_connection
97
- Feed2Email.smtp_connection
98
- end
99
83
  end
100
84
  end
@@ -0,0 +1,38 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ module Feed2Email
5
+ class RedirectionChecker
6
+ def initialize(uri)
7
+ @uri = uri
8
+ check
9
+ end
10
+
11
+ def location; @location end
12
+
13
+ def permanently_redirected?
14
+ redirected? && code == 301
15
+ end
16
+
17
+ private
18
+
19
+ def check
20
+ parsed_uri = URI.parse(uri)
21
+ http = Net::HTTP.new(parsed_uri.host, parsed_uri.port)
22
+ http.use_ssl = (parsed_uri.scheme == 'https')
23
+ response = http.head(parsed_uri.request_uri)
24
+ @code = response.code.to_i
25
+ @location = response['location']
26
+ end
27
+
28
+ def code; @code end
29
+
30
+ def redirected?
31
+ [301, 302].include?(code) &&
32
+ location != uri && # prevent redirection to the same location
33
+ location =~ %r{\Ahttps?://} # sanitize location
34
+ end
35
+
36
+ def uri; @uri end
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module Feed2Email
2
- VERSION = '0.7.0'
2
+ VERSION = '0.8.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feed2email
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aggelos Orfanakos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-11 00:00:00.000000000 Z
11
+ date: 2015-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: feedzirra
@@ -66,6 +66,34 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.6.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: thor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.19.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.19.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: nokogiri
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.6.5
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.6.5
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rspec
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +167,8 @@ dependencies:
139
167
  description:
140
168
  email: me@agorf.gr
141
169
  executables:
170
+ - f2e
171
+ - feed2email-migrate-feedlist
142
172
  - feed2email-migrate-history
143
173
  - feed2email
144
174
  extensions: []
@@ -147,21 +177,25 @@ files:
147
177
  - CHANGELOG.md
148
178
  - LICENSE.txt
149
179
  - README.md
150
- - TODO.md
180
+ - bin/f2e
151
181
  - bin/feed2email
182
+ - bin/feed2email-migrate-feedlist
152
183
  - bin/feed2email-migrate-history
153
184
  - lib/feed2email.rb
185
+ - lib/feed2email/cli.rb
154
186
  - lib/feed2email/config.rb
187
+ - lib/feed2email/configurable.rb
155
188
  - lib/feed2email/core_ext.rb
156
189
  - lib/feed2email/entry.rb
157
190
  - lib/feed2email/feed.rb
158
- - lib/feed2email/feed_data_file.rb
191
+ - lib/feed2email/feed_autodiscoverer.rb
159
192
  - lib/feed2email/feed_history.rb
160
- - lib/feed2email/feed_meta.rb
161
- - lib/feed2email/feeds.rb
193
+ - lib/feed2email/feed_list.rb
162
194
  - lib/feed2email/lazy_smtp_connection.rb
195
+ - lib/feed2email/loggable.rb
163
196
  - lib/feed2email/logger.rb
164
197
  - lib/feed2email/mail.rb
198
+ - lib/feed2email/redirection_checker.rb
165
199
  - lib/feed2email/version.rb
166
200
  homepage: https://github.com/agorf/feed2email
167
201
  licenses:
@@ -169,18 +203,30 @@ licenses:
169
203
  metadata: {}
170
204
  post_install_message: |2+
171
205
 
172
- WARNING! feed2email now maintains a separate history file per feed!
206
+ Important changes since version 0.6.0:
207
+
208
+ * Each feed has its own history file. Please run the provided migration script
209
+ to migrate your history before using feed2email: feed2email-migrate-history
210
+ If you don't, feed2email will think it is run for the first time and will
211
+ treat all entries as old (thus no email will be sent and you may miss some
212
+ entries).
213
+
214
+ * `sender` is a required config option. Please update your config file
215
+ (~/.feed2email/config.yml) to set it to an email address to send email from.
216
+
217
+ Important changes since version 0.8.0:
218
+
219
+ * Feed metadata is stored in the feed list (~/.feed2email/feeds.yml). Please run
220
+ the provided migration script before using feed2email:
221
+ `feed2email-migrate-feedlist`
173
222
 
174
- Please run the provided migration script `feed2email-migrate-history` before
175
- using feed2email. This will split an existing single history file to many small
176
- ones, one for each feed.
223
+ It is safe to remove any feed meta files: rm ~/.feed2email/meta-*.yml
177
224
 
178
- If history is not migrated, feed2email will think it is run for the first time
179
- and will treat all entries as old (thus no email will be sent and you may miss
180
- some entries).
225
+ * A command-line interface is available. Running feed2email without any options
226
+ will display some help text. To run it so that it processes your feed list,
227
+ issue: `feed2email process`
181
228
 
182
- WARNING! "sender" is now a required config option. Please update your config
183
- file by setting it to an email address to send email from.
229
+ Don't forget to update any cron jobs too!
184
230
 
185
231
  rdoc_options: []
186
232
  require_paths: