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.
@@ -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
@@ -0,0 +1,4 @@
1
+ # encoding: UTF-8
2
+ class Ghee
3
+ VERSION = "0.7.0"
4
+ end
@@ -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