activerecord-beagle-turso 0.1.3 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b4bdeec9d75fe6297e7f42aba4ab966b31407731a5992de2af9a214483c53c7
4
- data.tar.gz: bc8dcf3930ec2eb8104712b5f910b38118cd75ae0840d0d98bcb9a145d958f21
3
+ metadata.gz: 96e5e44a6be08be7733d3f7ea9590e6577826c43d8ecffe178f6bb6314d3396c
4
+ data.tar.gz: 66627c1ff6245cb9f44180c6152020248d49f58722359c7070ce883a9cbe4ed2
5
5
  SHA512:
6
- metadata.gz: 7b31dbac6656267e980d1fc85b7c8ce805ea538564750b9f537b957ba1d6443cddee350d375730a64a83c8444b35fe76df90952fd76f11661494f07ae0ea5683
7
- data.tar.gz: 0e77f74a3dca74225fd169e4556b6b948ae42c2702b555e19c3e034cf1301da3267428b04198eefad73da422037de6574d61ee46ca77d06541a026eccb5ff60e
6
+ metadata.gz: cc94306cead989dba2cf18789880c63cc4eb27b973812bec9f807fbbdf40b81baef90edcc512d099a90c2955427ed56582c51c0dd1d7fb6fc1439e973dfbbd5f
7
+ data.tar.gz: d15c112f354343e61bda83c8f146603ae0aa1c6da19381e158eb01ce84ee487f82e67e775d416328196d5c2cf7a4f25872954219a63f75181535318681d806a3
data/README.md CHANGED
@@ -94,7 +94,7 @@ All three default their `connection` argument to `ActiveRecord::Base.connection`
94
94
  so `Beagle::Turso::SyncManager.sync!` alone is the usual call. Calling any of
95
95
  them against a **local-only** connection (no `remote_url:`/`auth_token:` in
96
96
  `database.yml`) raises `Beagle::Turso::SyncManager::NotSyncedError` — a
97
- distinct, rescuable type — rather than the driver's bare `RuntimeError`.
97
+ distinct, rescuable type — rather than the driver's own `Beagle::Turso::NotSyncedError`.
98
98
 
99
99
  To sync on a timer, wire
100
100
  `ActiveRecord::ConnectionAdapters::BeagleTurso::SyncJob` (a thin delegator to
@@ -46,23 +46,14 @@ module Beagle
46
46
  module SyncManager
47
47
  # Raised by +push!+/+pull!+/+sync!+ when the given connection is
48
48
  # local-only (its database.yml entry has no +remote_url+/+auth_token+,
49
- # so BeagleTursoAdapter opened it via +Database.open_local+). The
50
- # underlying Client#push/#pull already raise a bare RuntimeError for
51
- # this (see Beagle::Turso::Database#push/#pull); SyncManager re-raises
52
- # it as this distinct, documented type so callers can rescue it
53
- # specifically instead of matching on the driver's message text. A
49
+ # so BeagleTursoAdapter opened it via +Database.open_local+). The driver
50
+ # raises Beagle::Turso::NotSyncedError for this; SyncManager re-raises it
51
+ # as this type (kept for compatibility) with a database.yml hint. A
54
52
  # genuine sync failure against a real remote (network error, auth
55
- # failure, etc.) is NOT reclassified -- it propagates as whatever error
56
- # the driver raised, unchanged.
53
+ # failure, etc.) is NOT reclassified -- it propagates as
54
+ # Beagle::Turso::SyncError (or another Beagle::Turso::Error), unchanged.
57
55
  class NotSyncedError < StandardError; end
58
56
 
59
- # Matches only the driver's local-only wording (see
60
- # beagle_turso_core's `Error::Sync` in `Database::push`/`Database::pull`)
61
- # so a real sync failure -- which also surfaces as a RuntimeError, just
62
- # with different text -- is never misreported as "not synced".
63
- LOCAL_ONLY_MESSAGE_REGEX = /on a local-only database/i
64
- private_constant :LOCAL_ONLY_MESSAGE_REGEX
65
-
66
57
  module_function
67
58
 
68
59
  # Push this connection's local writes to its remote Turso database.
@@ -88,9 +79,7 @@ module Beagle
88
79
 
89
80
  def reraise_local_only
90
81
  yield
91
- rescue RuntimeError => e
92
- raise e unless LOCAL_ONLY_MESSAGE_REGEX.match?(e.message)
93
-
82
+ rescue Beagle::Turso::NotSyncedError => e
94
83
  raise NotSyncedError,
95
84
  "#{e.message} -- this connection is local-only; configure remote_url: " \
96
85
  "and auth_token: in database.yml to enable sync"
@@ -223,6 +223,65 @@ module ActiveRecord
223
223
  "#{super} AND name NOT LIKE '\\_\\_turso\\_internal\\_%' ESCAPE '\\'"
224
224
  end
225
225
 
226
+ # A column definition in the DDL that carries a parenthesised type
227
+ # argument: `"price" decimal (10, 2) NOT NULL`, `a varchar (50)`. The
228
+ # name may be bare or quoted with ", ` or []; the type may be several
229
+ # words ("double precision"); the argument is one or two integers.
230
+ DECLARED_TYPE_REGEX = /
231
+ \A\s*
232
+ (?:"(?<name>[^"]*)" | `(?<name>[^`]*)` | \[(?<name>[^\]]*)\] | (?<name>\w+))
233
+ \s+
234
+ (?<type>[A-Za-z_][A-Za-z0-9_\ ]*?)
235
+ \s*\(\s*(?<args>\d+\s*(?:,\s*\d+)?)\s*\)
236
+ /x
237
+ private_constant :DECLARED_TYPE_REGEX
238
+
239
+ # Restore type arguments that beagle_turso's `PRAGMA table_info` drops.
240
+ #
241
+ # turso reports a column's declared type as the bare type name: a
242
+ # `decimal(10,2)` column comes back as `decimal`, `varchar(50)` as
243
+ # `varchar`, `datetime(6)` as `datetime`. Rails derives precision,
244
+ # scale and limit by parsing exactly that string, so all three silently
245
+ # become nil. Real SQLite returns the full declared type here, and the
246
+ # information is not lost on disk -- the CREATE TABLE in sqlite_master
247
+ # still says `decimal (10, 2)` -- so recover it from there.
248
+ #
249
+ # Left alone this is worse than cosmetic schema.rb churn: a dump emits
250
+ # `t.decimal "price"` with no precision/scale, and loading that dump
251
+ # builds a column whose values are no longer rounded to 2 decimal
252
+ # places. `super` already splits the DDL into per-column strings to
253
+ # recover COLLATE, AUTOINCREMENT and GENERATED ALWAYS AS for the same
254
+ # reason; this is the same trick for the type itself.
255
+ #
256
+ # Only fills in a missing argument -- a type that already arrived with
257
+ # one is left untouched, so this becomes an automatic no-op if turso
258
+ # starts reporting declared types in full.
259
+ def table_structure_with_collation(table_name, basic_structure)
260
+ columns = super
261
+ return columns unless columns.any? { |column| !column["type"].to_s.include?("(") }
262
+
263
+ declared = declared_column_types(table_name, columns.filter_map { |c| c["name"] })
264
+ return columns if declared.empty?
265
+
266
+ columns.each do |column|
267
+ next if column["type"].to_s.include?("(")
268
+
269
+ full_type = declared[column["name"]]
270
+ column["type"] = full_type if full_type
271
+ end
272
+ end
273
+
274
+ # Maps column name -> declared type with its argument normalised back
275
+ # to Rails' own spelling (`decimal (10, 2)` -> `decimal(10,2)`), which
276
+ # is what Rails' precision/scale/limit extraction expects to see.
277
+ def declared_column_types(table_name, column_names)
278
+ table_structure_sql(table_name, column_names).each_with_object({}) do |column_string, declared|
279
+ next unless (match = DECLARED_TYPE_REGEX.match(column_string))
280
+
281
+ declared[match[:name]] = "#{match[:type]}(#{match[:args].gsub(/\s+/, '')})"
282
+ end
283
+ end
284
+
226
285
  # The single seam. AbstractAdapter#raw_execute funnels every statement
227
286
  # here and expects an ActiveRecord::Result back, with the notification
228
287
  # payload's affected_rows/row_count filled in. cast_result (inherited)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module BeagleTurso
5
- VERSION = "0.1.3"
5
+ VERSION = "0.1.5"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-beagle-turso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - BeagleSoftwareUK
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-09 00:00:00.000000000 Z
11
+ date: 2026-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.1'
33
+ version: '0.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.1'
40
+ version: '0.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: sqlite3
43
43
  requirement: !ruby/object:Gem::Requirement