tina4ruby 3.13.31 → 3.13.32

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: 6140bb67a60387c462e70052c88bbde69ba3b669c0371380df401bd8f9a0b039
4
- data.tar.gz: 7e0043ae026ed273a444f6185f289d0099458fc9ce605bfe4380c1fda9b6f672
3
+ metadata.gz: 02b84dbb5f8e4d009306495921ab7849a28fd769d81d13019b311523d970a0e5
4
+ data.tar.gz: e1b298c7bd03444def8787941f87a7e6fd0adcb6be23efe810990c6005d27910
5
5
  SHA512:
6
- metadata.gz: 83b74a98d73ebcfdd0f22a9335b09382d5e518d51f3443b5e29dafdddb892374e41c8b9cbbc0b4a3d354e3bd405996a376c2e93df1f996778a73d349561a2879
7
- data.tar.gz: 4eadb6a043ec78b5058465b73e4f10f2bf8d91ed19f5e22e5a5a00e2f4b57de8f59fe321b89b5e1e6417dc2dd15ff9142bb36635eed1275f85b59148149498fd
6
+ metadata.gz: 8bf774f6208956221c87bb2f546a04aab950a3cae5962dae94bbd430ee55463481886ba315a21f2680b017828d99df0c6e7a54fff331cff0e559b176da45c5a9
7
+ data.tar.gz: 5242a9295d6e70763ca68a377f2aa3dfd2ac8b37402b8660f439ea6bc6e0c5ddb1f278d277c2ff4ab8bf62b6d80f0ad316c8c07c099f07ecdee9040bea0ddfe5
@@ -366,11 +366,20 @@ module Tina4
366
366
  # v3.13.12: default `limit` is **nil** (no truncation) — the method
367
367
  # name says fetch_all, so it returns all matching rows. Pre-v3.13.12
368
368
  # silently truncated to 100. Pass an explicit `limit:` to cap.
369
- def fetch_all(sql, params = [], limit: nil, offset: nil)
370
- fetch(sql, params, limit: limit, offset: offset).records
369
+ #
370
+ # Pass `no_cache: true` to bypass the query cache for this call (see #fetch).
371
+ def fetch_all(sql, params = [], limit: nil, offset: nil, no_cache: false)
372
+ fetch(sql, params, limit: limit, offset: offset, no_cache: no_cache).records
371
373
  end
372
374
 
373
- def fetch(sql, params = [], limit: 100, offset: nil)
375
+ # Fetch rows with pagination, returning a DatabaseResult.
376
+ #
377
+ # Pass `no_cache: true` to bypass the query cache entirely for this single
378
+ # call — no lookup, no store — and run the query directly against the
379
+ # driver. Works for both the request-scoped auto-cache and the persistent
380
+ # DB cache. The default `false` preserves the cached behaviour. Parity with
381
+ # Python db.fetch(no_cache=) / PHP / Node.
382
+ def fetch(sql, params = [], limit: 100, offset: nil, no_cache: false)
374
383
  offset ||= 0
375
384
  drv = current_driver
376
385
 
@@ -388,7 +397,7 @@ module Tina4
388
397
  effective_sql = drv.apply_limit(effective_sql, limit, offset)
389
398
  end
390
399
 
391
- if @cache_enabled
400
+ if @cache_enabled && !no_cache
392
401
  key = cache_key(effective_sql, params)
393
402
  cached = cache_get(key)
394
403
  if cached
@@ -406,9 +415,15 @@ module Tina4
406
415
  Tina4::DatabaseResult.new(rows, sql: effective_sql, db: self)
407
416
  end
408
417
 
409
- def fetch_one(sql, params = [])
418
+ # Fetch a single row (or nil).
419
+ #
420
+ # Pass `no_cache: true` to bypass the query cache entirely for this call —
421
+ # no lookup, no store — running the query directly. The `no_cache` flag is
422
+ # propagated to the inner #fetch so the request-scoped/persistent cache is
423
+ # never populated either. Default `false` preserves cached behaviour.
424
+ def fetch_one(sql, params = [], no_cache: false)
410
425
  sql = Tina4::Database.strip_trailing_semicolons(sql)
411
- if @cache_enabled
426
+ if @cache_enabled && !no_cache
412
427
  key = cache_key(sql + ":ONE", params)
413
428
  cached = cache_get(key)
414
429
  if cached
@@ -422,7 +437,7 @@ module Tina4
422
437
  return value
423
438
  end
424
439
 
425
- result = fetch(sql, params, limit: 1)
440
+ result = fetch(sql, params, limit: 1, no_cache: no_cache)
426
441
  result.first
427
442
  end
428
443
 
@@ -86,6 +86,7 @@ module Tina4
86
86
  if hit
87
87
  if response.respond_to?(:call)
88
88
  new_response = response.call(hit.body, hit.status_code, hit.content_type)
89
+ set_cache_headers(new_response, "HIT", remaining_ttl(hit.expires_at))
89
90
  return [request, new_response]
90
91
  end
91
92
  end
@@ -106,16 +107,17 @@ module Tina4
106
107
  def after_cache(request, response)
107
108
  return [request, response] unless enabled?
108
109
 
109
- method = if request.respond_to?(:[])
110
- request[:_cache_method]
111
- else
112
- request.instance_variable_get(:@_cache_method)
113
- end
114
- url = if request.respond_to?(:[])
115
- request[:_cache_url]
116
- else
117
- request.instance_variable_get(:@_cache_url)
118
- end
110
+ # Read the tags using the SAME mechanism before_cache wrote them with.
111
+ # before_cache keys the write on respond_to?(:[]=), so read the same way:
112
+ # a Tina4::Request responds to #[] (read-only param lookup) but NOT #[]=,
113
+ # so the tags live on instance variables, not the param hash.
114
+ if request.respond_to?(:[]=)
115
+ method = request[:_cache_method]
116
+ url = request[:_cache_url]
117
+ else
118
+ method = request.instance_variable_get(:@_cache_method)
119
+ url = request.instance_variable_get(:@_cache_url)
120
+ end
119
121
  return [request, response] if method.nil? || url.nil?
120
122
 
121
123
  status = if response.respond_to?(:status_code)
@@ -137,6 +139,9 @@ module Tina4
137
139
  end
138
140
 
139
141
  internal_store(method, url, status.to_i, content_type.to_s, body)
142
+ # The handler ran (cache miss) — annotate the response so clients can
143
+ # see this was a fresh response and how long it will be cached.
144
+ set_cache_headers(response, "MISS", @ttl)
140
145
  [request, response]
141
146
  end
142
147
 
@@ -279,6 +284,26 @@ module Tina4
279
284
  "#{method}:#{url}"
280
285
  end
281
286
 
287
+ # Stamp X-Cache / X-Cache-TTL on a response. `state` is "HIT" or "MISS";
288
+ # `ttl` is the remaining (HIT) or configured (MISS) TTL in seconds.
289
+ # Parity with Python/PHP/Node ResponseCache, which set the same two
290
+ # headers (no Cache-Control). No-op for responses that don't carry a
291
+ # mutable headers hash (e.g. test doubles).
292
+ def set_cache_headers(response, state, ttl)
293
+ return unless response.respond_to?(:headers) && response.headers.is_a?(Hash)
294
+
295
+ response.headers["X-Cache"] = state
296
+ response.headers["X-Cache-TTL"] = ttl.to_i.to_s
297
+ end
298
+
299
+ # Remaining whole seconds until `expires_at` (a monotonic-ish Time.now.to_f
300
+ # epoch), floored at 0 so an entry on the cusp of expiry never reports a
301
+ # negative TTL.
302
+ def remaining_ttl(expires_at)
303
+ remaining = (expires_at || 0) - Time.now.to_f
304
+ remaining.positive? ? remaining : 0
305
+ end
306
+
282
307
  # Internal: retrieve a cached response. Used by middleware hooks only.
283
308
  def internal_lookup(method, url)
284
309
  return nil unless enabled?
data/lib/tina4/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tina4
4
- VERSION = "3.13.31"
4
+ VERSION = "3.13.32"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.31
4
+ version: 3.13.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team