litestack 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile +8 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +166 -0
  6. data/Rakefile +12 -0
  7. data/WHYLITESTACK.md +26 -0
  8. data/assets/litecache_logo_teal.png +0 -0
  9. data/assets/litedb_logo_teal.png +0 -0
  10. data/assets/litejob_logo_teal.png +0 -0
  11. data/assets/litestack_logo_teal.png +0 -0
  12. data/assets/litestack_logo_teal_large.png +0 -0
  13. data/bench/bench.rb +23 -0
  14. data/bench/bench_cache_rails.rb +67 -0
  15. data/bench/bench_cache_raw.rb +68 -0
  16. data/bench/bench_jobs_rails.rb +38 -0
  17. data/bench/bench_jobs_raw.rb +27 -0
  18. data/bench/bench_queue.rb +16 -0
  19. data/bench/bench_rails.rb +81 -0
  20. data/bench/bench_raw.rb +72 -0
  21. data/bench/rails_job.rb +18 -0
  22. data/bench/skjob.rb +13 -0
  23. data/bench/uljob.rb +15 -0
  24. data/lib/active_job/queue_adapters/litejob_adapter.rb +47 -0
  25. data/lib/active_job/queue_adapters/ultralite_adapter.rb +49 -0
  26. data/lib/active_record/connection_adapters/litedb_adapter.rb +102 -0
  27. data/lib/active_support/cache/litecache.rb +100 -0
  28. data/lib/active_support/cache/ultralite_cache_store.rb +100 -0
  29. data/lib/litestack/litecache.rb +254 -0
  30. data/lib/litestack/litedb.rb +47 -0
  31. data/lib/litestack/litejob.rb +84 -0
  32. data/lib/litestack/litejobqueue.rb +161 -0
  33. data/lib/litestack/litequeue.rb +105 -0
  34. data/lib/litestack/litesupport.rb +74 -0
  35. data/lib/litestack/version.rb +5 -0
  36. data/lib/litestack.rb +15 -0
  37. data/lib/railties/rails/commands/dbconsole.rb +87 -0
  38. data/lib/sequel/adapters/litedb.rb +43 -0
  39. data/samples/ultrajob.yaml +2 -0
  40. metadata +115 -0
@@ -0,0 +1,47 @@
1
+ # all components should require the support module
2
+ require_relative 'litesupport'
3
+
4
+ # Litedb inherits from the SQLite3::Database class and adds a few initialization options
5
+ class Litedb < ::SQLite3::Database
6
+
7
+ # overrride the original initilaizer to allow for connection configuration
8
+ def initialize(file, options = {}, zfs = nil )
9
+ if block_given?
10
+ super(file, options, zfs) do |db|
11
+ init unless options[:noinit] == true
12
+ yield db
13
+ end
14
+ else
15
+ super(file, options, zfs)
16
+ init unless options[:noinit] == true
17
+ end
18
+ end
19
+
20
+ # enforce immediate mode to avoid deadlocks for a small performance penalty
21
+ def transaction(mode = :immediate)
22
+ super(mode)
23
+ end
24
+
25
+ private
26
+
27
+ # default connection configuration values
28
+ def init
29
+ # version 3.37 is required for strict typing support and the newest json operators
30
+ raise Litesupport::Error if SQLite3::SQLITE_VERSION_NUMBER < 3037000
31
+ # time to wait to obtain a write lock before raising an exception
32
+ self.busy_handler{|i| sleep 0.001}
33
+ # level of database durability, 2 = "FULL" (sync on every write), other values include 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
34
+ self.synchronous = 1
35
+ # Journal mode WAL allows for greater concurrency (many readers + one writer)
36
+ self.journal_mode = "WAL"
37
+ # impose a limit on the WAL file to prevent unlimited growth (with a negative impact on read performance as well)
38
+ self.journal_size_limit = 64 * 1024 * 1024
39
+ # set the global memory map so all processes can share data
40
+ self.mmap_size = 128 * 1024 * 1024
41
+ # increase the local connection cache to 2000 pages
42
+ self.cache_size = 2000
43
+ end
44
+
45
+ end
46
+
47
+
@@ -0,0 +1,84 @@
1
+ # frozen_stringe_literal: true
2
+
3
+ require_relative './litejobqueue'
4
+
5
+ ##
6
+ #Litejob is a Ruby module that enables seamless integration of the Litejobqueue job queueing system into Ruby applications. By including the Litejob module in a class and implementing the #perform method, developers can easily enqueue and process jobs asynchronously.
7
+ #
8
+ #When a job is enqueued, Litejob creates a new instance of the class and passes it any necessary arguments. The class's #perform method is then called asynchronously to process the job. This allows the application to continue running without waiting for the job to finish, improving overall performance and responsiveness.
9
+ #
10
+ #One of the main benefits of using Litejob is its simplicity. Because it integrates directly with Litejobqueue, developers do not need to worry about managing job queues or processing logic themselves. Instead, they can focus on implementing the #perform method to handle the specific job tasks.
11
+ #
12
+ #Litejob also provides a number of useful features, including the ability to set job priorities, retry failed jobs, and limit the number of retries. These features can be configured using simple configuration options in the class that includes the Litejob module.
13
+ #
14
+ #Overall, Litejob is a powerful and flexible module that allows developers to easily integrate Litejobqueue job queueing into their Ruby applications. By enabling asynchronous job processing, Litejob can help improve application performance and scalability, while simplifying the development and management of background job processing logic.
15
+ # class EasyJob
16
+ # include ::Litejob
17
+ #
18
+ # def perform(params)
19
+ # # do stuff
20
+ # end
21
+ # end
22
+ #
23
+ #Then later you can perform a job asynchronously:
24
+ #
25
+ # EasyJob.perform_async(params) # perform a job synchronously
26
+ #Or perform it at a specific time:
27
+ # EasyJob.perform_at(time, params) # perform a job at a specific time
28
+ #Or perform it after a certain delay:
29
+ # EasyJob.perform_in(delay, params) # perform a job after a certain delay
30
+ #You can also specify a specific queue to be used
31
+ # class EasyJob
32
+ # include ::Litejob
33
+ #
34
+ # self.queue = :urgent
35
+ #
36
+ # def perform(params)
37
+ # # do stuff
38
+ # end
39
+ # end
40
+ #
41
+ module Litejob
42
+
43
+ private
44
+ def self.included(klass)
45
+ klass.extend(ClassMethods)
46
+ klass.get_queue
47
+ end
48
+
49
+ module ClassMethods
50
+ def perform_async(*params)
51
+ get_queue.push(self.name, params, 0, queue)
52
+ end
53
+
54
+ def perform_at(time, *params)
55
+ delay = time - Time.now.to_i
56
+ get_queue.push(self.name, params, delay, queue)
57
+ end
58
+
59
+ def perfrom_in(delay, *params)
60
+ get_queue.push(self.name, params, delay, queue)
61
+ end
62
+
63
+ def options
64
+ @@options ||= {}
65
+ end
66
+
67
+ def options=(options)
68
+ @@options = options
69
+ end
70
+
71
+ def queue
72
+ @@queue_name ||= "default"
73
+ end
74
+
75
+ def queue=(queue_name)
76
+ @@queue_name = queue_name.to_s
77
+ end
78
+
79
+ def get_queue
80
+ Litejobqueue.queue(options)
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,161 @@
1
+ # frozen_stringe_literal: true
2
+
3
+ require 'oj'
4
+ require 'yaml'
5
+ require_relative './litequeue'
6
+
7
+ ##
8
+ #Litejobqueue is a job queueing and processing system designed for Ruby applications. It is built on top of SQLite, which is an embedded relational database management system that is #lightweight and fast.
9
+ #
10
+ #One of the main benefits of Litejobqueue is that it is very low on resources, making it an ideal choice for applications that need to manage a large number of jobs without incurring #high resource costs. In addition, because it is built on SQLite, it is easy to use and does not require any additional configuration or setup.
11
+ #
12
+ #Litejobqueue also integrates well with various I/O frameworks like Async and Polyphony, making it a great choice for Ruby applications that use these frameworks. It provides a #simple and easy-to-use API for adding jobs to the queue and for processing them.
13
+ #
14
+ #Overall, LiteJobQueue is an excellent choice for Ruby applications that require a lightweight, embedded job queueing and processing system that is fast, efficient, and easy to use.
15
+ class Litejobqueue
16
+
17
+ # the default options for the job queue
18
+ # can be overriden by passing new options in a hash
19
+ # to Litejobqueue.new, it will also be then passed to the underlying Litequeue object
20
+ # config_path: "./litejob.yml" -> were to find the configuration file (if any)
21
+ # path: "./queue.db"
22
+ # mmap_size: 128 * 1024 * 1024 -> 128MB to be held in memory
23
+ # sync: 1 -> sync only when checkpointing
24
+ # queues: [["default", 1, "spawn"]] -> an array of queues to process
25
+ # workers: 1 -> number of job processing workers
26
+ # sleep_intervals: [0.001, 0.005, 0.025, 0.125, 0.625, 3.125] -> sleep intervals for workers
27
+ # queues will be processed according to priority, such that if the queues are as such
28
+ # queues: [["default", 1, "spawn"], ["urgent", 10]]
29
+ # it means that roughly, if the queues are full, for each 10 urgent jobs, 1 default job will be processed
30
+ # the priority value is mandatory. The optional "spawn" parameter tells the job workers to spawn a separate execution context (thread or fiber, based on environment) for each job.
31
+ # This can be particularly useful for long running, IO bound jobs. It is not recommended though for threaded environments, as it can result in creating many threads that may consudme a lot of memory.
32
+ DEFAULT_OPTIONS = {
33
+ config_path: "./litejob.yml",
34
+ path: "./queue.db",
35
+ queues: [["default", 1, "spawn"]],
36
+ workers: 1,
37
+ sleep_intervals: [0.001, 0.005, 0.025, 0.125, 0.625, 3.125]
38
+ }
39
+
40
+ @@queue = nil
41
+
42
+ # a method that returns a single instance of the job queue
43
+ # for use by Litejob
44
+ def self.queue(options = {})
45
+ @@queue ||= Litesupport.synchronize{self.new(options)}
46
+ end
47
+
48
+ def self.new(options = {})
49
+ return @@queue if @@queue
50
+ @@queue = allocate
51
+ @@queue.send(:initialize, options)
52
+ @@queue
53
+ end
54
+
55
+ # create new queue instance (only once instance will be created in the process)
56
+ # jobqueue = Litejobqueue.new
57
+ #
58
+ def initialize(options = {})
59
+ @options = DEFAULT_OPTIONS.merge(options)
60
+ @worker_sleep_index = 0
61
+ config = YAML.load_file(@options[:config_path]) rescue {} #an empty hash won't hurt
62
+ config.each_key do |k| # symbolize keys
63
+ config[k.to_sym] = config[k]
64
+ config.delete k
65
+ end
66
+ @options.merge!(config)
67
+ @queue = Litequeue.new(@options) # create a new queue object
68
+ # group and order queues according to their priority
69
+ pgroups = {}
70
+ @options[:queues].each do |q|
71
+ pgroups[q[1]] = [] unless pgroups[q[1]]
72
+ pgroups[q[1]] << [q[0], q[2] == "spawn"]
73
+ end
74
+ @queues = pgroups.keys.sort.reverse.collect{|p| [p, pgroups[p]]}
75
+ @workers = @options[:workers].times.collect{create_worker}
76
+ end
77
+
78
+ # push a job to the queue
79
+ # class EasyJob
80
+ # def perform(any, number, of_params)
81
+ # # do anything
82
+ # end
83
+ # end
84
+ # jobqueue = Litejobqueue.new
85
+ # jobqueue.push(EasyJob, params) # the job will be performed asynchronously
86
+ def push(jobclass, params, delay=0, queue=nil)
87
+ payload = Oj.dump([jobclass, params])
88
+ @queue.push(payload, delay, queue)
89
+ end
90
+
91
+ # delete a job from the job queue
92
+ # class EasyJob
93
+ # def perform(any, number, of_params)
94
+ # # do anything
95
+ # end
96
+ # end
97
+ # jobqueue = Litejobqueue.new
98
+ # id = jobqueue.push(EasyJob, params, 10) # queue for processing in 10 seconds
99
+ # jobqueue.delete(id)
100
+ def delete(id)
101
+ job = @queue.delete(id)
102
+ Oj.load(job) if job
103
+ end
104
+
105
+ private
106
+
107
+ # optionally run a job in its own context
108
+ def schedule(spawn = false, &block)
109
+ if spawn
110
+ Litesupport.spawn &block
111
+ else
112
+ yield
113
+ end
114
+ end
115
+
116
+ # create a worker according to environment
117
+ def create_worker
118
+ Litesupport.spawn do
119
+ Litesupport.switch
120
+ loop do
121
+ processed = 0
122
+ @queues.each do |level| # iterate through the levels
123
+ level[1].each do |q| # iterate through the queues in the level
124
+ index = 0
125
+ max = level[0]
126
+ while index < max && payload = @queue.pop(q[0])
127
+ processed += 1
128
+ index += 1
129
+ begin
130
+ id, job = payload[0], payload[1]
131
+ job = Oj.load(job)
132
+ klass = eval(job[0])
133
+ schedule(q[1]) do # run the job in a new context
134
+ begin
135
+ klass.new.perform(*job[1])
136
+ rescue Exception => e
137
+ puts e
138
+ puts e.message
139
+ puts e.backtrace
140
+ end
141
+ end
142
+ rescue Exception => e
143
+ puts e
144
+ puts e.message
145
+ puts e.backtrace
146
+ end
147
+ Litesupport.switch #give other context a chance to run here
148
+ end
149
+ end
150
+ end
151
+ if processed == 0
152
+ sleep @options[:sleep_intervals][@worker_sleep_index]
153
+ @worker_sleep_index += 1 if @worker_sleep_index < @options[:sleep_intervals].length - 1
154
+ else
155
+ @worker_sleep_index = 0 # reset the index
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_stringe_literal: true
2
+
3
+ # all components should require the support module
4
+ require_relative 'litesupport'
5
+
6
+ ##
7
+ #Litequeue is a simple queueing system for Ruby applications that allows you to push and pop values from a queue. It provides a straightforward API for creating and managing named queues, and for adding and removing values from those queues. Additionally, it offers options for scheduling pops at a certain time in the future, which can be useful for delaying processing until a later time.
8
+ #
9
+ #Litequeue is built on top of SQLite, which makes it very fast and efficient, even when handling large volumes of data. This lightweight and easy-to-use queueing system serves as a good foundation for building more advanced job processing frameworks that require basic queuing capabilities.
10
+ #
11
+
12
+ class Litequeue
13
+
14
+ # the default options for the queue
15
+ # can be overriden by passing new options in a hash
16
+ # to Litequeue.new
17
+ # path: "./queue.db"
18
+ # mmap_size: 128 * 1024 * 1024 -> 128MB to be held in memory
19
+ # sync: 1 -> sync only when checkpointing
20
+
21
+ DEFAULT_OPTIONS = {
22
+ path: "./queue.db",
23
+ mmap_size: 32 * 1024,
24
+ sync: 1
25
+ }
26
+
27
+ # create a new instance of the litequeue object
28
+ # accepts an optional options hash which will be merged with the DEFAULT_OPTIONS
29
+ # queue = Litequeue.new
30
+ # queue.push("somevalue", 2) # the value will be ready to pop in 2 seconds
31
+ # queue.pop # => nil
32
+ # sleep 2
33
+ # queue.pop # => "somevalue"
34
+
35
+ def initialize(options = {})
36
+ @options = DEFAULT_OPTIONS.merge(options)
37
+ @queue = create_db #(@options[:path])
38
+ prepare
39
+ end
40
+
41
+ # push an item to the queue, optionally specifying the queue name (defaults to default) and after how many seconds it should be ready to pop (defaults to zero)
42
+ # a unique job id is returned from this method, can be used later to delete it before it fires. You can push string, integer, float, true, false or nil values
43
+ #
44
+ def push(value, delay=0, queue='default')
45
+ result = @push.execute!(queue, delay, value)[0]
46
+ return result[0] if result
47
+ end
48
+
49
+ alias_method :"<<", :push
50
+
51
+ # pop an item from the queue, optionally with a specific queue name (default queue name is 'default')
52
+ def pop(queue='default')
53
+ result = nil
54
+ Litesupport.synchronize do
55
+ result = @pop.execute!(queue)[0]
56
+ end
57
+ result
58
+ end
59
+
60
+ # delete an item from the queue
61
+ # queue = Litequeue.new
62
+ # id = queue.push("somevalue")
63
+ # queue.delete(id) # => "somevalue"
64
+ # queue.pop # => nil
65
+ def delete(id, queue='default')
66
+ fire_at, id = id.split("_")
67
+ result = @deleter.execute!(queue, fire_at.to_i, id)[0]
68
+ end
69
+
70
+ # deletes all the entries in all queues, or if a queue name is given, deletes all entries in that specific queue
71
+ def clear(queue=nil)
72
+ @queue.execute("DELETE FROM _ul_queue_ WHERE iif(?, queue = ?, 1)", queue)
73
+ end
74
+
75
+ # returns a count of entries in all queues, or if a queue name is given, reutrns the count of entries in that queue
76
+ def count(queue=nil)
77
+ @queue.get_first_value("SELECT count(*) FROM _ul_queue_ WHERE iif(?, queue = ?, 1)", queue)
78
+ end
79
+
80
+ # return the size of the queue file on disk
81
+ def size
82
+ @queue.get_first_value("SELECT size.page_size * count.page_count FROM pragma_page_size() AS size, pragma_page_count() AS count")
83
+ end
84
+
85
+ private
86
+
87
+ def create_db
88
+ db = Litesupport.create_db(@options[:path])
89
+ db.synchronous = @options[:sync]
90
+ db.wal_autocheckpoint = 10000
91
+ db.mmap_size = @options[:mmap_size]
92
+ db.execute("CREATE TABLE IF NOT EXISTS _ul_queue_(queue TEXT DEFAULT('default') NOT NULL ON CONFLICT REPLACE, fire_at INTEGER DEFAULT(unixepoch()) NOT NULL ON CONFLICT REPLACE, id TEXT DEFAULT(hex(randomblob(8)) || (strftime('%f') * 100)) NOT NULL ON CONFLICT REPLACE, value TEXT, created_at INTEGER DEFAULT(unixepoch()) NOT NULL ON CONFLICT REPLACE, PRIMARY KEY(queue, fire_at ASC, id) ) WITHOUT ROWID")
93
+ db
94
+ end
95
+
96
+ def prepare
97
+ @push = @queue.prepare("INSERT INTO _ul_queue_(queue, fire_at, value) VALUES ($1, (strftime('%s') + $2), $3) RETURNING fire_at || '-' || id")
98
+ @pop = @queue.prepare("DELETE FROM _ul_queue_ WHERE (queue, fire_at, id) = (SELECT queue, min(fire_at), id FROM _ul_queue_ WHERE queue = ifnull($1, 'default') AND fire_at <= (unixepoch()) limit 1) RETURNING fire_at || '-' || id, value")
99
+ @deleter = @queue.prepare("DELETE FROM _ul_queue_ WHERE queue = ifnull($1, 'default') AND fire_at = $2 AND id = $3 RETURNING value")
100
+ end
101
+
102
+ end
103
+
104
+
105
+
@@ -0,0 +1,74 @@
1
+ require 'sqlite3'
2
+
3
+ module Litesupport
4
+
5
+ class Error < StandardError; end
6
+
7
+ # cache the environment we are running in
8
+ # it is an error to change the environment for a process
9
+ # or for a child forked from that process
10
+ def self.environment
11
+ @env ||= detect_environment
12
+ end
13
+
14
+ # identify which environment we are running in
15
+ # we currently support :fiber, :polyphony, :iodine & :threaded
16
+ # in the future we might want to expand to other environments
17
+ def self.detect_environment
18
+ return :fiber if Fiber.scheduler
19
+ return :polyphony if defined? Polyphony
20
+ return :iodine if defined? Iodine
21
+ return :threaded # fall back for all other environments
22
+ end
23
+
24
+ # spawn a new execution context
25
+ def self.spawn(&block)
26
+ if self.environment == :fiber
27
+ Fiber.schedule(&block)
28
+ elsif self.environment == :polyphony
29
+ spin(&block)
30
+ elsif self.environment == :threaded or self.environment == :iodine
31
+ Thread.new(&block)
32
+ end
33
+ # we should never reach here
34
+ end
35
+
36
+ # switch the execution context to allow others to run
37
+ def self.switch
38
+ if self.environment == :fiber
39
+ Fiber.scheduler.yield
40
+ elsif self.environment == :polyphony
41
+ Fiber.current.schedule
42
+ Thread.current.switch_fiber
43
+ else
44
+ # do nothing in case of thread, switching will auto-happen
45
+ end
46
+ end
47
+
48
+ # mutex initialization
49
+ def self.mutex
50
+ # a single mutex per process (is that ok?)
51
+ @@mutex ||= Mutex.new
52
+ end
53
+
54
+ # bold assumption, we will only synchronize threaded code
55
+ # if some code explicitly wants to synchronize a fiber
56
+ # they must send (true) as a parameter to this method
57
+ # else it is a no-op for fibers
58
+ def self.synchronize(fiber_sync = false, &block)
59
+ if self.environment == :fiber or self.environment == :polyphony
60
+ yield # do nothing, just run the block as is
61
+ else
62
+ self.mutex.synchronize(&block)
63
+ end
64
+ end
65
+
66
+ # common db object options
67
+ def self.create_db(path)
68
+ db = SQLite3::Database.new(path)
69
+ db.busy_handler{ sleep 0.001 }
70
+ db.journal_mode = "WAL"
71
+ db
72
+ end
73
+
74
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Litestack
4
+ VERSION = "0.1.1"
5
+ end
data/lib/litestack.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # load core classes
4
+ #require_relative "./version"
5
+ require_relative "litestack/litesupport"
6
+ #require_relative "litedb"
7
+ require_relative "litestack/litecache"
8
+ require_relative "litestack/litejob"
9
+
10
+ # conditionally load integration with other libraries
11
+ #require_relative "../sequel/adapters/litedb" if defined? Sequel
12
+ #require_relative "../active_record/connection_adapters/litedb_adapter" if defined? ActiveRecord
13
+ require_relative "active_support/cache/litecache" if defined? ActiveSupport
14
+ require_relative "active_job/queue_adapters/litejob_adapter" if defined? ActiveJob
15
+ #require_relative "../railties/rails/commands/dbconsole" if defined? Rails
@@ -0,0 +1,87 @@
1
+ module Rails
2
+ class DBConsole
3
+
4
+ def start
5
+ ENV["RAILS_ENV"] ||= @options[:environment] || environment
6
+ config = db_config.configuration_hash
7
+
8
+ case db_config.adapter
9
+ when /^(jdbc)?mysql/
10
+ args = {
11
+ host: "--host",
12
+ port: "--port",
13
+ socket: "--socket",
14
+ username: "--user",
15
+ encoding: "--default-character-set",
16
+ sslca: "--ssl-ca",
17
+ sslcert: "--ssl-cert",
18
+ sslcapath: "--ssl-capath",
19
+ sslcipher: "--ssl-cipher",
20
+ sslkey: "--ssl-key"
21
+ }.filter_map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }
22
+
23
+ if config[:password] && @options[:include_password]
24
+ args << "--password=#{config[:password]}"
25
+ elsif config[:password] && !config[:password].to_s.empty?
26
+ args << "-p"
27
+ end
28
+
29
+ args << db_config.database
30
+
31
+ find_cmd_and_exec(["mysql", "mysql5"], *args)
32
+
33
+ when /^postgres|^postgis/
34
+ ENV["PGUSER"] = config[:username] if config[:username]
35
+ ENV["PGHOST"] = config[:host] if config[:host]
36
+ ENV["PGPORT"] = config[:port].to_s if config[:port]
37
+ ENV["PGPASSWORD"] = config[:password].to_s if config[:password] && @options[:include_password]
38
+ ENV["PGSSLMODE"] = config[:sslmode].to_s if config[:sslmode]
39
+ ENV["PGSSLCERT"] = config[:sslcert].to_s if config[:sslcert]
40
+ ENV["PGSSLKEY"] = config[:sslkey].to_s if config[:sslkey]
41
+ ENV["PGSSLROOTCERT"] = config[:sslrootcert].to_s if config[:sslrootcert]
42
+ find_cmd_and_exec("psql", db_config.database)
43
+
44
+ when "sqlite3", "litedb"
45
+ args = []
46
+
47
+ args << "-#{@options[:mode]}" if @options[:mode]
48
+ args << "-header" if @options[:header]
49
+ args << File.expand_path(db_config.database, Rails.respond_to?(:root) ? Rails.root : nil)
50
+
51
+ find_cmd_and_exec("sqlite3", *args)
52
+
53
+
54
+ when "oracle", "oracle_enhanced"
55
+ logon = ""
56
+
57
+ if config[:username]
58
+ logon = config[:username].dup
59
+ logon << "/#{config[:password]}" if config[:password] && @options[:include_password]
60
+ logon << "@#{db_config.database}" if db_config.database
61
+ end
62
+
63
+ find_cmd_and_exec("sqlplus", logon)
64
+
65
+ when "sqlserver"
66
+ args = []
67
+
68
+ args += ["-d", "#{db_config.database}"] if db_config.database
69
+ args += ["-U", "#{config[:username]}"] if config[:username]
70
+ args += ["-P", "#{config[:password]}"] if config[:password]
71
+
72
+ if config[:host]
73
+ host_arg = +"tcp:#{config[:host]}"
74
+ host_arg << ",#{config[:port]}" if config[:port]
75
+ args += ["-S", host_arg]
76
+ end
77
+
78
+ find_cmd_and_exec("sqlcmd", *args)
79
+
80
+ else
81
+ abort "Unknown command-line client for #{db_config.database}."
82
+ end
83
+ end
84
+
85
+
86
+ end
87
+ end
@@ -0,0 +1,43 @@
1
+ require_relative '../../litestack/litedb'
2
+ require 'sequel'
3
+ require 'sequel/adapters/sqlite'
4
+
5
+ module Sequel
6
+ module Litedb
7
+ include SQLite
8
+
9
+ LITEDB_TYPES = SQLITE_TYPES
10
+
11
+ class Database < Sequel::SQLite::Database
12
+
13
+ set_adapter_scheme :litedb
14
+
15
+
16
+ def connect(server)
17
+ opts = server_opts(server)
18
+ opts[:database] = ':memory:' if blank_object?(opts[:database])
19
+ sqlite3_opts = {}
20
+ sqlite3_opts[:readonly] = typecast_value_boolean(opts[:readonly]) if opts.has_key?(:readonly)
21
+ db = ::Litedb.new(opts[:database].to_s, sqlite3_opts)
22
+
23
+ if sqlite_version >= 104
24
+ db.extended_result_codes = true
25
+ end
26
+
27
+ connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
28
+
29
+ class << db
30
+ attr_reader :prepared_statements
31
+ end
32
+ db.instance_variable_set(:@prepared_statements, {})
33
+
34
+ db
35
+ end
36
+
37
+ end
38
+
39
+ class Dataset < Sequel::SQLite::Dataset
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,2 @@
1
+ queues:
2
+ - [normal, 5]