feedbook 0.9.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +4 -6
- data/bin/feedbook +25 -11
- data/feedbook.gemspec +1 -1
- data/lib/feedbook/configuration.rb +7 -25
- data/lib/feedbook/errors.rb +0 -1
- data/lib/feedbook/factories/notifiers_factory.rb +9 -1
- data/lib/feedbook/liquid_extensions.rb +1 -0
- data/lib/feedbook/liquid_extensions/filters/googl_shortener_filter.rb +22 -0
- data/lib/feedbook/listener.rb +122 -47
- data/lib/feedbook/notification.rb +1 -0
- data/lib/feedbook/notifiers/facebook_notifier.rb +5 -6
- data/lib/feedbook/notifiers/null_notifier.rb +1 -1
- data/lib/feedbook/notifiers/twitter_notifier.rb +8 -9
- data/lib/feedbook/plugins_manager.rb +20 -0
- data/lib/feedbook/version.rb +1 -1
- data/spec/unit/lib/configuration_spec.rb +5 -5
- data/spec/unit/lib/factories/notifiers_factory_spec.rb +41 -0
- data/spec/unit/lib/liquid_extensions/filters/googl_shortener_filter_spec.rb +18 -0
- data/spec/unit/lib/listener_spec.rb +117 -1
- data/spec/unit/lib/notifiers/facebook_notifier_spec.rb +13 -1
- data/spec/unit/lib/plugins_manager_spec.rb +31 -0
- metadata +23 -17
- data/lib/feedbook/errors/notifier_notify_error.rb +0 -12
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NWM2NzUzZWNhOTIwZTE2MDhkZTQ5NTU3NGNhNTYwZmQ4ZDNiOThjOA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZmIzOTgxMmU5MWNmNzJiMjFjN2RlNzFlOGQ4MjY4NTg3Y2EzYzQ1Mw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MmM1NDJlYTY4MDUxYjM5YmFiY2RjNzNlZjhmNjRiN2Q5MTNiZTAxODEyOTUy
|
10
|
+
NDY5MjI4ZTVkMmZkZWY5Y2RlNjJjM2Y3Yjg3MmFhOTUyYzk5YzFiYThmMjNi
|
11
|
+
NWVlNGRmNzZlYTljMTM4YzhjNjIwNmNmMTcxMGI4Nzk0YjliOTU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MWI2YjRlODRkNTJmNzFmMDE4ZTBiYjdlYTdkZjBlZjE0MGVhMDcwODlmZmE1
|
14
|
+
MjUxYTFmMWJjMzYyMWVjZjc5ZTYyZmRkOGU5ZGZmNjNiOTQ0OGU4N2VjZjhi
|
15
|
+
NGU4ODNkMzU1ZTg3MjBlODc4YjdhYzRlMmVkNWFkZjNkMWZmMzM=
|
data/README.md
CHANGED
@@ -112,13 +112,11 @@ Feedbook uses Liquid for generating output. If you know Mustache it will not be
|
|
112
112
|
Because Feedbook is still in development there is many to do:
|
113
113
|
|
114
114
|
* add Google+ Notifier
|
115
|
-
|
116
|
-
* add links shortener (bit.ly or goo.gl) as a Liquid tag/filter
|
117
|
-
Twitter limits Tweet length to 140 characters, why not to shorten links and rescue too long message.
|
118
|
-
* add ability to add own Notifier without forking Feedbook project
|
119
|
-
I would like to give ability to users to create own Notifier plugins without any special development to Feedbook.
|
115
|
+
* Unfortunately Google+ does not provides API with ability to push messages to streams. Because of that I need to find a tool that will provide that feature without use of Google+.
|
120
116
|
* add more tests
|
121
|
-
|
117
|
+
* I would like to add integration tests to make sure that all features work.
|
118
|
+
* add offline mode
|
119
|
+
* I would like to add ability to run Feedbook with cron (start Feedbook, look for changes, save list of already notified posts and turn off)
|
122
120
|
|
123
121
|
## Contributing
|
124
122
|
|
data/bin/feedbook
CHANGED
@@ -10,28 +10,42 @@ include GLI::App
|
|
10
10
|
|
11
11
|
program_desc 'Feedbook is a simple gem and application that notifies about new posts and Atom/RSS updates on your social media sites (like Facebook and Twitter), e-mail or IRC.'
|
12
12
|
|
13
|
-
flag [:c,:config], default_value: 'feedbook.yml', desc: 'Feedbook configuration file location'
|
14
|
-
|
15
13
|
desc 'Parses feedbook.yml file for feeds and configuration and listen for changes in RSS/Atom feeds to notify about updates on social media sites'
|
16
14
|
command :start do |c|
|
17
15
|
|
18
|
-
c.
|
19
|
-
|
16
|
+
c.flag [:c, :config], default_value: 'feedbook.yml', desc: 'Feedbook configuration file location'
|
17
|
+
c.flag [:p, :plugins], default_value: './plugins', desc: 'Plugins directory'
|
18
|
+
c.flag [:a, :archive], default_value: 'feedbook_offline.yml', desc: 'Feedbook archive file'
|
19
|
+
|
20
|
+
c.default_desc 'Parses feedbook.yml file for feeds and configuration and listen for changes in RSS/Atom feeds to notify about updates on social media sites'
|
21
|
+
c.action do |_, options, _|
|
22
|
+
Feedbook::Listener.start(options[:config], options[:plugins])
|
23
|
+
end
|
24
|
+
|
25
|
+
c.desc 'Parses feedbook.yml file for feeds and configuration and listen for changes in RSS/Atom feeds to notify about updates on social media sites'
|
26
|
+
c.command :offline do |cc|
|
27
|
+
|
28
|
+
cc.action do |_, options, _|
|
29
|
+
Feedbook::Listener.start_offline(options[:config], options[:archive], options[:plugins])
|
30
|
+
end
|
20
31
|
end
|
21
32
|
end
|
22
33
|
|
23
34
|
desc 'Creates basic feedbook config file'
|
24
35
|
command :init do |c|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
36
|
+
|
37
|
+
c.flag [:c,:config], default_value: 'feedbook.yml', desc: 'Feedbook configuration file location'
|
38
|
+
|
39
|
+
c.action do |_, options, _|
|
40
|
+
abort 'There is already a feedbook configuration file' if File.exist?(options[:config])
|
41
|
+
File.open(options[:config], 'w') { |f| f.write(Box::FILES['feedbook']) }
|
42
|
+
puts "=> Created default feedbook configuration file: #{options[:config]}"
|
29
43
|
end
|
30
44
|
end
|
31
45
|
|
32
46
|
desc 'Outputs feedbook gem version'
|
33
47
|
command :version do |c|
|
34
|
-
c.action do |
|
48
|
+
c.action do |_, _, _|
|
35
49
|
puts "feedbook version: #{Feedbook::VERSION}"
|
36
50
|
end
|
37
51
|
end
|
@@ -67,7 +81,7 @@ configuration:
|
|
67
81
|
facebook:
|
68
82
|
token: SECRET_FACEBOOK_TOKEN
|
69
83
|
twitter:
|
70
|
-
|
71
|
-
|
84
|
+
api_key: SECRET_API_KEY
|
85
|
+
api_secret: SECRET_API_SECRET
|
72
86
|
access_token: SECRET_TWITTER_ACCESS_TOKEN
|
73
87
|
access_token_secret: SECRET_TWITTER_ACCESS_TOKEN_SECRET
|
data/feedbook.gemspec
CHANGED
@@ -26,11 +26,11 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_runtime_dependency 'box', '~> 0.1'
|
27
27
|
spec.add_runtime_dependency 'twitter', '~> 5.1'
|
28
28
|
spec.add_runtime_dependency 'mail', '~> 2.6'
|
29
|
+
spec.add_runtime_dependency 'googl', '~> 0.6'
|
29
30
|
spec.add_runtime_dependency 'irc-notify'
|
30
31
|
|
31
32
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
32
33
|
spec.add_development_dependency 'rake', '~> 10.3'
|
33
34
|
spec.add_development_dependency 'rspec'
|
34
35
|
spec.add_development_dependency 'coveralls'
|
35
|
-
spec.add_development_dependency 'pry-debugger'
|
36
36
|
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require 'feedbook/
|
1
|
+
require 'feedbook/factories/notifiers_factory'
|
2
2
|
require 'feedbook/helpers/time_interval_parser'
|
3
3
|
|
4
4
|
module Feedbook
|
5
5
|
class Configuration
|
6
|
-
attr_reader :interval
|
6
|
+
attr_reader :interval, :options
|
7
7
|
|
8
8
|
INTERVAL_FORMAT = /\A(\d+)(s|m|h|d)\z/
|
9
9
|
|
@@ -12,36 +12,18 @@ module Feedbook
|
|
12
12
|
#
|
13
13
|
# @return [NilClass] nil
|
14
14
|
def initialize(opts = {})
|
15
|
-
@interval = Helpers::TimeIntervalParser.parse
|
16
|
-
@
|
17
|
-
@facebook = opts.fetch(:facebook, nil)
|
18
|
-
@irc = opts.fetch(:irc, nil)
|
19
|
-
@mail = opts.fetch(:mail, nil)
|
15
|
+
@interval = Helpers::TimeIntervalParser.parse opts.delete('interval')
|
16
|
+
@options = opts
|
20
17
|
end
|
21
18
|
|
22
19
|
# Load notifiers configuration
|
23
20
|
#
|
24
21
|
# @return [NilClass] nil
|
25
22
|
def load_notifiers
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
unless facebook.nil?
|
31
|
-
Notifiers::FacebookNotifier.instance.load_configuration(facebook)
|
32
|
-
end
|
33
|
-
|
34
|
-
unless irc.nil?
|
35
|
-
Notifiers::IRCNotifier.instance.load_configuration(irc)
|
36
|
-
end
|
37
|
-
|
38
|
-
unless mail.nil?
|
39
|
-
Notifiers::MailNotifier.instance.load_configuration(mail)
|
23
|
+
options.each do |name, config|
|
24
|
+
notifier = Factories::NotifiersFactory.create(name)
|
25
|
+
notifier.load_configuration(config) unless notifier.nil?
|
40
26
|
end
|
41
27
|
end
|
42
|
-
|
43
|
-
private
|
44
|
-
attr_reader :twitter, :facebook, :irc, :mail
|
45
|
-
|
46
28
|
end
|
47
29
|
end
|
data/lib/feedbook/errors.rb
CHANGED
@@ -19,8 +19,16 @@ module Feedbook
|
|
19
19
|
Notifiers::FacebookNotifier.instance
|
20
20
|
when :irc, 'irc'
|
21
21
|
Notifiers::IRCNotifier.instance
|
22
|
+
when :mail, 'mail'
|
23
|
+
Notifiers::MailNotifier.instance
|
22
24
|
else
|
23
|
-
|
25
|
+
if Notifiers.const_defined?("#{type.capitalize}Notifier")
|
26
|
+
Notifiers.const_get("#{type.capitalize}Notifier").instance
|
27
|
+
elsif Notifiers.const_defined?("#{type.upcase}Notifier")
|
28
|
+
Notifiers.const_get("#{type.upcase}Notifier").instance
|
29
|
+
else
|
30
|
+
puts "notifier #{type} is not supported by Feedbook."
|
31
|
+
end
|
24
32
|
end
|
25
33
|
end
|
26
34
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'feedbook/liquid_extensions/filters/googl_shortener_filter'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'googl'
|
2
|
+
require 'liquid'
|
3
|
+
|
4
|
+
module Feedbook
|
5
|
+
module LiquidExtensions
|
6
|
+
module Filters
|
7
|
+
module GooglShortenerFilter
|
8
|
+
|
9
|
+
# Shorten links with goo.gl
|
10
|
+
# @param input [String] url
|
11
|
+
#
|
12
|
+
# @return [String] url in shorten form
|
13
|
+
def googl(input)
|
14
|
+
url = Googl.shorten(input)
|
15
|
+
url.short_url
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Liquid::Template.register_filter(Feedbook::LiquidExtensions::Filters::GooglShortenerFilter)
|
data/lib/feedbook/listener.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'timeloop'
|
3
3
|
require 'feedbook/feed'
|
4
|
+
require 'feedbook/plugins_manager'
|
4
5
|
require 'feedbook/errors'
|
5
6
|
require 'feedbook/configuration'
|
6
7
|
require 'feedbook/comparers/posts_comparer'
|
@@ -10,23 +11,16 @@ module Feedbook
|
|
10
11
|
|
11
12
|
# Starts listening on feeds and notifies if there is new post.
|
12
13
|
# @param path [String] configuration file path
|
13
|
-
|
14
|
+
# @param plugins_path [String] plugins directory path
|
15
|
+
def self.start(path, plugins_path)
|
14
16
|
handle_exceptions do
|
15
|
-
|
16
|
-
feeds, configuration = load_configuration(path)
|
17
|
-
feeds.each { |feed| feed.valid? }
|
18
|
-
puts 'completed.'
|
19
|
-
|
20
|
-
puts 'Loading notifiers... '
|
21
|
-
configuration.load_notifiers
|
22
|
-
puts 'completed.'
|
17
|
+
feeds, configuration = initialize_listener(path, plugins_path)
|
23
18
|
|
24
19
|
print 'Fetching feeds for the first use... '
|
25
20
|
observed_feeds = feeds.map do |feed|
|
26
21
|
{
|
27
|
-
feed:
|
28
|
-
|
29
|
-
new_posts: []
|
22
|
+
feed: feed,
|
23
|
+
posts: feed.fetch
|
30
24
|
}
|
31
25
|
end
|
32
26
|
puts 'completed.'
|
@@ -36,34 +30,104 @@ module Feedbook
|
|
36
30
|
|
37
31
|
puts 'Fetching feeds...'
|
38
32
|
observed_feeds.each do |feed|
|
39
|
-
|
40
|
-
|
41
|
-
difference = Comparers::PostsComparer.get_new_posts(feed[:old_posts], new_posts)
|
42
|
-
|
43
|
-
if difference.empty?
|
44
|
-
puts 'No new posts found.'
|
45
|
-
else
|
46
|
-
puts "#{difference.size} new posts found."
|
47
|
-
print 'Started sending notifications... '
|
48
|
-
end
|
49
|
-
|
50
|
-
difference.each do |post|
|
51
|
-
feed[:feed].notifications.each do |notification|
|
52
|
-
notification.notify(post)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
unless difference.empty?
|
57
|
-
puts 'completed.'
|
58
|
-
end
|
59
|
-
|
60
|
-
feed[:old_posts] = new_posts
|
61
|
-
feed[:new_posts] = []
|
33
|
+
observe_and_notify(feed)
|
62
34
|
end
|
63
35
|
end
|
64
36
|
end
|
65
37
|
end
|
66
38
|
|
39
|
+
# Starts listening on feeds and notifies if there is new post (offline mode).
|
40
|
+
# @param path [String] configuration file path
|
41
|
+
# @param plugins_path [String] plugins directory path
|
42
|
+
def self.start_offline(path, archive_path, plugins_path)
|
43
|
+
handle_exceptions do
|
44
|
+
feeds, configuration = initialize_listener(path, plugins_path)
|
45
|
+
|
46
|
+
observed_feeds = load_feeds_archive(archive_path)
|
47
|
+
|
48
|
+
if observed_feeds.blank?
|
49
|
+
print 'Fetching feeds for the first use... '
|
50
|
+
observed_feeds = feeds.map do |feed|
|
51
|
+
{
|
52
|
+
feed: feed,
|
53
|
+
posts: feed.fetch
|
54
|
+
}
|
55
|
+
end
|
56
|
+
puts 'completed.'
|
57
|
+
end
|
58
|
+
|
59
|
+
puts 'Fetching feeds...'
|
60
|
+
observed_feeds.each do |feed|
|
61
|
+
observe_and_notify(feed)
|
62
|
+
end
|
63
|
+
|
64
|
+
save_feeds_archive(archive_path, observed_feeds)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Load feeds from serialized YAML file.
|
69
|
+
# @param [String] path to YAML file with serialized objects
|
70
|
+
#
|
71
|
+
# @return [Array] Array of feeds with archived posts
|
72
|
+
def self.load_feeds_archive(path)
|
73
|
+
print 'Reading feeds from file... '
|
74
|
+
|
75
|
+
if File.exist? path
|
76
|
+
puts 'completed.'
|
77
|
+
YAML::load(File.read(path))
|
78
|
+
else
|
79
|
+
puts 'canceled. File does not exist.'
|
80
|
+
[]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Saves feeds into serialized YAML file
|
85
|
+
# @param path [String] path to file
|
86
|
+
# @param feeds [Array] Array with feeds to be saved
|
87
|
+
#
|
88
|
+
# @return [NilClass] nil
|
89
|
+
def self.save_feeds_archive(path, feeds)
|
90
|
+
print 'Saving feeds to file... '
|
91
|
+
|
92
|
+
File.open(path, 'w') do |f|
|
93
|
+
f.write YAML::dump(feeds)
|
94
|
+
end
|
95
|
+
|
96
|
+
puts 'completed.'
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# Single run of loop for listening for changes in RSS feeds and notifies about updates
|
101
|
+
# @param feed [Feedbook::Feed] requested feed to be observed (hash with feed and posts)
|
102
|
+
#
|
103
|
+
# @return [NilClass] nil
|
104
|
+
def self.observe_and_notify(feed)
|
105
|
+
new_posts = feed[:feed].fetch
|
106
|
+
|
107
|
+
difference = Comparers::PostsComparer.get_new_posts(feed[:posts], new_posts)
|
108
|
+
|
109
|
+
if difference.empty?
|
110
|
+
puts 'No new posts found.'
|
111
|
+
else
|
112
|
+
puts "#{difference.size} new posts found."
|
113
|
+
print 'Started sending notifications... '
|
114
|
+
end
|
115
|
+
|
116
|
+
difference.each do |post|
|
117
|
+
feed[:feed].notifications.each do |notification|
|
118
|
+
notification.notify(post)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
unless difference.empty?
|
123
|
+
puts 'completed.'
|
124
|
+
end
|
125
|
+
|
126
|
+
feed[:posts] = new_posts
|
127
|
+
|
128
|
+
feed
|
129
|
+
end
|
130
|
+
|
67
131
|
private
|
68
132
|
|
69
133
|
# Handle exceptions rescued from given block
|
@@ -72,23 +136,39 @@ module Feedbook
|
|
72
136
|
rescue Errors::InvalidFeedUrlError
|
73
137
|
abort 'feed url collection is not valid (contains empty or invalid urls)'
|
74
138
|
rescue Errors::InvalidIntervalFormatError
|
75
|
-
abort 'interval value in configuration is not
|
139
|
+
abort 'interval value in configuration is not valid (should be in format: "(Number)(TimeType)" where TimeType is s, m, h or d)'
|
76
140
|
rescue Errors::InvalidVariablesFormatError
|
77
141
|
abort 'invalid variables format in configuration (should be a key-value pairs)'
|
78
142
|
rescue Errors::NoConfigurationFileError => e
|
79
143
|
abort "configuration file could not be loaded: #{e.message}"
|
80
144
|
rescue Errors::NotifierConfigurationError => e
|
81
145
|
abort "notifier #{e.notifier} has invalid configuration (#{e.message})."
|
82
|
-
rescue Errors::NotifierNotifyError => e
|
83
|
-
p "notifier #{e.notifier} did not notify because of client error (#{e.message})."
|
84
146
|
rescue Errors::ParseFeedError => e
|
85
147
|
p "feed on #{e.url} could not be parsed because of fetching/parsing error."
|
86
|
-
rescue Errors::UnsupportedNotifierError => e
|
87
|
-
abort "notifier #{e.notifier} is not supported by Feedbook."
|
88
148
|
rescue Errors::TemplateSyntaxError
|
89
149
|
abort "one of your templates in configuration file is not valid."
|
90
150
|
end
|
91
151
|
|
152
|
+
# Initializes listener configuration, loads plugins and notifiers.
|
153
|
+
# @param path [String] config file path
|
154
|
+
# @param plugins_path [String] plugins directory path
|
155
|
+
#
|
156
|
+
# @return [[Array, Feedbook::Configuration]] feeds and Configuration instance
|
157
|
+
def self.initialize_listener(path, plugins_path)
|
158
|
+
print "Loading configuration from file #{path}... "
|
159
|
+
feeds, configuration = load_configuration(path)
|
160
|
+
feeds.each { |feed| feed.valid? }
|
161
|
+
puts 'completed.'
|
162
|
+
|
163
|
+
Feedbook::PluginsManager.load_plugins(plugins_path)
|
164
|
+
|
165
|
+
puts 'Loading notifiers... '
|
166
|
+
configuration.load_notifiers
|
167
|
+
puts 'completed.'
|
168
|
+
|
169
|
+
[feeds, configuration]
|
170
|
+
end
|
171
|
+
|
92
172
|
# Load configuration from given path
|
93
173
|
# @param path [String] configuration file path
|
94
174
|
#
|
@@ -107,16 +187,11 @@ module Feedbook
|
|
107
187
|
|
108
188
|
configuration_hash = config.fetch('configuration', {})
|
109
189
|
|
110
|
-
configuration = Configuration.new(
|
111
|
-
twitter: configuration_hash['twitter'],
|
112
|
-
facebook: configuration_hash['facebook'],
|
113
|
-
interval: configuration_hash['interval'],
|
114
|
-
)
|
190
|
+
configuration = Configuration.new(configuration_hash)
|
115
191
|
|
116
192
|
[feeds, configuration]
|
117
193
|
rescue Errno::ENOENT => e
|
118
194
|
raise Errors::NoConfigurationFileError.new(e)
|
119
195
|
end
|
120
|
-
|
121
196
|
end
|
122
|
-
end
|
197
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
require 'koala'
|
3
3
|
require 'feedbook/errors/notifier_configuration_error'
|
4
|
-
require 'feedbook/errors/notifier_notify_error'
|
5
4
|
|
6
5
|
module Feedbook
|
7
6
|
module Notifiers
|
@@ -21,17 +20,17 @@ module Feedbook
|
|
21
20
|
puts "New message has been notified on Facebook: #{message}"
|
22
21
|
end
|
23
22
|
rescue Koala::KoalaError => e
|
24
|
-
|
23
|
+
p "FacebookNotifier did not notify because of client error (#{e.message})."
|
25
24
|
end
|
26
25
|
|
27
26
|
# Load configuration for FacebookNotifier
|
28
|
-
# @param configuration = {} [Hash] Configuration hash (required: token)
|
29
|
-
#
|
27
|
+
# @param configuration = {} [Hash] Configuration hash (required: token, optional: app_secret)
|
28
|
+
#
|
30
29
|
# @return [NilClass] nil
|
31
30
|
# @raise [Feedbook::Errors::NotifierConfigurationError] if notifier configuration fails
|
32
31
|
def load_configuration(configuration = {})
|
33
|
-
@client = Koala::Facebook::API.new(configuration.fetch('token'))
|
34
|
-
|
32
|
+
@client = Koala::Facebook::API.new(configuration.fetch('token'), configuration.fetch('app_secret', nil))
|
33
|
+
|
35
34
|
puts 'Configuration loaded for FacebookNotifier'
|
36
35
|
rescue Koala::KoalaError => e
|
37
36
|
raise Errors::NotifierConfigurationError.new(:facebook, e.message)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
require 'twitter'
|
3
3
|
require 'feedbook/errors/notifier_configuration_error'
|
4
|
-
require 'feedbook/errors/notifier_notify_error'
|
5
4
|
|
6
5
|
module Feedbook
|
7
6
|
module Notifiers
|
@@ -10,9 +9,9 @@ module Feedbook
|
|
10
9
|
|
11
10
|
# Sends notification to Twitter
|
12
11
|
# @param message [String] message to be send to Twitter
|
13
|
-
#
|
12
|
+
#
|
14
13
|
# @return [NilClass] nil
|
15
|
-
# @raise [Feedbook::Errors::NotifierNotifyError] if notify method fails
|
14
|
+
# @raise [Feedbook::Errors::NotifierNotifyError] if notify method fails
|
16
15
|
def notify(message)
|
17
16
|
if client.nil?
|
18
17
|
puts "Message has not been notified on Twitter: #{message} because of invalid client configuration"
|
@@ -21,18 +20,18 @@ module Feedbook
|
|
21
20
|
puts "New message has been notified on Twitter: #{message}"
|
22
21
|
end
|
23
22
|
rescue Twitter::Error => e
|
24
|
-
|
23
|
+
p "TwitterNotifier did not notify because of client error (#{e.message})."
|
25
24
|
end
|
26
25
|
|
27
26
|
# Load configuration for TwitterNotifier
|
28
27
|
# @param configuration = {} [Hash] Configuration hash (required: consumer_key, consumer_secret, access_token, access_token_secret)
|
29
|
-
#
|
28
|
+
#
|
30
29
|
# @return [NilClass] nil
|
31
|
-
# @raise [Feedbook::Errors::NotifierConfigurationError] if notifier configuration fails
|
30
|
+
# @raise [Feedbook::Errors::NotifierConfigurationError] if notifier configuration fails
|
32
31
|
def load_configuration(configuration = {})
|
33
32
|
@client = Twitter::REST::Client.new do |config|
|
34
|
-
config.consumer_key = configuration.fetch('
|
35
|
-
config.consumer_secret = configuration.fetch('
|
33
|
+
config.consumer_key = configuration.fetch('api_key')
|
34
|
+
config.consumer_secret = configuration.fetch('api_secret')
|
36
35
|
config.access_token = configuration.fetch('access_token')
|
37
36
|
config.access_token_secret = configuration.fetch('access_token_secret')
|
38
37
|
end
|
@@ -46,4 +45,4 @@ module Feedbook
|
|
46
45
|
attr_reader :client
|
47
46
|
end
|
48
47
|
end
|
49
|
-
end
|
48
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Feedbook
|
2
|
+
class PluginsManager
|
3
|
+
|
4
|
+
# Loads plugins and extensions from given path.
|
5
|
+
# @param path [String] plugins directory path
|
6
|
+
#
|
7
|
+
# @return [NilClass] nil
|
8
|
+
def self.load_plugins(path)
|
9
|
+
print "Loading plugins from #{path}... "
|
10
|
+
if File.directory? path
|
11
|
+
Dir[File.join(path, '**', '*.rb')].sort.each do |f|
|
12
|
+
require f
|
13
|
+
end
|
14
|
+
puts 'completed.'
|
15
|
+
else
|
16
|
+
puts "Plugins directory could not be found in #{path}."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/feedbook/version.rb
CHANGED
@@ -4,16 +4,16 @@ describe Feedbook::Configuration do
|
|
4
4
|
|
5
5
|
let(:hash) do
|
6
6
|
{
|
7
|
-
twitter
|
8
|
-
facebook
|
9
|
-
irc
|
10
|
-
interval
|
7
|
+
'twitter' => {},
|
8
|
+
'facebook' => {},
|
9
|
+
'irc' => {},
|
10
|
+
'interval' => '5m'
|
11
11
|
}
|
12
12
|
end
|
13
13
|
|
14
14
|
before :each do
|
15
15
|
allow(Feedbook::Helpers::TimeIntervalParser).to receive(:parse).with('5m').and_return(300)
|
16
|
-
allow(Feedbook::Helpers::TimeIntervalParser).to receive(:parse).with(
|
16
|
+
allow(Feedbook::Helpers::TimeIntervalParser).to receive(:parse).with(nil).and_raise(Feedbook::Errors::InvalidIntervalFormatError.new)
|
17
17
|
end
|
18
18
|
|
19
19
|
subject { Feedbook::Configuration.new(hash) }
|
@@ -14,6 +14,11 @@ describe Feedbook::Factories::NotifiersFactory do
|
|
14
14
|
it { expect(Feedbook::Factories::NotifiersFactory.create(:irc)).to be(Feedbook::Notifiers::IRCNotifier.instance) }
|
15
15
|
end
|
16
16
|
|
17
|
+
context 'MailNotifier' do
|
18
|
+
it { expect(Feedbook::Factories::NotifiersFactory.create('mail')).to be(Feedbook::Notifiers::MailNotifier.instance) }
|
19
|
+
it { expect(Feedbook::Factories::NotifiersFactory.create(:mail)).to be(Feedbook::Notifiers::MailNotifier.instance) }
|
20
|
+
end
|
21
|
+
|
17
22
|
context 'NullNotifier' do
|
18
23
|
it { expect(Feedbook::Factories::NotifiersFactory.create('null')).to be(Feedbook::Notifiers::NullNotifier.instance) }
|
19
24
|
it { expect(Feedbook::Factories::NotifiersFactory.create(:null)).to be(Feedbook::Notifiers::NullNotifier.instance) }
|
@@ -24,5 +29,41 @@ describe Feedbook::Factories::NotifiersFactory do
|
|
24
29
|
it { expect(Feedbook::Factories::NotifiersFactory.create(:twitter)).to be(Feedbook::Notifiers::TwitterNotifier.instance) }
|
25
30
|
end
|
26
31
|
|
32
|
+
context 'Notifier is not defined in Factory' do
|
33
|
+
context 'and Notifier exists as TestNotifier' do
|
34
|
+
before :each do
|
35
|
+
expect(Feedbook::Notifiers).to receive(:const_defined?).with('TestNotifier').and_return(true)
|
36
|
+
expect(Feedbook::Notifiers).to receive(:const_get).with('TestNotifier').and_return(Feedbook::Notifiers::NullNotifier)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns instance of given Notifier' do
|
40
|
+
expect(Feedbook::Factories::NotifiersFactory.create('test')).to be(Feedbook::Notifiers::NullNotifier.instance)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'and Notifier exists as TESTNotifier' do
|
45
|
+
before :each do
|
46
|
+
expect(Feedbook::Notifiers).to receive(:const_defined?).with('TestNotifier').and_return(false)
|
47
|
+
expect(Feedbook::Notifiers).to receive(:const_defined?).with('TESTNotifier').and_return(true)
|
48
|
+
expect(Feedbook::Notifiers).to receive(:const_get).with('TESTNotifier').and_return(Feedbook::Notifiers::NullNotifier)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns instance of given Notifier' do
|
52
|
+
expect(Feedbook::Factories::NotifiersFactory.create('test')).to be(Feedbook::Notifiers::NullNotifier.instance)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'and Notifier does not exist' do
|
57
|
+
before :each do
|
58
|
+
expect(Feedbook::Notifiers).to receive(:const_defined?).with('TestNotifier').and_return(false)
|
59
|
+
expect(Feedbook::Notifiers).to receive(:const_defined?).with('TESTNotifier').and_return(false)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'does nothing' do
|
63
|
+
expect(Feedbook::Factories::NotifiersFactory.create('test')).to be(nil)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
27
68
|
end
|
28
69
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Feedbook::LiquidExtensions::Filters::GooglShortenerFilter do
|
4
|
+
|
5
|
+
describe '#googl' do
|
6
|
+
before :each do
|
7
|
+
allow(Googl).to receive(:shorten).with('https://github.com/pinoss').and_return(OpenStruct.new(short_url: 'http://goo.gl/jExQ6W'))
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'converts given link into shorten form by Googl API service' do
|
11
|
+
class TestClass
|
12
|
+
include Feedbook::LiquidExtensions::Filters::GooglShortenerFilter
|
13
|
+
end
|
14
|
+
|
15
|
+
expect(TestClass.new.googl('https://github.com/pinoss')).to eq('http://goo.gl/jExQ6W')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -15,7 +15,10 @@ describe Feedbook::Listener do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'parses hash and creates Feed instance' do
|
18
|
+
expect(Feedbook::PluginsManager).to receive(:load_plugins).with('./plugins')
|
19
|
+
|
18
20
|
allow(configuration).to receive(:load_notifiers)
|
21
|
+
|
19
22
|
feeds.each do |feed|
|
20
23
|
expect(feed).to receive(:fetch)
|
21
24
|
expect(feed).to receive(:valid?)
|
@@ -23,7 +26,120 @@ describe Feedbook::Listener do
|
|
23
26
|
|
24
27
|
expect(Object).to receive(:every).with(300).and_return(300)
|
25
28
|
|
26
|
-
subject.start('feedbook.yml')
|
29
|
+
subject.start('feedbook.yml', './plugins')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#start_offline' do
|
34
|
+
|
35
|
+
context 'there is no file with archived feeds' do
|
36
|
+
let(:configuration) { double }
|
37
|
+
let(:feeds) { [double, double, double] }
|
38
|
+
let(:feeds_with_posts) do
|
39
|
+
feeds.map do |feed|
|
40
|
+
{
|
41
|
+
feed: feed,
|
42
|
+
posts: nil
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
before :each do
|
48
|
+
allow(subject).to receive(:load_configuration).with('feedbook.yml').and_return([feeds, configuration])
|
49
|
+
allow(subject).to receive(:load_feeds_archive).with('feedbook_offline.yml').and_return([])
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'parses hash and creates Feed instance' do
|
53
|
+
expect(Feedbook::PluginsManager).to receive(:load_plugins).with('./plugins')
|
54
|
+
|
55
|
+
allow(configuration).to receive(:load_notifiers)
|
56
|
+
|
57
|
+
feeds.each do |feed|
|
58
|
+
expect(feed).to receive(:fetch)
|
59
|
+
expect(feed).to receive(:valid?)
|
60
|
+
expect(subject).to receive(:observe_and_notify).with({
|
61
|
+
feed: feed,
|
62
|
+
posts: nil
|
63
|
+
})
|
64
|
+
end
|
65
|
+
|
66
|
+
expect(subject).to receive(:save_feeds_archive).with('feedbook_offline.yml', feeds_with_posts)
|
67
|
+
|
68
|
+
subject.start_offline('feedbook.yml', 'feedbook_offline.yml', './plugins')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'there is file with archived feeds' do
|
73
|
+
let(:configuration) { double }
|
74
|
+
let(:feeds) { [double, double, double] }
|
75
|
+
|
76
|
+
before :each do
|
77
|
+
allow(subject).to receive(:load_configuration).with('feedbook.yml').and_return([feeds, configuration])
|
78
|
+
allow(subject).to receive(:load_feeds_archive).with('feedbook_offline.yml').and_return(feeds)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'parses hash and creates Feed instance' do
|
82
|
+
expect(Feedbook::PluginsManager).to receive(:load_plugins).with('./plugins')
|
83
|
+
|
84
|
+
allow(configuration).to receive(:load_notifiers)
|
85
|
+
|
86
|
+
feeds.each do |feed|
|
87
|
+
expect(feed).to receive(:valid?)
|
88
|
+
expect(subject).to receive(:observe_and_notify).with(feed)
|
89
|
+
end
|
90
|
+
|
91
|
+
expect(subject).to receive(:save_feeds_archive).with('feedbook_offline.yml', feeds)
|
92
|
+
|
93
|
+
subject.start_offline('feedbook.yml', 'feedbook_offline.yml', './plugins')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '#observe_and_notify' do
|
99
|
+
|
100
|
+
context 'there are new posts' do
|
101
|
+
let(:feed) do
|
102
|
+
{
|
103
|
+
posts: [OpenStruct.new(url: '1'), OpenStruct.new(url: '2')],
|
104
|
+
feed: double
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
let(:notifications) { [double, double] }
|
109
|
+
|
110
|
+
before :each do
|
111
|
+
expect(feed[:feed]).to receive(:fetch).and_return([OpenStruct.new(url: '1'), OpenStruct.new(url: '2'), OpenStruct.new(url: '3'), OpenStruct.new(url: '4')])
|
112
|
+
expect(feed[:feed]).to receive(:notifications).twice.and_return(notifications)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'parses hash and creates Feed instance' do
|
116
|
+
expect(Feedbook::Comparers::PostsComparer).to receive(:get_new_posts).with([OpenStruct.new(url: '1'), OpenStruct.new(url: '2')], [OpenStruct.new(url: '1'), OpenStruct.new(url: '2'), OpenStruct.new(url: '3'), OpenStruct.new(url: '4')]).and_return([OpenStruct.new(url: '3'), OpenStruct.new(url: '4')])
|
117
|
+
notifications.each do |notification|
|
118
|
+
expect(notification).to receive(:notify).with(OpenStruct.new(url: '3')).and_return(nil)
|
119
|
+
expect(notification).to receive(:notify).with(OpenStruct.new(url: '4')).and_return(nil)
|
120
|
+
end
|
121
|
+
|
122
|
+
expect(subject.observe_and_notify(feed)[:posts]).to eq([OpenStruct.new(url: '1'), OpenStruct.new(url: '2'), OpenStruct.new(url: '3'), OpenStruct.new(url: '4')])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'there are no new posts' do
|
127
|
+
let(:feed) do
|
128
|
+
{
|
129
|
+
posts: [OpenStruct.new(url: '1'), OpenStruct.new(url: '2')],
|
130
|
+
feed: double
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
before :each do
|
135
|
+
expect(feed[:feed]).to receive(:fetch).and_return([OpenStruct.new(url: '1'), OpenStruct.new(url: '2')])
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'parses hash and creates Feed instance' do
|
139
|
+
expect(Feedbook::Comparers::PostsComparer).to receive(:get_new_posts).with([OpenStruct.new(url: '1'), OpenStruct.new(url: '2')], [OpenStruct.new(url: '1'), OpenStruct.new(url: '2')]).and_return([])
|
140
|
+
|
141
|
+
expect(subject.observe_and_notify(feed)[:posts]).to eq([OpenStruct.new(url: '1'), OpenStruct.new(url: '2')])
|
142
|
+
end
|
27
143
|
end
|
28
144
|
end
|
29
145
|
end
|
@@ -8,13 +8,25 @@ describe Feedbook::Notifiers::FacebookNotifier do
|
|
8
8
|
}
|
9
9
|
end
|
10
10
|
|
11
|
+
let(:hash_with_app_token) do
|
12
|
+
{
|
13
|
+
'token' => 'token123',
|
14
|
+
'app_secret' => 'app_token_123'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
11
18
|
subject { Feedbook::Notifiers::FacebookNotifier.instance }
|
12
19
|
|
13
20
|
describe '#load_configuration' do
|
14
21
|
it 'parses configuration hash and creates client instance' do
|
15
|
-
expect(Koala::Facebook::API).to receive(:new).with('token123')
|
22
|
+
expect(Koala::Facebook::API).to receive(:new).with('token123', nil)
|
16
23
|
subject.load_configuration(hash)
|
17
24
|
end
|
25
|
+
|
26
|
+
it 'parses configuration hash and creates client instance with app secret' do
|
27
|
+
expect(Koala::Facebook::API).to receive(:new).with('token123', 'app_token_123')
|
28
|
+
subject.load_configuration(hash_with_app_token)
|
29
|
+
end
|
18
30
|
end
|
19
31
|
|
20
32
|
describe '#notify' do
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Feedbook::PluginsManager do
|
4
|
+
|
5
|
+
describe '#load_plugins' do
|
6
|
+
|
7
|
+
context 'when plugins directory exists' do
|
8
|
+
before :each do
|
9
|
+
expect(File).to receive(:directory?).with('./plugins').and_return(true)
|
10
|
+
expect(File).to receive(:join).with('./plugins', '**', '*.rb').and_return('./plugins/**/.fb')
|
11
|
+
expect(Dir).to receive(:[]).with('./plugins/**/.fb').and_return(['time'])
|
12
|
+
expect(Object).to receive(:require).with('time')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'requires all file inside plugins directory' do
|
16
|
+
Feedbook::PluginsManager.load_plugins('./plugins')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when plugins directory do not exists' do
|
21
|
+
before :each do
|
22
|
+
expect(File).to receive(:directory?).with('./plugins').and_return(false)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'does nothing' do
|
26
|
+
Feedbook::PluginsManager.load_plugins('./plugins')
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feedbook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maciej Paruszewski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: feedjira
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ~>
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '2.6'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: googl
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.6'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.6'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: irc-notify
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -192,20 +206,6 @@ dependencies:
|
|
192
206
|
- - ! '>='
|
193
207
|
- !ruby/object:Gem::Version
|
194
208
|
version: '0'
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: pry-debugger
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
198
|
-
requirements:
|
199
|
-
- - ! '>='
|
200
|
-
- !ruby/object:Gem::Version
|
201
|
-
version: '0'
|
202
|
-
type: :development
|
203
|
-
prerelease: false
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
205
|
-
requirements:
|
206
|
-
- - ! '>='
|
207
|
-
- !ruby/object:Gem::Version
|
208
|
-
version: '0'
|
209
209
|
description: Feedbook is a simple console application that allows to notify on IRC,
|
210
210
|
mail and social media (like Twitter and Facebook) about RSS/Atom updates.
|
211
211
|
email:
|
@@ -233,13 +233,14 @@ files:
|
|
233
233
|
- lib/feedbook/errors/invalid_variables_format_error.rb
|
234
234
|
- lib/feedbook/errors/no_configuration_file_error.rb
|
235
235
|
- lib/feedbook/errors/notifier_configuration_error.rb
|
236
|
-
- lib/feedbook/errors/notifier_notify_error.rb
|
237
236
|
- lib/feedbook/errors/parse_feed_error.rb
|
238
237
|
- lib/feedbook/errors/template_syntax_error.rb
|
239
238
|
- lib/feedbook/errors/unsupported_notifier_error.rb
|
240
239
|
- lib/feedbook/factories/notifiers_factory.rb
|
241
240
|
- lib/feedbook/feed.rb
|
242
241
|
- lib/feedbook/helpers/time_interval_parser.rb
|
242
|
+
- lib/feedbook/liquid_extensions.rb
|
243
|
+
- lib/feedbook/liquid_extensions/filters/googl_shortener_filter.rb
|
243
244
|
- lib/feedbook/listener.rb
|
244
245
|
- lib/feedbook/notification.rb
|
245
246
|
- lib/feedbook/notifiers.rb
|
@@ -248,6 +249,7 @@ files:
|
|
248
249
|
- lib/feedbook/notifiers/mail_notifier.rb
|
249
250
|
- lib/feedbook/notifiers/null_notifier.rb
|
250
251
|
- lib/feedbook/notifiers/twitter_notifier.rb
|
252
|
+
- lib/feedbook/plugins_manager.rb
|
251
253
|
- lib/feedbook/post.rb
|
252
254
|
- lib/feedbook/version.rb
|
253
255
|
- spec/spec_helper.rb
|
@@ -256,6 +258,7 @@ files:
|
|
256
258
|
- spec/unit/lib/factories/notifiers_factory_spec.rb
|
257
259
|
- spec/unit/lib/feed_spec.rb
|
258
260
|
- spec/unit/lib/helpers/time_interval_parser_spec.rb
|
261
|
+
- spec/unit/lib/liquid_extensions/filters/googl_shortener_filter_spec.rb
|
259
262
|
- spec/unit/lib/listener_spec.rb
|
260
263
|
- spec/unit/lib/notification_spec.rb
|
261
264
|
- spec/unit/lib/notifiers/facebook_notifier_spec.rb
|
@@ -263,6 +266,7 @@ files:
|
|
263
266
|
- spec/unit/lib/notifiers/mail_notifier_spec.rb
|
264
267
|
- spec/unit/lib/notifiers/null_notifier_spec.rb
|
265
268
|
- spec/unit/lib/notifiers/twitter_notifier_spec.rb
|
269
|
+
- spec/unit/lib/plugins_manager_spec.rb
|
266
270
|
- spec/unit/lib/post_spec.rb
|
267
271
|
homepage: https://github.com/pinoss/feedbook
|
268
272
|
licenses:
|
@@ -296,6 +300,7 @@ test_files:
|
|
296
300
|
- spec/unit/lib/factories/notifiers_factory_spec.rb
|
297
301
|
- spec/unit/lib/feed_spec.rb
|
298
302
|
- spec/unit/lib/helpers/time_interval_parser_spec.rb
|
303
|
+
- spec/unit/lib/liquid_extensions/filters/googl_shortener_filter_spec.rb
|
299
304
|
- spec/unit/lib/listener_spec.rb
|
300
305
|
- spec/unit/lib/notification_spec.rb
|
301
306
|
- spec/unit/lib/notifiers/facebook_notifier_spec.rb
|
@@ -303,4 +308,5 @@ test_files:
|
|
303
308
|
- spec/unit/lib/notifiers/mail_notifier_spec.rb
|
304
309
|
- spec/unit/lib/notifiers/null_notifier_spec.rb
|
305
310
|
- spec/unit/lib/notifiers/twitter_notifier_spec.rb
|
311
|
+
- spec/unit/lib/plugins_manager_spec.rb
|
306
312
|
- spec/unit/lib/post_spec.rb
|