feed2email 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: