I've been working on trying to use more keywords from the JSON Schema dialect, such as minimum
, maximum
, and required
. I wasn't able to get shale's JSON mapping to work which makes sense if it's just for serializing/deserializing, so as of now I've thought of three possible approaches
1. Custom classes for every possibility
I think this is what developers would have to do right now to use more of the dialect. To have an integer with a minimum value of 1 might look like this:
class IntegerMinimumOneType < Shale::Type::Integer
end
class IntegerMinimumOneJSONType < Shale::Schema::JSONGenerator::Base
def as_type
{ "type" => "integer", "minimum" => 1 }
end
end
Shale::Schema::JSONGenerator.register_json_type(IntegerMinimumOneType, IntegerMinimumOneJSONType)
class PersonMapper < Shale::Mapper
model Person
attribute :age, IntegerMinimumOneType
end
This would be fine for common things, like PositiveInteger
, but this would get increasingly difficult to manage if the schema needed multiple validations for properties, such as a minimum
, maximum
, and required
all at once.
2. Instantiated types for more flexibility
This isn't possible with shale right now, but I have a local branch where I've gotten it to work and would be happy to make a PR if this is a direction you'd like to go in. To achieve the same schema as above the API could look like this:
class BoundedIntegerType < Shale::Type::Integer
attr_reader :min, :max
def initialize(min = nil, max = nil)
@min = min
@max = max
end
end
class BoundedIntegerJSONType < Shale::Schema::JSONGenerator::Base
def as_type
{ "type" => "integer", "minimum" => instance.min, "maximum": instance.max }.compact
end
end
Shale::Schema::JSONGenerator.register_json_type(BoundedIntegerType, BoundedIntegerJSONType)
class PersonMapper < Shale::Mapper
model Person
attribute :age, BoundedIntegerType.new(1) # minimum of 1, no maximum
end
The responsibility is still on the consumer of the gem to create the types needed for their schema, but having a generator that can receive an instance of a class can allow for a lot of flexibility.
3. Modify the API to include more options, similar to collection
Another option to allow more of the dialect to be used is to allow the attribute API to receive all the possible keywords, either individually or through some sort of hash.
class PersonMapper < Shale::Mapper
model Person
attribute :age, Shale::Type::Integer, minimum: 1
end
This might involve some more work on the shale side of things to ensure that the provided keywords are compatible with the type. For example this should be considered invalid since maxItems
is for arrays:
attribute :age, Shale::Type::Integer, max_items: 5
Apologies if I've overlooked something, but based on reading the docs and trying things out I think right now Option 1 is all I can do, so I wanted to explore Option 2 since it seemed less cumbersome. It also seemed like implementing Option 3 would add a lot more complexity to the gem. Should I go ahead and make a PR for this, or do you have guidance on how to achieve this another way?