gisty 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +49 -0
- data/Rakefile +118 -0
- data/bin/gisty +119 -0
- data/lib/gisty.rb +194 -0
- data/test/fixtures/24835 +241 -0
- data/test/fixtures/30119 +376 -0
- data/test/fixtures/bar.user.js +1 -0
- data/test/fixtures/foo.user.js +1 -0
- data/test/fixtures/mine_login_foo_token_bar +494 -0
- data/test/fixtures/mine_page_1_login_foo_token_bar +518 -0
- data/test/fixtures/mine_page_2_login_foo_token_bar +511 -0
- data/test/fixtures/mine_page_3_login_foo_token_bar +190 -0
- data/test/fixtures/swdyh +302 -0
- data/test/fixtures/swdyh_page_4 +178 -0
- data/test/gisty_test.rb +175 -0
- data/test/test_helper.rb +3 -0
- metadata +88 -0
data/README.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
= gisty
|
3
|
+
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
yet another command line client for gist
|
8
|
+
|
9
|
+
== Installation
|
10
|
+
|
11
|
+
=== Gem Installation
|
12
|
+
|
13
|
+
gem sources -a http://gems.github.com (you only have to do this once)
|
14
|
+
sudo gem install swdyh-gisty
|
15
|
+
|
16
|
+
set environment variable GISTY_DIR.
|
17
|
+
example .zshrc
|
18
|
+
|
19
|
+
export GISTY_DIR="$HOME/dev/gists"
|
20
|
+
|
21
|
+
set global git config. see "Global Git Config" at https://github.com/account
|
22
|
+
|
23
|
+
git config --global github.user your_id
|
24
|
+
git config --global github.token your_token
|
25
|
+
|
26
|
+
if you use zsh command completion, download this file to $fpath directory.
|
27
|
+
http://github.com/swdyh/gisty/raw/master/_gisty
|
28
|
+
|
29
|
+
|
30
|
+
== Features/Problems
|
31
|
+
|
32
|
+
use API token with HTTP. at your own risk.
|
33
|
+
|
34
|
+
== Synopsis
|
35
|
+
|
36
|
+
gisty list show local list.
|
37
|
+
gisty post file1 file2 ... post new gist.
|
38
|
+
gisty private_post file1 file2 ... post new private gist.
|
39
|
+
gisty sync sync remote gist. (clone all remote gist)
|
40
|
+
gisty sync_delete sync remote gist. delete local gist if remote gist was deleted.
|
41
|
+
gisty pull_all pull all gist.
|
42
|
+
gisty about show about gisty
|
43
|
+
gisty help show help
|
44
|
+
|
45
|
+
== Copyright
|
46
|
+
|
47
|
+
Author:: swdyh <http://mailhide.recaptcha.net/d?k=01AhB7crgrlHptVaYRD0oPwA==&c=L_iqOZrGmo6hcGpPTFg1QYnjr-WpAStyQ4Y8ShfgOHs=>
|
48
|
+
Copyright:: Copyright (c) 2008 swdyh
|
49
|
+
License:: MIT
|
data/Rakefile
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'rake/contrib/sshpublisher'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'lib/gisty'
|
12
|
+
include FileUtils
|
13
|
+
|
14
|
+
NAME = "gisty"
|
15
|
+
AUTHOR = "swdyh"
|
16
|
+
EMAIL = "http://mailhide.recaptcha.net/d?k=01AhB7crgrlHptVaYRD0oPwA==&c=L_iqOZrGmo6hcGpPTFg1QYnjr-WpAStyQ4Y8ShfgOHs="
|
17
|
+
DESCRIPTION = "yet another command line client for gist"
|
18
|
+
RUBYFORGE_PROJECT = "gisty"
|
19
|
+
HOMEPATH = "http://github.com/swdyh/gisty/tree/master"
|
20
|
+
BIN_FILES = %w( gisty )
|
21
|
+
|
22
|
+
VERS = Gisty::VERSION
|
23
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
24
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config', '*.gemspec']
|
25
|
+
RDOC_OPTS = [
|
26
|
+
'--title', "#{NAME} documentation",
|
27
|
+
"--charset", "utf-8",
|
28
|
+
"--line-numbers",
|
29
|
+
"--main", "README.rdoc",
|
30
|
+
"--inline-source",
|
31
|
+
]
|
32
|
+
|
33
|
+
task :default => [:test]
|
34
|
+
task :package => [:clean]
|
35
|
+
|
36
|
+
Rake::TestTask.new("test") do |t|
|
37
|
+
t.libs << "test"
|
38
|
+
t.pattern = "test/**/*_test.rb"
|
39
|
+
t.verbose = true
|
40
|
+
end
|
41
|
+
|
42
|
+
spec = Gem::Specification.new do |s|
|
43
|
+
s.name = NAME
|
44
|
+
s.version = VERS
|
45
|
+
s.platform = Gem::Platform::RUBY
|
46
|
+
s.has_rdoc = true
|
47
|
+
s.extra_rdoc_files = ["README.rdoc"]
|
48
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
49
|
+
s.summary = DESCRIPTION
|
50
|
+
s.description = DESCRIPTION
|
51
|
+
s.author = AUTHOR
|
52
|
+
s.email = EMAIL
|
53
|
+
s.homepage = HOMEPATH
|
54
|
+
s.executables = BIN_FILES
|
55
|
+
s.rubyforge_project = RUBYFORGE_PROJECT
|
56
|
+
s.bindir = "bin"
|
57
|
+
s.require_path = "lib"
|
58
|
+
#s.autorequire = ""
|
59
|
+
s.test_files = Dir["test/*_test.rb"]
|
60
|
+
|
61
|
+
#s.add_dependency('activesupport', '>=1.3.1')
|
62
|
+
s.add_dependency('nokogiri', '>=1.0.0')
|
63
|
+
#s.required_ruby_version = '>= 1.8.2'
|
64
|
+
|
65
|
+
s.files = %w(README.rdoc Rakefile) +
|
66
|
+
Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
|
67
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
68
|
+
Dir.glob("examples/**/*.rb") +
|
69
|
+
Dir.glob("tools/*.rb") +
|
70
|
+
Dir.glob("rails/*.rb")
|
71
|
+
|
72
|
+
s.extensions = FileList["ext/**/extconf.rb"].to_a
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
Rake::GemPackageTask.new(spec) do |p|
|
77
|
+
p.need_tar = true
|
78
|
+
p.gem_spec = spec
|
79
|
+
end
|
80
|
+
|
81
|
+
Rake::RDocTask.new do |rdoc|
|
82
|
+
rdoc.rdoc_dir = 'html'
|
83
|
+
rdoc.options += RDOC_OPTS
|
84
|
+
rdoc.template = "resh"
|
85
|
+
#rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
86
|
+
if ENV['DOC_FILES']
|
87
|
+
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
88
|
+
else
|
89
|
+
rdoc.rdoc_files.include('README.rdoc')
|
90
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
91
|
+
rdoc.rdoc_files.include('ext/**/*.c')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
desc 'Update gem spec'
|
96
|
+
task :gemspec do
|
97
|
+
open("#{NAME}.gemspec", 'w') { |f| f.puts spec.to_ruby }
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'gem build'
|
101
|
+
task :build_gem => [:gemspec] do
|
102
|
+
sh "gem build #{NAME}.gemspec"
|
103
|
+
end
|
104
|
+
|
105
|
+
desc 'refresh fixtures'
|
106
|
+
task :reflresh_fixtures do
|
107
|
+
g = Gisty.new 'tmp'
|
108
|
+
re = /page=\d+/
|
109
|
+
urls = g.map_pages do |url, page|
|
110
|
+
m = url.match re
|
111
|
+
if m
|
112
|
+
fn = 'mine_' + m.to_a.first.sub('=', '_') + '_login_foo_token_bar'
|
113
|
+
path = File.join 'test', 'fixtures', fn
|
114
|
+
puts "write #{path}"
|
115
|
+
open(path, 'w') { |f| f.write page.gsub(/(&)?(login|token)=\w+(&)?/, '') }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/bin/gisty
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'gisty'
|
5
|
+
|
6
|
+
@cmds = {}
|
7
|
+
|
8
|
+
def cmd name
|
9
|
+
@cmds[name.to_s] = Proc.new { |i| yield i }
|
10
|
+
end
|
11
|
+
|
12
|
+
cmd :about do
|
13
|
+
puts Gisty::AA
|
14
|
+
puts
|
15
|
+
puts 'version: ' + Gisty::VERSION
|
16
|
+
puts 'url: ' + Gisty::GISTY_URL
|
17
|
+
end
|
18
|
+
|
19
|
+
cmd :help do
|
20
|
+
puts <<-EOS
|
21
|
+
usage:
|
22
|
+
gisty commands
|
23
|
+
commands:
|
24
|
+
gisty list show local list.
|
25
|
+
gisty post file1 file2 ... post new gist.
|
26
|
+
gisty private_post file1 file2 ... post new private gist.
|
27
|
+
gisty sync sync remote gist. (clone all remote gist)
|
28
|
+
gisty sync_delete sync remote gist. delete local gist if remote gist was deleted.
|
29
|
+
gisty pull_all pull all gist.
|
30
|
+
gisty about show about gisty
|
31
|
+
gisty help show help
|
32
|
+
EOS
|
33
|
+
end
|
34
|
+
|
35
|
+
cmd :list do
|
36
|
+
list = @g.list
|
37
|
+
puts '- public gist -'
|
38
|
+
list[:public].each do |i|
|
39
|
+
puts "#{i[0]}: #{i[1].join(' ')}"
|
40
|
+
end
|
41
|
+
puts '- private gist -'
|
42
|
+
list[:private].each do |i|
|
43
|
+
puts "#{i[0]}: #{i[1].join(' ')}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
cmd :sync do
|
48
|
+
@g.sync
|
49
|
+
puts '---'
|
50
|
+
puts 'sync finished.'
|
51
|
+
end
|
52
|
+
|
53
|
+
cmd :sync_delete do
|
54
|
+
@g.sync true
|
55
|
+
puts '---'
|
56
|
+
puts 'sync finished.'
|
57
|
+
end
|
58
|
+
|
59
|
+
cmd :pull_all do
|
60
|
+
@g.pull_all
|
61
|
+
puts '---'
|
62
|
+
puts 'pull_all finished.'
|
63
|
+
end
|
64
|
+
|
65
|
+
cmd :post do |fs|
|
66
|
+
if fs.size > 0
|
67
|
+
begin
|
68
|
+
url = @g.create fs
|
69
|
+
rescue Gisty::InvalidFileException => e
|
70
|
+
puts "Error: invalid file"
|
71
|
+
rescue Exception => e
|
72
|
+
puts "Error: #{e}"
|
73
|
+
else
|
74
|
+
id = url.split('/').last
|
75
|
+
@g.clone id
|
76
|
+
system "open #{url}" if /darwin/ === RUBY_PLATFORM
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
cmd :private_post do |fs|
|
82
|
+
if fs.size > 0
|
83
|
+
begin
|
84
|
+
url = @g.create fs, :private => true
|
85
|
+
rescue Exception => e
|
86
|
+
puts "Error: #{e}"
|
87
|
+
else
|
88
|
+
system "open #{url}" if /darwin/ === RUBY_PLATFORM
|
89
|
+
id = url.split('/').last
|
90
|
+
@g.clone id
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
if ENV['GISTY_DIR']
|
97
|
+
begin
|
98
|
+
@g = Gisty.new ENV['GISTY_DIR']
|
99
|
+
rescue Gisty::UnsetAuthInfoException => e
|
100
|
+
puts 'Error: set your api token.'
|
101
|
+
puts 'see Global Git Config at https://github.com/account'
|
102
|
+
exit
|
103
|
+
end
|
104
|
+
else
|
105
|
+
puts "Error: please set ENV['GISTY_DIR']"
|
106
|
+
exit
|
107
|
+
end
|
108
|
+
|
109
|
+
if ARGV.size == 0
|
110
|
+
@cmds['help'].call []
|
111
|
+
else
|
112
|
+
c = @cmds[ARGV.first]
|
113
|
+
if c
|
114
|
+
c.call ARGV.last(ARGV.size - 1)
|
115
|
+
else
|
116
|
+
puts 'unknown commands.'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
data/lib/gisty.rb
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'net/http'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'nokogiri'
|
7
|
+
|
8
|
+
class Gisty
|
9
|
+
VERSION = '0.0.15'
|
10
|
+
GIST_URL = 'http://gist.github.com/'
|
11
|
+
GISTY_URL = 'http://github.com/swdyh/gisty/tree/master'
|
12
|
+
|
13
|
+
class UnsetAuthInfoException < Exception
|
14
|
+
end
|
15
|
+
|
16
|
+
class InvalidFileException < Exception
|
17
|
+
end
|
18
|
+
|
19
|
+
class PostFailureException < Exception
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.extract_ids str
|
23
|
+
doc = Nokogiri::HTML str
|
24
|
+
doc.css('.file .info a').map { |i| i['href'].sub('/', '') }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.extract url
|
28
|
+
doc = Nokogiri::HTML open(url)
|
29
|
+
{
|
30
|
+
:id => url.split('/').last,
|
31
|
+
:author => doc.css('#owner a').inner_text,
|
32
|
+
:files => doc.css('.meta .info').map { |i| i.inner_text.strip },
|
33
|
+
:clone => doc.css('a[rel="#git-clone"]').inner_text,
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize path, login = nil, token = nil
|
38
|
+
@auth = (login && token) ? { 'login' => login, 'token' => token } : auth
|
39
|
+
raise UnsetAuthInfoException if @auth['login'].nil? || @auth['token'].nil?
|
40
|
+
@auth_query = "login=#{@auth['login']}&token=#{@auth['token']}"
|
41
|
+
@dir = Pathname.pwd.realpath.join path
|
42
|
+
FileUtils.mkdir_p @dir unless @dir.exist?
|
43
|
+
end
|
44
|
+
|
45
|
+
def next_link str
|
46
|
+
doc = Nokogiri::HTML str
|
47
|
+
a = doc.at('.pagination a[hotkey="l"]')
|
48
|
+
a ? a['href'] : nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def map_pages
|
52
|
+
result = []
|
53
|
+
base_url = GIST_URL.sub(/\/$/, '')
|
54
|
+
path = "/mine?page=1"
|
55
|
+
loop do
|
56
|
+
url = base_url + path + "&#{@auth_query}"
|
57
|
+
page = open(url).read
|
58
|
+
result << yield(url, page)
|
59
|
+
path = next_link page
|
60
|
+
break unless path
|
61
|
+
end
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
def remote_ids
|
66
|
+
map_pages { |url, page| Gisty.extract_ids page }.flatten.uniq.sort
|
67
|
+
end
|
68
|
+
|
69
|
+
def clone id
|
70
|
+
FileUtils.cd @dir do
|
71
|
+
c = "git clone git@gist.github.com:#{id}.git"
|
72
|
+
Kernel.system c
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def list
|
77
|
+
dirs = Pathname.glob(@dir.to_s + '/*').map do |i|
|
78
|
+
[i.basename.to_s,
|
79
|
+
Pathname.glob(i.to_s + '/*').map { |i| i.basename.to_s }]
|
80
|
+
end
|
81
|
+
re_pub = /^\d+$/
|
82
|
+
pub = dirs.select { |i| re_pub.match(i.first) }.sort_by { |i| i.first.to_i }.reverse
|
83
|
+
pri = dirs.select { |i| !re_pub.match(i.first) }.sort_by { |i| i.first }
|
84
|
+
{ :public => pub, :private => pri }
|
85
|
+
end
|
86
|
+
|
87
|
+
def local_ids
|
88
|
+
dirs = Pathname.glob(@dir.to_s + '/*')
|
89
|
+
dirs.map { |i| i.basename.to_s }
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete id
|
93
|
+
FileUtils.rm_rf @dir.join(id) if @dir.join(id).exist?
|
94
|
+
end
|
95
|
+
|
96
|
+
def sync delete = false
|
97
|
+
remote = remote_ids
|
98
|
+
local = local_ids
|
99
|
+
|
100
|
+
if delete
|
101
|
+
(local - remote).each do |id|
|
102
|
+
print "delete #{id}? [y/n]"
|
103
|
+
confirm = $stdin.gets.strip
|
104
|
+
if confirm == 'y' || confirm == 'yes'
|
105
|
+
puts "delete #{id}"
|
106
|
+
delete id
|
107
|
+
else
|
108
|
+
puts "skip #{id}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
ids = remote
|
112
|
+
else
|
113
|
+
ids = (remote + local).uniq
|
114
|
+
end
|
115
|
+
|
116
|
+
FileUtils.cd @dir do
|
117
|
+
ids.each do |id|
|
118
|
+
if File.exist? id
|
119
|
+
# FileUtils.cd id do
|
120
|
+
# c = "git pull"
|
121
|
+
# Kernel.system c
|
122
|
+
# end
|
123
|
+
else
|
124
|
+
c = "git clone git@gist.github.com:#{id}.git"
|
125
|
+
Kernel.system c
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def pull_all
|
132
|
+
ids = local_ids
|
133
|
+
FileUtils.cd @dir do
|
134
|
+
ids.each do |id|
|
135
|
+
if File.exist? id
|
136
|
+
FileUtils.cd id do
|
137
|
+
c = "git pull"
|
138
|
+
Kernel.system c
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def auth
|
146
|
+
user = `git config --global github.user`.strip
|
147
|
+
token = `git config --global github.token`.strip
|
148
|
+
user.empty? ? {} : { 'login' => user, 'token' => token }
|
149
|
+
end
|
150
|
+
|
151
|
+
def post params
|
152
|
+
url = URI.parse('http://gist.github.com/gists')
|
153
|
+
res = Net::HTTP.post_form(url, params)
|
154
|
+
if res['Location']
|
155
|
+
res['Location']
|
156
|
+
else
|
157
|
+
raise PostFailureException, res.inspect
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def build_params paths
|
162
|
+
list = (Array === paths ? paths : [paths]).map { |i| Pathname.new i }
|
163
|
+
raise InvalidFileException if list.any?{ |i| !i.file? }
|
164
|
+
|
165
|
+
params = {}
|
166
|
+
list.each_with_index do |i, index|
|
167
|
+
params["file_ext[gistfile#{index + 1}]"] = i.extname
|
168
|
+
params["file_name[gistfile#{index + 1}]"] = i.basename.to_s
|
169
|
+
params["file_contents[gistfile#{index + 1}]"] = IO.read(i)
|
170
|
+
end
|
171
|
+
params
|
172
|
+
end
|
173
|
+
|
174
|
+
def create paths, opt = { :private => false }
|
175
|
+
params = build_params paths
|
176
|
+
if opt[:private]
|
177
|
+
params['private'] = 'on'
|
178
|
+
params['action_button'] = 'private'
|
179
|
+
end
|
180
|
+
post params.merge(auth)
|
181
|
+
end
|
182
|
+
|
183
|
+
# `figlet -f contributed/bdffonts/clb8x8.flf gisty`.gsub('#', 'm')
|
184
|
+
AA = <<-EOS
|
185
|
+
mm mm
|
186
|
+
mm
|
187
|
+
mmmmmm mmmm mmmmm mmmmmm mm mm
|
188
|
+
mm mm mm mm mm mm mm
|
189
|
+
mm mm mm mmmm mm mm mm
|
190
|
+
mmmmmm mm mm mm mmmmm
|
191
|
+
mm mmmmmm mmmmm mmm mm
|
192
|
+
mmmmm mmmm
|
193
|
+
EOS
|
194
|
+
end
|