buffer 0.0.1 → 0.1.0

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