bormashino-sequel-sqljs-adapter 0.0.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +43 -0
- data/README.md +5 -0
- data/lib/bormashino_sequel_sqljs_adapter/shared/sqljs.rb +1068 -0
- data/lib/bormashino_sequel_sqljs_adapter/sqljs.rb +458 -0
- data/lib/bormashino_sequel_sqljs_adapter/version.rb +5 -0
- data/lib/bormashino_sequel_sqljs_adapter.rb +6 -0
- metadata +67 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
require_relative 'shared/sqljs'
|
|
2
|
+
Sequel.single_threaded = true
|
|
3
|
+
|
|
4
|
+
module Sequel
|
|
5
|
+
module Sqljs
|
|
6
|
+
class Wrapper
|
|
7
|
+
class Error < StandardError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(jsobject)
|
|
11
|
+
@db = jsobject
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def exec(sql)
|
|
15
|
+
columns = @db.call(:prepareGetColumnNames, sql).to_rb
|
|
16
|
+
ret = @db.call(:exec, sql).to_rb
|
|
17
|
+
|
|
18
|
+
if ret.is_a?(Hash) && ret['type'] == 'exception'
|
|
19
|
+
err = Error.new(ret['message']).tap do |e|
|
|
20
|
+
e.set_backtrace(ret['stack'])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
raise err
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
{ result: ret, columns: }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get_rows_modified
|
|
30
|
+
@db.call(:getRowsModified).to_rb
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
FALSE_VALUES = (%w'0 false f no n'.each(&:freeze) + [0]).freeze
|
|
35
|
+
|
|
36
|
+
blob = Object.new
|
|
37
|
+
def blob.call(s)
|
|
38
|
+
Sequel::SQL::Blob.new(s.to_s)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
boolean = Object.new
|
|
42
|
+
def boolean.call(s)
|
|
43
|
+
s = s.downcase if s.is_a?(String)
|
|
44
|
+
!FALSE_VALUES.include?(s)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
date = Object.new
|
|
48
|
+
def date.call(s)
|
|
49
|
+
case s
|
|
50
|
+
when String
|
|
51
|
+
Sequel.string_to_date(s)
|
|
52
|
+
when Integer
|
|
53
|
+
Date.jd(s)
|
|
54
|
+
when Float
|
|
55
|
+
Date.jd(s.to_i)
|
|
56
|
+
else
|
|
57
|
+
raise Sequel::Error, "unhandled type when converting to date: #{s.inspect} (#{s.class.inspect})"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
integer = Object.new
|
|
62
|
+
def integer.call(s)
|
|
63
|
+
s ? s.to_i : nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
float = Object.new
|
|
67
|
+
def float.call(s)
|
|
68
|
+
s.to_f
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
numeric = Object.new
|
|
72
|
+
def numeric.call(s)
|
|
73
|
+
s = s.to_s unless s.is_a?(String)
|
|
74
|
+
begin
|
|
75
|
+
BigDecimal(s)
|
|
76
|
+
rescue StandardError
|
|
77
|
+
s
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
time = Object.new
|
|
82
|
+
def time.call(s)
|
|
83
|
+
case s
|
|
84
|
+
when String
|
|
85
|
+
Sequel.string_to_time(s)
|
|
86
|
+
when Integer
|
|
87
|
+
Sequel::SQLTime.create(s / 3600, (s % 3600) / 60, s % 60)
|
|
88
|
+
when Float
|
|
89
|
+
s, f = s.divmod(1)
|
|
90
|
+
Sequel::SQLTime.create(s / 3600, (s % 3600) / 60, s % 60, (f * 1000000).round)
|
|
91
|
+
else
|
|
92
|
+
raise Sequel::Error, "unhandled type when converting to date: #{s.inspect} (#{s.class.inspect})"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Hash with string keys and callable values for converting SQLite types.
|
|
97
|
+
sqlite_types = {}
|
|
98
|
+
{
|
|
99
|
+
%w[date] => date,
|
|
100
|
+
%w[time] => time,
|
|
101
|
+
%w[bit bool boolean] => boolean,
|
|
102
|
+
%w[integer smallint mediumint int bigint] => integer,
|
|
103
|
+
%w[numeric decimal money] => numeric,
|
|
104
|
+
%w[float double real dec fixed] + ['double precision'] => float,
|
|
105
|
+
%w[blob] => blob,
|
|
106
|
+
}.each do |k, v|
|
|
107
|
+
k.each { |n| sqlite_types[n] = v }
|
|
108
|
+
end
|
|
109
|
+
SQLITE_TYPES = sqlite_types
|
|
110
|
+
|
|
111
|
+
class Database < Sequel::Database
|
|
112
|
+
include ::Sequel::Sqljs::DatabaseMethods
|
|
113
|
+
|
|
114
|
+
set_adapter_scheme :sqljs
|
|
115
|
+
|
|
116
|
+
# Mimic the file:// uri, by having 2 preceding slashes specify a relative
|
|
117
|
+
# path, and 3 preceding slashes specify an absolute path.
|
|
118
|
+
def self.uri_to_options(uri) # :nodoc:
|
|
119
|
+
{ database: uri.host.nil? && uri.path == '/' ? nil : "#{uri.host}#{uri.path}" }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private_class_method :uri_to_options
|
|
123
|
+
|
|
124
|
+
# The conversion procs to use for this database
|
|
125
|
+
attr_reader :conversion_procs
|
|
126
|
+
|
|
127
|
+
def initialize(opts = OPTS)
|
|
128
|
+
super
|
|
129
|
+
@allow_regexp = typecast_value_boolean(opts[:setup_regexp_function])
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Connect to the database. Since SQLite is a file based database,
|
|
133
|
+
# available options are limited:
|
|
134
|
+
#
|
|
135
|
+
# :database :: database name (filename or ':memory:' or file: URI)
|
|
136
|
+
# :readonly :: open database in read-only mode; useful for reading
|
|
137
|
+
# static data that you do not want to modify
|
|
138
|
+
# :timeout :: how long to wait for the database to be available if it
|
|
139
|
+
# is locked, given in milliseconds (default is 5000)
|
|
140
|
+
def connect(server)
|
|
141
|
+
opts = server_opts(server)
|
|
142
|
+
# db = ::SQLite3::Database.new(opts[:database].to_s, sqlite3_opts)
|
|
143
|
+
db = Wrapper.new(JS.global[opts[:database].to_sym])
|
|
144
|
+
|
|
145
|
+
# db.busy_timeout(typecast_value_integer(opts.fetch(:timeout, 5000)))
|
|
146
|
+
|
|
147
|
+
# db.extended_result_codes = true
|
|
148
|
+
|
|
149
|
+
connection_pragmas.each { |s| log_connection_yield(s, db) { db.exec(s) } }
|
|
150
|
+
|
|
151
|
+
if typecast_value_boolean(opts[:setup_regexp_function])
|
|
152
|
+
db.create_function('regexp', 2) do |func, regexp_str, string|
|
|
153
|
+
func.result = Regexp.new(regexp_str).match(string) ? 1 : 0
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
class << db
|
|
158
|
+
attr_reader :prepared_statements
|
|
159
|
+
end
|
|
160
|
+
db.instance_variable_set(:@prepared_statements, {})
|
|
161
|
+
|
|
162
|
+
db
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Whether this Database instance is setup to allow regexp matching.
|
|
166
|
+
# True if the :setup_regexp_function option was passed when creating the Database.
|
|
167
|
+
def allow_regexp?
|
|
168
|
+
@allow_regexp
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Disconnect given connections from the database.
|
|
172
|
+
def disconnect_connection(c)
|
|
173
|
+
c.prepared_statements.each_value { |v| v.first.close }
|
|
174
|
+
c.close
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Run the given SQL with the given arguments and yield each row.
|
|
178
|
+
def execute(sql, opts = OPTS, &)
|
|
179
|
+
_execute(:select, sql, opts, &)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Run the given SQL with the given arguments and return the number of changed rows.
|
|
183
|
+
def execute_dui(sql, opts = OPTS)
|
|
184
|
+
_execute(:update, sql, opts)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Drop any prepared statements on the connection when executing DDL. This is because
|
|
188
|
+
# prepared statements lock the table in such a way that you can't drop or alter the
|
|
189
|
+
# table while a prepared statement that references it still exists.
|
|
190
|
+
def execute_ddl(sql, opts = OPTS)
|
|
191
|
+
synchronize(opts[:server]) do |conn|
|
|
192
|
+
conn.prepared_statements.each_value { |cps, _s| cps.close }
|
|
193
|
+
conn.prepared_statements.clear
|
|
194
|
+
super
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def execute_insert(sql, opts = OPTS)
|
|
199
|
+
_execute(:insert, sql, opts)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def freeze
|
|
203
|
+
@conversion_procs.freeze
|
|
204
|
+
super
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Handle Integer and Float arguments, since SQLite can store timestamps as integers and floats.
|
|
208
|
+
def to_application_timestamp(s)
|
|
209
|
+
case s
|
|
210
|
+
when String
|
|
211
|
+
super
|
|
212
|
+
when Integer
|
|
213
|
+
super(Time.zone.at(s).to_s)
|
|
214
|
+
when Float
|
|
215
|
+
super(DateTime.jd(s).to_s)
|
|
216
|
+
else
|
|
217
|
+
raise Sequel::Error, "unhandled type when converting to : #{s.inspect} (#{s.class.inspect})"
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
private
|
|
222
|
+
|
|
223
|
+
def adapter_initialize
|
|
224
|
+
@conversion_procs = SQLITE_TYPES.dup
|
|
225
|
+
@conversion_procs['datetime'] = @conversion_procs['timestamp'] = method(:to_application_timestamp)
|
|
226
|
+
set_integer_booleans
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Yield an available connection. Rescue
|
|
230
|
+
# any SQLite3::Exceptions and turn them into DatabaseErrors.
|
|
231
|
+
def _execute(type, sql, opts, &)
|
|
232
|
+
synchronize(opts[:server]) do |conn|
|
|
233
|
+
return execute_prepared_statement(conn, type, sql, opts, &) if sql.is_a?(Symbol)
|
|
234
|
+
|
|
235
|
+
log_args = opts[:arguments]
|
|
236
|
+
args = {}
|
|
237
|
+
opts.fetch(:arguments, OPTS).each { |k, v| args[k] = prepared_statement_argument(v) }
|
|
238
|
+
case type
|
|
239
|
+
when :select
|
|
240
|
+
log_connection_yield(sql, conn, log_args) do
|
|
241
|
+
result = conn.exec(sql)
|
|
242
|
+
# fetch table names
|
|
243
|
+
tables = conn.exec("select name from sqlite_master where type='table'")[:result].first['values']
|
|
244
|
+
target_table = tables.map(&:first).find { |n| sql.include?("FROM \`#{n}\`") }
|
|
245
|
+
if target_table
|
|
246
|
+
types_result = conn.exec("pragma table_info('#{target_table}')")
|
|
247
|
+
result[:types] = types_result[:result].first['values'].to_h { |e| [e[1], e[2]] }
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
yield result
|
|
251
|
+
end
|
|
252
|
+
when :insert
|
|
253
|
+
log_connection_yield(sql, conn, log_args) { conn.exec(sql) }
|
|
254
|
+
conn.exec('select last_insert_rowid()')[:result].first['values'].first.first
|
|
255
|
+
when :update
|
|
256
|
+
log_connection_yield(sql, conn, log_args) { conn.exec(sql) }
|
|
257
|
+
conn.get_rows_modified
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
rescue StandardError => e
|
|
261
|
+
raise_error(e)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# The SQLite adapter does not need the pool to convert exceptions.
|
|
265
|
+
# Also, force the max connections to 1 if a memory database is being
|
|
266
|
+
# used, as otherwise each connection gets a separate database.
|
|
267
|
+
def connection_pool_default_options
|
|
268
|
+
o = super.dup
|
|
269
|
+
# Default to only a single connection if a memory database is used,
|
|
270
|
+
# because otherwise each connection will get a separate database
|
|
271
|
+
o[:max_connections] = 1 if @opts[:database] == ':memory:' || blank_object?(@opts[:database])
|
|
272
|
+
o
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def prepared_statement_argument(arg)
|
|
276
|
+
case arg
|
|
277
|
+
when Date, DateTime, Time
|
|
278
|
+
literal(arg)[1...-1]
|
|
279
|
+
when SQL::Blob
|
|
280
|
+
arg.to_blob
|
|
281
|
+
when true, false
|
|
282
|
+
if integer_booleans
|
|
283
|
+
arg ? 1 : 0
|
|
284
|
+
else
|
|
285
|
+
literal(arg)[1...-1]
|
|
286
|
+
end
|
|
287
|
+
else
|
|
288
|
+
arg
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Execute a prepared statement on the database using the given name.
|
|
293
|
+
def execute_prepared_statement(conn, type, name, opts, &block)
|
|
294
|
+
ps = prepared_statement(name)
|
|
295
|
+
sql = ps.prepared_sql
|
|
296
|
+
args = opts[:arguments]
|
|
297
|
+
ps_args = {}
|
|
298
|
+
args.each { |k, v| ps_args[k] = prepared_statement_argument(v) }
|
|
299
|
+
if cpsa = conn.prepared_statements[name]
|
|
300
|
+
cps, cps_sql = cpsa
|
|
301
|
+
if cps_sql != sql
|
|
302
|
+
cps.close
|
|
303
|
+
cps = nil
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
unless cps
|
|
307
|
+
cps = log_connection_yield("PREPARE #{name}: #{sql}", conn) { conn.prepare(sql) }
|
|
308
|
+
conn.prepared_statements[name] = [cps, sql]
|
|
309
|
+
end
|
|
310
|
+
log_sql = String.new
|
|
311
|
+
log_sql << "EXECUTE #{name}"
|
|
312
|
+
if ps.log_sql
|
|
313
|
+
log_sql << ' ('
|
|
314
|
+
log_sql << sql
|
|
315
|
+
log_sql << ')'
|
|
316
|
+
end
|
|
317
|
+
if block
|
|
318
|
+
log_connection_yield(log_sql, conn, args) { cps.execute(ps_args, &block) }
|
|
319
|
+
else
|
|
320
|
+
log_connection_yield(log_sql, conn, args) { cps.execute!(ps_args) { |r| } }
|
|
321
|
+
case type
|
|
322
|
+
when :insert
|
|
323
|
+
conn.last_insert_row_id
|
|
324
|
+
when :update
|
|
325
|
+
conn.changes
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# SQLite3 raises ArgumentError in addition to SQLite3::Exception in
|
|
331
|
+
# some cases, such as operations on a closed database.
|
|
332
|
+
def database_error_classes
|
|
333
|
+
[::Sequel::Sqljs::Wrapper::Error, ArgumentError]
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def dataset_class_default
|
|
337
|
+
Dataset
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Support SQLite exception codes if ruby-sqlite3 supports them.
|
|
341
|
+
def sqlite_error_code(exception)
|
|
342
|
+
exception.code if exception.respond_to?(:code)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def connection_execute_method
|
|
346
|
+
:exec
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
class Dataset < Sequel::Dataset
|
|
351
|
+
include ::Sequel::Sqljs::DatasetMethods
|
|
352
|
+
|
|
353
|
+
module ArgumentMapper
|
|
354
|
+
include Sequel::Dataset::ArgumentMapper
|
|
355
|
+
|
|
356
|
+
protected
|
|
357
|
+
|
|
358
|
+
# Return a hash with the same values as the given hash,
|
|
359
|
+
# but with the keys converted to strings.
|
|
360
|
+
def map_to_prepared_args(hash)
|
|
361
|
+
args = {}
|
|
362
|
+
hash.each { |k, v| args[k.to_s.gsub('.', '__')] = v }
|
|
363
|
+
args
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
private
|
|
367
|
+
|
|
368
|
+
# SQLite uses a : before the name of the argument for named
|
|
369
|
+
# arguments.
|
|
370
|
+
def prepared_arg(k)
|
|
371
|
+
LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}")
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
|
|
376
|
+
PreparedStatementMethods = prepared_statements_module(:prepare, BindArgumentMethods)
|
|
377
|
+
|
|
378
|
+
# Support regexp functions if using :setup_regexp_function Database option.
|
|
379
|
+
def complex_expression_sql_append(sql, op, args)
|
|
380
|
+
case op
|
|
381
|
+
when :~, :!~, :'~*', :'!~*'
|
|
382
|
+
return super unless supports_regexp?
|
|
383
|
+
|
|
384
|
+
case_insensitive = %i[~* !~*].include?(op)
|
|
385
|
+
sql << 'NOT ' if %i[!~ !~*].include?(op)
|
|
386
|
+
sql << '('
|
|
387
|
+
sql << 'LOWER(' if case_insensitive
|
|
388
|
+
literal_append(sql, args[0])
|
|
389
|
+
sql << ')' if case_insensitive
|
|
390
|
+
sql << ' REGEXP '
|
|
391
|
+
sql << 'LOWER(' if case_insensitive
|
|
392
|
+
literal_append(sql, args[1])
|
|
393
|
+
sql << ')' if case_insensitive
|
|
394
|
+
sql << ')'
|
|
395
|
+
else
|
|
396
|
+
super
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def fetch_rows(sql, &)
|
|
401
|
+
execute(sql) do |result|
|
|
402
|
+
columns = result[:columns].map(&:to_sym)
|
|
403
|
+
type_procs =
|
|
404
|
+
if types = result[:types]
|
|
405
|
+
cps = db.conversion_procs
|
|
406
|
+
columns.map { |n| types[n.to_s] }.map { |t| cps[base_type_name(t)] }
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
result[:result].each do |r|
|
|
410
|
+
r['values'].map do |values|
|
|
411
|
+
values.map.with_index do |v, i|
|
|
412
|
+
value =
|
|
413
|
+
if type_procs && type_proc = type_procs[i]
|
|
414
|
+
type_proc.call(v)
|
|
415
|
+
else
|
|
416
|
+
v
|
|
417
|
+
end
|
|
418
|
+
[columns[i].to_sym, value]
|
|
419
|
+
end.to_h
|
|
420
|
+
end.each(&)
|
|
421
|
+
end
|
|
422
|
+
self.columns = columns
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Support regexp if using :setup_regexp_function Database option.
|
|
427
|
+
def supports_regexp?
|
|
428
|
+
db.allow_regexp?
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
private
|
|
432
|
+
|
|
433
|
+
# The base type name for a given type, without any parenthetical part.
|
|
434
|
+
def base_type_name(t)
|
|
435
|
+
(t =~ /^(.*?)\(/ ? Regexp.last_match(1) : t).downcase if t
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# Quote the string using the adapter class method.
|
|
439
|
+
def literal_string_append(sql, v)
|
|
440
|
+
# sql << "'" << ::SQLite3::Database.quote(v) << "'"
|
|
441
|
+
sql << "'" << v.gsub(/'/, "''") << "'"
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def bound_variable_modules
|
|
445
|
+
[BindArgumentMethods]
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def prepared_statement_modules
|
|
449
|
+
[PreparedStatementMethods]
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# SQLite uses a : before the name of the argument as a placeholder.
|
|
453
|
+
def prepared_arg_placeholder
|
|
454
|
+
':'
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: bormashino-sequel-sqljs-adapter
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Kenichiro Yasuda
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2022-10-16 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bormashino
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.1.9
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.1.9
|
|
27
|
+
description: " SQL.JS adapter for Sequel on browser with Bormaŝino / ruby.wasm\n"
|
|
28
|
+
email:
|
|
29
|
+
- keyasuda@users.noreply.github.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- LICENSE.txt
|
|
35
|
+
- README.md
|
|
36
|
+
- lib/bormashino_sequel_sqljs_adapter.rb
|
|
37
|
+
- lib/bormashino_sequel_sqljs_adapter/shared/sqljs.rb
|
|
38
|
+
- lib/bormashino_sequel_sqljs_adapter/sqljs.rb
|
|
39
|
+
- lib/bormashino_sequel_sqljs_adapter/version.rb
|
|
40
|
+
homepage: https://github.com/keyasuda/bormashino-sequel-sqljs-adapter
|
|
41
|
+
licenses:
|
|
42
|
+
- MIT
|
|
43
|
+
metadata:
|
|
44
|
+
homepage_uri: https://github.com/keyasuda/bormashino-sequel-sqljs-adapter
|
|
45
|
+
source_code_uri: https://github.com/keyasuda/bormashino-sequel-sqljs-adapter
|
|
46
|
+
changelog_uri: https://github.com/keyasuda/bormashino-sequel-sqljs-adapter/blob/main/CHANGELOG.md
|
|
47
|
+
rubygems_mfa_required: 'true'
|
|
48
|
+
post_install_message:
|
|
49
|
+
rdoc_options: []
|
|
50
|
+
require_paths:
|
|
51
|
+
- lib
|
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: 3.2.0.pre.preview1
|
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
requirements: []
|
|
63
|
+
rubygems_version: 3.4.0.dev
|
|
64
|
+
signing_key:
|
|
65
|
+
specification_version: 4
|
|
66
|
+
summary: SQL.JS adapter for Sequel
|
|
67
|
+
test_files: []
|