pstore 0.2.0 → 0.3.0

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: d4af5fcacc680025ce66c1ada2096341526492d999f65d9e406a4f48f105c60e
4
- data.tar.gz: fcb504575c025d4797935076248d4490fd2ceaa72a17176129481f40aaadd95b
3
+ metadata.gz: be4c744cb12983612ebef3ae585d25571252869cfef51a8799a0c3328be339c8
4
+ data.tar.gz: 9527c38bdea78cfee23874de9ecfdfee3ce9beefb541bd6e26abfa3ba55e3a3c
5
5
  SHA512:
6
- metadata.gz: 16e1c1157c8edfdaabb2e5f90979a6e05bb1c77ebec3f4dc4b3bf503715e0e36bbc4efaa0ddcd56fcbe035a55dca4cadc72f493b6242da14912e1842f78de0a3
7
- data.tar.gz: 64b2f0fbe41c31a35a991ea1b1abfce61907ae880a9bab90ba58875c4abdbe922ecaf2dd8bbaa3d83001fbea272104484a3358571b58f3ab4ecb917d6417d9da
6
+ metadata.gz: 7fa202004bbd5761b8a457fda173f69d2b0574d1b995ef3e2800c2881213719fb6e9cacd4d0cccfb29b0864c28f149a0520b4102f0172ecfccf7ae277367d023
7
+ data.tar.gz: b2724f28ea0ede1bac0675f77bc00665771b88b6e946da3dfd00ab0e946adef9589a350bcce05732c4568b867714b98fc7133810083a5af60ee62fdd67936efc
data/.document ADDED
@@ -0,0 +1,4 @@
1
+ BSDL
2
+ COPYING
3
+ README.md
4
+ lib/
@@ -0,0 +1,4 @@
1
+ changelog:
2
+ exclude:
3
+ labels:
4
+ - dependencies # Added by Dependabot
@@ -23,24 +23,24 @@ jobs:
23
23
 
24
24
  steps:
25
25
  - name: Harden Runner
26
- uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
26
+ uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
27
27
  with:
28
28
  egress-policy: audit
29
29
 
30
- - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
30
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
31
31
 
32
32
  - name: Set up Ruby
33
- uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0
33
+ uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
34
34
  with:
35
35
  bundler-cache: true
36
36
  ruby-version: ruby
37
37
 
38
38
  - name: Publish to RubyGems
39
- uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1.1.1
39
+ uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1
40
40
 
41
41
  - name: Create GitHub release
42
42
  run: |
43
43
  tag_name="$(git describe --tags --abbrev=0)"
44
44
  gh release create "${tag_name}" --verify-tag --generate-notes
45
45
  env:
46
- GITHUB_TOKEN: ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }}
46
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -15,13 +15,17 @@ jobs:
15
15
  strategy:
16
16
  matrix:
17
17
  ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
18
- os: [ ubuntu-latest, macos-latest ]
18
+ os: [ ubuntu-latest, macos-latest, windows-latest ]
19
19
  exclude:
20
20
  - { os: macos-latest, ruby: '2.4' }
21
21
  - { os: macos-latest, ruby: '2.5' }
22
+ - { os: windows-latest, ruby: head }
23
+ include:
24
+ - { os: windows-latest, ruby: mingw }
25
+ - { os: windows-latest, ruby: mswin }
22
26
  runs-on: ${{ matrix.os }}
23
27
  steps:
24
- - uses: actions/checkout@v4
28
+ - uses: actions/checkout@v7
25
29
  - name: Set up Ruby
26
30
  uses: ruby/setup-ruby@v1
27
31
  with:
data/lib/pstore.rb CHANGED
@@ -326,11 +326,15 @@ require "digest"
326
326
  # end
327
327
  #
328
328
  class PStore
329
- VERSION = "0.2.0"
329
+ # :stopdoc:
330
+ VERSION = "0.3.0"
330
331
 
331
- RDWR_ACCESS = {mode: IO::RDWR | IO::CREAT | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
332
- RD_ACCESS = {mode: IO::RDONLY | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
333
- WR_ACCESS = {mode: IO::WRONLY | IO::CREAT | IO::TRUNC | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
332
+ RDWR_ACCESS = IO::RDWR | IO::CREAT | IO::BINARY
333
+ RD_ACCESS = IO::RDONLY | IO::BINARY
334
+ WR_ACCESS = IO::WRONLY | IO::CREAT | IO::TRUNC | IO::BINARY
335
+ NOFOLLOW = IO.const_defined?(:NOFOLLOW) ? IO::NOFOLLOW : 0
336
+ private_constant :RDWR_ACCESS, :RD_ACCESS, :WR_ACCESS, :NOFOLLOW
337
+ # :startdoc:
334
338
 
335
339
  # The error type thrown by all PStore methods.
336
340
  class Error < StandardError
@@ -364,12 +368,26 @@ class PStore
364
368
  #
365
369
  # A \PStore object is
366
370
  # {reentrant}[https://en.wikipedia.org/wiki/Reentrancy_(computing)].
367
- # If argument +thread_safe+ is given as +true+,
371
+ # If argument or keyword argument +thread_safe+ is given as +true+,
368
372
  # the object is also thread-safe (at the cost of a small performance penalty):
369
373
  #
370
374
  # store = PStore.new(path, true)
375
+ # store = PStore.new(path, thread_safe: true)
371
376
  #
372
- def initialize(file, thread_safe = false)
377
+ # If keyword argument +ultra_safe+ is given as +true+, the object is
378
+ # set to +ultra_safe+ mode.
379
+ #
380
+ # store = PStore.new(path, ultra_safe: true)
381
+ #
382
+ # If keyword argument +follow_symlink+ is given as +false+, the store file
383
+ # itself must not be a symbolic link. This option has no effect on platforms
384
+ # that do not support IO::NOFOLLOW. Symbolic links in parent directories are
385
+ # not affected.
386
+ #
387
+ # store = PStore.new(path, follow_symlink: false)
388
+ #
389
+ def initialize(file, _thread_safe = false, thread_safe: _thread_safe,
390
+ ultra_safe: false, follow_symlink: true)
373
391
  dir = File::dirname(file)
374
392
  unless File::directory? dir
375
393
  raise PStore::Error, format("directory %s does not exist", dir)
@@ -377,16 +395,17 @@ class PStore
377
395
  if File::exist? file and not File::readable? file
378
396
  raise PStore::Error, format("file %s not readable", file)
379
397
  end
380
- @filename = file
398
+ @filename = File.path(file)
381
399
  @abort = false
382
- @ultra_safe = false
400
+ @ultra_safe = ultra_safe
383
401
  @thread_safe = thread_safe
402
+ @nofollow = follow_symlink ? 0 : NOFOLLOW
384
403
  @lock = Thread::Mutex.new
385
404
  end
386
405
 
387
406
  # Raises PStore::Error if the calling code is not in a PStore#transaction.
388
407
  def in_transaction
389
- raise PStore::Error, "not in transaction" unless @lock.locked?
408
+ raise PStore::Error, "not in transaction" unless @lock.owned?
390
409
  end
391
410
  #
392
411
  # Raises PStore::Error if the calling code is not in a PStore#transaction or
@@ -436,7 +455,7 @@ class PStore
436
455
  def fetch(key, default=PStore::Error)
437
456
  in_transaction
438
457
  unless @table.key? key
439
- if default == PStore::Error
458
+ if PStore::Error.equal?(default)
440
459
  raise PStore::Error, format("undefined key '%s'", key)
441
460
  else
442
461
  return default
@@ -524,7 +543,7 @@ class PStore
524
543
  def commit
525
544
  in_transaction
526
545
  @abort = false
527
- throw :pstore_abort_transaction
546
+ throw self
528
547
  end
529
548
 
530
549
  # Exits the current transaction block, discarding any changes
@@ -535,7 +554,7 @@ class PStore
535
554
  def abort
536
555
  in_transaction
537
556
  @abort = true
538
- throw :pstore_abort_transaction
557
+ throw self
539
558
  end
540
559
 
541
560
  # Opens a transaction block for the store.
@@ -567,7 +586,7 @@ class PStore
567
586
  begin
568
587
  @table, checksum, original_data_size = load_data(file, read_only)
569
588
 
570
- catch(:pstore_abort_transaction) do
589
+ catch(self) do
571
590
  value = yield(self)
572
591
  end
573
592
 
@@ -580,7 +599,7 @@ class PStore
580
599
  else
581
600
  # This can only occur if read_only == true.
582
601
  @table = {}
583
- catch(:pstore_abort_transaction) do
602
+ catch(self) do
584
603
  value = yield(self)
585
604
  end
586
605
  end
@@ -591,6 +610,8 @@ class PStore
591
610
  end
592
611
 
593
612
  private
613
+ # :stopdoc:
614
+
594
615
  # Constant for relieving Ruby's garbage collector.
595
616
  CHECKSUM_ALGO = %w[SHA512 SHA384 SHA256 SHA1 RMD160 MD5].each do |algo|
596
617
  begin
@@ -604,6 +625,7 @@ class PStore
604
625
 
605
626
  EMPTY_MARSHAL_DATA.freeze
606
627
  EMPTY_MARSHAL_CHECKSUM.freeze
628
+ private_constant :CHECKSUM_ALGO, :EMPTY_STRING, :EMPTY_MARSHAL_DATA, :EMPTY_MARSHAL_CHECKSUM
607
629
 
608
630
  #
609
631
  # Open the specified filename (either in read-only mode or in
@@ -615,23 +637,26 @@ class PStore
615
637
  # All exceptions are propagated.
616
638
  #
617
639
  def open_and_lock_file(filename, read_only)
618
- if read_only
619
- begin
620
- file = File.new(filename, **RD_ACCESS)
640
+ filename = File.path(filename)
641
+ loop do
642
+ if read_only
621
643
  begin
622
- file.flock(File::LOCK_SH)
623
- return file
624
- rescue
625
- file.close
626
- raise
644
+ file = File.new(filename, mode: RD_ACCESS | @nofollow, encoding: Encoding::ASCII_8BIT)
645
+ rescue Errno::ENOENT
646
+ return nil
627
647
  end
628
- rescue Errno::ENOENT
629
- return nil
648
+ else
649
+ file = File.new(filename, mode: RDWR_ACCESS | @nofollow, encoding: Encoding::ASCII_8BIT)
650
+ end
651
+ current = false
652
+ begin
653
+ file.flock(read_only ? File::LOCK_SH : File::LOCK_EX)
654
+ # An atomic save may have replaced the file while this lock was pending.
655
+ current = File.identical?(file, filename)
656
+ return file if current
657
+ ensure
658
+ file.close unless current
630
659
  end
631
- else
632
- file = File.new(filename, **RDWR_ACCESS)
633
- file.flock(File::LOCK_EX)
634
- return file
635
660
  end
636
661
  end
637
662
 
@@ -645,6 +670,7 @@ class PStore
645
670
  table = load(file)
646
671
  raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash)
647
672
  rescue EOFError
673
+ raise unless file.size == 0
648
674
  # This seems to be a newly-created file.
649
675
  table = {}
650
676
  end
@@ -667,23 +693,16 @@ class PStore
667
693
  end
668
694
  end
669
695
 
670
- def on_windows?
671
- is_windows = RUBY_PLATFORM =~ /mswin|mingw|bccwin|wince/
672
-
673
- on_windows_proc = Proc.new do
674
- is_windows
675
- end
676
- Ractor.make_shareable(on_windows_proc) if defined?(Ractor)
677
- self.class.__send__(:define_method, :on_windows?, &on_windows_proc)
678
- is_windows
679
- end
696
+ ON_WINDOWS = true & (/mswin|mingw|bccwin|wince/ =~ RUBY_PLATFORM) # :nodoc:
697
+ private_constant :ON_WINDOWS
680
698
 
681
699
  def save_data(original_checksum, original_file_size, file)
682
700
  new_data = dump(@table)
683
701
 
684
702
  if new_data.bytesize != original_file_size || CHECKSUM_ALGO.digest(new_data) != original_checksum
685
- if @ultra_safe && !on_windows?
686
- # Windows doesn't support atomic file renames.
703
+ if @ultra_safe && !ON_WINDOWS
704
+ # Once a file is locked, Windows does not guarantee that the
705
+ # lock will be released until the file is closed.
687
706
  save_data_with_atomic_file_rename_strategy(new_data, file)
688
707
  else
689
708
  save_data_with_fast_strategy(new_data, file)
@@ -695,13 +714,14 @@ class PStore
695
714
 
696
715
  def save_data_with_atomic_file_rename_strategy(data, file)
697
716
  temp_filename = "#{@filename}.tmp.#{Process.pid}.#{rand 1000000}"
698
- temp_file = File.new(temp_filename, **WR_ACCESS)
717
+ temp_file = File.new(temp_filename, mode: WR_ACCESS | @nofollow, encoding: Encoding::ASCII_8BIT, perm: 0o000)
699
718
  begin
700
719
  temp_file.flock(File::LOCK_EX)
701
720
  temp_file.write(data)
702
721
  temp_file.flush
722
+ temp_file.chmod(file.stat.mode)
703
723
  File.rename(temp_filename, @filename)
704
- rescue
724
+ rescue Exception
705
725
  File.unlink(temp_file) rescue nil
706
726
  raise
707
727
  ensure
data/pstore.gemspec CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.description = spec.summary
18
18
  spec.homepage = "https://github.com/ruby/pstore"
19
19
  spec.licenses = ["Ruby", "BSD-2-Clause"]
20
+ spec.required_ruby_version = ">= 2.4"
20
21
 
21
22
  spec.metadata["homepage_uri"] = spec.homepage
22
23
  spec.metadata["source_code_uri"] = "https://github.com/ruby/pstore"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yukihiro Matsumoto
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-03-12 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: Transactional File Storage for Ruby Objects
14
13
  email:
@@ -17,7 +16,9 @@ executables: []
17
16
  extensions: []
18
17
  extra_rdoc_files: []
19
18
  files:
19
+ - ".document"
20
20
  - ".github/dependabot.yml"
21
+ - ".github/release.yml"
21
22
  - ".github/workflows/push_gem.yml"
22
23
  - ".github/workflows/test.yml"
23
24
  - ".gitignore"
@@ -37,7 +38,6 @@ licenses:
37
38
  metadata:
38
39
  homepage_uri: https://github.com/ruby/pstore
39
40
  source_code_uri: https://github.com/ruby/pstore
40
- post_install_message:
41
41
  rdoc_options: []
42
42
  require_paths:
43
43
  - lib
@@ -45,15 +45,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: '0'
48
+ version: '2.4'
49
49
  required_rubygems_version: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
54
  requirements: []
55
- rubygems_version: 3.5.11
56
- signing_key:
55
+ rubygems_version: 3.6.9
57
56
  specification_version: 4
58
57
  summary: Transactional File Storage for Ruby Objects
59
58
  test_files: []