fileverse 0.1.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 573a4f8f540d7cdbd5bbdf4f512f140f72f1cdf446f43cc514ca41ae7017df0e
4
- data.tar.gz: 5273cc14c2996fff2b767a2345fab8d311e18c237af069c75372c90d60b6d9b8
3
+ metadata.gz: f609f1f8013055b606d5202c6acadee64bf13e51b23d157a06d5ae86ead7a920
4
+ data.tar.gz: 691d5a36f90d9c06798ac40823f832134524997a4eaae936e33659abf0914d6a
5
5
  SHA512:
6
- metadata.gz: '0980b59c9f3808f14da4b03ce101196a9f5e2246db4b64073e18c435fd63b765b79810cf82b0a7fa39ecee8c9bd592878808008ccd78df3558d39a8768d97cce'
7
- data.tar.gz: 2030aa26e26b658eac85d65ec94f6719f137f06db41d011767e2ea07067d9dbbcb1645313c5cd8d0937dbbc8cfc26bb4453e7c7c5e75d1ba7297430906f48285
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 --<< {file_path} (p)
16
- - preview -->> {file_path} (p)
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
- - snapshots {file_path} (c)
20
+ - summary {file_path} (sm)
21
21
  - restore {file_path} (r)
22
22
 
23
+ ## Sample
24
+ ![Sample](./sample.gif)
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/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
- Files.write_content(@path, @parser.cursor_content)
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::Header.new(@hidden_path)
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
@@ -1,260 +1,286 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fileverse
4
- module Parser
5
- # Fileverse::Parser::Header : is the main parser for the storage file.
6
- # Lets take a storage file example:
7
- #
8
- # == Storage file Example content
9
- #
10
- # <######0
11
- # template>shot> 6 ~> 9
12
- # 9 ~> 12
13
- # 12 ~> 15
14
- # 15 ~> 18
15
- # ######>
16
- # // New file
17
- # // Write here
18
- # // When you can
19
- # File 1
20
- # Content 1 of file 1
21
- # Content 2 of file 1
22
- # File 2
23
- # Content 1 of file 2
24
- # Content 2 of file 2
25
- # File 3
26
- # Content 1 of file 3
27
- # Content 2 of file 3
28
- #
29
- #
30
- # == Example end
31
- #
32
- # It uses the head section which is what is between "<######{cursor}" and "######>".
33
- # It starts with "templates" and then "files". Each has a range which is represented like "{start} ~> {stop}".
34
- # templates are represented as "template>{name}>{range}". While files are just represented as "{range}".
35
- # so the above example will parse as follows:
36
- #
37
- # - template>shot> 6 ~> 9
38
- #
39
- # =======================================
40
- # | // New file
41
- # | // Write here
42
- # | // When you can
43
- # ======================================
44
- #
45
- # - 9 ~> 12
46
- #
47
- # =======================================
48
- # | File 1
49
- # | Content 1 of file 1
50
- # | Content 2 of file 1
51
- # ======================================
52
- #
53
- # - 12 ~> 15
54
- #
55
- # =======================================
56
- # | File 2
57
- # | Content 1 of file 2
58
- # | Content 2 of file 2
59
- # ======================================
60
- #
61
- # - 15 ~> 18
62
- #
63
- # =======================================
64
- # | File 3
65
- # | Content 1 of file 3
66
- # | Content 2 of file 3
67
- # ======================================
68
- #
69
- class Header # rubocop:disable Metrics/ClassLength
70
- START_TAG = "<######"
71
- CLOSE_TAG = "######>"
72
-
73
- attr_reader :path, :cursor, :line_index
74
-
75
- def initialize(path)
76
- @path = path
77
- @iterator = File.foreach(path, chomp: true)
78
- @line_index = 0
79
- @cursor = -1
80
- @snapshots = []
81
- @templates = []
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
- def parse
85
- return if peek_line.nil?
83
+ def parse
84
+ return unless File.exist?(@path) && !peek_line.nil?
86
85
 
87
- verify_first_header
88
- parse_header_template_lines
89
- parse_header_snapshot_lines
90
- parse_snapshots
86
+ verify_first_header
87
+ parse_header_template_lines
88
+ parse_header_snapshot_lines
89
+ parse_snapshots
91
90
 
92
- raise CorruptFormat, " Content remains after parsing." unless peek_line.nil?
93
- end
91
+ raise CorruptFormat, " Content remains after parsing." unless peek_line.nil?
92
+ end
94
93
 
95
- def snapshot_count
96
- @snapshots.length
97
- end
94
+ def snapshot_count
95
+ @snapshots.length
96
+ end
98
97
 
99
- def add_snapshot(content)
100
- last_snapshot = @snapshots[-1]
101
- start = last_snapshot&.stop || 3
102
- snapshot = Snapshot.new(start, start + content.length)
103
- snapshot.content = content
104
- last_snapshot&.next_snapshot = snapshot
105
- @snapshots.push(snapshot)
106
- reset
107
- end
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
- def cursor_content
110
- @snapshots[@cursor]&.content || []
111
- end
109
+ def cursor_content
110
+ raise InvalidCursorPointer if @cursor.negative? || @cursor > @snapshots.length
112
111
 
113
- def remove_cursor_snapshot
114
- snapshot = @snapshots[@cursor]
115
- return unless snapshot
112
+ @snapshots[@cursor].content
113
+ end
116
114
 
117
- snapshot_before = @snapshots[@cursor - 1]
118
- snapshot_before.next_snapshot = snapshot.next_snapshot if snapshot_before
119
- @snapshots = @snapshots[0, @cursor].concat(@snapshots[@cursor + 1..])
120
- reset
121
- end
115
+ def template_content(name)
116
+ @templates.find { |template| template.name == name }&.content
117
+ end
122
118
 
123
- def to_writable_lines
124
- [*head_lines, *template_lines, *snapshot_lines]
125
- end
119
+ def remove_cursor_snapshot
120
+ snapshot = @snapshots[@cursor]
121
+ return unless snapshot
126
122
 
127
- def increment_cursor
128
- raise InvalidCursorPointer if @cursor + 1 >= @snapshots.length
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
- @cursor += 1
131
- end
129
+ def to_writable_lines
130
+ [*head_lines, *template_lines, *snapshot_lines]
131
+ end
132
132
 
133
- def decrement_cursor
134
- raise InvalidCursorPointer if (@cursor - 1).negative?
133
+ def increment_cursor
134
+ raise InvalidCursorPointer if @cursor + 1 >= @snapshots.length
135
135
 
136
- @cursor -= 1
137
- end
136
+ @cursor += 1
137
+ end
138
138
 
139
- def cursor=(value)
140
- raise InvalidCursorPointer if value.negative? || value > @snapshots.length
139
+ def decrement_cursor
140
+ raise InvalidCursorPointer if (@cursor - 1).negative?
141
141
 
142
- @cursor = value
143
- end
142
+ @cursor -= 1
143
+ end
144
144
 
145
- def reset
146
- @cursor = @snapshots.length - 1
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
- private
148
+ @cursor = value
149
+ end
151
150
 
152
- def verify_first_header
153
- first_line = next_line
154
- /\A<\#{6}(?<cursor>\d+)\z/ =~ first_line
155
- raise CorruptFormat unless cursor
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
- @cursor = cursor.to_i
158
- end
157
+ def parse_head
158
+ return unless File.exist?(@path) && !peek_line.nil?
159
159
 
160
- def parse_header_template_lines
161
- loop do
162
- break unless /\A\s*template>(?<name>\w+)>\s*(?<start>\d+)\s*~>\s*(?<stop>\d+)\s*\z/ =~ peek_line
160
+ verify_first_header
161
+ parse_header_template_lines
162
+ parse_header_snapshot_lines
163
+ end
163
164
 
164
- next_line
165
- @templates.push(Snapshot.new(start.to_i, stop.to_i, name))
166
- end
167
- end
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
- def parse_header_snapshot_lines
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
- raise CorruptFormat
176
- end
177
- raise CorruptFormat if stop.to_i < start.to_i
175
+ def last_snapshot(is_template: false)
176
+ is_template ? @templates[-1] : @snapshots[-1] || @templates[-1]
177
+ end
178
178
 
179
- @snapshots.push Snapshot.new(start.to_i, stop.to_i)
180
- end
181
- end
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
- def parse_snapshots
184
- last_snap = nil
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
- snap.content = parse_snap_content(snap)
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
- last_snap&.next_snapshot = snap
191
- last_snap = snap
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
- def parse_snap_content(snap)
196
- result = []
197
- (snap.stop - snap.start).times { result.push next_line }
198
- result
199
- end
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
- def next_line
202
- @line_index += 1
203
- @iterator.next
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
- def peek_line
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
- def head_lines
215
- [
216
- "#{START_TAG}#{cursor}",
217
- *@templates.map { |template| "template>#{template.name}> #{template.start} ~> #{template.stop}" },
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
- def template_lines
224
- @templates.map(&:content).flatten
225
- end
215
+ snap.content = parse_snap_content(snap)
226
216
 
227
- def snapshot_lines
228
- @snapshots.map(&:content).flatten
217
+ last_snap&.next_snapshot = snap
218
+ last_snap = snap
229
219
  end
220
+ end
230
221
 
231
- # Snapshot for each file
232
- class Snapshot
233
- attr_reader :start, :stop, :name, :content
234
- attr_accessor :next_snapshot
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
- def initialize(start, stop, name = nil)
237
- @start = start
238
- @stop = stop
239
- @name = name
240
- end
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
- def content=(value)
243
- @content = value
244
- update_stop
245
- end
235
+ def peek_line
236
+ @iterator.peek
237
+ rescue StopIteration
238
+ nil
239
+ end
246
240
 
247
- def update_start(new_start)
248
- @start = new_start
249
- update_stop
250
- end
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
- private
250
+ def template_lines
251
+ @templates.map(&:content).flatten
252
+ end
253
253
 
254
- def update_stop
255
- @stop = start + content.length
256
- next_snapshot&.update_start @stop
257
- end
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fileverse
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/fileverse.rb CHANGED
@@ -8,11 +8,6 @@ require_relative "fileverse/previewer"
8
8
 
9
9
  # Parent module
10
10
  module Fileverse
11
- def self.create_hidden_file(path)
12
- File.open(path, "w") do |writer|
13
- writer.write(inital_header)
14
- end
15
- end
16
11
  end
17
12
 
18
13
  require_relative "fileverse/cli"
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.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-02 00:00:00.000000000 Z
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: