Class: Palsy

Inherits:
SQLite3::Database
  • Object
show all
Includes:
Singleton
Defined in:
lib/palsy.rb,
lib/palsy/basic/map.rb,
lib/palsy/basic/set.rb,
lib/palsy/basic/list.rb,
lib/palsy/basic/object.rb,
lib/palsy/basic/generic.rb,
lib/palsy/basic/collection.rb

Overview

Present ruby core data structures in a manner similar to perl’s tie backed by a SQLite database. Intended to be as simple as possible, sacrificing performance and flexibility to do so.

It is not a 1:1 emulation of tie, as ruby cannot support this. What it does do is provide a convincing enough facsimile by emulating the interface, and making it easy to convert to native ruby types when that’s not enough.

This library is completely unapologetic with regards to how little it does or how slow it does things.

All writes are fully consistent, which is something SQLite gives us. Reads always hit the database. This allows us to reason more clearly about how our data persists, even in concurrent models where shared state can get very complicated to use.

Defined Under Namespace

Classes: Collection, Generic, List, Map, Object, Set

Constant Summary collapse

VERSION =

Palsy’s version, as a string.

PalsyVersion

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePalsy

Initializer. Since Palsy is a proper Ruby singleton, calling Palsy.instance will run this the first time, but calling this directly is not advised. Use the Palsy class methods like Palsy.change_db, or set Palsy.database= before working with the rest of the library.



70
71
72
73
# File 'lib/palsy.rb', line 70

def initialize
  @palsy_lock = Mutex.new
  super(connect)
end

Class Attribute Details

.databaseObject

The name of the database Palsy will manipulate. Set this before using Palsy. Look at Palsy.change_db for a better way to swap DB’s during runtime.



35
36
37
# File 'lib/palsy.rb', line 35

def database
  @database
end

Class Method Details

.change_db(db_name) ⇒ Object

Given a db name, assigns that to Palsy and initiates a reconnection.

Note that existing Palsy objects will immediately start working against this new database, and expect everything needed to read the data to exist, namely the tables they’re keyed against.



45
46
47
48
# File 'lib/palsy.rb', line 45

def self.change_db(db_name)
  self.database = db_name
  self.reconnect
end

.closeObject

Closes the SQLite database.



60
61
62
# File 'lib/palsy.rb', line 60

def self.close
  self.instance.close
end

.reconnectObject

Initiates a reopening of the SQLite database.



53
54
55
# File 'lib/palsy.rb', line 53

def self.reconnect
  self.instance.reconnect
end

Instance Method Details

#connectObject

Opens the database. Note that this is a no-op unless Palsy.database= is set.



87
88
89
90
91
# File 'lib/palsy.rb', line 87

def connect
  if self.class.database
    SQLite3::Database.new(self.class.database)
  end
end

#execute_t(query, args = [], &block) ⇒ Object

Execute this query in an exclusive transaction.



155
156
157
158
159
160
161
# File 'lib/palsy.rb', line 155

def execute_t(query, args=[], &block)
  result = nil

  with_t { result = execute(query, args, &block) }

  return result
end

#no_lockObject

Override the explicit lock for the current thread. For things that generate queries, will not attempt to acquire a mutex, will not attempt to run a transaction. Will just run the block. See #with_t for information on lock semantics.

For Ruby 2.0 users and above: If you wrap your palsy operations in #no_lock on the current thread, #with_t will not try to use a mutex lock or transaction.

This is useful if you want to use palsy in a signal handler:

trap("INFO") do
  Palsy.instance.no_lock do
    some_palsy_thing
  end
end

The caveat is that any writes done in this region will not be isolated and will likely cause problems.



113
114
115
116
117
118
119
# File 'lib/palsy.rb', line 113

def no_lock
  retval = nil
  Thread.current[:palsy_no_lock] = true
  retval = yield
  Thread.current[:palsy_no_lock] = false
  return retval
end

#reconnectObject

Initiates a reopening of the SQLite database. Instance method that Palsy.reconnect uses.



79
80
81
82
# File 'lib/palsy.rb', line 79

def reconnect
  close rescue nil
  __setobj__(connect)
end

#with_tObject

Start a mutex-wrapped exclusive transaction, then execute the block. If we’re already the owner of the transaction, just execute.

This is used by most things in Palsy. If you want to step around the lock (necessary for specific situations, and has certain caveats), take a look at #no_lock.



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/palsy.rb', line 129

def with_t
  result = nil
  tc = Thread.current

  if tc[:lock] or tc[:palsy_no_lock]
    result = yield
  else
    @palsy_lock.synchronize do
      begin
        tc[:lock] = @palsy_lock
        transaction(:exclusive) { result = yield }
      rescue StandardError => e
        rollback if transaction_active?
        raise e
      ensure
        tc[:lock] = nil
      end
    end
  end

  return result
end