Skip to content

Conversation

@hakanensari
Copy link
Owner

Summary

Implements #15 by adding a null keyword parameter to both attribute and attribute? methods. When set to false, Structure will raise an ArgumentError if the coerced value is nil.

Usage

User = Structure.new do
  attribute(:id, String, null: false)              # required key, must be non-null
  attribute?(:name, String, null: false)           # optional key, but must be non-null when present
  attribute?(:description, String)                 # optional key, can be null (default)
end

User.parse(id: "123", name: "Alice")               # ✓ valid
User.parse(id: "123", name: nil)                   # ✗ raises ArgumentError: cannot be null: :name
User.parse(id: "123")                              # ✓ valid (name is optional)

Changes

  • Add @non_nullable Set to Builder to track null: false attributes
  • Add null: true parameter to attribute and attribute? methods
  • Add Builder#non_nullable accessor method
  • Update Structure metadata to include non_nullable
  • Add validation in parse method after coercion
  • Update RBS type signatures
  • Add comprehensive test suite (18 tests)
  • Add User fixture for RBS smoke testing

Design Decisions

  • Default null: true: Backward compatible, most attributes allow nil
  • Post-coercion validation: Catches nil from payload, defaults, or coercion results
  • Works with optional attributes: attribute?(:name, String, null: false) raises only if key exists but value is nil
  • GraphQL semantics: Matches graphql-ruby vocabulary perfectly
  • Error message: "cannot be null: :attr" - lowercase, consistent with parse-time errors

Test Coverage

  • Required + null: false (missing key vs nil value)
  • Optional + null: false (missing allowed, nil not allowed)
  • Default values that are nil
  • Coercion results that are nil
  • Block transformations that return nil
  • Mixed nullable and non-nullable attributes
  • Nested structures and arrays

Closes #15

🤖 Generated with Claude Code

hakanensari and others added 2 commits October 16, 2025 21:07
Implements issue #15 by adding a `null` keyword parameter to both
`attribute` and `attribute?` methods. When set to false, Structure
will raise an ArgumentError if the coerced value is nil.

- attribute(:id, String, null: false) enforces non-null on required keys
- attribute?(:name, String, null: false) enforces non-null on optional
  keys when present (missing keys are allowed)

This matches GraphQL's non-null type modifier semantics and makes
Structure ideal for parsing GraphQL responses.

Changes:
- Add @non_nullable Set to Builder to track null: false attributes
- Add null: true parameter to attribute and attribute? methods
- Add Builder#non_nullable accessor method
- Update Structure metadata to include non_nullable
- Add validation in parse method after coercion
- Add comprehensive test suite (18 tests)
- Update RBS type signatures

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Adds a User fixture demonstrating the null: false feature:
- id: required + non-null
- name: optional + non-null when present
- bio: optional + nullable

This fixture serves as both a test and documentation of the feature,
and ensures Steep type checking works correctly with the new option.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@hakanensari hakanensari merged commit d283f65 into main Oct 16, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add null: false attribute to mirror GraphQL’s non-null modifier

1 participant