germinate-gist 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2009-07-15
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
@@ -0,0 +1,67 @@
1
+ = germinate-gist
2
+
3
+ by Avdi Grimm
4
+
5
+ http://github.com/devver/germinate-gist/
6
+
7
+ == DESCRIPTION
8
+
9
+ A Germinate plugin for publishing source code to the Gist service (http://gist.github.com)
10
+
11
+ See Germinate: http://github.com/devver/germinate/
12
+
13
+ == FEATURES
14
+
15
+ * Uses your global github.user/github.token config variables
16
+ * Updates the source file with the Gist ID
17
+ * Publishing again will update the same Gist, won't start a new one
18
+
19
+ == SYNOPSIS:
20
+
21
+ $ cat my_article.rb
22
+ # :PUBLISHER: source, gist
23
+ # This is my awesome article
24
+ puts "Hello, world"
25
+
26
+ $ germ publish source my_article.rb
27
+ INFO -- germinate: Gist published at http://gist.github.com/12345
28
+
29
+ $ echo " # more text..." >> my_article.rb
30
+ $ germ publish source my_article.rb
31
+ INFO -- germinate: Gist http://gist.github.com/12345 updated
32
+
33
+
34
+ == REQUIREMENTS:
35
+
36
+ * Nokogiri
37
+ * schacon-git
38
+ * RestClient
39
+
40
+ == INSTALL:
41
+
42
+ * sudo gem install devver-germinate-gist --source http://gems.github.com
43
+
44
+ == LICENSE:
45
+
46
+ (The MIT License)
47
+
48
+ Copyright (c) 2009
49
+
50
+ Permission is hereby granted, free of charge, to any person obtaining
51
+ a copy of this software and associated documentation files (the
52
+ 'Software'), to deal in the Software without restriction, including
53
+ without limitation the rights to use, copy, modify, merge, publish,
54
+ distribute, sublicense, and/or sell copies of the Software, and to
55
+ permit persons to whom the Software is furnished to do so, subject to
56
+ the following conditions:
57
+
58
+ The above copyright notice and this permission notice shall be
59
+ included in all copies or substantial portions of the Software.
60
+
61
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
62
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
63
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
64
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
65
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
66
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
67
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+
3
+ # Look in the tasks/setup.rb file for the various options that can be
4
+ # configured in this Rakefile. The .rake files in the tasks directory
5
+ # are where the options are used.
6
+
7
+ begin
8
+ require 'bones'
9
+ Bones.setup
10
+ rescue LoadError
11
+ begin
12
+ load 'tasks/setup.rb'
13
+ rescue LoadError
14
+ raise RuntimeError, '### please install the "bones" gem ###'
15
+ end
16
+ end
17
+
18
+ ensure_in_path 'lib'
19
+
20
+ require 'germinate'
21
+ require 'germinate/publisher'
22
+ require 'germinate-gist'
23
+
24
+ task :default => 'spec:run'
25
+
26
+ PROJ.name = 'germinate-gist'
27
+ PROJ.authors = 'Avdi Grimm'
28
+ PROJ.email = 'avdi@avdi.org'
29
+ PROJ.url = 'http://github.com/devver/germinate-gist/'
30
+ PROJ.version = GerminateGist::VERSION
31
+ PROJ.rubyforge.name = 'germinate-gist'
32
+ PROJ.ignore_file = '.gitignore'
33
+
34
+ PROJ.spec.opts << '--color'
35
+
36
+ PROJ.ruby_opts = []
37
+
38
+ depend_on 'nokogiri', '~> 1.2.3'
39
+ depend_on 'schacon-git', '~> 1.1.1'
40
+ depend_on 'rest_client', '~> 1.0.3'
41
+
42
+ PROJ.spec.opts << '--color'
43
+
44
+ # EOF
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), %w[.. lib germinate-gist]))
5
+
6
+ # Put your code here
7
+
8
+ # EOF
@@ -0,0 +1,46 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{germinate-gist}
5
+ s.version = "1.0.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Avdi Grimm"]
9
+ s.date = %q{2009-07-20}
10
+ s.default_executable = %q{germinate-gist}
11
+ s.description = %q{A Germinate plugin for publishing source code to the Gist service (http://gist.github.com) See Germinate: http://github.com/devver/germinate/}
12
+ s.email = %q{avdi@avdi.org}
13
+ s.executables = ["germinate-gist"]
14
+ s.extra_rdoc_files = ["History.txt", "README.txt", "bin/germinate-gist"]
15
+ s.files = ["History.txt", "README.txt", "Rakefile", "bin/germinate-gist", "lib/germinate-gist.rb", "lib/germinate-gist/errors.rb", "lib/germinate-gist/gist_publisher.rb", "lib/germinate_plugin_v0_init.rb", "spec/germinate-gist/gist_publiser_spec.rb", "spec/germinate-gist/gist_publisher.rb", "spec/germinate-gist_spec.rb", "spec/spec_helper.rb", "tasks/ann.rake", "tasks/bones.rake", "tasks/gem.rake", "tasks/git.rake", "tasks/notes.rake", "tasks/post_load.rake", "tasks/rdoc.rake", "tasks/rubyforge.rake", "tasks/setup.rb", "tasks/spec.rake", "tasks/svn.rake", "tasks/test.rake", "tasks/zentest.rake", "test/test_germinate-gist.rb"]
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://github.com/devver/germinate-gist/}
18
+ s.rdoc_options = ["--main", "README.txt"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{germinate-gist}
21
+ s.rubygems_version = %q{1.3.1}
22
+ s.summary = %q{A Germinate plugin for publishing source code to the Gist service (http://gist}
23
+ s.test_files = ["test/test_germinate-gist.rb"]
24
+
25
+ if s.respond_to? :specification_version then
26
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
27
+ s.specification_version = 2
28
+
29
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
30
+ s.add_runtime_dependency(%q<nokogiri>, ["~> 1.2.3"])
31
+ s.add_runtime_dependency(%q<schacon-git>, ["~> 1.1.1"])
32
+ s.add_runtime_dependency(%q<rest_client>, ["~> 1.0.3"])
33
+ s.add_development_dependency(%q<bones>, [">= 2.5.1"])
34
+ else
35
+ s.add_dependency(%q<nokogiri>, ["~> 1.2.3"])
36
+ s.add_dependency(%q<schacon-git>, ["~> 1.1.1"])
37
+ s.add_dependency(%q<rest_client>, ["~> 1.0.3"])
38
+ s.add_dependency(%q<bones>, [">= 2.5.1"])
39
+ end
40
+ else
41
+ s.add_dependency(%q<nokogiri>, ["~> 1.2.3"])
42
+ s.add_dependency(%q<schacon-git>, ["~> 1.1.1"])
43
+ s.add_dependency(%q<rest_client>, ["~> 1.0.3"])
44
+ s.add_dependency(%q<bones>, [">= 2.5.1"])
45
+ end
46
+ end
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+
3
+ module GerminateGist
4
+
5
+ # :stopdoc:
6
+ VERSION = '1.0.0'
7
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
8
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
9
+ # :startdoc:
10
+
11
+ # Returns the version string for the library.
12
+ #
13
+ def self.version
14
+ VERSION
15
+ end
16
+
17
+ # Returns the library path for the module. If any arguments are given,
18
+ # they will be joined to the end of the libray path using
19
+ # <tt>File.join</tt>.
20
+ #
21
+ def self.libpath( *args )
22
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
23
+ end
24
+
25
+ # Returns the lpath for the module. If any arguments are given,
26
+ # they will be joined to the end of the path using
27
+ # <tt>File.join</tt>.
28
+ #
29
+ def self.path( *args )
30
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
31
+ end
32
+
33
+ # Utility method used to require all files ending in .rb that lie in the
34
+ # directory below this file that has the same name as the filename passed
35
+ # in. Optionally, a specific _directory_ name can be passed in such that
36
+ # the _filename_ does not have to be equivalent to the directory.
37
+ #
38
+ def self.require_all_libs_relative_to( fname, dir = nil )
39
+ dir ||= ::File.basename(fname, '.*')
40
+ search_me = ::File.expand_path(
41
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
42
+
43
+ Dir.glob(search_me).sort.each {|rb| require rb}
44
+ end
45
+
46
+ end # module GerminateGist
47
+
48
+ GerminateGist.require_all_libs_relative_to(__FILE__)
49
+
50
+ # EOF
@@ -0,0 +1,16 @@
1
+ module GerminateGist
2
+ class UserError < RuntimeError
3
+ end
4
+
5
+ class GithubConfigError < UserError
6
+ MESSAGE = <<END
7
+ No global Github config found. See
8
+ http://github.com/blog/180-local-github-config for how to set up your login and
9
+ token.
10
+ END
11
+
12
+ def initialize(message=MESSAGE)
13
+ super
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,96 @@
1
+ require 'git'
2
+ require 'rest_client'
3
+ require 'pathname'
4
+ require 'nokogiri'
5
+
6
+ class GerminateGist::GistPublisher < Germinate::Publisher
7
+ identifier "gist"
8
+
9
+ GISTS_URL = 'http://gist.github.com/gists'
10
+ NEW_GIST_URL = 'http://gist.github.com/api/v1/xml/new'
11
+ EXISTING_GIST_URL = 'http://gist.github.com/api/v1/xml/'
12
+ PUBLIC_GIST_URL = 'http://gist.github.com/'
13
+
14
+ def initialize(name, librarian, options={})
15
+ @resource_class = options.delete(:resource_class) { RestClient::Resource }
16
+ super
17
+ @git = Git.init
18
+ self.selector = "$SOURCE"
19
+ end
20
+
21
+ def publish!(output, extra_options={})
22
+ if librarian.variables.key?('GIST_ID')
23
+ put_gist!(librarian.variables['GIST_ID'])
24
+ else
25
+ post_gist!
26
+ end
27
+
28
+ rescue RestClient::ExceptionWithResponse => error
29
+ log.error "Error posting to gist: #{error.message}"
30
+ log.debug "Response from Gist: #{error.response.message}\n#{error.response.body}"
31
+ raise UserError, error.message
32
+ end
33
+
34
+ def github_login
35
+ @git.config('github.user') or raise GerminateGist::GithubConfigError
36
+ end
37
+
38
+ def github_token
39
+ @git.config('github.token') or raise GerminateGist::GithubConfigError
40
+ end
41
+
42
+ private
43
+
44
+ def source_path
45
+ Pathname(input.source_path)
46
+ end
47
+
48
+ def extname
49
+ source_path.extname.to_s
50
+ end
51
+
52
+ def basename
53
+ source_path.basename.to_s
54
+ end
55
+
56
+ def gist_data
57
+ {
58
+ 'file_ext' => {
59
+ basename => extname
60
+ },
61
+ 'file_name' => {
62
+ basename => basename
63
+ },
64
+ 'file_contents' => {
65
+ basename => input.join
66
+ },
67
+ 'login' => github_login,
68
+ 'token' => github_token
69
+ }
70
+ end
71
+
72
+ def post_gist!
73
+ gists = @resource_class.new(NEW_GIST_URL)
74
+ response = gists.post(gist_data)
75
+ log.debug "Gist Response:\n#{response}"
76
+ gist_id = extract_gist_id(response)
77
+ log.info "Gist published at #{gist_public_url(gist_id)}"
78
+ librarian.variables['GIST_ID'] = gist_id
79
+ end
80
+
81
+ # See http://support.github.com/discussions/gist/65-update-gists-in-the-api
82
+ def put_gist!(gist_id)
83
+ gist = @resource_class.new('http://gist.github.com/gists/' + gist_id)
84
+ response = gist.put(gist_data)
85
+ log.debug "Gist PUT response:\n#{response}"
86
+ log.info "Gist #{gist_public_url(gist_id)} updated"
87
+ end
88
+
89
+ def extract_gist_id(response)
90
+ Nokogiri::XML.parse(response).xpath('//repo').first.content
91
+ end
92
+
93
+ def gist_public_url(gist_id)
94
+ PUBLIC_GIST_URL + gist_id
95
+ end
96
+ end
@@ -0,0 +1 @@
1
+ require 'germinate-gist'
@@ -0,0 +1,161 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
2
+
3
+ module GerminateGist
4
+ describe GistPublisher do
5
+ before :each do
6
+ @git = stub("Git")
7
+ @hunk = Germinate::Hunk.new(["line 1\n", "line 2\n"])
8
+ @contents = "line 1\nline 2\n"
9
+ @variables = {}
10
+ @librarian = stub("Librarian",
11
+ :[] => @hunk,
12
+ :make_pipeline => lambda{|x|x},
13
+ :variables => @variables)
14
+ @output = stub("Output")
15
+ @hunk.stub!(:source_path).and_return("/some/path/my_article.c")
16
+ @post_response = <<'END'
17
+ <?xml version="1.0" encoding="UTF-8"?>
18
+ <gists type="array">
19
+ <gist>
20
+ <public type="boolean">true</public>
21
+ <description nil="true"></description>
22
+ <repo>12345</repo>
23
+ <created-at type="datetime">2008-08-06T13:30:32-07:00</created-at>
24
+ </gist>
25
+ </gists>
26
+ END
27
+ @gists = stub("Gists Resource", :post => @post_response)
28
+ @gist = stub("Single Gist Resource", :put => @post_response)
29
+ @gist_id = "54321"
30
+ @resource_class = stub("Resource Class")
31
+ Git.stub!(:init).and_return(@git)
32
+ @resource_class.stub!(:new).and_return(@gists)
33
+ end
34
+
35
+ it "should be a kind of Publisher" do
36
+ GistPublisher.should be < Germinate::Publisher
37
+ end
38
+
39
+ it "should identify itself as 'gist'" do
40
+ GistPublisher.identifier.should == "gist"
41
+ end
42
+
43
+ context "given a name and a librarian" do
44
+ before :each do
45
+ @it = GistPublisher.new("source", @librarian, {:resource_class => @resource_class})
46
+ end
47
+
48
+ it "should know its own name" do
49
+ @it.name.should == "source"
50
+ end
51
+
52
+ it "should have a default selector of $SOURCE" do
53
+ @it.selector.should == "$SOURCE"
54
+ end
55
+ end
56
+
57
+ context "given a global git config" do
58
+
59
+ before :each do
60
+ @git = stub("Git")
61
+ @git.stub!(:config).with('github.user').and_return("johnq")
62
+ @git.stub!(:config).with('github.token').and_return("abcde")
63
+ Git.stub!(:init).and_return(@git)
64
+
65
+ @it = GistPublisher.new("source", @librarian, {:resource_class => @resource_class})
66
+ end
67
+
68
+ it "should know the user's github login" do
69
+ @it.github_login.should == "johnq"
70
+ end
71
+
72
+ it "should know the user's github token" do
73
+ @it.github_token.should == "abcde"
74
+ end
75
+
76
+ context "publishing" do
77
+ it "should be able to post a new gist" do
78
+ @gists.should_receive(:post).
79
+ with(
80
+ {
81
+ 'file_ext' => {
82
+ 'my_article.c' => '.c'
83
+ },
84
+ 'file_name' => {
85
+ 'my_article.c' => 'my_article.c',
86
+ },
87
+ 'file_contents' => {
88
+ 'my_article.c' => "line 1\nline 2\n"
89
+ },
90
+ 'login' => 'johnq',
91
+ 'token' => 'abcde'
92
+ })
93
+ @it.publish!(@output)
94
+ end
95
+
96
+ it "should save the published gist ID in a variable" do
97
+ @variables.should_receive(:[]=).with('GIST_ID', '12345')
98
+ @it.publish!(@output)
99
+ end
100
+ end
101
+ end
102
+
103
+ context "given a pre-existing gist" do
104
+ before :each do
105
+ @git = stub("Git")
106
+ @git.stub!(:config).with('github.user').and_return("johnq")
107
+ @git.stub!(:config).with('github.token').and_return("abcde")
108
+ Git.stub!(:init).and_return(@git)
109
+ @resource_class = stub("Resource Class")
110
+ @it = GistPublisher.new("source", @librarian, {:resource_class => @resource_class})
111
+ @variables['GIST_ID'] = '54321'
112
+ end
113
+
114
+ context "on publish" do
115
+ it "should update the pre-exsting gist" do
116
+ @resource_class.should_receive(:new).
117
+ with("http://gist.github.com/gists/#{@gist_id}").
118
+ and_return(@gist)
119
+
120
+ @gist.should_receive(:put).with(
121
+ {
122
+ 'file_ext' => {
123
+ 'my_article.c' => '.c'
124
+ },
125
+ 'file_name' => {
126
+ 'my_article.c' => 'my_article.c',
127
+ },
128
+ 'file_contents' => {
129
+ 'my_article.c' => "line 1\nline 2\n"
130
+ },
131
+ 'login' => 'johnq',
132
+ 'token' => 'abcde'
133
+ })
134
+ @it.publish!(@output)
135
+ end
136
+ end
137
+ end
138
+
139
+ context "given no global github user config" do
140
+
141
+ before :each do
142
+ @git.stub!(:config).with('github.user').and_return(nil)
143
+ @git.stub!(:config).with('github.token').and_return(nil)
144
+ @it = GistPublisher.new("source", @librarian, {:resource_class => @resource_class})
145
+ end
146
+
147
+ it "should fail fast trying to access github login" do
148
+ lambda do
149
+ @it.github_login
150
+ end.should raise_error(GithubConfigError)
151
+ end
152
+
153
+ it "should fail fast trying to access github token" do
154
+ lambda do
155
+ @it.github_token
156
+ end.should raise_error(GithubConfigError)
157
+ end
158
+
159
+ end
160
+ end
161
+ end