buff 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f3e891d111f7733cd52ef4cbd52b53cb2acabab6
4
+ data.tar.gz: 357899d36e7488127e46b03f36f7ad0a3eefa715
5
+ SHA512:
6
+ metadata.gz: af4fd75e337c70021cd21a1fcf7a960d571ada956d9b8cd1a3e2ed184f4e79cd70476decfb4838fdd31a1dfb669e3fa9dcd3141f1927be046ae96a8e70b78b71
7
+ data.tar.gz: 31ff60c79b65e92510ff20245c45b6179ebdfb002ef83da36f852f8b4e2226bbcf0dabcb3cf861f8f0ccb9dbe677e06dcff749a9e20c8e0ffa83bd5266846d43
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ notes.txt
19
+ .DS_STORE
20
+ spec/fixtures/
21
+ utility.rb
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.0.0-p195
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in buff.gemspec
4
+ gem 'coveralls', require: false
5
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ end
10
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 ZPH
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Buff
2
+
3
+ Buff is a Buffer API Wrapper written in Ruby. It provides more thorough API coverage than the existing gem.
4
+
5
+ Since the gem is currently in ALPHA development, the interface is prone to change. Please wait until v0.1.0 is released to become reliant on interface.
6
+
7
+ ## Installation
8
+
9
+ [![Coverage Status](https://coveralls.io/repos/zph/buff/badge.png?branch=master)](https://coveralls.io/r/zph/buff?branch=master)
10
+
11
+ Once Buff is released as a gem, the following instructions will work. For now please `git clone` the repo.
12
+
13
+ #### Note: Fixtures are not currently public. They will be uploaded after sensitive info is sanitized.
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'buff', :git => 'zph/buff'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Once gem is pushed to RubyGems:
24
+ > Or install it yourself as:
25
+
26
+ > $ gem install buff
27
+
28
+ ## Usage
29
+
30
+ * Note which API coverage exists and have fun!
31
+ * Authentication is not included in this gem (Try OAuth-buffer)
32
+
33
+ ## API Coverage
34
+
35
+ #### Implemented
36
+
37
+ * User
38
+ * Profiles (:get)
39
+ * Updates (:get)
40
+ * Updates (:post, #create_update, #change_update_text, #destroy_update, #share_update, #shuffle_updates, #reorder_updates)
41
+ * Links
42
+ * Info
43
+ * Error Codes
44
+
45
+ #### Not Implemented
46
+
47
+ * Profiles (:post, #set_schedules)
48
+ * Caching
49
+
50
+ ## Contributing
51
+
52
+ 1. Fork it
53
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
54
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
55
+ 4. Push to the branch (`git push origin my-new-feature`)
56
+ 5. Create new Pull Request
57
+
58
+ Issues, advice and refactoring is welcome.
59
+
60
+ Also, this project is newcomer friendly!! We'd love to be your first Open Source Software contribution and would be happy to assist in that process.
61
+
62
+ Reach out on Twitter [@_ZPH](http://twitter.com/_ZPH).
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Default: run specs.'
6
+ task :default => :spec
7
+
8
+ desc "Run specs"
9
+ RSpec::Core::RakeTask.new do |t|
10
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
11
+ # Put spec opts in a file named .rspec in root
12
+ end
13
+
14
+ desc "Generate code coverage"
15
+ RSpec::Core::RakeTask.new(:coverage) do |t|
16
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
17
+ t.rcov = true
18
+ t.rcov_opts = ['--exclude', 'spec']
19
+ end
20
+
21
+ task :default => :spec
22
+
23
+ task :curl_dump, [ :url ] do |t, args|
24
+ access_token = `cat ~/.bufferapprc | head -3 | tail -1`.chomp
25
+ sh "curl -is #{args[:url]}?access_token=#{access_token}"
26
+ end
data/buff.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'buff/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "buff"
8
+ gem.version = Buff::VERSION
9
+ gem.authors = ["ZPH"]
10
+ gem.email = ["Zander@civet.ws"]
11
+ gem.description = %q{Buff is an API Wrapper Gem for Bufferapp.com's API}
12
+ gem.summary = %q{Buff is an API Wrapper Gem for Bufferapp.com's API}
13
+ gem.homepage = "http://github.com/zph/buff"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency 'rspec'
21
+ gem.add_development_dependency 'webmock'
22
+ gem.add_development_dependency 'guard-rspec'
23
+ gem.add_development_dependency 'guard-bundler'
24
+ gem.add_development_dependency 'rb-fsevent'
25
+ gem.add_development_dependency 'growl'
26
+ gem.add_development_dependency 'pry-full'
27
+ gem.add_development_dependency 'rake'
28
+
29
+ gem.add_runtime_dependency 'multi_json'
30
+ gem.add_runtime_dependency 'yajl-ruby'
31
+ gem.add_runtime_dependency 'faraday'
32
+ gem.add_runtime_dependency 'faraday_middleware'
33
+ gem.add_runtime_dependency 'rash'
34
+ gem.add_runtime_dependency 'addressable'
35
+ end
data/lib/buff/core.rb ADDED
@@ -0,0 +1,93 @@
1
+ module Buff
2
+ class Client
3
+ module Core
4
+ API_VERSION = "1"
5
+
6
+ attr_reader :error_table
7
+
8
+ def get(path, options={})
9
+ options.merge!(auth_query)
10
+ response = @conn.get do |req|
11
+ req.url path.gsub(%r{^\/}, '')
12
+ req.params = options
13
+ end
14
+
15
+ interpret_response(response)
16
+ end
17
+
18
+ def post(path, post_data)
19
+ @conn.post do |req|
20
+ req.url path.gsub(%r{^\/}, '')
21
+ req.headers['Content-Type'] = "application/x-www-form-urlencoded"
22
+ req.body = post_data
23
+ req.params = auth_query
24
+ end
25
+ end
26
+
27
+ def basic_request(path, verb, options={})
28
+ response = self.class.send(verb.to_sym, path, options)
29
+ interpret_response(response)
30
+ end
31
+
32
+ def interpret_response(response)
33
+ case response.status
34
+ when 200
35
+ JSON.parse response.body
36
+ else
37
+ handle_response_code(response)
38
+ end
39
+ end
40
+
41
+ def handle_response_code(response)
42
+ error = Hashie::Mash.new( response.body )
43
+ if ERROR_TABLE[error.code]
44
+ error_explanation = "Buffer API Error Code: #{error.code}\n"
45
+ error_explanation += "HTTP Code: #{response.code}. Description: #{error.error}"
46
+ else
47
+ error_explanation = "Buffer API Unknown Error in Response"
48
+ end
49
+
50
+ raise Buff::APIError, error_explanation
51
+ end
52
+
53
+ def self.error_table
54
+ @error_table ||= ERROR_TABLE
55
+ end
56
+
57
+ ERROR_TABLE = {
58
+ "403"=>"Permission denied.",
59
+ "404"=>"Endpoint not found.",
60
+ "405"=>"Method not allowed.",
61
+ "1000"=>"An unknown error occurred.",
62
+ "1001"=>"Access token required.",
63
+ "1002"=>"Not within application scope.",
64
+ "1003"=>"Parameter not recognized.",
65
+ "1004"=>"Required parameter missing.",
66
+ "1005"=>"Unsupported response format.",
67
+ "1006"=>"Parameter value not within bounds.",
68
+ "1010"=>"Profile could not be found.",
69
+ "1011"=>"No authorization to access profile.",
70
+ "1012"=>"Profile did not save successfully.",
71
+ "1013"=>"Profile schedule limit reached.",
72
+ "1014"=>"Profile limit for user has been reached.",
73
+ "1015"=>"Profile could not be destroyed.",
74
+ "1020"=>"Update could not be found.",
75
+ "1021"=>"No authorization to access update.",
76
+ "1022"=>"Update did not save successfully.",
77
+ "1023"=>"Update limit for profile has been reached.",
78
+ "1024"=>"Update limit for team profile has been reached.",
79
+ "1025"=>"Update was recently posted, can't post duplicate content.",
80
+ "1026"=>"Update must be in error status to requeue.",
81
+ "1028"=>"Update soft limit for profile reached.",
82
+ "1029"=>"Event type not supported.",
83
+ "1030"=>"Media filetype not supported.",
84
+ "1031"=>"Media filesize out of acceptable range.",
85
+ "1032"=>"Unable to post image to LinkedIn group(s).",
86
+ "1042"=>"User did not save successfully.",
87
+ "1050"=>"Client could not be found.",
88
+ "1051"=>"No authorization to access client."
89
+ }
90
+
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,8 @@
1
+ module Buff
2
+ class Encode
3
+ def self.encode(hash)
4
+ raise ArgumentError unless hash.is_a?(Hash)
5
+
6
+ end
7
+ end
8
+ end
data/lib/buff/error.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Buff
2
+ module Error
3
+ end
4
+ end
data/lib/buff/link.rb ADDED
@@ -0,0 +1,10 @@
1
+ module Buff
2
+ class Client
3
+ module Link
4
+ def link(options)
5
+ response = get("/links/shares.json", options)
6
+ Buff::Link.new(response)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ module Buff
2
+ class Client
3
+ module Profile
4
+ def profiles(options={})
5
+ response = get("/profiles.json", options)
6
+ response.map { |profile| Buff::Profile.new(profile) }
7
+ end
8
+
9
+ def profile_by_id(id, options={})
10
+ response = get("/profiles/#{id}.json")
11
+ Buff::Profile.new(response)
12
+ end
13
+
14
+ def profile_schedules_by_id(id, options={})
15
+ response = get("/profiles/#{id}/schedules.json")
16
+ response.map { |r| Buff::Schedule.new(r) }
17
+ end
18
+
19
+ # TODO massive bug
20
+ # currently deletes schedule due to malformed request
21
+ def set_schedules(id, options={})
22
+ # schedules = options.fetch(:schedules) { raise ArgumentError }
23
+ response = post("/profiles/#{id}/schedules/update.json", options )
24
+ Buff::Response.new(JSON.parse(response.body))
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,84 @@
1
+ module Buff
2
+ class Client
3
+ module Update
4
+ def update_by_id(id, options={})
5
+ check_id(id)
6
+ response = get("/updates/#{id}.json")
7
+ Buff::Update.new(response)
8
+ end
9
+
10
+ def updates_by_profile_id(id, options={})
11
+ optional_params = [ :page, :count, :since, :utc ]
12
+ status = options.fetch(:status) do
13
+ raise Buff::MissingStatus, "Include :pending or :sent in args"
14
+ end
15
+ options.delete(:status)
16
+ response = get("/profiles/#{id}/updates/#{status.to_s}.json", options )
17
+ updates = response['updates'].map { |r| Buff::Update.new(r) }
18
+ Buff::Updates.new (
19
+ { total: response['total'], updates: updates } )
20
+ end
21
+
22
+ def interactions_by_update_id(id, options={})
23
+ optional_params = [:page, :count, :event]
24
+ response = get("/updates/#{id}/interactions.json", options)
25
+ interactions = response['interactions'].map { |r| Buff::Interaction.new(r) }
26
+ Buff::Interactions.new(
27
+ { total: response['total'], interactions: interactions }
28
+ )
29
+ end
30
+
31
+ def reorder_updates(profile_id, options={})
32
+ # order, optional: offset, utc
33
+ order = options.fetch(:order) { raise ArgumentError }
34
+ response = post("/profiles/#{profile_id}/updates/reorder.json", options)
35
+ end
36
+
37
+ def shuffle_updates(profile_id, options={})
38
+ # optional count, utc
39
+ response = post("/profiles/#{profile_id}/updates/shuffle.json", options)
40
+ end
41
+
42
+ #TODO
43
+ def create_update(options={})
44
+
45
+ # POST Data
46
+ # text=This%20is%20an%20example%20update&
47
+ # profile_ids[]=4eb854340acb04e870000010&
48
+ # profile_ids[]=4eb9276e0acb04bb81000067&
49
+ # media[link]=http%3A%2F%2Fgoogle.com&
50
+ # media[description]=The%20google%20homepage
51
+ # options = {
52
+ # text: "bodytext",
53
+ # profile_ids: ["230958239058", "23059u2350923"],
54
+ # media: {
55
+ # link: "http://example.com",
56
+ # description: "That example page"
57
+ # }
58
+ # }
59
+ response = post("/updates/create.json", options)
60
+ Hashie::Mash.new(JSON.parse response.body)
61
+ end
62
+
63
+ def modify_update_text(update_id, options={})
64
+ # text, (now, media, utc)
65
+ options.fetch(:text) { raise ArgumentError }
66
+ response = post("/updates/#{update_id}/update.json", options)
67
+ Hashie::Mash.new(JSON.parse response.body)
68
+ end
69
+
70
+ def share_update(update_id, options={})
71
+ response = post("/updates/#{update_id}/share.json", options)
72
+ end
73
+
74
+ def destroy_update(update_id, options={})
75
+ response = post("/updates/#{update_id}/destroy.json", options)
76
+ end
77
+
78
+ def check_id(id)
79
+ raise Buff::InvalidIdLength unless id.length == 24
80
+ raise Buff::InvalidIdContent unless id[/^[a-f0-9]+$/i]
81
+ end
82
+ end
83
+ end
84
+ end
data/lib/buff/user.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Buff
2
+ class Client
3
+ module User
4
+ def user_info(options={})
5
+ Buff::UserInfo.new(get("/user.json"))
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Buff
2
+ VERSION = "0.0.1"
3
+ end
data/lib/buff.rb ADDED
@@ -0,0 +1,72 @@
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+ require "json"
4
+ require "rash"
5
+
6
+ require "buff/version"
7
+ require "buff/core"
8
+ require "buff/user"
9
+ require "buff/profile"
10
+ require "buff/update"
11
+ require "buff/link"
12
+ require "buff/error"
13
+ require "buff/encode"
14
+
15
+ module Buff
16
+
17
+ begin
18
+ ACCESS_TOKEN = File.open(File.expand_path("~/.bufferapprc")).readlines[2].chomp if File.exists?(File.expand_path("~/.bufferapprc"))
19
+ end
20
+
21
+ class UserInfo < Hashie::Mash; end
22
+ class Profile < Hashie::Mash; end
23
+ class Response < Hashie::Mash; end
24
+ class Update < Hashie::Mash; end
25
+ class Updates < Hashie::Mash; end
26
+ class Interaction < Hashie::Mash; end
27
+ class Interactions < Hashie::Mash; end
28
+ class Link < Hashie::Mash; end
29
+ class Info < Hashie::Mash; end
30
+
31
+ class Schedule < Hashie::Mash; end
32
+ Schedules = Class.new(Array) do
33
+ def dump
34
+ { schedules: self }.to_json
35
+ end
36
+ end
37
+
38
+ InvalidIdLength = Class.new(ArgumentError)
39
+ InvalidIdContent = Class.new(ArgumentError)
40
+ MissingStatus = Class.new(ArgumentError)
41
+ APIError = Class.new(StandardError)
42
+ UnauthorizeRequest = Class.new(StandardError)
43
+
44
+ class Client
45
+ include Core
46
+ include User
47
+ include Profile
48
+ include Update
49
+ include Link
50
+ include Error
51
+
52
+ attr_accessor :access_token, :auth_query
53
+
54
+ def initialize(access_token)
55
+ @access_token = access_token
56
+ @conn = Faraday.new(:url => "https://api.bufferapp.com/1/") do |faraday|
57
+ faraday.request :url_encoded # form-encode POST params
58
+ faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
59
+ end
60
+ end
61
+
62
+ def auth_query
63
+ { :access_token => @access_token }
64
+ end
65
+
66
+ def info
67
+ response = get("/info/configuration.json")
68
+ Buff::Info.new(response)
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,36 @@
1
+ require "spec_helper"
2
+
3
+ describe Buff::Encode do
4
+ describe "#encode" do
5
+
6
+ let(:uri) { Addressable::URI.new }
7
+ it "throws error unless input is a hash" do
8
+ lambda { Buff::Encode.encode([]) }.
9
+ should raise_error(ArgumentError)
10
+ end
11
+
12
+ it "should convert {:pony => 'pony' }" do
13
+ h = { pony: "pony"}
14
+ uri.query_values = h
15
+ uri.query.should eq("pony=pony")
16
+ end
17
+
18
+ it "should convert {:pony => ['pony', 'horse'] }" do
19
+ h = { pony: ["pony", "horse"]}
20
+ uri.query_values = h
21
+ uri.query.should eq("pony=pony&pony=horse")
22
+ end
23
+
24
+ it "should convert {:days => [mon, tues], :times => [....]}" do
25
+ h = { days: ["mon", "tue"], times: ["12:00", "13:00"]}
26
+ uri.query_values = h
27
+ uri.query.should eq("days=mon&days=tue&times=12%3A00&times=13%3A00")
28
+ end
29
+ xit "should convert {schedules => {:days => [mon, tues], :times => [....]}" do
30
+ # h = { :days => ["mon", "tue", "wed", "thu"], :times => ["12:00", "13:00"]}
31
+ h = {:a => "a", :bd => ["c", "d", "e"]}
32
+ uri.query_values = h
33
+ uri.query.should eq("days=mon&days=tue&times=12%3A00&times=13%3A00")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buff::Client do
4
+ describe "#link" do
5
+ let(:client) { Buff::Client.new("some_token") }
6
+ let(:url) { %q{http://bufferapp.com} }
7
+
8
+ before do
9
+ stub_request(:get, "https://api.bufferapp.com/1/links/shares.json?access_token=some_token&url=http://bufferapp.com").
10
+ to_return(fixture('link.txt'))
11
+ end
12
+
13
+ it "connects to the correct endpoint" do
14
+ client.link({url: url})
15
+ end
16
+
17
+ it "parses the shares of a link" do
18
+ client.link({url: url}).shares.should eq(47348)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buff::Client do
4
+ let(:client) { Buff::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/4eb8565e0acb04bb82000004.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(Buff::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
+ xit "requires an id"
64
+ xit "connects to the correct endpoint"
65
+ xit "returns an object where total is accessible"
66
+ it "allows optional params" do
67
+ url = "https://api.bufferapp.com/1/updates/4ecda476542f7ee521000006/interactions.json?access_token=some_token&page=2"
68
+ id = "4ecda476542f7ee521000006"
69
+ stub_request(:get, url).
70
+ to_return(fixture("interactions_by_update_id.txt"))
71
+ client.interactions_by_update_id(id, page: 2)
72
+ end
73
+ end
74
+
75
+ describe "#check_id" do
76
+ it "fails if id is not 24 chars" do
77
+ stub_request(:get, "https://api.bufferapp.com/1/updates/4eb8565e0acb04bb82000004X.json?access_token=some_token").
78
+ to_return(:status => 200, :body => "", :headers => {})
79
+ id = "4eb8565e0acb04bb82000004X"
80
+ lambda { client.update_by_id(id) }.
81
+ should raise_error(Buff::InvalidIdLength)
82
+ end
83
+
84
+ it "fails if id is not numbers and a-f" do
85
+ stub_request(:get, "https://api.bufferapp.com/1/updates/4eb8565e0acb04bb8200000X.json?access_token=some_token").
86
+ to_return(:status => 200, :body => "", :headers => {})
87
+ id = "4eb8565e0acb04bb8200000X"
88
+ lambda { client.update_by_id(id) }.
89
+ should raise_error(Buff::InvalidIdContent)
90
+ end
91
+ end
92
+
93
+ describe "#reorder_updates" do
94
+ it "connects to appropriate endpoint" do
95
+ id_no = "4ecda256512f7ee521000001"
96
+ order_hash = { order: [id_no, id_no, id_no] }
97
+ stub_request(:post, %r{https://api\.bufferapp\.com/1/profiles/4ecda256512f7ee521000001/updates/reorder\.json\?access_token=.*}).
98
+ with(:body => {"order"=>["4ecda256512f7ee521000001", "4ecda256512f7ee521000001", "4ecda256512f7ee521000001"]},
99
+ :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.8.7'}).
100
+ to_return(:status => 200, :body => "", :headers => {})
101
+ client.reorder_updates(id_no, order_hash)
102
+
103
+ end
104
+ end
105
+
106
+ describe "#shuffle_updates" do
107
+ it "connects to appropriate endpoint" do
108
+ id_no = "4ecda256512f7ee521000001"
109
+ stub_request(:post, %r{https://api\.bufferapp\.com/1/profiles/4ecda256512f7ee521000001/updates/shuffle\.json\?access_token=.*}).
110
+ with(:body => {"count"=>"10"})
111
+ client.shuffle_updates(id_no, {count: 10})
112
+ end
113
+ end
114
+ describe "#share_update" do
115
+ it "should connect to correct endpoint" do
116
+ stub_request(:post, %r{https://api\.bufferapp\.com/1/updates/4ecda256512f7ee521000001/share\.json\?access_token=.*}).
117
+ to_return(:status => 200, :body => "{'success': true}", :headers => {})
118
+ update_id = "4ecda256512f7ee521000001"
119
+ client.share_update(update_id)
120
+ end
121
+ end
122
+
123
+ describe "#destroy_update" do
124
+ it "connects to correct endpoint" do
125
+ stub_request(:post, %r{https://api\.bufferapp\.com/1/updates/4ecda256512f7ee521000001/destroy\.json\?access_token=.*}).
126
+ to_return(fixture('destroy.txt'))
127
+ update_id = "4ecda256512f7ee521000001"
128
+ client.destroy_update(update_id)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,261 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe Buff::Client do
5
+ let(:id) { "5160746d54f04a5e3a00000f" }
6
+
7
+ subject do
8
+ Buff::Client.new("some_token")
9
+ end
10
+
11
+ describe "#initialize" do
12
+ it "allows a token to be set and retrieved" do
13
+ subject.access_token.should eq("some_token")
14
+ end
15
+ end
16
+
17
+ describe "#user_info" do
18
+ let(:rash) { subject.user_info }
19
+
20
+ before(:each) do
21
+ stub_request(:get, 'https://api.bufferapp.com/1/user.json').
22
+ with(query: { 'access_token' => 'some_token'}).
23
+ to_return(fixture('user_authenticated.txt'))
24
+ end
25
+
26
+ it "returns a Rash object" do
27
+ rash.class.should eq(Buff::UserInfo)
28
+ end
29
+
30
+ it "provides an accessor for plan" do
31
+ rash.plan.should eq("free")
32
+ end
33
+ end
34
+
35
+ describe "#profiles" do
36
+ let(:rash) { Buff::Client.new("some_token").profiles }
37
+
38
+ before(:each) do
39
+ stub_request(:get, 'https://api.bufferapp.com/1/profiles.json').
40
+ with(query: { 'access_token' => 'some_token'}).
41
+ to_return(fixture('profile_authenticated.txt'))
42
+ end
43
+
44
+ it "makes the correct url request" do
45
+ subject.profiles
46
+ end
47
+
48
+ it "returns a Rash collection object" do
49
+ rash[0].class.should eq(Buff::Profile)
50
+ end
51
+
52
+ it "provides an accessor for plan" do
53
+ rash[0].service.should eq("twitter")
54
+ end
55
+ end
56
+
57
+ describe "#profile_by_id" do
58
+ let(:id) { "5160746d54f04a5e3a00000f" }
59
+ before(:each) do
60
+ stub_request(:get, "https://api.bufferapp.com/1/profiles/#{id}.json").
61
+ with(query: { 'access_token' => 'some_token'}).
62
+ to_return(fixture('profiles_by_id.txt'))
63
+ end
64
+
65
+ let(:rash) { Buff::Client.new("some_token").profile_by_id(id) }
66
+
67
+ it "returns a rash collection" do
68
+ rash.class.should eq(Buff::Profile)
69
+ end
70
+
71
+ it "accesses formatted service" do
72
+ rash.formatted_service.should eq("Twitter")
73
+ end
74
+ end
75
+
76
+ describe "#profile_schedules_by_id" do
77
+ before(:each) do
78
+ stub_request(:get, "https://api.bufferapp.com/1/profiles/#{id}/schedules.json").
79
+ with(query: { 'access_token' => 'some_token'}).
80
+ to_return(fixture('profile_schedules_by_id.txt'))
81
+ end
82
+
83
+ let(:rash) { Buff::Client.new("some_token").profile_schedules_by_id(id) }
84
+
85
+ it "returns a rash collection" do
86
+ rash[0].class.should eq(Buff::Schedule)
87
+ end
88
+
89
+ it "accesses days" do
90
+ expect(rash[0].days).to include("mon")
91
+ end
92
+
93
+ it "accesses times" do
94
+ expect(rash[0].times).to include("06:13")
95
+ end
96
+ end
97
+
98
+ describe "#set_schedules" do
99
+ #not yet implemented
100
+ xit "throw error if schedules is empty" do
101
+ lambda {
102
+ Buff::Client.new("some_token").set_schedules(id) }.
103
+ should raise_error(ArgumentError)
104
+
105
+ end
106
+ end
107
+
108
+ describe "#info" do
109
+ before do
110
+ stub_request(:get, "https://api.bufferapp.com/1/info/configuration.json?access_token=some_token").
111
+ to_return(fixture("info.txt"))
112
+ end
113
+
114
+ it "connects to the correct endpoint" do
115
+ subject.info
116
+ end
117
+
118
+ it "retrieves the correct name" do
119
+ subject.info.services.twitter.types.profile.name.should eq("Twitter")
120
+ end
121
+ end
122
+ end
123
+
124
+ describe Buff::Schedules do
125
+ before do
126
+ @schedule = JSON.parse <<EOF
127
+ {
128
+ "days" : [
129
+ "mon",
130
+ "tue",
131
+ "wed",
132
+ "thu",
133
+ "fri"
134
+ ],
135
+ "times" : [
136
+ "12:00",
137
+ "17:00",
138
+ "18:00"
139
+ ]
140
+ }
141
+ EOF
142
+
143
+ @sample_schedules = JSON.parse <<EOF
144
+ { "schedules" : [{
145
+ "days" : [
146
+ "mon",
147
+ "tue",
148
+ "wed",
149
+ "thu",
150
+ "fri"
151
+ ],
152
+ "times" : [
153
+ "12:00",
154
+ "17:00",
155
+ "18:00"
156
+ ]
157
+ },
158
+ {
159
+ "days" : [
160
+ "mon",
161
+ "tue",
162
+ "wed",
163
+ "thu",
164
+ "fri"
165
+ ],
166
+ "times" : [
167
+ "12:00",
168
+ "17:00",
169
+ "18:00"
170
+ ]
171
+ }]
172
+ }
173
+ EOF
174
+ end
175
+
176
+ it "accepts an array of days" do
177
+ lambda {
178
+ schedules = Buff::Schedules.new
179
+ schedules << Buff::Schedule.new
180
+ }.should_not raise_error
181
+ end
182
+
183
+ it "dumping a double schedule yields correct json" do
184
+ schedules = Buff::Schedules.new
185
+ schedules << @schedule
186
+ schedules << @schedule
187
+ @sample_schedules = @sample_schedules.to_json
188
+
189
+ schedules.dump.should eq(@sample_schedules)
190
+ end
191
+ end
192
+
193
+ describe Buff::Client::Core do
194
+
195
+ let(:client) { Buff::Client.new("some_token") }
196
+ describe "#get" do
197
+ it "delegates to #handle_response_code when code != 200" do
198
+ stub_request(:get, "https://api.bufferapp.com/1/info/configuration.json?access_token=some_token").
199
+ to_return(:status => 403, :body => "", :headers => {})
200
+ client.should_receive(:handle_response_code).once
201
+ client.info
202
+ end
203
+
204
+
205
+ it "does not delegate to #handle_response_code when code = 200" do
206
+ stub_request(:get, "https://api.bufferapp.com/1/info/configuration.json?access_token=some_token").
207
+ to_return(fixture("link.txt"))
208
+ client.should_not_receive(:handle_response_code)
209
+ client.info
210
+ end
211
+ end
212
+
213
+ describe "#post" do
214
+
215
+ it "connects to the correct endpoint" do
216
+
217
+ #TODO improve test
218
+ response = %Q[{"success": true, "message": "Schedule saved successfully"}]
219
+ id = "4eb854340acb04e870000010"
220
+ stub_request(:post, %r{https://api\.bufferapp\.com/1/profiles/4eb854340acb04e870000010/schedules/update\.json\?access_token=.*}).
221
+ with(:body => {"schedules"=>{"days"=>["mon", "tue", "wed"], "times"=>["12:00", "17:00", "18:00"]}},
222
+ :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.8.7'}).
223
+ to_return(:status => 200, :body => response, :headers => {})
224
+ client.set_schedules(id, :schedules => sample_schedules).success.
225
+ should eq(true)
226
+ end
227
+
228
+ xit "does not delegate to #handle_response_code when code = 200" do
229
+ stub_request(:get, "https://api.bufferapp.com/1/info/configuration.json?access_token=some_token").
230
+ to_return(fixture("link.txt"))
231
+ client.should_not_receive(:handle_response_code)
232
+ client.info
233
+ end
234
+
235
+ xit "transforms sample_schedule into correct formatted url" do
236
+ u = Addressable::URI.new
237
+ u.query_values = {schedules: sample_schedules[0]}
238
+ u.query.should eq(post_data)
239
+ end
240
+ end
241
+
242
+ describe "#handle_response_code" do
243
+ context "fails gracefully with undocumented responses" do
244
+ it "responds to 401 unauthorized response" do
245
+ id = "4eb8565e0acb04bb82000004"
246
+ stub_request(:get, "https://api.bufferapp.com/1/updates/#{id}.json?access_token=some_token").
247
+ to_return(fixture("update_by_id_non_auth.txt"))
248
+
249
+ lambda { client.update_by_id(id) }.
250
+ should raise_error(Buff::APIError)
251
+ end
252
+ end
253
+ end
254
+
255
+ describe "error_table" do
256
+ it "should report 1001 as appropriate message" do
257
+ table = Buff::Client::Core.error_table
258
+ table['1001'].should eq("Access token required.")
259
+ end
260
+ end
261
+ end
File without changes
@@ -0,0 +1,66 @@
1
+ require 'buff'
2
+ require 'rspec'
3
+ require 'webmock/rspec'
4
+ require 'json'
5
+
6
+ require 'coveralls'
7
+ Coveralls.wear!
8
+
9
+ def fixture_path
10
+ File.expand_path(File.join("..", "fixtures"), __FILE__)
11
+ end
12
+
13
+ def fixture(file)
14
+ File.new(File.join(fixture_path, file))
15
+ end
16
+
17
+ def post_data
18
+ <<EOF
19
+ 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&
20
+ EOF
21
+ end
22
+
23
+ def sample_schedules2
24
+ [{ days: %w[mon tue wed],
25
+ times: %w[12:00 17:00 18:00]},
26
+ {days: %w[mon tue wed],
27
+ times: %w[12:00 17:00 18:00]},
28
+ ]
29
+ end
30
+ def sample_schedules
31
+ [
32
+ [{ days: %w[mon tue wed],
33
+ times: %w[12:00 17:00 18:00]}]
34
+ ]
35
+ # @sample_schedules = JSON.parse <<EOF
36
+ # [{
37
+ # "days" : [
38
+ # "mon",
39
+ # "tue",
40
+ # "wed",
41
+ # "thu",
42
+ # "fri"
43
+ # ],
44
+ # "times" : [
45
+ # "12:00",
46
+ # "17:00",
47
+ # "18:00"
48
+ # ]
49
+ # },
50
+ # {
51
+ # "days" : [
52
+ # "mon",
53
+ # "tue",
54
+ # "wed",
55
+ # "thu",
56
+ # "fri"
57
+ # ],
58
+ # "times" : [
59
+ # "12:00",
60
+ # "17:00",
61
+ # "18:00"
62
+ # ]
63
+ # }]
64
+ # EOF
65
+ end
66
+
metadata ADDED
@@ -0,0 +1,270 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: buff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - ZPH
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: webmock
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: guard-rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard-bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rb-fsevent
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: growl
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry-full
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: multi_json
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: yajl-ruby
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: faraday
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: faraday_middleware
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rash
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: addressable
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - '>='
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :runtime
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - '>='
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ description: Buff is an API Wrapper Gem for Bufferapp.com's API
210
+ email:
211
+ - Zander@civet.ws
212
+ executables: []
213
+ extensions: []
214
+ extra_rdoc_files: []
215
+ files:
216
+ - .gitignore
217
+ - .ruby-version
218
+ - .travis.yml
219
+ - Gemfile
220
+ - Guardfile
221
+ - LICENSE.txt
222
+ - README.md
223
+ - Rakefile
224
+ - buff.gemspec
225
+ - lib/buff.rb
226
+ - lib/buff/core.rb
227
+ - lib/buff/encode.rb
228
+ - lib/buff/error.rb
229
+ - lib/buff/link.rb
230
+ - lib/buff/profile.rb
231
+ - lib/buff/update.rb
232
+ - lib/buff/user.rb
233
+ - lib/buff/version.rb
234
+ - spec/lib/buff/encode_spec.rb
235
+ - spec/lib/buff/link_spec.rb
236
+ - spec/lib/buff/update_spec.rb
237
+ - spec/lib/buff_spec.rb
238
+ - spec/lib/core_spec.rb
239
+ - spec/spec_helper.rb
240
+ homepage: http://github.com/zph/buff
241
+ licenses: []
242
+ metadata: {}
243
+ post_install_message:
244
+ rdoc_options: []
245
+ require_paths:
246
+ - lib
247
+ required_ruby_version: !ruby/object:Gem::Requirement
248
+ requirements:
249
+ - - '>='
250
+ - !ruby/object:Gem::Version
251
+ version: '0'
252
+ required_rubygems_version: !ruby/object:Gem::Requirement
253
+ requirements:
254
+ - - '>='
255
+ - !ruby/object:Gem::Version
256
+ version: '0'
257
+ requirements: []
258
+ rubyforge_project:
259
+ rubygems_version: 2.0.3
260
+ signing_key:
261
+ specification_version: 4
262
+ summary: Buff is an API Wrapper Gem for Bufferapp.com's API
263
+ test_files:
264
+ - spec/lib/buff/encode_spec.rb
265
+ - spec/lib/buff/link_spec.rb
266
+ - spec/lib/buff/update_spec.rb
267
+ - spec/lib/buff_spec.rb
268
+ - spec/lib/core_spec.rb
269
+ - spec/spec_helper.rb
270
+ has_rdoc: