dcuddeback-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.markdown +144 -0
- data/Rakefile +94 -0
- data/VERSION.yml +4 -0
- data/contrib/backup.rb +100 -0
- data/dcuddeback-octopi.gemspec +108 -0
- data/examples/authenticated.rb +20 -0
- data/examples/issues.rb +18 -0
- data/examples/overall.rb +50 -0
- data/lib/ext/string_ext.rb +5 -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/deploy_key.rb +27 -0
- data/lib/octopi/deploy_key_set.rb +18 -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 +136 -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 +131 -0
- data/lib/octopi.rb +135 -0
- data/test/api_test.rb +58 -0
- data/test/authenticated_test.rb +39 -0
- data/test/base_test.rb +20 -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 +151 -0
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,136 @@
|
|
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.join) }
|
113
|
+
end
|
114
|
+
|
115
|
+
def deploy_keys
|
116
|
+
DeployKey.all(:user => self.owner, :repo => self)
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.create(options={})
|
120
|
+
raise AuthenticationRequired, "To create a repository you must be authenticated." if Api.api.read_only?
|
121
|
+
self.validate_args(options[:name] => :repo)
|
122
|
+
new(Api.api.post(path_for(:create), options.merge( :cache => false ))["repository"])
|
123
|
+
end
|
124
|
+
|
125
|
+
def delete!
|
126
|
+
raise APIError, "You must be authenticated as the owner of this repository to delete it" if Api.me.login != owner.login
|
127
|
+
token = Api.api.post(self.class.path_for(:delete), :id => self.name)['delete_token']
|
128
|
+
Api.api.post(self.class.path_for(:delete), :id => self.name, :delete_token => token) unless token.nil?
|
129
|
+
end
|
130
|
+
|
131
|
+
def to_s
|
132
|
+
name
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -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,131 @@
|
|
1
|
+
module Octopi
|
2
|
+
class User < Base
|
3
|
+
include Resource
|
4
|
+
attr_accessor :company, :name, :following_count, :gravatar_id,
|
5
|
+
:blog, :public_repo_count, :public_gist_count,
|
6
|
+
:id, :login, :followers_count, :created_at,
|
7
|
+
:email, :location, :disk_usage, :private_repo_count,
|
8
|
+
:private_gist_count, :collaborators, :plan,
|
9
|
+
:owned_private_repo_count, :total_private_repo_count,
|
10
|
+
|
11
|
+
# These come from search results, which doesn't
|
12
|
+
# contain the above information.
|
13
|
+
:actions, :score, :language, :followers, :following,
|
14
|
+
:fullname, :type, :username, :repos, :pushed, :created
|
15
|
+
|
16
|
+
def plan=(attributes={})
|
17
|
+
@plan = Plan.new(attributes)
|
18
|
+
end
|
19
|
+
|
20
|
+
find_path "/user/search/:query"
|
21
|
+
resource_path "/user/show/:id"
|
22
|
+
|
23
|
+
# Finds a single user identified by the given username
|
24
|
+
#
|
25
|
+
# Example:
|
26
|
+
#
|
27
|
+
# user = User.find("fcoury")
|
28
|
+
# puts user.login # should return 'fcoury'
|
29
|
+
def self.find(username)
|
30
|
+
self.validate_args(username => :user)
|
31
|
+
super username
|
32
|
+
end
|
33
|
+
|
34
|
+
# Finds all users whose username matches a given string
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
#
|
38
|
+
# User.find_all("oe") # Matches joe, moe and monroe
|
39
|
+
#
|
40
|
+
def self.find_all(username)
|
41
|
+
self.validate_args(username => :user)
|
42
|
+
super username
|
43
|
+
end
|
44
|
+
|
45
|
+
class << self
|
46
|
+
alias_method :search, :find_all
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a collection of Repository objects, containing
|
50
|
+
# all repositories of the user.
|
51
|
+
#
|
52
|
+
# If user is the current authenticated user, some
|
53
|
+
# additional information will be provided for the
|
54
|
+
# Repositories.
|
55
|
+
def repositories
|
56
|
+
rs = RepositorySet.new(Repository.find(:user => self.login))
|
57
|
+
rs.user = self
|
58
|
+
rs
|
59
|
+
end
|
60
|
+
|
61
|
+
# Searches for user Repository identified by name
|
62
|
+
def repository(options={})
|
63
|
+
options = { :name => options } if options.is_a?(String)
|
64
|
+
self.class.ensure_hash(options)
|
65
|
+
Repository.find({ :user => login }.merge!(options))
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_repository(name, options = {})
|
69
|
+
self.class.validate_args(name => :repo)
|
70
|
+
Repository.create(self, name, options)
|
71
|
+
end
|
72
|
+
|
73
|
+
def watching
|
74
|
+
repositories = []
|
75
|
+
Api.api.get("/repos/watched/#{login}")["repositories"].each do |repo|
|
76
|
+
repositories << Repository.new(repo)
|
77
|
+
end
|
78
|
+
repositories
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# Gets a list of followers.
|
83
|
+
# Returns an array of logins.
|
84
|
+
def followers
|
85
|
+
user_property("followers")
|
86
|
+
end
|
87
|
+
|
88
|
+
# Gets a list of followers.
|
89
|
+
# Returns an array of user objects.
|
90
|
+
# If user has a large number of followers you may be rate limited by the API.
|
91
|
+
def followers!
|
92
|
+
user_property("followers", true)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Gets a list of people this user is following.
|
96
|
+
# Returns an array of logins.
|
97
|
+
def following
|
98
|
+
user_property("following")
|
99
|
+
end
|
100
|
+
|
101
|
+
# Gets a list of people this user is following.
|
102
|
+
# Returns an array of user objectrs.
|
103
|
+
# If user has a large number of people whom they follow, you may be rate limited by the API.
|
104
|
+
def following!
|
105
|
+
user_property("following", true)
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# If a user object is passed into a method, we can use this.
|
110
|
+
# It'll also work if we pass in just the login.
|
111
|
+
def to_s
|
112
|
+
login
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
# Helper method for "deep" finds.
|
118
|
+
# Determines whether to return an array of logins (light) or user objects (heavy).
|
119
|
+
def user_property(property, deep=false)
|
120
|
+
users = []
|
121
|
+
property(property, login).each_pair do |k,v|
|
122
|
+
return v unless deep
|
123
|
+
|
124
|
+
v.each { |u| users << User.find(u) }
|
125
|
+
end
|
126
|
+
|
127
|
+
users
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|