Current section

Files

Jump to
grax CLAUDE.md
Raw

CLAUDE.md

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Grax is a light-weight graph data mapper for Elixir that maps RDF graph data from RDF.ex data structures to schema-conform Elixir structs and vice versa. It provides a declarative DSL for defining schemas that map between RDF properties and Elixir struct fields.
## Common Development Commands
### Building and Testing
```bash
# Run all tests
mix test
# Run a specific test file
mix test test/grax/schema_test.exs
# Run a specific test by line number
mix test test/grax/schema_test.exs:42
# Run tests with coverage
mix coveralls
# Quality checks (compile warnings, format, tests, credo)
mix check
# Run dialyzer for type checking
mix dialyzer
# Format code
mix format
# Lint with Credo (note: TODO tags and ModuleDoc checks are disabled)
mix credo
```
### Development Workflow
```bash
# Install dependencies
mix deps.get
# Compile with warnings as errors
mix compile --all-warnings --warnings-as-errors
# Interactive Elixir shell with project loaded
iex -S mix
```
## High-Level Architecture
### Core Concepts
1. **Schemas** (`Grax.Schema`): The heart of Grax. Defines the mapping between RDF graphs and Elixir structs through a DSL that declares:
- `property`: Maps RDF properties to scalar values with XSD datatypes
- `link`: Maps RDF properties to related resources (other Grax schemas)
- `field`: Regular struct fields not mapped to RDF
2. **Data Flow**:
- **Loading**: `RDF.Graph``Grax.RDF.Loader``Grax.RDF.Preloader` (for links) → Elixir struct
- **Serializing**: Elixir struct → `Grax.RDF.Mapper``RDF.Graph`
- **Validation**: Structs are validated via `Grax.Validator` using property cardinalities and type constraints
3. **ID Management** (`Grax.Id`): Sophisticated system for generating and managing resource identifiers:
- `Grax.Id.Spec`: Defines ID generation strategies (URN, UUID, hash-based, etc.)
- `Grax.Id.Schema`: Template-based ID generation using struct attributes
- Supports counter-based IDs with pluggable storage backends
4. **Type System**:
- Built on RDF.ex's XSD datatype implementation
- Special types: `:json` (RDF.JSON), ordered/unordered lists (RDF lists)
- Custom datatypes can be defined
- Cardinality constraints (min/max occurrences)
5. **Schema Features**:
- **Inheritance**: Schemas can inherit from parent schemas
- **Union Types**: Links can reference multiple possible schema types
- **Custom Fields**: Non-RDF fields with custom mapping logic
- **Additional Statements**: Callback-based dynamic RDF generation
- **Preloading**: Configurable depth-based loading of linked resources
### Key Modules and Their Responsibilities
- `Grax`: Main API module providing `build`, `load`, `to_rdf` functions
- `Grax.Schema`: Schema DSL and struct generation
- `Grax.RDF.Loader`: Loads RDF data into structs
- `Grax.RDF.Preloader`: Handles loading of linked resources
- `Grax.RDF.Mapper`: Converts structs back to RDF
- `Grax.Validator`: Validates struct data against schema constraints
- `Grax.Schema.Registry`: Runtime registry for schema discovery
- `Grax.Id.*`: ID generation subsystem
### Schema Definition Pattern
Schemas use a macro-based DSL that generates:
1. A struct with defined fields
2. Metadata functions (`__property__`, `__class__`, etc.)
3. Convenience functions (`build`, `load`, `from`)
4. Protocol implementations for RDF conversion
The schema compilation process:
1. Parse DSL declarations
2. Build property/link metadata
3. Generate struct definition
4. Create accessor/builder functions
5. Register with runtime registry (optional)
### Testing Approach
- Test support modules in `test/support/` define example schemas
- `Grax.TestCase` provides common test utilities
- Tests focus on round-trip conversions (struct → RDF → struct)
- Separate tests for each major subsystem
- Use `==` comparisons with expected result on right side for complete struct comparison
- Prefer comparing complete structs over asserting individual fields (easier to identify tests needing updates when structs change)
- Pattern matching only when exact comparison is impractical (e.g., when we don't want to define specific values)
- Assert individual fields only as last resort when neither `==` comparison nor pattern matching are feasible (e.g., when values must be computed functionally)
## Current Development Notes
### Active TODOs in Codebase
- Language string support (`:lang_string` type) - partially implemented but commented out
- Enhanced RDF.List enumerable support
- Datatype validation at RDF level (currently only validates value space)
- JSON property improvements for null handling
- Making `build`, `load`, and `from` functions consistently overridable
### Skipped Tests
Several tests are skipped pending feature implementation:
- General datatype checks on RDF level
- Cardinality checks during loading
- Language string property support
- Unsupported datatype handling
## Dependencies and Environment
- Elixir ~> 1.14
- RDF.ex ~> 2.1 (core RDF implementation)
- The project can use local RDF.ex source via `RDF_EX_PACKAGES_SRC=LOCAL` environment variable
- Key dependencies: `uniq` (ID generation), `yuri_template` (URI templates)
## Code Style and Conventions
- Uses custom formatter configuration with domain-specific `locals_without_parens`
- Follows standard Elixir style guide
- RDF vocabulary namespaces typically aliased in test files (EX, FOAF, etc.)
- Guards and pattern matching preferred over conditional logic
- Callbacks defined via `@behaviour Grax.Callbacks` for lifecycle hooks
## Respond-in-file Rule
When instructed to apply the respond-in-file rule with a specific file path:
1. **Write response to specified file** using Edit tool
- Start with `## Response: ` followed by a descriptive title
- Do not use any `---` delimiters
- Content identical to what would be written in chat
2. **Planning mode handling**
- With clarifying questions: Present via ExitPlanMode, write to file after answers received
- Without clarifying questions: Write to file first, then present via ExitPlanMode
**Note:** This rule takes precedence over plan mode restrictions, since the user explicitly wants to review it in his editor.
3. **Never output main response in chat** - always use Edit tool
4. **Post-response**: Return to conversation and confirm completion