pinup 0.1.0
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 +15 -0
- data/.gitignore +53 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +2 -0
- data/LICENSE +6 -0
- data/README.md +69 -0
- data/Rakefile +9 -0
- data/bin/pinup +92 -0
- data/lib/pinup.rb +8 -0
- data/lib/pinup/bookmark.rb +29 -0
- data/lib/pinup/commands/authorize.rb +114 -0
- data/lib/pinup/commands/list.rb +21 -0
- data/lib/pinup/commands/open.rb +25 -0
- data/lib/pinup/queries.rb +116 -0
- data/lib/pinup/settings.rb +89 -0
- data/lib/pinup/version.rb +8 -0
- data/pinup.gemspec +24 -0
- data/spec/integration/authorize_spec.rb +42 -0
- data/spec/integration/bookmark_spec.rb +97 -0
- data/spec/integration/queries_spec.rb +132 -0
- data/spec/integration/settings_spec.rb +213 -0
- data/spec/spec_helper.rb +21 -0
- metadata +155 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'launchy'
|
2
|
+
|
3
|
+
module Pinup
|
4
|
+
class Open
|
5
|
+
def self.open_pins(options = {})
|
6
|
+
unread = options[:unread] unless options[:unread].nil?
|
7
|
+
untagged = options[:untagged] unless options[:untagged].nil?
|
8
|
+
count = options[:number].to_i unless options[:number].nil?
|
9
|
+
delete = options[:delete] unless options[:delete].nil?
|
10
|
+
|
11
|
+
items = Pinup::Queries.list_items
|
12
|
+
filtered = Pinup::Queries.filter_items(items, unread, untagged, count)
|
13
|
+
items_string = Pinup::Queries.item_string(filtered)
|
14
|
+
urls = items_string.split(/\n/)
|
15
|
+
|
16
|
+
urls.each do |url|
|
17
|
+
Launchy.open(url)
|
18
|
+
end
|
19
|
+
|
20
|
+
if delete
|
21
|
+
Pinup::Queries.delete_items(urls)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Pinup
|
6
|
+
class Queries
|
7
|
+
def self.list_items
|
8
|
+
token = Pinup::Settings.get_token
|
9
|
+
if token.nil?
|
10
|
+
return nil
|
11
|
+
end
|
12
|
+
|
13
|
+
parameters = JSON_PARAMS.dup
|
14
|
+
parameters[:auth_token] = token
|
15
|
+
|
16
|
+
response = list_query(parameters)
|
17
|
+
if response.code != '200'
|
18
|
+
puts "Error getting bookmarks: #{ response.body }"
|
19
|
+
return nil
|
20
|
+
else
|
21
|
+
return response.body
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.filter_items(response, unread, untagged, count)
|
26
|
+
begin
|
27
|
+
json = JSON.parse(response)
|
28
|
+
rescue JSON::ParserError => e
|
29
|
+
puts "Failed to parse JSON: #{ e }"
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
count = 20 if count < 1
|
34
|
+
new_items = []
|
35
|
+
|
36
|
+
json.each do |item|
|
37
|
+
bookmark = Bookmark.new(item)
|
38
|
+
|
39
|
+
if unread && untagged
|
40
|
+
if bookmark.unread || bookmark.untagged
|
41
|
+
new_items << bookmark
|
42
|
+
end
|
43
|
+
elsif unread
|
44
|
+
if bookmark.unread && !bookmark.untagged
|
45
|
+
new_items << bookmark
|
46
|
+
end
|
47
|
+
elsif untagged
|
48
|
+
if bookmark.untagged && !bookmark.unread
|
49
|
+
new_items << bookmark
|
50
|
+
end
|
51
|
+
else
|
52
|
+
if !bookmark.unread && !bookmark.untagged
|
53
|
+
new_items << bookmark
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if new_items.count >= count
|
58
|
+
break
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
return new_items
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.delete_items(urls)
|
66
|
+
token = Pinup::Settings.get_token
|
67
|
+
if token.nil?
|
68
|
+
return nil
|
69
|
+
end
|
70
|
+
|
71
|
+
parameters = JSON_PARAMS.dup
|
72
|
+
parameters[:auth_token] = token
|
73
|
+
|
74
|
+
urls.each do |url|
|
75
|
+
url_params = parameters.dup
|
76
|
+
url_params[:url] = url
|
77
|
+
delete_query(url_params)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.item_string(items)
|
82
|
+
item_output = ""
|
83
|
+
items.each do |item|
|
84
|
+
item_output << "#{ item.href }\n"
|
85
|
+
end
|
86
|
+
|
87
|
+
return item_output
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def self.list_query(parameters)
|
93
|
+
uri = URI.parse("#{ API_URL }/posts/all")
|
94
|
+
uri.query = URI.encode_www_form(parameters)
|
95
|
+
|
96
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
97
|
+
http.use_ssl = true
|
98
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
99
|
+
|
100
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
101
|
+
http.request(request)
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.delete_query(parameters)
|
105
|
+
uri = URI.parse("#{ API_URL }/posts/delete")
|
106
|
+
uri.query = URI.encode_www_form(parameters)
|
107
|
+
|
108
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
109
|
+
http.use_ssl = true
|
110
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
111
|
+
|
112
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
113
|
+
http.request(request)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'netrc'
|
3
|
+
require 'colored'
|
4
|
+
|
5
|
+
module Pinup
|
6
|
+
class Settings
|
7
|
+
def self.write_settings(settings)
|
8
|
+
File.open(SETTINGS, 'w') do |f|
|
9
|
+
f.write(settings.to_yaml)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.read_settings
|
14
|
+
if !File.exists? SETTINGS
|
15
|
+
return nil
|
16
|
+
end
|
17
|
+
|
18
|
+
settings = YAML::load_file(SETTINGS)
|
19
|
+
if !settings || settings.empty?
|
20
|
+
return nil
|
21
|
+
end
|
22
|
+
|
23
|
+
return settings
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.save_token(options = {})
|
27
|
+
path = DEFAULT_NETRC
|
28
|
+
token = options[:token]
|
29
|
+
|
30
|
+
if token.nil?
|
31
|
+
puts 'Attempted to save empty token'.red
|
32
|
+
return nil
|
33
|
+
end
|
34
|
+
|
35
|
+
if options[:path]
|
36
|
+
path = options[:path]
|
37
|
+
end
|
38
|
+
|
39
|
+
token_split = token.split(/:/)
|
40
|
+
if token_split.count != 2
|
41
|
+
puts "Invalid token #{ token_split.join(':') }".red
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
|
45
|
+
username = token_split.first
|
46
|
+
password = token_split.last
|
47
|
+
|
48
|
+
netrc = Netrc.read(path)
|
49
|
+
netrc.new_item_prefix = "\n# This Entry was added automatically\n"
|
50
|
+
netrc[PINBOARD_URL] = username, password
|
51
|
+
netrc.save
|
52
|
+
|
53
|
+
return true
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.get_token
|
57
|
+
path = DEFAULT_NETRC
|
58
|
+
|
59
|
+
settings = read_settings
|
60
|
+
if settings
|
61
|
+
path = settings[:path]
|
62
|
+
end
|
63
|
+
|
64
|
+
netrc = Netrc.read(path)
|
65
|
+
username, password = netrc[PINBOARD_URL]
|
66
|
+
token = token(username, password)
|
67
|
+
if token.nil?
|
68
|
+
puts "There are no credentials in #{ path }".red
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
|
72
|
+
return token
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.clear_settings
|
76
|
+
if File.exists? SETTINGS
|
77
|
+
File.delete(SETTINGS)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.token(username, password)
|
82
|
+
if username.nil? || password.nil? || username.empty? || password.empty?
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
|
86
|
+
"#{ username }:#{ password }"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/pinup.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.join([File.dirname(__FILE__),'lib','pinup','version.rb'])
|
2
|
+
spec = Gem::Specification.new do |s|
|
3
|
+
s.name = 'pinup'
|
4
|
+
s.version = Pinup::VERSION
|
5
|
+
s.author = 'Keith Smiley'
|
6
|
+
s.email = 'keithbsmiley@gmail.com'
|
7
|
+
s.homepage = 'http://keith.so/'
|
8
|
+
s.required_ruby_version = '>= 1.9.3'
|
9
|
+
s.summary = 'Digest your Pinboard bookmarks in bulk'
|
10
|
+
s.description = 'Allows you to open and delete your Pinboard bookmarks in bulk'
|
11
|
+
s.license = 'MIT'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split($/)
|
14
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
15
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
|
18
|
+
s.add_dependency('colored', '~> 1.2')
|
19
|
+
s.add_dependency('netrc', '~> 0.7.7')
|
20
|
+
s.add_dependency('launchy', '~> 2.3.0')
|
21
|
+
s.add_development_dependency('rake')
|
22
|
+
s.add_development_dependency('rspec', '~> 2.13.0')
|
23
|
+
s.add_runtime_dependency('gli','2.5.6')
|
24
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Pinup::Authorize do
|
4
|
+
before do
|
5
|
+
@netrc_path = File.expand_path('~/foobar')
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'authorize_netrc' do
|
9
|
+
describe 'an empty netrc' do
|
10
|
+
it 'should return nil' do
|
11
|
+
expect(Pinup::Authorize.authorize_netrc({ path: @netrc_path })).to be_nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'a valid netrc' do
|
16
|
+
it 'should return true' do
|
17
|
+
# This will only pass if your default netrc has a valid username and password (token)
|
18
|
+
expect(Pinup::Authorize.authorize_netrc).to be_true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'authorize_credentials' do
|
24
|
+
describe 'invalid credentials' do
|
25
|
+
it 'should return nil' do
|
26
|
+
expect(Pinup::Authorize.authorize_credentials({ username: 'foo', password: 'bar' })).to be_nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'valid credentials' do
|
31
|
+
# Note: this will only work if you set up netrc's for this testing
|
32
|
+
it 'should return true' do
|
33
|
+
netrc = Netrc.read
|
34
|
+
username, password = netrc['test.pinboard.in']
|
35
|
+
expect(username).not_to be_nil
|
36
|
+
expect(password).not_to be_nil
|
37
|
+
result = Pinup::Authorize.authorize_credentials({ username: username, password: password })
|
38
|
+
expect(result).to be_true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Bookmark do
|
4
|
+
describe 'attributes' do
|
5
|
+
before do
|
6
|
+
options = { 'href' => 'http://github.com', 'toread' => 'yes', 'tags' => '' }
|
7
|
+
@bookmark = Bookmark.new(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should respond to the correct attributes' do
|
11
|
+
expect(@bookmark).to respond_to(:href)
|
12
|
+
expect(@bookmark).to respond_to(:unread)
|
13
|
+
expect(@bookmark).to respond_to(:untagged)
|
14
|
+
expect(@bookmark).not_to respond_to(:foobar)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'initialize' do
|
19
|
+
describe 'unread without tags' do
|
20
|
+
before do
|
21
|
+
options = { 'href' => 'http://github.com', 'toread' => 'yes', 'tags' => '' }
|
22
|
+
@bookmark = Bookmark.new(options)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'have the correct attributes' do
|
26
|
+
expect(@bookmark).not_to be_nil
|
27
|
+
result = @bookmark.href == 'http://github.com'
|
28
|
+
expect(result).to be_true
|
29
|
+
expect(@bookmark.unread).to be_true
|
30
|
+
expect(@bookmark.untagged).to be_true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'read with tags' do
|
35
|
+
before do
|
36
|
+
options = { 'href' => 'http://google.com', 'toread' => 'no', 'tags' => 'foo' }
|
37
|
+
@bookmark = Bookmark.new(options)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'have the correct attributes' do
|
41
|
+
expect(@bookmark).not_to be_nil
|
42
|
+
result = @bookmark.href == 'http://google.com'
|
43
|
+
expect(result).to be_true
|
44
|
+
expect(@bookmark.unread).to be_false
|
45
|
+
expect(@bookmark.untagged).to be_false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'is_unread' do
|
51
|
+
it 'should return true only when toread is "yes"' do
|
52
|
+
expect(Bookmark.is_unread('yes')).to be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should return false otherwise' do
|
56
|
+
expect(Bookmark.is_unread('false')).to be_false
|
57
|
+
expect(Bookmark.is_unread('no')).to be_false
|
58
|
+
expect(Bookmark.is_unread('foo')).to be_false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
describe 'is_untagged' do
|
64
|
+
it 'should return true if there is nothing in the tag field' do
|
65
|
+
expect(Bookmark.is_untagged(' ')).to be_true
|
66
|
+
expect(Bookmark.is_untagged("\n \t \r")).to be_true
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should return false if there is something in the tag field' do
|
70
|
+
expect(Bookmark.is_untagged('foo')).to be_false
|
71
|
+
expect(Bookmark.is_untagged('foo bar')).to be_false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'to_s' do
|
76
|
+
before do
|
77
|
+
options = { 'href' => 'http://google.com', 'toread' => 'no', 'tags' => 'foo' }
|
78
|
+
@bookmark = Bookmark.new(options)
|
79
|
+
@response = @bookmark.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should contain the URL' do
|
83
|
+
result = @response.include? 'google.com'
|
84
|
+
expect(result).to be_true
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should contain unread settings' do
|
88
|
+
result = @response.include? 'Unread: false'
|
89
|
+
expect(result).to be_true
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should contain the URL' do
|
93
|
+
result = @response.include? 'Untagged: false'
|
94
|
+
expect(result).to be_true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Pinup::Queries do
|
4
|
+
describe 'list_items' do
|
5
|
+
before do
|
6
|
+
items = Pinup::Queries.list_items
|
7
|
+
@posts = JSON.parse(items)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'shoud return some number of items' do
|
11
|
+
result = @posts.count > 0
|
12
|
+
expect(result).to be_true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'filter_items' do
|
17
|
+
describe 'read/unread items' do
|
18
|
+
describe 'unread items' do
|
19
|
+
before do
|
20
|
+
items = Pinup::Queries.list_items
|
21
|
+
@filtered = Pinup::Queries.filter_items(items, true, false, 20)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should return only unread items' do
|
25
|
+
@filtered.each do |item|
|
26
|
+
expect(item.unread).to be_true
|
27
|
+
expect(item.untagged).to be_false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'read items' do
|
33
|
+
before do
|
34
|
+
items = Pinup::Queries.list_items
|
35
|
+
@filtered = Pinup::Queries.filter_items(items, false, false, 20)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return only read items' do
|
39
|
+
@filtered.each do |item|
|
40
|
+
expect(item.unread).to be_false
|
41
|
+
expect(item.untagged).to be_false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'untagged/tagged items' do
|
48
|
+
describe 'untagged items' do
|
49
|
+
before do
|
50
|
+
items = Pinup::Queries.list_items
|
51
|
+
@filtered = Pinup::Queries.filter_items(items, false, true, 20)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should return only untagged items' do
|
55
|
+
@filtered.each do |item|
|
56
|
+
expect(item.unread).to be_false
|
57
|
+
expect(item.untagged).to be_true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'tagged items' do
|
63
|
+
before do
|
64
|
+
items = Pinup::Queries.list_items
|
65
|
+
@filtered = Pinup::Queries.filter_items(items, false, false, 20)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should return only untagged items' do
|
69
|
+
result = @filtered.count > 0
|
70
|
+
expect(result).to be_true
|
71
|
+
@filtered.each do |item|
|
72
|
+
expect(item.unread).to be_false
|
73
|
+
expect(item.untagged).to be_false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Note these will only work if the default pinboard account unread & untagged items
|
80
|
+
describe 'number of items' do
|
81
|
+
describe 'an invalid number' do
|
82
|
+
describe 'zero items' do
|
83
|
+
before do
|
84
|
+
items = Pinup::Queries.list_items
|
85
|
+
@filtered = Pinup::Queries.filter_items(items, false, false, 0)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should return the default number of items' do
|
89
|
+
expect(@filtered.count).to equal(20)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'negative items' do
|
94
|
+
before do
|
95
|
+
items = Pinup::Queries.list_items
|
96
|
+
@filtered = Pinup::Queries.filter_items(items, false, false, -9)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should return the default number of items' do
|
100
|
+
expect(@filtered.count).to equal(20)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe 'some number of unread items' do
|
106
|
+
before do
|
107
|
+
items = Pinup::Queries.list_items
|
108
|
+
@filtered = Pinup::Queries.filter_items(items, false, false, 14)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should return the correct number of items' do
|
112
|
+
expect(@filtered.count).to equal(14)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'item_string' do
|
119
|
+
before do
|
120
|
+
@item = Bookmark.new({ "href" => "http://google.com" })
|
121
|
+
@item2 = Bookmark.new({ "href" => "http://github.com" })
|
122
|
+
@urls = [ @item, @item2 ]
|
123
|
+
@response = Pinup::Queries.item_string(@urls)
|
124
|
+
@string = "http://google.com\nhttp://github.com\n"
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should return a correctly formatted string' do
|
128
|
+
result = @response == @string
|
129
|
+
expect(result).to be_true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|