rutt 0.3.2 → 0.3.3
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/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/rutt +31 -14
- data/lib/rutt.rb +9 -477
- data/lib/rutt/config.rb +23 -0
- data/lib/rutt/content_screen.rb +57 -0
- data/lib/rutt/feed.rb +74 -0
- data/lib/rutt/feed_screen.rb +93 -0
- data/lib/rutt/instapaper.rb +32 -0
- data/lib/rutt/item.rb +40 -0
- data/lib/rutt/item_screen.rb +103 -0
- data/lib/rutt/opml.rb +21 -0
- data/lib/rutt/screen.rb +51 -0
- data/rutt.gemspec +11 -3
- metadata +13 -5
- data/lib/instapaper.rb +0 -34
data/Rakefile
CHANGED
@@ -16,7 +16,7 @@ Jeweler::Tasks.new do |gem|
|
|
16
16
|
gem.homepage = "http://github.com/abhiyerra/rutt"
|
17
17
|
gem.license = "BSD"
|
18
18
|
gem.summary = %Q{The Mutt of RSS/Atom feeds.}
|
19
|
-
gem.description = %Q{
|
19
|
+
gem.description = %Q{The Mutt of RSS/Atom feeds.}
|
20
20
|
gem.email = "abhi@berkeley.edu"
|
21
21
|
gem.authors = ["Abhi Yerra"]
|
22
22
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.3
|
data/bin/rutt
CHANGED
@@ -13,46 +13,63 @@ require 'sqlite3'
|
|
13
13
|
require 'feedparser'
|
14
14
|
require 'parallel'
|
15
15
|
require 'instapaper'
|
16
|
+
require 'oauth'
|
16
17
|
|
17
18
|
require 'rutt'
|
18
|
-
require 'instapaper'
|
19
19
|
|
20
20
|
$db = SQLite3::Database.new('rutt.db')
|
21
21
|
$db.results_as_hash = true
|
22
22
|
|
23
|
+
def start_screen
|
24
|
+
stdscr = Ncurses.initscr()
|
25
|
+
|
26
|
+
Ncurses.start_color();
|
27
|
+
Ncurses.cbreak();
|
28
|
+
Ncurses.noecho();
|
29
|
+
Ncurses.keypad(stdscr, true);
|
30
|
+
|
31
|
+
stdscr.clear
|
32
|
+
|
33
|
+
feed_screen = Rutt::FeedScreen.new(stdscr)
|
34
|
+
feed_screen.loop
|
35
|
+
ensure
|
36
|
+
Ncurses.endwin()
|
37
|
+
end
|
38
|
+
|
39
|
+
|
23
40
|
def main
|
24
41
|
|
25
|
-
Config
|
26
|
-
Feed
|
27
|
-
Item
|
42
|
+
Rutt::Config.make_table!
|
43
|
+
Rutt::Feed.make_table!
|
44
|
+
Rutt::Item.make_table!
|
28
45
|
|
29
46
|
options = {}
|
30
47
|
OptionParser.new do |opts|
|
31
48
|
opts.banner = "Usage: rutt.rb [options]"
|
32
49
|
|
33
50
|
opts.on('-a', '--add FEED', "Add a feed") do |url|
|
34
|
-
Feed::add(url)
|
51
|
+
Rutt::Feed::add(url)
|
35
52
|
exit
|
36
53
|
end
|
37
54
|
|
38
55
|
opts.on('-r', '--refresh', "Refresh feeds.") do
|
39
|
-
Feed::refresh
|
56
|
+
Rutt::Feed::refresh
|
40
57
|
exit
|
41
58
|
end
|
42
59
|
|
43
60
|
opts.on('-o', '--import-opml FILE', "Import opml") do |file|
|
44
|
-
urls = Opml::get_urls(file)
|
61
|
+
urls = Rutt::Opml::get_urls(file)
|
45
62
|
|
46
63
|
urls.each do |url|
|
47
64
|
puts "Adding #{url}"
|
48
|
-
Feed::add(url, false)
|
65
|
+
Rutt::Feed::add(url, false)
|
49
66
|
end
|
50
67
|
|
51
68
|
exit
|
52
69
|
end
|
53
70
|
|
54
71
|
opts.on('-s', '--set-key', "Set config") do |key, value|
|
55
|
-
Config.set(ARGV[0], ARGV[1])
|
72
|
+
Rutt::Config.set(ARGV[0], ARGV[1])
|
56
73
|
exit
|
57
74
|
end
|
58
75
|
|
@@ -60,12 +77,12 @@ def main
|
|
60
77
|
|
61
78
|
end.parse!
|
62
79
|
|
63
|
-
consumer_key = Config.get("instapaper.consumer-key")
|
64
|
-
consumer_secret = Config.get("instapaper.consumer-secret")
|
65
|
-
username = Config.get("instapaper.username")
|
66
|
-
password = Config.get("instapaper.password")
|
80
|
+
consumer_key = Rutt::Config.get("instapaper.consumer-key")
|
81
|
+
consumer_secret = Rutt::Config.get("instapaper.consumer-secret")
|
82
|
+
username = Rutt::Config.get("instapaper.username")
|
83
|
+
password = Rutt::Config.get("instapaper.password")
|
67
84
|
|
68
|
-
$instapaper = Instapaper::API.new(consumer_key, consumer_secret)
|
85
|
+
$instapaper = Rutt::Instapaper::API.new(consumer_key, consumer_secret)
|
69
86
|
$instapaper.authorize(username, password)
|
70
87
|
|
71
88
|
start_screen
|
data/lib/rutt.rb
CHANGED
@@ -1,477 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
UNIQUE(key))
|
11
|
-
})
|
12
|
-
end
|
13
|
-
|
14
|
-
def get(key)
|
15
|
-
$db.get_first_value("select value from config where key = ?", key)
|
16
|
-
end
|
17
|
-
|
18
|
-
def set(key, value)
|
19
|
-
$db.execute("insert or replace into config(key, value) values (?, ?)", key, value)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
module Opml
|
24
|
-
def self.get_urls(file)
|
25
|
-
doc = Nokogiri::XML(open(file))
|
26
|
-
|
27
|
-
urls = []
|
28
|
-
|
29
|
-
doc.xpath('opml/body/outline').each do |outline|
|
30
|
-
if outline['xmlUrl']
|
31
|
-
urls << outline['xmlUrl']
|
32
|
-
else
|
33
|
-
(outline/'outline').each do |outline2|
|
34
|
-
urls << outline2['xmlUrl']
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
return urls
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
module Feed
|
44
|
-
extend self
|
45
|
-
|
46
|
-
def make_table!
|
47
|
-
$db.execute(%{
|
48
|
-
create table if not exists feeds (
|
49
|
-
id integer PRIMARY KEY,
|
50
|
-
title text,
|
51
|
-
url text,
|
52
|
-
update_interval integer default 3600,
|
53
|
-
created_at NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
54
|
-
updated_at NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
55
|
-
UNIQUE(url))
|
56
|
-
})
|
57
|
-
end
|
58
|
-
|
59
|
-
def add(url, refresh=true)
|
60
|
-
$db.execute("insert or ignore into feeds (url) values ('#{url}')")
|
61
|
-
$db.execute("select * from feeds where id = (select last_insert_rowid())") do |feed|
|
62
|
-
refresh_for(feed)
|
63
|
-
end if refresh
|
64
|
-
end
|
65
|
-
|
66
|
-
def get(id)
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
def delete(feed)
|
71
|
-
$db.execute("delete from items where feed_id = ?", feed['id'])
|
72
|
-
$db.execute("delete from feeds where id = ?", feed['id'])
|
73
|
-
end
|
74
|
-
|
75
|
-
def all(min_limit=0, max_limit=-1)
|
76
|
-
$db.execute(%{
|
77
|
-
select f.*,
|
78
|
-
(select count(*) from items iu where iu.feed_id = f.id) as num_items,
|
79
|
-
(select count(*) from items ir where ir.read = 0 and ir.feed_id = f.id) as unread
|
80
|
-
from feeds f where unread > 0 order by id desc limit #{min_limit},#{max_limit}
|
81
|
-
})
|
82
|
-
end
|
83
|
-
|
84
|
-
def refresh
|
85
|
-
feeds = $db.execute("select * from feeds")
|
86
|
-
results = Parallel.map(feeds, :in_threads => 8) do |feed|
|
87
|
-
refresh_for(feed)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def refresh_for(feed)
|
92
|
-
content = open(feed['url']).read
|
93
|
-
rss = FeedParser::Feed::new(content)
|
94
|
-
|
95
|
-
puts rss.title
|
96
|
-
|
97
|
-
$db.execute("update feeds set title = ? where id = ?", rss.title, feed['id'])
|
98
|
-
|
99
|
-
rss.items.each do |item|
|
100
|
-
$db.execute("insert or ignore into items (feed_id, title, url, published_at) values (?, ?, ?, ?)", feed['id'], item.title, item.link, item.date.to_i)
|
101
|
-
end
|
102
|
-
rescue Exception => e
|
103
|
-
# no-op
|
104
|
-
end
|
105
|
-
|
106
|
-
def unread(feed_id)
|
107
|
-
$db.execute("select * from items where feed_id = ? and read = 0", feed)
|
108
|
-
end
|
109
|
-
|
110
|
-
def mark_as_read(feed)
|
111
|
-
$db.execute("update items set read = 1 where feed_id = ?", feed['id'])
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
|
116
|
-
module Item
|
117
|
-
|
118
|
-
extend self
|
119
|
-
|
120
|
-
def make_table!
|
121
|
-
$db.execute(%{
|
122
|
-
create table if not exists items (
|
123
|
-
id integer PRIMARY KEY,
|
124
|
-
feed_id integer,
|
125
|
-
title text,
|
126
|
-
url text,
|
127
|
-
description text,
|
128
|
-
read int default 0,
|
129
|
-
prioritize int default 0,
|
130
|
-
published_at DATE NOT NULL DEFAULT (datetime('now','localtime')),
|
131
|
-
created_at NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
132
|
-
updated_at NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
133
|
-
UNIQUE(url),
|
134
|
-
FOREIGN KEY(feed_id) REFERENCES feeds(id))
|
135
|
-
})
|
136
|
-
end
|
137
|
-
|
138
|
-
def all(feed, min_limit=0, max_limit=-1)
|
139
|
-
$db.execute("select * from items where feed_id = ? order by published_at desc limit #{min_limit},#{max_limit}", feed['id'])
|
140
|
-
end
|
141
|
-
|
142
|
-
def mark_as_unread(item)
|
143
|
-
$db.execute("update items set read = 0 where id = #{item['id']}")
|
144
|
-
end
|
145
|
-
|
146
|
-
def mark_as_read(item)
|
147
|
-
$db.execute("update items set read = 1 where id = #{item['id']}")
|
148
|
-
end
|
149
|
-
|
150
|
-
def sent_to_instapaper(item)
|
151
|
-
$db.execute("update items set read = 2 where id = #{item['id']}")
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
class Screen
|
156
|
-
def initialize(stdscr)
|
157
|
-
@stdscr = stdscr
|
158
|
-
|
159
|
-
@min_y = 1
|
160
|
-
@max_y = @stdscr.getmaxy
|
161
|
-
|
162
|
-
@min_limit = @min_y - 1
|
163
|
-
@max_limit = @max_y - 5
|
164
|
-
|
165
|
-
@cur_y = 1
|
166
|
-
@cur_x = 0
|
167
|
-
end
|
168
|
-
|
169
|
-
def incr_page
|
170
|
-
@min_limit = @max_limit
|
171
|
-
@max_limit += (@max_y - 5)
|
172
|
-
end
|
173
|
-
|
174
|
-
def decr_page
|
175
|
-
@max_limit = @min_limit
|
176
|
-
@min_limit -= (@max_y - 5)
|
177
|
-
|
178
|
-
if @max_limit <= 0
|
179
|
-
@min_limit = @min_y - 1
|
180
|
-
@max_limit = @max_y - 5
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def display_menu
|
185
|
-
@stdscr.clear
|
186
|
-
@stdscr.move(0, 0)
|
187
|
-
@stdscr.addstr(" rutt #{@menu}\n")
|
188
|
-
end
|
189
|
-
|
190
|
-
def move_pointer(pos, move_to=false)
|
191
|
-
@stdscr.move(@cur_y, 0)
|
192
|
-
@stdscr.addstr(" ")
|
193
|
-
|
194
|
-
if move_to == true
|
195
|
-
@cur_y = pos
|
196
|
-
else
|
197
|
-
@cur_y += pos
|
198
|
-
end
|
199
|
-
|
200
|
-
@stdscr.move(@cur_y, 0)
|
201
|
-
@stdscr.addstr(">")
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
|
206
|
-
class FeedScreen < Screen
|
207
|
-
def initialize(stdscr)
|
208
|
-
@menu = "q:Quit d:delete r:refresh all"
|
209
|
-
|
210
|
-
super(stdscr)
|
211
|
-
end
|
212
|
-
|
213
|
-
def display_feeds
|
214
|
-
@cur_y = @min_y
|
215
|
-
|
216
|
-
@feeds = Feed::all(@min_limit, @max_limit)
|
217
|
-
@feeds.each do |feed|
|
218
|
-
# next if feed['unread'] == 0 # This should be configurable: feed.showread
|
219
|
-
|
220
|
-
@stdscr.move(@cur_y, 0)
|
221
|
-
@stdscr.addstr(" #{feed['unread']}/#{feed['num_items']}\t\t#{feed['title']}\n")
|
222
|
-
|
223
|
-
@cur_y += 1
|
224
|
-
end
|
225
|
-
|
226
|
-
@cur_y = @min_y
|
227
|
-
@stdscr.refresh
|
228
|
-
end
|
229
|
-
|
230
|
-
def window
|
231
|
-
@stdscr.clear
|
232
|
-
|
233
|
-
display_menu
|
234
|
-
display_feeds
|
235
|
-
move_pointer(0)
|
236
|
-
end
|
237
|
-
|
238
|
-
def loop
|
239
|
-
window
|
240
|
-
|
241
|
-
while true do
|
242
|
-
c = @stdscr.getch
|
243
|
-
|
244
|
-
if c > 0 && c < 255
|
245
|
-
case c.chr
|
246
|
-
when /q/i
|
247
|
-
break
|
248
|
-
when /a/i
|
249
|
-
# no-op
|
250
|
-
when /r/i
|
251
|
-
cur_y = @cur_y
|
252
|
-
|
253
|
-
Feed::refresh
|
254
|
-
|
255
|
-
window
|
256
|
-
move_pointer(cur_y, move_to=true)
|
257
|
-
when /d/i
|
258
|
-
cur_y = @cur_y - 1
|
259
|
-
|
260
|
-
@stdscr.clear
|
261
|
-
display_menu
|
262
|
-
feed = @feeds[cur_y]
|
263
|
-
@stdscr.move(2, 0)
|
264
|
-
@stdscr.addstr("Are you sure you want to delete #{feed['title']}? ")
|
265
|
-
d = @stdscr.getch
|
266
|
-
if d.chr =~ /y/i
|
267
|
-
Feed::delete(feed)
|
268
|
-
end
|
269
|
-
window
|
270
|
-
move_pointer(cur_y, move_to=true)
|
271
|
-
when /p/i
|
272
|
-
decr_page
|
273
|
-
window
|
274
|
-
when /n/i
|
275
|
-
incr_page
|
276
|
-
window
|
277
|
-
when / /
|
278
|
-
cur_y = @cur_y
|
279
|
-
@stdscr.addstr("#{@feeds[cur_y]}")
|
280
|
-
item_screen = ItemScreen.new(@stdscr, @feeds[cur_y - 1])
|
281
|
-
item_screen.loop
|
282
|
-
|
283
|
-
window
|
284
|
-
move_pointer(cur_y, move_to=true)
|
285
|
-
end
|
286
|
-
else
|
287
|
-
case c
|
288
|
-
when Ncurses::KEY_UP
|
289
|
-
move_pointer(-1)
|
290
|
-
when Ncurses::KEY_DOWN
|
291
|
-
move_pointer(1)
|
292
|
-
end
|
293
|
-
end
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
class ItemScreen < Screen
|
299
|
-
def initialize(stdscr, feed)
|
300
|
-
@feed = feed
|
301
|
-
@menu = " i:quit r:refresh m:mark as read u:mark as unread a:mark all as read b:open in browser"
|
302
|
-
|
303
|
-
super(stdscr)
|
304
|
-
end
|
305
|
-
|
306
|
-
def display_items
|
307
|
-
@cur_y = @min_y
|
308
|
-
|
309
|
-
@items = Item::all(@feed, @min_limit, @max_limit)
|
310
|
-
@items.each do |item|
|
311
|
-
item_status = case item['read'].to_i
|
312
|
-
when 0 then 'N'
|
313
|
-
when 1 then ' '
|
314
|
-
when 2 then 'I'
|
315
|
-
else ' '
|
316
|
-
end
|
317
|
-
@stdscr.addstr(" #{item_status}\t#{Time.at(item['published_at']).strftime("%b %d, %Y %R:%M")}\t#{item['title']}\n")
|
318
|
-
@cur_y += 1
|
319
|
-
end
|
320
|
-
|
321
|
-
@cur_y = @min_y
|
322
|
-
@stdscr.refresh
|
323
|
-
end
|
324
|
-
|
325
|
-
def window
|
326
|
-
@stdscr.clear
|
327
|
-
|
328
|
-
display_menu
|
329
|
-
display_items
|
330
|
-
move_pointer(0)
|
331
|
-
end
|
332
|
-
|
333
|
-
def loop
|
334
|
-
window
|
335
|
-
|
336
|
-
while true do
|
337
|
-
c = @stdscr.getch
|
338
|
-
|
339
|
-
if c > 0 && c < 255
|
340
|
-
case c.chr
|
341
|
-
when /[iq]/i
|
342
|
-
break
|
343
|
-
when /s/i
|
344
|
-
cur_y = @cur_y - 1
|
345
|
-
$instapaper.request('/api/1/bookmarks/add', {
|
346
|
-
'url' => @items[cur_y]['url'],
|
347
|
-
'title' => @items[cur_y]['title'],
|
348
|
-
})
|
349
|
-
Item::sent_to_instapaper(@items[cur_y])
|
350
|
-
window
|
351
|
-
move_pointer(@cur_y, move_to=true)
|
352
|
-
when /a/i
|
353
|
-
Feed::mark_as_read(@feed)
|
354
|
-
window
|
355
|
-
move_pointer(@cur_y, move_to=true)
|
356
|
-
when /p/i
|
357
|
-
decr_page
|
358
|
-
window
|
359
|
-
when /n/i
|
360
|
-
incr_page
|
361
|
-
window
|
362
|
-
when /b/i
|
363
|
-
cur_y = @cur_y - 1
|
364
|
-
Item::mark_as_read(@items[cur_y])
|
365
|
-
Launchy.open(@items[cur_y]['url'])
|
366
|
-
window
|
367
|
-
move_pointer(@cur_y, move_to=true)
|
368
|
-
when /m/i
|
369
|
-
cur_y = @cur_y - 1
|
370
|
-
Item::mark_as_read(@items[cur_y])
|
371
|
-
window
|
372
|
-
move_pointer(cur_y + 1, move_to=true)
|
373
|
-
when /u/i
|
374
|
-
cur_y = @cur_y - 1
|
375
|
-
Item::mark_as_unread(@items[cur_y])
|
376
|
-
window
|
377
|
-
move_pointer(cur_y + 1, move_to=true)
|
378
|
-
when /r/i
|
379
|
-
Feed::refresh_for(@feed)
|
380
|
-
window
|
381
|
-
when / /
|
382
|
-
content_screen = ContentScreen.new(@stdscr, @items[@cur_y - 1])
|
383
|
-
content_screen.loop
|
384
|
-
|
385
|
-
window
|
386
|
-
move_pointer(@cur_y, move_to=true)
|
387
|
-
end
|
388
|
-
else
|
389
|
-
case c
|
390
|
-
when Ncurses::KEY_UP
|
391
|
-
move_pointer(-1)
|
392
|
-
when Ncurses::KEY_DOWN
|
393
|
-
move_pointer(1)
|
394
|
-
end
|
395
|
-
end
|
396
|
-
end
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
class ContentScreen < Screen
|
405
|
-
def initialize(stdscr, item)
|
406
|
-
@item = item
|
407
|
-
@menu = "i:back b:open in browser"
|
408
|
-
|
409
|
-
super(stdscr)
|
410
|
-
end
|
411
|
-
|
412
|
-
def display_content
|
413
|
-
@content = `elinks -dump -dump-charset ascii -force-html #{@item['url']}`
|
414
|
-
@content = @content.split("\n")
|
415
|
-
|
416
|
-
@stdscr.addstr(" #{@item['title']} (#{@item['url']})\n\n")
|
417
|
-
|
418
|
-
lines = @content[@min_limit..@max_limit]
|
419
|
-
lines.each { |line| @stdscr.addstr(" #{line}\n") } if lines
|
420
|
-
|
421
|
-
@stdscr.refresh
|
422
|
-
end
|
423
|
-
|
424
|
-
def window
|
425
|
-
@stdscr.clear
|
426
|
-
display_menu
|
427
|
-
display_content
|
428
|
-
end
|
429
|
-
|
430
|
-
def loop
|
431
|
-
@cur_line = 0
|
432
|
-
|
433
|
-
window
|
434
|
-
|
435
|
-
while true do
|
436
|
-
c = @stdscr.getch
|
437
|
-
if c > 0 && c < 255
|
438
|
-
case c.chr
|
439
|
-
when /[iq]/i
|
440
|
-
Item::mark_as_read(@item)
|
441
|
-
break
|
442
|
-
when /b/i
|
443
|
-
Launchy.open(@item['url'])
|
444
|
-
end
|
445
|
-
else
|
446
|
-
case c
|
447
|
-
when Ncurses::KEY_UP
|
448
|
-
decr_page
|
449
|
-
window
|
450
|
-
when Ncurses::KEY_DOWN
|
451
|
-
incr_page
|
452
|
-
window
|
453
|
-
end
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
def start_screen
|
463
|
-
stdscr = Ncurses.initscr()
|
464
|
-
|
465
|
-
Ncurses.start_color();
|
466
|
-
Ncurses.cbreak();
|
467
|
-
Ncurses.noecho();
|
468
|
-
Ncurses.keypad(stdscr, true);
|
469
|
-
|
470
|
-
stdscr.clear
|
471
|
-
|
472
|
-
feed_screen = FeedScreen.new(stdscr)
|
473
|
-
feed_screen.loop
|
474
|
-
ensure
|
475
|
-
Ncurses.endwin()
|
476
|
-
end
|
477
|
-
|
1
|
+
require 'rutt/config'
|
2
|
+
require 'rutt/opml'
|
3
|
+
require 'rutt/instapaper'
|
4
|
+
require 'rutt/feed'
|
5
|
+
require 'rutt/item'
|
6
|
+
require 'rutt/screen'
|
7
|
+
require 'rutt/feed_screen'
|
8
|
+
require 'rutt/item_screen'
|
9
|
+
require 'rutt/content_screen'
|
data/lib/rutt/config.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rutt
|
2
|
+
module Config
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def make_table!
|
6
|
+
$db.execute(%{
|
7
|
+
create table if not exists config (
|
8
|
+
id integer PRIMARY KEY,
|
9
|
+
key text,
|
10
|
+
value text,
|
11
|
+
UNIQUE(key))
|
12
|
+
})
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(key)
|
16
|
+
$db.get_first_value("select value from config where key = ?", key)
|
17
|
+
end
|
18
|
+
|
19
|
+
def set(key, value)
|
20
|
+
$db.execute("insert or replace into config(key, value) values (?, ?)", key, value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Rutt
|
2
|
+
class ContentScreen < Screen
|
3
|
+
def initialize(stdscr, item)
|
4
|
+
@item = item
|
5
|
+
@menu = "i:back b:open in browser"
|
6
|
+
|
7
|
+
super(stdscr)
|
8
|
+
end
|
9
|
+
|
10
|
+
def display_content
|
11
|
+
@content = `elinks -dump -dump-charset ascii -force-html #{@item['url']}`
|
12
|
+
@content = @content.split("\n")
|
13
|
+
|
14
|
+
@stdscr.addstr(" #{@item['title']} (#{@item['url']})\n\n")
|
15
|
+
|
16
|
+
lines = @content[@min_limit..@max_limit]
|
17
|
+
lines.each { |line| @stdscr.addstr(" #{line}\n") } if lines
|
18
|
+
|
19
|
+
@stdscr.refresh
|
20
|
+
end
|
21
|
+
|
22
|
+
def window
|
23
|
+
@stdscr.clear
|
24
|
+
display_menu
|
25
|
+
display_content
|
26
|
+
end
|
27
|
+
|
28
|
+
def loop
|
29
|
+
@cur_line = 0
|
30
|
+
|
31
|
+
window
|
32
|
+
|
33
|
+
while true do
|
34
|
+
c = @stdscr.getch
|
35
|
+
if c > 0 && c < 255
|
36
|
+
case c.chr
|
37
|
+
when /[iq]/i
|
38
|
+
Item::mark_as_read(@item)
|
39
|
+
break
|
40
|
+
when /b/i
|
41
|
+
Launchy.open(@item['url'])
|
42
|
+
end
|
43
|
+
else
|
44
|
+
case c
|
45
|
+
when Ncurses::KEY_UP
|
46
|
+
decr_page
|
47
|
+
window
|
48
|
+
when Ncurses::KEY_DOWN
|
49
|
+
incr_page
|
50
|
+
window
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/rutt/feed.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
module Rutt
|
2
|
+
|
3
|
+
module Feed
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def make_table!
|
7
|
+
$db.execute(%{
|
8
|
+
create table if not exists feeds (
|
9
|
+
id integer PRIMARY KEY,
|
10
|
+
title text,
|
11
|
+
url text,
|
12
|
+
update_interval integer default 3600,
|
13
|
+
created_at NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
14
|
+
updated_at NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
15
|
+
UNIQUE(url))
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(url, refresh=true)
|
20
|
+
$db.execute("insert or ignore into feeds (url) values ('#{url}')")
|
21
|
+
$db.execute("select * from feeds where id = (select last_insert_rowid())") do |feed|
|
22
|
+
refresh_for(feed)
|
23
|
+
end if refresh
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(id)
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(feed)
|
31
|
+
$db.execute("delete from items where feed_id = ?", feed['id'])
|
32
|
+
$db.execute("delete from feeds where id = ?", feed['id'])
|
33
|
+
end
|
34
|
+
|
35
|
+
def all(min_limit=0, max_limit=-1)
|
36
|
+
$db.execute(%{
|
37
|
+
select f.*,
|
38
|
+
(select count(*) from items iu where iu.feed_id = f.id) as num_items,
|
39
|
+
(select count(*) from items ir where ir.read = 0 and ir.feed_id = f.id) as unread
|
40
|
+
from feeds f where unread > 0 order by id desc limit #{min_limit},#{max_limit}
|
41
|
+
})
|
42
|
+
end
|
43
|
+
|
44
|
+
def refresh
|
45
|
+
feeds = $db.execute("select * from feeds")
|
46
|
+
results = Parallel.map(feeds, :in_threads => 8) do |feed|
|
47
|
+
refresh_for(feed)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def refresh_for(feed)
|
52
|
+
content = open(feed['url']).read
|
53
|
+
rss = FeedParser::Feed::new(content)
|
54
|
+
|
55
|
+
puts rss.title
|
56
|
+
|
57
|
+
$db.execute("update feeds set title = ? where id = ?", rss.title, feed['id'])
|
58
|
+
|
59
|
+
rss.items.each do |item|
|
60
|
+
$db.execute("insert or ignore into items (feed_id, title, url, published_at) values (?, ?, ?, ?)", feed['id'], item.title, item.link, item.date.to_i)
|
61
|
+
end
|
62
|
+
rescue Exception => e
|
63
|
+
# no-op
|
64
|
+
end
|
65
|
+
|
66
|
+
def unread(feed_id)
|
67
|
+
$db.execute("select * from items where feed_id = ? and read = 0", feed)
|
68
|
+
end
|
69
|
+
|
70
|
+
def mark_as_read(feed)
|
71
|
+
$db.execute("update items set read = 1 where feed_id = ?", feed['id'])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Rutt
|
2
|
+
class FeedScreen < Screen
|
3
|
+
def initialize(stdscr)
|
4
|
+
@menu = "q:Quit d:delete r:refresh all"
|
5
|
+
|
6
|
+
super(stdscr)
|
7
|
+
end
|
8
|
+
|
9
|
+
def display_feeds
|
10
|
+
@cur_y = @min_y
|
11
|
+
|
12
|
+
@feeds = Feed::all(@min_limit, @max_limit)
|
13
|
+
@feeds.each do |feed|
|
14
|
+
# next if feed['unread'] == 0 # This should be configurable: feed.showread
|
15
|
+
|
16
|
+
@stdscr.move(@cur_y, 0)
|
17
|
+
@stdscr.addstr(" #{feed['unread']}/#{feed['num_items']}\t\t#{feed['title']}\n")
|
18
|
+
|
19
|
+
@cur_y += 1
|
20
|
+
end
|
21
|
+
|
22
|
+
@cur_y = @min_y
|
23
|
+
@stdscr.refresh
|
24
|
+
end
|
25
|
+
|
26
|
+
def window
|
27
|
+
@stdscr.clear
|
28
|
+
|
29
|
+
display_menu
|
30
|
+
display_feeds
|
31
|
+
move_pointer(0)
|
32
|
+
end
|
33
|
+
|
34
|
+
def loop
|
35
|
+
window
|
36
|
+
|
37
|
+
while true do
|
38
|
+
c = @stdscr.getch
|
39
|
+
|
40
|
+
if c > 0 && c < 255
|
41
|
+
case c.chr
|
42
|
+
when /q/i
|
43
|
+
break
|
44
|
+
when /a/i
|
45
|
+
# no-op
|
46
|
+
when /r/i
|
47
|
+
cur_y = @cur_y
|
48
|
+
|
49
|
+
Feed::refresh
|
50
|
+
|
51
|
+
window
|
52
|
+
move_pointer(cur_y, move_to=true)
|
53
|
+
when /d/i
|
54
|
+
cur_y = @cur_y - 1
|
55
|
+
|
56
|
+
@stdscr.clear
|
57
|
+
display_menu
|
58
|
+
feed = @feeds[cur_y]
|
59
|
+
@stdscr.move(2, 0)
|
60
|
+
@stdscr.addstr("Are you sure you want to delete #{feed['title']}? ")
|
61
|
+
d = @stdscr.getch
|
62
|
+
if d.chr =~ /y/i
|
63
|
+
Feed::delete(feed)
|
64
|
+
end
|
65
|
+
window
|
66
|
+
move_pointer(cur_y, move_to=true)
|
67
|
+
when /p/i
|
68
|
+
decr_page
|
69
|
+
window
|
70
|
+
when /n/i
|
71
|
+
incr_page
|
72
|
+
window
|
73
|
+
when / /
|
74
|
+
cur_y = @cur_y
|
75
|
+
@stdscr.addstr("#{@feeds[cur_y]}")
|
76
|
+
item_screen = ItemScreen.new(@stdscr, @feeds[cur_y - 1])
|
77
|
+
item_screen.loop
|
78
|
+
|
79
|
+
window
|
80
|
+
move_pointer(cur_y, move_to=true)
|
81
|
+
end
|
82
|
+
else
|
83
|
+
case c
|
84
|
+
when Ncurses::KEY_UP
|
85
|
+
move_pointer(-1)
|
86
|
+
when Ncurses::KEY_DOWN
|
87
|
+
move_pointer(1)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Rutt
|
2
|
+
module Instapaper
|
3
|
+
class API
|
4
|
+
Url = "http://www.instapaper.com"
|
5
|
+
|
6
|
+
def initialize(consumer_key, consumer_secret)
|
7
|
+
@consumer_key = consumer_key
|
8
|
+
@consumer_secret = consumer_secret
|
9
|
+
end
|
10
|
+
|
11
|
+
def authorize(username, password)
|
12
|
+
@consumer = OAuth::Consumer.new(@consumer_key, @consumer_secret, {
|
13
|
+
:site => "https://www.instapaper.com",
|
14
|
+
:access_token_path => "/api/1/oauth/access_token",
|
15
|
+
:http_method => :post
|
16
|
+
})
|
17
|
+
|
18
|
+
access_token = @consumer.get_access_token(nil, {}, {
|
19
|
+
:x_auth_username => username,
|
20
|
+
:x_auth_password => password,
|
21
|
+
:x_auth_mode => "client_auth",
|
22
|
+
})
|
23
|
+
|
24
|
+
@access_token = OAuth::AccessToken.new(@consumer, access_token.token, access_token.secret)
|
25
|
+
end
|
26
|
+
|
27
|
+
def request(path, params={})
|
28
|
+
@access_token.request(:post, "#{Url}#{path}", params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/rutt/item.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rutt
|
2
|
+
module Item
|
3
|
+
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def make_table!
|
7
|
+
$db.execute(%{
|
8
|
+
create table if not exists items (
|
9
|
+
id integer PRIMARY KEY,
|
10
|
+
feed_id integer,
|
11
|
+
title text,
|
12
|
+
url text,
|
13
|
+
description text,
|
14
|
+
read int default 0,
|
15
|
+
prioritize int default 0,
|
16
|
+
published_at DATE NOT NULL DEFAULT (datetime('now','localtime')),
|
17
|
+
created_at NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
18
|
+
updated_at NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
19
|
+
UNIQUE(url),
|
20
|
+
FOREIGN KEY(feed_id) REFERENCES feeds(id))
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def all(feed, min_limit=0, max_limit=-1)
|
25
|
+
$db.execute("select * from items where feed_id = ? order by published_at desc limit #{min_limit},#{max_limit}", feed['id'])
|
26
|
+
end
|
27
|
+
|
28
|
+
def mark_as_unread(item)
|
29
|
+
$db.execute("update items set read = 0 where id = #{item['id']}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def mark_as_read(item)
|
33
|
+
$db.execute("update items set read = 1 where id = #{item['id']}")
|
34
|
+
end
|
35
|
+
|
36
|
+
def sent_to_instapaper(item)
|
37
|
+
$db.execute("update items set read = 2 where id = #{item['id']}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Rutt
|
2
|
+
class ItemScreen < Screen
|
3
|
+
def initialize(stdscr, feed)
|
4
|
+
@feed = feed
|
5
|
+
@menu = " i:quit r:refresh m:mark as read u:mark as unread a:mark all as read b:open in browser"
|
6
|
+
|
7
|
+
super(stdscr)
|
8
|
+
end
|
9
|
+
|
10
|
+
def display_items
|
11
|
+
@cur_y = @min_y
|
12
|
+
|
13
|
+
@items = Item::all(@feed, @min_limit, @max_limit)
|
14
|
+
@items.each do |item|
|
15
|
+
item_status = case item['read'].to_i
|
16
|
+
when 0 then 'N'
|
17
|
+
when 1 then ' '
|
18
|
+
when 2 then 'I'
|
19
|
+
else ' '
|
20
|
+
end
|
21
|
+
@stdscr.addstr(" #{item_status}\t#{Time.at(item['published_at']).strftime("%b %d, %Y %R:%M")}\t#{item['title']}\n")
|
22
|
+
@cur_y += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
@cur_y = @min_y
|
26
|
+
@stdscr.refresh
|
27
|
+
end
|
28
|
+
|
29
|
+
def window
|
30
|
+
@stdscr.clear
|
31
|
+
|
32
|
+
display_menu
|
33
|
+
display_items
|
34
|
+
move_pointer(0)
|
35
|
+
end
|
36
|
+
|
37
|
+
def loop
|
38
|
+
window
|
39
|
+
|
40
|
+
while true do
|
41
|
+
c = @stdscr.getch
|
42
|
+
|
43
|
+
if c > 0 && c < 255
|
44
|
+
case c.chr
|
45
|
+
when /[iq]/i
|
46
|
+
break
|
47
|
+
when /s/i
|
48
|
+
cur_y = @cur_y - 1
|
49
|
+
$instapaper.request('/api/1/bookmarks/add', {
|
50
|
+
'url' => @items[cur_y]['url'],
|
51
|
+
'title' => @items[cur_y]['title'],
|
52
|
+
})
|
53
|
+
Item::sent_to_instapaper(@items[cur_y])
|
54
|
+
window
|
55
|
+
move_pointer(@cur_y, move_to=true)
|
56
|
+
when /a/i
|
57
|
+
Feed::mark_as_read(@feed)
|
58
|
+
window
|
59
|
+
move_pointer(@cur_y, move_to=true)
|
60
|
+
when /p/i
|
61
|
+
decr_page
|
62
|
+
window
|
63
|
+
when /n/i
|
64
|
+
incr_page
|
65
|
+
window
|
66
|
+
when /b/i
|
67
|
+
cur_y = @cur_y - 1
|
68
|
+
Item::mark_as_read(@items[cur_y])
|
69
|
+
Launchy.open(@items[cur_y]['url'])
|
70
|
+
window
|
71
|
+
move_pointer(@cur_y, move_to=true)
|
72
|
+
when /m/i
|
73
|
+
cur_y = @cur_y - 1
|
74
|
+
Item::mark_as_read(@items[cur_y])
|
75
|
+
window
|
76
|
+
move_pointer(cur_y + 1, move_to=true)
|
77
|
+
when /u/i
|
78
|
+
cur_y = @cur_y - 1
|
79
|
+
Item::mark_as_unread(@items[cur_y])
|
80
|
+
window
|
81
|
+
move_pointer(cur_y + 1, move_to=true)
|
82
|
+
when /r/i
|
83
|
+
Feed::refresh_for(@feed)
|
84
|
+
window
|
85
|
+
when / /
|
86
|
+
content_screen = ContentScreen.new(@stdscr, @items[@cur_y - 1])
|
87
|
+
content_screen.loop
|
88
|
+
|
89
|
+
window
|
90
|
+
move_pointer(@cur_y, move_to=true)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
case c
|
94
|
+
when Ncurses::KEY_UP
|
95
|
+
move_pointer(-1)
|
96
|
+
when Ncurses::KEY_DOWN
|
97
|
+
move_pointer(1)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/rutt/opml.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rutt
|
2
|
+
module Opml
|
3
|
+
def self.get_urls(file)
|
4
|
+
doc = Nokogiri::XML(open(file))
|
5
|
+
|
6
|
+
urls = []
|
7
|
+
|
8
|
+
doc.xpath('opml/body/outline').each do |outline|
|
9
|
+
if outline['xmlUrl']
|
10
|
+
urls << outline['xmlUrl']
|
11
|
+
else
|
12
|
+
(outline/'outline').each do |outline2|
|
13
|
+
urls << outline2['xmlUrl']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
return urls
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/rutt/screen.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module Rutt
|
2
|
+
class Screen
|
3
|
+
def initialize(stdscr)
|
4
|
+
@stdscr = stdscr
|
5
|
+
|
6
|
+
@min_y = 1
|
7
|
+
@max_y = @stdscr.getmaxy
|
8
|
+
|
9
|
+
@min_limit = @min_y - 1
|
10
|
+
@max_limit = @max_y - 5
|
11
|
+
|
12
|
+
@cur_y = 1
|
13
|
+
@cur_x = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def incr_page
|
17
|
+
@min_limit = @max_limit
|
18
|
+
@max_limit += (@max_y - 5)
|
19
|
+
end
|
20
|
+
|
21
|
+
def decr_page
|
22
|
+
@max_limit = @min_limit
|
23
|
+
@min_limit -= (@max_y - 5)
|
24
|
+
|
25
|
+
if @max_limit <= 0
|
26
|
+
@min_limit = @min_y - 1
|
27
|
+
@max_limit = @max_y - 5
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def display_menu
|
32
|
+
@stdscr.clear
|
33
|
+
@stdscr.move(0, 0)
|
34
|
+
@stdscr.addstr(" rutt #{@menu}\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
def move_pointer(pos, move_to=false)
|
38
|
+
@stdscr.move(@cur_y, 0)
|
39
|
+
@stdscr.addstr(" ")
|
40
|
+
|
41
|
+
if move_to == true
|
42
|
+
@cur_y = pos
|
43
|
+
else
|
44
|
+
@cur_y += pos
|
45
|
+
end
|
46
|
+
|
47
|
+
@stdscr.move(@cur_y, 0)
|
48
|
+
@stdscr.addstr(">")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/rutt.gemspec
CHANGED
@@ -5,13 +5,13 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rutt}
|
8
|
-
s.version = "0.3.
|
8
|
+
s.version = "0.3.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Abhi Yerra"]
|
12
12
|
s.date = %q{2011-04-23}
|
13
13
|
s.default_executable = %q{rutt}
|
14
|
-
s.description = %q{
|
14
|
+
s.description = %q{The Mutt of RSS/Atom feeds.}
|
15
15
|
s.email = %q{abhi@berkeley.edu}
|
16
16
|
s.executables = ["rutt"]
|
17
17
|
s.extra_rdoc_files = [
|
@@ -28,8 +28,16 @@ Gem::Specification.new do |s|
|
|
28
28
|
"Rakefile",
|
29
29
|
"VERSION",
|
30
30
|
"bin/rutt",
|
31
|
-
"lib/instapaper.rb",
|
32
31
|
"lib/rutt.rb",
|
32
|
+
"lib/rutt/config.rb",
|
33
|
+
"lib/rutt/content_screen.rb",
|
34
|
+
"lib/rutt/feed.rb",
|
35
|
+
"lib/rutt/feed_screen.rb",
|
36
|
+
"lib/rutt/instapaper.rb",
|
37
|
+
"lib/rutt/item.rb",
|
38
|
+
"lib/rutt/item_screen.rb",
|
39
|
+
"lib/rutt/opml.rb",
|
40
|
+
"lib/rutt/screen.rb",
|
33
41
|
"rutt.gemspec",
|
34
42
|
"spec/rutt_spec.rb",
|
35
43
|
"spec/spec_helper.rb"
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rutt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 3
|
10
|
+
version: 0.3.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Abhi Yerra
|
@@ -178,7 +178,7 @@ dependencies:
|
|
178
178
|
name: rcov
|
179
179
|
version_requirements: *id011
|
180
180
|
prerelease: false
|
181
|
-
description:
|
181
|
+
description: The Mutt of RSS/Atom feeds.
|
182
182
|
email: abhi@berkeley.edu
|
183
183
|
executables:
|
184
184
|
- rutt
|
@@ -197,8 +197,16 @@ files:
|
|
197
197
|
- Rakefile
|
198
198
|
- VERSION
|
199
199
|
- bin/rutt
|
200
|
-
- lib/instapaper.rb
|
201
200
|
- lib/rutt.rb
|
201
|
+
- lib/rutt/config.rb
|
202
|
+
- lib/rutt/content_screen.rb
|
203
|
+
- lib/rutt/feed.rb
|
204
|
+
- lib/rutt/feed_screen.rb
|
205
|
+
- lib/rutt/instapaper.rb
|
206
|
+
- lib/rutt/item.rb
|
207
|
+
- lib/rutt/item_screen.rb
|
208
|
+
- lib/rutt/opml.rb
|
209
|
+
- lib/rutt/screen.rb
|
202
210
|
- rutt.gemspec
|
203
211
|
- spec/rutt_spec.rb
|
204
212
|
- spec/spec_helper.rb
|
data/lib/instapaper.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'rubygems'
|
3
|
-
require 'oauth'
|
4
|
-
|
5
|
-
module Instapaper
|
6
|
-
class API
|
7
|
-
Url = "http://www.instapaper.com"
|
8
|
-
|
9
|
-
def initialize(consumer_key, consumer_secret)
|
10
|
-
@consumer_key = consumer_key
|
11
|
-
@consumer_secret = consumer_secret
|
12
|
-
end
|
13
|
-
|
14
|
-
def authorize(username, password)
|
15
|
-
@consumer = OAuth::Consumer.new(@consumer_key, @consumer_secret, {
|
16
|
-
:site => "https://www.instapaper.com",
|
17
|
-
:access_token_path => "/api/1/oauth/access_token",
|
18
|
-
:http_method => :post
|
19
|
-
})
|
20
|
-
|
21
|
-
access_token = @consumer.get_access_token(nil, {}, {
|
22
|
-
:x_auth_username => username,
|
23
|
-
:x_auth_password => password,
|
24
|
-
:x_auth_mode => "client_auth",
|
25
|
-
})
|
26
|
-
|
27
|
-
@access_token = OAuth::AccessToken.new(@consumer, access_token.token, access_token.secret)
|
28
|
-
end
|
29
|
-
|
30
|
-
def request(path, params={})
|
31
|
-
@access_token.request(:post, "#{Url}#{path}", params)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|