delicious-cli 0.1.1

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Chris Gahan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,63 @@
1
+ = delicious-cli
2
+
3
+ Locally mirror your delicious links, then search them. Example:
4
+
5
+ dels <search query>
6
+
7
+ == Installation
8
+
9
+ gem sources --add http://gemcutter.org
10
+ gem install delicious-cli
11
+
12
+ == Example:
13
+
14
+ === Setting your delicious username/password:
15
+
16
+ epi@fizz$ dels -c
17
+
18
+ Delicious login info
19
+ ---------------------------
20
+
21
+ Username: mydeliciouslogin
22
+ Password: mypassword
23
+
24
+ User: "mydeliciouslogin" | Pass: "mypassword"
25
+ Is this correct? [y/N] y
26
+
27
+ * Checking that login/password works...
28
+ |_ Login successful! Saving config...
29
+
30
+
31
+ === Downloading all your delicious links:
32
+
33
+ epi@fizz$ dels -r
34
+
35
+ * Redownloading database...
36
+ * Retrieving all links... (3465 links found)
37
+ * Inserting links into database...done!
38
+
39
+ === Downloading new links (since last sync):
40
+
41
+ epi@fizz$ dels -s
42
+
43
+ * Synchronizing database...
44
+ * Retrieving new links... (5 links found)
45
+ * Inserting links into database...done!
46
+
47
+ == Commandline options:
48
+
49
+ Usage: dels [options] <search query>
50
+
51
+ Specific options:
52
+ -d, --debug Debug info
53
+ -s, --sync Synchronize links
54
+ -r, --redownload Erase database and redownload all links
55
+ -c, --config Configure app (set delicious username/password)
56
+ -t, --test-auth Test that authentication info works
57
+
58
+ Common options:
59
+ -h, --help Show this message
60
+
61
+ == Copyright
62
+
63
+ Copyright (c) 2009 Chris Gahan. See LICENSE for details.
data/bin/dels ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path(File.dirname(__FILE__))
4
+
5
+ require 'delicious-cli'
6
+ main
@@ -0,0 +1,36 @@
1
+ require 'httparty'
2
+ require 'delicious-cli/settings'
3
+
4
+ # Documentation:
5
+ # http://delicious.com/help/api
6
+
7
+ class Delicious
8
+ include HTTParty
9
+ base_uri 'api.del.icio.us:443/v1'
10
+ format :xml
11
+
12
+ def self.posts_update
13
+ result = get('/posts/update')
14
+ if result["update"]
15
+ result["update"]["time"]
16
+ else
17
+ nil
18
+ end
19
+ end
20
+
21
+ def self.posts_all(options={})
22
+ result = get('/posts/all', :query=>options)
23
+ [result["posts"]["post"]].flatten # ensure it's an array
24
+ end
25
+
26
+ def self.posts_since(time)
27
+ $log.debug "Retrieving links newer than #{time}"
28
+ results = posts_all(:fromdt=>time)
29
+ results.select { |r| r["time"] != time }
30
+ end
31
+
32
+ def self.valid_auth?
33
+ not posts_update.nil?
34
+ end
35
+
36
+ end
@@ -0,0 +1,113 @@
1
+ require 'sequel'
2
+ require 'sequel/extensions/blank'
3
+
4
+ require 'delicious-cli/settings'
5
+ require 'delicious-cli/api'
6
+
7
+ #################################################################
8
+
9
+ $log.debug "Loading database..."
10
+ DB = Sequel.sqlite(configfile('delicious.db'))
11
+ $log.debug "done."
12
+
13
+ #################################################################
14
+
15
+ =begin
16
+ Sample record:
17
+
18
+ {
19
+ "href"=>"http://guru.com/",
20
+ "time"=>"2009-04-24T06:06:37Z",
21
+ "hash"=>"80e9a5f0bccb7f9e9dfd2e309eabd7de",
22
+ "tag"=>"jobs business freelance employment outsourcing exchanges networking",
23
+ "meta"=>"485e955fe2937fbef85390623ad0984c",
24
+ "description"=>"Guru.com",
25
+ "extended"=>"Worldwide freelancer marketplace."
26
+ }
27
+ =end
28
+
29
+ #################################################################
30
+
31
+ class Database
32
+ @@posts = DB[:posts]
33
+
34
+ def self.init!
35
+ create_posts! unless DB.table_exists?(:posts)
36
+ end
37
+
38
+ def self.clear!
39
+ DB.drop_table(:posts)
40
+ create_posts!
41
+ end
42
+
43
+ def self.create_posts!
44
+ DB.create_table :posts do
45
+ primary_key :id
46
+
47
+ string :href
48
+ string :time
49
+ string :tag
50
+ string :description
51
+ text :extended
52
+ string :hash
53
+ string :meta
54
+
55
+ index :hash, {:unique=>true}
56
+ index [:description, :extended, :tag]
57
+ end
58
+ end
59
+
60
+ def self.sync(all=false)
61
+ all = true if @@posts.empty?
62
+
63
+ if all
64
+ print "* Retrieving all links..."
65
+ STDOUT.flush
66
+ posts = Delicious.posts_all
67
+ else
68
+ print "* Retrieving new links..."
69
+ STDOUT.flush
70
+ posts = Delicious.posts_since(most_recent_time)
71
+ end
72
+
73
+ $log.debug "sync: got #{posts.size} posts"
74
+
75
+ puts " (#{posts.size} links found)"
76
+
77
+ return if posts.empty?
78
+
79
+ print "* Inserting links into database..."
80
+
81
+ counter = 0
82
+ for post in posts
83
+ counter += 1
84
+ begin
85
+ add post
86
+ rescue Sequel::DatabaseError => e
87
+ $log.debug "error: #{e} adding #{post.inspect}"
88
+ end
89
+
90
+ if counter % 37 == 0
91
+ print "."
92
+ STDOUT.flush
93
+ end
94
+ end
95
+
96
+ puts "done!"
97
+ end
98
+
99
+ def self.most_recent_time
100
+ @@posts.order(:time.desc).limit(1).first[:time]
101
+ end
102
+
103
+ def self.add(params)
104
+ @@posts << params
105
+ end
106
+
107
+ def self.find(query)
108
+ query = "%#{query}%" if query.is_a? String
109
+ @@posts.filter(:extended.like(query) | :description.like(query) | :tag.like(query)).order(:time)
110
+ end
111
+
112
+
113
+ end
@@ -0,0 +1,51 @@
1
+ require 'date'
2
+ require 'colorize'
3
+
4
+ #################################################################
5
+ ## Load the colorize gem, and define the "hilite" function
6
+ begin
7
+ require 'rubygems'
8
+ require 'colorize'
9
+ # Colourized hilite...
10
+ class String
11
+ def hilite(query, color=:white)
12
+ query = Regexp.new( Regexp.escape(query), Regexp::IGNORECASE )
13
+ self.to_s.send(color).gsub(/(.*)(#{query})(.*)/) { $1.send(color) + $2.black.on_yellow + $3.send(color)}
14
+ end
15
+ end
16
+ rescue LoadError
17
+ STDERR.puts "Note: You should install the 'colorize' gem for extra prettiness.\n"
18
+ # Monochrome hilite does nothing...
19
+ class String
20
+ def hilite(query); self; end
21
+ end
22
+ end
23
+
24
+ #################################################################
25
+
26
+ def formatdate(date, width=11)
27
+ dt = DateTime.parse(date)
28
+ time = "%l:%M%P"
29
+ date = "%d %b %g"
30
+ #dt.strftime("#{date} #{time}")
31
+ result = dt.strftime(date)
32
+
33
+ result.ljust(width).light_yellow
34
+ end
35
+
36
+
37
+ def display(post, query, indent_size=11)
38
+ indent = " " * indent_size
39
+
40
+ date = formatdate(post[:time], indent_size)
41
+ desc = post[:description].hilite(query, :light_white)
42
+ ext = post[:extended].hilite(query, :white)
43
+ url = post[:href].hilite(query, :light_blue)
44
+ tags = post[:tag].hilite(query, :light_cyan)
45
+
46
+ puts date + desc
47
+ puts indent + ext if post[:extended].any?
48
+ puts indent + url
49
+ puts indent + "(".cyan + tags + ")".cyan
50
+ puts
51
+ end
@@ -0,0 +1,7 @@
1
+ require 'logger'
2
+
3
+ $log = Logger.new(STDOUT)
4
+ $log.level = Logger::INFO
5
+ #$log.level = Logger::DEBUG
6
+
7
+ $log.datetime_format = "%d %b %g @ %l:%M:%S. %L %P "
@@ -0,0 +1,60 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+
4
+ require 'delicious-cli/log'
5
+
6
+ #################################################################
7
+ HOMEDIR = File.expand_path("~")
8
+ CONFIGDIR = File.join(HOMEDIR, ".delicious")
9
+ #################################################################
10
+
11
+ #################################################################
12
+ class FileNotFound < IOError; end
13
+ #################################################################
14
+
15
+ #################################################################
16
+ def configfile(filename, check=false)
17
+ path = File.join(CONFIGDIR, filename)
18
+
19
+ if check and not File.exists? path
20
+ raise FileNotFound, path
21
+ end
22
+
23
+ path
24
+ end
25
+ #################################################################
26
+
27
+
28
+
29
+ #################################################################
30
+
31
+ unless File.exists? CONFIGDIR
32
+ $log.info "* Creating new config directory: #{CONFIGDIR}"
33
+ FileUtils.mkdir_p CONFIGDIR
34
+ end
35
+
36
+ class Settings
37
+
38
+ def self.settings
39
+ @settings ||= {}
40
+ end
41
+
42
+ def self.[](key)
43
+ settings[key]
44
+ end
45
+
46
+ def self.[]=(key, val)
47
+ settings[key] = val
48
+ end
49
+
50
+ def self.load!
51
+ @settings = YAML::load_file( configfile('settings.yml') )
52
+ end
53
+
54
+ def self.save!
55
+ open( configfile('settings.yml'), "w" ) do |f|
56
+ f.write YAML::dump(settings)
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #################################################################
4
+
5
+ require "delicious-cli/log"
6
+
7
+ libs = %w[
8
+ rubygems
9
+
10
+ optparse
11
+ optparse/time
12
+ ostruct
13
+
14
+ delicious-cli/db
15
+ delicious-cli/display
16
+ delicious-cli/api
17
+ delicious-cli/settings
18
+ ]
19
+
20
+ libs.each do |lib|
21
+ #$log.debug "loading #{lib}"
22
+ require lib
23
+ end
24
+
25
+ #################################################################
26
+
27
+ def search(query)
28
+ matches = Database.find(query)
29
+ matches.each { |match| display(match, query) }
30
+ end
31
+
32
+ def sync
33
+ puts "* Synchronizing database..."
34
+ Database.sync
35
+ end
36
+
37
+ def redownload
38
+ puts "* Redownloading database..."
39
+ Database.clear!
40
+ Database.sync
41
+ end
42
+
43
+ def config
44
+ # prompt user and stuff
45
+ puts "Delicious login info"
46
+ puts "---------------------------"
47
+ puts
48
+ print "Username: "
49
+ username = gets.strip
50
+ print "Password: "
51
+ password = gets.strip
52
+
53
+ puts
54
+ puts "User: #{username.inspect} | Pass: #{password.inspect}"
55
+ print "Is this correct? [y/N] "
56
+ answer = gets.strip
57
+ if answer =~ /^[Yy]$/
58
+ puts
59
+
60
+ puts "* Checking that login/password works..."
61
+ Delicious.basic_auth(username, password)
62
+ if Delicious.valid_auth?
63
+ puts " |_ Login successful! Saving config..."
64
+ Settings["username"] = username
65
+ Settings["password"] = password
66
+ Settings.save!
67
+ else
68
+ puts " |_ Login failed! (Please check your credentials or network.)"
69
+ end
70
+ puts
71
+ else
72
+ puts "Aborting..."
73
+ end
74
+ end
75
+
76
+
77
+ #################################################################
78
+
79
+ def main
80
+
81
+ # In it!
82
+
83
+ Database.init!
84
+ Settings.load!
85
+
86
+ Delicious.basic_auth(Settings["username"], Settings["password"])
87
+
88
+ # Parse de opts
89
+
90
+ ARGV.push("--help") if ARGV.empty?
91
+
92
+ options = OpenStruct.new
93
+ OptionParser.new do |opts|
94
+ opts.banner = "Usage: dels [options] <search query>"
95
+ opts.separator ""
96
+ opts.separator "Specific options:"
97
+
98
+ opts.on("-d", "--debug", "Debug info") do |opt|
99
+ options.debug = true
100
+ end
101
+
102
+ opts.on("-s", "--sync", "Synchronize links") do |opt|
103
+ options.sync = true
104
+ end
105
+
106
+ opts.on("-r", "--redownload", "Erase database and redownload all links") do |opt|
107
+ options.redownload = true
108
+ end
109
+
110
+ opts.on("-c", "--config", "Configure app (set delicious username/password)") do |opt|
111
+ options.config = true
112
+ end
113
+
114
+ opts.on("-t", "--test-auth", "Test that authentication info works") do |opt|
115
+ options.test_auth = true
116
+ end
117
+
118
+
119
+ opts.separator ""
120
+ opts.separator "Common options:"
121
+
122
+ # No argument, shows at tail. This will print an options summary.
123
+ # Try it and see!
124
+ opts.on_tail("-h", "--help", "Show this message") do
125
+ puts opts
126
+ puts
127
+ exit
128
+ end
129
+
130
+ end.parse!
131
+
132
+
133
+ # Handle options
134
+
135
+ $log.level = Logger::DEBUG if options.debug
136
+
137
+ if options.test_auth
138
+ puts "Logging in as '#{Settings["username"]}': #{Delicious.valid_auth? ? "success!" : "fail!"}"
139
+ elsif options.config
140
+ config
141
+ elsif options.redownload
142
+ redownload
143
+ elsif options.sync
144
+ sync
145
+ else
146
+ exit 1 unless query = ARGV[0]
147
+ query = ARGV[0]
148
+ search(query)
149
+ end
150
+
151
+ end
152
+
153
+
154
+ if $0 == __FILE__
155
+ main
156
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class DeliciousCliTest < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'delicious-cli'
8
+
9
+ class Test::Unit::TestCase
10
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delicious-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - epitron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-29 00:00:00 -04:00
13
+ default_executable: dels
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sequel
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: httparty
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: colorize
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ description: A commandline tool which lets you download all your delicious.com links into a local SQLite database and search them (with pretty color-coded results).
46
+ email: chris@ill-logic.com
47
+ executables:
48
+ - dels
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.rdoc
54
+ files:
55
+ - lib/delicious-cli.rb
56
+ - lib/delicious-cli/api.rb
57
+ - lib/delicious-cli/db.rb
58
+ - lib/delicious-cli/display.rb
59
+ - lib/delicious-cli/log.rb
60
+ - lib/delicious-cli/settings.rb
61
+ - LICENSE
62
+ - README.rdoc
63
+ has_rdoc: true
64
+ homepage: http://github.com/epitron/delicious-cli
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options:
69
+ - --charset=UTF-8
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ requirements: []
85
+
86
+ rubyforge_project:
87
+ rubygems_version: 1.3.3
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Delicious.com commandline interface
91
+ test_files:
92
+ - test/delicious-cli_test.rb
93
+ - test/test_helper.rb