hackpad-cli 0.0.4 → 0.0.5
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.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +22 -12
- data/README.md +42 -17
- data/Rakefile +19 -0
- data/bin/hpcli +2 -2
- data/hackpad-cli.gemspec +3 -0
- data/lib/hackpad/cli/api.rb +60 -0
- data/lib/hackpad/cli/client.rb +79 -0
- data/lib/hackpad/cli/config.rb +42 -0
- data/lib/hackpad/cli/pad.rb +52 -0
- data/lib/hackpad/cli/padlist.rb +32 -0
- data/lib/hackpad/cli/plain_colors.rb +5 -0
- data/lib/hackpad/cli/runner.rb +74 -0
- data/lib/hackpad/cli/store.rb +62 -0
- data/lib/hackpad/cli/version.rb +2 -4
- data/lib/hackpad/cli.rb +2 -41
- data/spec/lib/hackpad/cli/client_spec.rb +14 -0
- data/spec/lib/hackpad/cli/pad_spec.rb +41 -0
- data/spec/lib/hackpad/cli/runner_spec.rb +49 -0
- data/spec/lib/hackpad/cli/store_spec.rb +27 -0
- data/spec/spec_helper.rb +13 -0
- metadata +65 -6
- data/lib/hackpad/client.rb +0 -100
- data/lib/hackpad/config.rb +0 -40
- data/lib/hackpad/store.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec98c6b6d399cc33172d9a3ac28928d7e55e4c7c
|
4
|
+
data.tar.gz: 02cacca94e93f019eacbb6656facde0bf6da7a77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 524979fef3768413d2b37695c90282712c7fd9764e37478e7a6fff4b9704ab7db9a1b638fd152910fdb524cc443de2a4d29895d9c0ab274a42dc84ef3815b24a
|
7
|
+
data.tar.gz: a32ff357be9208fd0d85ce050c0171277719868fa06c0f57be92d5ed6e0e701cc6057d1e62c5661146896d28eddf78f6512f36424e738c168df363136b854696
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
repo_token: LH9PLfB9g4e8PIt5fDw06hQ2ac8ivdY48
|
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,26 +1,36 @@
|
|
1
1
|
Hackpad-cli changelog
|
2
2
|
==========================
|
3
3
|
|
4
|
-
v0.0.
|
4
|
+
v0.0.5 - 2014-05-04
|
5
5
|
--------------------
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
- add a dot-timer for when padlist refreshes so one knows that something is happening
|
8
|
+
- add an option `-u` to display urls rather than pad id
|
9
|
+
- add an option `-r` to force the refresh of the cache
|
10
|
+
- add storage of cache for pad and pad lists
|
11
|
+
- add some tests
|
12
|
+
- add a flag for removing colors `-p`
|
9
13
|
|
10
|
-
v0.0.
|
14
|
+
v0.0.4 - 2014-05-01
|
15
|
+
--------------------
|
16
|
+
|
17
|
+
- add options in pad info for `hpcli info [pad_id]`
|
18
|
+
- implement a search command `hpcli search [term]`
|
19
|
+
|
20
|
+
v0.0.3 - 2014-05-01
|
11
21
|
--------------
|
12
22
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
23
|
+
- add a better way to manage alternative configuration file
|
24
|
+
- fix alternate config dir setup
|
25
|
+
- verify compat with ruby 1.9.3
|
26
|
+
- better readme
|
17
27
|
|
18
|
-
v0.0.2 - 2014-
|
28
|
+
v0.0.2 - 2014-05-01
|
19
29
|
---------------
|
20
30
|
|
21
|
-
|
31
|
+
- damn, forgot to remove awesome_print. huhu
|
22
32
|
|
23
|
-
v0.0.1 - 2014-
|
33
|
+
v0.0.1 - 2014-05-01
|
24
34
|
------------------------
|
25
35
|
|
26
|
-
|
36
|
+
- initial release of a draft
|
data/README.md
CHANGED
@@ -1,13 +1,21 @@
|
|
1
1
|
Hackpad-Cli
|
2
2
|
===================
|
3
3
|
|
4
|
+
[](http://rubygems.org/gems/hackpad-cli)
|
5
|
+
[](https://travis-ci.org/mose/hackpad-cli)
|
6
|
+
[](https://coveralls.io/r/mose/hackpad-cli)
|
7
|
+
[](https://gemnasium.com/mose/hackpad-cli)
|
8
|
+
[](https://codeclimate.com/github/mose/hackpad-cli)
|
9
|
+
|
10
|
+
----
|
11
|
+
|
4
12
|
This is a command-line utility to check and manipulate hackpad documents.
|
5
13
|
It uses Hackpad REST API 1.0 https://hackpad.com/fQD2DRz22Wf and was tested with ruby 1.9.3 and 2.1.1.
|
6
14
|
|
7
15
|
Initially this tool was created to overcome the frustration of the md export of pads,
|
8
|
-
because we need to copy them to other places sometimes. Proper markdown would be appreciated.
|
16
|
+
because we need to copy them to other places sometimes. Proper markdown would be appreciated. It does that by transforming the html in markdown with the https://github.com/xijo/reverse_markdown gem.
|
9
17
|
|
10
|
-
So
|
18
|
+
Then it felt right to cache the pads content and list because then we can browse and search in the whole workspace very fast. So by default the `list`, `show` and `info` are cached unless you pass the `-r` option at the end of the commandline. Note that the longest is the `list` because the API don't provide pads titles, so `hpcli list` actually downloads the whole list of the pads in txt format. But it makes the `info` and the `show` very fast after that.
|
11
19
|
|
12
20
|
Installation
|
13
21
|
------------------
|
@@ -25,11 +33,24 @@ Usage
|
|
25
33
|
|
26
34
|
(use `bundle exec` if you need, mostly in clone mode when not using rvm)
|
27
35
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
36
|
+
```
|
37
|
+
Commands:
|
38
|
+
hpcli help [COMMAND] # Describe available commands or one specific command
|
39
|
+
hpcli info [pad_id] # gets info for the pad <pad_id>.
|
40
|
+
hpcli list # Lists available pads.
|
41
|
+
hpcli search [term] # Lists available pads matching [term].
|
42
|
+
hpcli show [pad_id] [format] # shows pad <pad_id> in format [html,txt,md] (default txt).
|
43
|
+
hpcli version # Displays the hackpad-cli version.
|
44
|
+
|
45
|
+
Options:
|
46
|
+
-c, [--configdir=CONFIGDIR] # Path to the hackpad-cli directory to use.
|
47
|
+
# Default: /home/mose/.hackpad-cli/
|
48
|
+
-w, [--workspace=WORKSPACE] # Name of the workspace to use.
|
49
|
+
# Default: default
|
50
|
+
-r, [--refresh], [--no-refresh] # Add this if you want refresh the cache.
|
51
|
+
-u, [--urls], [--no-urls] # Displays urls rather than pad ids.
|
52
|
+
-p, [--plain], [--no-plain] # Add this if you don't want colors.
|
53
|
+
```
|
33
54
|
|
34
55
|
At first launch it will create your config dir (default ~/.hackpad-cli/), and will ask you questions to create the config file (default is .. default.yml). If you pass the `-w whatever` option at the end, it will ask questions again to write whatever.yml config file.
|
35
56
|
|
@@ -39,20 +60,24 @@ Roadmap and todoz
|
|
39
60
|
|
40
61
|
Check the [Changelog](CHANGELOG.md) for past evolutions.
|
41
62
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
63
|
+
- for v0.1.0
|
64
|
+
- <s>add freaking cool badges on the readme</s>
|
65
|
+
- <s>cache the pads list in a local storage</s>
|
66
|
+
- <s>have a choice to refresh cache</s>
|
67
|
+
- display cached date in output
|
68
|
+
- write proper tests
|
69
|
+
- for v0.2.0
|
70
|
+
- add commands for creating a new pad, linked to $EDITOR
|
71
|
+
- add a gateway to github so a pad could be copied over a wiki page directly or in a repo somehow
|
72
|
+
- for v0.3.0
|
73
|
+
- add admin commands for managing users
|
74
|
+
- implement pretty much all what the hackpad API v1 offers
|
75
|
+
- nag hackpad for they add REST endpoints to query collections
|
51
76
|
|
52
77
|
Contributing
|
53
78
|
------------------
|
54
79
|
|
55
|
-
1. Fork it
|
80
|
+
1. Fork it
|
56
81
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
82
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
58
83
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
@@ -1 +1,20 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
require 'bundler/setup'
|
1
8
|
require "bundler/gem_tasks"
|
9
|
+
require "rake/testtask"
|
10
|
+
require "rspec/core/rake_task" # RSpec 2.0
|
11
|
+
|
12
|
+
desc "launch rspec tests"
|
13
|
+
task :spec do
|
14
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
15
|
+
t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
|
16
|
+
t.pattern = 'spec/lib/**/*_spec.rb'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
task :default => :spec
|
data/bin/hpcli
CHANGED
data/hackpad-cli.gemspec
CHANGED
@@ -25,4 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
|
26
26
|
spec.add_development_dependency "bundler", "~> 1.5"
|
27
27
|
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "rspec"
|
29
|
+
spec.add_development_dependency "webmock"
|
30
|
+
spec.add_development_dependency "coveralls"
|
28
31
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'oauth'
|
2
|
+
require 'net/http'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Hackpad
|
6
|
+
module Cli
|
7
|
+
|
8
|
+
class ApiException < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
module Api
|
12
|
+
extend self
|
13
|
+
|
14
|
+
def prepare(config)
|
15
|
+
site = URI.parse config['site']
|
16
|
+
consumer = OAuth::Consumer.new(
|
17
|
+
config['client_id'],
|
18
|
+
config['secret'],
|
19
|
+
site: config['site']
|
20
|
+
)
|
21
|
+
@token = OAuth::AccessToken.new consumer
|
22
|
+
end
|
23
|
+
|
24
|
+
def search(term, start=0)
|
25
|
+
get "/api/1.0/search?q=#{CGI.escape term}&start=#{start}&limit=100"
|
26
|
+
end
|
27
|
+
|
28
|
+
def list
|
29
|
+
get "/api/1.0/pads/all"
|
30
|
+
end
|
31
|
+
|
32
|
+
def title(id)
|
33
|
+
show(id, 'txt').lines.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_options(id)
|
37
|
+
get "/api/1.0/pad/#{id}/options"
|
38
|
+
end
|
39
|
+
|
40
|
+
def read(id, ext)
|
41
|
+
get "/api/1.0/pad/#{id}/content.#{ext}", false
|
42
|
+
end
|
43
|
+
|
44
|
+
def get(url, json=true)
|
45
|
+
res = @token.get url
|
46
|
+
if res.is_a? Net::HTTPSuccess
|
47
|
+
puts res.body.inspect if ENV['DEBUG']
|
48
|
+
if json
|
49
|
+
JSON.parse res.body
|
50
|
+
else
|
51
|
+
res.body
|
52
|
+
end
|
53
|
+
else
|
54
|
+
raise ApiException, "HTTP error, code #{res.code}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'reverse_markdown'
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
require_relative 'config'
|
5
|
+
require_relative 'api'
|
6
|
+
require_relative 'store'
|
7
|
+
require_relative 'pad'
|
8
|
+
require_relative 'padlist'
|
9
|
+
|
10
|
+
module Hackpad
|
11
|
+
module Cli
|
12
|
+
class Client
|
13
|
+
|
14
|
+
def initialize(options)
|
15
|
+
@options = options
|
16
|
+
Store.prepare @options
|
17
|
+
@config = Config.load @options
|
18
|
+
Api.prepare @config
|
19
|
+
if @options[:plain]
|
20
|
+
load File.expand_path('../plain_colors.rb', __FILE__)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# GET /api/1.0/pads/all
|
25
|
+
def search(term,start=0)
|
26
|
+
payload = Api.search(term,start)
|
27
|
+
payload.each do |a|
|
28
|
+
puts "#{(@config['site'] + '/') if @options['urls']}#{a['id'].bold} - #{unescape(a['title']).yellow}"
|
29
|
+
puts " #{extract a['snippet']}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def list
|
34
|
+
padlist = Padlist.new @options['refresh']
|
35
|
+
puts padlist.all.map { |pad|
|
36
|
+
"#{(@config['site'] + '/') if @options['urls']}#{pad.id} - #{pad.title}"
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def info(id)
|
41
|
+
pad = Pad.new id
|
42
|
+
pad.load 'txt'
|
43
|
+
table "Id", "#{id}".bold
|
44
|
+
table "Title", "#{pad.title}".yellow
|
45
|
+
table "URI", "#{@config['site']}/#{id}"
|
46
|
+
table "Chars", "#{pad.chars}"
|
47
|
+
table "Lines", "#{pad.lines}"
|
48
|
+
table "Guest Policy", "#{pad.guest_policy}"
|
49
|
+
table "Moderated", "#{pad.moderated}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def show(id,format)
|
53
|
+
ext = (format == 'md') ? 'html' : format
|
54
|
+
pad = Pad.new id
|
55
|
+
pad.load ext
|
56
|
+
if format == 'md'
|
57
|
+
puts ReverseMarkdown.convert(pad.content, github_flavored: true)
|
58
|
+
else
|
59
|
+
puts pad.content
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def unescape(s)
|
66
|
+
CGI.unescapeHTML s
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract(s)
|
70
|
+
unescape(s).gsub(/<b class="hit">([^<]*)<\/b>/) { |e| $1.cyan.bold }
|
71
|
+
end
|
72
|
+
|
73
|
+
def table(key,value)
|
74
|
+
printf "%-20s %s\n", key, value
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Hackpad
|
2
|
+
module Cli
|
3
|
+
module Config
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def load(options)
|
7
|
+
configdir = options[:configdir]
|
8
|
+
configfile = File.join(configdir, "#{options[:workspace]}.yml")
|
9
|
+
# temporary migration path
|
10
|
+
if !File.exists?(configfile) && File.exists?(File.join(configdir, "config.yml"))
|
11
|
+
FileUtils.mv File.join(configdir, "config.yml"), configfile
|
12
|
+
end
|
13
|
+
if !Dir.exists?(configdir) || !File.exists?(configfile)
|
14
|
+
setup configfile
|
15
|
+
end
|
16
|
+
YAML::load_file configfile
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def setup(configfile)
|
22
|
+
config = {}
|
23
|
+
FileUtils.mkdir_p File.dirname(configfile)
|
24
|
+
puts "We need first to initialize your hackpad-cli configuration.".blue
|
25
|
+
puts "Please gather your information from https://<subdomain>.hackpad.com/ep/account/settings/".light_blue
|
26
|
+
print "What is your Client ID? "
|
27
|
+
STDOUT.flush
|
28
|
+
config['client_id'] = STDIN.gets.chomp
|
29
|
+
print "What is your Secret Key? "
|
30
|
+
STDOUT.flush
|
31
|
+
config['secret'] = STDIN.gets.chomp
|
32
|
+
print "What is the URI of your pad? "
|
33
|
+
STDOUT.flush
|
34
|
+
config['site'] = STDIN.gets.chomp
|
35
|
+
File.open(configfile, "w") do |f|
|
36
|
+
f.write YAML::dump(config)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative "store"
|
2
|
+
require_relative "api"
|
3
|
+
|
4
|
+
module Hackpad
|
5
|
+
module Cli
|
6
|
+
class UndefinedPad < StandardError
|
7
|
+
end
|
8
|
+
class UnknownFormat < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
class Pad
|
12
|
+
|
13
|
+
attr_reader :id, :content, :guest_policy, :moderated
|
14
|
+
|
15
|
+
def initialize(id)
|
16
|
+
@id = id
|
17
|
+
end
|
18
|
+
|
19
|
+
def title
|
20
|
+
@title ||= (@content.lines.first.strip if @content)
|
21
|
+
end
|
22
|
+
|
23
|
+
def chars
|
24
|
+
@content.length if @content
|
25
|
+
end
|
26
|
+
|
27
|
+
def lines
|
28
|
+
@content.lines.count if @content
|
29
|
+
end
|
30
|
+
|
31
|
+
def load(ext, refresh=false)
|
32
|
+
raise UnknownFormat unless FORMATS.include? ext
|
33
|
+
raise UndefinedPad unless @id
|
34
|
+
if refresh or !Store.exists? ext, id
|
35
|
+
@content = Api.read id, ext
|
36
|
+
Store.save self, ext
|
37
|
+
options = Api.read_options id
|
38
|
+
@guest_policy = options['guestPolicy']
|
39
|
+
@moderated = !!options['isModerated']
|
40
|
+
Store.save_meta @id, options
|
41
|
+
else
|
42
|
+
@content = Store.read id, ext
|
43
|
+
options = Store.read_options id
|
44
|
+
@guest_policy = options['guestPolicy']
|
45
|
+
@moderated = !!options['isModerated']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative "store"
|
2
|
+
require_relative "api"
|
3
|
+
require_relative "pad"
|
4
|
+
|
5
|
+
module Hackpad
|
6
|
+
module Cli
|
7
|
+
class Padlist
|
8
|
+
|
9
|
+
attr_reader :all
|
10
|
+
|
11
|
+
def initialize(refresh=false)
|
12
|
+
if refresh or !Store.exists? "padlist"
|
13
|
+
print "Refreshing "
|
14
|
+
list = Api.list
|
15
|
+
@all = []
|
16
|
+
list.each do |a|
|
17
|
+
print "."
|
18
|
+
pad = Pad.new a
|
19
|
+
pad.load 'txt', refresh
|
20
|
+
@all << OpenStruct.new( id: a, title: pad.title )
|
21
|
+
end
|
22
|
+
puts " all done."
|
23
|
+
Store.save_list all
|
24
|
+
else
|
25
|
+
@all = Store.read_list
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "yaml"
|
3
|
+
require_relative "client"
|
4
|
+
require_relative "version"
|
5
|
+
|
6
|
+
module Hackpad
|
7
|
+
module Cli
|
8
|
+
class Runner < Thor
|
9
|
+
|
10
|
+
class_option :configdir,
|
11
|
+
aliases: "-c",
|
12
|
+
default: File.join(ENV["HOME"], ".hackpad-cli/"),
|
13
|
+
desc: "Path to the hackpad-cli directory to use."
|
14
|
+
|
15
|
+
class_option :workspace,
|
16
|
+
aliases: "-w",
|
17
|
+
default: "default",
|
18
|
+
desc: "Name of the workspace to use."
|
19
|
+
|
20
|
+
class_option :refresh,
|
21
|
+
aliases: "-r",
|
22
|
+
type: 'boolean',
|
23
|
+
default: false,
|
24
|
+
desc: "Add this if you want refresh the cache."
|
25
|
+
|
26
|
+
class_option :urls,
|
27
|
+
aliases: "-u",
|
28
|
+
type: 'boolean',
|
29
|
+
default: false,
|
30
|
+
desc: "Displays urls rather than pad ids."
|
31
|
+
|
32
|
+
class_option :plain,
|
33
|
+
aliases: "-p",
|
34
|
+
type: 'boolean',
|
35
|
+
default: false,
|
36
|
+
desc: "Add this if you don't want colors."
|
37
|
+
|
38
|
+
default_task :help
|
39
|
+
|
40
|
+
desc "search [term]", "Lists available pads matching [term]."
|
41
|
+
def search(term)
|
42
|
+
Hackpad::Cli::Client.new(options).search term
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "list", "Lists available pads."
|
46
|
+
def list
|
47
|
+
Hackpad::Cli::Client.new(options).list
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "info [pad_id]", "gets info for the pad <pad_id>."
|
51
|
+
def info(pad)
|
52
|
+
Hackpad::Cli::Client.new(options).info pad
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "show [pad_id] [format]", "shows pad <pad_id> in format [html,txt,md] (default txt)."
|
56
|
+
def show(pad,format='txt')
|
57
|
+
Hackpad::Cli::Client.new(options).show pad, format
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "version", "Displays the hackpad-cli version."
|
61
|
+
def version
|
62
|
+
puts Hackpad::Cli::VERSION
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
desc "colors", "displays colorize color matrix.", hide: true
|
67
|
+
def colors
|
68
|
+
require 'colorize'
|
69
|
+
String.color_matrix ' xoxo '
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative '../cli'
|
3
|
+
|
4
|
+
module Hackpad
|
5
|
+
module Cli
|
6
|
+
module Store
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def prepare(config)
|
10
|
+
@refresh = config['refresh']
|
11
|
+
@dir = File.join(config['configdir'], config['workspace'])
|
12
|
+
@pads_dir = File.join(@dir, 'pads')
|
13
|
+
@list_cache = File.join(@dir, 'pads.list')
|
14
|
+
FileUtils.mkdir_p @dir unless Dir.exists?(@dir)
|
15
|
+
(Hackpad::Cli::FORMATS + ['meta']).each { |f| FileUtils.mkdir_p File.join(@pads_dir, f) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def exists?(*path)
|
19
|
+
!@refresh && File.exists?(File.join(@pads_dir, *path))
|
20
|
+
end
|
21
|
+
|
22
|
+
def save(pad, ext)
|
23
|
+
File.open(File.join(@pads_dir, ext, pad.id), 'w') do |f|
|
24
|
+
f.puts pad.content
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def save_meta(id, options)
|
29
|
+
File.open(File.join(@pads_dir, 'meta', id), 'w') do |f|
|
30
|
+
f.puts JSON.pretty_generate(options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def save_list(pads)
|
35
|
+
File.open(File.join(@pads_dir, 'padlist'), 'w') do |f|
|
36
|
+
pads.each do |p|
|
37
|
+
f.puts "#{p.id} #{p.title}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def read(id, ext)
|
43
|
+
file = File.join(@pads_dir, ext, id)
|
44
|
+
File.read(file)
|
45
|
+
end
|
46
|
+
|
47
|
+
def read_options(id)
|
48
|
+
file = File.join(@pads_dir, 'meta', id)
|
49
|
+
JSON.parse File.read(file)
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_list
|
53
|
+
File.read(File.join(@pads_dir, 'padlist')).lines.reduce([]) { |a,line|
|
54
|
+
/(?<id>[a-zA-Z0-9]*) (?<title>.*)/ =~ line
|
55
|
+
a << OpenStruct.new( id: id, title: title )
|
56
|
+
a
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/hackpad/cli/version.rb
CHANGED
data/lib/hackpad/cli.rb
CHANGED
@@ -1,44 +1,5 @@
|
|
1
|
-
require "thor"
|
2
|
-
require "colorize"
|
3
|
-
require "yaml"
|
4
|
-
require_relative "client"
|
5
|
-
|
6
1
|
module Hackpad
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
class_option :configdir,
|
11
|
-
aliases: "-c",
|
12
|
-
default: File.join(ENV["HOME"], ".hackpad-cli/"),
|
13
|
-
desc: "Path to the hackpad-cli directory to use."
|
14
|
-
|
15
|
-
class_option :workspace,
|
16
|
-
aliases: "-w",
|
17
|
-
default: "default",
|
18
|
-
desc: "Name of the workspace to use."
|
19
|
-
|
20
|
-
default_task :help
|
21
|
-
|
22
|
-
desc "search [term]", "Lists available pads matching [term]."
|
23
|
-
def search(term)
|
24
|
-
Hackpad::Client.new(options).search term
|
25
|
-
end
|
26
|
-
|
27
|
-
desc "list", "Lists available pads."
|
28
|
-
def list
|
29
|
-
Hackpad::Client.new(options).listall
|
30
|
-
end
|
31
|
-
|
32
|
-
desc "getinfo [pad_id]", "gets info for the pad <pad_id>."
|
33
|
-
def info(pad)
|
34
|
-
Hackpad::Client.new(options).getinfo pad
|
35
|
-
end
|
36
|
-
|
37
|
-
desc "show [pad_id] [format]", "shows pad <pad_id> in format [html,txt,md] (default txt)."
|
38
|
-
def show(pad,format='txt')
|
39
|
-
Hackpad::Client.new(options).show pad, format
|
40
|
-
end
|
41
|
-
|
2
|
+
module Cli
|
3
|
+
FORMATS = %w(txt md html)
|
42
4
|
end
|
43
|
-
|
44
5
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require "hackpad/cli/client"
|
5
|
+
|
6
|
+
describe Hackpad::Cli::Client do
|
7
|
+
|
8
|
+
pending "Hackpad::Cli::Client.new"
|
9
|
+
pending "Hackpad::Cli::Client.search"
|
10
|
+
pending "Hackpad::Cli::Client.list"
|
11
|
+
pending "Hackpad::Cli::Client.info"
|
12
|
+
pending "Hackpad::Cli::Client.show"
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require "hackpad/cli"
|
5
|
+
require "hackpad/cli/pad"
|
6
|
+
require "hackpad/cli/api"
|
7
|
+
require "hackpad/cli/store"
|
8
|
+
|
9
|
+
describe Hackpad::Cli::Pad do
|
10
|
+
|
11
|
+
before :each do
|
12
|
+
Hackpad::Cli::Api.stub(:read).with('123', 'txt').and_return("content\nand body")
|
13
|
+
Hackpad::Cli::Api.stub(:read_options).with('123').and_return({"success" => "true"})
|
14
|
+
options = {
|
15
|
+
"configdir" => File.expand_path('../../../files', __FILE__),
|
16
|
+
"workspace" => 'default'
|
17
|
+
}
|
18
|
+
Hackpad::Cli::Store.prepare options
|
19
|
+
@pad = Hackpad::Cli::Pad.new "123"
|
20
|
+
@pad.load 'txt'
|
21
|
+
end
|
22
|
+
|
23
|
+
after :each do
|
24
|
+
FileUtils.rm_rf File.expand_path('../../../files/default', __FILE__)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "creates a new pad object" do
|
28
|
+
expect(@pad.id).to eq "123"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "Can extract the title" do
|
32
|
+
expect(@pad.title).to eq "content"
|
33
|
+
end
|
34
|
+
it "Can count chars from content" do
|
35
|
+
expect(@pad.chars).to be 16
|
36
|
+
end
|
37
|
+
it "Can count lines from content" do
|
38
|
+
expect(@pad.lines).to be 2
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require "hackpad/cli/runner"
|
5
|
+
require "hackpad/cli/client"
|
6
|
+
|
7
|
+
describe Hackpad::Cli::Runner do
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
@cli = Hackpad::Cli::Runner.new
|
11
|
+
Hackpad::Cli::Client.stub(:new, {}).and_return(Object)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "calls the search method in client class" do
|
15
|
+
Object.stub(:search)
|
16
|
+
@cli.shell.mute do
|
17
|
+
@cli.search "xxx"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "calls the list method in client class" do
|
22
|
+
Object.stub(:list)
|
23
|
+
@cli.shell.mute do
|
24
|
+
@cli.list
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "calls the list method in client class" do
|
29
|
+
Object.stub(:list)
|
30
|
+
@cli.shell.mute do
|
31
|
+
@cli.list
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "calls the info method in client class" do
|
36
|
+
Object.stub(:info)
|
37
|
+
@cli.shell.mute do
|
38
|
+
@cli.info 'pad'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "calls the show method in client class" do
|
43
|
+
Object.stub(:show)
|
44
|
+
@cli.shell.mute do
|
45
|
+
@cli.show 'pad', 'md'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require "hackpad/cli/store"
|
5
|
+
|
6
|
+
describe Hackpad::Cli::Store do
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
options = {
|
10
|
+
"configdir" => File.expand_path('../../../files', __FILE__),
|
11
|
+
"workspace" => 'default'
|
12
|
+
}
|
13
|
+
Hackpad::Cli::Store.prepare options
|
14
|
+
end
|
15
|
+
|
16
|
+
it "reads pads list from file" do
|
17
|
+
File.stub(:read).and_return("gy23ui first one\ngy3u4 second one\n23489g third")
|
18
|
+
list = Hackpad::Cli::Store.read_list
|
19
|
+
expect(list).to be_an Array
|
20
|
+
expect(list[0]).to be_an OpenStruct
|
21
|
+
expect(list[0].id).to eq "gy23ui"
|
22
|
+
expect(list[0].title).to eq "first one"
|
23
|
+
expect(list[2].id).to eq "23489g"
|
24
|
+
expect(list[2].title).to eq "third"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
|
5
|
+
require 'coveralls'
|
6
|
+
Coveralls.wear!
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.mock_with :rspec
|
10
|
+
config.expect_with :rspec do |c|
|
11
|
+
c.syntax = :expect
|
12
|
+
end
|
13
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hackpad-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- mose
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -94,6 +94,48 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webmock
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: coveralls
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
97
139
|
description: A Command Line Interface for consuming the Hackpad REST API.
|
98
140
|
email:
|
99
141
|
- mose@mose.com
|
@@ -102,9 +144,11 @@ executables:
|
|
102
144
|
extensions: []
|
103
145
|
extra_rdoc_files: []
|
104
146
|
files:
|
147
|
+
- ".coveralls.yml"
|
105
148
|
- ".gitignore"
|
106
149
|
- ".ruby-gemset"
|
107
150
|
- ".ruby-version"
|
151
|
+
- ".travis.yml"
|
108
152
|
- CHANGELOG.md
|
109
153
|
- Gemfile
|
110
154
|
- LICENSE.txt
|
@@ -113,10 +157,20 @@ files:
|
|
113
157
|
- bin/hpcli
|
114
158
|
- hackpad-cli.gemspec
|
115
159
|
- lib/hackpad/cli.rb
|
160
|
+
- lib/hackpad/cli/api.rb
|
161
|
+
- lib/hackpad/cli/client.rb
|
162
|
+
- lib/hackpad/cli/config.rb
|
163
|
+
- lib/hackpad/cli/pad.rb
|
164
|
+
- lib/hackpad/cli/padlist.rb
|
165
|
+
- lib/hackpad/cli/plain_colors.rb
|
166
|
+
- lib/hackpad/cli/runner.rb
|
167
|
+
- lib/hackpad/cli/store.rb
|
116
168
|
- lib/hackpad/cli/version.rb
|
117
|
-
- lib/hackpad/
|
118
|
-
- lib/hackpad/
|
119
|
-
- lib/hackpad/
|
169
|
+
- spec/lib/hackpad/cli/client_spec.rb
|
170
|
+
- spec/lib/hackpad/cli/pad_spec.rb
|
171
|
+
- spec/lib/hackpad/cli/runner_spec.rb
|
172
|
+
- spec/lib/hackpad/cli/store_spec.rb
|
173
|
+
- spec/spec_helper.rb
|
120
174
|
homepage: https://github.com/mose/hackpad-cli
|
121
175
|
licenses:
|
122
176
|
- MIT
|
@@ -141,4 +195,9 @@ rubygems_version: 2.2.2
|
|
141
195
|
signing_key:
|
142
196
|
specification_version: 4
|
143
197
|
summary: CLI for hackpad browsing and editing.
|
144
|
-
test_files:
|
198
|
+
test_files:
|
199
|
+
- spec/lib/hackpad/cli/client_spec.rb
|
200
|
+
- spec/lib/hackpad/cli/pad_spec.rb
|
201
|
+
- spec/lib/hackpad/cli/runner_spec.rb
|
202
|
+
- spec/lib/hackpad/cli/store_spec.rb
|
203
|
+
- spec/spec_helper.rb
|
data/lib/hackpad/client.rb
DELETED
@@ -1,100 +0,0 @@
|
|
1
|
-
require 'oauth'
|
2
|
-
require 'net/http'
|
3
|
-
require 'json'
|
4
|
-
require 'cgi'
|
5
|
-
require 'reverse_markdown'
|
6
|
-
|
7
|
-
require_relative 'config'
|
8
|
-
|
9
|
-
module Hackpad
|
10
|
-
class Client
|
11
|
-
|
12
|
-
def initialize(options)
|
13
|
-
@config = Config.load options
|
14
|
-
site = URI.parse @config['site']
|
15
|
-
consumer = OAuth::Consumer.new(
|
16
|
-
@config['client_id'],
|
17
|
-
@config['secret'],
|
18
|
-
site: @config['site']
|
19
|
-
)
|
20
|
-
@token = OAuth::AccessToken.new consumer
|
21
|
-
end
|
22
|
-
|
23
|
-
# GET /api/1.0/pads/all
|
24
|
-
def search(term,start=0)
|
25
|
-
res = @token.get "/api/1.0/search?q=#{CGI.escape term}&start=#{start}&limit=100"
|
26
|
-
if res.is_a? Net::HTTPSuccess
|
27
|
-
all = JSON.parse res.body
|
28
|
-
all.each do |a|
|
29
|
-
puts "#{a['id'].bold} - #{unescape(a['title']).colorize(:yellow)}\n #{extract a['snippet']}"
|
30
|
-
end
|
31
|
-
|
32
|
-
else
|
33
|
-
puts "#{res.inspect}".colorize :red
|
34
|
-
puts "#{res.body}".colorize :red
|
35
|
-
return back
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def listall
|
40
|
-
res = @token.get "/api/1.0/pads/all"
|
41
|
-
if res.is_a? Net::HTTPSuccess
|
42
|
-
all = JSON.parse res.body
|
43
|
-
all.each do |a|
|
44
|
-
getinfo(a)
|
45
|
-
end
|
46
|
-
else
|
47
|
-
puts "#{res.inspect}".colorize :red
|
48
|
-
puts "#{res.body}".colorize :red
|
49
|
-
return back
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def getinfo(pad)
|
54
|
-
res = @token.get "/api/1.0/pad/#{pad}/content.txt"
|
55
|
-
if res.is_a? Net::HTTPSuccess
|
56
|
-
printf "%-20s %s\n", "Id", "#{pad}".bold
|
57
|
-
printf "%-20s %s\n", "Title", "#{res.body.lines.first.chomp}".colorize(:yellow)
|
58
|
-
printf "%-20s %s\n", "URI", "#{@config['site']}/#{pad}"
|
59
|
-
printf "%-20s %s\n", "Size", "#{res.body.length} chars"
|
60
|
-
else
|
61
|
-
puts "#{pad} failed".colorize :red
|
62
|
-
end
|
63
|
-
res = @token.get "/api/1.0/pad/#{pad}/options"
|
64
|
-
if res.is_a? Net::HTTPSuccess
|
65
|
-
a = JSON.parse res.body
|
66
|
-
printf "%-20s %s\n", "Guest Policy", "#{a['options']['guestPolicy']}"
|
67
|
-
printf "%-20s %s\n", "Moderated", "#{a['options']['isModerated'] || "No"}"
|
68
|
-
else
|
69
|
-
puts "#{pad} failed".colorize :red
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def show(pad,format)
|
74
|
-
ext = (format == 'md') ? 'html' : format
|
75
|
-
res = @token.get "/api/1.0/pad/#{pad}/content.#{ext}"
|
76
|
-
if res.is_a? Net::HTTPSuccess
|
77
|
-
puts "#{@config['site']}/#{pad}"
|
78
|
-
puts
|
79
|
-
if format == 'md'
|
80
|
-
puts ReverseMarkdown.convert(res.body, github_flavored: true)
|
81
|
-
else
|
82
|
-
puts res.body
|
83
|
-
end
|
84
|
-
else
|
85
|
-
puts "#{pad} failed".colorize :red
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def unescape(s)
|
92
|
-
CGI.unescapeHTML s
|
93
|
-
end
|
94
|
-
|
95
|
-
def extract(s)
|
96
|
-
unescape(s).gsub(/<b class="hit">([^<]*)<\/b>/) { |e| $1.colorize(:cyan).bold }
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
end
|
data/lib/hackpad/config.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
module Hackpad
|
2
|
-
module Config
|
3
|
-
extend self
|
4
|
-
|
5
|
-
def load(options)
|
6
|
-
configdir = options[:configdir]
|
7
|
-
configfile = File.join(configdir, "#{options[:workspace]}.yml")
|
8
|
-
# temporary migration path
|
9
|
-
if !File.exists?(configfile) && File.exists?(File.join(configdir, "config.yml"))
|
10
|
-
FileUtils.mv File.join(configdir, "config.yml"), configfile
|
11
|
-
end
|
12
|
-
if !Dir.exists?(configdir) || !File.exists?(configfile)
|
13
|
-
setup configfile
|
14
|
-
end
|
15
|
-
YAML::load_file configfile
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def setup(configfile)
|
21
|
-
config = {}
|
22
|
-
FileUtils.mkdir_p File.dirname(configfile)
|
23
|
-
puts "We need first to initialize your hackpad-cli configuration.".colorize(:blue)
|
24
|
-
puts "Please gather your information from https://<subdomain>.hackpad.com/ep/account/settings/"
|
25
|
-
print "What is your Client ID? "
|
26
|
-
STDOUT.flush
|
27
|
-
config['client_id'] = STDIN.gets.chomp
|
28
|
-
print "What is your Secret Key? "
|
29
|
-
STDOUT.flush
|
30
|
-
config['secret'] = STDIN.gets.chomp
|
31
|
-
print "What is the URI of your pad? "
|
32
|
-
STDOUT.flush
|
33
|
-
config['site'] = STDIN.gets.chomp
|
34
|
-
File.open(configfile, "w") do |f|
|
35
|
-
f.write YAML::dump(config)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|