feedcellar 0.1.3 → 0.2.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: 99485cd81b7d2ccc7e871f93c89f54d484c1a725
4
- data.tar.gz: acf336156d13924926fe66805a457d7f6c574520
3
+ metadata.gz: e574a4dc6570abaed41db607382a859f4e99d245
4
+ data.tar.gz: 57d69eaac737b2f5369bd37cd67b8dc3e8292af2
5
5
  SHA512:
6
- metadata.gz: ce6bbda93db793f7c36e82f6461890128dbb01031a2759ca2d8491a7bcae746e71329a3b01a9eb6218b2e2dcc0508104112d8e8ea0cf0fbe3419877b844c0266
7
- data.tar.gz: 05daf037e95a72d01b876f20a8830f38d361c1cbc99bd63dc435715d84d42113473f53017964c46afb77bf8059fd79fb287fa90f1f1e6a44a1d011dcabdd492b
6
+ metadata.gz: 8a8dcd5f66b52a3036edb153abc251cef13015b77e7fd9ed3fd94b5aa6c257216d4da571bc0ba77a11e7dc3e77585b243c21ab94637dfcdab0663d90f2d2eda3
7
+ data.tar.gz: 1b9bdea33770e3959a27bedc815c4712d31d0eaefa9b3057d6d652556657bb888b9bdcf6e7c8455e7fe77f1ea0b7de2d883edc347bbd982cf8841afe18c3cd32
data/NEWS.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # NEWS
2
2
 
3
+ ## 0.2.0: 2013-06-09
4
+
5
+ Improve something to do with output release!
6
+
7
+ ### Changes
8
+
9
+ * Improvements
10
+ * Change default format of search command to one liner
11
+ * Change registers list format to one liner
12
+ * Add export command
13
+ * Add unregister command
14
+ * Use "short_text" instead of "text"
15
+ * Support raw object for API access
16
+
17
+ * Fixes
18
+ * Convert line-feed to space in feed title
19
+ * Extract parse of URL for resource of feed
20
+ * Extract parse of RSS feed
21
+ * Fix default database directory
22
+ * Fix groonga zone
23
+ * Fix description and date for atom
24
+
3
25
  ## 0.1.3: 2013-06-06
4
26
 
5
27
  Experimental functions release!
data/README.md CHANGED
@@ -32,6 +32,10 @@ Import URL from OPML
32
32
 
33
33
  $ feedcellar import registers.xml
34
34
 
35
+ Export registerd resources to OPML to STDOUT
36
+
37
+ $ feedcellar export
38
+
35
39
  Show registers
36
40
 
37
41
  $ feedcellar list
data/TODO.md CHANGED
@@ -3,5 +3,10 @@
3
3
  ## Improvements
4
4
 
5
5
  * Manage TAG outline from OPML format.
6
+ * Support pager option
7
+
8
+ ## Fixes
9
+
10
+ * Review table column types.
6
11
 
7
12
  ## Release
data/lib/feedcellar.rb CHANGED
@@ -2,6 +2,8 @@ require "feedcellar/version"
2
2
  require "feedcellar/groonga_database"
3
3
  require "feedcellar/opml"
4
4
  require "feedcellar/command"
5
+ require "feedcellar/resource"
6
+ require "feedcellar/feed"
5
7
 
6
8
  module Feedcellar
7
9
  end
@@ -1,14 +1,16 @@
1
1
  require "thor"
2
- require "rss"
3
2
  require "feedcellar/version"
4
3
  require "feedcellar/groonga_database"
5
4
  require "feedcellar/opml"
5
+ require "feedcellar/feed"
6
+ require "feedcellar/resource"
6
7
 
7
8
  module Feedcellar
8
9
  class Command < Thor
9
10
  def initialize(*args)
10
11
  super
11
- @work_dir = File.join(File.expand_path("~"), ".feedcellar")
12
+ @base_dir = File.join(File.expand_path("~"), ".feedcellar")
13
+ @database_dir = File.join(@base_dir, "db")
12
14
  end
13
15
 
14
16
  desc "version", "Show version number."
@@ -18,134 +20,93 @@ module Feedcellar
18
20
 
19
21
  desc "register URL", "Register a URL."
20
22
  def register(url)
21
- @database = GroongaDatabase.new
22
- @database.open(@work_dir) do |database|
23
- begin
24
- rss = RSS::Parser.parse(url)
25
- rescue RSS::InvalidRSSError
26
- rss = RSS::Parser.parse(url, false)
27
- rescue
28
- $stderr.puts "WARNNING: #{$!} (#{url})"
29
- return 1
30
- end
31
-
32
- unless rss
33
- $stderr.puts "ERROR: Invalid URL"
34
- return 1
35
- end
36
-
37
- resource = {}
38
- if rss.is_a?(RSS::Atom::Feed)
39
- resource["xmlUrl"] = url
40
- resource["title"] = rss.title.content
41
- resource["htmlUrl"] = rss.link.href
42
- resource["description"] = rss.dc_description
43
- else
44
- resource["xmlUrl"] = url
45
- resource["title"] = rss.channel.title
46
- resource["htmlUrl"] = rss.channel.link
47
- resource["description"] = rss.channel.description
48
- end
23
+ resource = Resource.parse(url)
24
+ return 1 unless resource
49
25
 
26
+ GroongaDatabase.new.open(@database_dir) do |database|
50
27
  database.register(resource["title"], resource)
51
28
  end
52
29
  end
53
30
 
31
+ desc "unregister TITLE_OR_URL", "Unregister a resource of feed."
32
+ def unregister(title_or_url)
33
+ GroongaDatabase.new.open(@database_dir) do |database|
34
+ database.unregister(title_or_url)
35
+ end
36
+ end
37
+
54
38
  desc "import FILE", "Import feed resources by OPML format."
55
39
  def import(opml_xml)
56
- @database = GroongaDatabase.new
57
- @database.open(@work_dir) do |database|
40
+ GroongaDatabase.new.open(@database_dir) do |database|
58
41
  Feedcellar::Opml.parse(opml_xml).each do |resource|
59
42
  database.register(resource["title"], resource)
60
43
  end
61
44
  end
62
45
  end
63
46
 
64
- desc "list", "Show feed url list."
47
+ desc "export", "Export feed resources by OPML format."
48
+ def export
49
+ GroongaDatabase.new.open(@database_dir) do |database|
50
+ puts Opml.build(database.resources.records)
51
+ end
52
+ end
53
+
54
+ desc "list", "Show registered resources list of title and URL."
65
55
  def list
66
- @database = GroongaDatabase.new
67
- @database.open(@work_dir) do |database|
56
+ GroongaDatabase.new.open(@database_dir) do |database|
68
57
  database.resources.each do |record|
69
- puts record.title
70
- puts " #{record.xmlUrl}"
71
- puts
58
+ puts "#{record.title} #{record.xmlUrl}"
72
59
  end
73
60
  end
74
61
  end
75
62
 
76
- desc "collect", "Collect feeds."
63
+ desc "collect", "Collect feeds from WWW."
77
64
  def collect
78
- @database = GroongaDatabase.new
79
- @database.open(@work_dir) do |database|
80
- resources = database.resources
81
-
82
- resources.each do |record|
65
+ GroongaDatabase.new.open(@database_dir) do |database|
66
+ database.resources.each do |record|
83
67
  feed_url = record["xmlUrl"]
84
68
  next unless feed_url
85
69
 
86
- begin
87
- rss = RSS::Parser.parse(feed_url)
88
- rescue RSS::InvalidRSSError
89
- begin
90
- rss = RSS::Parser.parse(feed_url, false)
91
- rescue
92
- $stderr.puts "WARNNING: #{$!} (#{feed_url})"
93
- next
94
- end
95
- rescue
96
- $stderr.puts "WARNNING: #{$!} (#{feed_url})"
97
- next
98
- end
99
- next unless rss
100
-
101
- rss.items.each do |item|
102
- if rss.is_a?(RSS::Atom::Feed)
103
- title = item.title.content
104
- link = item.link.href if item.link
105
- description = item.dc_description
106
- date = item.dc_date
107
- else
108
- title = item.title
109
- link = item.link
110
- description = item.description
111
- date = item.date
112
- end
113
-
114
- unless link
115
- $stderr.puts "WARNNING: missing link (#{title})"
116
- next
117
- end
118
-
119
- database.add(feed_url, title, link, description, date)
70
+ items = Feed.parse(feed_url)
71
+ next unless items
72
+
73
+ items.each do |item|
74
+ database.add(feed_url,
75
+ item.title,
76
+ item.link,
77
+ item.description,
78
+ item.date)
120
79
  end
121
80
  end
122
81
  end
123
82
  end
124
83
 
125
- desc "search WORD", "Search feeds."
84
+ desc "search WORD", "Search feeds from local database."
126
85
  option :desc, :type => :boolean, :aliases => "-d", :desc => "show description"
127
- option :simple, :type => :boolean, :desc => "simple format as one liner"
128
86
  option :browser, :type => :boolean, :desc => "open *ALL* links in browser"
129
- def search(word)
130
- @database = GroongaDatabase.new
131
- @database.open(@work_dir) do |database|
132
- feeds = @database.feeds
133
- resources = @database.resources
87
+ def search(word, api=false)
88
+ GroongaDatabase.new.open(@database_dir) do |database|
89
+ feeds = database.feeds
90
+ resources = database.resources
134
91
 
135
92
  records = feeds.select do |v|
136
93
  (v.title =~ word) | (v.description =~ word)
137
94
  end
138
95
 
139
- records.sort([{:key => "date", :order => "ascending"}]).each do |record|
96
+ sorted_records = records.sort([{:key => "date", :order => "ascending"}])
97
+ return sorted_records if api
98
+
99
+ sorted_records.each do |record|
140
100
  feed_resources = resources.select {|v| v.xmlUrl =~ record.resource }
141
101
  next unless feed_resources
142
- if options[:simple]
143
- # TODO This format will be to default from 0.2.0
102
+ next unless feed_resources.first # FIXME
103
+
104
+ unless options[:desc]
144
105
  date = record.date.strftime("%Y/%m/%d")
145
- title= record.title
106
+ title = record.title
146
107
  resource = feed_resources.first.title
147
108
  link = record.link
148
- puts "#{date} #{title} - #{resource}"
109
+ puts "#{date} #{title.gsub(/\n/, " ")} - #{resource}"
149
110
  else
150
111
  puts feed_resources.first.title
151
112
  puts " #{record.title}"
@@ -0,0 +1,52 @@
1
+ require "rss"
2
+
3
+ module Feedcellar
4
+ class Feed
5
+ attr_reader :title, :link, :description, :date
6
+ def initialize(title, link, description, date)
7
+ @title = title
8
+ @link = link
9
+ @description = description
10
+ @date = date
11
+ end
12
+
13
+ def self.parse(feed_url)
14
+ feeds = []
15
+
16
+ begin
17
+ rss = RSS::Parser.parse(feed_url)
18
+ rescue RSS::InvalidRSSError
19
+ begin
20
+ rss = RSS::Parser.parse(feed_url, false)
21
+ rescue
22
+ $stderr.puts "WARNNING: #{$!} (#{feed_url})"
23
+ return nil
24
+ end
25
+ rescue
26
+ $stderr.puts "WARNNING: #{$!} (#{feed_url})"
27
+ return nil
28
+ end
29
+ return nil unless rss
30
+
31
+ rss.items.each do |item|
32
+ if rss.is_a?(RSS::Atom::Feed)
33
+ title = item.title.content
34
+ link = item.link.href if item.link
35
+ description = item.summary.content if item.summary
36
+ date = item.updated.content if item.updated
37
+ else
38
+ title = item.title
39
+ link = item.link
40
+ description = item.description
41
+ date = item.date
42
+ end
43
+
44
+ next unless link
45
+
46
+ feeds << new(title, link, description, date)
47
+ end
48
+
49
+ feeds
50
+ end
51
+ end
52
+ end
@@ -39,6 +39,14 @@ module Feedcellar
39
39
  :date => date)
40
40
  end
41
41
 
42
+ def unregister(title_or_url)
43
+ feeds = Groonga["Resources"]
44
+ feeds.delete do |record|
45
+ (record.title == title_or_url) |
46
+ (record.xmlUrl == title_or_url)
47
+ end
48
+ end
49
+
42
50
  def close
43
51
  @database.close
44
52
  @database = nil
@@ -71,23 +79,23 @@ module Feedcellar
71
79
  Groonga::Schema.define do |schema|
72
80
  schema.create_table("Resources", :type => :hash) do |table|
73
81
  table.text("text")
74
- table.text("isComment")
75
- table.text("isBreakpoint")
76
- table.text("created")
77
- table.text("category")
82
+ table.short_text("isComment")
83
+ table.short_text("isBreakpoint")
84
+ table.short_text("created")
85
+ table.short_text("category")
78
86
  table.text("description")
79
- table.text("url")
80
- table.text("htmlUrl")
81
- table.text("xmlUrl")
82
- table.text("title")
83
- table.text("version")
84
- table.text("language")
87
+ table.short_text("url")
88
+ table.short_text("htmlUrl")
89
+ table.short_text("xmlUrl")
90
+ table.short_text("title")
91
+ table.short_text("version")
92
+ table.short_text("language")
85
93
  end
86
94
 
87
95
  schema.create_table("Feeds", :type => :hash) do |table|
88
- table.text("resource")
89
- table.text("title")
90
- table.text("link")
96
+ table.short_text("resource")
97
+ table.short_text("title")
98
+ table.short_text("link")
91
99
  table.text("description")
92
100
  table.time("date")
93
101
  end
@@ -31,5 +31,31 @@ module Feedcellar
31
31
  end
32
32
  outlines
33
33
  end
34
+
35
+ def self.build(items)
36
+ document = REXML::Document.new
37
+
38
+ xml_decl = REXML::XMLDecl.new
39
+ xml_decl.version = "1.0"
40
+ xml_decl.encoding = "UTF-8"
41
+ document.add(xml_decl)
42
+
43
+ root = document.add_element("opml")
44
+ root.add_attributes("version" => "1.0")
45
+
46
+ head = root.add_element("head")
47
+ title = head.add_element("title")
48
+ title.add_text("registers in feedcellar")
49
+
50
+ body = root.add_element("body")
51
+ items.each do |item|
52
+ outline = body.add_element("outline")
53
+ item.attributes.each do |key, value|
54
+ outline.add_attributes(key => value)
55
+ end
56
+ end
57
+
58
+ document.to_s
59
+ end
34
60
  end
35
61
  end
@@ -0,0 +1,36 @@
1
+ require "rss"
2
+
3
+ module Feedcellar
4
+ class Resource
5
+ def self.parse(url)
6
+ begin
7
+ rss = RSS::Parser.parse(url)
8
+ rescue RSS::InvalidRSSError
9
+ rss = RSS::Parser.parse(url, false)
10
+ rescue
11
+ $stderr.puts "WARNNING: #{$!} (#{url})"
12
+ return nil
13
+ end
14
+
15
+ unless rss
16
+ $stderr.puts "ERROR: Invalid URL"
17
+ return nil
18
+ end
19
+
20
+ resource = {}
21
+ if rss.is_a?(RSS::Atom::Feed)
22
+ resource["xmlUrl"] = url
23
+ resource["title"] = rss.title.content
24
+ resource["htmlUrl"] = rss.link.href
25
+ resource["description"] = rss.dc_description
26
+ else
27
+ resource["xmlUrl"] = url
28
+ resource["title"] = rss.channel.title
29
+ resource["htmlUrl"] = rss.channel.link
30
+ resource["description"] = rss.channel.description
31
+ end
32
+
33
+ resource
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,3 @@
1
1
  module Feedcellar
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
data/test/test-command.rb CHANGED
@@ -9,10 +9,11 @@ class CommandTest < Test::Unit::TestCase
9
9
  @tmpdir = File.join(File.dirname(__FILE__), "tmp", "database")
10
10
  FileUtils.mkdir_p(@tmpdir)
11
11
  @command = Feedcellar::Command.new
12
- @command.instance_variable_set(:@work_dir, @tmpdir)
12
+ @command.instance_variable_set(:@database_dir, @tmpdir)
13
13
  end
14
14
 
15
15
  def test_command
16
+ # confirm version command
16
17
  s = ""
17
18
  io = StringIO.new(s)
18
19
  $stdout = io
@@ -20,6 +21,7 @@ class CommandTest < Test::Unit::TestCase
20
21
  assert_equal("#{Feedcellar::VERSION}\n", s)
21
22
  $stdout = STDOUT
22
23
 
24
+ # confirm register command if invalid URL
23
25
  s = ""
24
26
  io = StringIO.new(s)
25
27
  $stderr = io
@@ -27,12 +29,14 @@ class CommandTest < Test::Unit::TestCase
27
29
  assert_equal("ERROR: Invalid URL\n", s)
28
30
  $stderr = STDERR
29
31
 
32
+ # confirm register command
30
33
  @command.register("http://myokoym.github.io/entries.rss")
31
34
  @command.register("https://rubygems.org/gems/mister_fairy/versions.atom")
32
35
  Feedcellar::GroongaDatabase.new.open(@tmpdir) do |database|
33
36
  assert_equal(2, database.resources.size)
34
37
  end
35
38
 
39
+ # confirm import command
36
40
  file = File.join(File.dirname(__FILE__), "fixtures", "subscriptions.xml")
37
41
  @command.import(file)
38
42
  @command.collect
@@ -41,11 +45,48 @@ class CommandTest < Test::Unit::TestCase
41
45
  assert_true(database.feeds.count > 0)
42
46
  end
43
47
 
48
+ # confirm export command
49
+ s = ""
50
+ io = StringIO.new(s)
51
+ $stdout = io
52
+ @command.export
53
+ assert_equal(1, s.scan(/<opml/).size)
54
+ assert_equal(4, s.scan(/<outline/).size)
55
+ $stdout = STDOUT
56
+
57
+ # confirm search command
44
58
  s = ""
45
59
  io = StringIO.new(s)
46
60
  $stdout = io
47
61
  @command.search("ruby")
48
- assert_true(s.size > 500)
62
+ assert_true(s.size > 100)
63
+
64
+ # confirm search command in case of API
65
+ s = ""
66
+ io = StringIO.new(s)
67
+ ret = @command.search("ruby", true)
68
+ assert_equal(Groonga::Array, ret.class)
69
+ assert_equal(0, s.size)
70
+ assert_not_equal(0, ret.size)
71
+ $stdout = STDOUT
72
+
73
+ # confirm unregister command
74
+ @command.unregister("my_letter")
75
+ Feedcellar::GroongaDatabase.new.open(@tmpdir) do |database|
76
+ assert_equal(3, database.resources.size)
77
+ end
78
+ @command.unregister("https://rubygems.org/gems/mister_fairy/versions.atom")
79
+ Feedcellar::GroongaDatabase.new.open(@tmpdir) do |database|
80
+ assert_equal(2, database.resources.size)
81
+ end
82
+
83
+ # confirm search command after unregister
84
+ s = ""
85
+ io = StringIO.new(s)
86
+ ret = @command.search("ruby", true)
87
+ assert_equal(Groonga::Array, ret.class)
88
+ assert_equal(0, s.size)
89
+ assert_not_equal(0, ret.size)
49
90
  $stdout = STDOUT
50
91
  end
51
92
 
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.1.3
4
+ version: 0.2.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-06 00:00:00.000000000 Z
11
+ date: 2013-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rroonga
@@ -127,8 +127,10 @@ files:
127
127
  - feedcellar.gemspec
128
128
  - lib/feedcellar.rb
129
129
  - lib/feedcellar/command.rb
130
+ - lib/feedcellar/feed.rb
130
131
  - lib/feedcellar/groonga_database.rb
131
132
  - lib/feedcellar/opml.rb
133
+ - lib/feedcellar/resource.rb
132
134
  - lib/feedcellar/version.rb
133
135
  - test/fixtures/subscriptions.xml
134
136
  - test/run-test.rb