lideo 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'cli'
3
+ Cli.start(ARGV)
data/lib/channel.rb ADDED
@@ -0,0 +1,12 @@
1
+ class Channel
2
+ attr_reader :name, :headlines
3
+
4
+ def initialize(name, headlines)
5
+ @name = name
6
+ @headlines = headlines
7
+ end
8
+
9
+ def id
10
+ @name.gsub(/[^0-9a-z]/i, '')
11
+ end
12
+ end
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: []