fileverse 0.1.2 → 0.1.3
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/README.md +6 -3
- data/exe/fileverse +0 -1
- data/lib/fileverse/cli.rb +26 -5
- data/lib/fileverse/parser.rb +238 -212
- data/lib/fileverse/version.rb +1 -1
- data/lib/fileverse.rb +0 -5
- data/sample.gif +0 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f609f1f8013055b606d5202c6acadee64bf13e51b23d157a06d5ae86ead7a920
|
4
|
+
data.tar.gz: 691d5a36f90d9c06798ac40823f832134524997a4eaae936e33659abf0914d6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51aa99351f24296543e10893ca5977c07c9042e2d30b60e684637967e98a60ce2cbdb88870a2f2e49d0c2f5ca7b898d88ef0a05a757b756704ca2742e1fef93b
|
7
|
+
data.tar.gz: a5810560fe7c016d57c622afe108e54322e95caf658d95989a2c0db6dd57db192639329831216b7ecb4e3a9af7ae9747801b4094bd3c778bf7cb7a3eda1ae52a
|
data/README.md
CHANGED
@@ -12,14 +12,17 @@ A simple ruby cli tool for keeping different versions of a file.
|
|
12
12
|
|
13
13
|
## Commands / Shortcuts
|
14
14
|
- snap {file_path} (s)
|
15
|
-
- preview
|
16
|
-
- preview
|
15
|
+
- preview --bwd {file_path} (p)
|
16
|
+
- preview --fwd {file_path} (p)
|
17
17
|
- preview --name="" {file_path} (p)
|
18
18
|
- preview --index=0 {file_path} (p)
|
19
19
|
- reset {file_path} (x)
|
20
|
-
-
|
20
|
+
- summary {file_path} (sm)
|
21
21
|
- restore {file_path} (r)
|
22
22
|
|
23
|
+
## Sample
|
24
|
+

|
25
|
+
|
23
26
|
## Development
|
24
27
|
|
25
28
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/exe/fileverse
CHANGED
data/lib/fileverse/cli.rb
CHANGED
@@ -6,22 +6,22 @@ module Fileverse
|
|
6
6
|
# CLI class
|
7
7
|
class CLI < Thor
|
8
8
|
desc "snap file content", "store current file content"
|
9
|
+
options template: :boolean, name: :string
|
9
10
|
def snap(path)
|
10
11
|
setup path
|
11
12
|
@parser.parse
|
12
|
-
@parser.add_snapshot(Files.read(@path))
|
13
|
+
@parser.add_snapshot(Files.read(@path), is_template: options[:template], template_name: options[:name])
|
13
14
|
Files.write_content(@path)
|
14
15
|
Files.write_content(@hidden_path, @parser.to_writable_lines)
|
15
16
|
end
|
16
17
|
map "s" => "snap"
|
17
18
|
|
18
19
|
desc "restore content", "restore content in the current cursor"
|
20
|
+
options template: :boolean, name: :string
|
19
21
|
def restore(path)
|
20
22
|
setup path
|
21
23
|
@parser.parse
|
22
|
-
|
23
|
-
@parser.remove_cursor_snapshot
|
24
|
-
Files.write_content(@hidden_path, @parser.to_writable_lines)
|
24
|
+
options[:template] ? restore_template : restore_snapshot
|
25
25
|
end
|
26
26
|
map "r" => "restore"
|
27
27
|
|
@@ -49,15 +49,36 @@ module Fileverse
|
|
49
49
|
end
|
50
50
|
map "x" => "reset"
|
51
51
|
|
52
|
+
desc "summary", "return all the summary of snapshots"
|
53
|
+
def summary(path)
|
54
|
+
setup path
|
55
|
+
@parser.parse_head
|
56
|
+
puts @parser.summary
|
57
|
+
end
|
58
|
+
map "sm" => "summary"
|
59
|
+
|
52
60
|
private
|
53
61
|
|
54
62
|
def setup(path)
|
55
63
|
@path = Files.expand_path(path)
|
56
64
|
@hidden_path = Files.expand_hidden_path(path)
|
57
|
-
@parser = Parser
|
65
|
+
@parser = Parser.new(@hidden_path)
|
58
66
|
@previewer = Previewer.new(@path)
|
59
67
|
end
|
60
68
|
|
69
|
+
def restore_snapshot
|
70
|
+
Files.write_content(@path, @parser.cursor_content)
|
71
|
+
@parser.remove_cursor_snapshot
|
72
|
+
Files.write_content(@hidden_path, @parser.to_writable_lines)
|
73
|
+
end
|
74
|
+
|
75
|
+
def restore_template
|
76
|
+
template_content = @parser.template_content(options[:name])
|
77
|
+
return if template_content.nil?
|
78
|
+
|
79
|
+
Files.write_content(@path, template_content)
|
80
|
+
end
|
81
|
+
|
61
82
|
def update_preview_content
|
62
83
|
if options[:bwd]
|
63
84
|
@parser.decrement_cursor
|
data/lib/fileverse/parser.rb
CHANGED
@@ -1,260 +1,286 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Fileverse
|
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
|
-
end
|
4
|
+
# Fileverse::Parser : is the main parser for the storage file.
|
5
|
+
# Lets take a storage file example:
|
6
|
+
#
|
7
|
+
# == Storage file Example content
|
8
|
+
#
|
9
|
+
# <######0
|
10
|
+
# template>shot> 6 ~> 9
|
11
|
+
# 9 ~> 12
|
12
|
+
# 12 ~> 15
|
13
|
+
# 15 ~> 18
|
14
|
+
# ######>
|
15
|
+
# // New file
|
16
|
+
# // Write here
|
17
|
+
# // When you can
|
18
|
+
# File 1
|
19
|
+
# Content 1 of file 1
|
20
|
+
# Content 2 of file 1
|
21
|
+
# File 2
|
22
|
+
# Content 1 of file 2
|
23
|
+
# Content 2 of file 2
|
24
|
+
# File 3
|
25
|
+
# Content 1 of file 3
|
26
|
+
# Content 2 of file 3
|
27
|
+
#
|
28
|
+
#
|
29
|
+
# == Example end
|
30
|
+
#
|
31
|
+
# It uses the head section which is what is between "<######{cursor}" and "######>".
|
32
|
+
# It starts with "templates" and then "files". Each has a range which is represented like "{start} ~> {stop}".
|
33
|
+
# templates are represented as "template>{name}>{range}". While files are just represented as "{range}".
|
34
|
+
# so the above example will parse as follows:
|
35
|
+
#
|
36
|
+
# - template>shot> 6 ~> 9
|
37
|
+
#
|
38
|
+
# =======================================
|
39
|
+
# | // New file
|
40
|
+
# | // Write here
|
41
|
+
# | // When you can
|
42
|
+
# ======================================
|
43
|
+
#
|
44
|
+
# - 9 ~> 12
|
45
|
+
#
|
46
|
+
# =======================================
|
47
|
+
# | File 1
|
48
|
+
# | Content 1 of file 1
|
49
|
+
# | Content 2 of file 1
|
50
|
+
# ======================================
|
51
|
+
#
|
52
|
+
# - 12 ~> 15
|
53
|
+
#
|
54
|
+
# =======================================
|
55
|
+
# | File 2
|
56
|
+
# | Content 1 of file 2
|
57
|
+
# | Content 2 of file 2
|
58
|
+
# ======================================
|
59
|
+
#
|
60
|
+
# - 15 ~> 18
|
61
|
+
#
|
62
|
+
# =======================================
|
63
|
+
# | File 3
|
64
|
+
# | Content 1 of file 3
|
65
|
+
# | Content 2 of file 3
|
66
|
+
# ======================================
|
67
|
+
#
|
68
|
+
class Parser # rubocop:disable Metrics/ClassLength
|
69
|
+
START_TAG = "<######"
|
70
|
+
CLOSE_TAG = "######>"
|
71
|
+
|
72
|
+
attr_reader :path, :cursor, :line_index
|
73
|
+
|
74
|
+
def initialize(path)
|
75
|
+
@path = path
|
76
|
+
@iterator = File.foreach(path, chomp: true)
|
77
|
+
@line_index = 0
|
78
|
+
@cursor = -1
|
79
|
+
@snapshots = []
|
80
|
+
@templates = []
|
81
|
+
end
|
83
82
|
|
84
|
-
|
85
|
-
|
83
|
+
def parse
|
84
|
+
return unless File.exist?(@path) && !peek_line.nil?
|
86
85
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
86
|
+
verify_first_header
|
87
|
+
parse_header_template_lines
|
88
|
+
parse_header_snapshot_lines
|
89
|
+
parse_snapshots
|
91
90
|
|
92
|
-
|
93
|
-
|
91
|
+
raise CorruptFormat, " Content remains after parsing." unless peek_line.nil?
|
92
|
+
end
|
94
93
|
|
95
|
-
|
96
|
-
|
97
|
-
|
94
|
+
def snapshot_count
|
95
|
+
@snapshots.length
|
96
|
+
end
|
98
97
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
98
|
+
def add_snapshot(content, is_template: false, template_name: nil)
|
99
|
+
prev_snapshot = last_snapshot is_template: is_template
|
100
|
+
start = prev_snapshot&.stop || 3
|
101
|
+
snapshot = Snapshot.new(start, start + content.length, template_name)
|
102
|
+
snapshot.content = content
|
103
|
+
prev_snapshot&.next_snapshot = snapshot
|
104
|
+
snapshot.next_snapshot = @snapshots[0] if is_template
|
105
|
+
(is_template ? @templates : @snapshots).push(snapshot)
|
106
|
+
reset
|
107
|
+
end
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
end
|
109
|
+
def cursor_content
|
110
|
+
raise InvalidCursorPointer if @cursor.negative? || @cursor > @snapshots.length
|
112
111
|
|
113
|
-
|
114
|
-
|
115
|
-
return unless snapshot
|
112
|
+
@snapshots[@cursor].content
|
113
|
+
end
|
116
114
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
reset
|
121
|
-
end
|
115
|
+
def template_content(name)
|
116
|
+
@templates.find { |template| template.name == name }&.content
|
117
|
+
end
|
122
118
|
|
123
|
-
|
124
|
-
|
125
|
-
|
119
|
+
def remove_cursor_snapshot
|
120
|
+
snapshot = @snapshots[@cursor]
|
121
|
+
return unless snapshot
|
126
122
|
|
127
|
-
|
128
|
-
|
123
|
+
snapshot_before = @snapshots[@cursor - 1]
|
124
|
+
snapshot_before.next_snapshot = snapshot.next_snapshot if snapshot_before
|
125
|
+
@snapshots = @snapshots[0, @cursor].concat(@snapshots[@cursor + 1..])
|
126
|
+
reset
|
127
|
+
end
|
129
128
|
|
130
|
-
|
131
|
-
|
129
|
+
def to_writable_lines
|
130
|
+
[*head_lines, *template_lines, *snapshot_lines]
|
131
|
+
end
|
132
132
|
|
133
|
-
|
134
|
-
|
133
|
+
def increment_cursor
|
134
|
+
raise InvalidCursorPointer if @cursor + 1 >= @snapshots.length
|
135
135
|
|
136
|
-
|
137
|
-
|
136
|
+
@cursor += 1
|
137
|
+
end
|
138
138
|
|
139
|
-
|
140
|
-
|
139
|
+
def decrement_cursor
|
140
|
+
raise InvalidCursorPointer if (@cursor - 1).negative?
|
141
141
|
|
142
|
-
|
143
|
-
|
142
|
+
@cursor -= 1
|
143
|
+
end
|
144
144
|
|
145
|
-
|
146
|
-
|
147
|
-
@snapshots[0]&.update_start @snapshots.length + 2
|
148
|
-
end
|
145
|
+
def cursor=(value)
|
146
|
+
raise InvalidCursorPointer if value.negative? || value > @snapshots.length
|
149
147
|
|
150
|
-
|
148
|
+
@cursor = value
|
149
|
+
end
|
151
150
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
151
|
+
def reset
|
152
|
+
@cursor = @snapshots.length - 1
|
153
|
+
all_snapshots = [*@templates, *@snapshots]
|
154
|
+
all_snapshots[0]&.update_start all_snapshots.length + 2
|
155
|
+
end
|
156
156
|
|
157
|
-
|
158
|
-
|
157
|
+
def parse_head
|
158
|
+
return unless File.exist?(@path) && !peek_line.nil?
|
159
159
|
|
160
|
-
|
161
|
-
|
162
|
-
|
160
|
+
verify_first_header
|
161
|
+
parse_header_template_lines
|
162
|
+
parse_header_snapshot_lines
|
163
|
+
end
|
163
164
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
165
|
+
def summary
|
166
|
+
[
|
167
|
+
"#{@snapshots.length} snapshots",
|
168
|
+
"#{@templates.length} templates",
|
169
|
+
(@templates.length.positive? ? "template name: #{@templates.map(&:name).join(",")}" : "").to_s
|
170
|
+
]
|
171
|
+
end
|
168
172
|
|
169
|
-
|
170
|
-
loop do
|
171
|
-
line = next_line
|
172
|
-
unless /\A\s*(?<start>\d+)\s*~>\s*(?<stop>\d+)\s*\z/ =~ line
|
173
|
-
break if line == CLOSE_TAG
|
173
|
+
private
|
174
174
|
|
175
|
-
|
176
|
-
|
177
|
-
|
175
|
+
def last_snapshot(is_template: false)
|
176
|
+
is_template ? @templates[-1] : @snapshots[-1] || @templates[-1]
|
177
|
+
end
|
178
178
|
|
179
|
-
|
180
|
-
|
181
|
-
|
179
|
+
def verify_first_header
|
180
|
+
first_line = next_line
|
181
|
+
/\A<\#{6}(?<cursor>-?\d+)\z/ =~ first_line
|
182
|
+
raise CorruptFormat, " Error parsing header" unless cursor
|
182
183
|
|
183
|
-
|
184
|
-
|
185
|
-
[*@templates, *@snapshots].each do |snap|
|
186
|
-
raise CorruptFormat, " Wrong indexing in header." if line_index != snap.start
|
184
|
+
@cursor = cursor.to_i
|
185
|
+
end
|
187
186
|
|
188
|
-
|
187
|
+
def parse_header_template_lines
|
188
|
+
loop do
|
189
|
+
break unless /\A\s*template>(?<name>\w+)>\s*(?<start>\d+)\s*~>\s*(?<stop>\d+)\s*\z/ =~ peek_line
|
189
190
|
|
190
|
-
|
191
|
-
|
192
|
-
end
|
191
|
+
next_line
|
192
|
+
@templates.push(Snapshot.new(start.to_i, stop.to_i, name))
|
193
193
|
end
|
194
|
+
end
|
194
195
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
196
|
+
def parse_header_snapshot_lines
|
197
|
+
loop do
|
198
|
+
line = next_line
|
199
|
+
unless /\A\s*(?<start>\d+)\s*~>\s*(?<stop>\d+)\s*\z/ =~ line
|
200
|
+
break if line == CLOSE_TAG
|
200
201
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
rescue StopIteration
|
205
|
-
raise CorruptFormat, " No content to parse."
|
206
|
-
end
|
202
|
+
raise CorruptFormat
|
203
|
+
end
|
204
|
+
raise CorruptFormat if stop.to_i < start.to_i
|
207
205
|
|
208
|
-
|
209
|
-
@iterator.peek
|
210
|
-
rescue StopIteration
|
211
|
-
nil
|
206
|
+
@snapshots.push Snapshot.new(start.to_i, stop.to_i)
|
212
207
|
end
|
208
|
+
end
|
213
209
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
*@snapshots.map { |snap| "#{snap.start} ~> #{snap.stop}" },
|
219
|
-
CLOSE_TAG
|
220
|
-
]
|
221
|
-
end
|
210
|
+
def parse_snapshots
|
211
|
+
last_snap = nil
|
212
|
+
[*@templates, *@snapshots].each do |snap|
|
213
|
+
raise CorruptFormat, " Wrong indexing in header." if line_index != snap.start
|
222
214
|
|
223
|
-
|
224
|
-
@templates.map(&:content).flatten
|
225
|
-
end
|
215
|
+
snap.content = parse_snap_content(snap)
|
226
216
|
|
227
|
-
|
228
|
-
|
217
|
+
last_snap&.next_snapshot = snap
|
218
|
+
last_snap = snap
|
229
219
|
end
|
220
|
+
end
|
230
221
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
222
|
+
def parse_snap_content(snap)
|
223
|
+
result = []
|
224
|
+
(snap.stop - snap.start).times { result.push next_line }
|
225
|
+
result
|
226
|
+
end
|
235
227
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
228
|
+
def next_line
|
229
|
+
@line_index += 1
|
230
|
+
@iterator.next
|
231
|
+
rescue StopIteration
|
232
|
+
raise CorruptFormat, " No content to parse."
|
233
|
+
end
|
241
234
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
235
|
+
def peek_line
|
236
|
+
@iterator.peek
|
237
|
+
rescue StopIteration
|
238
|
+
nil
|
239
|
+
end
|
246
240
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
241
|
+
def head_lines
|
242
|
+
[
|
243
|
+
"#{START_TAG}#{cursor}",
|
244
|
+
*@templates.map { |template| "template>#{template.name}> #{template.start} ~> #{template.stop}" },
|
245
|
+
*@snapshots.map { |snap| "#{snap.start} ~> #{snap.stop}" },
|
246
|
+
CLOSE_TAG
|
247
|
+
]
|
248
|
+
end
|
251
249
|
|
252
|
-
|
250
|
+
def template_lines
|
251
|
+
@templates.map(&:content).flatten
|
252
|
+
end
|
253
253
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
254
|
+
def snapshot_lines
|
255
|
+
@snapshots.map(&:content).flatten
|
256
|
+
end
|
257
|
+
|
258
|
+
# Snapshot for each file
|
259
|
+
class Snapshot
|
260
|
+
attr_reader :start, :stop, :name, :content
|
261
|
+
attr_accessor :next_snapshot
|
262
|
+
|
263
|
+
def initialize(start, stop, name = nil)
|
264
|
+
@start = start
|
265
|
+
@stop = stop
|
266
|
+
@name = name
|
267
|
+
end
|
268
|
+
|
269
|
+
def content=(value)
|
270
|
+
@content = value
|
271
|
+
update_stop
|
272
|
+
end
|
273
|
+
|
274
|
+
def update_start(new_start)
|
275
|
+
@start = new_start
|
276
|
+
update_stop
|
277
|
+
end
|
278
|
+
|
279
|
+
private
|
280
|
+
|
281
|
+
def update_stop
|
282
|
+
@stop = start + content.length
|
283
|
+
next_snapshot&.update_start @stop
|
258
284
|
end
|
259
285
|
end
|
260
286
|
end
|
data/lib/fileverse/version.rb
CHANGED
data/lib/fileverse.rb
CHANGED
data/sample.gif
ADDED
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fileverse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Unegbu Kingsley
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -49,6 +49,7 @@ files:
|
|
49
49
|
- lib/fileverse/parser.rb
|
50
50
|
- lib/fileverse/previewer.rb
|
51
51
|
- lib/fileverse/version.rb
|
52
|
+
- sample.gif
|
52
53
|
- sig/fileverse.rbs
|
53
54
|
homepage: https://github.com/urchmaney/fileverse
|
54
55
|
licenses:
|