pstore 0.1.0

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: 3de8bbd117d11303d2372e9fd0b9db5a15fb4d6b566905101e70689cd82fbe4d
4
+ data.tar.gz: bbab746d38d3e6fc07bfaf54235bcb6e34727a2a20ae586620064a16de834dc2
5
+ SHA512:
6
+ metadata.gz: 4a4f1df2cafeee50e8c82a1755e8f0836f7c2b685f5372df1461cdac183d43cf3ad19b1dfa9a516ab3698e0ad1837f050be0e86b1b4944dbd5d3c625665f2b5d
7
+ data.tar.gz: f2b9d2cb5172cc4e09eba1ae2ec499bce9972b54028e0b89e6d26e6f66f16918254d3d643dc49d4caed17fe45ffc3278737431864de1d9a87bc429bf4c345c5b
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ Gemfile.lock
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.3
7
+ before_install: gem install bundler -v 2.0.2
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem "bundler"
7
+ gem "rake"
8
+ gem "test-unit"
9
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
@@ -0,0 +1,95 @@
1
+ # Pstore
2
+
3
+ PStore implements a file based persistence mechanism based on a Hash. User
4
+ code can store hierarchies of Ruby objects (values) into the data store file
5
+ by name (keys). An object hierarchy may be just a single object. User code
6
+ may later read values back from the data store or even update data, as needed.
7
+
8
+ The transactional behavior ensures that any changes succeed or fail together.
9
+ This can be used to ensure that the data store is not left in a transitory
10
+ state, where some values were updated but others were not.
11
+
12
+ Behind the scenes, Ruby objects are stored to the data store file with
13
+ Marshal. That carries the usual limitations. Proc objects cannot be
14
+ marshalled, for example.
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'pstore'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install pstore
31
+
32
+ ## Usage
33
+
34
+ ```ruby
35
+ require "pstore"
36
+
37
+ # a mock wiki object...
38
+ class WikiPage
39
+ def initialize( page_name, author, contents )
40
+ @page_name = page_name
41
+ @revisions = Array.new
42
+
43
+ add_revision(author, contents)
44
+ end
45
+
46
+ attr_reader :page_name
47
+
48
+ def add_revision( author, contents )
49
+ @revisions << { :created => Time.now,
50
+ :author => author,
51
+ :contents => contents }
52
+ end
53
+
54
+ def wiki_page_references
55
+ [@page_name] + @revisions.last[:contents].scan(/\b(?:[A-Z]+[a-z]+){2,}/)
56
+ end
57
+
58
+ # ...
59
+ end
60
+
61
+ # create a new page...
62
+ home_page = WikiPage.new( "HomePage", "James Edward Gray II",
63
+ "A page about the JoysOfDocumentation..." )
64
+
65
+ # then we want to update page data and the index together, or not at all...
66
+ wiki = PStore.new("wiki_pages.pstore")
67
+ wiki.transaction do # begin transaction; do all of this or none of it
68
+ # store page...
69
+ wiki[home_page.page_name] = home_page
70
+ # ensure that an index has been created...
71
+ wiki[:wiki_index] ||= Array.new
72
+ # update wiki index...
73
+ wiki[:wiki_index].push(*home_page.wiki_page_references)
74
+ end # commit changes to wiki data store file
75
+
76
+ ### Some time later... ###
77
+
78
+ # read wiki data...
79
+ wiki.transaction(true) do # begin read-only transaction, no changes allowed
80
+ wiki.roots.each do |data_root_name|
81
+ p data_root_name
82
+ p wiki[data_root_name]
83
+ end
84
+ end
85
+ ```
86
+
87
+ ## Development
88
+
89
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
90
+
91
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
92
+
93
+ ## Contributing
94
+
95
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/pstore.
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test/lib"
6
+ t.ruby_opts << "-rhelper"
7
+ t.test_files = FileList["test/**/test_*.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pstore"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,491 @@
1
+ # frozen_string_literal: true
2
+ # = PStore -- Transactional File Storage for Ruby Objects
3
+ #
4
+ # pstore.rb -
5
+ # originally by matz
6
+ # documentation by Kev Jackson and James Edward Gray II
7
+ # improved by Hongli Lai
8
+ #
9
+ # See PStore for documentation.
10
+
11
+ require "digest"
12
+
13
+ #
14
+ # PStore implements a file based persistence mechanism based on a Hash. User
15
+ # code can store hierarchies of Ruby objects (values) into the data store file
16
+ # by name (keys). An object hierarchy may be just a single object. User code
17
+ # may later read values back from the data store or even update data, as needed.
18
+ #
19
+ # The transactional behavior ensures that any changes succeed or fail together.
20
+ # This can be used to ensure that the data store is not left in a transitory
21
+ # state, where some values were updated but others were not.
22
+ #
23
+ # Behind the scenes, Ruby objects are stored to the data store file with
24
+ # Marshal. That carries the usual limitations. Proc objects cannot be
25
+ # marshalled, for example.
26
+ #
27
+ # == Usage example:
28
+ #
29
+ # require "pstore"
30
+ #
31
+ # # a mock wiki object...
32
+ # class WikiPage
33
+ # def initialize( page_name, author, contents )
34
+ # @page_name = page_name
35
+ # @revisions = Array.new
36
+ #
37
+ # add_revision(author, contents)
38
+ # end
39
+ #
40
+ # attr_reader :page_name
41
+ #
42
+ # def add_revision( author, contents )
43
+ # @revisions << { :created => Time.now,
44
+ # :author => author,
45
+ # :contents => contents }
46
+ # end
47
+ #
48
+ # def wiki_page_references
49
+ # [@page_name] + @revisions.last[:contents].scan(/\b(?:[A-Z]+[a-z]+){2,}/)
50
+ # end
51
+ #
52
+ # # ...
53
+ # end
54
+ #
55
+ # # create a new page...
56
+ # home_page = WikiPage.new( "HomePage", "James Edward Gray II",
57
+ # "A page about the JoysOfDocumentation..." )
58
+ #
59
+ # # then we want to update page data and the index together, or not at all...
60
+ # wiki = PStore.new("wiki_pages.pstore")
61
+ # wiki.transaction do # begin transaction; do all of this or none of it
62
+ # # store page...
63
+ # wiki[home_page.page_name] = home_page
64
+ # # ensure that an index has been created...
65
+ # wiki[:wiki_index] ||= Array.new
66
+ # # update wiki index...
67
+ # wiki[:wiki_index].push(*home_page.wiki_page_references)
68
+ # end # commit changes to wiki data store file
69
+ #
70
+ # ### Some time later... ###
71
+ #
72
+ # # read wiki data...
73
+ # wiki.transaction(true) do # begin read-only transaction, no changes allowed
74
+ # wiki.roots.each do |data_root_name|
75
+ # p data_root_name
76
+ # p wiki[data_root_name]
77
+ # end
78
+ # end
79
+ #
80
+ # == Transaction modes
81
+ #
82
+ # By default, file integrity is only ensured as long as the operating system
83
+ # (and the underlying hardware) doesn't raise any unexpected I/O errors. If an
84
+ # I/O error occurs while PStore is writing to its file, then the file will
85
+ # become corrupted.
86
+ #
87
+ # You can prevent this by setting <em>pstore.ultra_safe = true</em>.
88
+ # However, this results in a minor performance loss, and only works on platforms
89
+ # that support atomic file renames. Please consult the documentation for
90
+ # +ultra_safe+ for details.
91
+ #
92
+ # Needless to say, if you're storing valuable data with PStore, then you should
93
+ # backup the PStore files from time to time.
94
+ class PStore
95
+ RDWR_ACCESS = {mode: IO::RDWR | IO::CREAT | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
96
+ RD_ACCESS = {mode: IO::RDONLY | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
97
+ WR_ACCESS = {mode: IO::WRONLY | IO::CREAT | IO::TRUNC | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
98
+
99
+ # The error type thrown by all PStore methods.
100
+ class Error < StandardError
101
+ end
102
+
103
+ # Whether PStore should do its best to prevent file corruptions, even when under
104
+ # unlikely-to-occur error conditions such as out-of-space conditions and other
105
+ # unusual OS filesystem errors. Setting this flag comes at the price in the form
106
+ # of a performance loss.
107
+ #
108
+ # This flag only has effect on platforms on which file renames are atomic (e.g.
109
+ # all POSIX platforms: Linux, MacOS X, FreeBSD, etc). The default value is false.
110
+ attr_accessor :ultra_safe
111
+
112
+ #
113
+ # To construct a PStore object, pass in the _file_ path where you would like
114
+ # the data to be stored.
115
+ #
116
+ # PStore objects are always reentrant. But if _thread_safe_ is set to true,
117
+ # then it will become thread-safe at the cost of a minor performance hit.
118
+ #
119
+ def initialize(file, thread_safe = false)
120
+ dir = File::dirname(file)
121
+ unless File::directory? dir
122
+ raise PStore::Error, format("directory %s does not exist", dir)
123
+ end
124
+ if File::exist? file and not File::readable? file
125
+ raise PStore::Error, format("file %s not readable", file)
126
+ end
127
+ @filename = file
128
+ @abort = false
129
+ @ultra_safe = false
130
+ @thread_safe = thread_safe
131
+ @lock = Thread::Mutex.new
132
+ end
133
+
134
+ # Raises PStore::Error if the calling code is not in a PStore#transaction.
135
+ def in_transaction
136
+ raise PStore::Error, "not in transaction" unless @lock.locked?
137
+ end
138
+ #
139
+ # Raises PStore::Error if the calling code is not in a PStore#transaction or
140
+ # if the code is in a read-only PStore#transaction.
141
+ #
142
+ def in_transaction_wr
143
+ in_transaction
144
+ raise PStore::Error, "in read-only transaction" if @rdonly
145
+ end
146
+ private :in_transaction, :in_transaction_wr
147
+
148
+ #
149
+ # Retrieves a value from the PStore file data, by _name_. The hierarchy of
150
+ # Ruby objects stored under that root _name_ will be returned.
151
+ #
152
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
153
+ # raise PStore::Error if called at any other time.
154
+ #
155
+ def [](name)
156
+ in_transaction
157
+ @table[name]
158
+ end
159
+ #
160
+ # This method is just like PStore#[], save that you may also provide a
161
+ # _default_ value for the object. In the event the specified _name_ is not
162
+ # found in the data store, your _default_ will be returned instead. If you do
163
+ # not specify a default, PStore::Error will be raised if the object is not
164
+ # found.
165
+ #
166
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
167
+ # raise PStore::Error if called at any other time.
168
+ #
169
+ def fetch(name, default=PStore::Error)
170
+ in_transaction
171
+ unless @table.key? name
172
+ if default == PStore::Error
173
+ raise PStore::Error, format("undefined root name `%s'", name)
174
+ else
175
+ return default
176
+ end
177
+ end
178
+ @table[name]
179
+ end
180
+ #
181
+ # Stores an individual Ruby object or a hierarchy of Ruby objects in the data
182
+ # store file under the root _name_. Assigning to a _name_ already in the data
183
+ # store clobbers the old data.
184
+ #
185
+ # == Example:
186
+ #
187
+ # require "pstore"
188
+ #
189
+ # store = PStore.new("data_file.pstore")
190
+ # store.transaction do # begin transaction
191
+ # # load some data into the store...
192
+ # store[:single_object] = "My data..."
193
+ # store[:obj_hierarchy] = { "Kev Jackson" => ["rational.rb", "pstore.rb"],
194
+ # "James Gray" => ["erb.rb", "pstore.rb"] }
195
+ # end # commit changes to data store file
196
+ #
197
+ # *WARNING*: This method is only valid in a PStore#transaction and it cannot
198
+ # be read-only. It will raise PStore::Error if called at any other time.
199
+ #
200
+ def []=(name, value)
201
+ in_transaction_wr
202
+ @table[name] = value
203
+ end
204
+ #
205
+ # Removes an object hierarchy from the data store, by _name_.
206
+ #
207
+ # *WARNING*: This method is only valid in a PStore#transaction and it cannot
208
+ # be read-only. It will raise PStore::Error if called at any other time.
209
+ #
210
+ def delete(name)
211
+ in_transaction_wr
212
+ @table.delete name
213
+ end
214
+
215
+ #
216
+ # Returns the names of all object hierarchies currently in the store.
217
+ #
218
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
219
+ # raise PStore::Error if called at any other time.
220
+ #
221
+ def roots
222
+ in_transaction
223
+ @table.keys
224
+ end
225
+ #
226
+ # Returns true if the supplied _name_ is currently in the data store.
227
+ #
228
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
229
+ # raise PStore::Error if called at any other time.
230
+ #
231
+ def root?(name)
232
+ in_transaction
233
+ @table.key? name
234
+ end
235
+ # Returns the path to the data store file.
236
+ def path
237
+ @filename
238
+ end
239
+
240
+ #
241
+ # Ends the current PStore#transaction, committing any changes to the data
242
+ # store immediately.
243
+ #
244
+ # == Example:
245
+ #
246
+ # require "pstore"
247
+ #
248
+ # store = PStore.new("data_file.pstore")
249
+ # store.transaction do # begin transaction
250
+ # # load some data into the store...
251
+ # store[:one] = 1
252
+ # store[:two] = 2
253
+ #
254
+ # store.commit # end transaction here, committing changes
255
+ #
256
+ # store[:three] = 3 # this change is never reached
257
+ # end
258
+ #
259
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
260
+ # raise PStore::Error if called at any other time.
261
+ #
262
+ def commit
263
+ in_transaction
264
+ @abort = false
265
+ throw :pstore_abort_transaction
266
+ end
267
+ #
268
+ # Ends the current PStore#transaction, discarding any changes to the data
269
+ # store.
270
+ #
271
+ # == Example:
272
+ #
273
+ # require "pstore"
274
+ #
275
+ # store = PStore.new("data_file.pstore")
276
+ # store.transaction do # begin transaction
277
+ # store[:one] = 1 # this change is not applied, see below...
278
+ # store[:two] = 2 # this change is not applied, see below...
279
+ #
280
+ # store.abort # end transaction here, discard all changes
281
+ #
282
+ # store[:three] = 3 # this change is never reached
283
+ # end
284
+ #
285
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
286
+ # raise PStore::Error if called at any other time.
287
+ #
288
+ def abort
289
+ in_transaction
290
+ @abort = true
291
+ throw :pstore_abort_transaction
292
+ end
293
+
294
+ #
295
+ # Opens a new transaction for the data store. Code executed inside a block
296
+ # passed to this method may read and write data to and from the data store
297
+ # file.
298
+ #
299
+ # At the end of the block, changes are committed to the data store
300
+ # automatically. You may exit the transaction early with a call to either
301
+ # PStore#commit or PStore#abort. See those methods for details about how
302
+ # changes are handled. Raising an uncaught Exception in the block is
303
+ # equivalent to calling PStore#abort.
304
+ #
305
+ # If _read_only_ is set to +true+, you will only be allowed to read from the
306
+ # data store during the transaction and any attempts to change the data will
307
+ # raise a PStore::Error.
308
+ #
309
+ # Note that PStore does not support nested transactions.
310
+ #
311
+ def transaction(read_only = false) # :yields: pstore
312
+ value = nil
313
+ if !@thread_safe
314
+ raise PStore::Error, "nested transaction" unless @lock.try_lock
315
+ else
316
+ begin
317
+ @lock.lock
318
+ rescue ThreadError
319
+ raise PStore::Error, "nested transaction"
320
+ end
321
+ end
322
+ begin
323
+ @rdonly = read_only
324
+ @abort = false
325
+ file = open_and_lock_file(@filename, read_only)
326
+ if file
327
+ begin
328
+ @table, checksum, original_data_size = load_data(file, read_only)
329
+
330
+ catch(:pstore_abort_transaction) do
331
+ value = yield(self)
332
+ end
333
+
334
+ if !@abort && !read_only
335
+ save_data(checksum, original_data_size, file)
336
+ end
337
+ ensure
338
+ file.close
339
+ end
340
+ else
341
+ # This can only occur if read_only == true.
342
+ @table = {}
343
+ catch(:pstore_abort_transaction) do
344
+ value = yield(self)
345
+ end
346
+ end
347
+ ensure
348
+ @lock.unlock
349
+ end
350
+ value
351
+ end
352
+
353
+ private
354
+ # Constant for relieving Ruby's garbage collector.
355
+ CHECKSUM_ALGO = %w[SHA512 SHA384 SHA256 SHA1 RMD160 MD5].each do |algo|
356
+ begin
357
+ break Digest(algo)
358
+ rescue LoadError
359
+ end
360
+ end
361
+ EMPTY_STRING = ""
362
+ EMPTY_MARSHAL_DATA = Marshal.dump({})
363
+ EMPTY_MARSHAL_CHECKSUM = CHECKSUM_ALGO.digest(EMPTY_MARSHAL_DATA)
364
+
365
+ #
366
+ # Open the specified filename (either in read-only mode or in
367
+ # read-write mode) and lock it for reading or writing.
368
+ #
369
+ # The opened File object will be returned. If _read_only_ is true,
370
+ # and the file does not exist, then nil will be returned.
371
+ #
372
+ # All exceptions are propagated.
373
+ #
374
+ def open_and_lock_file(filename, read_only)
375
+ if read_only
376
+ begin
377
+ file = File.new(filename, **RD_ACCESS)
378
+ begin
379
+ file.flock(File::LOCK_SH)
380
+ return file
381
+ rescue
382
+ file.close
383
+ raise
384
+ end
385
+ rescue Errno::ENOENT
386
+ return nil
387
+ end
388
+ else
389
+ file = File.new(filename, **RDWR_ACCESS)
390
+ file.flock(File::LOCK_EX)
391
+ return file
392
+ end
393
+ end
394
+
395
+ # Load the given PStore file.
396
+ # If +read_only+ is true, the unmarshalled Hash will be returned.
397
+ # If +read_only+ is false, a 3-tuple will be returned: the unmarshalled
398
+ # Hash, a checksum of the data, and the size of the data.
399
+ def load_data(file, read_only)
400
+ if read_only
401
+ begin
402
+ table = load(file)
403
+ raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash)
404
+ rescue EOFError
405
+ # This seems to be a newly-created file.
406
+ table = {}
407
+ end
408
+ table
409
+ else
410
+ data = file.read
411
+ if data.empty?
412
+ # This seems to be a newly-created file.
413
+ table = {}
414
+ checksum = empty_marshal_checksum
415
+ size = empty_marshal_data.bytesize
416
+ else
417
+ table = load(data)
418
+ checksum = CHECKSUM_ALGO.digest(data)
419
+ size = data.bytesize
420
+ raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash)
421
+ end
422
+ data.replace(EMPTY_STRING)
423
+ [table, checksum, size]
424
+ end
425
+ end
426
+
427
+ def on_windows?
428
+ is_windows = RUBY_PLATFORM =~ /mswin|mingw|bccwin|wince/
429
+ self.class.__send__(:define_method, :on_windows?) do
430
+ is_windows
431
+ end
432
+ is_windows
433
+ end
434
+
435
+ def save_data(original_checksum, original_file_size, file)
436
+ new_data = dump(@table)
437
+
438
+ if new_data.bytesize != original_file_size || CHECKSUM_ALGO.digest(new_data) != original_checksum
439
+ if @ultra_safe && !on_windows?
440
+ # Windows doesn't support atomic file renames.
441
+ save_data_with_atomic_file_rename_strategy(new_data, file)
442
+ else
443
+ save_data_with_fast_strategy(new_data, file)
444
+ end
445
+ end
446
+
447
+ new_data.replace(EMPTY_STRING)
448
+ end
449
+
450
+ def save_data_with_atomic_file_rename_strategy(data, file)
451
+ temp_filename = "#{@filename}.tmp.#{Process.pid}.#{rand 1000000}"
452
+ temp_file = File.new(temp_filename, **WR_ACCESS)
453
+ begin
454
+ temp_file.flock(File::LOCK_EX)
455
+ temp_file.write(data)
456
+ temp_file.flush
457
+ File.rename(temp_filename, @filename)
458
+ rescue
459
+ File.unlink(temp_file) rescue nil
460
+ raise
461
+ ensure
462
+ temp_file.close
463
+ end
464
+ end
465
+
466
+ def save_data_with_fast_strategy(data, file)
467
+ file.rewind
468
+ file.write(data)
469
+ file.truncate(data.bytesize)
470
+ end
471
+
472
+
473
+ # This method is just a wrapped around Marshal.dump
474
+ # to allow subclass overriding used in YAML::Store.
475
+ def dump(table) # :nodoc:
476
+ Marshal::dump(table)
477
+ end
478
+
479
+ # This method is just a wrapped around Marshal.load.
480
+ # to allow subclass overriding used in YAML::Store.
481
+ def load(content) # :nodoc:
482
+ Marshal::load(content)
483
+ end
484
+
485
+ def empty_marshal_data
486
+ EMPTY_MARSHAL_DATA
487
+ end
488
+ def empty_marshal_checksum
489
+ EMPTY_MARSHAL_CHECKSUM
490
+ end
491
+ end
@@ -0,0 +1,3 @@
1
+ class PStore
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "pstore/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "pstore"
7
+ spec.version = PStore::VERSION
8
+ spec.authors = ["Hiroshi SHIBATA"]
9
+ spec.email = ["hsbt@ruby-lang.org"]
10
+
11
+ spec.summary = %q{Transactional File Storage for Ruby Objects}
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/ruby/pstore"
14
+ spec.license = "BSD-2-Clause"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/ruby/pstore"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pstore
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hiroshi SHIBATA
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-11-06 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Transactional File Storage for Ruby Objects
14
+ email:
15
+ - hsbt@ruby-lang.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - ".travis.yml"
22
+ - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - bin/console
27
+ - bin/setup
28
+ - lib/pstore.rb
29
+ - lib/pstore/version.rb
30
+ - pstore.gemspec
31
+ homepage: https://github.com/ruby/pstore
32
+ licenses:
33
+ - BSD-2-Clause
34
+ metadata:
35
+ homepage_uri: https://github.com/ruby/pstore
36
+ source_code_uri: https://github.com/ruby/pstore
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubygems_version: 3.0.3
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Transactional File Storage for Ruby Objects
56
+ test_files: []