ghee 0.7.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.
- data/.autotest +1 -0
- data/.gitignore +5 -0
- data/.rspec +0 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +353 -0
- data/Rakefile +15 -0
- data/ghee.gemspec +31 -0
- data/lib/ghee.rb +49 -0
- data/lib/ghee/api/authorizations.rb +14 -0
- data/lib/ghee/api/events.rb +25 -0
- data/lib/ghee/api/gists.rb +75 -0
- data/lib/ghee/api/issues.rb +98 -0
- data/lib/ghee/api/milestones.rb +47 -0
- data/lib/ghee/api/orgs.rb +103 -0
- data/lib/ghee/api/repos.rb +96 -0
- data/lib/ghee/api/users.rb +72 -0
- data/lib/ghee/connection.rb +33 -0
- data/lib/ghee/resource_proxy.rb +111 -0
- data/lib/ghee/state_methods.rb +29 -0
- data/lib/ghee/version.rb +4 -0
- data/spec/ghee/api/authorizations_spec.rb +46 -0
- data/spec/ghee/api/events_spec.rb +47 -0
- data/spec/ghee/api/gists_spec.rb +153 -0
- data/spec/ghee/api/hooks_spec.rb +52 -0
- data/spec/ghee/api/issues_spec.rb +122 -0
- data/spec/ghee/api/milestones_spec.rb +103 -0
- data/spec/ghee/api/orgs_spec.rb +56 -0
- data/spec/ghee/api/repos_spec.rb +126 -0
- data/spec/ghee/api/teams_spec.rb +90 -0
- data/spec/ghee/api/users_spec.rb +102 -0
- data/spec/ghee/connection_spec.rb +54 -0
- data/spec/ghee/resource_proxy_spec.rb +207 -0
- data/spec/ghee_spec.rb +42 -0
- data/spec/settings.yml.sample +5 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +16 -0
- metadata +208 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
class Ghee
|
2
|
+
|
3
|
+
# API module encapsulates all of API endpoints
|
4
|
+
# implemented thus far
|
5
|
+
#
|
6
|
+
module API
|
7
|
+
|
8
|
+
# The Users module handles all of the Github User
|
9
|
+
# API endpoints
|
10
|
+
#
|
11
|
+
module Users
|
12
|
+
|
13
|
+
# Users::Proxy inherits from Ghee::Proxy and
|
14
|
+
# enables defining methods on the proxy object
|
15
|
+
#
|
16
|
+
class Proxy < ::Ghee::ResourceProxy
|
17
|
+
include Ghee::CUD
|
18
|
+
|
19
|
+
# Gists for a user
|
20
|
+
#
|
21
|
+
# Returns json
|
22
|
+
#
|
23
|
+
def gists(params={})
|
24
|
+
Ghee::API::Gists::Proxy.new(connection,"#{path_prefix}/gists",params)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Repos for a user
|
28
|
+
#
|
29
|
+
# Returns json
|
30
|
+
#
|
31
|
+
def repos(name=nil, params={})
|
32
|
+
params = name if name.is_a?Hash
|
33
|
+
prefix = name.is_a?(String) ? "/repos/#{self["login"]}/#{name}" : "#{path_prefix}/repos"
|
34
|
+
Ghee::API::Repos::Proxy.new(connection,prefix, params)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns list of the provided users organizations or
|
38
|
+
# an organization by name
|
39
|
+
#
|
40
|
+
# org - String name of the organization (optional)
|
41
|
+
#
|
42
|
+
# Returns json
|
43
|
+
#
|
44
|
+
def orgs(org=nil, params={})
|
45
|
+
params = org if org.is_a?Hash
|
46
|
+
prefix = org.is_a?(String) ? "/orgs/#{org}" : "#{path_prefix}/orgs"
|
47
|
+
Ghee::API::Orgs::Proxy.new(connection, prefix, params)
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get authenticated user
|
54
|
+
#
|
55
|
+
# Returns json
|
56
|
+
#
|
57
|
+
def user
|
58
|
+
Proxy.new(connection, '/user')
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get a single user
|
62
|
+
#
|
63
|
+
# user - String of user login
|
64
|
+
#
|
65
|
+
# Returns json
|
66
|
+
#
|
67
|
+
def users(user)
|
68
|
+
Proxy.new(connection, "/users/#{user}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require 'multi_json'
|
4
|
+
|
5
|
+
class Ghee
|
6
|
+
class Connection < Faraday::Connection
|
7
|
+
attr_reader :hash
|
8
|
+
|
9
|
+
# Instantiates connection, accepts an options hash
|
10
|
+
# for authenticated access
|
11
|
+
#
|
12
|
+
# OAuth2 expects
|
13
|
+
# :access_token => "OAuth Token"
|
14
|
+
#
|
15
|
+
# Basic auth expects
|
16
|
+
# :basic_auth => {:user_name => "octocat", :password => "secret"}
|
17
|
+
def initialize(hash={})
|
18
|
+
@hash = hash
|
19
|
+
access_token = hash[:access_token] if hash.has_key?:access_token
|
20
|
+
basic_auth = hash[:basic_auth] if hash.has_key?:basic_auth
|
21
|
+
|
22
|
+
super('https://api.github.com') do |builder|
|
23
|
+
builder.use Faraday::Request::JSON
|
24
|
+
builder.use Faraday::Response::ParseJson
|
25
|
+
builder.adapter Faraday.default_adapter
|
26
|
+
end
|
27
|
+
|
28
|
+
self.headers["Authorization"] = "token #{access_token}" if access_token
|
29
|
+
self.basic_auth(basic_auth[:user_name], basic_auth[:password]) if basic_auth
|
30
|
+
self.headers["Accept"] = 'application/json'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
class Ghee
|
2
|
+
|
3
|
+
# ResourceProxy lets us create a virtual
|
4
|
+
# proxy for any API resource, utilizing
|
5
|
+
# method_missing to handle passing
|
6
|
+
# messages to the real object
|
7
|
+
#
|
8
|
+
class ResourceProxy
|
9
|
+
|
10
|
+
# Undefine methods that might get in the way
|
11
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval|instance_variable_get|object_id/ }
|
12
|
+
|
13
|
+
# Make connection and path_prefix readable
|
14
|
+
attr_reader :connection, :path_prefix, :params
|
15
|
+
|
16
|
+
# Expose pagination data
|
17
|
+
attr_reader :current_page, :total, :pagination
|
18
|
+
|
19
|
+
# Instantiates proxy with the connection
|
20
|
+
# and path_prefix
|
21
|
+
#
|
22
|
+
# connection - Ghee::Connection object
|
23
|
+
# path_prefix - String
|
24
|
+
#
|
25
|
+
def initialize(connection, path_prefix, params = {})
|
26
|
+
@connection, @path_prefix, @params = connection, path_prefix, params
|
27
|
+
end
|
28
|
+
|
29
|
+
# Method_missing takes any message passed
|
30
|
+
# to the ResourceProxy and sends it to the
|
31
|
+
# real object
|
32
|
+
#
|
33
|
+
# message - Message object
|
34
|
+
# args* - Arguements passed
|
35
|
+
#
|
36
|
+
def method_missing(message, *args, &block)
|
37
|
+
subject.send(message, *args, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Subject is the response body parsed
|
41
|
+
# as json
|
42
|
+
#
|
43
|
+
# Returns json
|
44
|
+
#
|
45
|
+
def subject
|
46
|
+
@subject ||= connection.get(path_prefix){|req| req.params.merge!params }.body
|
47
|
+
end
|
48
|
+
|
49
|
+
# Paginate is a helper method to handle
|
50
|
+
# request pagination to the github api
|
51
|
+
#
|
52
|
+
# options - Hash containing pagination params
|
53
|
+
# eg;
|
54
|
+
# :per_page => 100, :page => 1
|
55
|
+
#
|
56
|
+
# Returns self
|
57
|
+
#
|
58
|
+
def paginate(options)
|
59
|
+
@current_page = options.fetch(:page) {raise ArgumentError, ":page parameter required"}
|
60
|
+
per_page = options.delete(:per_page) || 30
|
61
|
+
response = connection.get do |req|
|
62
|
+
req.url path_prefix, :per_page => per_page, :page => current_page
|
63
|
+
req.params.merge! params
|
64
|
+
end
|
65
|
+
|
66
|
+
if @subject.nil?
|
67
|
+
@subject = response.body
|
68
|
+
else
|
69
|
+
@subject = @subject.concat response.body
|
70
|
+
end
|
71
|
+
|
72
|
+
parse_link_header response.headers.delete("link")
|
73
|
+
|
74
|
+
return self
|
75
|
+
end
|
76
|
+
|
77
|
+
def all
|
78
|
+
return self if pagination && next_page.nil?
|
79
|
+
|
80
|
+
self.paginate :per_page => 100, :page => next_page || 1
|
81
|
+
|
82
|
+
self.all
|
83
|
+
end
|
84
|
+
|
85
|
+
# Generate first_page, last_page, next_page, prev_page convienence methods
|
86
|
+
%w{ next prev first last }.each do |term|
|
87
|
+
define_method "#{term}_page" do
|
88
|
+
pagination ? pagination[term.to_sym] ? pagination[term.to_sym][:page] : nil : nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def parse_link_header(header)
|
95
|
+
return @total = subject.size, @pagination = {} if header.nil?
|
96
|
+
require 'cgi'
|
97
|
+
pattern = /<(?<link>.*)>;\s+rel="(?<rel>.*)"/
|
98
|
+
matches = {}
|
99
|
+
header.split(',').each do |m|
|
100
|
+
match = pattern.match m
|
101
|
+
uri = URI.parse(match[:link])
|
102
|
+
uri_params = CGI.parse(uri.query)
|
103
|
+
page = uri_params["page"].first.to_i
|
104
|
+
per_page = uri_params["per_page"] ? uri_params["per_page"].first.to_i : 30
|
105
|
+
matches[match[:rel].to_sym] = {:link => match[:link], :page => page, :per_page => per_page}
|
106
|
+
end
|
107
|
+
@pagination = matches
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Ghee
|
2
|
+
module CUD
|
3
|
+
|
4
|
+
# Creates
|
5
|
+
#
|
6
|
+
# return json
|
7
|
+
#
|
8
|
+
def create(attributes)
|
9
|
+
connection.post(path_prefix,attributes).body
|
10
|
+
end
|
11
|
+
|
12
|
+
# Patchs
|
13
|
+
#
|
14
|
+
# return json
|
15
|
+
#
|
16
|
+
def patch(attributes)
|
17
|
+
connection.patch(path_prefix, attributes).body
|
18
|
+
end
|
19
|
+
|
20
|
+
# Destroys
|
21
|
+
#
|
22
|
+
# return boolean
|
23
|
+
#
|
24
|
+
def destroy
|
25
|
+
connection.delete(path_prefix).status == 204
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/lib/ghee/version.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ghee::API::Authorizations do
|
4
|
+
subject { Ghee.new(GH_AUTH) }
|
5
|
+
|
6
|
+
describe "#authorizations" do
|
7
|
+
|
8
|
+
context "with a test authorization" do
|
9
|
+
before :all do
|
10
|
+
VCR.use_cassette "authorizations.create.test" do
|
11
|
+
@test_auth = subject.authorizations.create :scopes => ["public_repo"]
|
12
|
+
@test_auth.should_not be_nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:test_auth) {@test_auth}
|
17
|
+
|
18
|
+
it "should return an auth" do
|
19
|
+
VCR.use_cassette "authorizations(id)" do
|
20
|
+
auth = subject.authorizations(test_auth["id"])
|
21
|
+
auth["scopes"].should include("public_repo")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return a list of auths" do
|
26
|
+
VCR.use_cassette "authorizations" do
|
27
|
+
auth = subject.authorizations
|
28
|
+
auth.size().should > 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should patch an auth" do
|
33
|
+
VCR.use_cassette "authorization(id).patch" do
|
34
|
+
auth = subject.authorizations(test_auth["id"]).patch :add_scopes => ["repo"]
|
35
|
+
auth["scopes"].should include("repo")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
after :all do
|
40
|
+
VCR.use_cassette "authorizations(id).destroy" do
|
41
|
+
subject.authorizations(test_auth["id"]).destroy.should be_true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ghee::API::Events do
|
4
|
+
subject { Ghee.new(GH_AUTH) }
|
5
|
+
|
6
|
+
EventTypes = ["CommitComment","CreateEvent","DeleteEvent","DownloadEvent","FollowEvent",
|
7
|
+
"ForkEvent","ForkApplyEvent","GistEvent","GollumEvent","IssueCommentEvent",
|
8
|
+
"IssuesEvent","MemberEvent","PublicEvent","PullRequestEvent","PushEvent",
|
9
|
+
"TeamAddEvent","WatchEvent"]
|
10
|
+
|
11
|
+
def should_be_an_event(event)
|
12
|
+
EventTypes.should include(event['type'])
|
13
|
+
event['repo'].should be_instance_of(Hash)
|
14
|
+
event['actor'].should be_instance_of(Hash)
|
15
|
+
event['org'].should be_instance_of(Hash)
|
16
|
+
event['created_at'].should_not be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#events" do
|
20
|
+
it "should return public events" do
|
21
|
+
VCR.use_cassette('events') do
|
22
|
+
events = subject.events
|
23
|
+
events.size.should > 0
|
24
|
+
should_be_an_event(events.first)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
describe "#paginate" do
|
28
|
+
it "should return page 1" do
|
29
|
+
VCR.use_cassette "events.page1" do
|
30
|
+
events = subject.events.paginate :page => 1
|
31
|
+
events.size.should > 0
|
32
|
+
events.next_page.should == 2
|
33
|
+
should_be_an_event(events.first)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
it "should return page 2" do
|
37
|
+
VCR.use_cassette "events.page2" do
|
38
|
+
events = subject.events.paginate :page => 2
|
39
|
+
events.size.should > 0
|
40
|
+
events.next_page.should == 3
|
41
|
+
events.prev_page.should == 1
|
42
|
+
should_be_an_event(events.first)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ghee::API::Gists do
|
4
|
+
subject { Ghee.new(GH_AUTH) }
|
5
|
+
|
6
|
+
def should_be_a_gist(gist)
|
7
|
+
gist['url'].should include('https://api.github.com/gists/')
|
8
|
+
gist['user']['url'].should include('https://api.github.com/users/')
|
9
|
+
gist['created_at'].should_not be_nil
|
10
|
+
gist['files'].should be_instance_of(Hash)
|
11
|
+
gist['files'].size.should > 0
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#users" do
|
15
|
+
describe "#gists" do
|
16
|
+
it "should return users gists" do
|
17
|
+
VCR.use_cassette('users(login).gists') do
|
18
|
+
gists = subject.users('jonmagic').gists
|
19
|
+
gists.size.should > 0
|
20
|
+
should_be_a_gist(gists.first)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#gists" do
|
27
|
+
it "should return gists for authenticated user" do
|
28
|
+
VCR.use_cassette('gists') do
|
29
|
+
gists = subject.gists
|
30
|
+
gists.size.should > 0
|
31
|
+
should_be_a_gist(gists.first)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#public" do
|
36
|
+
it "should return public gists" do
|
37
|
+
VCR.use_cassette('gists.public') do
|
38
|
+
gists = subject.gists.public
|
39
|
+
gists.size.should > 0
|
40
|
+
should_be_a_gist(gists.first)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#starred" do
|
46
|
+
it "should return starred gists" do
|
47
|
+
VCR.use_cassette('gists.starred') do
|
48
|
+
gists = subject.gists.starred
|
49
|
+
gists.size.should > 0
|
50
|
+
should_be_a_gist(gists.first)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#create" do
|
56
|
+
it "should create a gist" do
|
57
|
+
VCR.use_cassette('gists.create') do
|
58
|
+
gist = subject.gists.create({
|
59
|
+
:description => "Testing the ghee api",
|
60
|
+
:public => true,
|
61
|
+
:files => {
|
62
|
+
'ghee_test.txt' => {
|
63
|
+
:content => "Booya!"
|
64
|
+
}
|
65
|
+
}
|
66
|
+
})
|
67
|
+
should_be_a_gist(gist)
|
68
|
+
subject.gists(gist['id']).destroy
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "with gist id" do
|
74
|
+
before(:all) do
|
75
|
+
VCR.use_cassette('gists.test') do
|
76
|
+
@test_gist = subject.gists.create({:public => false, :files => {'file.txt' => {:content => 'ready to destroy'}}})
|
77
|
+
end
|
78
|
+
end
|
79
|
+
let(:test_gist) { @test_gist }
|
80
|
+
|
81
|
+
it "should return single gist" do
|
82
|
+
VCR.use_cassette('gists(id)') do
|
83
|
+
gist = subject.gists(test_gist['id'])
|
84
|
+
gist['id'].should == test_gist['id']
|
85
|
+
should_be_a_gist(gist)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#patch" do
|
90
|
+
it "should patch the gist" do
|
91
|
+
VCR.use_cassette('gists(id).patch') do
|
92
|
+
gist = subject.gists(test_gist['id']).patch({
|
93
|
+
:files => {
|
94
|
+
'test.md' => {
|
95
|
+
:content => 'clarified butter'
|
96
|
+
}
|
97
|
+
}
|
98
|
+
})
|
99
|
+
should_be_a_gist(gist)
|
100
|
+
gist['files']['test.md']['content'].should == 'clarified butter'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#star" do
|
106
|
+
it "should star the gist" do
|
107
|
+
VCR.use_cassette('gists(id).star') do
|
108
|
+
subject.gists(test_gist['id']).star.should be_true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#unstar" do
|
114
|
+
it "should star the gist" do
|
115
|
+
VCR.use_cassette('gists(id).unstar') do
|
116
|
+
subject.gists(test_gist['id']).unstar.should be_true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "#starred?" do
|
122
|
+
it "should return true if gist is starred" do
|
123
|
+
VCR.use_cassette('gists(id).starred? is true') do
|
124
|
+
subject.gists(test_gist['id']).star
|
125
|
+
subject.gists(test_gist['id']).starred?.should be_true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should return false if gist is unstarred" do
|
130
|
+
VCR.use_cassette('gists(id).starred? is false') do
|
131
|
+
subject.gists(test_gist['id']).unstar
|
132
|
+
subject.gists(test_gist['id']).starred?.should be_false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "#destroy" do
|
138
|
+
it "should delete the gist and return true" do
|
139
|
+
VCR.use_cassette('gists(id).destroy true') do
|
140
|
+
gist = subject.gists.create({:public => false, :files => {'file.txt' => {:content => 'ready to destroy'}}})
|
141
|
+
subject.gists(gist['id']).destroy.should be_true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should return false if gist doesn't exist" do
|
146
|
+
VCR.use_cassette('gists(id).destroy false') do
|
147
|
+
subject.gists("12345678901234567890").destroy.should be_false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|