json_schema_view 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +58 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +18 -0
- data/LICENSE.md +202 -0
- data/README.md +117 -0
- data/Rakefile +12 -0
- data/json_schema_view.gemspec +42 -0
- data/lib/json_schema_view/base_component.rb +82 -0
- data/lib/json_schema_view/base_props.rb +32 -0
- data/lib/json_schema_view/configuration/schema_set_dictionary.rb +51 -0
- data/lib/json_schema_view/configuration.rb +44 -0
- data/lib/json_schema_view/json_schema_definable.rb +22 -0
- data/lib/json_schema_view/json_world_extensions/additional_properties.rb +58 -0
- data/lib/json_schema_view/json_world_extensions/any_of.rb +41 -0
- data/lib/json_schema_view/json_world_extensions/camelizable.rb +36 -0
- data/lib/json_schema_view/json_world_extensions/compact_optional_properties.rb +20 -0
- data/lib/json_schema_view/json_world_extensions/constant_property.rb +39 -0
- data/lib/json_schema_view/json_world_extensions/declarable.rb +55 -0
- data/lib/json_schema_view/json_world_extensions/enum_type.rb +45 -0
- data/lib/json_schema_view/json_world_extensions/map_type.rb +47 -0
- data/lib/json_schema_view/json_world_extensions/validatable.rb +17 -0
- data/lib/json_schema_view/json_world_extensions.rb +31 -0
- data/lib/json_schema_view/rails/generators/install/USAGE +5 -0
- data/lib/json_schema_view/rails/generators/install/install_generator.rb +50 -0
- data/lib/json_schema_view/rails/generators/install/templates/base_component.rb.tt +17 -0
- data/lib/json_schema_view/rails/generators/install/templates/base_props.rb.tt +7 -0
- data/lib/json_schema_view/rails/generators/install/templates/component_schema_set.rb.tt +25 -0
- data/lib/json_schema_view/rails/generators/install/templates/example/todo_item_resource.rb +52 -0
- data/lib/json_schema_view/rails/generators/install/templates/example/todo_list_component.rb +67 -0
- data/lib/json_schema_view/rails/generators/install/templates/initializer.rb.tt +15 -0
- data/lib/json_schema_view/rails/generators.rb +8 -0
- data/lib/json_schema_view/rails/rails_engine.rb +20 -0
- data/lib/json_schema_view/rails/tasks/json_schema_component.rake +9 -0
- data/lib/json_schema_view/rails.rb +8 -0
- data/lib/json_schema_view/renderers/base.rb +27 -0
- data/lib/json_schema_view/renderers/json.rb +28 -0
- data/lib/json_schema_view/renderers/react_on_rails.rb +17 -0
- data/lib/json_schema_view/renderers.rb +22 -0
- data/lib/json_schema_view/schema_set/constant_search_helper.rb +114 -0
- data/lib/json_schema_view/schema_set.rb +58 -0
- data/lib/json_schema_view/version.rb +5 -0
- data/lib/json_schema_view.rb +29 -0
- data/sig/json_schema_view.rbs +4 -0
- metadata +134 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
|
5
|
+
module JsonSchemaView
|
6
|
+
class Configuration
|
7
|
+
# :nodoc:
|
8
|
+
class SchemaSetDictionary < Delegator
|
9
|
+
attr_reader :hash
|
10
|
+
|
11
|
+
# @param contents [Hash, nil]
|
12
|
+
def initialize(contents = nil)
|
13
|
+
@hash = ActiveSupport::HashWithIndifferentAccess.new(contents)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
# @overload {Delegator#__getobj__}
|
18
|
+
# @return [HashWithIndifferentAccess{Symbol => Symbol}]
|
19
|
+
def __getobj__
|
20
|
+
hash
|
21
|
+
end
|
22
|
+
|
23
|
+
# @overload {Delegator#__setobj__}
|
24
|
+
def __setobj__(_)
|
25
|
+
# noop
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param key [#to_sym] The alias name of SchemaSet
|
29
|
+
# @param value [String, Symbol] The name of SchemaSet class
|
30
|
+
def []=(key, value)
|
31
|
+
raise TypeError, "key (#{key}) cannot be converted to a symbol" unless key.respond_to?(:to_sym)
|
32
|
+
|
33
|
+
if !value.is_a?(String) && !value.is_a?(Symbol)
|
34
|
+
raise TypeError,
|
35
|
+
"value (#{value}) must be the name of SchemaSet class"
|
36
|
+
end
|
37
|
+
|
38
|
+
hash[key.to_sym] = value.to_sym
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [SchemaSet]
|
42
|
+
def fetch_instance(key)
|
43
|
+
klass = fetch(key).constantize
|
44
|
+
|
45
|
+
raise TypeError, "#{klass} is not the name of SchemaSet class" unless klass < JsonSchemaView::SchemaSet
|
46
|
+
|
47
|
+
klass.new
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonSchemaView
|
4
|
+
# The singleton holds the configuration for the gem.
|
5
|
+
# it can be accessed by both its class methods and Rails's configuration.
|
6
|
+
class Configuration
|
7
|
+
require_relative "configuration/schema_set_dictionary"
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# @return [JsonSchemaView::Configuration]
|
11
|
+
def instance
|
12
|
+
@instance ||= new
|
13
|
+
end
|
14
|
+
|
15
|
+
# @yield [configuration]
|
16
|
+
# @yieldparam configuration [JsonSchemaView::Configuration]
|
17
|
+
def configure(&block)
|
18
|
+
instance.configure(&block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@schema_sets = SchemaSetDictionary.new
|
24
|
+
@validate_by_default = false
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [SchemaSetDictionary{Symbol => Symbol}] The renderers.
|
28
|
+
attr_reader :schema_sets
|
29
|
+
|
30
|
+
# @return [Boolean] Whether if validate components with its schema on render by default.
|
31
|
+
attr_accessor :validate_by_default
|
32
|
+
|
33
|
+
# @param new_sets [Hash{String, Symbol => String, Symbol}]
|
34
|
+
def schema_sets=(new_sets)
|
35
|
+
@schema_sets = SchemaSetDictionary.new(new_sets)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @yield [configuration]
|
39
|
+
# @yieldparam configuration [JsonSchemaView::Configuration]
|
40
|
+
def configure
|
41
|
+
yield self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json_world"
|
4
|
+
|
5
|
+
require_relative "json_world_extensions"
|
6
|
+
|
7
|
+
module JsonSchemaView
|
8
|
+
# @abstract
|
9
|
+
module JsonSchemaDefinable
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
include ::JsonWorld::DSL
|
13
|
+
include JsonWorldExtensions::Schema202012
|
14
|
+
include JsonWorldExtensions::CompactOptionalProperties
|
15
|
+
include JsonWorldExtensions::Camelizable
|
16
|
+
include JsonWorldExtensions::Validatable
|
17
|
+
|
18
|
+
included do
|
19
|
+
additional_properties(false)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
|
5
|
+
module JsonSchemaView
|
6
|
+
module JsonWorldExtensions
|
7
|
+
# Define JSON Schema's additionalProperties property.
|
8
|
+
# @see https://json-schema.org/understanding-json-schema/reference/object.html#additional-properties
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# class Zoo
|
12
|
+
# include JsonWorld::DSL
|
13
|
+
# include JsonSchemaView::JsonWorldExtensions::MapType::DSL
|
14
|
+
#
|
15
|
+
# additional_properties(false)
|
16
|
+
# property(
|
17
|
+
# :dogMap,
|
18
|
+
# type: map_type(Dog),
|
19
|
+
# )
|
20
|
+
# end
|
21
|
+
module AdditionalProperties
|
22
|
+
extend ActiveSupport::Concern
|
23
|
+
|
24
|
+
class_methods do
|
25
|
+
# @override {Class.override}
|
26
|
+
def inherited(child)
|
27
|
+
super
|
28
|
+
child.additional_properties(additional_properties)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return the additionalProperties property.
|
32
|
+
# Set the additionalProperties property if arguments are given.
|
33
|
+
# @see https://json-schema.org/draft/2020-12/json-schema-core.html#additionalProperties
|
34
|
+
# @see https://json-schema.org/understanding-json-schema/reference/object.html#id5
|
35
|
+
#
|
36
|
+
# @param value [false, Hash]
|
37
|
+
# @param value_keywords [Hash]
|
38
|
+
def additional_properties(value = nil, **value_keywords)
|
39
|
+
return @additional_properties = value unless value.nil?
|
40
|
+
return @additional_properties = value_keywords unless value_keywords.empty?
|
41
|
+
|
42
|
+
@additional_properties
|
43
|
+
end
|
44
|
+
|
45
|
+
# @override {JsonWorld::DSL::ClassMethods#as_json_schema}
|
46
|
+
def as_json_schema
|
47
|
+
# Do not allow undefined properities.
|
48
|
+
# @see https://json-schema.org/understanding-json-schema/reference/object.html#additionalproperties
|
49
|
+
if @additional_properties.nil?
|
50
|
+
super
|
51
|
+
else
|
52
|
+
super.merge("additionalProperties": @additional_properties)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonSchemaView
|
4
|
+
module JsonWorldExtensions
|
5
|
+
# This class implements `anyOf` schema composition.
|
6
|
+
# Instances of this class acts as an json world compatible object and prints the schema for anyOf combination.
|
7
|
+
# @see https://json-schema.org/understanding-json-schema/reference/combining.html#id6
|
8
|
+
# @example
|
9
|
+
# class Zoo
|
10
|
+
# include JsonWorld::DSL
|
11
|
+
# include JsonSchemaView::JsonWorldExtensions::MapType::DSL
|
12
|
+
# property(
|
13
|
+
# :animal,
|
14
|
+
# type: any_of.new(Dog, Cat),
|
15
|
+
# )
|
16
|
+
# end
|
17
|
+
class AnyOf
|
18
|
+
module DSL
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
|
21
|
+
class_methods do
|
22
|
+
def any_of(*types)
|
23
|
+
JsonWorldExtensions::AnyOf.new(*types)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :types
|
29
|
+
|
30
|
+
def initialize(*types)
|
31
|
+
@types = types
|
32
|
+
end
|
33
|
+
|
34
|
+
def as_json_schema
|
35
|
+
{ "anyOf" => types.map(&:as_json_schema) }
|
36
|
+
end
|
37
|
+
|
38
|
+
alias as_json_schema_without_links as_json_schema
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
|
5
|
+
module JsonSchemaView
|
6
|
+
module JsonWorldExtensions
|
7
|
+
# Make the object to be serialized with camelized properties.
|
8
|
+
module Camelizable
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
class_methods do
|
12
|
+
# @param (see JsonWorld::DSL::ClassMethods#property)
|
13
|
+
# @param camelize [Boolean] if true, camelize the property name on json format.
|
14
|
+
def property(raw_property_name, camelize: true, **options)
|
15
|
+
raw_property_name = raw_property_name.to_sym
|
16
|
+
property_name = camelize ? camelize_name(raw_property_name) : raw_property_name
|
17
|
+
|
18
|
+
super(property_name, **options)
|
19
|
+
|
20
|
+
# Define a method with camelized name as an alias of the method with raw name.
|
21
|
+
if camelize && property_name != raw_property_name
|
22
|
+
define_method(property_name) do
|
23
|
+
public_send(raw_property_name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param name [String, Symbol]
|
29
|
+
# @return [Symbol]
|
30
|
+
def camelize_name(name)
|
31
|
+
ActiveSupport::Inflector.camelize(name.to_s, false).to_sym
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonSchemaView
|
4
|
+
module JsonWorldExtensions
|
5
|
+
# This module automatically removes properties with null value from encoded json if the property is optional.
|
6
|
+
#
|
7
|
+
# JSON Schema distinguishs a optional property and a nullable property.
|
8
|
+
# If the value of a optional property is empty, you should remove the property from the encoded json.
|
9
|
+
# This module make safe such the optional properties.
|
10
|
+
# @see https://json-schema.org/understanding-json-schema/reference/object.html#required-properties
|
11
|
+
module CompactOptionalProperties
|
12
|
+
# @note Overrides {JsonWorld::JsonEncodable#properties}
|
13
|
+
def properties(options)
|
14
|
+
optional_property_names = self.class.property_definitions.select(&:optional?).map(&:property_name)
|
15
|
+
|
16
|
+
super(options).reject { |key, value| value.nil? && optional_property_names.include?(key) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonSchemaView
|
4
|
+
module JsonWorldExtensions
|
5
|
+
# This module allows {JsonWorld::DSL::ClassMethods#property} to define constant values.
|
6
|
+
# Defined constant values are reflect to JSON Schema.
|
7
|
+
# @see https://json-schema.org/understanding-json-schema/reference/generic.html#constant-values
|
8
|
+
module ConstantProperty
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
class_methods do
|
12
|
+
# If property is defined with `const` parameter, define an instance method to return the value.
|
13
|
+
# @note Overrides {JsonWorld::DSL::ClassMethods#property}
|
14
|
+
def property(name, **options)
|
15
|
+
if options[:const]
|
16
|
+
const = options[:const].freeze
|
17
|
+
define_method(name) { const }
|
18
|
+
end
|
19
|
+
|
20
|
+
super(name, **options)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Add constant values informations JSON Schema
|
24
|
+
# @note Overrides {JsonWorld::DSL::ClassMethods#as_json_schema}
|
25
|
+
def as_json_schema
|
26
|
+
property_hash = property_definitions.map do |property|
|
27
|
+
if (const = property.raw_options[:const])
|
28
|
+
# For now, use enumerated values instead of constant values.
|
29
|
+
[property.property_name, { const: const }]
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end.compact.to_h
|
34
|
+
super.deep_merge(properties: property_hash)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonSchemaView
|
4
|
+
module JsonWorldExtensions
|
5
|
+
# Add methods to declare a common schema. Declared schema are exported as `$defs` property.
|
6
|
+
#
|
7
|
+
# @see: https://json-schema.org/understanding-json-schema/structuring.html#defs
|
8
|
+
module Declarable
|
9
|
+
extend ::ActiveSupport::Concern
|
10
|
+
|
11
|
+
class_methods do
|
12
|
+
attr_writer :declarations
|
13
|
+
|
14
|
+
# @override {Class.inherited}
|
15
|
+
def inherited(child)
|
16
|
+
super
|
17
|
+
child.declarations = declarations.clone
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param type_or_name [Class<JsonWorld::DSL>, Symbol]
|
21
|
+
# @param (see JsonWorld::DSL::ClassMethods#property)
|
22
|
+
def declare(type_or_name, **options)
|
23
|
+
property_type = options[:type] || type_or_name
|
24
|
+
property_name = type_or_name.is_a?(Module) ? type_or_name.name.demodulize.to_sym : type_or_name.to_sym
|
25
|
+
|
26
|
+
declaration = JsonWorld::PropertyDefinition.new(property_name: property_name, type: property_type, **options)
|
27
|
+
declarations << declaration
|
28
|
+
end
|
29
|
+
|
30
|
+
# @override {JsonWorld::DSL::ClassMethods#as_json_schema}
|
31
|
+
def as_json_schema
|
32
|
+
decls = declarations_as_json_schema
|
33
|
+
if decls.present?
|
34
|
+
super.merge("$defs": decls)
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def declarations_as_json_schema
|
41
|
+
declarations.inject({}) do |result, declaration|
|
42
|
+
result.merge(
|
43
|
+
declaration.property_name => declaration.as_json_schema,
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Array<PropertyDefinition>]
|
49
|
+
def declarations
|
50
|
+
@declarations ||= []
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonSchemaView
|
4
|
+
module JsonWorldExtensions
|
5
|
+
# This module allows {JsonWorld::DSL::ClassMethods#property} to define enum values.
|
6
|
+
# Defined constant values are reflect to JSON Schema.
|
7
|
+
# @see https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values
|
8
|
+
# @example
|
9
|
+
# class Zoo
|
10
|
+
# include JsonWorld::DSL
|
11
|
+
# include JsonSchemaView::JsonWorldExtensions::EnumType::DSL
|
12
|
+
# property(
|
13
|
+
# :animal,
|
14
|
+
# type: enum_type("Dog", "Cat"),
|
15
|
+
# )
|
16
|
+
# end
|
17
|
+
class EnumType
|
18
|
+
module DSL
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
|
21
|
+
class_methods do
|
22
|
+
def enum_type(*args)
|
23
|
+
JsonWorldExtensions::EnumType.new(*args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :candidates
|
29
|
+
|
30
|
+
# @param type [String, Symbol, #as_json_schema]
|
31
|
+
# @param candidates [Array<Object>]
|
32
|
+
def initialize(*candidates)
|
33
|
+
@candidates = candidates
|
34
|
+
end
|
35
|
+
|
36
|
+
def as_json_schema
|
37
|
+
{
|
38
|
+
enum: candidates
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
alias as_json_schema_without_links as_json_schema
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonSchemaView
|
4
|
+
module JsonWorldExtensions
|
5
|
+
# This class implements map type (string keys to values with the given type) in JSON Schema.
|
6
|
+
# You can specify the type of values but you cannot specify the type of keys,
|
7
|
+
# because JSON and JSON Schema only support String keys in objects.
|
8
|
+
#
|
9
|
+
# @see https://json-schema.org/understanding-json-schema/reference/object.html#additional-properties
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# class Zoo
|
13
|
+
# include JsonWorld::DSL
|
14
|
+
# include JsonSchemaView::JsonWorldExtensions::MapType::DSL
|
15
|
+
# property(
|
16
|
+
# :dogMap,
|
17
|
+
# type: map_type(Dog),
|
18
|
+
# )
|
19
|
+
# end
|
20
|
+
class MapType
|
21
|
+
module DSL
|
22
|
+
extend ActiveSupport::Concern
|
23
|
+
|
24
|
+
class_methods do
|
25
|
+
def map_type(type)
|
26
|
+
JsonWorldExtensions::MapType.new(type)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :type
|
32
|
+
|
33
|
+
def initialize(type)
|
34
|
+
@type = type
|
35
|
+
end
|
36
|
+
|
37
|
+
def as_json_schema
|
38
|
+
{
|
39
|
+
type: "object",
|
40
|
+
additionalProperties: JsonWorld::PropertyDefinition.new(type: type).as_json_schema
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
alias as_json_schema_without_links as_json_schema
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "json-schema"
|
5
|
+
|
6
|
+
module JsonSchemaView
|
7
|
+
module JsonWorldExtensions
|
8
|
+
# Add method to validate self properties with {JSON::Validator}.
|
9
|
+
module Validatable
|
10
|
+
# @raise [JSON::Schema::ValidationError] if self is not valid.
|
11
|
+
# @return [void]
|
12
|
+
def validate_json!
|
13
|
+
JSON::Validator.validate!(self.class.as_json_schema, as_json)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
|
5
|
+
module JsonSchemaView
|
6
|
+
# A collection of extensions for {JsonWorld::DSL} to support recent JSON Schema.
|
7
|
+
# @private
|
8
|
+
module JsonWorldExtensions
|
9
|
+
require_relative "json_world_extensions/additional_properties"
|
10
|
+
require_relative "json_world_extensions/any_of"
|
11
|
+
require_relative "json_world_extensions/camelizable"
|
12
|
+
require_relative "json_world_extensions/compact_optional_properties"
|
13
|
+
require_relative "json_world_extensions/constant_property"
|
14
|
+
require_relative "json_world_extensions/declarable"
|
15
|
+
require_relative "json_world_extensions/enum_type"
|
16
|
+
require_relative "json_world_extensions/map_type"
|
17
|
+
require_relative "json_world_extensions/validatable"
|
18
|
+
|
19
|
+
# @see https://json-schema.org/specification.html
|
20
|
+
module Schema202012
|
21
|
+
extend ActiveSupport::Concern
|
22
|
+
|
23
|
+
include AdditionalProperties
|
24
|
+
include AnyOf::DSL
|
25
|
+
include ConstantProperty
|
26
|
+
include Declarable
|
27
|
+
include EnumType::DSL
|
28
|
+
include MapType::DSL
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
|
5
|
+
module JsonSchemaView
|
6
|
+
module Generators
|
7
|
+
# Install files to setup json_schema_view gem.
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
10
|
+
|
11
|
+
class_option :components_path, type: :string, default: "app/components"
|
12
|
+
class_option :export_path, type: :string, default: "json_schema"
|
13
|
+
class_option :renderer_type, type: :string, default: "react_on_rails"
|
14
|
+
|
15
|
+
def create_initializer
|
16
|
+
template "initializer.rb", "config/initializers/json_schema_view.rb"
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_base_classes
|
20
|
+
template "base_component.rb", File.join(components_path, "base_component.rb")
|
21
|
+
template "base_props.rb", File.join(components_path, "base_props.rb")
|
22
|
+
template "component_schema_set.rb", File.join(components_path, "component_schema_set.rb")
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_exmaples
|
26
|
+
template "example/todo_list_component.rb", File.join(components_path, "example_todo_list_component.rb")
|
27
|
+
template "example/todo_item_resource.rb",
|
28
|
+
File.join(components_path, "example_todo_list_component", "todo_item_resource.rb")
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def components_path
|
34
|
+
options[:components_path]
|
35
|
+
end
|
36
|
+
|
37
|
+
def export_path
|
38
|
+
options[:export_path]
|
39
|
+
end
|
40
|
+
|
41
|
+
def renderer_type
|
42
|
+
options[:renderer_type]
|
43
|
+
end
|
44
|
+
|
45
|
+
def schema_set_class_name
|
46
|
+
"ComponentSchemaSet"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json_schema_view"
|
4
|
+
|
5
|
+
# @abstract
|
6
|
+
class BaseComponent < ::JsonSchemaView::BaseComponent
|
7
|
+
# You can configure how this components is rendered.
|
8
|
+
#
|
9
|
+
# There are some preset renderers:
|
10
|
+
# * `:json` - serialize the component as json text.
|
11
|
+
# * `:react_on_rails` - render the component by using react_on_rails gem.
|
12
|
+
renderer_class(:<%= renderer_type %>)
|
13
|
+
|
14
|
+
def base_props_class
|
15
|
+
BaseProps
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json_schema_view"
|
4
|
+
|
5
|
+
# Configure what resource to export its JSON Schema and where directory to export.
|
6
|
+
class <%= schema_set_class_name %> < ::JsonSchemaView::SchemaSet
|
7
|
+
# <%= schema_set_class_name %> looks up component classses in this directory.
|
8
|
+
def root_path
|
9
|
+
__dir__
|
10
|
+
end
|
11
|
+
|
12
|
+
# <%= schema_set_class_name %> exports JSON Schema of these component classes.
|
13
|
+
def resource_classes_to_export
|
14
|
+
[
|
15
|
+
# Export all component classes (except for BaseComopnent) in `root_path`.
|
16
|
+
*search_component_classes.excluding(BaseComponent),
|
17
|
+
# You can add extra classes including {JsonSchemaDefinable} here to export their JSON Schemas.
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
# <%= schema_set_class_name %> exports JSON Schema to this directory.
|
22
|
+
def export_path
|
23
|
+
Rails.root.join("<%= export_path %>")
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json_schema_view"
|
4
|
+
|
5
|
+
class ExampleTodoListComponent
|
6
|
+
# This is an example definition of a resource.
|
7
|
+
#
|
8
|
+
# A class that includes {JsonSchemaView::JsonSchemaDefinable} can generate its JSON Schema
|
9
|
+
# and it can be used as a type in another resource's property.
|
10
|
+
class TodoItemResource
|
11
|
+
include JsonSchemaView::JsonSchemaDefinable
|
12
|
+
|
13
|
+
property(
|
14
|
+
:name,
|
15
|
+
description: "The title of TODO item.",
|
16
|
+
type: String,
|
17
|
+
)
|
18
|
+
|
19
|
+
property(
|
20
|
+
:done,
|
21
|
+
description: "Whether the TODO item is done or not.",
|
22
|
+
type: [TrueClass, FalseClass],
|
23
|
+
)
|
24
|
+
|
25
|
+
property(
|
26
|
+
:note,
|
27
|
+
description: "The additional description of TODO item.",
|
28
|
+
type: String,
|
29
|
+
optional: true,
|
30
|
+
)
|
31
|
+
|
32
|
+
# @param todo_item [Hash] A todo item ({ name: String, done: Boolean, note: String or nil }).
|
33
|
+
def initialize(todo_item)
|
34
|
+
@todo_item = todo_item
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String] The title of TODO item.
|
38
|
+
def title
|
39
|
+
@todo_item[:title]
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Boolean] Whether the TODO item is done or not.
|
43
|
+
def done
|
44
|
+
@todo_item[:done]
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param note [String, nil] The additional description of TODO item.
|
48
|
+
def note
|
49
|
+
@todo_item[:note]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|