buffer 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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/.bufferapprc.template +23 -0
  3. data/.gitignore +6 -0
  4. data/.rubocop.yml +18 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/API_COVERAGE.md +19 -0
  8. data/Gemfile +1 -1
  9. data/Guardfile +10 -0
  10. data/{LICENSE → LICENSE.txt} +1 -1
  11. data/README.md +62 -80
  12. data/Rakefile +25 -1
  13. data/TODO.md +3 -0
  14. data/bin/buffer +35 -0
  15. data/buffer.gemspec +26 -16
  16. data/lib/buffer/client.rb +26 -0
  17. data/lib/buffer/core.rb +64 -0
  18. data/lib/buffer/datastructure.rb +18 -0
  19. data/lib/buffer/encode.rb +33 -0
  20. data/lib/buffer/error.rb +10 -0
  21. data/lib/buffer/info.rb +10 -0
  22. data/lib/buffer/link.rb +10 -0
  23. data/lib/buffer/profile.rb +27 -0
  24. data/lib/buffer/update.rb +75 -0
  25. data/lib/buffer/user.rb +9 -0
  26. data/lib/buffer/version.rb +1 -1
  27. data/lib/buffer.rb +16 -102
  28. data/spec/fixtures/destroy.txt +10 -0
  29. data/spec/fixtures/info.txt +10 -0
  30. data/spec/fixtures/interactions_by_update_id.txt +10 -0
  31. data/spec/fixtures/link.txt +12 -0
  32. data/spec/fixtures/profile_authenticated.txt +11 -0
  33. data/spec/fixtures/profile_schedules_by_id.txt +10 -0
  34. data/spec/fixtures/profiles_by_id.txt +10 -0
  35. data/spec/fixtures/update_by_id.txt +10 -0
  36. data/spec/fixtures/update_by_id_non_auth.txt +9 -0
  37. data/spec/fixtures/updates_by_profile_id.txt +10 -0
  38. data/spec/fixtures/updates_by_profile_id_pending.txt +10 -0
  39. data/spec/fixtures/user_authenticated.txt +19 -0
  40. data/spec/lib/buffer/encode_spec.rb +42 -0
  41. data/spec/lib/buffer/link_spec.rb +22 -0
  42. data/spec/lib/buffer/profile_spec.rb +87 -0
  43. data/spec/lib/buffer/schedule_spec.rb +80 -0
  44. data/spec/lib/buffer/update_spec.rb +227 -0
  45. data/spec/lib/buffer/user_spec.rb +26 -0
  46. data/spec/lib/buffer_spec.rb +30 -0
  47. data/spec/lib/core_spec.rb +60 -0
  48. data/spec/spec_helper.rb +171 -0
  49. metadata +215 -67
  50. data/spec/buffer_spec.rb +0 -354
  51. data/spec/fixtures/create_body.txt +0 -1
  52. data/spec/fixtures/success.json +0 -1
  53. data/spec/fixtures/user.json +0 -1
  54. data/spec/helper.rb +0 -43
@@ -0,0 +1,227 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buffer::Client do
4
+ let(:client) { Buffer::Client.new("some_token") }
5
+ let(:profile_id) { "4eb854340acb04e870000010" }
6
+ let(:id) { "4eb8565e0acb04bb82000004" }
7
+
8
+ describe "updates" do
9
+ describe "#update_by_id" do
10
+
11
+ before do
12
+ stub_request(:get, "https://api.bufferapp.com/1/updates/#{ id }.json?access_token=some_token").
13
+ to_return(fixture("update_by_id.txt"))
14
+ end
15
+ it "fails without an id" do
16
+ lambda {
17
+ update = client.update_by_id}.
18
+ should raise_error(ArgumentError)
19
+ end
20
+
21
+ it "connects to the correct endpoint" do
22
+ client.update_by_id(id)
23
+ end
24
+
25
+ it "returns a well formed update rash" do
26
+ client.update_by_id(id).sent_at.should eq(1320744001)
27
+ end
28
+
29
+ end
30
+
31
+
32
+ describe "#updates_by_profile_id" do
33
+ it "requires an id arg" do
34
+ lambda { client.updates_by_profile_id }.
35
+ should raise_error(ArgumentError)
36
+ end
37
+
38
+ it "fails without a :status arg" do
39
+ lambda { client.updates_by_profile_id(profile_id)}.
40
+ should raise_error(Buffer::Error::MissingStatus)
41
+ end
42
+
43
+ it "connects to the correct endpoint" do
44
+ url = "https://api.bufferapp.com/1/profiles/4eb854340acb04e870000010/updates/pending.json?access_token=some_token"
45
+
46
+ stub_request(:get, url).
47
+ to_return(fixture('updates_by_profile_id_pending.txt'))
48
+ client.updates_by_profile_id(profile_id, status: :pending).
49
+ total.should eq(1)
50
+ end
51
+
52
+ it "utilizes the optional params" do
53
+ url = "https://api.bufferapp.com/1/profiles/4eb854340acb04e870000010/updates/pending.json?access_token=some_token&count=3&page=2"
54
+
55
+ stub_request(:get, url).
56
+ to_return(fixture('updates_by_profile_id_pending.txt'))
57
+ client.updates_by_profile_id(profile_id, status: :pending, page: 2, count: 3).
58
+ total.should eq(1)
59
+ end
60
+ end
61
+
62
+ describe "#interactions_by_update_id" do
63
+ let(:url) { "https://api.bufferapp.com/1/updates/4ecda476542f7ee521000006/interactions.json?access_token=some_token&page=2" }
64
+ let(:id) { "4ecda476542f7ee521000006" }
65
+
66
+ before do
67
+ stub_request(:get, url).
68
+ to_return(fixture("interactions_by_update_id.txt"))
69
+ end
70
+
71
+ it "requires an id" do
72
+ lambda { client.interactions_by_update_id(page: 2) }.
73
+ should raise_error(Buffer::Error::InvalidIdLength)
74
+ end
75
+
76
+ it "allows optional params" do
77
+ response =<<EOF
78
+ {
79
+ "total":2,
80
+ "interactions":[
81
+ {
82
+ "_id":"50f98310c5ac415d7f2e74fd",
83
+ "created_at":1358509258,
84
+ "event":"favorite",
85
+ "id":"50f98310c5ac415d7f2e74fd",
86
+ "interaction_id":"292235127847788544",
87
+ "user":{
88
+ "username":"Crispy Potatoes",
89
+ "followers":160,
90
+ "avatar":"http:\/\/si0.twimg.com\/profile_images\/...",
91
+ "avatar_https":"https:\/\/si0.twimg.com\/profile_images\/...",
92
+ "twitter_id":"70712344376"
93
+ }
94
+ },
95
+ {
96
+ "_id":"50f8623ac5ac415d7f1d4f77",
97
+ "created_at":1358454592,
98
+ "event":"retweet",
99
+ "id":"50f8623ac5ac415d7f1d4f77",
100
+ "interaction_id":"292005842654461953",
101
+ "user":{
102
+ "username":"Lucky Number 8",
103
+ "followers":36079,
104
+ "avatar":"http:\/\/si0.twimg.com\/profile_images\/2901468678\/...",
105
+ "avatar_https":"https:\/\/si0.twimg.com\/profile_images\/2901468678\/...",
106
+ "twitter_id":"1423444249"
107
+ }
108
+ }
109
+ ]
110
+ }
111
+ EOF
112
+ stub_request(:get, "https://api.bufferapp.com/1/updates/4ecda476542f7ee521000006/interactions.json?access_token=some_token&count=3&event=favorite&page=2").
113
+ with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Faraday v0.9.1'}).
114
+ to_return(:status => 200, :body => response, :headers => {})
115
+ client.interactions_by_update_id(id, page: 2, count: 3, event: "favorite")
116
+ end
117
+ end
118
+
119
+ describe "#check_id" do
120
+ it "fails if id is not 24 chars" do
121
+ stub_request(:get, "https://api.bufferapp.com/1/updates/4eb8565e0acb04bb82000004X.json?access_token=some_token").
122
+ to_return(:status => 200, :body => "", :headers => {})
123
+ id = "4eb8565e0acb04bb82000004X"
124
+ lambda { client.update_by_id(id) }.
125
+ should raise_error(Buffer::Error::InvalidIdLength)
126
+ end
127
+
128
+ it "fails if id is not numbers and a-f" do
129
+ stub_request(:get, "https://api.bufferapp.com/1/updates/4eb8565e0acb04bb8200000X.json?access_token=some_token").
130
+ to_return(:status => 200, :body => "", :headers => {})
131
+ id = "4eb8565e0acb04bb8200000X"
132
+ lambda { client.update_by_id(id) }.
133
+ should raise_error(Buffer::Error::InvalidIdContent)
134
+ end
135
+ end
136
+
137
+ describe "#reorder_updates" do
138
+ it "connects to appropriate endpoint" do
139
+ id_no = "4ecda256512f7ee521000001"
140
+ order_hash = { order: [id_no, id_no, id_no] }
141
+ stub_request(:post, %r{https://api\.bufferapp\.com/1/profiles/4ecda256512f7ee521000001/updates/reorder\.json\?access_token=.*}).
142
+ with(:body => {"order"=>["4ecda256512f7ee521000001", "4ecda256512f7ee521000001", "4ecda256512f7ee521000001"]},
143
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.9.1'}).
144
+ to_return(:status => 200, :body => reorder_updates_body_response, :headers => {})
145
+ client.reorder_updates(id_no, order_hash)
146
+ end
147
+ end
148
+
149
+ describe "#shuffle_updates" do
150
+ it "connects to appropriate endpoint" do
151
+ id_no = "4ecda256512f7ee521000001"
152
+ stub_request(:post, %r{https://api\.bufferapp\.com/1/profiles/4ecda256512f7ee521000001/updates/shuffle\.json\?access_token=.*}).
153
+ with(:body => {"count"=>"10"}).
154
+ to_return(:status => 200, :body => '{"success": true,
155
+ "updates": [],
156
+ "time_to_shuffle":0.0041220188140869}')
157
+ client.shuffle_updates(id_no, count: 10)
158
+ end
159
+ end
160
+
161
+ describe "#share_update" do
162
+ it "should connect to correct endpoint" do
163
+ stub_request(:post, %r{https://api\.bufferapp\.com/1/updates/4ecda256512f7ee521000001/share\.json\?access_token=.*}).
164
+ to_return(:status => 200, :body => '{"success": true}', :headers => {})
165
+ update_id = "4ecda256512f7ee521000001"
166
+ client.share_update(update_id)
167
+ end
168
+ end
169
+
170
+ describe "#create_update" do
171
+
172
+ let(:body_content) do {text: "Text for an update",
173
+ profile_ids: [
174
+ "4eb854340acb04e870000010",
175
+ "4eb9276e0acb04bb81000067"
176
+ ]}
177
+ end
178
+
179
+ let(:url) { %r{https://api\.bufferapp\.com/1/updates/create\.json\?access_token=.*} }
180
+
181
+ context "should create an update" do
182
+ it "when only required params are present" do
183
+ stub_request(:post, url).
184
+ with(:body => body_content).
185
+ to_return(:status => 200, :body => create_update_return_body, :headers => {})
186
+ client.create_update(body: body_content)
187
+ end
188
+ it "when optional params are included" do
189
+ body_content[:media] = {}
190
+ body_content[:media][:link] = "http://google.com"
191
+ body_content[:media][:description] = "Google Homepage"
192
+ stub_request(:post, url).
193
+ with(:body => body_content).
194
+ to_return(:status => 200, :body => create_update_return_body, :headers => {})
195
+ client.create_update(body: body_content)
196
+
197
+ end
198
+ end
199
+ end
200
+
201
+ describe "#modify_update_text" do
202
+
203
+ let(:body_content) { {text: "Text for an updated text for update"} }
204
+
205
+ id = "4ecda256512f7ee521000004"
206
+ let(:url) { %r{https://api\.bufferapp\.com/1/updates/#{ id }/update\.json\?access_token=.*} }
207
+
208
+ context "should modify an update" do
209
+ it "when params are present" do
210
+ stub_request(:post, url).
211
+ with(:body => body_content).
212
+ to_return(:status => 200, :body => modify_update_response, :headers => {})
213
+ client.modify_update_text(id, body: body_content)
214
+ end
215
+ end
216
+ end
217
+
218
+ describe "#destroy_update" do
219
+ it "connects to correct endpoint" do
220
+ stub_request(:post, %r{https://api\.bufferapp\.com/1/updates/4ecda256512f7ee521000001/destroy\.json\?access_token=.*}).
221
+ to_return(fixture('destroy.txt'))
222
+ update_id = "4ecda256512f7ee521000001"
223
+ client.destroy_update(update_id)
224
+ end
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buffer::Client::User do
4
+ let(:id) { "5160746d54f04a5e3a00000f" }
5
+
6
+ subject do
7
+ Buffer::Client.new("some_token")
8
+ end
9
+
10
+ describe "#user_info" do
11
+ let(:rash) { subject.user_info }
12
+
13
+ before(:each) do
14
+ url = "#{ base_path }/user.json"
15
+ stub_with_to_return(:get, url, "user_authenticated.txt")
16
+ end
17
+
18
+ it "returns a Rash object" do
19
+ rash.class.should eq(Buffer::UserInfo)
20
+ end
21
+
22
+ it "provides an accessor for plan" do
23
+ rash.plan.should eq("free")
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buffer::Client do
4
+ let(:id) { "5160746d54f04a5e3a00000f" }
5
+
6
+ subject do
7
+ Buffer::Client.new("some_token")
8
+ end
9
+
10
+ describe "#initialize" do
11
+ it "allows a token to be set and retrieved" do
12
+ subject.access_token.should eq("some_token")
13
+ end
14
+ end
15
+
16
+ describe "#info" do
17
+ before do
18
+ stub_request(:get, "#{base_path}/info/configuration.json?access_token=some_token").
19
+ to_return(fixture("info.txt"))
20
+ end
21
+
22
+ it "connects to the correct endpoint" do
23
+ subject.info
24
+ end
25
+
26
+ it "retrieves the correct name" do
27
+ subject.info.services.twitter.types.profile.name.should eq("Twitter")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buffer::Client::Core do
4
+
5
+ let(:client) { Buffer::Client.new("some_token") }
6
+ describe "#get" do
7
+ it "delegates to #handle_response_code when code != 200" do
8
+ stub_request(:get, "#{base_path}/info/configuration.json?access_token=some_token").
9
+ to_return(:status => 403, :body => "", :headers => {})
10
+ client.should_receive(:handle_response_code).once
11
+ client.info
12
+ end
13
+
14
+
15
+ it "does not delegate to #handle_response_code when code = 200" do
16
+ stub_request(:get, "#{base_path}/info/configuration.json?access_token=some_token").
17
+ to_return(fixture("link.txt"))
18
+ client.should_not_receive(:handle_response_code)
19
+ client.info
20
+ end
21
+ end
22
+
23
+ describe "#post" do
24
+
25
+ it "connects to the correct endpoint" do
26
+
27
+ #TODO improve test
28
+ response = %Q[{"success": true, "message": "Schedule saved successfully"}]
29
+ id = "4eb854340acb04e870000010"
30
+ stub_request(:post, "#{ base_path }/profiles/#{id}/schedules/update.json?access_token=some_token").
31
+ with(:body => {"schedules"=>"schedules[0][days][]=mon&schedules[0][days][]=tue&schedules[0][days][]=wed&schedules[0][times][]=12%3A00&schedules[0][times][]=17%3A00&schedules[0][times][]=18%3A00"},
32
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.9.1'}).
33
+ to_return(:status => 200, :body => response, :headers => {})
34
+ client.set_schedules(id, :schedules => sample_schedules).success.
35
+ should eq(true)
36
+ end
37
+
38
+ it "does not delegate to #handle_response_code when code = 200" do
39
+ url = "#{base_path}/info/configuration.json"
40
+ fixture_name = "link.txt"
41
+ stub_with_to_return(:get, url, fixture_name)
42
+ client.should_not_receive(:handle_response_code)
43
+ client.info
44
+ end
45
+
46
+ end
47
+
48
+ describe "#handle_response_code" do
49
+ context "fails gracefully with undocumented responses" do
50
+ it "responds to 401 unauthorized response" do
51
+ id = "5520a65cf387f71753588135"
52
+ url = "#{base_path}/updates/#{id}.json?access_token=some_token"
53
+ stub_with_to_return(:get, url, "update_by_id_non_auth.txt")
54
+ lambda { client.update_by_id(id) }.
55
+ should raise_error(Buffer::Error::APIError)
56
+ end
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,171 @@
1
+ require 'buffer'
2
+ require 'rspec'
3
+ require 'webmock/rspec'
4
+ require 'json'
5
+
6
+ require 'coveralls'
7
+ Coveralls.wear!
8
+
9
+ def travis?
10
+ ENV['TRAVIS_CI']
11
+ end
12
+
13
+ def fixture_path
14
+ File.expand_path(File.join("..", "fixtures"), __FILE__)
15
+ end
16
+
17
+ def fixture(file)
18
+ File.new(File.join(fixture_path, file))
19
+ end
20
+
21
+ def post_data
22
+ <<EOF
23
+ schedules[0][days][]=mon&schedules[0][days][]=tue&schedules[0][days][]=wed&schedules[0][times][]=12:00&schedules[0][times][]=17:00&schedules[0][times][]=18:00&
24
+ EOF
25
+ end
26
+
27
+ def modify_update_response
28
+ response =<<EOF
29
+ {
30
+ "success" : true,
31
+ "buffer_count" : 10,
32
+ "buffer_percentage" : 20,
33
+ "update" : {
34
+ "id" : "4ecda256512f7ee521000004",
35
+ "client_id" : "4f850cc93733aa9301000002",
36
+ "created_at" : 1320703582,
37
+ "day" : "Saturday 26th November",
38
+ "due_at" : 1320742680,
39
+ "due_time" : "11:05 am",
40
+ "media" : {
41
+ "link" : "http://google.com",
42
+ "title" : "Google",
43
+ "description" : "The google homepage"
44
+ },
45
+ "profile_id" : "4eb854340acb04e870000010",
46
+ "profile_service" : "twitter",
47
+ "status" : "buffer",
48
+ "text" : "This is an edited update",
49
+ "text_formatted" : "This is an edited update",
50
+ "user_id" : "4eb9276e0acb04bb81000067",
51
+ "via" : "api"
52
+ }
53
+ }
54
+ EOF
55
+ end
56
+
57
+ def create_update_return_body
58
+ create_update_return_body =<<EOF
59
+ {
60
+ "success" : true,
61
+ "buffer_count" : 10,
62
+ "buffer_percentage" : 20,
63
+ "updates" : [{
64
+ "id" : "4ecda256512f7ee521000004",
65
+ "created_at" : 1320703582,
66
+ "day" : "Saturday 26th November",
67
+ "due_at" : 1320742680,
68
+ "due_time" : "11:05 am",
69
+ "media" : {
70
+ "link" : "http://google.com",
71
+ "title" : "Google",
72
+ "description" : "The google homepage"
73
+ },
74
+ "profile_id" : "4eb854340acb04e870000010",
75
+ "profile_service" : "twitter",
76
+ "status" : "buffer",
77
+ "text" : "This is an example update",
78
+ "text_formatted" : "This is an example update",
79
+ "user_id" : "4eb9276e0acb04bb81000067",
80
+ "via" : "api"
81
+ }
82
+ ]
83
+ }
84
+ EOF
85
+ end
86
+ def reorder_updates_body_response
87
+ return_body =<<EOF
88
+ {
89
+ "success" : true,
90
+ "updates" : [{
91
+ "id" : "4eb854340acb04e870000010",
92
+ "created_at" : 1320703582,
93
+ "day" : "Saturday 5th November",
94
+ "due_at" : 1320742680,
95
+ "due_time" : "08:01 am",
96
+ "profile_id" : "4eb854340acb04e870000010",
97
+ "profile_service" : "twitter",
98
+ "status" : "buffer",
99
+ "text" : "3 Incredible Stories Made Possible Through Twitter j.mp/u...",
100
+ "text_formatted" : "3 Incredible Stories Made Possible Through Twit...",
101
+ "user_id" : "4eb9276e0acb04bb81000067",
102
+ "via" : "safari"
103
+ }
104
+ ]
105
+ }
106
+ EOF
107
+ end
108
+
109
+ def sample_schedules2
110
+ [{ days: %w[mon tue wed],
111
+ times: %w[12:00 17:00 18:00]},
112
+ {days: %w[mon tue wed],
113
+ times: %w[12:00 17:00 18:00]},
114
+ ]
115
+
116
+ end
117
+
118
+ def base_path
119
+ "https://api.bufferapp.com/1"
120
+ end
121
+
122
+ def access_token_param
123
+ "access_token=some_token"
124
+ end
125
+
126
+ def stub_with_to_return(request_type, url, fixture_name, query_hash={})
127
+ query = access_hash.merge(query_hash)
128
+ stub_request(request_type, url).
129
+ with(query: query).
130
+ to_return(fixture(fixture_name))
131
+ end
132
+
133
+ def access_hash
134
+ { 'access_token' => 'some_token'}
135
+ end
136
+
137
+ def sample_schedules
138
+ [{ days: %w[mon tue wed],
139
+ times: %w[12:00 17:00 18:00]}]
140
+ # @sample_schedules = JSON.parse <<EOF
141
+ # [{
142
+ # "days" : [
143
+ # "mon",
144
+ # "tue",
145
+ # "wed",
146
+ # "thu",
147
+ # "fri"
148
+ # ],
149
+ # "times" : [
150
+ # "12:00",
151
+ # "17:00",
152
+ # "18:00"
153
+ # ]
154
+ # },
155
+ # {
156
+ # "days" : [
157
+ # "mon",
158
+ # "tue",
159
+ # "wed",
160
+ # "thu",
161
+ # "fri"
162
+ # ],
163
+ # "times" : [
164
+ # "12:00",
165
+ # "17:00",
166
+ # "18:00"
167
+ # ]
168
+ # }]
169
+ # EOF
170
+ end
171
+