page_view_parser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dc62810f4d5cdcc46810bf3cb009e2173e086f40289103c6de060ebef3916bed
4
+ data.tar.gz: 4f5c6a7d74a12615f98c5a8439c26bfcba6c8d04509033322792f8b2d154b7b5
5
+ SHA512:
6
+ metadata.gz: c361c7e10da596db6600f87b500d3a50dff9646d162f6a59dacf14493d9a72e8dbdbc9db672a87197afb9e307df66fe689894a089450bee05a0684ce2c5d9e1b
7
+ data.tar.gz: c196b8b6fa9e18ed3784d4c04cdb22fbba05a4d740d12245627c9a3cd08ef9f97c2fda524bb91b1c5e1a8decddb55894dedba7e025d7e5b47469405e45dad5ca
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'page.rb'
4
+
5
+ # Takes a log file where each line has the format of:
6
+ # PAGE_NAME VISITOR_IP
7
+ # eg.
8
+ # /about 123.456.789.123
9
+ # and turns it into a list of 'page' objects.
10
+ # The target object to turn each line in the log into is
11
+ # expected to be able to receive page: and visitor: keyword arguments.
12
+ class LogParser
13
+ class MalformedLog < StandardError; end
14
+
15
+ def initialize(page_class: Page)
16
+ @page_class = page_class
17
+ end
18
+
19
+ # arranging by path in a hash first instead of straight into an Array to avoid
20
+ # an iteration of the whole list so far on each check for existence. Instead,
21
+ # using the quicker lookup by key to then later just extract the values into
22
+ # the list.
23
+ def parse_from_file(file_path:)
24
+ page_views = {}.tap do |pages|
25
+ File.open(file_path).each do |line|
26
+ build_page line, pages
27
+ end
28
+ end
29
+ [].push(*page_views.values)
30
+ end
31
+
32
+ def parse_from_string(string:)
33
+ page_views = {}.tap do |pages|
34
+ string.split("\n").each do |line|
35
+ build_page line, pages
36
+ end
37
+ end
38
+ [].push(*page_views.values)
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :file_path, :page_class
44
+
45
+ def build_page(line, pages)
46
+ path, visitor = line.split(' ')
47
+ raise MalformedLog, 'Log malformed' if
48
+ path.nil? || visitor.nil?
49
+
50
+ if pages[path].nil? # The current path hasn't been seen before
51
+ pages[path] = page_class.new(path: path, visitors: [visitor])
52
+ else
53
+ pages[path].visitors << visitor
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Represents a record of a page that can be visited, holding a record of the
4
+ # visitors via their ip address
5
+ class Page
6
+ attr_reader :path, :visitors
7
+
8
+ def initialize(path:, visitors: [])
9
+ @path = path
10
+ @visitors = visitors
11
+ end
12
+
13
+ def <<(visitor)
14
+ visitors << visitor
15
+ end
16
+
17
+ def view_count
18
+ visitors.count
19
+ end
20
+
21
+ def unique_view_count
22
+ visitors.uniq.count
23
+ end
24
+
25
+ def to_s(strategy: DEFAULT_PAGE_PRESENTATION_STRATEGY)
26
+ strategy.call(self)
27
+ end
28
+
29
+ DEFAULT_PAGE_PRESENTATION_STRATEGY = proc do |page|
30
+ "#{page.path} visited by #{page.visitors.uniq}"
31
+ end
32
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PageListFormatStrategies
4
+ # Base class that represents the interface all pages format strategy
5
+ # classes must implement.
6
+ class Base
7
+ def initialize(pages:)
8
+ @pages = pages
9
+ end
10
+
11
+ # It is expected that all sub class strategies will override this method and
12
+ # provide it's own formatting algorithm / behaviour. The result however is
13
+ # always an array of the new format the Pages have taken.
14
+ def call
15
+ pages
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :pages
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../page_list_format_strategies/base.rb'
4
+
5
+ module PageListFormatStrategies
6
+ # A presentation strategy that presents a list of pages in descending order
7
+ # by the number of unique views with its unique view count.
8
+ class WithUniqueViewCount < Base
9
+ def call
10
+ presentation_strategy = proc do |page|
11
+ str = ''
12
+ str += "#{page.path} #{page.unique_view_count} "
13
+ str += "unique visit#{page.unique_view_count > 1 ? 's' : ''}"
14
+ str
15
+ end
16
+
17
+ pages.sort_by { |page| -page.unique_view_count }
18
+ .map { |page| page.to_s(strategy: presentation_strategy) }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../page_list_format_strategies/base.rb'
4
+
5
+ module PageListFormatStrategies
6
+ # A presentation strategy that presents a list of page paths in descending
7
+ # order (highest number of views at the top) with their total view count.
8
+ class WithViewCount < Base
9
+ def call
10
+ presentation_strategy = proc do |page|
11
+ "#{page.path} #{page.view_count} visit#{page.view_count > 1 ? 's' : ''}"
12
+ end
13
+
14
+ pages.sort_by { |page| -page.view_count }
15
+ .map { |page| page.to_s(strategy: presentation_strategy) }
16
+ end
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: page_view_parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Hayes-Reed
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: guard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: guard-rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.85'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.85'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.18'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.18'
83
+ description: A simple webserver parser that outputs page count information
84
+ email: john.hayes.reed@gmail.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - lib/log_parser.rb
90
+ - lib/page.rb
91
+ - lib/page_list_format_strategies/base.rb
92
+ - lib/page_list_format_strategies/with_unique_view_count.rb
93
+ - lib/page_list_format_strategies/with_view_count.rb
94
+ homepage:
95
+ licenses: []
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubygems_version: 3.0.6
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Page View Parser
116
+ test_files: []