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