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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c68cc360529496d84b72f86f7bde1c3da53a54fa
4
- data.tar.gz: 35ac9e19d288b2454cf8442077d1b41f7e3eae44
3
+ metadata.gz: 90de9f0e20628fe9c0296ceb73a55e068124996b
4
+ data.tar.gz: 35318c5da10ecd18d85c37f0e00c2dcf10c907ad
5
5
  SHA512:
6
- metadata.gz: b77c019cd534abc6b58cdee970c8cf69223947770c58ecbc65ab66766672ab91df965edff6495e9a4da2ac86a004da0e9909c4dc99dd577f627999ce5be47b54
7
- data.tar.gz: 0d29006fbcf81628d0dcf7b746b721b1c79edfb49b9344dc4a3402b146b6219b36f02a8101a338a963bc450608dbb206c73a96ad68d36120c36f88b99daa1917
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!
@@ -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["title"], 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
- database.register(resource["title"], resource)
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["xmlUrl"]
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(feed_url,
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
- (feed.title =~ word) | (feed.description =~ word)
96
- end
97
- end
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
- if options[:mtime]
100
- feeds = feeds.select do |feed|
101
- feed.date > (Time.now - (options[:mtime] * 60 * 60 * 24))
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
- if options[:resource]
106
- feeds = feeds.select do |feed|
107
- feed.resource =~ options[:resource]
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 = resources.first.title
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
- Gtk.show_uri(feed.link) if browser_available?
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
- populate_schema
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
- feeds = Groonga["Resources"]
30
- feeds.add(key, attributes)
35
+ resources.add(key, attributes)
31
36
  end
32
37
 
33
- def add(resource, title, link, description, date)
34
- feeds = Groonga["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
- feeds = Groonga["Resources"]
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
@@ -1,3 +1,3 @@
1
1
  module Feedcellar
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/feedcellar.rb CHANGED
@@ -4,6 +4,7 @@ require "feedcellar/opml"
4
4
  require "feedcellar/command"
5
5
  require "feedcellar/resource"
6
6
  require "feedcellar/feed"
7
+ require "feedcellar/gui"
7
8
 
8
9
  module Feedcellar
9
10
  end
@@ -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.com/entries.rss" htmlUrl="http://myokoym.github.com/"/>
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
- assert_equal(4, database.resources.size)
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(4, s.scan(/<outline/).size)
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(3, database.resources.size)
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(2, database.resources.size)
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.2.2
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-16 00:00:00.000000000 Z
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