pinup 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ # NOTE CI does not run all tests, some tests require valid Pinboard credentials in the netrc file
2
+ language: ruby
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
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,8 @@
1
+ require 'pinup/bookmark'
2
+ require 'pinup/queries'
3
+ require 'pinup/settings'
4
+ require 'pinup/version'
5
+
6
+ require 'pinup/commands/authorize'
7
+ require 'pinup/commands/list'
8
+ require 'pinup/commands/open'
@@ -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