hana

License: MIT Crystal

Crystal port of the Ruby gem [hana]3.

Implementation of [JSON Patch]1 (RFC 6902) and [JSON Pointer]2 (RFC 6901).

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      hana:
        github: cyangle/hana.cr
        version: ~> 0.1.1
  2. Run shards install

Requirements: Crystal >= 1.13.2

Usage

JSON Patch

Apply patches to JSON documents. Supports all RFC 6902 operations: add, remove, replace, move, copy, and test.

require "hana"

doc = JSON.parse(%({
  "name": "John",
  "age": 30,
  "tags": ["developer"]
}))

# Create patch from JSON string
patch = Hana::Patch.new(%([
  { "op": "replace", "path": "/name", "value": "Jane" },
  { "op": "add", "path": "/email", "value": "[email protected]" },
  { "op": "remove", "path": "/age" },
  { "op": "add", "path": "/tags/-", "value": "designer" }
]))

result = patch.apply(doc)
puts result.to_json
# {"name":"Jane","tags":["developer","designer"],"email":"[email protected]"}

Patch Operations

| Operation | Description | Example | |-----------|-------------|---------| | add | Add a value | {"op": "add", "path": "/foo", "value": "bar"} | | remove | Remove a value | {"op": "remove", "path": "/foo"} | | replace | Replace a value | {"op": "replace", "path": "/foo", "value": "baz"} | | move | Move a value | {"op": "move", "from": "/foo", "path": "/bar"} | | copy | Copy a value | {"op": "copy", "from": "/foo", "path": "/bar"} | | test | Test a value matches | {"op": "test", "path": "/foo", "value": "bar"} |

Creating Patches

# From JSON string
patch = Hana::Patch.new(%([{"op": "add", "path": "/foo", "value": "bar"}]))

# From IO
patch = Hana::Patch.new(File.open("patches.json"))

# From Array
ops = [{"op" => JSON::Any.new("add"), "path" => JSON::Any.new("/foo"), "value" => JSON::Any.new("bar")}]
patch = Hana::Patch.new(ops)

JSON Pointer

Evaluate JSON Pointers to extract values from JSON documents.

require "hana"

doc = JSON.parse(%({
  "users": [
    {"name": "Alice", "role": "admin"},
    {"name": "Bob", "role": "user"}
  ],
  "config": {
    "debug": true
  }
}))

# Get nested values
pointer = Hana::Pointer.new("/users/0/name")
puts pointer.eval(doc) # "Alice"

pointer = Hana::Pointer.new("/users/1/role")
puts pointer.eval(doc) # "user"

pointer = Hana::Pointer.new("/config/debug")
puts pointer.eval(doc) # true

Development

hana runs tests from json-patch/json-patch-tests. Fetch the git submodule by running:

git submodule init
git submodule update

Install dependencies with:

shards install

Then run the tests with:

crystal spec

Linting

Format code with Crystal's built-in formatter:

crystal tool format

Run static analysis with Ameba:

bin/ameba

Contributing

  1. Fork it (https://github.com/cyangle/hana.cr/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

License

This project is licensed under the MIT License - see the LICENSE file for details.