pinup 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NmE3YzhkNTM1NjZiY2E4OWUxOTI2M2NmYzI1ZjE1N2M5ZGVhOTk5OQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YzA2MzQxNjBiNmE1OWM5YjIwZDM3ZWFlMjZjMDViODAwOTkxOGM5Mw==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
Y2Q3ODFlNTAxNzk3NGQ2MzUwNDRhNjU3ZTAyMzFkYzE4OGJlZTMyNTdiZTky
|
10
|
+
Nzc3ZTE3Y2JmZWFjNzMxYzEwNTI2NGY0ZDlhNWE5ODVhYmVjMzBmMWU0YTYx
|
11
|
+
ZmQ3NjEyZTg4ZWVmMmQyYmMzYWRmMDZiMThmM2M5NTFkMWI2MGQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OWY4NDNmOWMzZDBkYjY3ZjE3YjY3MzdlNDdjODJjNzgzMjlmYTdkYzYxOWZk
|
14
|
+
YmMxZTE4NDZjM2E0N2IzZDZhNTU3MzE1ZDJhNmE4ZTI2OTc0OGE4ODAzMTc1
|
15
|
+
MWM3ZjY0ZDRlYzc3MTFiOTFmYzVhNmE5MWQ5ZTA1OTVhN2IyOTY=
|
data/.gitignore
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
####### OSX #######
|
19
|
+
.DS_Store
|
20
|
+
.AppleDouble
|
21
|
+
.LSOverride
|
22
|
+
Icon
|
23
|
+
|
24
|
+
|
25
|
+
# Thumbnails
|
26
|
+
._*
|
27
|
+
|
28
|
+
# Files that might appear on external disk
|
29
|
+
.Spotlight-V100
|
30
|
+
.Trashes
|
31
|
+
|
32
|
+
|
33
|
+
####### Ruby #######
|
34
|
+
*.gem
|
35
|
+
*.rbc
|
36
|
+
.bundle
|
37
|
+
.config
|
38
|
+
coverage
|
39
|
+
InstalledFiles
|
40
|
+
lib/bundler/man
|
41
|
+
pkg
|
42
|
+
rdoc
|
43
|
+
spec/reports
|
44
|
+
test/tmp
|
45
|
+
test/version_tmp
|
46
|
+
tmp
|
47
|
+
|
48
|
+
# YARD artifacts
|
49
|
+
.yardoc
|
50
|
+
_yardoc
|
51
|
+
doc/
|
52
|
+
|
53
|
+
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
Copyright (c) 2013 Keith Smiley (http://keith.so)
|
2
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
3
|
+
|
4
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
5
|
+
|
6
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# pinup
|
2
|
+
|
3
|
+
Pinup is a [RubyGem](http://rubygems.org/) for quickly getting through your [Pinboard](https://pinboard.in) bookmarks. It allows you to open or list your bookmarks and delete them afterwards (if you'd like).
|
4
|
+
|
5
|
+
[![Build Status](https://travis-ci.org/Keithbsmiley/pinup.png?branch=master)](https://travis-ci.org/Keithbsmiley/pinup)
|
6
|
+
[![Dependency Status](https://gemnasium.com/Keithbsmiley/pinup.png)](https://gemnasium.com/Keithbsmiley/pinup)
|
7
|
+
|
8
|
+
## Authorization
|
9
|
+
|
10
|
+
First you need to setup your Pinboard credentials. Either:
|
11
|
+
|
12
|
+
```
|
13
|
+
pinup authorize
|
14
|
+
```
|
15
|
+
|
16
|
+
This will ask you for your username and password, which are then used to get your user token from Pinboard and save it to your `~/.netrc`
|
17
|
+
|
18
|
+
Or in your `~/.netrc` (get your token from <https://pinboard.in/settings/password> and split it at the `:`)
|
19
|
+
|
20
|
+
```
|
21
|
+
machine pinboard.in
|
22
|
+
login username
|
23
|
+
password token
|
24
|
+
```
|
25
|
+
|
26
|
+
Note that in the end these achieve the same result. Your Pinboard username and token (not password) are saved in your `~/.netrc` file. If you'd like to save it in a different location check out the `--path` argument with `pinup authorize -h`
|
27
|
+
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
```
|
32
|
+
pinup open
|
33
|
+
```
|
34
|
+
|
35
|
+
By default this will open your 20 most recent unread or untagged bookmarks in the default browser. If that's not to your fancy you can use some flags to change the behavior:
|
36
|
+
|
37
|
+
```
|
38
|
+
pinup open --no-untagged
|
39
|
+
```
|
40
|
+
|
41
|
+
This will open your 20 most recent unread bookmarks, without the untagged ones. There is also a `--no-unread` flag for just untagged entires. Using:
|
42
|
+
|
43
|
+
```
|
44
|
+
pinup open --no-untagged --no-unread
|
45
|
+
```
|
46
|
+
|
47
|
+
Will open bookmarks that are both tagged and marked as read. I think you get the idea. But of course you can also limit the number of responses.
|
48
|
+
|
49
|
+
```
|
50
|
+
pinup open -n 5
|
51
|
+
pinup open --number=5
|
52
|
+
```
|
53
|
+
|
54
|
+
Will show the most recent five bookmarks. You can also delete the bookmarks from Pinboard as they are opened:
|
55
|
+
|
56
|
+
```
|
57
|
+
pinup open --no-untagged --delete -n 5
|
58
|
+
```
|
59
|
+
|
60
|
+
There is also a list command that takes the same arguments as `open` but instead of opening the URLs it just prints them one per line to the console. This way you could pipe the output in to another command for additional processing.
|
61
|
+
|
62
|
+
## Installation
|
63
|
+
|
64
|
+
```
|
65
|
+
[sudo] gem install pinup
|
66
|
+
```
|
67
|
+
|
68
|
+
Pinup requires Ruby 1.9+ I recommend you install it with [rbenv](https://github.com/sstephenson/rbenv/). [RVM](https://rvm.io/) exists as well. Note that this is higher than the default installation on OS X. Ruby 1.8 is [no longer being supported](http://www.ruby-lang.org/en/news/2011/10/06/plans-for-1-8-7/) so I suggest you upgrade anyways.
|
69
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
# NOTE CI does not run all tests, some tests require valid Pinboard credentials in the netrc file
|
5
|
+
RSpec::Core::RakeTask.new do |t|
|
6
|
+
t.pattern = FileList['spec/**/*.rb'].exclude(/authorize|queries/)
|
7
|
+
end
|
8
|
+
|
9
|
+
task :default => :spec
|
data/bin/pinup
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'gli'
|
3
|
+
require 'pinup'
|
4
|
+
|
5
|
+
include GLI::App
|
6
|
+
|
7
|
+
program_desc 'Digest your Pinboard bookmarks in bulk'
|
8
|
+
|
9
|
+
desc 'Print the version of pinup'
|
10
|
+
version Pinup::VERSION
|
11
|
+
|
12
|
+
desc "Print your bookmarks URL's to the console"
|
13
|
+
command :list do |c|
|
14
|
+
c.desc 'Only show unread items'
|
15
|
+
c.default_value true
|
16
|
+
c.switch :unread
|
17
|
+
|
18
|
+
c.desc 'Only show untagged items'
|
19
|
+
c.default_value true
|
20
|
+
c.switch :untagged
|
21
|
+
|
22
|
+
c.desc 'Delete the items after opening them'
|
23
|
+
c.default_value false
|
24
|
+
c.switch :delete
|
25
|
+
|
26
|
+
c.desc 'The number of recent items to search through'
|
27
|
+
c.default_value 20
|
28
|
+
c.flag [:n, :number]
|
29
|
+
|
30
|
+
c.action do |global_options,options,args|
|
31
|
+
Pinup::List.list_pins(options)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Open your bookmarks with the specified settings in your browser'
|
36
|
+
command :open do |c|
|
37
|
+
c.desc 'Only show unread items'
|
38
|
+
c.default_value true
|
39
|
+
c.switch :unread
|
40
|
+
|
41
|
+
c.desc 'Only show untagged items'
|
42
|
+
c.default_value true
|
43
|
+
c.switch :untagged
|
44
|
+
|
45
|
+
c.desc 'Delete the items after opening them'
|
46
|
+
c.default_value false
|
47
|
+
c.switch :delete
|
48
|
+
|
49
|
+
c.desc 'The number of recent items to search through'
|
50
|
+
c.default_value 20
|
51
|
+
c.flag [:n, :number]
|
52
|
+
|
53
|
+
c.action do |global_options,options,args|
|
54
|
+
Pinup::Open.open_pins(options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'Authorize your user credentials, you must run this first'
|
59
|
+
command :authorize do |c|
|
60
|
+
c.desc 'Get your Pinboard credentials from your netrc'
|
61
|
+
c.switch :netrc
|
62
|
+
|
63
|
+
c.desc 'Custom netrc path'
|
64
|
+
c.flag :path
|
65
|
+
|
66
|
+
c.action do |global_options,options,args|
|
67
|
+
Pinup::Authorize.authorize_command(options)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
pre do |global,command,options,args|
|
72
|
+
# Pre logic here
|
73
|
+
# Return true to proceed; false to abort and not call the
|
74
|
+
# chosen command
|
75
|
+
# Use skips_pre before a command to skip this block
|
76
|
+
# on that command only
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
post do |global,command,options,args|
|
81
|
+
# Post logic here
|
82
|
+
# Use skips_post before a command to skip this
|
83
|
+
# block on that command only
|
84
|
+
end
|
85
|
+
|
86
|
+
on_error do |exception|
|
87
|
+
# Error logic here
|
88
|
+
# return false to skip default error handling
|
89
|
+
true
|
90
|
+
end
|
91
|
+
|
92
|
+
exit run(ARGV)
|
data/lib/pinup.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
class Bookmark
|
2
|
+
attr_accessor :href, :unread, :untagged
|
3
|
+
|
4
|
+
def initialize(options = {})
|
5
|
+
@href = options['href'] unless options['href'].nil?
|
6
|
+
@unread = self.class.is_unread(options['toread']) unless options['toread'].nil?
|
7
|
+
@untagged = self.class.is_untagged(options['tags']) unless options['tags'].nil?
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.is_unread(attribute)
|
11
|
+
if attribute == 'yes'
|
12
|
+
true
|
13
|
+
else
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.is_untagged(attribute)
|
19
|
+
if attribute.strip.empty?
|
20
|
+
true
|
21
|
+
else
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"<Bookmark: ##{ self.object_id } URL: #{ @href } Unread: #{ self.unread } Untagged: #{ self.untagged }>"
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'io/console'
|
2
|
+
require 'net/https'
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'netrc'
|
6
|
+
require 'colored'
|
7
|
+
|
8
|
+
module Pinup
|
9
|
+
class Authorize
|
10
|
+
def self.authorize_command(options)
|
11
|
+
if options[:netrc]
|
12
|
+
authorize_netrc(options)
|
13
|
+
else
|
14
|
+
authorize_credentials(options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.authorize_netrc(options = {})
|
19
|
+
path = DEFAULT_NETRC
|
20
|
+
if options[:path]
|
21
|
+
path = File.expand_path(options[:path])
|
22
|
+
end
|
23
|
+
|
24
|
+
netrc = Netrc.read(path)
|
25
|
+
username, password = netrc[PINBOARD_URL]
|
26
|
+
if username.nil? || password.nil?
|
27
|
+
puts "Couldn't read credentials from #{ path }".red
|
28
|
+
puts 'Valid netrc syntax for pinboard looks like:
|
29
|
+
|
30
|
+
machine pinboard.in
|
31
|
+
login username
|
32
|
+
password apitoken
|
33
|
+
|
34
|
+
NOTE: Just include the digits from the API token for the password'.yellow
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
token = Pinup::Settings.token(username, password)
|
39
|
+
parameters = JSON_PARAMS.dup
|
40
|
+
parameters[:auth_token] = token
|
41
|
+
response = authorize({ params: parameters })
|
42
|
+
|
43
|
+
if response.code != '200'
|
44
|
+
puts "Invalid user credentials in #{ path }".red
|
45
|
+
return nil
|
46
|
+
elsif !path.nil? && DEFAULT_NETRC != path
|
47
|
+
Pinup::Settings.write_settings({ path: path })
|
48
|
+
else
|
49
|
+
Pinup::Settings.clear_settings
|
50
|
+
end
|
51
|
+
|
52
|
+
return true
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.authorize_credentials(options = {})
|
56
|
+
# Ask for user and pass, save to passed or default netrc location
|
57
|
+
# Reading from options hash for testing
|
58
|
+
username = options[:username] || ask('Enter your username')
|
59
|
+
print 'Enter your password (not saved): ' if options[:password].nil?
|
60
|
+
password = options[:password] || STDIN.noecho(&:gets).chomp
|
61
|
+
|
62
|
+
parameters = { params: JSON_PARAMS.dup, username: username, password: password }
|
63
|
+
response = authorize(parameters)
|
64
|
+
|
65
|
+
if response.code != '200'
|
66
|
+
puts 'Invalid user credentials'.red
|
67
|
+
return nil
|
68
|
+
else
|
69
|
+
path = DEFAULT_NETRC
|
70
|
+
if options[:path]
|
71
|
+
path = File.expand_path(options[:path])
|
72
|
+
end
|
73
|
+
|
74
|
+
json = JSON.parse(response.body)
|
75
|
+
digits = json['result']
|
76
|
+
token = Pinup::Settings.token(username, digits)
|
77
|
+
|
78
|
+
options[:path] = path
|
79
|
+
options[:token] = token
|
80
|
+
|
81
|
+
Pinup::Settings.save_token(options)
|
82
|
+
|
83
|
+
return true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.ask(string)
|
88
|
+
print "#{ string }: "
|
89
|
+
gets.chomp
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def self.authorize(options = {})
|
95
|
+
parameters = options[:params] || {}
|
96
|
+
username = options[:username] || {}
|
97
|
+
password = options[:password] || {}
|
98
|
+
|
99
|
+
uri = URI.parse("#{ API_URL }/user/api_token")
|
100
|
+
uri.query = URI.encode_www_form(parameters)
|
101
|
+
|
102
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
103
|
+
http.use_ssl = true
|
104
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
105
|
+
|
106
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
107
|
+
if !username.nil? && !username.empty? && !password.nil? && !password.empty?
|
108
|
+
request.basic_auth(username, password)
|
109
|
+
end
|
110
|
+
|
111
|
+
http.request(request)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Pinup
|
2
|
+
class List
|
3
|
+
def self.list_pins(options = {})
|
4
|
+
unread = options[:unread] unless options[:unread].nil?
|
5
|
+
untagged = options[:untagged] unless options[:untagged].nil?
|
6
|
+
count = options[:number].to_i unless options[:number].nil?
|
7
|
+
delete = options[:delete] unless options[:delete].nil?
|
8
|
+
|
9
|
+
items = Pinup::Queries.list_items
|
10
|
+
filtered = Pinup::Queries.filter_items(items, unread, untagged, count)
|
11
|
+
items_string = Pinup::Queries.item_string(filtered)
|
12
|
+
urls = items_string.split(/\n/)
|
13
|
+
|
14
|
+
puts items_string
|
15
|
+
|
16
|
+
if delete
|
17
|
+
Pinup::Queries.delete_items(urls)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|