2017-01-17 15:47:38 +03:00
|
|
|
# frozen_string_literal: true
|
2005-10-27 12:18:38 +04:00
|
|
|
# = PStore -- Transactional File Storage for Ruby Objects
|
1999-01-20 07:59:39 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# pstore.rb -
|
2005-11-01 16:04:35 +03:00
|
|
|
# originally by matz
|
2005-10-27 12:18:38 +04:00
|
|
|
# documentation by Kev Jackson and James Edward Gray II
|
2008-04-10 11:12:41 +04:00
|
|
|
# improved by Hongli Lai
|
1998-01-16 15:19:09 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# See PStore for documentation.
|
1998-01-16 15:19:09 +03:00
|
|
|
|
2016-09-28 17:14:21 +03:00
|
|
|
require "digest"
|
1998-01-16 15:19:09 +03:00
|
|
|
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
2008-06-04 13:37:38 +04:00
|
|
|
# PStore implements a file based persistence mechanism based on a Hash. User
|
2005-10-27 12:18:38 +04:00
|
|
|
# code can store hierarchies of Ruby objects (values) into the data store file
|
2009-03-06 06:56:38 +03:00
|
|
|
# by name (keys). An object hierarchy may be just a single object. User code
|
2005-10-27 12:18:38 +04:00
|
|
|
# may later read values back from the data store or even update data, as needed.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# 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
|
2008-06-04 13:37:38 +04:00
|
|
|
# state, where some values were updated but others were not.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
|
|
|
# Behind the scenes, Ruby objects are stored to the data store file with
|
|
|
|
# Marshal. That carries the usual limitations. Proc objects cannot be
|
2005-10-27 12:18:38 +04:00
|
|
|
# marshalled, for example.
|
|
|
|
#
|
|
|
|
# == Usage example:
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# require "pstore"
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# # a mock wiki object...
|
|
|
|
# class WikiPage
|
|
|
|
# def initialize( page_name, author, contents )
|
|
|
|
# @page_name = page_name
|
|
|
|
# @revisions = Array.new
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# add_revision(author, contents)
|
|
|
|
# end
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# attr_reader :page_name
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# def add_revision( author, contents )
|
|
|
|
# @revisions << { :created => Time.now,
|
|
|
|
# :author => author,
|
|
|
|
# :contents => contents }
|
|
|
|
# end
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# def wiki_page_references
|
|
|
|
# [@page_name] + @revisions.last[:contents].scan(/\b(?:[A-Z]+[a-z]+){2,}/)
|
|
|
|
# end
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# # ...
|
|
|
|
# end
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# # create a new page...
|
|
|
|
# home_page = WikiPage.new( "HomePage", "James Edward Gray II",
|
|
|
|
# "A page about the JoysOfDocumentation..." )
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# # 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...
|
|
|
|
# wiki[home_page.page_name] = home_page
|
|
|
|
# # ensure that an index has been created...
|
|
|
|
# wiki[:wiki_index] ||= Array.new
|
|
|
|
# # update wiki index...
|
|
|
|
# wiki[:wiki_index].push(*home_page.wiki_page_references)
|
|
|
|
# end # commit changes to wiki data store file
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# ### Some time later... ###
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# # 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]
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2008-04-10 11:12:41 +04:00
|
|
|
# == 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 <em>pstore.ultra_safe = true</em>.
|
|
|
|
# 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.
|
1998-01-16 15:19:09 +03:00
|
|
|
class PStore
|
2020-12-22 15:43:30 +03:00
|
|
|
VERSION = "0.1.1"
|
2020-07-28 18:31:52 +03:00
|
|
|
|
2011-09-13 10:02:59 +04:00
|
|
|
RDWR_ACCESS = {mode: IO::RDWR | IO::CREAT | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
|
|
|
|
RD_ACCESS = {mode: IO::RDONLY | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
|
|
|
|
WR_ACCESS = {mode: IO::WRONLY | IO::CREAT | IO::TRUNC | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze
|
2006-09-08 23:56:37 +04:00
|
|
|
|
2005-10-27 12:18:38 +04:00
|
|
|
# The error type thrown by all PStore methods.
|
1999-01-20 07:59:39 +03:00
|
|
|
class Error < StandardError
|
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
# 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.
|
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
attr_accessor :ultra_safe
|
1998-01-16 15:19:09 +03:00
|
|
|
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
|
|
|
# To construct a PStore object, pass in the _file_ path where you would like
|
2005-10-27 12:18:38 +04:00
|
|
|
# the data to be stored.
|
2008-04-10 11:12:41 +04:00
|
|
|
#
|
|
|
|
# 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.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2008-04-10 11:12:41 +04:00
|
|
|
def initialize(file, thread_safe = false)
|
1998-01-16 15:19:09 +03:00
|
|
|
dir = File::dirname(file)
|
|
|
|
unless File::directory? dir
|
|
|
|
raise PStore::Error, format("directory %s does not exist", dir)
|
|
|
|
end
|
|
|
|
if File::exist? file and not File::readable? file
|
|
|
|
raise PStore::Error, format("file %s not readable", file)
|
|
|
|
end
|
|
|
|
@filename = file
|
|
|
|
@abort = false
|
2008-04-10 11:12:41 +04:00
|
|
|
@ultra_safe = false
|
2010-02-15 17:09:19 +03:00
|
|
|
@thread_safe = thread_safe
|
2016-08-30 09:22:30 +03:00
|
|
|
@lock = Thread::Mutex.new
|
1998-01-16 15:19:09 +03:00
|
|
|
end
|
|
|
|
|
2005-10-27 12:18:38 +04:00
|
|
|
# Raises PStore::Error if the calling code is not in a PStore#transaction.
|
1998-01-16 15:19:09 +03:00
|
|
|
def in_transaction
|
2011-03-07 17:03:41 +03:00
|
|
|
raise PStore::Error, "not in transaction" unless @lock.locked?
|
1998-01-16 15:19:09 +03:00
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# Raises PStore::Error if the calling code is not in a PStore#transaction or
|
|
|
|
# if the code is in a read-only PStore#transaction.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2011-12-20 18:57:29 +04:00
|
|
|
def in_transaction_wr
|
|
|
|
in_transaction
|
2004-02-17 18:26:03 +03:00
|
|
|
raise PStore::Error, "in read-only transaction" if @rdonly
|
|
|
|
end
|
|
|
|
private :in_transaction, :in_transaction_wr
|
1998-01-16 15:19:09 +03:00
|
|
|
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
2009-03-06 06:56:38 +03:00
|
|
|
# Retrieves a value from the PStore file data, by _name_. The hierarchy of
|
2005-10-27 12:18:38 +04:00
|
|
|
# Ruby objects stored under that root _name_ will be returned.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# *WARNING*: This method is only valid in a PStore#transaction. It will
|
|
|
|
# raise PStore::Error if called at any other time.
|
|
|
|
#
|
1998-01-16 15:19:09 +03:00
|
|
|
def [](name)
|
|
|
|
in_transaction
|
2002-07-11 12:22:18 +04:00
|
|
|
@table[name]
|
|
|
|
end
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
2009-03-06 06:56:38 +03:00
|
|
|
# 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
|
2005-10-27 12:18:38 +04:00
|
|
|
# found.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# *WARNING*: This method is only valid in a PStore#transaction. It will
|
|
|
|
# raise PStore::Error if called at any other time.
|
|
|
|
#
|
2002-07-11 12:22:18 +04:00
|
|
|
def fetch(name, default=PStore::Error)
|
2006-02-05 08:28:11 +03:00
|
|
|
in_transaction
|
2001-05-06 19:06:00 +04:00
|
|
|
unless @table.key? name
|
2008-04-10 11:12:41 +04:00
|
|
|
if default == PStore::Error
|
|
|
|
raise PStore::Error, format("undefined root name `%s'", name)
|
2002-07-11 12:22:18 +04:00
|
|
|
else
|
2008-04-10 11:12:41 +04:00
|
|
|
return default
|
2002-07-11 12:22:18 +04:00
|
|
|
end
|
1998-01-16 15:19:09 +03:00
|
|
|
end
|
2006-02-05 08:28:11 +03:00
|
|
|
@table[name]
|
1998-01-16 15:19:09 +03:00
|
|
|
end
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
|
|
|
# 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.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# == Example:
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# require "pstore"
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# store = PStore.new("data_file.pstore")
|
|
|
|
# store.transaction do # begin transaction
|
|
|
|
# # load some data into the store...
|
|
|
|
# store[:single_object] = "My data..."
|
2015-12-23 06:43:23 +03:00
|
|
|
# store[:obj_hierarchy] = { "Kev Jackson" => ["rational.rb", "pstore.rb"],
|
2005-10-27 12:18:38 +04:00
|
|
|
# "James Gray" => ["erb.rb", "pstore.rb"] }
|
|
|
|
# end # commit changes to data store file
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# *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.
|
|
|
|
#
|
1998-01-16 15:19:09 +03:00
|
|
|
def []=(name, value)
|
2011-12-20 18:57:29 +04:00
|
|
|
in_transaction_wr
|
1998-01-16 15:19:09 +03:00
|
|
|
@table[name] = value
|
|
|
|
end
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
|
|
|
# Removes an object hierarchy from the data store, by _name_.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# *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.
|
|
|
|
#
|
2000-03-06 07:15:42 +03:00
|
|
|
def delete(name)
|
2011-12-20 18:57:29 +04:00
|
|
|
in_transaction_wr
|
2000-03-06 07:15:42 +03:00
|
|
|
@table.delete name
|
|
|
|
end
|
1998-01-16 15:19:09 +03:00
|
|
|
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
|
|
|
# Returns the names of all object hierarchies currently in the store.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# *WARNING*: This method is only valid in a PStore#transaction. It will
|
|
|
|
# raise PStore::Error if called at any other time.
|
|
|
|
#
|
1998-01-16 15:19:09 +03:00
|
|
|
def roots
|
|
|
|
in_transaction
|
|
|
|
@table.keys
|
|
|
|
end
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
|
|
|
# Returns true if the supplied _name_ is currently in the data store.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# *WARNING*: This method is only valid in a PStore#transaction. It will
|
|
|
|
# raise PStore::Error if called at any other time.
|
|
|
|
#
|
1998-01-16 15:19:09 +03:00
|
|
|
def root?(name)
|
|
|
|
in_transaction
|
|
|
|
@table.key? name
|
|
|
|
end
|
2005-10-27 12:18:38 +04:00
|
|
|
# Returns the path to the data store file.
|
1998-01-16 15:19:09 +03:00
|
|
|
def path
|
|
|
|
@filename
|
|
|
|
end
|
|
|
|
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
|
|
|
# Ends the current PStore#transaction, committing any changes to the data
|
|
|
|
# store immediately.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# == Example:
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# require "pstore"
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# store = PStore.new("data_file.pstore")
|
|
|
|
# store.transaction do # begin transaction
|
|
|
|
# # load some data into the store...
|
|
|
|
# store[:one] = 1
|
|
|
|
# store[:two] = 2
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# store.commit # end transaction here, committing changes
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# store[:three] = 3 # this change is never reached
|
|
|
|
# end
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# *WARNING*: This method is only valid in a PStore#transaction. It will
|
|
|
|
# raise PStore::Error if called at any other time.
|
|
|
|
#
|
1998-01-16 15:19:09 +03:00
|
|
|
def commit
|
2001-05-06 19:06:00 +04:00
|
|
|
in_transaction
|
1998-01-16 15:19:09 +03:00
|
|
|
@abort = false
|
|
|
|
throw :pstore_abort_transaction
|
|
|
|
end
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
|
|
|
# Ends the current PStore#transaction, discarding any changes to the data
|
|
|
|
# store.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# == Example:
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# require "pstore"
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# 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...
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# store.abort # end transaction here, discard all changes
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# store[:three] = 3 # this change is never reached
|
|
|
|
# end
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# *WARNING*: This method is only valid in a PStore#transaction. It will
|
|
|
|
# raise PStore::Error if called at any other time.
|
|
|
|
#
|
1998-01-16 15:19:09 +03:00
|
|
|
def abort
|
2001-05-06 19:06:00 +04:00
|
|
|
in_transaction
|
1998-01-16 15:19:09 +03:00
|
|
|
@abort = true
|
|
|
|
throw :pstore_abort_transaction
|
|
|
|
end
|
|
|
|
|
2005-10-27 12:18:38 +04:00
|
|
|
#
|
|
|
|
# Opens a new transaction for the data store. Code executed inside a block
|
2009-03-06 06:56:38 +03:00
|
|
|
# passed to this method may read and write data to and from the data store
|
2005-10-27 12:18:38 +04:00
|
|
|
# file.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# At the end of the block, changes are committed to the data store
|
2009-03-06 06:56:38 +03:00
|
|
|
# automatically. You may exit the transaction early with a call to either
|
2005-10-27 12:18:38 +04:00
|
|
|
# PStore#commit or PStore#abort. See those methods for details about how
|
2009-03-06 06:56:38 +03:00
|
|
|
# changes are handled. Raising an uncaught Exception in the block is
|
2005-10-27 12:18:38 +04:00
|
|
|
# equivalent to calling PStore#abort.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# 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.
|
2009-03-06 06:56:38 +03:00
|
|
|
#
|
2005-10-27 12:18:38 +04:00
|
|
|
# Note that PStore does not support nested transactions.
|
|
|
|
#
|
* ext/pathname/lib/pathname.rb, ext/tk/lib/multi-tk.rb,
ext/tk/sample/demos-en/widget, lib/benchmark.rb, lib/irb/cmd/fork.rb,
lib/mkmf.rb, lib/net/ftp.rb, lib/net/smtp.rb, lib/open3.rb,
lib/pstore.rb, lib/rexml/element.rb, lib/rexml/light/node.rb,
lib/rinda/tuplespace.rb, lib/rss/maker/base.rb,
lib/rss/maker/entry.rb, lib/scanf.rb, lib/set.rb, lib/shell.rb,
lib/shell/command-processor.rb, lib/shell/process-controller.rb,
lib/shell/system-command.rb, lib/uri/common.rb: remove unused block
arguments to avoid creating Proc objects.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@33638 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2011-11-05 11:37:47 +04:00
|
|
|
def transaction(read_only = false) # :yields: pstore
|
2008-04-10 11:12:41 +04:00
|
|
|
value = nil
|
2012-11-11 08:23:04 +04:00
|
|
|
if !@thread_safe
|
|
|
|
raise PStore::Error, "nested transaction" unless @lock.try_lock
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
@lock.lock
|
|
|
|
rescue ThreadError
|
|
|
|
raise PStore::Error, "nested transaction"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
begin
|
2004-02-17 18:26:03 +03:00
|
|
|
@rdonly = read_only
|
2008-04-10 11:12:41 +04:00
|
|
|
@abort = false
|
|
|
|
file = open_and_lock_file(@filename, read_only)
|
|
|
|
if file
|
|
|
|
begin
|
|
|
|
@table, checksum, original_data_size = load_data(file, read_only)
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
catch(:pstore_abort_transaction) do
|
|
|
|
value = yield(self)
|
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
if !@abort && !read_only
|
|
|
|
save_data(checksum, original_data_size, file)
|
|
|
|
end
|
|
|
|
ensure
|
2016-11-22 02:05:41 +03:00
|
|
|
file.close
|
2008-04-10 11:12:41 +04:00
|
|
|
end
|
2004-02-17 18:26:03 +03:00
|
|
|
else
|
2008-04-10 11:12:41 +04:00
|
|
|
# This can only occur if read_only == true.
|
|
|
|
@table = {}
|
|
|
|
catch(:pstore_abort_transaction) do
|
|
|
|
value = yield(self)
|
|
|
|
end
|
|
|
|
end
|
2012-11-11 08:23:04 +04:00
|
|
|
ensure
|
|
|
|
@lock.unlock
|
2008-04-10 11:12:41 +04:00
|
|
|
end
|
2008-07-10 17:06:03 +04:00
|
|
|
value
|
2008-04-10 11:12:41 +04:00
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
private
|
|
|
|
# Constant for relieving Ruby's garbage collector.
|
2016-09-28 17:14:21 +03:00
|
|
|
CHECKSUM_ALGO = %w[SHA512 SHA384 SHA256 SHA1 RMD160 MD5].each do |algo|
|
|
|
|
begin
|
|
|
|
break Digest(algo)
|
|
|
|
rescue LoadError
|
|
|
|
end
|
|
|
|
end
|
2008-04-10 11:12:41 +04:00
|
|
|
EMPTY_STRING = ""
|
|
|
|
EMPTY_MARSHAL_DATA = Marshal.dump({})
|
2016-06-28 16:55:02 +03:00
|
|
|
EMPTY_MARSHAL_CHECKSUM = CHECKSUM_ALGO.digest(EMPTY_MARSHAL_DATA)
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
#
|
|
|
|
# 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(filename, read_only)
|
|
|
|
if read_only
|
|
|
|
begin
|
Make rb_scan_args handle keywords more similar to Ruby methods (#2460)
Cfuncs that use rb_scan_args with the : entry suffer similar keyword
argument separation issues that Ruby methods suffer if the cfuncs
accept optional or variable arguments.
This makes the following changes to : handling.
* Treats as **kw, prompting keyword argument separation warnings
if called with a positional hash.
* Do not look for an option hash if empty keywords are provided.
For backwards compatibility, treat an empty keyword splat as a empty
mandatory positional hash argument, but emit a a warning, as this
behavior will be removed in Ruby 3. The argument number check
needs to be moved lower so it can correctly handle an empty
positional argument being added.
* If the last argument is nil and it is necessary to treat it as an option
hash in order to make sure all arguments are processed, continue to
treat the last argument as the option hash. Emit a warning in this case,
as this behavior will be removed in Ruby 3.
* If splitting the keyword hash into two hashes, issue a warning, as we
will not be splitting hashes in Ruby 3.
* If the keyword argument is required to fill a mandatory positional
argument, continue to do so, but emit a warning as this behavior will
be going away in Ruby 3.
* If keyword arguments are provided and the last argument is not a hash,
that indicates something wrong. This can happen if a cfunc is calling
rb_scan_args multiple times, and providing arguments that were not
passed to it from Ruby. Callers need to switch to the new
rb_scan_args_kw function, which allows passing of whether keywords
were provided.
This commit fixes all warnings caused by the changes above.
It switches some function calls to *_kw versions with appropriate
kw_splat flags. If delegating arguments, RB_PASS_CALLED_KEYWORDS
is used. If creating new arguments, RB_PASS_KEYWORDS is used if
the last argument is a hash to be treated as keywords.
In open_key_args in io.c, use rb_scan_args_kw.
In this case, the arguments provided come from another C
function, not Ruby. The last argument may or may not be a hash,
so we can't set keyword argument mode. However, if it is a
hash, we don't want to warn when treating it as keywords.
In Ruby files, make sure to appropriately use keyword splats
or literal keywords when calling Cfuncs that now issue keyword
argument separation warnings through rb_scan_args. Also, make
sure not to pass nil in place of an option hash.
Work around Kernel#warn warnings due to problems in the Rubygems
override of the method. There is an open pull request to fix
these issues in Rubygems, but part of the Rubygems tests for
their override fail on ruby-head due to rb_scan_args not
recognizing empty keyword splats, which this commit fixes.
Implementation wise, adding rb_scan_args_kw is kind of a pain,
because rb_scan_args takes a variable number of arguments.
In order to not duplicate all the code, the function internals need
to be split into two functions taking a va_list, and to avoid passing
in a ton of arguments, a single struct argument is used to handle
the variables previously local to the function.
2019-09-25 21:18:49 +03:00
|
|
|
file = File.new(filename, **RD_ACCESS)
|
* array.c: replace rb_protect_inspect() and rb_inspecting_p() by
rb_exec_recursive() in eval.c.
* eval.c (rb_exec_recursive): new function.
* array.c (rb_ary_join): use rb_exec_recursive().
* array.c (rb_ary_inspect, rb_ary_hash): ditto.
* file.c (rb_file_join): ditto.
* hash.c (rb_hash_inspect, rb_hash_to_s, rb_hash_hash): ditto.
* io.c (rb_io_puts): ditto.
* object.c (rb_obj_inspect): ditto
* struct.c (rb_struct_inspect): ditto.
* lib/set.rb (SortedSet::setup): a hack to shut up warning.
[ruby-talk:132866]
* lib/time.rb (Time::strptime): add new function. inspired by
[ruby-talk:132815].
* lib/parsedate.rb (ParseDate::strptime): ditto.
* regparse.c: move st_*_strend() functions from st.c. fixed some
potential memory leaks.
* exception error messages updated. [ruby-core:04497]
* ext/socket/socket.c (Init_socket): add bunch of Socket
constants. Patch from Sam Roberts <sroberts@uniserve.com>.
[ruby-core:04409]
* array.c (rb_ary_s_create): no need for negative argc check.
[ruby-core:04463]
* array.c (rb_ary_unshift_m): ditto.
* lib/xmlrpc/parser.rb (XMLRPC::FaultException): make it subclass
of StandardError class, not Exception class. [ruby-core:04429]
* parse.y (fcall_gen): lvar(arg) will be evaluated as
lvar.call(arg) when lvar is a defined local variable. [new]
* object.c (rb_class_initialize): call inherited method before
calling initializing block.
* eval.c (rb_thread_start_1): initialize newly pushed frame.
* lib/open3.rb (Open3::popen3): $? should not be EXIT_FAILURE.
fixed: [ruby-core:04444]
* eval.c (is_defined): NODE_IASGN is an assignment.
* ext/readline/readline.c (Readline.readline): use rl_outstream
and rl_instream. [ruby-dev:25699]
* ext/etc/etc.c (Init_etc): sGroup needs HAVE_ST_GR_PASSWD check
[ruby-dev:25675]
* misc/ruby-mode.el: [ruby-core:04415]
* lib/rdoc/generators/html_generator.rb: [ruby-core:04412]
* lib/rdoc/generators/ri_generator.rb: ditto.
* struct.c (make_struct): fixed: [ruby-core:04402]
* ext/curses/curses.c (window_color_set): [ruby-core:04393]
* ext/socket/socket.c (Init_socket): SO_REUSEPORT added.
[ruby-talk:130092]
* object.c: [ruby-doc:818]
* parse.y (open_args): fix too verbose warnings for the space
before argument parentheses. [ruby-dev:25492]
* parse.y (parser_yylex): ditto.
* parse.y (parser_yylex): the first expression in the parentheses
should not be a command. [ruby-dev:25492]
* lib/irb/context.rb (IRB::Context::initialize): [ruby-core:04330]
* object.c (Init_Object): remove Object#type. [ruby-core:04335]
* st.c (st_foreach): report success/failure by return value.
[ruby-Bugs-1396]
* parse.y: forgot to initialize parser struct. [ruby-dev:25492]
* parse.y (parser_yylex): no tLABEL on EXPR_BEG.
[ruby-talk:127711]
* document updates - [ruby-core:04296], [ruby-core:04301],
[ruby-core:04302], [ruby-core:04307]
* dir.c (rb_push_glob): should work for NUL delimited patterns.
* dir.c (rb_glob2): should aware of offset in the pattern.
* string.c (rb_str_new4): should propagate taintedness.
* env.h: rename member names in struct FRAME; last_func -> callee,
orig_func -> this_func, last_class -> this_class.
* struct.c (rb_struct_set): use original method name, not callee
name, to retrieve member slot. [ruby-core:04268]
* time.c (time_strftime): protect from format modification from GC
finalizers.
* object.c (Init_Object): remove rb_obj_id_obsolete()
* eval.c (rb_mod_define_method): incomplete subclass check.
[ruby-dev:25464]
* gc.c (rb_data_object_alloc): klass may be NULL.
[ruby-list:40498]
* bignum.c (rb_big_rand): should return positive random number.
[ruby-dev:25401]
* bignum.c (rb_big_rand): do not use rb_big_modulo to generate
random bignums. [ruby-dev:25396]
* variable.c (rb_autoload): [ruby-dev:25373]
* eval.c (svalue_to_avalue): [ruby-dev:25366]
* string.c (rb_str_justify): [ruby-dev:25367]
* io.c (rb_f_select): [ruby-dev:25312]
* ext/socket/socket.c (sock_s_getservbyport): [ruby-talk:124072]
* struct.c (make_struct): [ruby-dev:25249]
* dir.c (dir_open_dir): new function. [ruby-dev:25242]
* io.c (rb_f_open): add type check for return value from to_open.
* lib/pstore.rb (PStore#transaction): Use the empty content when a
file is not found. [ruby-dev:24561]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@8068 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2005-03-04 09:47:45 +03:00
|
|
|
begin
|
|
|
|
file.flock(File::LOCK_SH)
|
2008-04-10 11:12:41 +04:00
|
|
|
return file
|
|
|
|
rescue
|
|
|
|
file.close
|
|
|
|
raise
|
* array.c: replace rb_protect_inspect() and rb_inspecting_p() by
rb_exec_recursive() in eval.c.
* eval.c (rb_exec_recursive): new function.
* array.c (rb_ary_join): use rb_exec_recursive().
* array.c (rb_ary_inspect, rb_ary_hash): ditto.
* file.c (rb_file_join): ditto.
* hash.c (rb_hash_inspect, rb_hash_to_s, rb_hash_hash): ditto.
* io.c (rb_io_puts): ditto.
* object.c (rb_obj_inspect): ditto
* struct.c (rb_struct_inspect): ditto.
* lib/set.rb (SortedSet::setup): a hack to shut up warning.
[ruby-talk:132866]
* lib/time.rb (Time::strptime): add new function. inspired by
[ruby-talk:132815].
* lib/parsedate.rb (ParseDate::strptime): ditto.
* regparse.c: move st_*_strend() functions from st.c. fixed some
potential memory leaks.
* exception error messages updated. [ruby-core:04497]
* ext/socket/socket.c (Init_socket): add bunch of Socket
constants. Patch from Sam Roberts <sroberts@uniserve.com>.
[ruby-core:04409]
* array.c (rb_ary_s_create): no need for negative argc check.
[ruby-core:04463]
* array.c (rb_ary_unshift_m): ditto.
* lib/xmlrpc/parser.rb (XMLRPC::FaultException): make it subclass
of StandardError class, not Exception class. [ruby-core:04429]
* parse.y (fcall_gen): lvar(arg) will be evaluated as
lvar.call(arg) when lvar is a defined local variable. [new]
* object.c (rb_class_initialize): call inherited method before
calling initializing block.
* eval.c (rb_thread_start_1): initialize newly pushed frame.
* lib/open3.rb (Open3::popen3): $? should not be EXIT_FAILURE.
fixed: [ruby-core:04444]
* eval.c (is_defined): NODE_IASGN is an assignment.
* ext/readline/readline.c (Readline.readline): use rl_outstream
and rl_instream. [ruby-dev:25699]
* ext/etc/etc.c (Init_etc): sGroup needs HAVE_ST_GR_PASSWD check
[ruby-dev:25675]
* misc/ruby-mode.el: [ruby-core:04415]
* lib/rdoc/generators/html_generator.rb: [ruby-core:04412]
* lib/rdoc/generators/ri_generator.rb: ditto.
* struct.c (make_struct): fixed: [ruby-core:04402]
* ext/curses/curses.c (window_color_set): [ruby-core:04393]
* ext/socket/socket.c (Init_socket): SO_REUSEPORT added.
[ruby-talk:130092]
* object.c: [ruby-doc:818]
* parse.y (open_args): fix too verbose warnings for the space
before argument parentheses. [ruby-dev:25492]
* parse.y (parser_yylex): ditto.
* parse.y (parser_yylex): the first expression in the parentheses
should not be a command. [ruby-dev:25492]
* lib/irb/context.rb (IRB::Context::initialize): [ruby-core:04330]
* object.c (Init_Object): remove Object#type. [ruby-core:04335]
* st.c (st_foreach): report success/failure by return value.
[ruby-Bugs-1396]
* parse.y: forgot to initialize parser struct. [ruby-dev:25492]
* parse.y (parser_yylex): no tLABEL on EXPR_BEG.
[ruby-talk:127711]
* document updates - [ruby-core:04296], [ruby-core:04301],
[ruby-core:04302], [ruby-core:04307]
* dir.c (rb_push_glob): should work for NUL delimited patterns.
* dir.c (rb_glob2): should aware of offset in the pattern.
* string.c (rb_str_new4): should propagate taintedness.
* env.h: rename member names in struct FRAME; last_func -> callee,
orig_func -> this_func, last_class -> this_class.
* struct.c (rb_struct_set): use original method name, not callee
name, to retrieve member slot. [ruby-core:04268]
* time.c (time_strftime): protect from format modification from GC
finalizers.
* object.c (Init_Object): remove rb_obj_id_obsolete()
* eval.c (rb_mod_define_method): incomplete subclass check.
[ruby-dev:25464]
* gc.c (rb_data_object_alloc): klass may be NULL.
[ruby-list:40498]
* bignum.c (rb_big_rand): should return positive random number.
[ruby-dev:25401]
* bignum.c (rb_big_rand): do not use rb_big_modulo to generate
random bignums. [ruby-dev:25396]
* variable.c (rb_autoload): [ruby-dev:25373]
* eval.c (svalue_to_avalue): [ruby-dev:25366]
* string.c (rb_str_justify): [ruby-dev:25367]
* io.c (rb_f_select): [ruby-dev:25312]
* ext/socket/socket.c (sock_s_getservbyport): [ruby-talk:124072]
* struct.c (make_struct): [ruby-dev:25249]
* dir.c (dir_open_dir): new function. [ruby-dev:25242]
* io.c (rb_f_open): add type check for return value from to_open.
* lib/pstore.rb (PStore#transaction): Use the empty content when a
file is not found. [ruby-dev:24561]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@8068 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2005-03-04 09:47:45 +03:00
|
|
|
end
|
2008-04-10 11:12:41 +04:00
|
|
|
rescue Errno::ENOENT
|
|
|
|
return nil
|
1999-11-17 10:30:37 +03:00
|
|
|
end
|
2008-04-10 11:12:41 +04:00
|
|
|
else
|
Make rb_scan_args handle keywords more similar to Ruby methods (#2460)
Cfuncs that use rb_scan_args with the : entry suffer similar keyword
argument separation issues that Ruby methods suffer if the cfuncs
accept optional or variable arguments.
This makes the following changes to : handling.
* Treats as **kw, prompting keyword argument separation warnings
if called with a positional hash.
* Do not look for an option hash if empty keywords are provided.
For backwards compatibility, treat an empty keyword splat as a empty
mandatory positional hash argument, but emit a a warning, as this
behavior will be removed in Ruby 3. The argument number check
needs to be moved lower so it can correctly handle an empty
positional argument being added.
* If the last argument is nil and it is necessary to treat it as an option
hash in order to make sure all arguments are processed, continue to
treat the last argument as the option hash. Emit a warning in this case,
as this behavior will be removed in Ruby 3.
* If splitting the keyword hash into two hashes, issue a warning, as we
will not be splitting hashes in Ruby 3.
* If the keyword argument is required to fill a mandatory positional
argument, continue to do so, but emit a warning as this behavior will
be going away in Ruby 3.
* If keyword arguments are provided and the last argument is not a hash,
that indicates something wrong. This can happen if a cfunc is calling
rb_scan_args multiple times, and providing arguments that were not
passed to it from Ruby. Callers need to switch to the new
rb_scan_args_kw function, which allows passing of whether keywords
were provided.
This commit fixes all warnings caused by the changes above.
It switches some function calls to *_kw versions with appropriate
kw_splat flags. If delegating arguments, RB_PASS_CALLED_KEYWORDS
is used. If creating new arguments, RB_PASS_KEYWORDS is used if
the last argument is a hash to be treated as keywords.
In open_key_args in io.c, use rb_scan_args_kw.
In this case, the arguments provided come from another C
function, not Ruby. The last argument may or may not be a hash,
so we can't set keyword argument mode. However, if it is a
hash, we don't want to warn when treating it as keywords.
In Ruby files, make sure to appropriately use keyword splats
or literal keywords when calling Cfuncs that now issue keyword
argument separation warnings through rb_scan_args. Also, make
sure not to pass nil in place of an option hash.
Work around Kernel#warn warnings due to problems in the Rubygems
override of the method. There is an open pull request to fix
these issues in Rubygems, but part of the Rubygems tests for
their override fail on ruby-head due to rb_scan_args not
recognizing empty keyword splats, which this commit fixes.
Implementation wise, adding rb_scan_args_kw is kind of a pain,
because rb_scan_args takes a variable number of arguments.
In order to not duplicate all the code, the function internals need
to be split into two functions taking a va_list, and to avoid passing
in a ton of arguments, a single struct argument is used to handle
the variables previously local to the function.
2019-09-25 21:18:49 +03:00
|
|
|
file = File.new(filename, **RDWR_ACCESS)
|
2008-04-10 11:12:41 +04:00
|
|
|
file.flock(File::LOCK_EX)
|
|
|
|
return file
|
|
|
|
end
|
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
# 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
|
2016-09-29 15:08:30 +03:00
|
|
|
# Hash, a checksum of the data, and the size of the data.
|
2008-04-10 11:12:41 +04:00
|
|
|
def load_data(file, read_only)
|
|
|
|
if read_only
|
|
|
|
begin
|
2008-04-10 18:10:19 +04:00
|
|
|
table = load(file)
|
2011-12-20 18:57:29 +04:00
|
|
|
raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash)
|
2008-04-10 11:12:41 +04:00
|
|
|
rescue EOFError
|
|
|
|
# This seems to be a newly-created file.
|
|
|
|
table = {}
|
1998-01-16 15:19:09 +03:00
|
|
|
end
|
2008-04-10 11:12:41 +04:00
|
|
|
table
|
|
|
|
else
|
|
|
|
data = file.read
|
|
|
|
if data.empty?
|
|
|
|
# This seems to be a newly-created file.
|
|
|
|
table = {}
|
2008-04-11 11:43:31 +04:00
|
|
|
checksum = empty_marshal_checksum
|
2011-12-20 18:56:07 +04:00
|
|
|
size = empty_marshal_data.bytesize
|
2008-04-10 11:12:41 +04:00
|
|
|
else
|
2008-04-10 18:10:19 +04:00
|
|
|
table = load(data)
|
2016-06-28 16:55:02 +03:00
|
|
|
checksum = CHECKSUM_ALGO.digest(data)
|
2011-12-20 18:56:07 +04:00
|
|
|
size = data.bytesize
|
2011-12-20 18:57:29 +04:00
|
|
|
raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash)
|
1998-01-16 15:19:09 +03:00
|
|
|
end
|
2008-04-10 11:12:41 +04:00
|
|
|
data.replace(EMPTY_STRING)
|
|
|
|
[table, checksum, size]
|
1998-01-16 15:19:09 +03:00
|
|
|
end
|
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
def on_windows?
|
2011-12-20 18:57:29 +04:00
|
|
|
is_windows = RUBY_PLATFORM =~ /mswin|mingw|bccwin|wince/
|
2008-04-10 11:12:41 +04:00
|
|
|
self.class.__send__(:define_method, :on_windows?) do
|
|
|
|
is_windows
|
|
|
|
end
|
|
|
|
is_windows
|
2004-05-27 11:43:38 +04:00
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
def save_data(original_checksum, original_file_size, file)
|
2011-12-20 18:56:07 +04:00
|
|
|
new_data = dump(@table)
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2016-06-28 16:55:02 +03:00
|
|
|
if new_data.bytesize != original_file_size || CHECKSUM_ALGO.digest(new_data) != original_checksum
|
2008-04-10 11:12:41 +04:00
|
|
|
if @ultra_safe && !on_windows?
|
|
|
|
# Windows doesn't support atomic file renames.
|
|
|
|
save_data_with_atomic_file_rename_strategy(new_data, file)
|
|
|
|
else
|
|
|
|
save_data_with_fast_strategy(new_data, file)
|
|
|
|
end
|
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
new_data.replace(EMPTY_STRING)
|
2004-05-27 11:43:38 +04:00
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
def save_data_with_atomic_file_rename_strategy(data, file)
|
|
|
|
temp_filename = "#{@filename}.tmp.#{Process.pid}.#{rand 1000000}"
|
Make rb_scan_args handle keywords more similar to Ruby methods (#2460)
Cfuncs that use rb_scan_args with the : entry suffer similar keyword
argument separation issues that Ruby methods suffer if the cfuncs
accept optional or variable arguments.
This makes the following changes to : handling.
* Treats as **kw, prompting keyword argument separation warnings
if called with a positional hash.
* Do not look for an option hash if empty keywords are provided.
For backwards compatibility, treat an empty keyword splat as a empty
mandatory positional hash argument, but emit a a warning, as this
behavior will be removed in Ruby 3. The argument number check
needs to be moved lower so it can correctly handle an empty
positional argument being added.
* If the last argument is nil and it is necessary to treat it as an option
hash in order to make sure all arguments are processed, continue to
treat the last argument as the option hash. Emit a warning in this case,
as this behavior will be removed in Ruby 3.
* If splitting the keyword hash into two hashes, issue a warning, as we
will not be splitting hashes in Ruby 3.
* If the keyword argument is required to fill a mandatory positional
argument, continue to do so, but emit a warning as this behavior will
be going away in Ruby 3.
* If keyword arguments are provided and the last argument is not a hash,
that indicates something wrong. This can happen if a cfunc is calling
rb_scan_args multiple times, and providing arguments that were not
passed to it from Ruby. Callers need to switch to the new
rb_scan_args_kw function, which allows passing of whether keywords
were provided.
This commit fixes all warnings caused by the changes above.
It switches some function calls to *_kw versions with appropriate
kw_splat flags. If delegating arguments, RB_PASS_CALLED_KEYWORDS
is used. If creating new arguments, RB_PASS_KEYWORDS is used if
the last argument is a hash to be treated as keywords.
In open_key_args in io.c, use rb_scan_args_kw.
In this case, the arguments provided come from another C
function, not Ruby. The last argument may or may not be a hash,
so we can't set keyword argument mode. However, if it is a
hash, we don't want to warn when treating it as keywords.
In Ruby files, make sure to appropriately use keyword splats
or literal keywords when calling Cfuncs that now issue keyword
argument separation warnings through rb_scan_args. Also, make
sure not to pass nil in place of an option hash.
Work around Kernel#warn warnings due to problems in the Rubygems
override of the method. There is an open pull request to fix
these issues in Rubygems, but part of the Rubygems tests for
their override fail on ruby-head due to rb_scan_args not
recognizing empty keyword splats, which this commit fixes.
Implementation wise, adding rb_scan_args_kw is kind of a pain,
because rb_scan_args takes a variable number of arguments.
In order to not duplicate all the code, the function internals need
to be split into two functions taking a va_list, and to avoid passing
in a ton of arguments, a single struct argument is used to handle
the variables previously local to the function.
2019-09-25 21:18:49 +03:00
|
|
|
temp_file = File.new(temp_filename, **WR_ACCESS)
|
2008-04-10 11:12:41 +04:00
|
|
|
begin
|
|
|
|
temp_file.flock(File::LOCK_EX)
|
|
|
|
temp_file.write(data)
|
|
|
|
temp_file.flush
|
|
|
|
File.rename(temp_filename, @filename)
|
|
|
|
rescue
|
|
|
|
File.unlink(temp_file) rescue nil
|
|
|
|
raise
|
|
|
|
ensure
|
|
|
|
temp_file.close
|
2004-02-17 18:26:03 +03:00
|
|
|
end
|
2008-04-10 11:12:41 +04:00
|
|
|
end
|
2009-03-06 06:56:38 +03:00
|
|
|
|
2008-04-10 11:12:41 +04:00
|
|
|
def save_data_with_fast_strategy(data, file)
|
|
|
|
file.rewind
|
|
|
|
file.write(data)
|
2011-12-20 18:56:07 +04:00
|
|
|
file.truncate(data.bytesize)
|
2004-02-17 18:26:03 +03:00
|
|
|
end
|
2008-04-10 18:10:19 +04:00
|
|
|
|
|
|
|
|
|
|
|
# This method is just a wrapped around Marshal.dump
|
|
|
|
# to allow subclass overriding used in YAML::Store.
|
|
|
|
def dump(table) # :nodoc:
|
|
|
|
Marshal::dump(table)
|
|
|
|
end
|
|
|
|
|
|
|
|
# This method is just a wrapped around Marshal.load.
|
|
|
|
# to allow subclass overriding used in YAML::Store.
|
|
|
|
def load(content) # :nodoc:
|
|
|
|
Marshal::load(content)
|
|
|
|
end
|
2008-04-11 11:43:31 +04:00
|
|
|
|
|
|
|
def empty_marshal_data
|
|
|
|
EMPTY_MARSHAL_DATA
|
|
|
|
end
|
|
|
|
def empty_marshal_checksum
|
|
|
|
EMPTY_MARSHAL_CHECKSUM
|
|
|
|
end
|
1998-01-16 15:19:09 +03:00
|
|
|
end
|