litestack 0.2.6 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/BENCHMARKS.md +11 -0
  3. data/CHANGELOG.md +19 -0
  4. data/Gemfile +2 -0
  5. data/README.md +1 -1
  6. data/assets/event_page.png +0 -0
  7. data/assets/index_page.png +0 -0
  8. data/assets/topic_page.png +0 -0
  9. data/bench/bench_jobs_rails.rb +1 -1
  10. data/bench/bench_jobs_raw.rb +1 -1
  11. data/bench/uljob.rb +1 -1
  12. data/lib/action_cable/subscription_adapter/litecable.rb +1 -11
  13. data/lib/active_support/cache/litecache.rb +1 -1
  14. data/lib/generators/litestack/install/templates/database.yml +5 -1
  15. data/lib/litestack/liteboard/liteboard.rb +172 -35
  16. data/lib/litestack/liteboard/views/index.erb +52 -20
  17. data/lib/litestack/liteboard/views/layout.erb +189 -38
  18. data/lib/litestack/liteboard/views/litecable.erb +118 -0
  19. data/lib/litestack/liteboard/views/litecache.erb +144 -0
  20. data/lib/litestack/liteboard/views/litedb.erb +168 -0
  21. data/lib/litestack/liteboard/views/litejob.erb +151 -0
  22. data/lib/litestack/litecable.rb +27 -37
  23. data/lib/litestack/litecable.sql.yml +1 -1
  24. data/lib/litestack/litecache.rb +7 -18
  25. data/lib/litestack/litedb.rb +17 -2
  26. data/lib/litestack/litejob.rb +2 -3
  27. data/lib/litestack/litejobqueue.rb +51 -48
  28. data/lib/litestack/litemetric.rb +46 -69
  29. data/lib/litestack/litemetric.sql.yml +14 -12
  30. data/lib/litestack/litemetric_collector.sql.yml +4 -4
  31. data/lib/litestack/litequeue.rb +9 -20
  32. data/lib/litestack/litescheduler.rb +84 -0
  33. data/lib/litestack/litesearch/index.rb +230 -0
  34. data/lib/litestack/litesearch/model.rb +178 -0
  35. data/lib/litestack/litesearch/schema.rb +193 -0
  36. data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +147 -0
  37. data/lib/litestack/litesearch/schema_adapters/basic_adapter.rb +128 -0
  38. data/lib/litestack/litesearch/schema_adapters/contentless_adapter.rb +17 -0
  39. data/lib/litestack/litesearch/schema_adapters/standalone_adapter.rb +33 -0
  40. data/lib/litestack/litesearch/schema_adapters.rb +9 -0
  41. data/lib/litestack/litesearch.rb +37 -0
  42. data/lib/litestack/litesupport.rb +55 -125
  43. data/lib/litestack/version.rb +1 -1
  44. data/lib/litestack.rb +2 -1
  45. data/lib/sequel/adapters/litedb.rb +3 -2
  46. metadata +20 -3
@@ -0,0 +1,147 @@
1
+ class Litesearch::Schema::BackedAdapter < Litesearch::Schema::ContentlessAdapter
2
+
3
+ private
4
+
5
+ def table
6
+ @schema[:table]
7
+ end
8
+
9
+ def generate_sql
10
+ super
11
+ @sql[:rebuild] = :rebuild_sql
12
+ @sql[:drop_primary_triggers] = :drop_primary_triggers_sql
13
+ @sql[:drop_secondary_triggers] = :drop_secondary_triggers_sql
14
+ @sql[:create_primary_triggers] = :create_primary_triggers_sql
15
+ @sql[:create_secondary_triggers] = :create_secondary_triggers_sql
16
+ end
17
+
18
+ def drop_primary_triggers_sql
19
+ sql = <<-SQL
20
+ DROP TRIGGER IF EXISTS #{name}_insert;
21
+ DROP TRIGGER IF EXISTS #{name}_update;
22
+ DROP TRIGGER IF EXISTS #{name}_update_not;
23
+ DROP TRIGGER IF EXISTS #{name}_delete;
24
+ SQL
25
+ end
26
+
27
+ def create_primary_triggers_sql(active=false)
28
+ when_stmt = "TRUE"
29
+ cols = active_cols_names
30
+ if filter = @schema[:filter_column]
31
+ when_stmt = "NEW.#{filter} = TRUE"
32
+ cols << filter
33
+ end
34
+
35
+ sql = <<-SQL
36
+ CREATE TRIGGER #{name}_insert AFTER INSERT ON #{table} WHEN #{when_stmt} BEGIN
37
+ INSERT OR REPLACE INTO #{name}(rowid, #{active_field_names.join(', ')}) VALUES (NEW.rowid, #{trigger_cols_sql});
38
+ END;
39
+ CREATE TRIGGER #{name}_update AFTER UPDATE OF #{cols.join(', ')} ON #{table} WHEN #{when_stmt} BEGIN
40
+ INSERT OR REPLACE INTO #{name}(rowid, #{active_field_names.join(', ')}) VALUES (NEW.rowid, #{trigger_cols_sql});
41
+ END;
42
+ CREATE TRIGGER #{name}_update_not AFTER UPDATE OF #{cols.join(', ')} ON #{table} WHEN NOT #{when_stmt} BEGIN
43
+ DELETE FROM #{name} WHERE rowid = NEW.rowid;
44
+ END;
45
+ CREATE TRIGGER #{name}_delete AFTER DELETE ON #{table} BEGIN
46
+ DELETE FROM #{name} WHERE rowid = OLD.id;
47
+ END;
48
+ SQL
49
+ end
50
+
51
+ def drop_secondary_trigger_sql(target_table, target_col, col)
52
+ "DROP TRIGGER IF EXISTS #{target_table}_#{target_col}_#{col}_#{name}_update;"
53
+ end
54
+
55
+ def create_secondary_trigger_sql(target_table, target_col, col)
56
+ sql = <<-SQL
57
+ CREATE TRIGGER #{target_table}_#{target_col}_#{col}_#{name}_update AFTER UPDATE OF #{target_col} ON #{target_table} BEGIN
58
+ #{rebuild_sql} AND #{table}.#{col} = NEW.id;
59
+ END;
60
+ SQL
61
+ end
62
+
63
+ def drop_secondary_triggers_sql
64
+ sql = ""
65
+ @schema[:fields].each do |name, field|
66
+ if field[:trigger_sql]
67
+ sql << drop_secondary_trigger_sql(field[:target_table], field[:target_col], field[:col])
68
+ end
69
+ end
70
+ return sql.empty? ? nil : sql
71
+ end
72
+
73
+
74
+ def create_secondary_triggers_sql
75
+ sql = ""
76
+ @schema[:fields].each do |name, field|
77
+ if field[:trigger_sql]
78
+ sql << create_secondary_trigger_sql(field[:target_table], field[:target_col], field[:col])
79
+ end
80
+ end
81
+ return sql.empty? ? nil : sql
82
+ end
83
+
84
+
85
+ def rebuild_sql
86
+ conditions = ""
87
+ jcs = join_conditions_sql
88
+ fs = filter_sql
89
+ conditions = " ON #{jcs} #{fs}" unless jcs.empty? && fs.empty?
90
+ "INSERT OR REPLACE INTO #{name}(rowid, #{active_field_names.join(', ')}) SELECT #{table}.id, #{select_cols_sql} FROM #{join_tables_sql} #{conditions}"
91
+ end
92
+
93
+ def enrich_schema
94
+ @schema[:fields].each do |name, field|
95
+ if field[:target] && ! field[:target].start_with?("#{table}.")
96
+ field[:target] = field[:target].downcase
97
+ target_table, target_col = field[:target].split('.')
98
+ field[:col] = "#{name}_id".to_sym unless field[:col]
99
+ field[:target_table] = target_table.to_sym
100
+ field[:target_col] = target_col.to_sym
101
+ field[:sql] = "(SELECT #{field[:target_col]} FROM #{field[:target_table]} WHERE id = NEW.#{field[:col]})"
102
+ field[:trigger_sql] = true # create_secondary_trigger_sql(field[:target_table], field[:target_col], field[:col])
103
+ field[:target_table_alias] = "#{field[:target_table]}_#{name}"
104
+ else
105
+ field[:col] = name unless field[:col]
106
+ field[:sql] = field[:col]
107
+ field[:target_table] = @schema[:table]
108
+ field[:target] = "#{@schema[:table]}.#{field[:sql]}"
109
+ end
110
+ end
111
+ end
112
+
113
+ def filter_sql
114
+ sql = ""
115
+ sql << " AND #{@schema[:filter_column]} = TRUE " if @schema[:filter_column]
116
+ sql
117
+ end
118
+
119
+ def trigger_cols_sql
120
+ active_fields.collect do |name, field|
121
+ field[:trigger_sql] ? field[:sql] : "NEW.#{field[:sql]}"
122
+ end.join(", ")
123
+ end
124
+
125
+ def select_cols_sql
126
+ active_fields.collect do |name, field|
127
+ field[:trigger_sql] != nil ? "#{field[:target_table_alias]}.#{field[:target_col]}" : field[:target]
128
+ end.join(', ')
129
+ end
130
+
131
+ def join_tables_sql
132
+ tables = [@schema[:table]]
133
+ active_fields.each do |name, field|
134
+ tables << "#{field[:target_table]} AS #{field[:target_table_alias]}" if field[:trigger_sql]
135
+ end
136
+ tables.uniq.join(", ")
137
+ end
138
+
139
+ def join_conditions_sql
140
+ conditions = []
141
+ active_fields.each do |name, field|
142
+ conditions << "#{field[:target_table_alias]}.id = #{@schema[:table]}.#{field[:col]}" if field[:trigger_sql]
143
+ end
144
+ conditions.join(" AND ")
145
+ end
146
+
147
+ end
@@ -0,0 +1,128 @@
1
+ class Litesearch::Schema::BasicAdapter
2
+
3
+ def initialize(schema)
4
+ @schema = schema
5
+ @sql = {}
6
+ enrich_schema
7
+ generate_sql
8
+ end
9
+
10
+ def name
11
+ @schema[:name]
12
+ end
13
+
14
+ def table
15
+ @schema[:table]
16
+ end
17
+
18
+ def fields
19
+ @schema[:fields]
20
+ end
21
+
22
+ def field_names
23
+ @schema[:fields].keys
24
+ end
25
+
26
+ def active_fields
27
+ @schema[:fields].select{|k, v| v[:weight] != 0 }
28
+ end
29
+
30
+ def active_field_names
31
+ active_fields.keys
32
+ end
33
+
34
+ def active_cols_names
35
+ active_fields.collect{|k, v| v[:col]}
36
+ end
37
+
38
+ def weights
39
+ @schema[:fields].values.collect{|v| v[:weight].to_f }
40
+ end
41
+
42
+ def active_weights
43
+ active_fields.values.collect{|v| v[:weight].to_f }
44
+ end
45
+
46
+ def tokenizer_sql
47
+ Litesearch::Schema::TOKENIZERS[@schema[:tokenizer]]
48
+ end
49
+
50
+ def order_fields(old_schema)
51
+ new_fields = {}
52
+ old_field_names = old_schema.schema[:fields].keys
53
+ old_field_names.each do |name|
54
+ new_fields[name] = @schema[:fields].delete(name)
55
+ end
56
+ missing_field_names = field_names - old_field_names
57
+ missing_field_names.each do |name|
58
+ new_fields[name] = @schema[:fields].delete(name)
59
+ end
60
+ @schema[:fields] = new_fields # this should be in order now
61
+ generate_sql
62
+ enrich_schema
63
+ end
64
+
65
+ def sql_for(method, *args)
66
+ if sql = @sql[method]
67
+ if sql.is_a? String
68
+ return sql
69
+ elsif sql.is_a? Proc
70
+ return sql.call(*args)
71
+ elsif sql.is_a? Symbol
72
+ return self.send(sql, *args)
73
+ elsif sql.is_a? Litesearch::SchemaChangeException
74
+ raise sql
75
+ end
76
+ end
77
+ end
78
+
79
+ def generate_sql
80
+ @sql[:create_index] = :create_index_sql
81
+ @sql[:insert] = "INSERT OR REPLACE INTO #{name}(rowid, #{active_col_names_sql}) VALUES (:id, #{active_col_names_var_sql}) RETURNING rowid"
82
+ @sql[:delete] = "DELETE FROM #{name} WHERE rowid = :id"
83
+ @sql[:count] = "SELECT count(*) FROM #{name}(:term)"
84
+ @sql[:count_all] = "SELECT count(*) FROM #{name}"
85
+ @sql[:delete_all] = "DELETE FROM #{name}"
86
+ @sql[:drop] = "DROP TABLE #{name}"
87
+ @sql[:expand_data] = "UPDATE #{name}_data SET block = block || zeroblob(:length) WHERE id = 1"
88
+ @sql[:expand_docsize] = "UPDATE #{name}_docsize SET sz = sz || zeroblob(:length)"
89
+ @sql[:ranks] = :ranks_sql
90
+ @sql[:set_config_value] = "INSERT OR REPLACE INTO #{name}_config(k, v) VALUES (:key, :value)"
91
+ @sql[:get_config_value] = "SELECT v FROM #{name}_config WHERE k = :key"
92
+ @sql[:search] = "SELECT rowid AS id, -rank AS search_rank FROM #{name}(:term) WHERE rank !=0 ORDER BY rank LIMIT :limit OFFSET :offset"
93
+ @sql[:update_index] = "UPDATE sqlite_schema SET sql = :sql WHERE name = '#{name}'"
94
+ @sql[:update_content_table] = "UPDATE sqlite_schema SET sql = :sql WHERE name = '#{name}_content'"
95
+ end
96
+
97
+ private
98
+
99
+ def ranks_sql(active=false)
100
+ if active
101
+ weights_sql = weights.join(', ')
102
+ else
103
+ weights_sql = active_weights.join(', ')
104
+ end
105
+ "INSERT INTO #{name}(#{name}, rank) VALUES ('rank', 'bm25(#{weights_sql})')"
106
+ end
107
+
108
+ def active_col_names_sql
109
+ active_field_names.join(', ')
110
+ end
111
+
112
+ def active_col_names_var_sql
113
+ ":#{active_field_names.join(', :')}"
114
+ end
115
+
116
+ def col_names_sql
117
+ field_names.join(', ')
118
+ end
119
+
120
+ def col_names_var_sql
121
+ ":#{field_names.join(', :')}"
122
+ end
123
+
124
+ def enrich_schema
125
+ end
126
+
127
+ end
128
+
@@ -0,0 +1,17 @@
1
+ class Litesearch::Schema::ContentlessAdapter < Litesearch::Schema::BasicAdapter
2
+
3
+ private
4
+
5
+ def generate_sql
6
+ super
7
+ #@sql[:rebuild_index] = Litesearch::SchemaChangeException.new("You cannot rebuild a contentless index")
8
+ #@sql[:rebuild] = Litesearch::SchemaChangeException.new("You cannot rebuild a contentless index")
9
+ end
10
+
11
+ def create_index_sql(active = false)
12
+ col_names = active ? active_col_names_sql : col_names_sql
13
+ "CREATE VIRTUAL TABLE #{name} USING FTS5(#{col_names}, content='', contentless_delete=1, tokenize='#{tokenizer_sql}')"
14
+ end
15
+
16
+ end
17
+
@@ -0,0 +1,33 @@
1
+ class Litesearch::Schema::StandaloneAdapter < Litesearch::Schema::BasicAdapter
2
+
3
+ def generate_sql
4
+ super
5
+ @sql[:move_content] = "ALTER TABLE #{name}_content RENAME TO #{name}_content_temp"
6
+ @sql[:adjust_temp_content] = "UPDATE sqlite_schema SET sql (SELECT sql FROM sqlite_schema WHERE name = '#{name}_content') WHERE name = #{name}_content_temp"
7
+ @sql[:restore_content] = "ALTER TABLE #{name}_content_temp RENAME TO #{name}_content"
8
+ @sql[:rebuild] = "INSERT INTO #{name}(#{name}) VALUES ('rebuild')"
9
+ @sql[:drop_content_table] = "DROP TABLE #{name}_content"
10
+ @sql[:drop_content_col] = :drop_content_col_sql
11
+ @sql[:create_content_table] = :create_content_table_sql
12
+ @sql[:search] = "SELECT rowid AS id, *, -rank AS search_rank FROM #{name}(:term) WHERE rank !=0 ORDER BY rank LIMIT :limit OFFSET :offset"
13
+ end
14
+
15
+ private
16
+
17
+ def create_index_sql(active = false)
18
+ col_names = active ? active_col_names_sql : col_names_sql
19
+ "CREATE VIRTUAL TABLE #{name} USING FTS5(#{col_names}, tokenize='#{tokenizer_sql}')"
20
+ end
21
+
22
+ def drop_content_col_sql(col_index)
23
+ "ALTER TABLE #{name}_content DROP COLUMN c#{col_index}"
24
+ end
25
+
26
+ def create_content_table_sql(count)
27
+ cols = []
28
+ count.times{|i| cols << "c#{i}" }
29
+ "CREATE TABLE #{name}_content(id INTEGER PRIMARY KEY, #{cols.join(', ')})"
30
+ end
31
+
32
+ end
33
+
@@ -0,0 +1,9 @@
1
+ require_relative './schema_adapters/basic_adapter.rb'
2
+ require_relative './schema_adapters/standalone_adapter.rb'
3
+ require_relative './schema_adapters/contentless_adapter.rb'
4
+ require_relative './schema_adapters/backed_adapter.rb'
5
+
6
+
7
+
8
+
9
+
@@ -0,0 +1,37 @@
1
+ module Litesearch
2
+ class Index; end
3
+ class Schema; end
4
+ end
5
+
6
+ require_relative './litesearch/index'
7
+ require_relative './litesearch/model'
8
+
9
+ module Litesearch
10
+
11
+ def litesearch_index_cache
12
+ @litesearch_index_cache ||= {}
13
+ end
14
+
15
+ def search_index(name)
16
+ # normalize the index name
17
+ # find the index in the db cache
18
+ name = name.to_s.downcase.to_sym
19
+ index = litesearch_index_cache[name]
20
+ # if the index is in the cache and no block is given then return it
21
+ return index if index && !block_given?
22
+ # if either there is no index in the cache or a block is given
23
+ # create a new index instance and then place it in the cache and return
24
+ if block_given?
25
+ index = Index.new(self, name) do |schema|
26
+ yield schema
27
+ schema.name(name)
28
+ end
29
+ else
30
+ index = Index.new(self, name)
31
+ end
32
+ litesearch_index_cache[name] = index
33
+ end
34
+
35
+ end
36
+
37
+
@@ -7,14 +7,11 @@ require 'yaml'
7
7
  require 'pathname'
8
8
  require 'fileutils'
9
9
 
10
+ require_relative "./litescheduler"
11
+
10
12
  module Litesupport
11
13
 
12
14
  class Error < StandardError; end
13
-
14
- def self.max_contexts
15
- return 50 if scheduler == :fiber || scheduler == :polyphony
16
- 5
17
- end
18
15
 
19
16
  # Detect the Rack or Rails environment.
20
17
  def self.detect_environment
@@ -23,7 +20,7 @@ module Litesupport
23
20
  elsif ENV["RACK_ENV"]
24
21
  ENV["RACK_ENV"]
25
22
  elsif ENV["APP_ENV"]
26
- ENV["RACK_ENV"]
23
+ ENV["APP_ENV"]
27
24
  else
28
25
  "development"
29
26
  end
@@ -32,89 +29,11 @@ module Litesupport
32
29
  def self.environment
33
30
  @environment ||= detect_environment
34
31
  end
35
-
36
- # cache the scheduler we are running in
37
- # it is an error to change the scheduler for a process
38
- # or for a child forked from that process
39
- def self.scheduler
40
- @scehduler ||= detect_scheduler
41
- end
42
-
43
- # identify which scheduler we are running in
44
- # we currently support :fiber, :polyphony, :iodine & :threaded
45
- # in the future we might want to expand to other schedulers
46
- def self.detect_scheduler
47
- return :fiber if Fiber.scheduler
48
- return :polyphony if defined? Polyphony
49
- return :iodine if defined? Iodine
50
- return :threaded # fall back for all other schedulers
51
- end
52
-
53
- # spawn a new execution context
54
- def self.spawn(&block)
55
- if self.scheduler == :fiber
56
- Fiber.schedule(&block)
57
- elsif self.scheduler == :polyphony
58
- spin(&block)
59
- elsif self.scheduler == :threaded or self.scheduler == :iodine
60
- Thread.new(&block)
61
- end
62
- # we should never reach here
63
- end
64
-
65
- def self.context
66
- if scheduler == :fiber || scheduler == :poylphony
67
- Fiber.current.storage
68
- else
69
- Thread.current
70
- end
71
- end
72
-
73
- def self.current_context
74
- if scheduler == :fiber || scheduler == :poylphony
75
- Fiber.current
76
- else
77
- Thread.current
78
- end
79
- end
80
-
81
- # switch the execution context to allow others to run
82
- def self.switch
83
- if self.scheduler == :fiber
84
- Fiber.scheduler.yield
85
- true
86
- elsif self.scheduler == :polyphony
87
- Fiber.current.schedule
88
- Thread.current.switch_fiber
89
- true
90
- else
91
- #Thread.pass
92
- false
93
- end
94
- end
95
-
96
- # mutex initialization
97
- def self.mutex
98
- # a single mutex per process (is that ok?)
99
- @@mutex ||= Mutex.new
100
- end
101
-
102
- # bold assumption, we will only synchronize threaded code
103
- # if some code explicitly wants to synchronize a fiber
104
- # they must send (true) as a parameter to this method
105
- # else it is a no-op for fibers
106
- def self.synchronize(fiber_sync = false, &block)
107
- if self.scheduler == :fiber or self.scheduler == :polyphony
108
- yield # do nothing, just run the block as is
109
- else
110
- self.mutex.synchronize(&block)
111
- end
112
- end
113
32
 
114
33
  # common db object options
115
34
  def self.create_db(path)
116
35
  db = SQLite3::Database.new(path)
117
- db.busy_handler{ switch || sleep(0.0001) }
36
+ db.busy_handler{ Litescheduler.switch || sleep(0.0001) }
118
37
  db.journal_mode = "WAL"
119
38
  db.instance_variable_set(:@stmts, {})
120
39
  class << db
@@ -153,7 +72,7 @@ module Litesupport
153
72
  end
154
73
 
155
74
  def synchronize(&block)
156
- if Litesupport.scheduler == :threaded || Litesupport.scheduler == :iodine
75
+ if Litescheduler.backend == :threaded || Litescheduler.backend == :iodine
157
76
  @mutex.synchronize{ block.call }
158
77
  else
159
78
  block.call
@@ -162,57 +81,32 @@ module Litesupport
162
81
 
163
82
  end
164
83
 
165
- module Forkable
166
-
167
- def _fork(*args)
168
- ppid = Process.pid
169
- result = super
170
- if Process.pid != ppid
171
- # trigger a restart of all connections owned by Litesupport::Pool
172
- end
173
- result
174
- end
175
-
176
- end
177
-
178
- #::Process.singleton_class.prepend(::Litesupport::Forkable)
179
-
180
84
  class Pool
181
85
 
182
86
  def initialize(count, &block)
183
87
  @count = count
184
88
  @block = block
185
- @resources = []
89
+ @resources = Thread::Queue.new
186
90
  @mutex = Litesupport::Mutex.new
187
91
  @count.times do
188
92
  resource = @mutex.synchronize{ block.call }
189
- @resources << [resource, :free]
93
+ @resources << resource
190
94
  end
191
95
  end
192
96
 
193
97
  def acquire
194
- # check for pid changes
195
- acquired = false
196
98
  result = nil
197
- while !acquired do
198
- @mutex.synchronize do
199
- if resource = @resources.find{|r| r[1] == :free }
200
- resource[1] = :busy
201
- begin
202
- result = yield resource[0]
203
- rescue Exception => e
204
- raise e
205
- ensure
206
- resource[1] = :free
207
- acquired = true
208
- end
209
- end
210
- end
211
- sleep 0.001 unless acquired
99
+ resource = @resources.pop
100
+ begin
101
+ result = yield resource
102
+ rescue Exception => e
103
+ raise e
104
+ ensure
105
+ @resources << resource
212
106
  end
213
107
  result
214
108
  end
215
-
109
+
216
110
  end
217
111
 
218
112
  module ForkListener
@@ -230,7 +124,7 @@ module Litesupport
230
124
  def _fork(*args)
231
125
  ppid = Process.pid
232
126
  result = super
233
- if Process.pid != ppid && [:threaded, :iodine].include?(Litesupport.scheduler)
127
+ if Process.pid != ppid && [:threaded, :iodine].include?(Litescheduler.backend)
234
128
  ForkListener.listeners.each{|l| l.call }
235
129
  end
236
130
  result
@@ -333,15 +227,20 @@ module Litesupport
333
227
  def run_method(method, *args)
334
228
  @conn.acquire{|q| q.send(method, *args)}
335
229
  end
230
+
231
+ def run_stmt_method(stmt, method, *args)
232
+ @conn.acquire{|q| q.stmts[stmt].send(method, *args)}
233
+ end
234
+
336
235
 
337
236
  def create_pooled_connection(count = 1)
338
237
  Litesupport::Pool.new(1){create_connection}
339
238
  end
340
239
 
341
240
  # common db object options
342
- def create_connection
241
+ def create_connection(path_to_sql_file = nil)
343
242
  conn = SQLite3::Database.new(@options[:path])
344
- conn.busy_handler{ Litesupport.switch || sleep(rand * 0.002) }
243
+ conn.busy_handler{ Litescheduler.switch || sleep(rand * 0.002) }
345
244
  conn.journal_mode = "WAL"
346
245
  conn.synchronous = @options[:sync] || 1
347
246
  conn.mmap_size = @options[:mmap_size] || 0
@@ -349,9 +248,40 @@ module Litesupport
349
248
  class << conn
350
249
  attr_reader :stmts
351
250
  end
251
+ yield conn if block_given?
252
+ # use the <client>.sql.yml file to define the schema and compile prepared statements
253
+ unless path_to_sql_file.nil?
254
+ sql = YAML.load_file(path_to_sql_file)
255
+ version = conn.get_first_value("PRAGMA user_version")
256
+ sql["schema"].each_pair do |v, obj|
257
+ if v > version
258
+ conn.transaction do
259
+ obj.each do |k, s|
260
+ begin
261
+ conn.execute(s)
262
+ rescue Exception => e
263
+ STDERR.puts "Error parsing #{k}"
264
+ STDERR.puts s
265
+ raise e
266
+ end
267
+ end
268
+ conn.user_version = v
269
+ end
270
+ end
271
+ end
272
+ sql["stmts"].each do |k, v|
273
+ begin
274
+ conn.stmts[k.to_sym] = conn.prepare(v)
275
+ rescue Exception => e
276
+ STDERR.puts "Error parsing #{k}"
277
+ STDERR.puts v
278
+ raise e
279
+ end
280
+ end
281
+ end
352
282
  conn
353
283
  end
354
-
284
+
355
285
  end
356
286
 
357
287
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Litestack
4
- VERSION = "0.2.6"
4
+ VERSION = "0.4.1"
5
5
  end
data/lib/litestack.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  # load core classes
4
4
  require_relative "./litestack/version"
5
+ require_relative "./litestack/litescheduler"
5
6
  require_relative "./litestack/litesupport"
6
7
  require_relative "./litestack/litemetric"
7
8
  require_relative "./litestack/litedb"
@@ -16,7 +17,7 @@ require_relative "./railties/rails/commands/dbconsole" if defined? Rails && defi
16
17
  require_relative "./active_support/cache/litecache" if defined? ActiveSupport
17
18
  require_relative "./active_job/queue_adapters/litejob_adapter" if defined? ActiveJob
18
19
  require_relative "./action_cable/subscription_adapter/litecable" if defined? ActionCable
19
- require_relative "./litestack/railtie" if defined? Rails
20
+ require_relative "./litestack/railtie" if defined? Rails::Railtie
20
21
 
21
22
  module Litestack
22
23
  class NotImplementedError < Exception; end
@@ -1,10 +1,10 @@
1
1
  require_relative '../../litestack/litedb'
2
2
  require 'sequel'
3
3
  require 'sequel/adapters/sqlite'
4
- #require 'shared/litedb'
5
4
 
6
5
  module Sequel
7
6
  module Litedb
7
+
8
8
  include SQLite
9
9
 
10
10
  LITEDB_TYPES = SQLITE_TYPES
@@ -15,7 +15,7 @@ module Sequel
15
15
 
16
16
  def connect(server)
17
17
 
18
- Sequel.extension :fiber_concurrency if [:fiber, :polyphony].include? Litesupport.scheduler
18
+ Sequel.extension :fiber_concurrency if [:fiber, :polyphony].include? Litescheduler.backend
19
19
 
20
20
  opts = server_opts(server)
21
21
  opts[:database] = ':memory:' if blank_object?(opts[:database])
@@ -36,6 +36,7 @@ module Sequel
36
36
  end
37
37
 
38
38
  db.instance_variable_set(:@prepared_statements, {})
39
+ @raw_db = db
39
40
  db
40
41
  end
41
42