/
archive.rb
184 lines (140 loc) · 3.93 KB
/
archive.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
module HAR
class Archive
include Serializable
def self.from_string(str, uri = nil)
new JSON.parse(str), uri
end
def self.from_file(path_or_io)
case path_or_io
when String
from_string File.read(path_or_io), path_or_io
when IO
from_string path_or_io.read, path_or_io.to_s
else
unless path_or_io.respond_to?(:to_io)
raise TypeError, "expected String, IO or #to_io"
end
from_file path_or_io.to_io
end
end
def self.by_merging(hars)
hars = hars.dup
result = hars.shift or raise ArgumentError, "no HARs given"
result = from_file(result) unless result.kind_of? self
hars.each do |har|
result.merge! har.kind_of?(self) ? har : from_file(har)
end
result
end
# @api private
def self.schemas
@schemas ||= {}
end
# @api private
def self.add_schema(path)
data = JSON.parse(File.read(path))
id = data.fetch('id')
schemas[id] = data
end
Dir[File.expand_path("../schemas/*.json", __FILE__)].each do |path|
add_schema path
end
attr_reader :uri
def initialize(input, uri = nil)
@data = input
@uri = uri
end
def view
Viewer.new([self]).show
end
def pages
@pages ||= raw_log.fetch('pages').map { |page|
Page.new page, entries_for(page['id'])
}
end
def entries
@entries ||= raw_entries.map { |e| Entry.new(e) }
end
def entries_before(time)
raise TypeError, "expected Time" unless time.is_a?(Time)
entries.select do |entry|
return false unless entry.time
entry.started_date_time + entry.time / 1000.0 <= time
end
end
# create a new archive by merging this and another archive
def merge(other)
assert_archive other
data = deep_clone(@data)
merge_data data, other.as_json, other.uri
self.class.new data
end
# destructively merge this with the given archive
def merge!(other)
assert_archive other
clear_caches
merge_data @data, other.as_json, other.uri
nil
end
def save_to(path)
File.open(path, "w") { |io| io << @data.to_json }
end
def valid?
Jschematic.validate @data, log_type_schema, :debug => true, :context => self.class.schemas.values
end
def validate!
Jschematic.validate! @data, log_type_schema, :debug => true, :context => self.class.schemas.values
nil
rescue Jschematic::ValidationError => ex
msg = ex.message
msg = "#{@uri}: #{msg}" if @uri
raise ValidationError, msg
end
private
def log_type_schema
@schema ||= self.class.schemas.fetch('logType')
end
def merge_data(left, right, uri)
log = left.fetch('log')
other_log = right.fetch('log')
pages = log.fetch('pages')
entries = log.fetch('entries')
other_pages = other_log.fetch("pages")
other_entries = other_log.fetch("entries")
if uri
deep_clone(other_pages).each do |page|
c = page['comment'] ||= ''
c << "(merged from #{File.basename uri})"
pages << page
end
else
pages.concat other_pages
end
entries.concat other_entries
end
def assert_archive(other)
unless other.kind_of?(self.class)
raise TypeError, "expected #{self.class}"
end
end
def clear_caches
@raw_entries = @entries_map = @pages = @entries = @log = nil
end
def entries_for(page_id)
entries = entries_map[page_id] || []
entries.map { |e| Entry.new(e) }
end
def raw_entries
@raw_entries ||= raw_log.fetch('entries')
end
def raw_log
@raw_log ||= @data.fetch 'log'
end
def entries_map
@entries_map ||= raw_entries.group_by { |e| e['pageref'] }
end
def deep_clone(obj)
Marshal.load Marshal.dump(obj)
end
end # Archive
end # HAR