delicious-cli 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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