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
data/lib/octopi/blob.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "resource")
|
2
|
+
module Octopi
|
3
|
+
class Blob < Base
|
4
|
+
attr_accessor :text, :data, :name, :sha, :size, :mode, :mime_type
|
5
|
+
include Resource
|
6
|
+
set_resource_name "blob"
|
7
|
+
|
8
|
+
resource_path "/blob/show/:id"
|
9
|
+
|
10
|
+
def self.find(options={})
|
11
|
+
ensure_hash(options)
|
12
|
+
user, repo = gather_details(options)
|
13
|
+
sha = options[:sha]
|
14
|
+
path = options[:path]
|
15
|
+
|
16
|
+
self.validate_args(sha => :sha, user => :user)
|
17
|
+
|
18
|
+
if path
|
19
|
+
super [user, repo, sha, path]
|
20
|
+
else
|
21
|
+
Api.api.get_raw(path_for(:resource), {:id => [user, repo, sha].join('/')})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Branch < Base
|
3
|
+
attr_accessor :name, :sha
|
4
|
+
include Resource
|
5
|
+
set_resource_name "branch", "branches"
|
6
|
+
|
7
|
+
resource_path "/repos/show/:id"
|
8
|
+
|
9
|
+
# Called when we ask for a resource.
|
10
|
+
# Arguments are passed in like [<name>, <sha>]
|
11
|
+
# TODO: Find out why args are doubly nested
|
12
|
+
def initialize(*args)
|
13
|
+
args = args.flatten!
|
14
|
+
self.name = args.first
|
15
|
+
self.sha = args.last
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
name
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.all(options={})
|
23
|
+
ensure_hash(options)
|
24
|
+
user, repo = gather_details(options)
|
25
|
+
self.validate_args(user => :user, repo => :repo)
|
26
|
+
BranchSet.new(find_plural([user, repo, 'branches'], :resource)) do |i|
|
27
|
+
{ :name => i.first, :hash => i.last }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "branch")
|
2
|
+
class Octopi::BranchSet < Array
|
3
|
+
include Octopi
|
4
|
+
attr_accessor :user, :repository
|
5
|
+
# Takes a name, returns a branch if it exists
|
6
|
+
def find(name)
|
7
|
+
branch = detect { |b| b.name == name }
|
8
|
+
raise NotFound, Branch if branch.nil?
|
9
|
+
branch
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Comment < Base
|
3
|
+
attr_accessor :content, :author, :title, :updated, :link, :published, :id, :repository
|
4
|
+
include Resource
|
5
|
+
set_resource_name "tree"
|
6
|
+
|
7
|
+
resource_path "/tree/show/:id"
|
8
|
+
|
9
|
+
def self.find(options={})
|
10
|
+
ensure_hash(options)
|
11
|
+
user, repo, branch, sha = gather_details(options)
|
12
|
+
self.validate_args(sha => :sha, user => :user, repo => :repo)
|
13
|
+
super [user, repo, sha]
|
14
|
+
end
|
15
|
+
|
16
|
+
def commit
|
17
|
+
Commit.find(:user => repository.owner, :repo => repository, :sha => /commit\/(.*?)#/.match(link)[1])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Commit < Base
|
3
|
+
include Resource
|
4
|
+
find_path "/commits/list/:query"
|
5
|
+
resource_path "/commits/show/:id"
|
6
|
+
|
7
|
+
attr_accessor :repository, :message, :parents, :author, :url, :id, :committed_date, :authored_date, :tree, :committer, :added, :removed, :modified
|
8
|
+
|
9
|
+
|
10
|
+
# Finds all commits for the given options:
|
11
|
+
#
|
12
|
+
# :repo or :repository or :name - A repository object or the name of a repository
|
13
|
+
# :user - A user object or the login of a user
|
14
|
+
# :branch - A branch object or the name of a branch. Defaults to master.
|
15
|
+
#
|
16
|
+
# Sample usage:
|
17
|
+
#
|
18
|
+
# >> find_all(:user => "fcoury", :repo => "octopi")
|
19
|
+
# => <Latest 30 commits for master branch>
|
20
|
+
#
|
21
|
+
# => find_all(:user => "fcoury", :repo => "octopi", :branch => "lazy") # branch is set to lazy.
|
22
|
+
# => <Latest 30 commits for lazy branch>
|
23
|
+
#
|
24
|
+
def self.find_all(options={})
|
25
|
+
ensure_hash(options)
|
26
|
+
user, repo, branch = gather_details(options)
|
27
|
+
commits = if options[:path]
|
28
|
+
super user, repo.name, branch, options[:path]
|
29
|
+
else
|
30
|
+
super user, repo.name, branch
|
31
|
+
end
|
32
|
+
# Repository is not passed in from the data, set it manually.
|
33
|
+
commits.each { |c| c.repository = repo }
|
34
|
+
commits
|
35
|
+
end
|
36
|
+
|
37
|
+
# Finds all commits for the given options:
|
38
|
+
#
|
39
|
+
# :repo or :repository or :name - A repository object or the name of a repository
|
40
|
+
# :user - A user object or the login of a user
|
41
|
+
# :branch - A branch object or the name of a branch. Defaults to master.
|
42
|
+
# :sha - The commit ID
|
43
|
+
#
|
44
|
+
# Sample usage:
|
45
|
+
#
|
46
|
+
# >> find(:user => "fcoury", :repo => "octopi", :sha => "f6609209c3ac0badd004512d318bfaa508ea10ae")
|
47
|
+
# => <Commit f6609209c3ac0badd004512d318bfaa508ea10ae for branch master>
|
48
|
+
#
|
49
|
+
# >> find(:user => "fcoury", :repo => "octopi", :branch => "lazy", :sha => "f6609209c3ac0badd004512d318bfaa508ea10ae") # branch is set to lazy.
|
50
|
+
# => <Commit f6609209c3ac0badd004512d318bfaa508ea10ae for branch lazy>
|
51
|
+
#
|
52
|
+
def self.find(options={})
|
53
|
+
ensure_hash(options)
|
54
|
+
user, repo, branch, sha = gather_details(options)
|
55
|
+
super [user, repo, sha]
|
56
|
+
end
|
57
|
+
|
58
|
+
def repo_identifier
|
59
|
+
url_parts = url.split('/')
|
60
|
+
if @repository
|
61
|
+
parts = [@repository.owner, @repository.name, url_parts[6]]
|
62
|
+
else
|
63
|
+
parts = [url_parts[3], url_parts[4], url_parts[6]]
|
64
|
+
end
|
65
|
+
|
66
|
+
parts.join('/')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/octopi/error.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Octopi
|
2
|
+
|
3
|
+
class FormatError < StandardError
|
4
|
+
def initialize(f)
|
5
|
+
super("Got unexpected format (got #{f.first} for #{f.last})")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class AuthenticationRequired < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class APIError < StandardError
|
13
|
+
end
|
14
|
+
|
15
|
+
class InvalidLogin < StandardError
|
16
|
+
end
|
17
|
+
|
18
|
+
class RetryableAPIError < RuntimeError
|
19
|
+
attr_reader :code
|
20
|
+
def initialize(code=nil)
|
21
|
+
@code = code.nil? ? '???' : code
|
22
|
+
@message = "GitHub returned status #{@code}. Retrying request."
|
23
|
+
super @message
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ArgumentMustBeHash < Exception; end
|
28
|
+
|
29
|
+
|
30
|
+
class NotFound < Exception
|
31
|
+
def initialize(klass)
|
32
|
+
super "The #{klass.to_s.split("::").last} you were looking for could not be found, or is private."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Octopi
|
2
|
+
class FileObject < Base
|
3
|
+
attr_accessor :name, :sha, :mode, :type
|
4
|
+
|
5
|
+
include Resource
|
6
|
+
set_resource_name "tree"
|
7
|
+
resource_path "/tree/show/:id"
|
8
|
+
|
9
|
+
def self.find(options={})
|
10
|
+
ensure_hash(options)
|
11
|
+
user, repo, branch, sha = gather_details(options)
|
12
|
+
self.validate_args(sha => :sha, user => :user, repo => :repo)
|
13
|
+
super [user, repo, sha]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/octopi/gist.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Octopi
|
2
|
+
# Gist API is... lacking at the moment.
|
3
|
+
# This class serves only as a reminder to implement it later
|
4
|
+
class Gist < Base
|
5
|
+
include HTTParty
|
6
|
+
attr_accessor :description, :repo, :public, :created_at
|
7
|
+
|
8
|
+
include Resource
|
9
|
+
set_resource_name "tree"
|
10
|
+
resource_path ":id"
|
11
|
+
|
12
|
+
def self.base_uri
|
13
|
+
"http://gist.github.com/api/v1/yaml"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.find(id)
|
17
|
+
result = get("#{base_uri}/#{id}")
|
18
|
+
# This returns an array of Gists, rather than a single record.
|
19
|
+
new(result["gists"].first)
|
20
|
+
end
|
21
|
+
|
22
|
+
# def files
|
23
|
+
# gists_folder = File.join(ENV['HOME'], ".octopi", "gists")
|
24
|
+
# File.mkdir_p(gists_folder)
|
25
|
+
# `git clone git://`
|
26
|
+
# end
|
27
|
+
end
|
28
|
+
end
|
data/lib/octopi/issue.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Issue < Base
|
3
|
+
include Resource
|
4
|
+
STATES = %w{open closed}
|
5
|
+
|
6
|
+
find_path "/issues/list/:query"
|
7
|
+
resource_path "/issues/show/:id"
|
8
|
+
|
9
|
+
|
10
|
+
attr_accessor :repository, :user, :updated_at, :votes, :number, :title, :body, :closed_at, :labels, :state, :created_at
|
11
|
+
|
12
|
+
def self.search(options={})
|
13
|
+
ensure_hash(options)
|
14
|
+
options[:state] ||= "open"
|
15
|
+
user, repo = gather_details(options)
|
16
|
+
Api.api.get("/issues/search/#{user}/#{repo}/#{options[:state]}/#{options[:keyword]}")
|
17
|
+
end
|
18
|
+
|
19
|
+
# Finds all issues for a given Repository
|
20
|
+
#
|
21
|
+
# You can provide the user and repo parameters as
|
22
|
+
# String or as User and Repository objects. When repo
|
23
|
+
# is provided as a Repository object, user is superfluous.
|
24
|
+
#
|
25
|
+
# If no state is given, "open" is assumed.
|
26
|
+
#
|
27
|
+
# Sample usage:
|
28
|
+
#
|
29
|
+
# find_all(repo, :state => "closed") # repo must be an object
|
30
|
+
# find_all("octopi", :user => "fcoury") # user must be provided
|
31
|
+
# find_all(:user => "fcoury", :repo => "octopi") # state defaults to open
|
32
|
+
#
|
33
|
+
def self.find_all(options={})
|
34
|
+
ensure_hash(options)
|
35
|
+
user, repo = gather_details(options)
|
36
|
+
state = (options[:state] || "open").downcase
|
37
|
+
validate_args(user => :user, repo.name => :repo, state => :state)
|
38
|
+
|
39
|
+
issues = super user, repo.name, state
|
40
|
+
issues.each { |i| i.repository = repo }
|
41
|
+
issues
|
42
|
+
end
|
43
|
+
|
44
|
+
# TODO: Make find use hashes like find_all
|
45
|
+
def self.find(options={})
|
46
|
+
ensure_hash(options)
|
47
|
+
# Do not cache issues, as they may change via other means.
|
48
|
+
@cache = false
|
49
|
+
user, repo = gather_details(options)
|
50
|
+
|
51
|
+
validate_args(user => :user, repo => :repo)
|
52
|
+
issue = super user, repo, options[:number]
|
53
|
+
issue.repository = repo
|
54
|
+
issue
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.open(options={})
|
58
|
+
ensure_hash(options)
|
59
|
+
user, repo = gather_details(options)
|
60
|
+
data = Api.api.post("/issues/open/#{user}/#{repo.name}", options[:params])
|
61
|
+
issue = new(data['issue'])
|
62
|
+
issue.repository = repo
|
63
|
+
issue
|
64
|
+
end
|
65
|
+
|
66
|
+
# Re-opens an issue.
|
67
|
+
def reopen!
|
68
|
+
data = Api.api.post(command_path("reopen"))
|
69
|
+
self.state = 'open'
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def close!
|
74
|
+
data = Api.api.post(command_path("close"))
|
75
|
+
self.state = 'closed'
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def save
|
80
|
+
data = Api.api.post(command_path("edit"), { :title => title, :body => body })
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
%w(add remove).each do |oper|
|
85
|
+
define_method("#{oper}_label") do |*labels|
|
86
|
+
labels.each do |label|
|
87
|
+
Api.api.post("#{prefix("label/#{oper}")}/#{label}/#{number}", { :cache => false })
|
88
|
+
if oper == "add"
|
89
|
+
self.labels << label
|
90
|
+
else
|
91
|
+
self.labels -= [label]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def comment(comment)
|
98
|
+
data = Api.api.post(command_path("comment"), { :comment => comment })
|
99
|
+
IssueComment.new(data['comment'])
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def prefix(command)
|
104
|
+
"/issues/#{command}/#{repository.owner}/#{repository.name}"
|
105
|
+
end
|
106
|
+
|
107
|
+
def command_path(command)
|
108
|
+
"#{prefix(command)}/#{number}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "issue")
|
2
|
+
class Octopi::IssueSet < Array
|
3
|
+
include Octopi
|
4
|
+
attr_accessor :user, :repository
|
5
|
+
|
6
|
+
def initialize(array)
|
7
|
+
self.user = array.first.user
|
8
|
+
self.repository = array.first.repository
|
9
|
+
super(array)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find(number)
|
13
|
+
issue = detect { |issue| issue.number == number }
|
14
|
+
raise NotFound, Issue if issue.nil?
|
15
|
+
issue
|
16
|
+
end
|
17
|
+
|
18
|
+
def search(options={})
|
19
|
+
Issue.search(options.merge(:user => user, :repo => repository))
|
20
|
+
end
|
21
|
+
end
|
data/lib/octopi/key.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Key < Base
|
3
|
+
include Resource
|
4
|
+
|
5
|
+
attr_accessor :title, :id, :key
|
6
|
+
find_path "/user/keys"
|
7
|
+
|
8
|
+
attr_reader :user
|
9
|
+
|
10
|
+
def self.find_all
|
11
|
+
Api.api.get("user/keys")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.add(options={})
|
15
|
+
ensure_hash(options)
|
16
|
+
Api.api.post("/user/key/add", { :title => options[:title], :key => options[:key], :cache => false })
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove
|
21
|
+
result = Api.api.post "/user/key/remove", { :id => id, :cache => false }
|
22
|
+
keys = result["public_keys"].select { |k| k["title"] == title }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "key")
|
2
|
+
class KeySet < Array
|
3
|
+
include Octopi
|
4
|
+
def find(title)
|
5
|
+
key = detect { |key| key.title == title }
|
6
|
+
raise NotFound, Key if key.nil?
|
7
|
+
key
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(options={})
|
11
|
+
ensure_hash(options)
|
12
|
+
Key.add(options)
|
13
|
+
end
|
14
|
+
end
|
data/lib/octopi/plan.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Repository < Base
|
3
|
+
include Resource
|
4
|
+
attr_accessor :description, :url, :forks, :name, :homepage, :watchers,
|
5
|
+
:owner, :private, :fork, :open_issues, :pledgie, :size,
|
6
|
+
# And now for the stuff returned by search results
|
7
|
+
:actions, :score, :language, :followers, :type, :username,
|
8
|
+
:id, :pushed, :created
|
9
|
+
set_resource_name "repository", "repositories"
|
10
|
+
|
11
|
+
create_path "/repos/create"
|
12
|
+
find_path "/repos/search/:query"
|
13
|
+
resource_path "/repos/show/:id"
|
14
|
+
delete_path "/repos/delete/:id"
|
15
|
+
|
16
|
+
attr_accessor :private
|
17
|
+
|
18
|
+
def owner=(owner)
|
19
|
+
@owner = User.find(owner)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns all branches for the Repository
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
# repo = Repository.find("fcoury", "octopi")
|
26
|
+
# repo.branches.each { |r| puts r.name }
|
27
|
+
#
|
28
|
+
def branches
|
29
|
+
Branch.all(:user => self.owner, :repo => self)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns all tags for the Repository
|
33
|
+
#
|
34
|
+
# Example:
|
35
|
+
# repo = Repository.find("fcoury", "octopi")
|
36
|
+
# repo.tags.each { |t| puts t.name }
|
37
|
+
#
|
38
|
+
def tags
|
39
|
+
Tag.all(:user => self.owner, :repo => self)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Returns all the comments for a Repository
|
44
|
+
def comments
|
45
|
+
# We have to specify xmlns as a prefix as the document is namespaced.
|
46
|
+
# Be wary!
|
47
|
+
path = "http#{'s' if private}://github.com/#{owner}/#{name}/comments.atom"
|
48
|
+
xml = Nokogiri::XML(Net::HTTP.get(URI.parse(path)))
|
49
|
+
entries = xml.xpath("//xmlns:entry")
|
50
|
+
comments = []
|
51
|
+
for entry in entries
|
52
|
+
content = entry.xpath("xmlns:content").text.gsub("<", "<").gsub(">", ">")
|
53
|
+
comments << Comment.new(
|
54
|
+
:id => entry.xpath("xmlns:id"),
|
55
|
+
:published => Time.parse(entry.xpath("xmlns:published").text),
|
56
|
+
:updated => Time.parse(entry.xpath("xmlns:updated").text),
|
57
|
+
:link => entry.xpath("xmlns:link/@href").text,
|
58
|
+
:title => entry.xpath("xmlns:title").text,
|
59
|
+
:content => content,
|
60
|
+
:author => entry.xpath("xmlns:author/xmlns:name").text,
|
61
|
+
:repository => self
|
62
|
+
)
|
63
|
+
end
|
64
|
+
comments
|
65
|
+
end
|
66
|
+
|
67
|
+
def clone_url
|
68
|
+
url = private || Api.api.login == self.owner.login ? "git@github.com:" : "git://github.com/"
|
69
|
+
url += "#{self.owner}/#{self.name}.git"
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.find(options={})
|
73
|
+
ensure_hash(options)
|
74
|
+
# Lots of people call the same thing differently.
|
75
|
+
# Can't call gather_details here because this method is used by it internally.
|
76
|
+
repo = options[:repo] || options[:repository] || options[:name]
|
77
|
+
user = options[:user].to_s
|
78
|
+
|
79
|
+
return find_plural(user, :resource) if repo.nil?
|
80
|
+
|
81
|
+
self.validate_args(user => :user, repo => :repo)
|
82
|
+
super user, repo
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.find_all(*args)
|
86
|
+
# FIXME: This should be URI escaped, but have to check how the API
|
87
|
+
# handles escaped characters first.
|
88
|
+
super args.join(" ").gsub(/ /,'+')
|
89
|
+
end
|
90
|
+
|
91
|
+
class << self
|
92
|
+
alias_method :search, :find_all
|
93
|
+
end
|
94
|
+
|
95
|
+
def commits(branch = "master")
|
96
|
+
Commit.find_all(:user => self.owner, :repo => self, :branch => branch)
|
97
|
+
end
|
98
|
+
|
99
|
+
def issues(state = "open")
|
100
|
+
IssueSet.new(Octopi::Issue.find_all(:user => owner, :repository => self))
|
101
|
+
end
|
102
|
+
|
103
|
+
def all_issues
|
104
|
+
Issue::STATES.map{|state| self.issues(state)}.flatten
|
105
|
+
end
|
106
|
+
|
107
|
+
def issue(number)
|
108
|
+
Issue.find(:user => self.owner, :repo => self, :number => number)
|
109
|
+
end
|
110
|
+
|
111
|
+
def collaborators
|
112
|
+
property('collaborators', [self.owner, self.name].join('/')).values.map { |v| User.find(v) }
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.create(options={})
|
116
|
+
raise AuthenticationRequired, "To create a repository you must be authenticated." if Api.api.read_only?
|
117
|
+
self.validate_args(options[:name] => :repo)
|
118
|
+
new(Api.api.post(path_for(:create), options)["repository"])
|
119
|
+
end
|
120
|
+
|
121
|
+
def delete!
|
122
|
+
raise APIError, "You must be authenticated as the owner of this repository to delete it" if Api.me.login != owner.login
|
123
|
+
token = Api.api.post(self.class.path_for(:delete), :id => self.name)['delete_token']
|
124
|
+
Api.api.post(self.class.path_for(:delete), :id => self.name, :delete_token => token) unless token.nil?
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_s
|
128
|
+
name
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|