rslog 0.0.15 → 0.0.16
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 +4 -4
- data/lib/rslog.rb +32 -32
- data/lib/rslog/args_handler.rb +60 -11
- data/lib/rslog/parser.rb +56 -13
- data/lib/rslog/presenter.rb +54 -25
- data/lib/rslog/validator.rb +5 -7
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '089ce1f53d07365d2cd5a8a132d3f016fd971aec94fb5e1d7d097b83c378d16b'
|
|
4
|
+
data.tar.gz: 690f6b582c62d555192f003bb9bf48f47c05c73e6144663fc5271ef263a8a6e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2607d6b3ba40a13ecba89d214d0ef3d92d8d7b28b3b0dedf96942b01ea939efd287797ad4fe56e99c6986eac2dcbe53feca954abe41b69f14633ed9a79249f82
|
|
7
|
+
data.tar.gz: f68723a370022e15d2df26516b60725cad3b20d60a3c59bc3d7c2fc55f4146d874cbd123b1900896f7cc5eeab9dbff3af027241a3e8d57fc1f41c734283c25c8
|
data/lib/rslog.rb
CHANGED
|
@@ -8,47 +8,47 @@ require_relative 'rslog/presenter'
|
|
|
8
8
|
require 'set'
|
|
9
9
|
|
|
10
10
|
module RSlog
|
|
11
|
-
VERSION = '0.0.
|
|
11
|
+
VERSION = '0.0.16'
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
# Module to hold main process
|
|
14
|
+
#
|
|
15
|
+
module Main
|
|
14
16
|
def self.run
|
|
15
|
-
file_names = RSlog::ArgsHandler.handle(ARGV)
|
|
16
|
-
|
|
17
|
-
return unless file_names.any?
|
|
18
|
-
|
|
19
17
|
file_names.each do |file_name|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return
|
|
23
|
-
end
|
|
24
|
-
|
|
18
|
+
puts "Statistics for file #{file_name}"
|
|
19
|
+
|
|
25
20
|
lines = IO.readlines(file_name)
|
|
26
21
|
|
|
27
22
|
RSlog::Validator.execute(lines)
|
|
28
|
-
|
|
29
|
-
config_sets
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
|
|
24
|
+
process(config_sets, lines)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.process(config_sets, lines)
|
|
29
|
+
config_sets.each do |conf|
|
|
30
|
+
parsed = RSlog::Parser.new(lines, conf).execute
|
|
31
|
+
RSlog::Presenter.new(parsed, conf).present
|
|
33
32
|
end
|
|
34
33
|
end
|
|
35
|
-
|
|
34
|
+
|
|
35
|
+
def self.file_names
|
|
36
|
+
file_names = RSlog::ArgsHandler.new(ARGV).handle
|
|
37
|
+
return file_names if file_names.all? { |file_name| File.file?(file_name) }
|
|
38
|
+
|
|
39
|
+
puts 'There is no file names given. Check input.'
|
|
40
|
+
[]
|
|
41
|
+
end
|
|
42
|
+
|
|
36
43
|
def self.config_sets
|
|
37
|
-
[
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
suffix: 'unique views',
|
|
46
|
-
calc: proc { |visits| Set.new(visits).size } },
|
|
47
|
-
|
|
48
|
-
{ title: "Average visits sorted:",
|
|
49
|
-
suffix: 'average visits',
|
|
50
|
-
calc: proc { |visits| Set.new(visits).size.to_f / visits.size } }
|
|
51
|
-
]
|
|
44
|
+
[{ title: %(List of webpages with most page views ordered from most pages views to less page views:),
|
|
45
|
+
# head_titles: %w[Url Visits Average],
|
|
46
|
+
head_titles: %w[Url Visits],
|
|
47
|
+
calc: proc { |visits| visits.size } },
|
|
48
|
+
{ title: 'List of webpages with most unique page views also ordered:',
|
|
49
|
+
# head_titles: %w[Url Unique\ views Average],
|
|
50
|
+
head_titles: %w[Url Unique\ views],
|
|
51
|
+
calc: proc { |visits| Set.new(visits).size } }]
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
end
|
data/lib/rslog/args_handler.rb
CHANGED
|
@@ -1,19 +1,68 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Class to hold data and implement processing
|
|
4
|
-
#
|
|
5
3
|
module RSlog
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
# Class to parse options and arguments
|
|
5
|
+
#
|
|
6
|
+
# returns Array of file names
|
|
7
|
+
#
|
|
8
|
+
class ArgsHandler
|
|
9
|
+
attr_reader :args, :options
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
def initialize(args)
|
|
12
|
+
@args = args
|
|
13
|
+
@options = @args.select { |el| el =~ /^-/ }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def handle
|
|
17
|
+
if options.any? || args.empty?
|
|
18
|
+
_handle_options
|
|
19
|
+
return []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# file_names array
|
|
23
|
+
args - options
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def _handle_options
|
|
29
|
+
_show_help || _show_version || _show_unknown_options_warning
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def _show_help
|
|
33
|
+
if options.delete('-h') || args.empty?
|
|
34
|
+
puts _help_message
|
|
35
|
+
return true
|
|
36
|
+
end
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def _help_message
|
|
41
|
+
%[
|
|
42
|
+
R(uby)S(imple)log statistics
|
|
43
|
+
Usage:
|
|
44
|
+
> rslog filename[.log|.txt]
|
|
45
|
+
|
|
46
|
+
> rslog [-h|-v]
|
|
47
|
+
-h - show this info
|
|
48
|
+
-v - show version
|
|
49
|
+
]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def _show_version
|
|
53
|
+
if @options.delete('-v')
|
|
54
|
+
puts " Version #{RSlog::VERSION}"
|
|
55
|
+
return true
|
|
56
|
+
end
|
|
57
|
+
false
|
|
58
|
+
end
|
|
14
59
|
|
|
15
|
-
|
|
60
|
+
def _show_unknown_options_warning
|
|
61
|
+
if @options.any?
|
|
62
|
+
puts " Unknown options #{@options.join(', ')}"
|
|
63
|
+
return true
|
|
64
|
+
end
|
|
65
|
+
false
|
|
16
66
|
end
|
|
17
67
|
end
|
|
18
68
|
end
|
|
19
|
-
|
data/lib/rslog/parser.rb
CHANGED
|
@@ -1,25 +1,68 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Class to hold data and implement processing
|
|
4
|
-
#
|
|
5
3
|
module RSlog
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
# Class to hold data_processing methods
|
|
5
|
+
#
|
|
6
|
+
class DataProcessing
|
|
7
|
+
attr_reader :result
|
|
9
8
|
|
|
10
9
|
def initialize(source, conf)
|
|
11
10
|
@calc = conf.fetch(:calc) { proc }
|
|
12
|
-
@separator = conf.fetch(:separator
|
|
13
|
-
@
|
|
11
|
+
@separator = conf.fetch(:separator, ' ')
|
|
12
|
+
@result = Array(source)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# extract
|
|
16
|
+
def extract
|
|
17
|
+
@result = @result.map { |item| item.split(@separator) }
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# group
|
|
22
|
+
def group
|
|
23
|
+
@result = @result.group_by { |url, _visits| url }.to_a
|
|
24
|
+
self
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# calculate
|
|
28
|
+
def calculate
|
|
29
|
+
# calculate visits
|
|
30
|
+
@result = @result.map { |url, visits| [url, @calc.call(visits)] }
|
|
31
|
+
# calculate average
|
|
32
|
+
# @result = @result.map { |url, visits_qty| [url, visits_qty, (visits_qty.to_f / _total)] }
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# sort
|
|
37
|
+
def order
|
|
38
|
+
@result = @result.sort_by { |_url, visits_by_page| visits_by_page }
|
|
39
|
+
@result.reverse!
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# calculate total
|
|
46
|
+
def _total
|
|
47
|
+
@result.sum { |_url, visits_qty| visits_qty }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
require 'forwardable'
|
|
52
|
+
# Class to parse data
|
|
53
|
+
#
|
|
54
|
+
class Parser
|
|
55
|
+
attr_reader :data_processing
|
|
56
|
+
|
|
57
|
+
extend Forwardable
|
|
58
|
+
def_delegators :@data_processing, :extract, :group, :calculate, :order
|
|
59
|
+
|
|
60
|
+
def initialize(source, conf)
|
|
61
|
+
@data_processing = DataProcessing.new(source, conf)
|
|
14
62
|
end
|
|
15
63
|
|
|
16
64
|
def execute
|
|
17
|
-
|
|
18
|
-
.map{|item| item.split(separator)} #extract, returns Array
|
|
19
|
-
.group_by{|page_name, _visits| page_name } #group, returns Hash
|
|
20
|
-
.map{|page_name, visits| [page_name, calc.call(visits)] } #calculate, returns Array
|
|
21
|
-
.sort_by{|_page_name, visits_by_page| visits_by_page } #sort, returns Array
|
|
22
|
-
.reverse
|
|
65
|
+
data_processing.extract.group.calculate.order.result
|
|
23
66
|
end
|
|
24
67
|
end
|
|
25
68
|
end
|
data/lib/rslog/presenter.rb
CHANGED
|
@@ -1,39 +1,68 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Class for format result according to passed 'format_string'
|
|
4
|
-
#
|
|
5
|
-
# result is array of formatted strings
|
|
6
|
-
# e.g
|
|
7
|
-
# [ "/about 1 visits",
|
|
8
|
-
# "/about/2 1 visits",
|
|
9
|
-
# "/home 1 visits",
|
|
10
|
-
# "/contact 1 visits",
|
|
11
|
-
# "/help_page/1 1 visits" ]
|
|
12
|
-
#
|
|
13
3
|
module RSlog
|
|
4
|
+
# Class for format result according to passed 'format_string'
|
|
5
|
+
#
|
|
6
|
+
# result is array of formatted strings
|
|
7
|
+
# e.g
|
|
8
|
+
# [ "/about 1 visits",
|
|
9
|
+
# "/about/2 1 visits",
|
|
10
|
+
# "/home 1 visits",
|
|
11
|
+
# "/contact 1 visits",
|
|
12
|
+
# "/help_page/1 1 visits" ]
|
|
13
|
+
#
|
|
14
14
|
class Presenter
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
LEFT_UP_CORNER = "\u250c"
|
|
16
|
+
RIGHT_UP_CORNER = "\u2510"
|
|
17
|
+
LEFT_DOWN_CORNER = "\u2514"
|
|
18
|
+
RIGHT_DOWN_CORNER = "\u2518"
|
|
19
|
+
VERTICAL_BORDER = "\u2502"
|
|
20
|
+
HORIZONTAL_BORDER = "\u2500"
|
|
21
|
+
VERTICAL_LEFT_BORDER = "\u251C"
|
|
22
|
+
VERTICAL_RIGHT_BORDER = "\u2524"
|
|
23
|
+
|
|
18
24
|
def initialize(source, conf)
|
|
19
25
|
@source = source
|
|
20
|
-
@
|
|
21
|
-
@
|
|
22
|
-
@
|
|
26
|
+
@col_size = conf.fetch(:col_size, 20)
|
|
27
|
+
@title = conf.fetch(:title, 'Stat Pages')
|
|
28
|
+
@formatter = conf.fetch(:formatter, "%-#{@col_size}s")
|
|
29
|
+
@columns = conf.fetch(:columns, @source&.first&.size || 1)
|
|
30
|
+
@head_titles = conf.fetch(:head_titles, Array.new(@columns, 'title'))
|
|
23
31
|
end
|
|
24
|
-
|
|
32
|
+
|
|
25
33
|
def present
|
|
26
|
-
puts title
|
|
27
|
-
puts
|
|
34
|
+
puts @title
|
|
35
|
+
puts _top_border
|
|
36
|
+
puts VERTICAL_BORDER + format(@formatter * @columns, *@head_titles) + VERTICAL_BORDER
|
|
37
|
+
puts _middle_border
|
|
38
|
+
puts _formatted_data
|
|
39
|
+
puts _bottom_border
|
|
28
40
|
puts
|
|
29
41
|
end
|
|
30
|
-
|
|
42
|
+
|
|
31
43
|
private
|
|
32
|
-
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
|
|
45
|
+
def _top_border
|
|
46
|
+
LEFT_UP_CORNER + _horisontal_line + RIGHT_UP_CORNER
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def _middle_border
|
|
50
|
+
VERTICAL_LEFT_BORDER + _horisontal_line + VERTICAL_RIGHT_BORDER
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def _bottom_border
|
|
54
|
+
LEFT_DOWN_CORNER + _horisontal_line + RIGHT_DOWN_CORNER
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def _horisontal_line
|
|
58
|
+
HORIZONTAL_BORDER * @col_size * @columns
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def _formatted_data
|
|
62
|
+
@source.map do |row|
|
|
63
|
+
row = row.map(&:to_s)
|
|
64
|
+
VERTICAL_BORDER + format(@formatter * @columns, *row).to_s + VERTICAL_BORDER
|
|
65
|
+
end
|
|
37
66
|
end
|
|
38
67
|
end
|
|
39
68
|
end
|
data/lib/rslog/validator.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Class to validate if we have valid data in lines, for example well formatted IPs
|
|
4
|
-
#
|
|
5
|
-
# Checks if IPs are comply with regex
|
|
6
|
-
# Output message "Valid IPs"/"Invalid IPs"
|
|
7
|
-
#
|
|
8
3
|
module RSlog
|
|
4
|
+
# Class to validate if we have valid data in lines, for example well formatted IPs
|
|
5
|
+
#
|
|
6
|
+
# Checks if IPs are comply with regex
|
|
7
|
+
# Output message "Valid IPs"/"Invalid IPs"
|
|
8
|
+
#
|
|
9
9
|
module Validator
|
|
10
10
|
TEMPLATES = {
|
|
11
11
|
# IP address regex, source https://regexr.com/38odc
|
|
@@ -23,8 +23,6 @@ module RSlog
|
|
|
23
23
|
puts
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
private
|
|
27
|
-
|
|
28
26
|
def self.valid?(source)
|
|
29
27
|
return :valid if source.all? TEMPLATES[:ip]
|
|
30
28
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rslog
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.16
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrey Eremeev
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-06-
|
|
11
|
+
date: 2021-06-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Ruby script to get overall Statistic for weblog logs!
|
|
14
14
|
email: a.eremeev@outlook.com
|