devver-octopi 0.2.8
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/.gitignore +4 -0
- data/.yardoc +0 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE +20 -0
- data/README.rdoc +144 -0
- data/Rakefile +100 -0
- data/VERSION.yml +5 -0
- data/contrib/backup.rb +100 -0
- data/examples/authenticated.rb +20 -0
- data/examples/issues.rb +18 -0
- data/examples/overall.rb +50 -0
- data/lib/ext/hash_ext.rb +5 -0
- data/lib/ext/string_ext.rb +5 -0
- data/lib/octopi.rb +136 -0
- data/lib/octopi/api.rb +213 -0
- data/lib/octopi/base.rb +115 -0
- data/lib/octopi/blob.rb +25 -0
- data/lib/octopi/branch.rb +31 -0
- data/lib/octopi/branch_set.rb +11 -0
- data/lib/octopi/comment.rb +20 -0
- data/lib/octopi/commit.rb +69 -0
- data/lib/octopi/error.rb +35 -0
- data/lib/octopi/file_object.rb +16 -0
- data/lib/octopi/gist.rb +28 -0
- data/lib/octopi/issue.rb +111 -0
- data/lib/octopi/issue_comment.rb +7 -0
- data/lib/octopi/issue_set.rb +21 -0
- data/lib/octopi/key.rb +25 -0
- data/lib/octopi/key_set.rb +14 -0
- data/lib/octopi/plan.rb +5 -0
- data/lib/octopi/repository.rb +132 -0
- data/lib/octopi/repository_set.rb +9 -0
- data/lib/octopi/resource.rb +70 -0
- data/lib/octopi/self.rb +33 -0
- data/lib/octopi/tag.rb +23 -0
- data/lib/octopi/user.rb +123 -0
- data/octopi.gemspec +99 -0
- data/test/api_test.rb +58 -0
- data/test/authenticated_test.rb +39 -0
- data/test/blob_test.rb +23 -0
- data/test/branch_test.rb +20 -0
- data/test/commit_test.rb +82 -0
- data/test/file_object_test.rb +39 -0
- data/test/gist_test.rb +16 -0
- data/test/issue_comment.rb +19 -0
- data/test/issue_set_test.rb +33 -0
- data/test/issue_test.rb +120 -0
- data/test/key_set_test.rb +29 -0
- data/test/key_test.rb +35 -0
- data/test/repository_set_test.rb +23 -0
- data/test/repository_test.rb +151 -0
- data/test/stubs/commits/fcoury/octopi/octopi.rb +818 -0
- data/test/tag_test.rb +20 -0
- data/test/test_helper.rb +246 -0
- data/test/user_test.rb +92 -0
- metadata +153 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
module Octopi
|
2
|
+
module Resource
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
base.set_resource_name(base.name)
|
6
|
+
(@@resources||={})[base.resource_name(:singular)] = base
|
7
|
+
(@@resources||={})[base.resource_name(:plural)] = base
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.for(name)
|
11
|
+
@@resources[name]
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def set_resource_name(singular, plural = "#{singular}s")
|
16
|
+
@resource_name = {:singular => declassify(singular), :plural => declassify(plural)}
|
17
|
+
end
|
18
|
+
|
19
|
+
def resource_name(key)
|
20
|
+
@resource_name[key]
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_path(path)
|
24
|
+
(@path_spec||={})[:create] = path
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_path(path)
|
28
|
+
(@path_spec||={})[:find] = path
|
29
|
+
end
|
30
|
+
|
31
|
+
def resource_path(path)
|
32
|
+
(@path_spec||={})[:resource] = path
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete_path(path)
|
36
|
+
(@path_spec||={})[:delete] = path
|
37
|
+
end
|
38
|
+
|
39
|
+
def find(*args)
|
40
|
+
args = args.join('/') if args.is_a? Array
|
41
|
+
result = Api.api.find(path_for(:resource), @resource_name[:singular], args, self, @cache)
|
42
|
+
key = result.keys.first
|
43
|
+
|
44
|
+
if result[key].is_a? Array
|
45
|
+
result[key].map { |r| new(r) }
|
46
|
+
else
|
47
|
+
Resource.for(key).new(result[key])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_all(*s)
|
52
|
+
find_plural(s, :find)
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_plural(s, path)
|
56
|
+
s = s.join('/') if s.is_a? Array
|
57
|
+
resources = Api.api.find_all(path_for(path), @resource_name[:plural], s, self)
|
58
|
+
resources.map { |item| self.new(item) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def declassify(s)
|
62
|
+
(s.split('::').last || '').downcase if s
|
63
|
+
end
|
64
|
+
|
65
|
+
def path_for(type)
|
66
|
+
@path_spec[type]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/octopi/self.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Octopi
|
2
|
+
module Self
|
3
|
+
# Returns a list of Key objects containing all SSH Public Keys this user
|
4
|
+
# currently has. Requires authentication.
|
5
|
+
def keys
|
6
|
+
raise AuthenticationRequired, "To view keys, you must be authenticated" if Api.api.read_only?
|
7
|
+
result = Api.api.get("/user/keys", { :cache => false })
|
8
|
+
return unless result and result["public_keys"]
|
9
|
+
KeySet.new(result["public_keys"].inject([]) { |result, element| result << Key.new(element) })
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns a list of Email objects containing the email addresses associated with this user.
|
13
|
+
# Requires authentication.
|
14
|
+
def emails
|
15
|
+
raise AuthenticationRequired, "To view emails, you must be authenticated" if Api.api.read_only?
|
16
|
+
get("/user/emails")['emails']
|
17
|
+
end
|
18
|
+
|
19
|
+
# Start following a user.
|
20
|
+
# Can only be called if you are authenticated.
|
21
|
+
def follow!(login)
|
22
|
+
raise AuthenticationRequired, "To begin following someone, you must be authenticated" if Api.api.read_only?
|
23
|
+
Api.api.post("/user/follow/#{login}")
|
24
|
+
end
|
25
|
+
|
26
|
+
# Stop following a user.
|
27
|
+
# Can only be called if you are authenticated.
|
28
|
+
def unfollow!(login)
|
29
|
+
raise AuthenticationRequired, "To stop following someone, you must be authenticated" if Api.api.read_only?
|
30
|
+
Api.api.post("/user/unfollow/#{login}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/octopi/tag.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Tag < Base
|
3
|
+
include Resource
|
4
|
+
|
5
|
+
attr_accessor :name, :sha
|
6
|
+
set_resource_name "tag"
|
7
|
+
|
8
|
+
resource_path "/repos/show/:id"
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
args = args.flatten!
|
12
|
+
self.name = args.first
|
13
|
+
self.sha = args.last
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.all(options={})
|
17
|
+
ensure_hash(options)
|
18
|
+
user, repo = gather_details(options)
|
19
|
+
self.validate_args(user => :user, repo => :repo)
|
20
|
+
find_plural([user, repo, 'tags'], :resource) { |i| Tag.new(i) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/octopi/user.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module Octopi
|
2
|
+
class User < Base
|
3
|
+
include Resource
|
4
|
+
attr_accessor :company, :name, :following_count, :blog, :public_repo_count, :public_gist_count, :id, :login, :followers_count, :created_at, :email, :location, :disk_usage, :private_repo_count, :private_gist_count, :collaborators, :plan, :owned_private_repo_count, :total_private_repo_count,
|
5
|
+
# These come from search results, which doesn't contain the above information.
|
6
|
+
:actions, :score, :language, :followers, :following, :fullname, :type, :username, :repos, :pushed, :created
|
7
|
+
|
8
|
+
def plan=(attributes={})
|
9
|
+
@plan = Plan.new(attributes)
|
10
|
+
end
|
11
|
+
|
12
|
+
find_path "/user/search/:query"
|
13
|
+
resource_path "/user/show/:id"
|
14
|
+
|
15
|
+
# Finds a single user identified by the given username
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
#
|
19
|
+
# user = User.find("fcoury")
|
20
|
+
# puts user.login # should return 'fcoury'
|
21
|
+
def self.find(username)
|
22
|
+
self.validate_args(username => :user)
|
23
|
+
super username
|
24
|
+
end
|
25
|
+
|
26
|
+
# Finds all users whose username matches a given string
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# User.find_all("oe") # Matches joe, moe and monroe
|
31
|
+
#
|
32
|
+
def self.find_all(username)
|
33
|
+
self.validate_args(username => :user)
|
34
|
+
super username
|
35
|
+
end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
alias_method :search, :find_all
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a collection of Repository objects, containing
|
42
|
+
# all repositories of the user.
|
43
|
+
#
|
44
|
+
# If user is the current authenticated user, some
|
45
|
+
# additional information will be provided for the
|
46
|
+
# Repositories.
|
47
|
+
def repositories
|
48
|
+
rs = RepositorySet.new(Repository.find(:user => self.login))
|
49
|
+
rs.user = self
|
50
|
+
rs
|
51
|
+
end
|
52
|
+
|
53
|
+
# Searches for user Repository identified by name
|
54
|
+
def repository(options={})
|
55
|
+
options = { :name => options } if options.is_a?(String)
|
56
|
+
self.class.ensure_hash(options)
|
57
|
+
Repository.find({ :user => login }.merge!(options))
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_repository(name, options = {})
|
61
|
+
self.class.validate_args(name => :repo)
|
62
|
+
Repository.create(self, name, options)
|
63
|
+
end
|
64
|
+
|
65
|
+
def watching
|
66
|
+
repositories = []
|
67
|
+
Api.api.get("/repos/watched/#{login}")["repositories"].each do |repo|
|
68
|
+
repositories << Repository.new(repo)
|
69
|
+
end
|
70
|
+
repositories
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# Gets a list of followers.
|
75
|
+
# Returns an array of logins.
|
76
|
+
def followers
|
77
|
+
user_property("followers")
|
78
|
+
end
|
79
|
+
|
80
|
+
# Gets a list of followers.
|
81
|
+
# Returns an array of user objects.
|
82
|
+
# If user has a large number of followers you may be rate limited by the API.
|
83
|
+
def followers!
|
84
|
+
user_property("followers", true)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Gets a list of people this user is following.
|
88
|
+
# Returns an array of logins.
|
89
|
+
def following
|
90
|
+
user_property("following")
|
91
|
+
end
|
92
|
+
|
93
|
+
# Gets a list of people this user is following.
|
94
|
+
# Returns an array of user objectrs.
|
95
|
+
# If user has a large number of people whom they follow, you may be rate limited by the API.
|
96
|
+
def following!
|
97
|
+
user_property("following", true)
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# If a user object is passed into a method, we can use this.
|
102
|
+
# It'll also work if we pass in just the login.
|
103
|
+
def to_s
|
104
|
+
login
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# Helper method for "deep" finds.
|
110
|
+
# Determines whether to return an array of logins (light) or user objects (heavy).
|
111
|
+
def user_property(property, deep=false)
|
112
|
+
users = []
|
113
|
+
property(property, login).each_pair do |k,v|
|
114
|
+
return v unless deep
|
115
|
+
|
116
|
+
v.each { |u| users << User.find(u) }
|
117
|
+
end
|
118
|
+
|
119
|
+
users
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
data/octopi.gemspec
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{octopi}
|
8
|
+
s.version = "0.2.5"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Felipe Coury"]
|
12
|
+
s.date = %q{2009-12-26}
|
13
|
+
s.email = %q{felipe.coury@gmail.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
".yardoc",
|
21
|
+
"CHANGELOG.md",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION.yml",
|
26
|
+
"contrib/backup.rb",
|
27
|
+
"lib/ext/hash_ext.rb",
|
28
|
+
"lib/ext/string_ext.rb",
|
29
|
+
"lib/octopi.rb",
|
30
|
+
"lib/octopi/api.rb",
|
31
|
+
"lib/octopi/base.rb",
|
32
|
+
"lib/octopi/blob.rb",
|
33
|
+
"lib/octopi/branch.rb",
|
34
|
+
"lib/octopi/branch_set.rb",
|
35
|
+
"lib/octopi/comment.rb",
|
36
|
+
"lib/octopi/commit.rb",
|
37
|
+
"lib/octopi/error.rb",
|
38
|
+
"lib/octopi/file_object.rb",
|
39
|
+
"lib/octopi/gist.rb",
|
40
|
+
"lib/octopi/issue.rb",
|
41
|
+
"lib/octopi/issue_comment.rb",
|
42
|
+
"lib/octopi/issue_set.rb",
|
43
|
+
"lib/octopi/key.rb",
|
44
|
+
"lib/octopi/key_set.rb",
|
45
|
+
"lib/octopi/plan.rb",
|
46
|
+
"lib/octopi/repository.rb",
|
47
|
+
"lib/octopi/repository_set.rb",
|
48
|
+
"lib/octopi/resource.rb",
|
49
|
+
"lib/octopi/self.rb",
|
50
|
+
"lib/octopi/tag.rb",
|
51
|
+
"lib/octopi/user.rb",
|
52
|
+
"octopi.gemspec"
|
53
|
+
]
|
54
|
+
s.homepage = %q{http://github.com/fcoury/octopi}
|
55
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
56
|
+
s.require_paths = ["lib"]
|
57
|
+
s.rubyforge_project = %q{octopi}
|
58
|
+
s.rubygems_version = %q{1.3.5}
|
59
|
+
s.summary = %q{A Ruby interface to GitHub API v2}
|
60
|
+
s.test_files = [
|
61
|
+
"test/api_test.rb",
|
62
|
+
"test/authenticated_test.rb",
|
63
|
+
"test/blob_test.rb",
|
64
|
+
"test/branch_test.rb",
|
65
|
+
"test/commit_test.rb",
|
66
|
+
"test/file_object_test.rb",
|
67
|
+
"test/gist_test.rb",
|
68
|
+
"test/issue_comment.rb",
|
69
|
+
"test/issue_set_test.rb",
|
70
|
+
"test/issue_test.rb",
|
71
|
+
"test/key_set_test.rb",
|
72
|
+
"test/key_test.rb",
|
73
|
+
"test/repository_set_test.rb",
|
74
|
+
"test/repository_test.rb",
|
75
|
+
"test/stubs/commits/fcoury/octopi/octopi.rb",
|
76
|
+
"test/tag_test.rb",
|
77
|
+
"test/test_helper.rb",
|
78
|
+
"test/user_test.rb",
|
79
|
+
"examples/authenticated.rb",
|
80
|
+
"examples/issues.rb",
|
81
|
+
"examples/overall.rb"
|
82
|
+
]
|
83
|
+
|
84
|
+
if s.respond_to? :specification_version then
|
85
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
86
|
+
s.specification_version = 3
|
87
|
+
|
88
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
89
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 1.3.1"])
|
90
|
+
s.add_runtime_dependency(%q<httparty>, [">= 0.4.5"])
|
91
|
+
else
|
92
|
+
s.add_dependency(%q<nokogiri>, [">= 1.3.1"])
|
93
|
+
s.add_dependency(%q<httparty>, [">= 0.4.5"])
|
94
|
+
end
|
95
|
+
else
|
96
|
+
s.add_dependency(%q<nokogiri>, [">= 1.3.1"])
|
97
|
+
s.add_dependency(%q<httparty>, [">= 0.4.5"])
|
98
|
+
end
|
99
|
+
end
|
data/test/api_test.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class AuthenticatedTest < Test::Unit::TestCase
|
4
|
+
include Octopi
|
5
|
+
|
6
|
+
def setup
|
7
|
+
fake_everything
|
8
|
+
@user = User.find("fcoury")
|
9
|
+
end
|
10
|
+
|
11
|
+
context "following" do
|
12
|
+
|
13
|
+
should "not be able to follow anyone if not authenticated" do
|
14
|
+
exception = assert_raise AuthenticationRequired do
|
15
|
+
Api.me.follow!("rails")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
should "be able to follow a user" do
|
20
|
+
auth do
|
21
|
+
assert_not_nil Api.me.follow!("rails")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "unfollowing" do
|
27
|
+
|
28
|
+
should "not be able to follow anyone if not authenticated" do
|
29
|
+
exception = assert_raise AuthenticationRequired do
|
30
|
+
Api.me.unfollow!("rails")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
should "be able to follow a user" do
|
35
|
+
auth do
|
36
|
+
assert_not_nil Api.me.unfollow!("rails")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "keys" do
|
42
|
+
should "not be able to see keys if not authenticated" do
|
43
|
+
exception = assert_raise AuthenticationRequired do
|
44
|
+
Api.me.keys
|
45
|
+
end
|
46
|
+
|
47
|
+
assert_equal "To view keys, you must be authenticated", exception.message
|
48
|
+
end
|
49
|
+
|
50
|
+
should "have some keys" do
|
51
|
+
auth do
|
52
|
+
keys = Api.me.keys
|
53
|
+
assert keys.is_a?(KeySet)
|
54
|
+
assert_equal 2, keys.size
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class AuthenticatedTest < Test::Unit::TestCase
|
4
|
+
include Octopi
|
5
|
+
def setup
|
6
|
+
fake_everything
|
7
|
+
end
|
8
|
+
|
9
|
+
context "Authenticating" do
|
10
|
+
should "be possible with username and password" do
|
11
|
+
authenticated_with(:login => "fcoury", :password => "yruocf") do
|
12
|
+
assert_equal "8f700e0d7747826f3e56ee13651414bd", Api.api.token
|
13
|
+
assert_not_nil User.find("fcoury")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
should "be possible with username and token" do
|
18
|
+
auth do
|
19
|
+
assert_not_nil User.find("fcoury")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
should "be possible using the .gitconfig" do
|
24
|
+
authenticated do
|
25
|
+
assert_not_nil User.find("fcoury")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
#
|
29
|
+
# should "be denied access when specifying an invalid token and login combination" do
|
30
|
+
# FakeWeb.clean_registry
|
31
|
+
# FakeWeb.register_uri(:get, "http://github.com/api/v2/yaml/user/show/fcoury", :status => ["404", "Not Found"])
|
32
|
+
# assert_raise InvalidLogin do
|
33
|
+
# authenticated_with :login => "fcoury", :token => "ba7bf2d7f0ebc073d3874dda887b18ae" do
|
34
|
+
# # Just blank will do us fine.
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
end
|
39
|
+
end
|