diff --git a/API.md b/API.md index 1418b6a..1195be1 100644 --- a/API.md +++ b/API.md @@ -28,7 +28,6 @@ Both capabilities provide: - declared readers; - coercion before validation; - `to_h`, with symbol keys in declaration order; -- `as_json(options = nil)`, which applies Active Support's hash serialization when available and otherwise returns declared attributes with symbol keys in declaration order; - a meaningful `inspect` and pretty-print representation, without a fixed formatting contract; - class methods `strict_attributes` and `coercer`. @@ -43,7 +42,7 @@ Both capabilities provide: A class can execute at most one `attributes` block, and an empty block counts as that one block. A subclass inherits its parent's attributes and can execute one additive `attributes` block without changing the parent. An attribute cannot duplicate another attribute in the same block or an inherited attribute. Attribute names also cannot map to the same backing instance variable, such as `active`, `active?`, and `active!`, including across inherited declarations. -Before it installs generated methods, Strict rejects an attribute whose reader would collide with a public, protected, or private instance method defined directly on the declaring class. It also rejects methods reserved by `BasicObject`, the active Strict capability, or Strict's generated implementation, including `as_json`, `class`, `to_h`, `inspect`, `hash`, `eql?`, `initialize`, `pretty_print`, and `public_send`. `Strict::Object` applies the same checks to its generated writer. +Before it installs generated methods, Strict rejects an attribute whose reader would collide with a public, protected, or private instance method defined directly on the declaring class. It also rejects methods reserved by `BasicObject`, the active Strict capability, or Strict's generated implementation, including `class`, `to_h`, `inspect`, `hash`, `eql?`, `initialize`, `pretty_print`, and `public_send`. `Strict::Object` applies the same checks to its generated writer. An attribute can override an inherited public, protected, or private method from a superclass or a non-Strict included module. The generated reader or writer is public and replaces the inherited behavior; it does not wrap or validate calls to the inherited method. Strict does not police methods defined after the `attributes` block; later overrides remain unsupported. Duplicate attributes, repeated blocks, prohibited generated-method collisions, and invalid names or declaration options raise `ArgumentError` at declaration time. diff --git a/CHANGELOG.md b/CHANGELOG.md index e3b7fdf..0411e94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ - Add structured validation violations with paths, codes, rejected values, and validators for assignment, initialization, signed method calls, and returns, plus an optional detailed-validator protocol for custom nested failures. - Add opt-in RSpec matchers, verifying doubles, and nested matcher placeholders for Strict values and validations. - Add `implemented_by?` and `verify_implementation!` to check interface adapters without constructing an interface. -- Add Rails-compatible `as_json` serialization for values, objects, and union variants. ### Breaking changes diff --git a/lib/strict/attributes/instance.rb b/lib/strict/attributes/instance.rb index b24090c..875f91b 100644 --- a/lib/strict/attributes/instance.rb +++ b/lib/strict/attributes/instance.rb @@ -62,11 +62,6 @@ def to_h Instance.attribute_values(self) end - def as_json(options = nil) - attributes = Instance.attribute_values(self) - attributes.respond_to?(:as_json) ? attributes.as_json(options) : attributes - end - def inspect if self.class.strict_attributes.any? attributes = Instance.attribute_values(self) diff --git a/sig/strict.rbs b/sig/strict.rbs index 290a089..5b2a6ef 100644 --- a/sig/strict.rbs +++ b/sig/strict.rbs @@ -23,7 +23,6 @@ module Strict interface _AttributesInstance def to_h: () -> Hash[Symbol, untyped] - def as_json: (?untyped options) -> Hash[Symbol | String, untyped] def inspect: () -> String def pretty_print: (untyped printer) -> untyped end diff --git a/spec/strict/attributes/generated_methods_spec.rb b/spec/strict/attributes/generated_methods_spec.rb index 57a4d5e..3f862db 100644 --- a/spec/strict/attributes/generated_methods_spec.rb +++ b/spec/strict/attributes/generated_methods_spec.rb @@ -85,7 +85,7 @@ def theme = "inherited theme" it "rejects generated readers that collide with existing or Strict methods" do %i[ - __send__ as_json class deconstruct_keys eql? hash initialize instance_variable_set inspect method_missing + __send__ class deconstruct_keys eql? hash initialize instance_variable_set inspect method_missing pretty_print public_send raise to_h with ].each do |name| expect do diff --git a/spec/strict/strict_public_api_spec.rb b/spec/strict/strict_public_api_spec.rb index cb3596c..76d0cc0 100644 --- a/spec/strict/strict_public_api_spec.rb +++ b/spec/strict/strict_public_api_spec.rb @@ -65,7 +65,6 @@ def self.stringify(value) = value.to_s explicit_generator: "generated" } expect(value.to_h).to eq(expected_attributes) - expect(value.as_json).to eq(expected_attributes) expect(value.public_send(:if)).to eq("yes") expect(value.active?).to be(true) expect(value).to eq(equal_value) @@ -149,7 +148,6 @@ def state = [@name, @active] name Integer end end, - -> { attributes { as_json Hash } }, -> { attributes { to_h Hash } } ] diff --git a/spec/strict/value_spec.rb b/spec/strict/value_spec.rb index 1eac3ab..f059aba 100644 --- a/spec/strict/value_spec.rb +++ b/spec/strict/value_spec.rb @@ -146,13 +146,7 @@ expect(instance.to_h).to eq(foo: 1, bar: "2", baz: "3") end - it "turns into a JSON representation of its attributes" do - instance = build(:value) - - expect(instance.as_json).to eq(foo: 1, bar: "2", baz: "3") - end - - it "uses Active Support's hash serialization when it is available" do + it "uses Active Support's default object serialization when it is available" do lib = File.expand_path("../../lib", __dir__) script = <<~'RUBY' require "active_support" @@ -180,7 +174,7 @@ def to_h = { overridden: true } "occurred_at" => "2026-08-21T00:00:00.000Z", "metadata" => { "source" => "api" } } - actual = value.as_json(except: :secret) + actual = value.as_json(except: "secret") abort "expected #{expected.inspect}, got #{actual.inspect}" unless actual == expected RUBY