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 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