stdlib/pstore/0/pstore.rbs in rbs-3.0.0.dev.2 vs stdlib/pstore/0/pstore.rbs in rbs-3.0.0.dev.3

- old
+ new

@@ -1,277 +1,492 @@ # <!-- 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 file -# 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. +# 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 +# Marshal. That carries the usual limitations. Proc objects cannot be # marshalled, for example. # -# ## Usage 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... +# # A mock wiki object. # class WikiPage -# def initialize( page_name, author, contents ) +# +# attr_reader :page_name +# +# def initialize(page_name, author, contents) # @page_name = page_name # @revisions = Array.new -# # add_revision(author, contents) # end # -# attr_reader :page_name -# -# def add_revision( author, contents ) -# @revisions << { :created => Time.now, -# :author => author, -# :contents => contents } +# 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 page... -# home_page = WikiPage.new( "HomePage", "James Edward Gray II", -# "A page about the JoysOfDocumentation..." ) +# # Create a new wiki page. +# home_page = WikiPage.new("HomePage", "James Edward Gray II", +# "A page about the JoysOfDocumentation..." ) # -# # then we want to update page data and the index together, or not at all... # wiki = PStore.new("wiki_pages.pstore") -# wiki.transaction do # begin transaction; do all of this or none of it -# # store page... +# # Update page data and the index together, or not at all. +# wiki.transaction do +# # Store page. # wiki[home_page.page_name] = home_page -# # ensure that an index has been created... +# # Create page index. # wiki[:wiki_index] ||= Array.new -# # update wiki index... +# # Update wiki index. # wiki[:wiki_index].push(*home_page.wiki_page_references) -# end # commit changes to wiki data store file +# end # -# ### Some time later... ### -# -# # read wiki data... -# wiki.transaction(true) do # begin read-only transaction, no changes allowed -# wiki.roots.each do |data_root_name| -# p data_root_name -# p wiki[data_root_name] +# # 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 # -# ## Transaction modes -# -# By default, file integrity is only ensured as long as the operating system -# (and the underlying hardware) doesn't raise any unexpected I/O errors. If an -# I/O error occurs while PStore is writing to its file, then the file will -# become corrupted. -# -# You can prevent this by setting *pstore.ultra_safe = true*. However, this -# results in a minor performance loss, and only works on platforms that support -# atomic file renames. Please consult the documentation for `ultra_safe` for -# details. -# -# Needless to say, if you're storing valuable data with PStore, then you should -# backup the PStore files from time to time. -# class PStore public # <!-- # rdoc-file=lib/pstore.rb - # - [](name) + # - [](key) # --> - # Retrieves a value from the PStore file data, by *name*. The hierarchy of Ruby - # objects stored under that root *name* will be returned. + # 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: # - # **WARNING**: This method is only valid in a PStore#transaction. It will - # raise PStore::Error if called at any other time. + # 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 - # - []=(name, value) + # - []=(key, value) # --> - # Stores an individual Ruby object or a hierarchy of Ruby objects in the data - # store file under the root *name*. Assigning to a *name* already in the data - # store clobbers the old data. + # Creates or replaces the value for the given `key`: # - # ## Example: + # example_store do |store| + # temp.transaction do + # temp[:bat] = 3 + # end + # end # - # require "pstore" + # See also [Hierarchical Values](rdoc-ref:PStore@Hierarchical+Values). # - # store = PStore.new("data_file.pstore") - # store.transaction do # begin transaction - # # load some data into the store... - # store[:single_object] = "My data..." - # store[:obj_hierarchy] = { "Kev Jackson" => ["rational.rb", "pstore.rb"], - # "James Gray" => ["erb.rb", "pstore.rb"] } - # end # commit changes to data store file + # Raises an exception if called outside a transaction block. # - # **WARNING**: This method is only valid in a PStore#transaction and it cannot - # be read-only. It will raise PStore::Error if called at any other time. - # def []=: (untyped name, untyped value) -> untyped # <!-- # rdoc-file=lib/pstore.rb # - abort() # --> - # Ends the current PStore#transaction, discarding any changes to the data store. + # Exits the current transaction block, discarding any changes specified in the + # transaction block. See [Committing or + # Aborting](rdoc-ref:PStore@Committing+or+Aborting). # - # ## Example: + # Raises an exception if called outside a transaction block. # - # require "pstore" - # - # store = PStore.new("data_file.pstore") - # store.transaction do # begin transaction - # store[:one] = 1 # this change is not applied, see below... - # store[:two] = 2 # this change is not applied, see below... - # - # store.abort # end transaction here, discard all changes - # - # store[:three] = 3 # this change is never reached - # end - # - # **WARNING**: This method is only valid in a PStore#transaction. It will - # raise PStore::Error if called at any other time. - # def abort: () -> untyped # <!-- # rdoc-file=lib/pstore.rb # - commit() # --> - # Ends the current PStore#transaction, committing any changes to the data store - # immediately. + # Exits the current transaction block, committing any changes specified in the + # transaction block. See [Committing or + # Aborting](rdoc-ref:PStore@Committing+or+Aborting). # - # ## Example: + # Raises an exception if called outside a transaction block. # - # require "pstore" - # - # store = PStore.new("data_file.pstore") - # store.transaction do # begin transaction - # # load some data into the store... - # store[:one] = 1 - # store[:two] = 2 - # - # store.commit # end transaction here, committing changes - # - # store[:three] = 3 # this change is never reached - # end - # - # **WARNING**: This method is only valid in a PStore#transaction. It will - # raise PStore::Error if called at any other time. - # def commit: () -> nil # <!-- # rdoc-file=lib/pstore.rb - # - delete(name) + # - delete(key) # --> - # Removes an object hierarchy from the data store, by *name*. + # Removes and returns the value at `key` if it exists: # - # **WARNING**: This method is only valid in a PStore#transaction and it cannot - # be read-only. It will raise PStore::Error if called at any other time. + # 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(name, default=PStore::Error) + # - fetch(key, default=PStore::Error) # --> - # This method is just like PStore#[], save that you may also provide a *default* - # value for the object. In the event the specified *name* is not found in the - # data store, your *default* will be returned instead. If you do not specify a - # default, PStore::Error will be raised if the object is not found. + # Like #[], except that it accepts a default value for the store. If the `key` + # does not exist: # - # **WARNING**: This method is only valid in a PStore#transaction. It will - # raise PStore::Error if called at any other time. + # * 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 path to the data store file. + # Returns the string file path used to create the store: # + # store.path # => "flat.store" + # def path: () -> untyped # <!-- # rdoc-file=lib/pstore.rb - # - root?(name) + # - root?(key) # --> - # Returns true if the supplied *name* is currently in the data store. # - # **WARNING**: This method is only valid in a PStore#transaction. It will - # raise PStore::Error if called at any other time. - # def root?: (untyped name) -> bool # <!-- # rdoc-file=lib/pstore.rb # - roots() # --> - # Returns the names of all object hierarchies currently in the store. # - # **WARNING**: This method is only valid in a PStore#transaction. It will - # raise PStore::Error if called at any other time. - # def roots: () -> Array[untyped] # <!-- # rdoc-file=lib/pstore.rb # - transaction(read_only = false) { |pstore| ... } # --> - # Opens a new transaction for the data store. Code executed inside a block - # passed to this method may read and write data to and from the data store file. + # Opens a transaction block for the store. See + # [Transactions](rdoc-ref:PStore@Transactions). # - # At the end of the block, changes are committed to the data store - # automatically. You may exit the transaction early with a call to either - # PStore#commit or PStore#abort. See those methods for details about how - # changes are handled. Raising an uncaught Exception in the block is equivalent - # to calling PStore#abort. + # With argument `read_only` as `false`, the block may both read from and write + # to the store. # - # If *read_only* is set to `true`, you will only be allowed to read from the - # data store during the transaction and any attempts to change the data will - # raise a PStore::Error. + # With argument `read_only` as `true`, the block may not include calls to + # #transaction, #[]=, or #delete. # - # Note that PStore does not support nested transactions. + # 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 under - # unlikely-to-occur error conditions such as out-of-space conditions and other - # unusual OS filesystem errors. Setting this flag comes at the price in the form - # of a performance loss. + # Whether PStore should do its best to prevent file corruptions, even when an + # unlikely error (such as memory-error or filesystem error) occurs: # - # This flag only has effect on platforms on which file renames are atomic (e.g. - # all POSIX platforms: Linux, MacOS X, FreeBSD, etc). The default value is - # false. + # * `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 under - # unlikely-to-occur error conditions such as out-of-space conditions and other - # unusual OS filesystem errors. Setting this flag comes at the price in the form - # of a performance loss. + # Whether PStore should do its best to prevent file corruptions, even when an + # unlikely error (such as memory-error or filesystem error) occurs: # - # This flag only has effect on platforms on which file renames are atomic (e.g. - # all POSIX platforms: Linux, MacOS X, FreeBSD, etc). The default value is - # false. + # * `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 @@ -309,14 +524,23 @@ # <!-- # rdoc-file=lib/pstore.rb # - new(file, thread_safe = false) # --> - # To construct a PStore object, pass in the *file* path where you would like the - # data to be stored. + # Returns a new PStore object. # - # PStore objects are always reentrant. But if *thread_safe* is set to true, then - # it will become thread-safe at the cost of a minor performance hit. + # 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