feedcellar 0.2.2 → 0.3.0
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.
- 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
|