hootenanny 0.0.1 → 0.1.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.
Files changed (112) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +5 -0
  3. data/Gemfile +5 -7
  4. data/Gemfile.lock +46 -28
  5. data/app/controllers/hootenanny/notifications_controller.rb +31 -0
  6. data/app/controllers/hootenanny/parameters.rb +16 -0
  7. data/app/controllers/hootenanny/subscriptions_controller.rb +24 -3
  8. data/app/models/hootenanny/publish_notification.rb +90 -0
  9. data/app/models/hootenanny/subscription.rb +72 -23
  10. data/config/routes.rb +2 -1
  11. data/db/migrate/20130607182642_add_started_at_and_lease_duration_to_subscriptions.rb +15 -0
  12. data/db/migrate/20130608225621_add_hmac_secret_to_subscription.rb +5 -0
  13. data/db/migrate/20130611235218_add_publish_notifications.rb +9 -0
  14. data/db/migrate/20130612153138_add_timestamps_to_publish_notification.rb +5 -0
  15. data/db/migrate/20130705200729_add_processed_flag_to_publish_notifications.rb +6 -0
  16. data/db/migrate/20130711061329_switch_publish_notification_process_state_from_boolean_to_string.rb +11 -0
  17. data/db/migrate/20130711061558_add_index_to_notifications_state.rb +5 -0
  18. data/hootenanny.gemspec +6 -2
  19. data/lib/hootenanny/configuration.rb +43 -0
  20. data/lib/hootenanny/correspondent.rb +44 -0
  21. data/lib/hootenanny/errors.rb +42 -1
  22. data/lib/hootenanny/feed.rb +75 -0
  23. data/lib/hootenanny/feed/atom_feed.rb +18 -0
  24. data/lib/hootenanny/feed/atom_feed_item.rb +8 -0
  25. data/lib/hootenanny/feed/digest_feed.rb +14 -0
  26. data/lib/hootenanny/feed/digest_feed_item.rb +11 -0
  27. data/lib/hootenanny/feed/feed_item.rb +30 -0
  28. data/lib/hootenanny/feed/file.rb +66 -0
  29. data/lib/hootenanny/feed/json_feed.rb +48 -0
  30. data/lib/hootenanny/feed/json_feed_item.rb +8 -0
  31. data/lib/hootenanny/feed/null_feed.rb +27 -0
  32. data/lib/hootenanny/feed/rss_feed.rb +52 -0
  33. data/lib/hootenanny/feed/rss_feed_item.rb +8 -0
  34. data/lib/hootenanny/feed_store.rb +30 -0
  35. data/lib/hootenanny/feed_store/file_feed_store.rb +55 -0
  36. data/lib/hootenanny/feed_store/web_feed_store.rb +42 -0
  37. data/lib/hootenanny/hub.rb +116 -22
  38. data/lib/hootenanny/publish_notification_expiration_policy.rb +17 -0
  39. data/lib/hootenanny/request.rb +40 -0
  40. data/lib/hootenanny/request/publish_notification.rb +94 -0
  41. data/lib/hootenanny/request/subscription.rb +153 -0
  42. data/lib/hootenanny/subscription_delivery.rb +47 -0
  43. data/lib/hootenanny/topic.rb +38 -0
  44. data/lib/hootenanny/topic_synchronizer.rb +71 -0
  45. data/lib/hootenanny/uri.rb +53 -0
  46. data/lib/hootenanny/verification.rb +108 -0
  47. data/lib/hootenanny/version.rb +1 -1
  48. data/spec/dummy/config/database.yml +12 -6
  49. data/spec/dummy/db/migrate/20130607183149_add_started_at_and_lease_duration_to_subscriptions.hootenanny.rb +16 -0
  50. data/spec/dummy/db/migrate/20130608231253_add_hmac_secret_to_subscription.hootenanny.rb +6 -0
  51. data/spec/dummy/db/migrate/20130611235546_add_publish_notifications.hootenanny.rb +10 -0
  52. data/spec/dummy/db/migrate/20130612153353_add_timestamps_to_publish_notification.hootenanny.rb +6 -0
  53. data/spec/dummy/db/migrate/20130705200832_add_processed_flag_to_publish_notifications.hootenanny.rb +7 -0
  54. data/spec/dummy/db/migrate/20130711061518_switch_publish_notification_process_state_from_boolean_to_string.hootenanny.rb +12 -0
  55. data/spec/dummy/db/migrate/20130711061629_add_index_to_notifications_state.hootenanny.rb +6 -0
  56. data/spec/factories/publish_notification.rb +13 -0
  57. data/spec/factories/requests/publish_notification.rb +11 -0
  58. data/spec/factories/requests/subscription.rb +46 -0
  59. data/spec/factories/subscription.rb +14 -2
  60. data/spec/factories/verification.rb +15 -0
  61. data/spec/features/publishers/can_notify_the_hub_of_content_updates_spec.rb +58 -0
  62. data/spec/features/subscribers/are_protected_from_unwarranted_subscriptions_spec.rb +59 -0
  63. data/spec/features/subscribers/can_receive_distributions_of_topic_content_spec.rb +175 -0
  64. data/spec/features/subscribers/can_subscribe_to_a_topic_spec.rb +76 -7
  65. data/spec/fixtures/feeds/atom/97d79220f68b4bf27.atom +0 -0
  66. data/spec/fixtures/feeds/atom/sample_feed.atom +51 -0
  67. data/spec/fixtures/feeds/digest/97d79220f68b4bf27.digest +1 -0
  68. data/spec/fixtures/feeds/digest/complete_broadcasted_items/5b187098da59f077f/97d79220f68b4bf27.digest +6 -0
  69. data/spec/fixtures/feeds/digest/incomplete_broadcasted_items/5b187098da59f077f/97d79220f68b4bf27.digest +5 -0
  70. data/spec/fixtures/feeds/digest/sample_feed.digest +6 -0
  71. data/spec/fixtures/feeds/json/97d79220f68b4bf27.json +1 -0
  72. data/spec/fixtures/feeds/json/feed_with_one_item.json +21 -0
  73. data/spec/fixtures/feeds/json/sample_feed.json +30 -0
  74. data/spec/fixtures/feeds/rss/5b187098da59f077f/97d79220f68b4bf27.rss +21 -0
  75. data/spec/fixtures/feeds/rss/97d79220f68b4bf27.rss +0 -0
  76. data/spec/fixtures/feeds/rss/feed_with_one_item.rss +15 -0
  77. data/spec/fixtures/feeds/rss/minimal_feed.rss +12 -0
  78. data/spec/fixtures/feeds/rss/sample_feed.rss +22 -0
  79. data/spec/fixtures/feeds/rss/sample_feed_2.rss +22 -0
  80. data/spec/lib/hootenanny/configuration_spec.rb +7 -0
  81. data/spec/lib/hootenanny/correspondent_spec.rb +94 -0
  82. data/spec/lib/hootenanny/errors_spec.rb +21 -0
  83. data/spec/lib/hootenanny/feed/atom_feed_item_spec.rb +9 -0
  84. data/spec/lib/hootenanny/feed/atom_feed_spec.rb +40 -0
  85. data/spec/lib/hootenanny/feed/digest_feed_item_spec.rb +9 -0
  86. data/spec/lib/hootenanny/feed/digest_feed_spec.rb +40 -0
  87. data/spec/lib/hootenanny/feed/feed_item_spec.rb +49 -0
  88. data/spec/lib/hootenanny/feed/file_spec.rb +66 -0
  89. data/spec/lib/hootenanny/feed/json_feed_item_spec.rb +9 -0
  90. data/spec/lib/hootenanny/feed/json_feed_spec.rb +128 -0
  91. data/spec/lib/hootenanny/feed/null_feed_spec.rb +9 -0
  92. data/spec/lib/hootenanny/feed/rss_feed_item_spec.rb +9 -0
  93. data/spec/lib/hootenanny/feed/rss_feed_spec.rb +143 -0
  94. data/spec/lib/hootenanny/feed_spec.rb +159 -0
  95. data/spec/lib/hootenanny/feed_store/file_feed_store_spec.rb +58 -0
  96. data/spec/lib/hootenanny/feed_store/web_feed_store_spec.rb +47 -0
  97. data/spec/lib/hootenanny/feed_store_spec.rb +27 -0
  98. data/spec/lib/hootenanny/hub_spec.rb +73 -0
  99. data/spec/lib/hootenanny/publish_notification_expiration_policy_spec.rb +35 -0
  100. data/spec/lib/hootenanny/request/publish_notification_spec.rb +43 -0
  101. data/spec/lib/hootenanny/request/subscription_spec.rb +89 -0
  102. data/spec/lib/hootenanny/request_spec.rb +21 -0
  103. data/spec/lib/hootenanny/subscription_delivery_spec.rb +54 -0
  104. data/spec/lib/hootenanny/topic_spec.rb +15 -0
  105. data/spec/lib/hootenanny/topic_synchronizer_spec.rb +98 -0
  106. data/spec/lib/hootenanny/uri_spec.rb +32 -0
  107. data/spec/lib/hootenanny/verification_spec.rb +92 -0
  108. data/spec/models/hootenanny/publish_notification_spec.rb +55 -0
  109. data/spec/models/hootenanny/subscription_spec.rb +58 -19
  110. data/spec/support/verification.rb +63 -0
  111. metadata +231 -14
  112. data/spec/controllers/hootenanny/hub_spec.rb +0 -15
@@ -0,0 +1,9 @@
1
+ require 'rspectacular'
2
+ require 'hootenanny/feed/null_feed'
3
+
4
+ module Hootenanny
5
+ class Feed
6
+ describe NullFeed do
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'rspectacular'
2
+ require 'hootenanny/feed/rss_feed_item'
3
+
4
+ module Hootenanny
5
+ class Feed
6
+ describe RSSFeedItem do
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,143 @@
1
+ require 'rspectacular'
2
+ require 'hootenanny/feed'
3
+
4
+ module Hootenanny
5
+ class Feed
6
+ describe RSSFeed do
7
+ let(:rss_content_path) { ::File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss') }
8
+ let(:rss_content) { ::File.read(rss_content_path).chomp }
9
+ let(:minimal_rss_content_path) { ::File.expand_path('./spec/fixtures/feeds/rss/minimal_feed.rss') }
10
+ let(:minimal_rss_content) { ::File.read(minimal_rss_content_path).chomp }
11
+
12
+ it 'can create itself from some RSS' do
13
+ allow(RSS::Parser).to receive(:parse)
14
+ .and_return('content')
15
+
16
+ adapter = RSSFeed.from_content(rss_content)
17
+
18
+ expect(adapter).to be_a Hootenanny::Feed::RSSFeed
19
+ expect(adapter.content).to eql 'content'
20
+ expect(RSS::Parser).to have_received(:parse)
21
+ .with(rss_content)
22
+ end
23
+
24
+ it 'throws an error if the content is not parsable' do
25
+ expect { RSSFeed.from_content('<asdf') }
26
+ .to raise_error(Hootenanny::Feed::ParseError)
27
+ end
28
+
29
+ it 'can iterate over each of the items in the feed' do
30
+ adapter = RSSFeed.from_content(rss_content)
31
+
32
+ expect(adapter).to have(2).items
33
+ end
34
+
35
+ it 'can wrap each item in a RSSFeedItem' do
36
+ adapter = RSSFeed.from_content(rss_content)
37
+
38
+ expect(adapter.items.first).to be_a Hootenanny::Feed::RSSFeedItem
39
+ end
40
+
41
+ it 'it properly sets items to an empty array even if the parsed RSS is nil' do
42
+ feed = RSSFeed.from_content('')
43
+
44
+ expect(feed.items).to eql []
45
+ end
46
+
47
+ it 'can properly set content for itself from a String' do
48
+ rss = RSS::Parser.parse(minimal_rss_content)
49
+ feed = RSSFeed.from_content(rss)
50
+
51
+ expect(feed.to_s).to eql minimal_rss_content
52
+ end
53
+
54
+ it 'if it is not blank, it always returns a String containing items' do
55
+ rss = RSS::Parser.parse(minimal_rss_content)
56
+ feed = RSSFeed.from_content(rss)
57
+
58
+ expect(feed.to_s).to eql minimal_rss_content
59
+ end
60
+
61
+ it 'if it is blank' do
62
+ feed = RSSFeed.from_content('')
63
+
64
+ expect(feed.to_s).to eql ''
65
+ end
66
+
67
+ it 'it properly sets items to an empty array even if no items are in the content' do
68
+ rss = RSS::Parser.parse(minimal_rss_content)
69
+ feed = RSSFeed.from_content(rss)
70
+
71
+ expect(feed.items).to eql []
72
+ end
73
+
74
+ it 'it properly sets items to an empty array even if the parsed RSS is nil' do
75
+ feed = RSSFeed.from_content('')
76
+
77
+ expect(feed.items).to eql []
78
+ end
79
+
80
+ it 'can properly set content for itself from a String' do
81
+ rss = RSS::Parser.parse(minimal_rss_content)
82
+ feed = RSSFeed.from_content(rss)
83
+
84
+ expect(feed.content).to be_an RSS::Rss
85
+ expect(feed.content.to_s).to include rss.to_s
86
+ end
87
+
88
+ it 'can properly set content for itself from an RSS feed' do
89
+ rss = RSS::Parser.parse(minimal_rss_content)
90
+ feed = RSSFeed.from_content(rss)
91
+
92
+ expect(feed.content).to be_an RSS::Rss
93
+ expect(feed.content.to_s).to include rss.to_s
94
+ end
95
+
96
+ it 'raises an error if it does not know how to parse content' do
97
+ expect { RSSFeed.from_content(2) }.to raise_error Hootenanny::Feed::ParseError
98
+ end
99
+
100
+ it 'can determine the difference between two feeds' do
101
+ first_content_path = ::File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
102
+ first_content = ::File.read(first_content_path)
103
+
104
+ second_content_path = ::File.expand_path('./spec/fixtures/feeds/rss/feed_with_one_item.rss')
105
+ second_content = ::File.read(second_content_path)
106
+
107
+ first_feed = Feed.infer(first_content, type: :rss)
108
+ second_feed = Feed.infer(second_content, type: :rss)
109
+
110
+ difference_feed = first_feed - second_feed
111
+
112
+ expect(difference_feed.items).to have(1).item
113
+ expect(difference_feed.items).to eql [first_feed.items[1]]
114
+ expect(difference_feed.content.channel.title).to eql 'Ruby Rogues'
115
+ expect(difference_feed.items.first.send(:content).title).to eql '108 RR Ruby Trends'
116
+
117
+ expect(difference_feed.to_s).to include 'Ruby Rogues'
118
+ expect(difference_feed.to_s).to include '108 RR Ruby Trends'
119
+ expect(difference_feed.to_s).not_to include '109 RR Extreme Programming with Will Read'
120
+ end
121
+
122
+ it 'can determine the difference between two feeds of differing types' do
123
+ first_content_path = ::File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
124
+ first_content = ::File.read(first_content_path)
125
+
126
+ second_content = '{"items": ["01bfe038abb650189f78891be1581a52a7754a9be5fee7f8feeb0f7d617d3dd6"]}'
127
+
128
+ first_feed = Feed.infer(first_content, type: :rss)
129
+ second_feed = Feed.infer(second_content, type: :digest)
130
+
131
+ difference_feed = first_feed - second_feed
132
+
133
+ expect(difference_feed.items).to have(1).item
134
+ expect(difference_feed.items).to eql [first_feed.items[1]]
135
+ expect(difference_feed.content.channel.title).to eql 'Ruby Rogues'
136
+
137
+ expect(difference_feed.to_s).to include 'Ruby Rogues'
138
+ expect(difference_feed.to_s).to include '108 RR Ruby Trends'
139
+ expect(difference_feed.to_s).not_to include '109 RR Extreme Programming with Will Read'
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,159 @@
1
+ require 'rspectacular'
2
+ require 'hootenanny/feed'
3
+
4
+ module Hootenanny
5
+ describe Feed do
6
+ context 'when the content passed in is to be parsed as an RSS document' do
7
+ let(:content_path) { File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss') }
8
+ let(:content) { File.read(content_path) }
9
+ let(:type) { :rss }
10
+
11
+ it 'can properly create a feed from that' do
12
+ feed = Feed.infer(content, type: type)
13
+
14
+ expect(feed).to be_a Hootenanny::Feed::RSSFeed
15
+ end
16
+
17
+ it 'knows it is an RSS feed' do
18
+ feed = Feed.infer(content, type: type)
19
+
20
+ expect(feed.type).to eql :rss
21
+ end
22
+ end
23
+
24
+ context 'when the content passed in is to be parsed as an JSON document' do
25
+ let(:content_path) { File.expand_path('./spec/fixtures/feeds/json/sample_feed.json') }
26
+ let(:content) { File.read(content_path) }
27
+ let(:type) { :json }
28
+
29
+ it 'can properly create a feed from that' do
30
+ feed = Feed.infer(content, type: type)
31
+
32
+ expect(feed).to be_a Hootenanny::Feed::JSONFeed
33
+ end
34
+
35
+ it 'knows it is a JSON feed' do
36
+ feed = Feed.infer(content, type: type)
37
+
38
+ expect(feed.type).to eql :json
39
+ end
40
+ end
41
+
42
+ context 'when the content passed in is to be parsed as an Atom document' do
43
+ let(:content_path) { File.expand_path('./spec/fixtures/feeds/atom/sample_feed.atom') }
44
+ let(:content) { File.read(content_path) }
45
+ let(:type) { :atom }
46
+
47
+ it 'can properly create a feed from that' do
48
+ feed = Feed.infer(content, type: type)
49
+
50
+ expect(feed).to be_a Hootenanny::Feed::AtomFeed
51
+ end
52
+
53
+ it 'knows it is an Atom feed' do
54
+ feed = Feed.infer(content, type: type)
55
+
56
+ expect(feed.type).to eql :atom
57
+ end
58
+ end
59
+
60
+ context 'when the content passed in is to be parsed as an Digest document' do
61
+ let(:content_path) { File.expand_path('./spec/fixtures/feeds/digest/sample_feed.digest') }
62
+ let(:content) { File.read(content_path) }
63
+ let(:type) { :digest }
64
+
65
+ it 'can properly create a feed from that' do
66
+ feed = Feed.infer(content, type: type)
67
+
68
+ expect(feed).to be_a Hootenanny::Feed::DigestFeed
69
+ end
70
+
71
+ it 'knows it is a digest feed' do
72
+ feed = Feed.infer(content, type: type)
73
+
74
+ expect(feed.type).to eql :digest
75
+ end
76
+ end
77
+
78
+ context 'when the content passed in cannot be parsed as any known type' do
79
+ let(:type) { :unknown }
80
+
81
+ it 'can properly create a feed from that' do
82
+ expect { Feed.infer('test content', type: type) }
83
+ .to raise_error(Hootenanny::Feed::UnknownContentTypeError)
84
+ end
85
+ end
86
+
87
+ it 'can combine two feeds of the same type together' do
88
+ first_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
89
+ first_content = File.read(first_content_path)
90
+
91
+ second_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed_2.rss')
92
+ second_content = File.read(second_content_path)
93
+
94
+ first_feed = Feed.infer(first_content, type: :rss)
95
+ second_feed = Feed.infer(second_content, type: :rss)
96
+
97
+ expect(first_feed + second_feed).to have(4).items
98
+ end
99
+
100
+ it 'eliminates duplicates when combining feeds' do
101
+ first_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
102
+ first_content = File.read(first_content_path)
103
+
104
+ second_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
105
+ second_content = File.read(second_content_path)
106
+
107
+ first_feed = Feed.infer(first_content, type: :rss)
108
+ second_feed = Feed.infer(second_content, type: :rss)
109
+
110
+ expect(first_feed + second_feed).to have(2).items
111
+ end
112
+
113
+ it 'cannot combine feeds of a different type' do
114
+ first_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
115
+ first_content = File.read(first_content_path)
116
+
117
+ second_content_path = File.expand_path('./spec/fixtures/feeds/digest/sample_feed.digest')
118
+ second_content = File.read(second_content_path)
119
+
120
+ first_feed = Feed.infer(first_content, type: :rss)
121
+ second_feed = Feed.infer(second_content, type: :digest)
122
+
123
+ expect { first_feed + second_feed }.to raise_error Hootenanny::Feed::CombinationError
124
+ end
125
+
126
+ it 'can convert an RSS feed into a Digest feed' do
127
+ content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
128
+ content = File.read(content_path)
129
+
130
+ feed = Feed::RSSFeed.from_content(content)
131
+
132
+ expect(feed.to_digest_feed.to_s).to eql %Q{{"items":["01bfe038abb650189f78891be1581a52a7754a9be5fee7f8feeb0f7d617d3dd6","a97acd993db7ef4cc74022f357954d3c4b52ab659218b89a59cdd355880d4ef6"]}}
133
+ end
134
+
135
+ it 'can convert a JSON feed into a Digest feed' do
136
+ content_path = File.expand_path('./spec/fixtures/feeds/json/sample_feed.json')
137
+ content = File.read(content_path)
138
+
139
+ feed = Feed::JSONFeed.from_content(content)
140
+
141
+ expect(feed.to_digest_feed.to_s).to eql %Q{{"items":["1a6ed1752c74c2b7fcc0f43ce39e53f8f220cd85f85e339b06eb75a0f16bbea8","6d557b1225129abebcb56d4c986dc63bf644ee865c3833f52ca69e75b4275052"]}}
142
+ end
143
+
144
+ it 'can convert a Digest feed into a Digest feed' do
145
+ content_path = File.expand_path('./spec/fixtures/feeds/digest/sample_feed.digest')
146
+ content = File.read(content_path)
147
+
148
+ feed = Feed::DigestFeed.from_content(content)
149
+
150
+ expect(feed.to_digest_feed.to_s).to eql %Q{{"items":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"]}}
151
+ end
152
+
153
+ it 'can convert an empty feed into a Digest feed' do
154
+ feed = Feed::RSSFeed.from_content('')
155
+
156
+ expect(feed.to_digest_feed.to_s).to eql %Q{{"items":[]}}
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,58 @@
1
+ require 'rspectacular'
2
+ require 'hootenanny/feed_store/file_feed_store'
3
+ require 'securerandom'
4
+
5
+ module Hootenanny
6
+ class FeedStore
7
+ describe FileFeedStore do
8
+ it 'can create a feed from the retrieved RSS content', :integration do
9
+ sample_feed_location = './spec/fixtures/feeds/rss'
10
+
11
+ store = FileFeedStore.new(location: sample_feed_location)
12
+
13
+ expect(store.fetch(url: 'http://topic.com')).to be_an Hootenanny::Feed::RSSFeed
14
+ end
15
+
16
+ it 'can create a feed from the retrieved JSON content', :integration do
17
+ sample_feed_location = './spec/fixtures/feeds/json'
18
+
19
+ store = FileFeedStore.new(location: sample_feed_location)
20
+
21
+ expect(store.fetch(url: 'http://topic.com')).to be_an Hootenanny::Feed::JSONFeed
22
+ end
23
+
24
+ it 'can create a feed from the retrieved Atom content', :integration do
25
+ sample_feed_location = './spec/fixtures/feeds/atom'
26
+
27
+ store = FileFeedStore.new(location: sample_feed_location)
28
+
29
+ expect(store.fetch(url: 'http://topic.com')).to be_an Hootenanny::Feed::AtomFeed
30
+ end
31
+
32
+ it 'can create a feed from the retrieved Digest content', :integration do
33
+ sample_feed_location = './spec/fixtures/feeds/digest'
34
+
35
+ store = FileFeedStore.new(location: sample_feed_location)
36
+
37
+ expect(store.fetch(url: 'http://topic.com')).to be_an Hootenanny::Feed::DigestFeed
38
+ end
39
+
40
+ it 'can store the contents of a feed into a file' do
41
+ feed = double(to_s: 'feed contents',
42
+ type: 'feedtype')
43
+
44
+ file_base_path = "./tmp/#{SecureRandom.hex(4)}"
45
+
46
+ store = FileFeedStore.new(location: file_base_path)
47
+
48
+ store.store(feed: feed,
49
+ url: 'http://example.com',
50
+ path: 'more_pathy')
51
+
52
+ stored_file_contents = ::File.read("#{file_base_path}/more_pathy/f0e6a6a97042a4f1f.feedtype")
53
+
54
+ expect(stored_file_contents).to eql 'feed contents'
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,47 @@
1
+ require 'rspectacular'
2
+ require 'hootenanny/feed_store/web_feed_store'
3
+
4
+ module Hootenanny
5
+ class FeedStore
6
+ describe WebFeedStore do
7
+ it 'can create a feed from the retrieved RSS content', :integration do
8
+ sample_feed = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
9
+ sample_feed_content = File.read(sample_feed)
10
+
11
+ stub_request(:get, 'http://example.com')
12
+ .to_return(body: sample_feed_content,
13
+ headers: {'Content-Type' => 'application/rss+xml'})
14
+
15
+ store = WebFeedStore.new
16
+
17
+ expect(store.fetch(url: 'http://example.com')).to be_an Hootenanny::Feed::RSSFeed
18
+ end
19
+
20
+ it 'can create a feed from the retrieved JSON content', :integration do
21
+ sample_feed = File.expand_path('./spec/fixtures/feeds/json/sample_feed.json')
22
+ sample_feed_content = File.read(sample_feed)
23
+
24
+ stub_request(:get, 'http://example.com')
25
+ .to_return(body: sample_feed_content,
26
+ headers: {'Content-Type' => 'application/json'})
27
+
28
+ store = WebFeedStore.new
29
+
30
+ expect(store.fetch(url: 'http://example.com')).to be_an Hootenanny::Feed::JSONFeed
31
+ end
32
+
33
+ it 'can create a feed from the retrieved Atom content', :integration do
34
+ sample_feed = File.expand_path('./spec/fixtures/feeds/atom/sample_feed.atom')
35
+ sample_feed_content = File.read(sample_feed)
36
+
37
+ stub_request(:get, 'http://example.com')
38
+ .to_return(body: sample_feed_content,
39
+ headers: {'Content-Type' => 'application/atom+xml'})
40
+
41
+ store = WebFeedStore.new
42
+
43
+ expect(store.fetch(url: 'http://example.com')).to be_an Hootenanny::Feed::AtomFeed
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ require 'rspectacular'
2
+ require 'hootenanny/feed_store'
3
+
4
+ module Hootenanny
5
+ describe FeedStore do
6
+ it 'can create the proper feed store based on the type requested' do
7
+ allow(Hootenanny::FeedStore::WebFeedStore).to receive(:new)
8
+ .and_return('feed store')
9
+
10
+ expect(FeedStore.infer(type: :web))
11
+ .to eql 'feed store'
12
+ expect(Hootenanny::FeedStore::WebFeedStore)
13
+ .to have_received(:new)
14
+ .with(type: :web)
15
+ end
16
+
17
+ it 'throws an error if the type is not supplied' do
18
+ expect { FeedStore.infer }
19
+ .to raise_error(Hootenanny::FeedStore::InferenceError)
20
+ end
21
+
22
+ it 'throws an error if the type is not valid' do
23
+ expect { FeedStore.infer(type: :unknown) }
24
+ .to raise_error(Hootenanny::FeedStore::UnknownStorageTypeError)
25
+ end
26
+ end
27
+ end