json_schematize

Take standardized API results and turn them into a Schema


Keywords
cache-layer, json, ruby, schema
License
MIT
Install
gem install json_schematize -v 0.8.0

Documentation

JsonSchematize

JsonSchematize is emant to be a simple schema control version used to aprse data returned from any API.

It can handle nested Schematized versions and build the tree all the way down

Installation

Add this line to your application's Gemfile:

gem 'json_schematize'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install json_schematize

Usage

Given hash value of the following:

{
  "status":"complete",
  "id":"127392",
  "body":
    [
      {
        "status":"failed",
        "id":"12345",
        "field_i_dont_care_about": false
      },
      {
        "status":"failed",
        "id":"6347",
        "field_i_dont_care_about": true
      }
    ]
}
# lib/schema/internal_body
require 'json_schematize/generator'

class InternalBody < JsonSchematize::Generator
  ALLOWED_STATUSES = [:failed, :completed, :success]

  add_field name: :id, type: Integer
  add_field name: :status, type: Symbol, validator: ->(transformed_value, raw_value) { ALLOWED_STATUSES.include?(transformed_value) }
end

###
# lib/schema/my_first_schema
require 'json_schematize/generator'
require 'internal_body' #dependeing on load order

class MyFirstSchema < JsonSchematize::Generator
  add_field name: :internals, type: InternalBody, array_of_types: true, dig: ["body"]
  add_field name: :id, type: Integer
  add_field name: :status, type: Symbol
end

schema = MyFirstSchema.new(**json_hash)
schema.internals.count #=> 2
schema.internals.first.status #=> :failed
schema.id #=> 127392
schema.id = 999999  #assignments are still subject to validation logic for each field
schema.id #=> 999999

Field options:

name -- Name of the field. Field name can be accessed from the instance
type -- Class of the expected field type
types -- To be used when you want the field to have multiple types. Useful for similar classes like DateTime, Date, Time (converter must be supplied when multiple types are given)
dig_type -- Methodolgy of how to dig into the given param for the field. All values of the `dig` array will be converted accordingly. Default is `none` and will attempt to use what is given. [:symbol, :string, :none].
dig -- Array telling JsonSchematize how to dig into the provided hash
validator -- Proc value to validate the data found in the params. Proc given (transformed_value, original_value) when calling in
required -- Default is true. When not set, each instance class can optionally decide if they want to raise when an this is set to false.
converter -- Proc return is set to the field value. No furter validation is done. Given (value) as a parameter
array_of_types -- Detailed example above. Set this value to true when the dig param is to an array and you want all values in array to be parsed the given type
empty_value -- When required is false, this value is used to fill the field. By default it is JsonSchematize::EmptyValue, but can be changed to anything

Schema defaults

Defaults can be added for all fields for any of the available options. This can be useful for returned API calls when the body is parsed as a Hash with String keys.

class SchemaWithDefaults < JsonSchematize::Generator
  schema_default option: :dig_type, value: :string

  add_field name: :internals, type: InternalBody, array_of_types: true
  add_field name: :id, type: Integer
  add_field name: :status, type: Symbol, required: false, empty_value: "empty"
end

Custom Classes

class CustomClasses < JsonSchematize::Generator
  # JsonSchematize::Boolean can be used as a type when expecting a conversion of possible true or false values converted into a TrueClass or FalseClass
  add_field name: :internals, type: JsonSchematize::Boolean
end

Caching Adapter

JsonSchematize is built to be schema for API results. But what happens when you dont expect the result to change? Introducing the caching layer. This layer lets you cache a JsonSchematize object that can be queried from later

Note: This requires redis

class CachedClass < JsonSchematize::Generator
  include JsonSchematize::Cache
  cache_options key: ->(instance_of_cached_class, cache_key_from_initialization) { "#{instance_of_cached_class.id}:#{cache_key_from_initialization}" }
  cache_options ttl: 7.days.to_i

  schema_default option: :dig_type, value: :string

  add_field name: :id, type: Integer
end

params = { id: 1 }
CachedClass.new(cache_key: User.first.id, **params)
###
params = { "id" => 1 }
CachedClass.new(params, cache_key: User.first.id)

Instance methods for Cache

# optional cache_key added on initialization: Can be used to customize the cache entry for the instance
item = CachedClass.new(cache_key: User.first.id, **params)

# Update the cached item -- Note: This will overwrite the previous cached item IFF the `__cache_key__` remains the same. This is not gaurenteed
# Optional Param: with_delete, Default true -- Will attempt to delete the original object
item.__update_cache_item__

# Manually delete the cached entry
item.__clear_entry__!

# Cache key for the item
item.__cache_key__

Class methods for Cache

# Retrieve all cached items for the class. Returns an array of CachedClass objects. Only objects that have not expired via TTL
# Optional: key_includes: "string_expected_in_cache", default is nil and return everthing
CachedClass.cached_items

# Retrieves all valid object keys from the cache
CachedClass.cached_keys

# Clears all cached items for the given class
CachedClass.clear_cache!

# manually clear objects that have expired
CachedClass.clear_unscored_items!

Cache options

# [Required] false; [Expect] Proc; [Return] String to be used as instance key; [Default] key will be the hash of the object
cache_options key: ->(instance_of_class, custom_key) { }

# [Required] false; [Expect] String; [Default] ENV["CACHE_LAYER_REDIS_URL"] || ENV["REDIS_URL"]
cache_options redis_url: _redis_url_value

# [Required] false; [Expect] Redis Object or Proc that returns value of Redis Client; [Default] Redis.new(url: redis_url)
cache_options redis_client: redis_client

# [Required] false; [Expect] Object that plays with to_s and has no spaces; [Default] Full class name downcased
cache_options cache_namespace: cache_namespace

# [Required] false; [Expect] Integer in seconds; [Default] 1 day
cache_options ttl: (60 * 60)

# [Required] false; [Expect] Boolean; [Default] true
# Update the cache when a value has been changed manually
cache_options update_on_change: true

# [Required] false; [Expect] Float; [Default] 0.8
# Expected value to be between 0 and 1. The sample rate that the class will clear oldcache values on retreival
cache_options stochastic_cache_bust: 0.8

Development

This gem can be developed against local machine or while using docker. Simpleified Docker commands can be found in the Makefile or execute make help

Contributing

This gem welcomes contribution.

Bug reports and pull requests are welcome on GitHub at https://github.com/matt-taylor/json_schematize.