rslog 0.0.1
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/rslog +5 -0
- data/lib/rslog.rb +23 -0
- data/lib/rslog/container.rb +48 -0
- data/lib/rslog/data_parser.rb +27 -0
- data/lib/rslog/extractor.rb +27 -0
- data/lib/rslog/input_parser.rb +64 -0
- data/lib/rslog/presenter.rb +44 -0
- data/lib/rslog/tools/array.rb +15 -0
- data/lib/rslog/tools/hash.rb +14 -0
- data/lib/rslog/validator.rb +32 -0
- metadata +53 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8772ce859d1a57c687b9c4cb7b8cd55bbddcce5467b4eff8715dc1e734bf77f1
|
|
4
|
+
data.tar.gz: fad32cd0d5a00e854680959383ba962776ee95b243c13b85134ac74227cb651c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ea4482e67eb1709cb5d344ecda30086d508dfb0680777e690f7d598ecb34d504692454cd876ef34f604c85a1fae07705a2dbb49852d71eae307c9eb9b3046254
|
|
7
|
+
data.tar.gz: 40370482e148847bd6135070bbe37383e81d120fd7f2e6a464c2b839435cefc5a4545e5daf7a60797759e249091f3ad7ec41f438099a0906a6d809086556d9ec
|
data/bin/rslog
ADDED
data/lib/rslog.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'rslog/container'
|
|
5
|
+
require_relative 'rslog/input_parser'
|
|
6
|
+
require_relative 'rslog/extractor'
|
|
7
|
+
require_relative 'rslog/validator'
|
|
8
|
+
require_relative 'rslog/data_parser'
|
|
9
|
+
require_relative 'rslog/presenter'
|
|
10
|
+
|
|
11
|
+
# For development
|
|
12
|
+
# require 'pry'
|
|
13
|
+
|
|
14
|
+
class RSlog
|
|
15
|
+
VERSION = '0.0.1'
|
|
16
|
+
|
|
17
|
+
def self.run
|
|
18
|
+
Container.new.process_all
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# For development
|
|
23
|
+
# RSlog.run.talk
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Container
|
|
4
|
+
attr_accessor(*%i[
|
|
5
|
+
argv file_name messages errors data options validator types result
|
|
6
|
+
])
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@errors = []
|
|
10
|
+
@messages = []
|
|
11
|
+
@validator = :ip
|
|
12
|
+
@types = %i[all uniq]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def file_name?
|
|
16
|
+
!file_name.empty? && no_errors?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def data?
|
|
20
|
+
messages.include?('Data in place') && no_errors?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def no_errors?
|
|
24
|
+
errors.empty?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def talk
|
|
28
|
+
puts messages.join("\n")
|
|
29
|
+
puts errors.join("\n")
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def process_all
|
|
34
|
+
InputParser.new(self).execute
|
|
35
|
+
Extractor.new(self).execute if file_name?
|
|
36
|
+
validate_parse_present if data?
|
|
37
|
+
talk
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def validate_parse_present
|
|
42
|
+
Validator.new(self).execute
|
|
43
|
+
types.each do |type|
|
|
44
|
+
DataParser.new(self).execute(type)
|
|
45
|
+
Presenter.new(self).execute(type, :text)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'tools/array'
|
|
4
|
+
require_relative 'tools/hash'
|
|
5
|
+
require_relative 'validator'
|
|
6
|
+
|
|
7
|
+
require 'pry'
|
|
8
|
+
|
|
9
|
+
class DataParser
|
|
10
|
+
# type = :count (default) -> just count in groups
|
|
11
|
+
# type = :uniq -> uniq entries in groups
|
|
12
|
+
def initialize(container)
|
|
13
|
+
@container = container
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def execute(type)
|
|
17
|
+
@container.result =
|
|
18
|
+
@container.data
|
|
19
|
+
.group_by_index(0)
|
|
20
|
+
.count_by_groups(type)
|
|
21
|
+
.to_a
|
|
22
|
+
.sort_by { |item| item[1] }
|
|
23
|
+
.reverse
|
|
24
|
+
.map { |i| i.map(&:to_s).join(' ') }
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'container'
|
|
4
|
+
|
|
5
|
+
class Extractor
|
|
6
|
+
def initialize(container)
|
|
7
|
+
@container = container
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def execute
|
|
11
|
+
check
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def check
|
|
18
|
+
if File.zero?(@container.file_name)
|
|
19
|
+
@container.messages << 'Empty file'
|
|
20
|
+
else
|
|
21
|
+
@container.data = File.open(@container.file_name, 'r').to_a
|
|
22
|
+
@container.messages << 'Data in place'
|
|
23
|
+
end
|
|
24
|
+
rescue StandardError => e
|
|
25
|
+
@container.errors << e
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
class InputParser
|
|
6
|
+
OPTS = {
|
|
7
|
+
version: {
|
|
8
|
+
descr: ['-v', '--version', 'Show version and exit'],
|
|
9
|
+
action: proc do
|
|
10
|
+
puts "#{File.basename($PROGRAM_NAME)}: #{RSlog::VERSION}"
|
|
11
|
+
exit
|
|
12
|
+
end
|
|
13
|
+
},
|
|
14
|
+
help: {
|
|
15
|
+
descr: ['-h', '--help', 'Prints this message and exit'],
|
|
16
|
+
action: proc do |opts|
|
|
17
|
+
puts opts
|
|
18
|
+
exit
|
|
19
|
+
end
|
|
20
|
+
}
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
def initialize(container)
|
|
24
|
+
@container = container
|
|
25
|
+
@argv = container.argv
|
|
26
|
+
@file_name = container.file_name
|
|
27
|
+
@options = container.options
|
|
28
|
+
@errors = container.errors
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def execute
|
|
32
|
+
create_opts
|
|
33
|
+
handle_opts
|
|
34
|
+
handle_args
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
attr_accessor :opts
|
|
41
|
+
|
|
42
|
+
def create_opts
|
|
43
|
+
@opts = OptionParser.new do |opts|
|
|
44
|
+
opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} FILENAME"
|
|
45
|
+
opts.on(*OPTS[:version][:descr]) do
|
|
46
|
+
OPTS[:version][:action].call
|
|
47
|
+
end
|
|
48
|
+
opts.on(*OPTS[:help][:descr]) do
|
|
49
|
+
OPTS[:help][:action].call(opts)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def handle_opts
|
|
55
|
+
@opts.parse!(into: @container.options)
|
|
56
|
+
rescue OptionParser::InvalidOption => e
|
|
57
|
+
@container.errors << e
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def handle_args
|
|
61
|
+
@container.file_name = ARGV.select { |str| str =~ /.\.log|.\.txt/ }[0] || ''
|
|
62
|
+
@container.errors << 'No file_name provided' if @container.file_name.empty?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'tools/hash'
|
|
4
|
+
require_relative 'tools/array'
|
|
5
|
+
|
|
6
|
+
class Presenter
|
|
7
|
+
DECORATORS = {
|
|
8
|
+
all: {
|
|
9
|
+
title:
|
|
10
|
+
'List of webpages with most page views ordered from most views to less',
|
|
11
|
+
suffix: 'visits'
|
|
12
|
+
},
|
|
13
|
+
uniq: {
|
|
14
|
+
title: 'List list of webpages with most unique page views also ordered',
|
|
15
|
+
suffix: 'unique views'
|
|
16
|
+
}
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
def initialize(container)
|
|
20
|
+
@container = container
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def execute(type, formatter)
|
|
24
|
+
@container.messages << format_as(type, formatter)
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def format_as(type, formatter)
|
|
31
|
+
case formatter
|
|
32
|
+
when :text
|
|
33
|
+
[
|
|
34
|
+
DECORATORS[type][:title],
|
|
35
|
+
@container.result.to_multiline_string(DECORATORS[type][:suffix]),
|
|
36
|
+
separator
|
|
37
|
+
].join("\n")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def separator
|
|
42
|
+
'-----------------------------'
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Array
|
|
4
|
+
def group_by_index(index = 0)
|
|
5
|
+
group_by { |str| str.split(' ')[index] }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def to_multiline_string(suffix = '')
|
|
9
|
+
arr = []
|
|
10
|
+
each do |item|
|
|
11
|
+
arr << "#{item} #{suffix}"
|
|
12
|
+
end
|
|
13
|
+
arr.join("\n")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
class Hash
|
|
6
|
+
def count_by_groups(type = :all)
|
|
7
|
+
res = {}
|
|
8
|
+
each do |group_key, arr|
|
|
9
|
+
res[group_key] = arr.size if type == :all
|
|
10
|
+
res[group_key] = Set.new(arr).size if type == :uniq
|
|
11
|
+
end
|
|
12
|
+
res
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'tools/array'
|
|
4
|
+
|
|
5
|
+
class Validator
|
|
6
|
+
TEMPLATES = {
|
|
7
|
+
# IP address regex, source https://regexr.com/38odc
|
|
8
|
+
ip: /\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b/
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
MESSAGES = {
|
|
12
|
+
valid: proc { |validator| "All #{validator.upcase}s are valid" },
|
|
13
|
+
invalid: proc { |validator| "Some #{validator.upcase}s are NOT valid" }
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
def initialize(container)
|
|
17
|
+
@container = container
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def execute
|
|
21
|
+
@container.messages << MESSAGES[validate].call(@container.validator)
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def validate
|
|
28
|
+
return :valid if @container.data.all? TEMPLATES[@container.validator]
|
|
29
|
+
|
|
30
|
+
:invalid
|
|
31
|
+
end
|
|
32
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rslog
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Andrey Eremeev
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2021-03-09 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Ruby script to get overall Statistic for weblog logs!
|
|
14
|
+
email: a.eremeev@outlook.com
|
|
15
|
+
executables:
|
|
16
|
+
- rslog
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- bin/rslog
|
|
21
|
+
- lib/rslog.rb
|
|
22
|
+
- lib/rslog/container.rb
|
|
23
|
+
- lib/rslog/data_parser.rb
|
|
24
|
+
- lib/rslog/extractor.rb
|
|
25
|
+
- lib/rslog/input_parser.rb
|
|
26
|
+
- lib/rslog/presenter.rb
|
|
27
|
+
- lib/rslog/tools/array.rb
|
|
28
|
+
- lib/rslog/tools/hash.rb
|
|
29
|
+
- lib/rslog/validator.rb
|
|
30
|
+
homepage: https://rubygems.org/gems/rslog
|
|
31
|
+
licenses:
|
|
32
|
+
- MIT
|
|
33
|
+
metadata: {}
|
|
34
|
+
post_install_message:
|
|
35
|
+
rdoc_options: []
|
|
36
|
+
require_paths:
|
|
37
|
+
- lib
|
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - "~>"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: 2.6.0
|
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
requirements: []
|
|
49
|
+
rubygems_version: 3.0.3
|
|
50
|
+
signing_key:
|
|
51
|
+
specification_version: 4
|
|
52
|
+
summary: R(uby)S(tatistic)log
|
|
53
|
+
test_files: []
|