DATA.save 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 82028c8bd9bc63cab5bdb572528e5ac79a5a452f
4
+ data.tar.gz: d258af74d79e3a80b807346b0b02f57b8ca9e619
5
+ SHA512:
6
+ metadata.gz: eec3648ff909e617dd11f301463e1e5480d376a3924d99cd9c342f755bf5f0af88f92e643968a7dd9962f54873828c328d75078530ee63550ed35388aa7a99e9
7
+ data.tar.gz: 8552ca116db426f91f90167012cd18cdf05fe5fc76268d83478abcda766d0e40a989161a099cd8e13b7dcc95d2d3ef4abc69deebe6af797014aa13c56c6c8ea4
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ **.gem
data/DATA.save.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ require_relative "lib/DATA.save"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "DATA.save"
5
+ s.version = DataSave::VERSION
6
+ s.authors = ["Josh Cheek"]
7
+ s.email = ["josh.cheek@gmail.com"]
8
+ s.homepage = "https://github.com/JoshCheek/DATA.save"
9
+ s.summary = %q{Store shit in your script's DATA segment}
10
+ s.description = %q{Store shit in your script's DATA segment}
11
+ s.license = "WTFPL"
12
+
13
+ s.rubyforge_project = "seeing_is_believing"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- spec/*`.split("\n")
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_development_dependency "rspec", "~> 3.2"
20
+ end
data/Readme.md ADDED
@@ -0,0 +1,67 @@
1
+ `DATA.save`
2
+ ===========
3
+
4
+ Stored shit in your script's data segment.
5
+
6
+
7
+ Install
8
+ -------
9
+
10
+ ```sh
11
+ $ gem install DATA.save
12
+ ```
13
+
14
+
15
+ Example
16
+ -------
17
+
18
+ A program that records how many times it's been run:
19
+
20
+ ```ruby
21
+ require 'DATA.save'
22
+
23
+ run_count = DATA.load.to_i
24
+ run_count += 1
25
+ DATA.save run_count
26
+
27
+ puts "Run count: #{run_count}"
28
+
29
+ __END__
30
+ 0
31
+ ```
32
+
33
+ When we invoke it, it increments the counter,
34
+ which it stores in its `DATA` segment.
35
+
36
+ ```sh
37
+ # run it 3 times
38
+ $ ruby count_runs.rb
39
+ Run count: 1
40
+
41
+ $ ruby count_runs.rb
42
+ Run count: 2
43
+
44
+ $ ruby count_runs.rb
45
+ Run count: 3
46
+
47
+ # now check out the file
48
+ $ cat count_runs.rb
49
+ require 'DATA.save'
50
+
51
+ run_count = DATA.load.to_i
52
+ run_count += 1
53
+ DATA.save run_count
54
+
55
+ puts "Run count: #{run_count}"
56
+
57
+ __END__
58
+ 3
59
+ ```
60
+
61
+
62
+ License
63
+ -------
64
+
65
+ Just do what the fuck you want to.
66
+
67
+ [http://www.wtfpl.net/about/](http://www.wtfpl.net/about/)
data/lib/DATA.save.rb ADDED
@@ -0,0 +1,47 @@
1
+ class DataSave
2
+ VERSION = '0.0.1'
3
+
4
+ def self.for(data_segment)
5
+ new data_segment.path, data_segment.pos
6
+ end
7
+
8
+ def self.on(data_segment)
9
+ store = DataSave.for data_segment
10
+ data_segment.define_singleton_method(:load) { store.load }
11
+ data_segment.define_singleton_method(:save) { |data| store.save data }
12
+ data_segment.define_singleton_method(:update) { |&block| store.update(&block) }
13
+ end
14
+
15
+ def initialize(path, offset)
16
+ self.path = path
17
+ self.offset = offset
18
+ end
19
+
20
+ def load
21
+ open_segment { |ds| ds.read }
22
+ end
23
+
24
+ def save(data)
25
+ open_segment do |ds|
26
+ ds.puts data
27
+ ds.truncate ds.pos
28
+ end
29
+ end
30
+
31
+ def update
32
+ save yield load
33
+ end
34
+
35
+ protected
36
+
37
+ attr_accessor :path, :offset
38
+
39
+ def open_segment
40
+ File.open path, 'r+' do |file|
41
+ file.seek offset
42
+ yield file
43
+ end
44
+ end
45
+ end
46
+
47
+ DataSave.on DATA if defined? DATA
@@ -0,0 +1,298 @@
1
+ require 'DATA.save'
2
+
3
+ module SpecHelpers
4
+ RunResult = Struct.new :status, :stdout, :stderr
5
+
6
+ class RunResults
7
+ attr_accessor :body
8
+
9
+ def initialize
10
+ self.body = ''
11
+ end
12
+
13
+ def add_run(status:, stdout:, stderr:)
14
+ result = RunResult.new status, stdout, stderr
15
+ results << result
16
+ end
17
+
18
+ def results
19
+ @results ||= []
20
+ end
21
+
22
+ def stdouts
23
+ results.map(&:stdout)
24
+ end
25
+
26
+ def stderrs
27
+ results.map(&:stderr)
28
+ end
29
+
30
+ def statuses
31
+ results.map(&:status)
32
+ end
33
+
34
+ def data_segment
35
+ body.split("\n__END__\n").last
36
+ end
37
+
38
+ def assert_no_err!
39
+ stderrs.each do |err|
40
+ next if err.empty?
41
+ raise RSpec::Expectations::ExpectationNotMetError,
42
+ "Expectected no stderr, got:\n\n#{err}"
43
+ end
44
+
45
+ statuses.each do |status|
46
+ next if status.success?
47
+ raise RSpec::Expectations::ExpectationNotMetError,
48
+ "Expected all success, got:\n\n#{status.inspect}"
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ def lib_dir
55
+ File.expand_path '../lib', __dir__
56
+ end
57
+
58
+ def capture3(*command)
59
+ read_stdout, write_stdout = IO.pipe
60
+ read_stderr, write_stderr = IO.pipe
61
+
62
+ pid = spawn *command,
63
+ out: write_stdout,
64
+ err: write_stderr
65
+
66
+ write_stdout.close
67
+ write_stderr.close
68
+
69
+ Process.wait pid
70
+
71
+ { status: $?,
72
+ stdout: read_stdout.read,
73
+ stderr: read_stderr.read,
74
+ }
75
+ ensure
76
+ read_stdout.close unless read_stdout.closed?
77
+ read_stderr.close unless read_stderr.closed?
78
+ end
79
+
80
+ require 'tempfile'
81
+ def with_file(name, body, closed: false)
82
+ Tempfile.open name do |file|
83
+ file.write body
84
+ file.rewind
85
+ file.close if closed
86
+ yield file
87
+ end
88
+ end
89
+
90
+ def run_file(name, body, num_times:1)
91
+ indentation = body[/\A */]
92
+ body = body.gsub /^#{indentation}/, ''
93
+ result = RunResults.new
94
+ with_file name, body, closed: true do |file|
95
+ num_times.times do
96
+ run = capture3 'ruby', '-w', '-I', lib_dir, file.path
97
+ result.add_run run
98
+ end
99
+ result.body = File.read file.path
100
+ end
101
+ result
102
+ end
103
+
104
+ def segment_for(obj)
105
+ body_after(body: '') { |storage| storage.save obj }
106
+ end
107
+
108
+ def body_after(body:, pos:0)
109
+ with_file 'body_after.rb', body do |file|
110
+ file.seek pos
111
+ yield DataSave.for(file), file
112
+ File.read file.path
113
+ end
114
+ end
115
+ end
116
+
117
+ RSpec.configure do |config|
118
+ config.include SpecHelpers
119
+ config.disable_monkey_patching!
120
+ config.fail_fast = true
121
+ end
122
+
123
+ RSpec.describe 'DATA.save' do
124
+ it 'loads data from the data segment' do
125
+ result = run_file 'file.rb', <<-FILE
126
+ require 'DATA.save'
127
+ p DATA.load
128
+ __END__
129
+ the data
130
+ FILE
131
+ result.assert_no_err!
132
+ expect(result.stdouts).to eq [%'"the data\\n"\n']
133
+ end
134
+
135
+ it 'saves data to the data segment' do
136
+ result = run_file 'file.rb', <<-FILE
137
+ require 'DATA.save'
138
+ DATA.save("new data")
139
+ __END__
140
+ old data
141
+ FILE
142
+ result.assert_no_err!
143
+ expect(result.data_segment).to eq "new data\n"
144
+ end
145
+
146
+ it 'works with the example from the readme' do
147
+ result = run_file 'count_runs.rb', <<-FILE, num_times: 3
148
+ require 'DATA.save'
149
+
150
+ run_count = DATA.load.to_i
151
+ run_count += 1
152
+ DATA.save run_count
153
+
154
+ puts "Run count: \#{run_count}"
155
+
156
+ __END__
157
+ 0
158
+ FILE
159
+
160
+ result.assert_no_err!
161
+ expect(result.stdouts).to eq [
162
+ "Run count: 1\n",
163
+ "Run count: 2\n",
164
+ "Run count: 3\n",
165
+ ]
166
+
167
+ expect(result.body).to eq <<-FILE.gsub(/^ */, '')
168
+ require 'DATA.save'
169
+
170
+ run_count = DATA.load.to_i
171
+ run_count += 1
172
+ DATA.save run_count
173
+
174
+ puts "Run count: \#{run_count}"
175
+
176
+ __END__
177
+ 3
178
+ FILE
179
+ end
180
+
181
+
182
+ describe '.for' do
183
+ it 'creates a new instance every time, when a data segment is given' do
184
+ with_file 'f.rb', 'zomg', closed: true do |file|
185
+ segment1 = File.open file
186
+ segment2 = File.open file
187
+ segment2.getc
188
+
189
+ storage1 = DataSave.for segment1
190
+ storage2 = DataSave.for segment2
191
+
192
+ expect(storage1.load).to eq 'zomg'
193
+ expect(storage2.load).to eq 'omg'
194
+ end
195
+ end
196
+ end
197
+
198
+
199
+ describe '.on' do
200
+ it 'is places the methods on the object' do
201
+ with_file 'f.rb', '-----' do |file|
202
+ file.seek 1
203
+ DataSave.on file
204
+ file.save 'a'
205
+ expect(File.read file).to eq "-a\n"
206
+ expect(file.load).to eq "a\n"
207
+ file.update { |loaded| expect(loaded).to eq "a\n"; "C" }
208
+ expect(File.read file).to eq "-C\n"
209
+ end
210
+ end
211
+ end
212
+
213
+
214
+ it 'calls #to_s when writing' do
215
+ o = Object.new
216
+ def o.inspect() "inspected\n" end
217
+ def o.to_s() "to_s'd\n" end
218
+ expect(segment_for o).to eq "to_s'd\n"
219
+ end
220
+
221
+
222
+ it 'appends a newline if the data doesn\'t have one (b/c this is a file)' do
223
+ expect(segment_for "a\n").to eq "a\n"
224
+ expect(segment_for "a" ).to eq "a\n"
225
+ end
226
+
227
+
228
+ it 'works when the written segment is shorter than the existing data segment' do
229
+ body = body_after(body: 'abcdefg', pos: 1) { |storage| storage.save "X\n" }
230
+ expect(body).to eq "aX\n"
231
+ end
232
+
233
+
234
+ it 'works when the written segment is longer than the existing data segment' do
235
+ body = body_after(body: 'ab', pos: 1) { |storage| storage.save "X\n" }
236
+ expect(body).to eq "aX\n"
237
+ end
238
+
239
+
240
+ it 'works when there are UTF8 characters in the body' do
241
+ body = body_after body: 'Ω1', pos: 'Ω'.bytesize do |storage|
242
+ storage.save "X\n"
243
+ end
244
+ expect(body).to eq "ΩX\n"
245
+ end
246
+
247
+
248
+ it 'works when there are UTF8 characters in the old DATA segment' do
249
+ body = body_after body: '1Ω', pos: 1 do |storage|
250
+ storage.save "X\n"
251
+ end
252
+ expect(body).to eq "1X\n"
253
+ end
254
+
255
+
256
+ it 'works when there are UTF8 characters in the new DATA segment' do
257
+ body = body_after body: '12', pos: 1 do |storage|
258
+ storage.save "Ω\n"
259
+ end
260
+ expect(body).to eq "1Ω\n"
261
+ end
262
+
263
+
264
+ it 'can be reinvoked multiple times' do
265
+ body = body_after body: "-0\n", pos: 1 do |storage, file|
266
+ expect(storage.load).to eq "0\n"
267
+
268
+ storage.save 1
269
+ expect(File.read file).to eq "-1\n"
270
+ expect(storage.load).to eq "1\n"
271
+
272
+ storage.save 2
273
+ expect(File.read file).to eq "-2\n"
274
+ expect(storage.load).to eq "2\n"
275
+ end
276
+ expect(body).to eq "-2\n"
277
+ end
278
+
279
+
280
+ it 'reloads the data every time' do
281
+ body = body_after body: "-0\n", pos: 1 do |storage, file|
282
+ expect(storage.load).to eq "0\n"
283
+ File.write file, 'zomg'
284
+ expect(storage.load).to eq 'omg'
285
+ end
286
+ expect(body).to eq "zomg"
287
+ end
288
+
289
+ it 'allows for upddating, which gives the current value to a block, and saves whatever comes back' do
290
+ body = body_after body: "-0\n", pos: 1 do |storage|
291
+ storage.update do |current|
292
+ expect(current).to eq "0\n"
293
+ 100
294
+ end
295
+ end
296
+ expect(body).to eq "-100\n"
297
+ end
298
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: DATA.save
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Josh Cheek
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ description: Store shit in your script's DATA segment
28
+ email:
29
+ - josh.cheek@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - DATA.save.gemspec
36
+ - Readme.md
37
+ - lib/DATA.save.rb
38
+ - spec/DATA.save_spec.rb
39
+ homepage: https://github.com/JoshCheek/DATA.save
40
+ licenses:
41
+ - WTFPL
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project: seeing_is_believing
59
+ rubygems_version: 2.4.1
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Store shit in your script's DATA segment
63
+ test_files:
64
+ - spec/DATA.save_spec.rb
65
+ has_rdoc: