Skip to content
Browse files

Merge branch 'master' of git://github.com/fcoury/octopi

Conflicts:
	lib/octopi.rb
  • Loading branch information...
2 parents 7fe95e3 + 0f92396 commit 006ecb2bc3fd3fa57b13bb870f76b72070770e52 @runpaint committed Apr 22, 2009
Showing with 256 additions and 39 deletions.
  1. +95 −3 README.rdoc
  2. +2 −2 VERSION.yml
  3. +12 −1 examples/authenticated.rb
  4. +0 −2 examples/github.yml.default
  5. +14 −0 examples/github.yml.example
  6. +69 −7 lib/octopi.rb
  7. +34 −2 lib/octopi/issue.rb
  8. +14 −6 lib/octopi/repository.rb
  9. +11 −11 lib/octopi/resource.rb
  10. +5 −5 octopi.gemspec
View
98 README.rdoc
@@ -2,7 +2,58 @@
Octopi is a Ruby interface to GitHub API v2 (http://develop.github.com). It's under early but active development and already works.
-== Example
+== Authenticated Usage
+
+=== Seamless authentication using .gitconfig defaults
+
+If you have your <tt>~/.gitconfig</tt> file in place, and you have a [github] section (if you don't, take a look at this GitHub Guides entry: http://github.com/guides/tell-git-your-user-name-and-email-address), you can use seamless authentication using this method:
+
+ authenticated do |g|
+ repo = g.repository("api-labrat")
+ (...)
+ end
+
+=== Explicit authentication
+
+Sometimes, you may not want to get authentication data from <tt>~/.gitconfig</tt>. You want to use GitHub API authenticated as a third party. For this use case, you have a couple of options too.
+
+<b>1. Providing login and token inline:</b>
+
+ authenticated_with "mylogin", "mytoken" do |g|
+ repo = g.repository("api-labrat")
+ issue = repo.open_issue :title => "Sample issue",
+ :body => "This issue was opened using GitHub API and Octopi"
+ puts issue.number
+ end
+
+<b>2. Providing a YAML file with authentication information:</b>
+
+Use the following format:
+
+ #
+ # Octopi GitHub API configuration file
+ #
+
+ # GitHub user login and token
+ login: github-username
+ token: github-token
+
+ # Trace level
+ # Possible values:
+ # false - no tracing, same as if the param is ommited
+ # true - will output each POST or GET operation to the stdout
+ # curl - same as true, but in addition will output the curl equivalent of each command (for debugging)
+ trace: curl
+
+This changes the way you connect to:
+
+ authenticated_with :config => "github.yml" do |g|
+ (...)
+ end
+
+== Anonymous Usage
+
+This reflects the usage of the API to retrieve information, on a read-only mode where the user doesn't have to be authenticated.
=== Users API
@@ -58,13 +109,54 @@ Single commit information:
puts "Diff:"
first_commit.details.modified.each {|m| puts "#{m['filename']} DIFF: #{m['diff']}" }
+== Tracing
+
+=== Levels
+
+You can can use tracing to enable better debugging output when something goes wrong. There are 3 tracing levels:
+
+* false (default) - no tracing
+* true - will output each GET and POST calls, along with URL and params
+* curl - same as true, but additionally outputs the curl command to replicate the issue
+
+If you choose curl tracing, the curl command equivalent to each command sent to GitHub will be output to the stdout, like this example:
+
+ => Trace on: curl
+ POST: /issues/open/webbynode/api-labrat params: body=This issue was opened using GitHub API and Octopi, title=Sample issue
+ ===== curl version
+ curl -F 'body=This issue was opened using GitHub API and Octopi' -F 'login=mylogin' -F 'token=mytoken' -F 'title=Sample issue' http://github.com/api/v2/issues/open/webbynode/api-labrat
+ ==================
+
+=== Enabling
+
+Tracing can be enabled in different ways, depending on the API feature you're using:
+
+<b>Anonymous (this will be improved later):</b>
+
+ ANONYMOUS_API.trace_level = "trace-level"
+
+<b>Seamless authenticated</b>
+
+ authenticated :trace => "trace-level" do |g|; ...; end
+
+<b>Explicitly authenticated</b>
+
+Current version of explicit authentication requires a :config param to a YAML file to allow tracing. For enabling tracing on a YAML file refer to the config.yml example presented on the Explicit authentication section.
+
== Author
* Felipe Coury - http://felipecoury.com
* HasMany.info blog - http://hasmany.info
-== Copyright
+== Contributors
-DISCLAIMER: The name of this library is pronounced <i>octo-pie</i> but no Octocats were harmed during its creation. It's not really an Octo Pie, but a contraction of the words Octocat and API.
+In alphabetical order:
+
+* Brandon Calloway - http://github.com/bcalloway
+* runpaint - http://github.com/runpaint
+
+Thanks guys!
+
+== Copyright
Copyright (c) 2009 Felipe Coury. See LICENSE for details.
View
4 VERSION.yml
@@ -1,4 +1,4 @@
---
-:major: 0
:minor: 0
-:patch: 5
+:patch: 6
+:major: 0
View
13 examples/authenticated.rb
@@ -4,6 +4,17 @@
authenticated_with :config => "github.yml" do |g|
repo = g.repository("api-labrat")
- repo.open_issue :title => "Sample issue",
+
+ issue = repo.open_issue :title => "Sample issue",
:body => "This issue was opened using GitHub API and Octopi"
+ puts "Successfully opened issue \##{issue.number}"
+
+ labels = issue.add_label "Working", "Todo"
+ puts "Labels: #{labels.inspect}"
+
+ issue.close
+ puts "Successfully closed issue \##{issue.number}"
+
+ labels = issue.remove_label "Todo"
+ puts "Successfully removed label Todo. Current labels: #{labels.inspect}"
end
View
2 examples/github.yml.default
@@ -1,2 +0,0 @@
-# login: github-username
-# token: github-token
View
14 examples/github.yml.example
@@ -0,0 +1,14 @@
+#
+# Octopi GitHub API configuration file
+#
+
+# GitHub user login and token
+login: github-username
+token: github-token
+
+# Trace level
+# Possible values:
+# false - no tracing, same as if the param is ommited
+# true - will output each POST or GET operation to the stdout
+# curl - same as true, but in addition will output the curl equivalent of each command (for debugging)
+trace: curl
View
76 lib/octopi.rb
@@ -7,6 +7,20 @@ module Octopi
class Api; end
ANONYMOUS_API = Api.new
+ def authenticated(*args, &block)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ config = read_gitconfig
+ login = config["github"]["user"]
+ token = config["github"]["token"]
+
+ api = Api.new(login, token)
+ api.trace_level = opts[:trace]
+
+ puts "=> Trace on: #{api.trace_level}" if api.trace_level
+
+ yield api
+ end
+
def authenticated_with(*args, &block)
opts = args.last.is_a?(Hash) ? args.last : {}
if opts[:config]
@@ -15,11 +29,33 @@ def authenticated_with(*args, &block)
login = config["login"]
token = config["token"]
+ trace = config["trace"]
else
login, token = *args
end
- yield Api.new(login, token)
+ puts "=> Trace on: #{trace}" if trace
+
+ api = Api.new(login, token)
+ api.trace_level = trace if trace
+ yield api
+ end
+
+ def read_gitconfig
+ config = {}
+ group = nil
+ File.foreach("#{ENV['HOME']}/.gitconfig") do |line|
+ line.strip!
+ if line[0] != ?# and line =~ /\S/
+ if line =~ /^\[(.*)\]$/
+ group = $1
+ else
+ key, value = line.split("=")
+ (config[group]||={})[key.strip] = value.strip
+ end
+ end
+ end
+ config
end
class Api
@@ -31,13 +67,16 @@ class Api
}
base_uri "http://github.com/api/v2"
- attr_accessor :format, :login, :token
+ attr_accessor :format, :login, :token, :trace_level, :read_only
- def initialize(login = nil, token = nil, format = "xml")
+ def initialize(login = nil, token = nil, format = "yaml")
@format = format
+ @read_only = true
+
if login
@login = login
@token = token
+ @read_only = false
end
end
@@ -58,7 +97,7 @@ def open_issue(user, repo, params)
end
def repository(name)
- repo = Repository.find(login, name)
+ repo = Repository.find(user, name, self)
repo.api = self
repo
end
@@ -83,21 +122,37 @@ def get_raw(path, params)
end
def post(path, params = {}, format = "yaml")
+ trace "POST", "/#{format}#{path}", params
submit(path, params, format) do |path, params, format|
- puts "POST: /#{format}#{path} with: #{params.inspect}"
resp = self.class.post "/#{format}#{path}", :query => params
- pp resp
resp
end
end
private
def submit(path, params = {}, format = "yaml", &block)
params.each_pair do |k,v|
- path = path.gsub(":#{k.to_s}", v)
+ if path =~ /:#{k.to_s}/
+ params.delete(k)
+ path = path.gsub(":#{k.to_s}", v)
+ end
end
query = login ? { :login => login, :token => token } : {}
+ query.merge!(params)
resp = yield(path, query.merge(params), format)
+
+ if @trace_level
+ case @trace_level
+ when "curl"
+ query_trace = []
+ query.each_pair { |k,v| query_trace << "-F '#{k}=#{v}'" }
+ puts "===== [curl version]"
+ puts "curl #{query_trace.join(" ")} #{self.class.base_uri}/#{format}#{path}"
+ puts "===================="
+ end
+ end
+
+ resp = yield(path, query, format)
raise APIError,
"GitHub returned status #{resp.code}" unless resp.code.to_i == 200
# FIXME: This fails for showing raw Git data because that call returns
@@ -112,10 +167,17 @@ def submit(path, params = {}, format = "yaml", &block)
end
def get(path, params = {}, format = "yaml")
+ trace "GET [#{format}]", "/#{format}#{path}", params
submit(path, params, format) do |path, params, format|
self.class.get "/#{format}#{path}"
end
end
+
+ def trace(oper, url, params)
+ return unless trace_level
+ par_str = " params: " + params.map { |p| "#{p[0]}=#{p[1]}" }.join(", ") if params and !params.empty?
+ puts "#{oper}: #{url}#{par_str}"
+ end
end
%w{error base resource user tag repository issue file_object blob commit}.
View
36 lib/octopi/issue.rb
@@ -60,8 +60,40 @@ def self.find(*args)
end
def self.open(user, repo, params, api = ANONYMOUS_API)
- data = api.post("/issues/open/#{user}/#{repo}", params)
- new(api, data)
+ user, repo_name = extract_names(user, repo)
+ data = api.post("/issues/open/#{user}/#{repo_name}", params)
+ issue = new(api, data['issue'])
+ issue.repository = repo if repo.is_a? Repository
+ issue
+ end
+
+ def reopen(*args)
+ data = @api.post(command_path("reopen"))
+ end
+
+ def close(*args)
+ data = @api.post(command_path("close"))
+ end
+
+ def save
+ data = @api.post(command_path("edit"), { :title => self.title, :body => self.body })
+ end
+
+ %[add remove].each do |oper|
+ define_method("#{oper}_label") do |*labels|
+ labels.each do |label|
+ @api.post("#{prefix("label/#{oper}")}/#{label}/#{number}")
+ end
+ end
+ end
+
+ private
+ def prefix(command)
+ "/issues/#{command}/#{repository.owner}/#{repository.name}"
+ end
+
+ def command_path(command)
+ "#{prefix(command)}/#{number}"
end
end
end
View
20 lib/octopi/repository.rb
@@ -22,11 +22,19 @@ def self.find_by_user(user)
find_plural(user, :resource)
end
- def self.find(user, name)
+ def self.find(*args)
+ api = args.last.is_a?(Api) ? args.pop : ANONYMOUS_API
+ repo = args.pop
+ user = args.pop
+
user = user.login if user.is_a? User
- name = repo.name if name.is_a? Repository
- self.validate_args(user => :user, name => :repo)
- super [user, name]
+ if repo.is_a? Repository
+ repo = repo.name
+ user ||= repo.owner
+ end
+
+ self.validate_args(user => :user, repo => :repo)
+ super user, repo, api
end
def self.find_all(*args)
@@ -40,7 +48,7 @@ def self.open_issue(args)
end
def open_issue(args)
- Issue.open(self.name, self.owner, args, @api)
+ Issue.open(self.owner, self, args, @api)
end
def commits(branch = "master")
@@ -55,4 +63,4 @@ def issue(number)
Issue.find(self, number)
end
end
-end
+end
View
22 lib/octopi/resource.rb
@@ -27,30 +27,30 @@ def find_path(path)
def resource_path(path)
(@path_spec||={})[:resource] = path
end
-
- def find(*s)
- s = s.join('/') if s.is_a? Array
- result = ANONYMOUS_API.find(path_for(:resource), @resource_name[:singular], s)
+
+ def find(*args)
+ api = args.last.is_a?(Api) ? args.pop : ANONYMOUS_API
+ args = args.join('/') if args.is_a? Array
+ result = api.find(path_for(:resource), @resource_name[:singular], args)
key = result.keys.first
+
if result[key].is_a? Array
- result[key].map do |r|
- new(ANONYMOUS_API, r)
- end
+ result[key].map { |r| new(api, r) }
else
- Resource.for(key).new(ANONYMOUS_API, result[key])
+ Resource.for(key).new(api, result[key])
end
end
def find_all(*s)
find_plural(s, :find)
end
- def find_plural(s, path)
+ def find_plural(s, path, api = ANONYMOUS_API)
s = s.join('/') if s.is_a? Array
- ANONYMOUS_API.find_all(path_for(path), @resource_name[:plural], s).
+ api.find_all(path_for(path), @resource_name[:plural], s).
map do |item|
payload = block_given? ? yield(item) : item
- new(ANONYMOUS_API, payload)
+ new(api, payload)
end
end
View
10 octopi.gemspec
@@ -2,24 +2,24 @@
Gem::Specification.new do |s|
s.name = %q{octopi}
- s.version = "0.0.5"
+ s.version = "0.0.6"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Felipe Coury"]
- s.date = %q{2009-04-20}
+ s.date = %q{2009-04-21}
s.email = %q{felipe.coury@gmail.com}
s.extra_rdoc_files = ["README.rdoc", "LICENSE"]
- s.files = ["README.rdoc", "VERSION.yml", "lib/octopi", "lib/octopi/base.rb", "lib/octopi/blob.rb", "lib/octopi/commit.rb", "lib/octopi/file_object.rb", "lib/octopi/issue.rb", "lib/octopi/repository.rb", "lib/octopi/resource.rb", "lib/octopi/tag.rb", "lib/octopi/user.rb", "lib/octopi.rb", "test/octopi_test.rb", "test/test_helper.rb", "LICENSE"]
+ s.files = ["README.rdoc", "VERSION.yml", "lib/octopi", "lib/octopi/base.rb", "lib/octopi/blob.rb", "lib/octopi/commit.rb", "lib/octopi/error.rb", "lib/octopi/file_object.rb", "lib/octopi/issue.rb", "lib/octopi/repository.rb", "lib/octopi/resource.rb", "lib/octopi/tag.rb", "lib/octopi/user.rb", "lib/octopi.rb", "test/octopi_test.rb", "test/test_helper.rb", "LICENSE"]
s.has_rdoc = true
s.homepage = %q{http://github.com/fcoury/octopi}
s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
s.require_paths = ["lib"]
- s.rubygems_version = %q{1.3.2}
+ s.rubygems_version = %q{1.3.1}
s.summary = %q{A Ruby interface to GitHub API v2}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
- s.specification_version = 3
+ s.specification_version = 2
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
else

0 comments on commit 006ecb2

Please sign in to comment.
Something went wrong with that request. Please try again.