Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand All @@ -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.

Expand Down
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 0 additions & 5 deletions lib/strict/attributes/instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 0 additions & 1 deletion sig/strict.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion spec/strict/attributes/generated_methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions spec/strict/strict_public_api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -149,7 +148,6 @@ def state = [@name, @active]
name Integer
end
end,
-> { attributes { as_json Hash } },
-> { attributes { to_h Hash } }
]

Expand Down
10 changes: 2 additions & 8 deletions spec/strict/value_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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

Expand Down