# <!-- rdoc-file=lib/pstore.rb -->
# PStore implements a file based persistence mechanism based on a Hash. User
# code can store hierarchies of Ruby objects (values) into the data store by
# name (keys). An object hierarchy may be just a single object. User code may
# later read values back from the data store or even update data, as needed.
#
# The transactional behavior ensures that any changes succeed or fail together.
# This can be used to ensure that the data store is not left in a transitory
# state, where some values were updated but others were not.
#
# Behind the scenes, Ruby objects are stored to the data store file with
# Marshal. That carries the usual limitations. Proc objects cannot be
# marshalled, for example.
#
# There are three important concepts here (details at the links):
#
# *   [Store](rdoc-ref:PStore@The+Store): a store is an instance of PStore.
# *   [Entries](rdoc-ref:PStore@Entries): the store is hash-like; each entry is
#     the key for a stored object.
# *   [Transactions](rdoc-ref:PStore@Transactions): each transaction is a
#     collection of prospective changes to the store; a transaction is defined
#     in the block given with a call to PStore#transaction.
#
# ## About the Examples
#
# Examples on this page need a store that has known properties. They can get a
# new (and populated) store by calling thus:
#
#     example_store do |store|
#       # Example code using store goes here.
#     end
#
# All we really need to know about `example_store` is that it yields a fresh
# store with a known population of entries; its implementation:
#
#     require 'pstore'
#     require 'tempfile'
#     # Yield a pristine store for use in examples.
#     def example_store
#       # Create the store in a temporary file.
#       Tempfile.create do |file|
#         store = PStore.new(file)
#         # Populate the store.
#         store.transaction do
#           store[:foo] = 0
#           store[:bar] = 1
#           store[:baz] = 2
#         end
#         yield store
#       end
#     end
#
# ## The Store
#
# The contents of the store are maintained in a file whose path is specified
# when the store is created (see PStore.new). The objects are stored and
# retrieved using module Marshal, which means that certain objects cannot be
# added to the store; see [Marshal::dump](rdoc-ref:Marshal.dump).
#
# ## Entries
#
# A store may have any number of entries. Each entry has a key and a value, just
# as in a hash:
#
# *   Key: as in a hash, the key can be (almost) any object; see [Hash
#     Keys](rdoc-ref:Hash@Hash+Keys). You may find it convenient to keep it
#     simple by using only symbols or strings as keys.
# *   Value: the value may be any object that can be marshalled by Marshal (see
#     [Marshal::dump](rdoc-ref:Marshal.dump)) and in fact may be a collection
#     (e.g., an array, a hash, a set, a range, etc). That collection may in turn
#     contain nested objects, including collections, to any depth; those objects
#     must also be Marshal-able. See [Hierarchical
#     Values](rdoc-ref:PStore@Hierarchical+Values).
#
# ## Transactions
#
# ### The Transaction Block
#
# The block given with a call to method #transaction# contains a *transaction*,
# which consists of calls to PStore methods that read from or write to the store
# (that is, all PStore methods except #transaction itself, #path, and
# Pstore.new):
#
#     example_store do |store|
#       store.transaction do
#         store.keys # => [:foo, :bar, :baz]
#         store[:bat] = 3
#         store.keys # => [:foo, :bar, :baz, :bat]
#       end
#     end
#
# Execution of the transaction is deferred until the block exits, and is
# executed *atomically* (all-or-nothing): either all transaction calls are
# executed, or none are. This maintains the integrity of the store.
#
# Other code in the block (including even calls to #path and PStore.new) is
# executed immediately, not deferred.
#
# The transaction block:
#
# *   May not contain a nested call to #transaction.
# *   Is the only context where methods that read from or write to the store are
#     allowed.
#
# As seen above, changes in a transaction are made automatically when the block
# exits. The block may be exited early by calling method #commit or #abort.
#
# *   Method #commit triggers the update to the store and exits the block:
#
#         example_store do |store|
#           store.transaction do
#             store.keys # => [:foo, :bar, :baz]
#             store[:bat] = 3
#             store.commit
#             fail 'Cannot get here'
#           end
#           store.transaction do
#             # Update was completed.
#             store.keys # => [:foo, :bar, :baz, :bat]
#           end
#         end
#
# *   Method #abort discards the update to the store and exits the block:
#
#         example_store do |store|
#           store.transaction do
#             store.keys # => [:foo, :bar, :baz]
#             store[:bat] = 3
#             store.abort
#             fail 'Cannot get here'
#           end
#           store.transaction do
#             # Update was not completed.
#             store.keys # => [:foo, :bar, :baz]
#           end
#         end
#
# ### Read-Only Transactions
#
# By default, a transaction allows both reading from and writing to the store:
#
#     store.transaction do
#       # Read-write transaction.
#       # Any code except a call to #transaction is allowed here.
#     end
#
# If argument `read_only` is passed as `true`, only reading is allowed:
#
#     store.transaction(true) do
#       # Read-only transaction:
#       # Calls to #transaction, #[]=, and #delete are not allowed here.
#     end
#
# ## Hierarchical Values
#
# The value for an entry may be a simple object (as seen above). It may also be
# a hierarchy of objects nested to any depth:
#
#     deep_store = PStore.new('deep.store')
#     deep_store.transaction do
#       array_of_hashes = [{}, {}, {}]
#       deep_store[:array_of_hashes] = array_of_hashes
#       deep_store[:array_of_hashes] # => [{}, {}, {}]
#       hash_of_arrays = {foo: [], bar: [], baz: []}
#       deep_store[:hash_of_arrays] = hash_of_arrays
#       deep_store[:hash_of_arrays]  # => {:foo=>[], :bar=>[], :baz=>[]}
#       deep_store[:hash_of_arrays][:foo].push(:bat)
#       deep_store[:hash_of_arrays]  # => {:foo=>[:bat], :bar=>[], :baz=>[]}
#     end
#
# And recall that you can use [dig methods](rdoc-ref:dig_methods.rdoc) in a
# returned hierarchy of objects.
#
# ## Working with the Store
#
# ### Creating a Store
#
# Use method PStore.new to create a store. The new store creates or opens its
# containing file:
#
#     store = PStore.new('t.store')
#
# ### Modifying the Store
#
# Use method #[]= to update or create an entry:
#
#     example_store do |store|
#       store.transaction do
#         store[:foo] = 1 # Update.
#         store[:bam] = 1 # Create.
#       end
#     end
#
# Use method #delete to remove an entry:
#
#     example_store do |store|
#       store.transaction do
#         store.delete(:foo)
#         store[:foo] # => nil
#       end
#     end
#
# ### Retrieving Values
#
# Use method #fetch (allows default) or #[] (defaults to `nil`) to retrieve an
# entry:
#
#     example_store do |store|
#       store.transaction do
#         store[:foo]             # => 0
#         store[:nope]            # => nil
#         store.fetch(:baz)       # => 2
#         store.fetch(:nope, nil) # => nil
#         store.fetch(:nope)      # Raises exception.
#       end
#     end
#
# ### Querying the Store
#
# Use method #key? to determine whether a given key exists:
#
#     example_store do |store|
#       store.transaction do
#         store.key?(:foo) # => true
#       end
#     end
#
# Use method #keys to retrieve keys:
#
#     example_store do |store|
#       store.transaction do
#         store.keys # => [:foo, :bar, :baz]
#       end
#     end
#
# Use method #path to retrieve the path to the store's underlying file; this
# method may be called from outside a transaction block:
#
#     store = PStore.new('t.store')
#     store.path # => "t.store"
#
# ## Transaction Safety
#
# For transaction safety, see:
#
# *   Optional argument `thread_safe` at method PStore.new.
# *   Attribute #ultra_safe.
#
# Needless to say, if you're storing valuable data with PStore, then you should
# backup the PStore file from time to time.
#
# ## An Example Store
#
#     require "pstore"
#
#     # A mock wiki object.
#     class WikiPage
#
#       attr_reader :page_name
#
#       def initialize(page_name, author, contents)
#         @page_name = page_name
#         @revisions = Array.new
#         add_revision(author, contents)
#       end
#
#       def add_revision(author, contents)
#         @revisions << {created: Time.now,
#                        author: author,
#                        contents: contents}
#       end
#
#       def wiki_page_references
#         [@page_name] + @revisions.last[:contents].scan(/\b(?:[A-Z]+[a-z]+){2,}/)
#       end
#
#     end
#
#     # Create a new wiki page.
#     home_page = WikiPage.new("HomePage", "James Edward Gray II",
#                              "A page about the JoysOfDocumentation..." )
#
#     wiki = PStore.new("wiki_pages.pstore")
#     # Update page data and the index together, or not at all.
#     wiki.transaction do
#       # Store page.
#       wiki[home_page.page_name] = home_page
#       # Create page index.
#       wiki[:wiki_index] ||= Array.new
#       # Update wiki index.
#       wiki[:wiki_index].push(*home_page.wiki_page_references)
#     end
#
#     # Read wiki data, setting argument read_only to true.
#     wiki.transaction(true) do
#       wiki.keys.each do |key|
#         puts key
#         puts wiki[key]
#       end
#     end
#
class PStore
  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - [](key)
  # -->
  # Returns the value for the given `key` if the key exists. `nil` otherwise; if
  # not `nil`, the returned value is an object or a hierarchy of objects:
  #
  #     example_store do |store|
  #       store.transaction do
  #         store[:foo]  # => 0
  #         store[:nope] # => nil
  #       end
  #     end
  #
  # Returns `nil` if there is no such key.
  #
  # See also [Hierarchical Values](rdoc-ref:PStore@Hierarchical+Values).
  #
  # Raises an exception if called outside a transaction block.
  #
  def []: (untyped name) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - []=(key, value)
  # -->
  # Creates or replaces the value for the given `key`:
  #
  #     example_store do |store|
  #       temp.transaction do
  #         temp[:bat] = 3
  #       end
  #     end
  #
  # See also [Hierarchical Values](rdoc-ref:PStore@Hierarchical+Values).
  #
  # Raises an exception if called outside a transaction block.
  #
  def []=: (untyped name, untyped value) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - abort()
  # -->
  # Exits the current transaction block, discarding any changes specified in the
  # [transaction block](rdoc-ref:PStore@The+Transaction+Block).
  #
  # Raises an exception if called outside a transaction block.
  #
  def abort: () -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - commit()
  # -->
  # Exits the current transaction block, committing any changes specified in the
  # [transaction block](rdoc-ref:PStore@The+Transaction+Block).
  #
  # Raises an exception if called outside a transaction block.
  #
  def commit: () -> nil

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - delete(key)
  # -->
  # Removes and returns the value at `key` if it exists:
  #
  #     example_store do |store|
  #       store.transaction do
  #         store[:bat] = 3
  #         store.delete(:bat)
  #       end
  #     end
  #
  # Returns `nil` if there is no such key.
  #
  # Raises an exception if called outside a transaction block.
  #
  def delete: (untyped name) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - fetch(key, default=PStore::Error)
  # -->
  # Like #[], except that it accepts a default value for the store. If the `key`
  # does not exist:
  #
  # *   Raises an exception if `default` is `PStore::Error`.
  # *   Returns the value of `default` otherwise:
  #
  #         example_store do |store|
  #           store.transaction do
  #             store.fetch(:nope, nil) # => nil
  #             store.fetch(:nope)      # Raises an exception.
  #           end
  #         end
  #
  # Raises an exception if called outside a transaction block.
  #
  def fetch: (untyped name, ?untyped default) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - path()
  # -->
  # Returns the string file path used to create the store:
  #
  #     store.path # => "flat.store"
  #
  def path: () -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - root?(key)
  # -->
  #
  def root?: (untyped name) -> bool

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - roots()
  # -->
  #
  def roots: () -> Array[untyped]

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - transaction(read_only = false) { |pstore| ... }
  # -->
  # Opens a transaction block for the store. See
  # [Transactions](rdoc-ref:PStore@Transactions).
  #
  # With argument `read_only` as `false`, the block may both read from and write
  # to the store.
  #
  # With argument `read_only` as `true`, the block may not include calls to
  # #transaction, #[]=, or #delete.
  #
  # Raises an exception if called within a transaction block.
  #
  def transaction: (?untyped read_only) -> untyped

  # <!-- rdoc-file=lib/pstore.rb -->
  # Whether PStore should do its best to prevent file corruptions, even when an
  # unlikely error (such as memory-error or filesystem error) occurs:
  #
  # *   `true`: changes are posted by creating a temporary file, writing the
  #     updated data to it, then renaming the file to the given #path. File
  #     integrity is maintained. Note: has effect only if the filesystem has
  #     atomic file rename (as do POSIX platforms Linux, MacOS, FreeBSD and
  #     others).
  #
  # *   `false` (the default): changes are posted by rewinding the open file and
  #     writing the updated data. File integrity is maintained if the filesystem
  #     raises no unexpected I/O error; if such an error occurs during a write to
  #     the store, the file may become corrupted.
  #
  def ultra_safe: () -> untyped

  # <!-- rdoc-file=lib/pstore.rb -->
  # Whether PStore should do its best to prevent file corruptions, even when an
  # unlikely error (such as memory-error or filesystem error) occurs:
  #
  # *   `true`: changes are posted by creating a temporary file, writing the
  #     updated data to it, then renaming the file to the given #path. File
  #     integrity is maintained. Note: has effect only if the filesystem has
  #     atomic file rename (as do POSIX platforms Linux, MacOS, FreeBSD and
  #     others).
  #
  # *   `false` (the default): changes are posted by rewinding the open file and
  #     writing the updated data. File integrity is maintained if the filesystem
  #     raises no unexpected I/O error; if such an error occurs during a write to
  #     the store, the file may become corrupted.
  #
  def ultra_safe=: (untyped) -> untyped

  private

  def dump: (untyped table) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - empty_marshal_checksum()
  # -->
  #
  def empty_marshal_checksum: () -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - empty_marshal_data()
  # -->
  #
  def empty_marshal_data: () -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - in_transaction()
  # -->
  # Raises PStore::Error if the calling code is not in a PStore#transaction.
  #
  def in_transaction: () -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - in_transaction_wr()
  # -->
  # Raises PStore::Error if the calling code is not in a PStore#transaction or if
  # the code is in a read-only PStore#transaction.
  #
  def in_transaction_wr: () -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - new(file, thread_safe = false)
  # -->
  # Returns a new PStore object.
  #
  # Argument `file` is the path to the file in which objects are to be stored; if
  # the file exists, it should be one that was written by PStore.
  #
  #     path = 't.store'
  #     store = PStore.new(path)
  #
  # A PStore object is
  # [reentrant](https://en.wikipedia.org/wiki/Reentrancy_(computing)). If argument
  # `thread_safe` is given as `true`, the object is also thread-safe (at the cost
  # of a small performance penalty):
  #
  #     store = PStore.new(path, true)
  #
  def initialize: (untyped file, ?boolish thread_safe) -> untyped

  def load: (untyped content) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - load_data(file, read_only)
  # -->
  # Load the given PStore file. If `read_only` is true, the unmarshalled Hash will
  # be returned. If `read_only` is false, a 3-tuple will be returned: the
  # unmarshalled Hash, a checksum of the data, and the size of the data.
  #
  def load_data: (untyped file, untyped read_only) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - on_windows?()
  # -->
  #
  def on_windows?: () -> bool

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - open_and_lock_file(filename, read_only)
  # -->
  # Open the specified filename (either in read-only mode or in read-write mode)
  # and lock it for reading or writing.
  #
  # The opened File object will be returned. If *read_only* is true, and the file
  # does not exist, then nil will be returned.
  #
  # All exceptions are propagated.
  #
  def open_and_lock_file: (untyped filename, untyped read_only) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - save_data(original_checksum, original_file_size, file)
  # -->
  #
  def save_data: (untyped original_checksum, untyped original_file_size, untyped file) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - save_data_with_atomic_file_rename_strategy(data, file)
  # -->
  #
  def save_data_with_atomic_file_rename_strategy: (untyped data, untyped file) -> untyped

  # <!--
  #   rdoc-file=lib/pstore.rb
  #   - save_data_with_fast_strategy(data, file)
  # -->
  #
  def save_data_with_fast_strategy: (untyped data, untyped file) -> untyped
end

PStore::EMPTY_MARSHAL_CHECKSUM: String

PStore::EMPTY_MARSHAL_DATA: String

PStore::EMPTY_STRING: String

PStore::RDWR_ACCESS: Hash[untyped, untyped]

PStore::RD_ACCESS: Hash[untyped, untyped]

PStore::VERSION: String

PStore::WR_ACCESS: Hash[untyped, untyped]
