litestack 0.2.6 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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