activeagent 1.6.3 → 1.6.4

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: 1611f71e0ff6a30b864e797b01cd20242a46acad9df3888c836799f0d297e3d1
4
- data.tar.gz: 5f93c9a8c43a98cf7eb599d9efb37f2915c1d2e5ef6f4138464c33cd1f4d3af1
3
+ metadata.gz: a0332669c4ffee1ccbea3e1645e126acac6b4e957b980000e5d96baedc908638
4
+ data.tar.gz: 1d6062b56a6e7fc0e76b965d308df706c3be563292344cdf68b500046e838784
5
5
  SHA512:
6
- metadata.gz: 7e27caea807dc1d32744260bf157086160d73935fba48aea9c4c59e3234c26a5aee707eb650104a76f2fdf2e59497b0408b6a80392b83ceeab2770b4c8eef3c0
7
- data.tar.gz: e44dc91193d40a6fafa9bc79c3cfc3a8c4f2acc215fc0fae0229424218a8c1bc6ca5b96c5803553bb610f89b86eb519b2f0c6061018117b65a38d77eba61100b
6
+ metadata.gz: c0a940227737d94497c19e8760cf1241ea02f6078987f8cd27f78b0b1a1fb22ac90c8b924dff87e85a4bf8f13c952f6a36eec388dd806f56d230f5b38c9fe59b
7
+ data.tar.gz: df01fdb757be0bcfa549086dfe0082db0ada892fc552600087d07fe9c1299a4aa4d4c897987cbda897832d11df50f19368261413647f63db4e636a4bb5bb98e5
data/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.6.4] - 2026-09-22
11
+
12
+ Releases `activeagent` and `actionagent` 1.6.4 from one tag. A patch on 1.6.3
13
+ carrying one fix to `SchemaTools`, for a filter that answered confidently and
14
+ wrongly instead of failing.
15
+
16
+ ### Fixed
17
+
18
+ - A Rails enum is offered to the model as its names (`{type: "string", enum:
19
+ [...]}`) instead of the integer backing it. `SchemaGenerator` reads enums from
20
+ inclusion validators and never consulted `defined_enums`, so a `status` column
21
+ reached the model as a bare integer with no labels.
22
+ - A filter value outside an enum — alone or inside an IN list — is rejected,
23
+ naming the valid values, instead of matching no rows. `status: "pending"`
24
+ returned `{count: 0}`, which an agent reports as a fact, indistinguishable
25
+ from "none match". Same reasoning as the unknown-operator rejection in
26
+ `range_predicates!`.
27
+ - An enum is no longer offered the range form. Its integer backing is a
28
+ declaration-order artefact, so `status: {gt: 1}` was a meaningless filter that
29
+ still returned a confident count.
30
+
10
31
  ## [1.6.3] - 2026-09-18
11
32
 
12
33
  Releases `activeagent` and `actionagent` 1.6.3 from one tag.
@@ -332,6 +332,8 @@ module ActiveAgent
332
332
  "`#{key}` is not a filterable attribute. Allowed filters: #{filterable.join(", ")}"
333
333
  end
334
334
 
335
+ validate_enum_value!(column, value)
336
+
335
337
  memo[column] = value
336
338
  end
337
339
 
@@ -386,6 +388,40 @@ module ActiveAgent
386
388
  end
387
389
  end
388
390
 
391
+ # A value outside a Rails enum reaches `where` as an unmatched name and
392
+ # returns zero rows — the same silent-nothing that {.range_predicates!}
393
+ # rejects for an unknown operator, and just as bad here: an agent reads
394
+ # "0 tickets under review" as a fact rather than a mistyped filter.
395
+ #
396
+ # The range form is not offered on an enum (see {.range_filterable?}), so
397
+ # a Hash here is a filter that cannot mean anything.
398
+ #
399
+ # @api private
400
+ # @raise [UnpermittedAttribute] on a value the enum does not define
401
+ def validate_enum_value!(column, value)
402
+ values = enum_values_for(column)
403
+ return if values.blank?
404
+
405
+ if value.is_a?(Hash)
406
+ raise UnpermittedAttribute,
407
+ "`#{column}` is an enum and cannot be compared as a range. " \
408
+ "Allowed values: #{values.keys.join(", ")}"
409
+ end
410
+
411
+ # An Array is an IN filter, which `where` silently narrows to its
412
+ # defined members just as it drops a lone undefined one.
413
+ Array(value).each do |member|
414
+ # The schema offers names only, so an integer here came from a
415
+ # model ignoring it. Accepted anyway: it is unambiguous, and a host
416
+ # calling the tool directly in Ruby reasonably passes the backing
417
+ # value.
418
+ next if values.key?(member.to_s) || values.value?(member)
419
+
420
+ raise UnpermittedAttribute,
421
+ "`#{member}` is not a valid `#{column}`. Allowed values: #{values.keys.join(", ")}"
422
+ end
423
+ end
424
+
389
425
  # Projects a record down to the declared return columns.
390
426
  #
391
427
  # The projection happens in SQL (+select+) as well as here, but the Ruby
@@ -455,14 +491,44 @@ module ActiveAgent
455
491
  properties = schema[:schema][:properties]
456
492
 
457
493
  filterable.index_with do |column|
494
+ next enum_property(column) if enum_column?(column)
495
+
458
496
  scalar = (properties[column] || { type: "string" }).deep_dup
459
497
  range_filterable?(column) ? with_range_form(column, scalar) : scalar
460
498
  end
461
499
  end
462
500
 
501
+ # A Rails enum is declared on the model, not through the inclusion
502
+ # validator SchemaGenerator reads, so without this a `status` column
503
+ # reaches the model as a bare integer: no labels, no constraint. The
504
+ # names are the model's own API (`Ticket.open`, `status: "open"`), so
505
+ # they are what the tool offers.
506
+ #
507
+ # @api private
508
+ def enum_property(column)
509
+ { type: "string", enum: enum_values_for(column).keys, description: "#{column.to_s.humanize} field" }
510
+ end
511
+
512
+ # @api private
513
+ def enum_column?(column)
514
+ enum_values_for(column).present?
515
+ end
516
+
517
+ # @api private
518
+ def enum_values_for(column)
519
+ @model.defined_enums[column.to_s] || {}
520
+ end
521
+
463
522
  # Dates, times and numbers are the columns a question like "overdue" or
464
523
  # "more than 10" actually needs a comparison on.
524
+ #
525
+ # An enum is backed by an integer but is not ordered in any sense a
526
+ # question can use: `status > 1` is an artefact of declaration order, so
527
+ # offering the range form on one invites a meaningless filter that still
528
+ # returns a confident count.
465
529
  def range_filterable?(column)
530
+ return false if enum_column?(column)
531
+
466
532
  RANGE_FILTERABLE_TYPES.include?(@model.type_for_attribute(column).type)
467
533
  end
468
534
 
@@ -1,3 +1,3 @@
1
1
  module ActiveAgent
2
- VERSION = "1.6.3"
2
+ VERSION = "1.6.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeagent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.3
4
+ version: 1.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Bowen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-18 00:00:00.000000000 Z
11
+ date: 2026-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack