Skip to content

Commit

Permalink
Create DBM signature and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
PanosCodes committed Nov 3, 2020
1 parent ab5df07 commit 425b611
Show file tree
Hide file tree
Showing 2 changed files with 385 additions and 0 deletions.
277 changes: 277 additions & 0 deletions stdlib/dbm/dbm.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
# ## Introduction
#
# The DBM class provides a wrapper to a Unix-style
# [dbm](http://en.wikipedia.org/wiki/Dbm) or Database Manager library.
#
# Dbm databases do not have tables or columns; they are simple key-value data
# stores, like a Ruby Hash except not resident in RAM. Keys and values must be
# strings.
#
# The exact library used depends on how Ruby was compiled. It could be any of
# the following:
#
# * The original ndbm library is released in 4.3BSD. It is based on dbm
# library in Unix Version 7 but has different API to support multiple
# databases in a process.
# * [Berkeley DB](http://en.wikipedia.org/wiki/Berkeley_DB) versions 1 thru 6,
# also known as BDB and Sleepycat DB, now owned by Oracle Corporation.
# * Berkeley DB 1.x, still found in 4.4BSD derivatives (FreeBSD, OpenBSD,
# etc).
# * [gdbm](http://www.gnu.org/software/gdbm/), the GNU implementation of dbm.
# * [qdbm](http://fallabs.com/qdbm/index.html), another open source
# reimplementation of dbm.
#
#
# All of these dbm implementations have their own Ruby interfaces available,
# which provide richer (but varying) APIs.
#
# ## Cautions
#
# Before you decide to use DBM, there are some issues you should consider:
#
# * Each implementation of dbm has its own file format. Generally, dbm
# libraries will not read each other's files. This makes dbm files a bad
# choice for data exchange.
#
# * Even running the same OS and the same dbm implementation, the database
# file format may depend on the CPU architecture. For example, files may not
# be portable between PowerPC and 386, or between 32 and 64 bit Linux.
#
# * Different versions of Berkeley DB use different file formats. A change to
# the OS may therefore break DBM access to existing files.
#
# * Data size limits vary between implementations. Original Berkeley DB was
# limited to 2GB of data. Dbm libraries also sometimes limit the total size
# of a key/value pair, and the total size of all the keys that hash to the
# same value. These limits can be as little as 512 bytes. That said, gdbm
# and recent versions of Berkeley DB do away with these limits.
#
#
# Given the above cautions, DBM is not a good choice for long term storage of
# important data. It is probably best used as a fast and easy alternative to a
# Hash for processing large amounts of data.
#
# ## Example
#
# require 'dbm'
# db = DBM.open('rfcs', 0666, DBM::WRCREAT)
# db['822'] = 'Standard for the Format of ARPA Internet Text Messages'
# db['1123'] = 'Requirements for Internet Hosts - Application and Support'
# db['3068'] = 'An Anycast Prefix for 6to4 Relay Routers'
# puts db['822']
#
class DBM
include Enumerable[untyped, untyped]

# Open a dbm database and yields it if a block is given. See also `DBM.new`.
#
def self.open: (*untyped) -> ::DBM

public

# Return a value from the database by locating the key string provided. If the
# key is not found, returns nil.
#
def []: (untyped) -> untyped

# Stores the specified string value in the database, indexed via the string key
# provided.
#
def []=: (untyped, untyped) -> untyped

# Deletes all data from the database.
#
def clear: () -> self

# Closes the database.
#
def close: () -> NilClass

# Returns true if the database is closed, false otherwise.
#
def closed?: () -> bool

# Deletes an entry from the database.
#
def delete: (untyped) -> untyped

# Deletes all entries for which the code block returns true. Returns self.
#
def delete_if: () ?{ (untyped) -> bool } -> self

# Calls the block once for each [key, value] pair in the database. Returns self.
#
def each: (?untyped, ?untyped) -> Enumerator[untyped, ::DBM]

# Calls the block once for each key string in the database. Returns self.
#
def each_key: () -> Enumerator[String, ::DBM]

# Calls the block once for each [key, value] pair in the database. Returns self.
#
def each_pair: (?untyped, ?untyped) -> Enumerator[untyped, ::DBM]

# Calls the block once for each value string in the database. Returns self.
#
def each_value: () -> Enumerator[untyped, ::DBM]

# Returns true if the database is empty, false otherwise.
#
def empty?: () -> bool

# Return a value from the database by locating the key string provided. If the
# key is not found, returns `ifnone`. If `ifnone` is not given, raises
# IndexError.
#
def fetch: (*String) -> untyped

# Returns true if the database contains the specified key, false otherwise.
#
def has_key?: (String) -> bool

# Returns true if the database contains the specified string value, false
# otherwise.
#
def has_value?: (untyped) -> bool

# Returns true if the database contains the specified key, false otherwise.
#
def include?: (String) -> bool

def index: (untyped) -> (String | NilClass)

# Returns a Hash (not a DBM database) created by using each value in the
# database as a key, with the corresponding key as its value.
#
def invert: () -> Hash[untyped, String]

# Returns the key for the specified value.
#
def key: (untyped) -> (String | NilClass)

# Returns true if the database contains the specified key, false otherwise.
#
def key?: (untyped) -> bool

# Returns an array of all the string keys in the database.
#
def keys: () -> Array[String]

# Returns the number of entries in the database.
#
def length: () -> Integer

# Returns true if the database contains the specified key, false otherwise.
#
def member?: (untyped) -> bool

# Converts the contents of the database to an in-memory Hash, then calls
# Hash#reject with the specified code block, returning a new Hash.
#
def reject: () -> Hash[untyped, untyped]

# Deletes all entries for which the code block returns true. Returns self.
#
def reject!: () { (String, untyped) -> untyped } -> self

# Replaces the contents of the database with the contents of the specified
# object. Takes any object which implements the each_pair method, including Hash
# and DBM objects.
#
def replace: (untyped) -> ::DBM

# Returns a new array consisting of the [key, value] pairs for which the code
# block returns true.
#
def select: () { () -> untyped } -> Array[untyped]

# Removes a [key, value] pair from the database, and returns it. If the database
# is empty, returns nil. The order in which values are removed/returned is not
# guaranteed.
#
def shift: () -> Array[untyped]

# Returns the number of entries in the database.
#
def size: () -> Integer

# Stores the specified string value in the database, indexed via the string key
# provided.
#
def store: (String, untyped) -> String

# Converts the contents of the database to an array of [key, value] arrays, and
# returns it.
#
def to_a: () -> Array[untyped]

# Converts the contents of the database to an in-memory Hash object, and returns
# it.
#
def to_hash: () -> Hash[String, untyped]

# Updates the database with multiple values from the specified object. Takes any
# object which implements the each_pair method, including Hash and DBM objects.
#
def update: (untyped) -> ::DBM

# Returns true if the database contains the specified string value, false
# otherwise.
#
def value?: (untyped) -> bool

# Returns an array of all the string values in the database.
#
def values: () -> Array[untyped]

# Returns an array containing the values associated with the given keys.
#
def values_at: (*String) -> Array[untyped]

private

# Open a dbm database with the specified name, which can include a directory
# path. Any file extensions needed will be supplied automatically by the dbm
# library. For example, Berkeley DB appends '.db', and GNU gdbm uses two
# physical files with extensions '.dir' and '.pag'.
#
# The mode should be an integer, as for Unix chmod.
#
# Flags should be one of READER, WRITER, WRCREAT or NEWDB.
#
def initialize: (*untyped) -> self
end

# Indicates that dbm_open() should open the database in read/write mode, create
# it if it does not already exist, and delete all contents if it does already
# exist.
#
DBM::NEWDB: Integer

# Indicates that dbm_open() should open the database in read-only mode
#
#
DBM::READER: Integer

# Identifies ndbm library version.
#
# Examples:
#
# * "ndbm (4.3BSD)"
# * "Berkeley DB 4.8.30: (April 9, 2010)"
# * "Berkeley DB (unknown)" (4.4BSD, maybe)
# * "GDBM version 1.8.3. 10/15/2002 (built Jul 1 2011 12:32:45)"
# * "QDBM 1.8.78"
#
#
DBM::VERSION: String

# Indicates that dbm_open() should open the database in read/write mode, and
# create it if it does not already exist
#
DBM::WRCREAT: Integer

# Indicates that dbm_open() should open the database in read/write mode
#
#
DBM::WRITER: Integer
108 changes: 108 additions & 0 deletions test/stdlib/DBM_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
require_relative "test_helper"
require "dbm"

class DBMSingletonTest < Minitest::Test
include TypeAssertions
library "dbm"
testing "singleton(::DBM)"

def test_open
path = "test/assets/dbm/"
FileUtils.mkdir_p(path)

assert_send_type "(*untyped) -> ::DBM ",
::DBM,
:open, "#{path}.test_open", 0666, ::DBM::WRCREAT

FileUtils.remove_dir(path)
end
end

class DBMTest < Minitest::Test
include TypeAssertions
library "dbm"
testing "::DBM"

def setup
super
path = "test/assets/dbm/"
FileUtils.mkdir_p(path) unless File.exist?(path)
@dbm = ::DBM.open("test/assets/dbm/sample", 0666, ::DBM::WRCREAT)
@dbm["setup"] = "Value"
end

def teardown
super
path = "test/assets/dbm/sample.db"
File.delete(path) if File.exist?(path)
end

def test_clear
assert_send_type "() -> self", @dbm, :clear
end

def test_closed?
assert_send_type "() -> bool", @dbm, :closed?
end

def test_each
assert_send_type "(?untyped, ?untyped) -> Enumerator[untyped, ::DBM]", @dbm, :each
end

def test_each_key
assert_send_type "(?untyped, ?untyped) -> Enumerator[untyped, ::DBM]", @dbm, :each_key
end

def test_each_pair
assert_send_type "(?untyped, ?untyped) -> Enumerator[untyped, ::DBM]", @dbm, :each_pair
end

def test_update
assert_send_type "(untyped) -> ::DBM", @dbm, :update, { "my_key": 14 }
end

def test_values_at
@dbm["key1"] = 1
@dbm["key2"] = 2

assert_send_type "(*String) -> Array[untyped]", @dbm, :values_at, "key1", "key2"
end

def test_to_a
assert_send_type "() -> Array[untyped]", @dbm, :to_a
end

def test_to_hash
assert_send_type "() -> Hash[String, untyped]", @dbm, :to_hash
end

def test_invert
assert_send_type "() -> Hash[untyped, String]", @dbm, :invert
end

def test_index
@dbm["key3"] = 1
assert_send_type "(untyped) -> (String | NilClass)", @dbm, :index, "key1"
assert_send_type "(untyped) -> (String | NilClass)", @dbm, :index, "keaaaay1"
end

def test_key
@dbm["key_key"] = 1
assert_send_type "(untyped) -> (String | NilClass)", @dbm, :key, "key_key"
assert_send_type "(untyped) -> (String | NilClass)", @dbm, :key, "keaaaay1"
end

def test_replace
@dbm["key4"] = 1
assert_send_type "(untyped) -> ::DBM", @dbm, :replace, { "key4": 55 }
end

def test_shift
@dbm["key5"] = 1
assert_send_type "() -> Array[untyped]", @dbm, :shift
end

def test_store
assert_send_type "(String, untyped) -> String", @dbm, :store, "keeey", Hash.new
end
end

0 comments on commit 425b611

Please sign in to comment.