lideo 1.0.6
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 +7 -0
- data/bin/lideo +3 -0
- data/lib/channel.rb +12 -0
- data/lib/cli.rb +119 -0
- data/lib/feed.rb +17 -0
- data/lib/fetcher.rb +19 -0
- data/lib/headline.rb +18 -0
- data/lib/html.rb +24 -0
- data/lib/lideo_controller.rb +35 -0
- data/lib/lideo_dao.rb +46 -0
- data/lib/printer.rb +18 -0
- data/lib/templates/news_feed.mustache +40 -0
- metadata +57 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9cc1aab3e6fd0b769bfa1dc673e99c29ef5757d2d2bc95c59411feae16f326b1
|
4
|
+
data.tar.gz: efb80dfc49a95d65b0a58f1a1e0ab260473ebba1172c064f5cde36011fbbd9df
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e3324132230af14b6cb33edc877849a644c5c5793dbfac189a698331d0e199f7433ce31577df8ed328090af45676e0eaf0c0a82f329aa7576fd911deced1e58e
|
7
|
+
data.tar.gz: c833964727045b18d3b6685a44ad028b6220fa43828c46c6d024e108723d21f65f6f6410af9b9438dacc2df54b27b51388385151440766078aa869839d42bae2
|
data/bin/lideo
ADDED
data/lib/channel.rb
ADDED
data/lib/cli.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'lideo_controller'
|
4
|
+
require 'html'
|
5
|
+
|
6
|
+
BANNER = "+-+-+-+-+-+ +-+-+-+\n" +
|
7
|
+
"|L|i|d|e|o| |R|S|S|\n" +
|
8
|
+
"+-+-+-+-+-+ +-+-+-+\n"
|
9
|
+
|
10
|
+
class Cli < Thor
|
11
|
+
map %w[--version -v] => :__print_version
|
12
|
+
|
13
|
+
desc '--version -v', 'Prints the current version'
|
14
|
+
|
15
|
+
def __print_version
|
16
|
+
puts version
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'add [URL]', 'Adds an RSS feed url. Use -g flag to specify a group.'
|
20
|
+
options g: :string
|
21
|
+
|
22
|
+
def add(url)
|
23
|
+
unless valid_url?(url)
|
24
|
+
puts 'Invalid URL'
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
LideoController.new.add(url, group(options))
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'fetch', 'Fetches and prints out the headlines of your feeds. Use -g flag to specify a group.'
|
32
|
+
long_desc <<-LONGDESC
|
33
|
+
Fetches and prints out the headlines of your feeds. Available flags:
|
34
|
+
|
35
|
+
With -g Fetches feeds for the specified group only
|
36
|
+
|
37
|
+
With --to [OPTION] Fetches the feeds and outputs to the method specified in the option parameter. If omitted will output to the console.
|
38
|
+
Available options:
|
39
|
+
|
40
|
+
HTML
|
41
|
+
LONGDESC
|
42
|
+
options g: :string
|
43
|
+
options to: :string
|
44
|
+
|
45
|
+
def fetch
|
46
|
+
headlines = LideoController.new.fetch(group(options))
|
47
|
+
puts 'No news for you this time' if headlines.empty?
|
48
|
+
|
49
|
+
export_html(headlines) && return if options[:to] && options[:to].downcase == 'html'
|
50
|
+
|
51
|
+
puts "#{banner}#{format_headlines_output(headlines)}" unless headlines.empty? || !options[:to].nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'feeds', 'Outputs a list of your feeds. Use -r [URL] to remove a feed'
|
55
|
+
options r: :string
|
56
|
+
|
57
|
+
def feeds
|
58
|
+
url_to_remove = from_r_option(options)
|
59
|
+
if url_to_remove.nil?
|
60
|
+
list_feeds
|
61
|
+
else
|
62
|
+
remove_feed(url_to_remove)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def list_feeds
|
69
|
+
s = StringIO.new
|
70
|
+
LideoController.new.feeds.map { |feed| s << "#{feed.to_s}\n" }
|
71
|
+
output = !s.string.empty? ? s.string : 'You have not added any feeds yet'
|
72
|
+
puts output
|
73
|
+
end
|
74
|
+
|
75
|
+
def remove_feed(url)
|
76
|
+
LideoController.new.remove_feed(url)
|
77
|
+
end
|
78
|
+
|
79
|
+
def export_html(headlines)
|
80
|
+
puts Html.new.export(headlines)
|
81
|
+
end
|
82
|
+
|
83
|
+
def group(options)
|
84
|
+
options[:g].nil? || options[:g].empty? ? 'default' : options[:g]
|
85
|
+
end
|
86
|
+
|
87
|
+
def from_r_option(options)
|
88
|
+
options[:r] unless options[:r].nil? || options[:r].empty?
|
89
|
+
end
|
90
|
+
|
91
|
+
def valid_url?(url)
|
92
|
+
uri = URI.parse(url)
|
93
|
+
uri.is_a?(URI::HTTP) && !uri.host.nil?
|
94
|
+
rescue URI::InvalidURIError
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
def version
|
99
|
+
@version ||= Gem::Specification.load('lideo.gemspec').version
|
100
|
+
end
|
101
|
+
|
102
|
+
def banner
|
103
|
+
s = StringIO.new
|
104
|
+
s << BANNER
|
105
|
+
s << "#{version}\n"
|
106
|
+
s.string
|
107
|
+
end
|
108
|
+
|
109
|
+
def format_headlines_output(headlines_grouped)
|
110
|
+
s = StringIO.new
|
111
|
+
headlines_grouped.each_key do |channel|
|
112
|
+
s << "\n#{channel}\n"
|
113
|
+
headlines_grouped[channel].each do |headline|
|
114
|
+
s << " > #{headline}\n"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
s.string
|
118
|
+
end
|
119
|
+
end
|
data/lib/feed.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class Feed
|
2
|
+
attr_reader :url, :group
|
3
|
+
|
4
|
+
def initialize(url, group)
|
5
|
+
@url = url
|
6
|
+
@group = group
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(other)
|
10
|
+
!other.nil? && @url == other.url && @group == other.group
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"[#{@group}] #{@url}"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/lib/fetcher.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'headline'
|
2
|
+
require 'rss'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'rss/atom'
|
5
|
+
|
6
|
+
class Fetcher
|
7
|
+
def fetch(feed)
|
8
|
+
open(feed.url) do |rss|
|
9
|
+
fetched = RSS::Parser.parse(rss)
|
10
|
+
fetched.items.map do |item|
|
11
|
+
title = fetched.is_a?(RSS::Atom::Feed) ? item.title.content : item.title
|
12
|
+
link = fetched.is_a?(RSS::Atom::Feed) ? item.link.href : item.link
|
13
|
+
channel = fetched.is_a?(RSS::Atom::Feed) ? fetched.author.name.content : fetched.channel.title
|
14
|
+
|
15
|
+
Headline.new(title, link, channel)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/headline.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class Headline
|
2
|
+
attr_reader :title, :url, :channel
|
3
|
+
|
4
|
+
def initialize(title, url, channel = '')
|
5
|
+
@title = title
|
6
|
+
@url = url
|
7
|
+
@channel = channel
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
!other.nil? && other.is_a?(Headline) && @title == other.title &&
|
12
|
+
@url == other.url && @channel == other.channel
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{title} [#{url}]"
|
17
|
+
end
|
18
|
+
end
|
data/lib/html.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
require 'channel'
|
3
|
+
|
4
|
+
class Html
|
5
|
+
HOME_FOLDER = File.expand_path('~')
|
6
|
+
|
7
|
+
def export(headlines)
|
8
|
+
Mustache.template_file = File.dirname(__FILE__) + '/templates/news_feed.mustache'
|
9
|
+
view = Mustache.new
|
10
|
+
view[:channels] = to_channel_array(headlines)
|
11
|
+
view[:generated_at] = Time.now.strftime('%d/%m/%Y %H:%M')
|
12
|
+
|
13
|
+
file_name = "#{HOME_FOLDER}/lideo_news_feed.html"
|
14
|
+
open(file_name, 'w') { |f|
|
15
|
+
f.write(view.render)
|
16
|
+
}
|
17
|
+
|
18
|
+
file_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_channel_array(headlines)
|
22
|
+
headlines.collect { |key, value| Channel.new(key, value) }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'feed'
|
3
|
+
require 'lideo_dao'
|
4
|
+
require 'fetcher'
|
5
|
+
|
6
|
+
class LideoController
|
7
|
+
def add(url, group)
|
8
|
+
LideoDao.new.save(Feed.new(url, group))
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch(group)
|
12
|
+
LideoDao.new.find(group)
|
13
|
+
.map { |feed| fetcher.fetch(feed) }
|
14
|
+
.flatten
|
15
|
+
.group_by(&:channel)
|
16
|
+
end
|
17
|
+
|
18
|
+
def feeds
|
19
|
+
LideoDao.new.all
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove_feed(url)
|
23
|
+
LideoDao.new.delete_feed(url)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def fetcher
|
29
|
+
@fetcher ||= Fetcher.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def save_to_file(headlines)
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
data/lib/lideo_dao.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'pstore'
|
2
|
+
|
3
|
+
class LideoDao
|
4
|
+
DB_FILE_NAME = 'lideo.pstore'
|
5
|
+
DB_FOLDER = '.lideo'
|
6
|
+
HOME_FOLDER = File.expand_path('~')
|
7
|
+
FULL_DB_FILE_PATH = "#{HOME_FOLDER}/#{DB_FOLDER}/#{DB_FILE_NAME}"
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
db_folder_full_path = "#{HOME_FOLDER}/#{DB_FOLDER}"
|
11
|
+
unless File.directory?(db_folder_full_path)
|
12
|
+
FileUtils.mkdir_p(db_folder_full_path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def save(feed)
|
17
|
+
PStore.new(FULL_DB_FILE_PATH).transaction { |store| store[feed.url] = feed }
|
18
|
+
end
|
19
|
+
|
20
|
+
def find(group)
|
21
|
+
store = PStore.new(FULL_DB_FILE_PATH)
|
22
|
+
list = []
|
23
|
+
store.transaction(true) do
|
24
|
+
store.roots
|
25
|
+
.map { |root| list << store[root] if store[root].group == group }
|
26
|
+
end
|
27
|
+
list
|
28
|
+
end
|
29
|
+
|
30
|
+
def all
|
31
|
+
store = PStore.new(FULL_DB_FILE_PATH)
|
32
|
+
list = []
|
33
|
+
store.transaction(true) do
|
34
|
+
store.roots.map { |root| list << store[root] }
|
35
|
+
end
|
36
|
+
list
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete_feed(url)
|
40
|
+
PStore.new(FULL_DB_FILE_PATH).transaction do |store|
|
41
|
+
raise ArgumentError, "Feed #{url} not found" if store[url].nil?
|
42
|
+
|
43
|
+
store.delete(url)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/printer.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class Printer
|
2
|
+
def to_html(headlines)
|
3
|
+
puts ''
|
4
|
+
end
|
5
|
+
#def print_to_file(filename, content)
|
6
|
+
# if content.empty?
|
7
|
+
# puts "No content found"
|
8
|
+
# else
|
9
|
+
# open("~/#{filename}", 'w') { |f|
|
10
|
+
# content.each do |channel, headlines|
|
11
|
+
# f.puts "#{channel}"
|
12
|
+
# headlines.each { |headline| f.puts " > #{headline}" }
|
13
|
+
# puts ''
|
14
|
+
# end
|
15
|
+
# }
|
16
|
+
# end
|
17
|
+
#end
|
18
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Lideo</title>
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
|
7
|
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
|
8
|
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
|
12
|
+
<div class="container">
|
13
|
+
<h2 style="text-align:left;float:left;">Lideo RSS Reader</h2>
|
14
|
+
<h4 style="text-align:right;float:left;">v1.0.0</h4>
|
15
|
+
<hr style="clear:both;"/>
|
16
|
+
<p><strong>Generated at:</strong> {{ generated_at }}</p>
|
17
|
+
<div class="panel-group" id="accordion">
|
18
|
+
{{#channels}}
|
19
|
+
<div class="panel panel-default">
|
20
|
+
<div class="panel-heading">
|
21
|
+
<h4 class="panel-title">
|
22
|
+
<a data-toggle="collapse" data-parent="#accordion" href="#collapse-{{ id }}">{{ name }}</a>
|
23
|
+
</h4>
|
24
|
+
</div>
|
25
|
+
<div id="collapse-{{ id }}" class="panel-collapse collapse">
|
26
|
+
<div class="panel-body">
|
27
|
+
{{#headlines}}
|
28
|
+
<a href="{{ url }}" class="block" target="_blank">
|
29
|
+
<p> {{ title }} </p>
|
30
|
+
</a>
|
31
|
+
{{/headlines}}
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
{{/channels}}
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
</body>
|
40
|
+
</html>
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lideo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gyowanny Queiroz
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-03-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A simple RSS aggregator CLI where you can read the headlines from your
|
14
|
+
terminal
|
15
|
+
email:
|
16
|
+
- gyowanny@gmail.com
|
17
|
+
executables:
|
18
|
+
- lideo
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- bin/lideo
|
23
|
+
- lib/channel.rb
|
24
|
+
- lib/cli.rb
|
25
|
+
- lib/feed.rb
|
26
|
+
- lib/fetcher.rb
|
27
|
+
- lib/headline.rb
|
28
|
+
- lib/html.rb
|
29
|
+
- lib/lideo_controller.rb
|
30
|
+
- lib/lideo_dao.rb
|
31
|
+
- lib/printer.rb
|
32
|
+
- lib/templates/news_feed.mustache
|
33
|
+
homepage: http://rubygems.org/gems/lideo
|
34
|
+
licenses:
|
35
|
+
- MIT
|
36
|
+
metadata:
|
37
|
+
source_code_uri: https://github.com/gyoqueiroz/lideo
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubygems_version: 3.0.3
|
54
|
+
signing_key:
|
55
|
+
specification_version: 4
|
56
|
+
summary: Lideo RSS Aggregator
|
57
|
+
test_files: []
|