feedcellar 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/NEWS.md +16 -0
- data/lib/feedcellar/command.rb +68 -38
- data/lib/feedcellar/groonga_database.rb +102 -8
- data/lib/feedcellar/gui.rb +27 -0
- data/lib/feedcellar/version.rb +1 -1
- data/lib/feedcellar.rb +1 -0
- data/test/fixtures/subscriptions.xml +1 -1
- data/test/test-command.rb +13 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90de9f0e20628fe9c0296ceb73a55e068124996b
|
4
|
+
data.tar.gz: 35318c5da10ecd18d85c37f0e00c2dcf10c907ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc398b74be7d927be4cdb120878ce6b556099d01ca675c18fd1e39c08ee61fe7ff7a278870b1af7a57db6210beda04e3c7265b94305d9e5df472e080847ce426
|
7
|
+
data.tar.gz: 3d92d30d502af29ccb90a1347ea7bf1463d26ad9635683cd8d0eb655733302c77dbc256862f03dcdc67bed8c7e2ef44f261935d9f053d3de7dcab9bd6d50ce33
|
data/NEWS.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# NEWS
|
2
2
|
|
3
|
+
## 0.3.0: 2013-06-23
|
4
|
+
|
5
|
+
Database schema improved release!
|
6
|
+
|
7
|
+
### Changes
|
8
|
+
|
9
|
+
* Improvements
|
10
|
+
* Added latest command
|
11
|
+
* Added "-v" option as version command
|
12
|
+
* Improved word search that select to groonga at a time
|
13
|
+
* Added a dump method as grndump
|
14
|
+
* Stopped empty word unless resource option for search command
|
15
|
+
* Changed Resources key to "xmlUrl"
|
16
|
+
* Supported for migration to 0.3.0 for existing database
|
17
|
+
* Use "reference" type for resource column of Feeds table
|
18
|
+
|
3
19
|
## 0.2.2: 2013-06-16
|
4
20
|
|
5
21
|
Improve search command release!
|
data/lib/feedcellar/command.rb
CHANGED
@@ -4,9 +4,12 @@ require "feedcellar/groonga_database"
|
|
4
4
|
require "feedcellar/opml"
|
5
5
|
require "feedcellar/feed"
|
6
6
|
require "feedcellar/resource"
|
7
|
+
require "feedcellar/gui"
|
7
8
|
|
8
9
|
module Feedcellar
|
9
10
|
class Command < Thor
|
11
|
+
map "-v" => :version
|
12
|
+
|
10
13
|
def initialize(*args)
|
11
14
|
super
|
12
15
|
@base_dir = File.join(File.expand_path("~"), ".feedcellar")
|
@@ -22,9 +25,10 @@ module Feedcellar
|
|
22
25
|
def register(url)
|
23
26
|
resource = Resource.parse(url)
|
24
27
|
return 1 unless resource
|
28
|
+
return 1 if resource["xmlUrl"].empty?
|
25
29
|
|
26
30
|
GroongaDatabase.new.open(@database_dir) do |database|
|
27
|
-
database.register(resource["
|
31
|
+
database.register(resource["xmlUrl"], resource)
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
@@ -39,7 +43,9 @@ module Feedcellar
|
|
39
43
|
def import(opml_xml)
|
40
44
|
GroongaDatabase.new.open(@database_dir) do |database|
|
41
45
|
Opml.parse(opml_xml).each do |resource|
|
42
|
-
|
46
|
+
next unless resource["xmlUrl"] # FIXME: better way
|
47
|
+
next if resource["xmlUrl"].empty?
|
48
|
+
database.register(resource["xmlUrl"], resource)
|
43
49
|
end
|
44
50
|
end
|
45
51
|
end
|
@@ -64,14 +70,14 @@ module Feedcellar
|
|
64
70
|
def collect
|
65
71
|
GroongaDatabase.new.open(@database_dir) do |database|
|
66
72
|
database.resources.each do |record|
|
67
|
-
feed_url = record
|
73
|
+
feed_url = record.xmlUrl
|
68
74
|
next unless feed_url
|
69
75
|
|
70
76
|
items = Feed.parse(feed_url)
|
71
77
|
next unless items
|
72
78
|
|
73
79
|
items.each do |item|
|
74
|
-
database.add(
|
80
|
+
database.add(record.xmlUrl,
|
75
81
|
item.title,
|
76
82
|
item.link,
|
77
83
|
item.description,
|
@@ -81,6 +87,23 @@ module Feedcellar
|
|
81
87
|
end
|
82
88
|
end
|
83
89
|
|
90
|
+
desc "latest", "Show latest feeds by resources."
|
91
|
+
def latest
|
92
|
+
GroongaDatabase.new.open(@database_dir) do |database|
|
93
|
+
feeds = database.feeds
|
94
|
+
# TODO: I want to use the groonga method for grouping.
|
95
|
+
feeds.group_by {|feed| feed.resource.xmlUrl }.each do |url, group|
|
96
|
+
latest_feed = group.sort_by {|feed| feed.date }.last
|
97
|
+
next unless latest_feed
|
98
|
+
|
99
|
+
title = latest_feed.title.gsub(/\n/, " ")
|
100
|
+
next unless title
|
101
|
+
date = latest_feed.date.strftime("%Y/%m/%d")
|
102
|
+
puts "#{date} #{title} - #{latest_feed.resource.title}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
84
107
|
desc "search WORD", "Search feeds from local database."
|
85
108
|
option :browser, :type => :boolean, :desc => "open *ALL* links in browser"
|
86
109
|
option :long, :type => :boolean, :aliases => "-l", :desc => "use a long listing format"
|
@@ -88,40 +111,62 @@ module Feedcellar
|
|
88
111
|
option :mtime, :type => :numeric, :desc => "feed's data was last modified n*24 hours ago."
|
89
112
|
option :resource, :type => :string, :desc => "search of partial match by feed's resource url"
|
90
113
|
def search(*words)
|
114
|
+
if words.empty? &&
|
115
|
+
(options["resource"].nil? || options["resource"].empty?)
|
116
|
+
$stderr.puts "WARNING: required one of word or resource option."
|
117
|
+
return 1
|
118
|
+
end
|
119
|
+
|
120
|
+
if options[:browser]
|
121
|
+
unless GUI.available?
|
122
|
+
$stderr.puts "WARNING: browser option required \"gtk2\"."
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
91
126
|
GroongaDatabase.new.open(@database_dir) do |database|
|
92
127
|
feeds = database.feeds
|
93
|
-
words.each do |word|
|
94
128
|
feeds = feeds.select do |feed|
|
95
|
-
|
96
|
-
|
97
|
-
|
129
|
+
expression = nil
|
130
|
+
words.each do |word|
|
131
|
+
sub_expression = (feed.title =~ word) |
|
132
|
+
(feed.description =~ word)
|
133
|
+
if expression.nil?
|
134
|
+
expression = sub_expression
|
135
|
+
else
|
136
|
+
expression &= sub_expression
|
137
|
+
end
|
138
|
+
end
|
98
139
|
|
99
|
-
|
100
|
-
|
101
|
-
feed.date >
|
140
|
+
if options[:mtime]
|
141
|
+
base_date = (Time.now - (options[:mtime] * 60 * 60 * 24))
|
142
|
+
mtime_expression = feed.date > base_date
|
143
|
+
if expression.nil?
|
144
|
+
expression = mtime_expression
|
145
|
+
else
|
146
|
+
expression &= mtime_expression
|
147
|
+
end
|
102
148
|
end
|
103
|
-
end
|
104
149
|
|
105
|
-
|
106
|
-
|
107
|
-
|
150
|
+
if options[:resource]
|
151
|
+
resource_expression = feed.resource =~ options[:resource]
|
152
|
+
if expression.nil?
|
153
|
+
expression = resource_expression
|
154
|
+
else
|
155
|
+
expression &= resource_expression
|
156
|
+
end
|
108
157
|
end
|
158
|
+
|
159
|
+
expression
|
109
160
|
end
|
110
161
|
|
111
162
|
order = options[:reverse] ? "descending" : "ascending"
|
112
163
|
sorted_feeds = feeds.sort([{:key => "date", :order => order}])
|
113
164
|
|
114
165
|
sorted_feeds.each do |feed|
|
115
|
-
resources = database.resources.select do |resource|
|
116
|
-
resource.xmlUrl == feed.resource
|
117
|
-
end
|
118
|
-
next unless resources
|
119
|
-
next unless resources.first # FIXME
|
120
|
-
|
121
166
|
title = feed.title.gsub(/\n/, " ")
|
122
167
|
if options[:long]
|
123
168
|
date = feed.date.strftime("%Y/%m/%d %H:%M")
|
124
|
-
resource =
|
169
|
+
resource = feed.resource.title
|
125
170
|
puts "#{date} #{title} - #{resource} / #{feed.link}"
|
126
171
|
else
|
127
172
|
date = feed.date.strftime("%Y/%m/%d")
|
@@ -129,25 +174,10 @@ module Feedcellar
|
|
129
174
|
end
|
130
175
|
|
131
176
|
if options[:browser]
|
132
|
-
|
177
|
+
GUI.show_uri(feed.link)
|
133
178
|
end
|
134
179
|
end
|
135
180
|
end
|
136
181
|
end
|
137
|
-
|
138
|
-
private
|
139
|
-
def browser_available?
|
140
|
-
if @browser.nil?
|
141
|
-
begin
|
142
|
-
require "gtk2"
|
143
|
-
rescue LoadError
|
144
|
-
$stderr.puts "WARNING: Sorry, browser option required \"gtk2\"."
|
145
|
-
@browser = false
|
146
|
-
else
|
147
|
-
@browser = true
|
148
|
-
end
|
149
|
-
end
|
150
|
-
@browser
|
151
|
-
end
|
152
182
|
end
|
153
183
|
end
|
@@ -11,7 +11,13 @@ module Feedcellar
|
|
11
11
|
path = File.join(base_path, "feedcellar.db")
|
12
12
|
if File.exist?(path)
|
13
13
|
@database = Groonga::Database.open(path)
|
14
|
-
|
14
|
+
begin
|
15
|
+
populate_schema
|
16
|
+
rescue Groonga::Schema::ColumnCreationWithDifferentOptions
|
17
|
+
# NOTE: migrate to feedcellar-0.3.0 from 0.2.2 or earlier.
|
18
|
+
populate_new_schema
|
19
|
+
transform_resources_key
|
20
|
+
end
|
15
21
|
else
|
16
22
|
FileUtils.mkdir_p(base_path)
|
17
23
|
populate(path)
|
@@ -26,13 +32,11 @@ module Feedcellar
|
|
26
32
|
end
|
27
33
|
|
28
34
|
def register(key, attributes)
|
29
|
-
|
30
|
-
feeds.add(key, attributes)
|
35
|
+
resources.add(key, attributes)
|
31
36
|
end
|
32
37
|
|
33
|
-
def add(
|
34
|
-
feeds
|
35
|
-
feeds.add(link, :resource => resource,
|
38
|
+
def add(resource_key, title, link, description, date)
|
39
|
+
feeds.add(link, :resource => resources[resource_key],
|
36
40
|
:title => title,
|
37
41
|
:link => link,
|
38
42
|
:description => description,
|
@@ -40,8 +44,7 @@ module Feedcellar
|
|
40
44
|
end
|
41
45
|
|
42
46
|
def unregister(title_or_url)
|
43
|
-
|
44
|
-
feeds.delete do |record|
|
47
|
+
resources.delete do |record|
|
45
48
|
(record.title == title_or_url) |
|
46
49
|
(record.xmlUrl == title_or_url)
|
47
50
|
end
|
@@ -64,6 +67,10 @@ module Feedcellar
|
|
64
67
|
@feeds ||= Groonga["Feeds"]
|
65
68
|
end
|
66
69
|
|
70
|
+
def dump
|
71
|
+
Groonga::DatabaseDumper.dump
|
72
|
+
end
|
73
|
+
|
67
74
|
private
|
68
75
|
def reset_context(encoding)
|
69
76
|
Groonga::Context.default_options = {:encoding => encoding}
|
@@ -76,6 +83,59 @@ module Feedcellar
|
|
76
83
|
end
|
77
84
|
|
78
85
|
def populate_schema
|
86
|
+
Groonga::Schema.define do |schema|
|
87
|
+
schema.create_table("Resources", :type => :hash) do |table|
|
88
|
+
table.text("text")
|
89
|
+
table.short_text("isComment")
|
90
|
+
table.short_text("isBreakpoint")
|
91
|
+
table.short_text("created")
|
92
|
+
table.short_text("category")
|
93
|
+
table.text("description")
|
94
|
+
table.short_text("url")
|
95
|
+
table.short_text("htmlUrl")
|
96
|
+
table.short_text("xmlUrl")
|
97
|
+
table.short_text("title")
|
98
|
+
table.short_text("version")
|
99
|
+
table.short_text("language")
|
100
|
+
end
|
101
|
+
|
102
|
+
schema.create_table("Feeds", :type => :hash) do |table|
|
103
|
+
table.reference("resource", "Resources")
|
104
|
+
table.short_text("title")
|
105
|
+
table.short_text("link")
|
106
|
+
table.text("description")
|
107
|
+
table.time("date")
|
108
|
+
end
|
109
|
+
|
110
|
+
schema.create_table("Terms",
|
111
|
+
:type => :patricia_trie,
|
112
|
+
:key_normalize => true,
|
113
|
+
:default_tokenizer => "TokenBigram") do |table|
|
114
|
+
table.index("Feeds.title")
|
115
|
+
table.index("Feeds.description")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def populate_new_schema
|
121
|
+
populate_old_schema
|
122
|
+
add_new_column
|
123
|
+
migrate_value
|
124
|
+
delete_old_column
|
125
|
+
end
|
126
|
+
|
127
|
+
def transform_resources_key
|
128
|
+
resources.each do |resource|
|
129
|
+
next unless resource.xmlUrl
|
130
|
+
values = resource.attributes.reject do |key, value|
|
131
|
+
/^_/ =~ key
|
132
|
+
end
|
133
|
+
resources.add(resource.xmlUrl, values)
|
134
|
+
resources.delete(resource.id)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def populate_old_schema
|
79
139
|
Groonga::Schema.define do |schema|
|
80
140
|
schema.create_table("Resources", :type => :hash) do |table|
|
81
141
|
table.text("text")
|
@@ -109,5 +169,39 @@ module Feedcellar
|
|
109
169
|
end
|
110
170
|
end
|
111
171
|
end
|
172
|
+
|
173
|
+
def add_new_column
|
174
|
+
Groonga::Schema.define do |schema|
|
175
|
+
schema.change_table("Feeds") do |table|
|
176
|
+
table.reference("new_resource", "Resources")
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def migrate_value
|
182
|
+
feeds = Groonga["Feeds"]
|
183
|
+
resources = Groonga["Resources"]
|
184
|
+
tmp_resources = {}
|
185
|
+
resources.each do |resource|
|
186
|
+
tmp_resources[resource.xmlUrl] = resource.title
|
187
|
+
end
|
188
|
+
|
189
|
+
feeds.each do |feed|
|
190
|
+
feed["new_resource"] = resources[tmp_resources[feed.resource]]
|
191
|
+
feed["resource"] = nil
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def delete_old_column
|
196
|
+
Groonga::Schema.define do |schema|
|
197
|
+
schema.change_table("Feeds") do |table|
|
198
|
+
table.remove_column("resource")
|
199
|
+
end
|
200
|
+
|
201
|
+
schema.change_table("Feeds") do |table|
|
202
|
+
table.rename_column("new_resource", "resource")
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
112
206
|
end
|
113
207
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Feedcellar
|
2
|
+
class GUI
|
3
|
+
class << self
|
4
|
+
def available?
|
5
|
+
gtk_available?
|
6
|
+
end
|
7
|
+
|
8
|
+
def show_uri(uri)
|
9
|
+
Gtk.show_uri(uri) if gtk_available?
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def gtk_available?
|
14
|
+
if @gtk_available.nil?
|
15
|
+
begin
|
16
|
+
require "gtk2"
|
17
|
+
rescue LoadError
|
18
|
+
@gtk_available = false
|
19
|
+
else
|
20
|
+
@gtk_available = true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@gtk_available
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/feedcellar/version.rb
CHANGED
data/lib/feedcellar.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
</head>
|
6
6
|
<body>
|
7
7
|
<outline text="my_letter" title="my_letter" type="rss"
|
8
|
-
xmlUrl="http://myokoym.github.
|
8
|
+
xmlUrl="http://myokoym.github.io/entries.rss" htmlUrl="http://myokoym.github.com/"/>
|
9
9
|
<outline text="Rubygems | Latest Versions for mister_fairy"
|
10
10
|
title="Rubygems | Latest Versions for mister_fairy" type="rss"
|
11
11
|
xmlUrl="https://rubygems.org/gems/mister_fairy/versions.atom" htmlUrl="https://rubygems.org/gems"/>
|
data/test/test-command.rb
CHANGED
@@ -41,7 +41,8 @@ class CommandTest < Test::Unit::TestCase
|
|
41
41
|
@command.import(file)
|
42
42
|
@command.collect
|
43
43
|
Feedcellar::GroongaDatabase.new.open(@tmpdir) do |database|
|
44
|
-
|
44
|
+
# NOTE: a tag of outline is not register.
|
45
|
+
assert_equal(3, database.resources.size)
|
45
46
|
assert_true(database.feeds.count > 0)
|
46
47
|
end
|
47
48
|
|
@@ -51,7 +52,7 @@ class CommandTest < Test::Unit::TestCase
|
|
51
52
|
$stdout = io
|
52
53
|
@command.export
|
53
54
|
assert_equal(1, s.scan(/<opml/).size)
|
54
|
-
assert_equal(
|
55
|
+
assert_equal(3, s.scan(/<outline/).size)
|
55
56
|
$stdout = STDOUT
|
56
57
|
|
57
58
|
# confirm search command
|
@@ -65,12 +66,20 @@ class CommandTest < Test::Unit::TestCase
|
|
65
66
|
# confirm unregister command
|
66
67
|
@command.unregister("my_letter")
|
67
68
|
Feedcellar::GroongaDatabase.new.open(@tmpdir) do |database|
|
68
|
-
assert_equal(
|
69
|
+
assert_equal(2, database.resources.size)
|
69
70
|
end
|
70
71
|
@command.unregister("https://rubygems.org/gems/mister_fairy/versions.atom")
|
71
72
|
Feedcellar::GroongaDatabase.new.open(@tmpdir) do |database|
|
72
|
-
assert_equal(
|
73
|
+
assert_equal(1, database.resources.size)
|
73
74
|
end
|
75
|
+
|
76
|
+
# confirm latest command
|
77
|
+
s = ""
|
78
|
+
io = StringIO.new(s)
|
79
|
+
$stdout = io
|
80
|
+
@command.latest
|
81
|
+
assert_true(s.size > 0)
|
82
|
+
$stdout = STDOUT
|
74
83
|
end
|
75
84
|
|
76
85
|
def teardown
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feedcellar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masafumi Yokoyama
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rroonga
|
@@ -129,6 +129,7 @@ files:
|
|
129
129
|
- lib/feedcellar/command.rb
|
130
130
|
- lib/feedcellar/feed.rb
|
131
131
|
- lib/feedcellar/groonga_database.rb
|
132
|
+
- lib/feedcellar/gui.rb
|
132
133
|
- lib/feedcellar/opml.rb
|
133
134
|
- lib/feedcellar/resource.rb
|
134
135
|
- lib/feedcellar/version.rb
|