chronicle-core 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.github/workflows/rspec.yml +1 -1
 - data/.gitignore +3 -1
 - data/.rubocop-plugin.yml +4 -0
 - data/.rubocop.yml +16 -2
 - data/Gemfile +2 -2
 - data/Guardfile +3 -3
 - data/LICENSE.txt +1 -1
 - data/README.md +87 -2
 - data/Rakefile +63 -1
 - data/bin/console +6 -6
 - data/chronicle-core.gemspec +32 -26
 - data/lib/chronicle/core/version.rb +1 -3
 - data/lib/chronicle/core.rb +1 -3
 - data/lib/chronicle/models/base.rb +96 -0
 - data/lib/chronicle/models/builder.rb +35 -0
 - data/lib/chronicle/models/generation.rb +89 -0
 - data/lib/chronicle/models/model_factory.rb +63 -0
 - data/lib/chronicle/models.rb +17 -0
 - data/lib/chronicle/schema/rdf_parsing/graph_transformer.rb +122 -0
 - data/lib/chronicle/schema/rdf_parsing/rdf_serializer.rb +138 -0
 - data/lib/chronicle/schema/rdf_parsing/schemaorg.rb +50 -0
 - data/lib/chronicle/schema/rdf_parsing/ttl_graph_builder.rb +142 -0
 - data/lib/chronicle/schema/rdf_parsing.rb +11 -0
 - data/lib/chronicle/schema/schema_graph.rb +145 -0
 - data/lib/chronicle/schema/schema_property.rb +81 -0
 - data/lib/chronicle/schema/schema_type.rb +110 -0
 - data/lib/chronicle/schema/types.rb +9 -0
 - data/lib/chronicle/schema/validation/base_contract.rb +22 -0
 - data/lib/chronicle/schema/validation/contract_factory.rb +133 -0
 - data/lib/chronicle/schema/validation/edge_validator.rb +53 -0
 - data/lib/chronicle/schema/validation/generation.rb +29 -0
 - data/lib/chronicle/schema/validation/validator.rb +23 -0
 - data/lib/chronicle/schema/validation.rb +41 -0
 - data/lib/chronicle/schema.rb +9 -2
 - data/lib/chronicle/serialization/hash_serializer.rb +5 -11
 - data/lib/chronicle/serialization/jsonapi_serializer.rb +41 -26
 - data/lib/chronicle/serialization/jsonld_serializer.rb +38 -0
 - data/lib/chronicle/serialization/record.rb +90 -0
 - data/lib/chronicle/serialization/serializer.rb +31 -18
 - data/lib/chronicle/serialization.rb +6 -4
 - data/lib/chronicle/utils/hash_utils.rb +19 -16
 - data/schema/chronicle_schema_v1.json +1008 -0
 - data/schema/chronicle_schema_v1.rb +147 -0
 - data/schema/chronicle_schema_v1.ttl +562 -0
 - metadata +107 -15
 - data/lib/chronicle/schema/activity.rb +0 -5
 - data/lib/chronicle/schema/base.rb +0 -79
 - data/lib/chronicle/schema/entity.rb +0 -5
 - data/lib/chronicle/schema/raw.rb +0 -9
 - data/lib/chronicle/schema/validator.rb +0 -55
 
| 
         @@ -0,0 +1,122 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Chronicle
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Schema
         
     | 
| 
      
 3 
     | 
    
         
            +
                module RDFParsing
         
     | 
| 
      
 4 
     | 
    
         
            +
                  # A class that inteprets a DSL to transform a base schema graph into
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # a new one by walking through the types and properties of the base
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # and selecting which ones to include in the new graph.
         
     | 
| 
      
 7 
     | 
    
         
            +
                  class GraphTransformer
         
     | 
| 
      
 8 
     | 
    
         
            +
                    attr_reader :graph, :base_graph, :new_graph
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    def initialize
         
     | 
| 
      
 11 
     | 
    
         
            +
                      @base_graph = nil
         
     | 
| 
      
 12 
     | 
    
         
            +
                      @new_graph = Chronicle::Schema::SchemaGraph.new(default_namespace: 'https://schema.chronicle.app/')
         
     | 
| 
      
 13 
     | 
    
         
            +
                      @current_parent = nil
         
     | 
| 
      
 14 
     | 
    
         
            +
                    end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    def self.transform(&)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      transformer = new
         
     | 
| 
      
 18 
     | 
    
         
            +
                      transformer.start_evaluating(&)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                      # TODO: figure out if we need to this still
         
     | 
| 
      
 21 
     | 
    
         
            +
                      transformer.new_graph.properties.each do |property|
         
     | 
| 
      
 22 
     | 
    
         
            +
                        property.domain = property.domain.select do |type_id|
         
     | 
| 
      
 23 
     | 
    
         
            +
                          transformer.new_graph.find_type_by_id(type_id)
         
     | 
| 
      
 24 
     | 
    
         
            +
                        end
         
     | 
| 
      
 25 
     | 
    
         
            +
                      end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                      transformer.new_graph.build_references!
         
     | 
| 
      
 28 
     | 
    
         
            +
                      transformer.new_graph
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    def self.transform_from_file(definition_file_path)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      dsl_commands = File.read(definition_file_path)
         
     | 
| 
      
 33 
     | 
    
         
            +
                      transform_from_string(dsl_commands)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    def self.transform_from_string(dsl_definition)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      transform do
         
     | 
| 
      
 38 
     | 
    
         
            +
                        instance_eval(dsl_definition)
         
     | 
| 
      
 39 
     | 
    
         
            +
                      end
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    def start_evaluating(&)
         
     | 
| 
      
 43 
     | 
    
         
            +
                      instance_eval(&) if block_given?
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    private
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    def version(version)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      @new_graph.version = version
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    def set_base_graph(name, version)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      case name
         
     | 
| 
      
 54 
     | 
    
         
            +
                      when 'schema.org'
         
     | 
| 
      
 55 
     | 
    
         
            +
                        @base_graph = Chronicle::Schema::RDFParsing::Schemaorg.graph_for_version(version)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      else
         
     | 
| 
      
 57 
     | 
    
         
            +
                        raise ArgumentError, "Unknown base graph: #{name}"
         
     | 
| 
      
 58 
     | 
    
         
            +
                      end
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    def pick_type(subtype_identifier, &)
         
     | 
| 
      
 62 
     | 
    
         
            +
                      id = @base_graph.identifier_to_uri(subtype_identifier)
         
     | 
| 
      
 63 
     | 
    
         
            +
                      type = @base_graph.find_type_by_id(id)
         
     | 
| 
      
 64 
     | 
    
         
            +
                      raise ArgumentError, "Subtype not found: #{subtype_identifier}" unless type
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                      new_subtype = @new_graph.add_type(subtype_identifier)
         
     | 
| 
      
 67 
     | 
    
         
            +
                      new_subtype.comment = type.comment
         
     | 
| 
      
 68 
     | 
    
         
            +
                      new_subtype.see_also = type.id
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                      @current_parent&.add_subtype_id(new_subtype.id)
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                      previous_parent = @current_parent
         
     | 
| 
      
 73 
     | 
    
         
            +
                      @current_parent = new_subtype
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                      instance_eval(&) if block_given?
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                      @current_parent = previous_parent
         
     | 
| 
      
 78 
     | 
    
         
            +
                    end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                    def pick_all_subtypes(&)
         
     | 
| 
      
 81 
     | 
    
         
            +
                      @base_graph.find_type(@current_parent.short_id.to_sym).subtype_ids.each do |subtype_id|
         
     | 
| 
      
 82 
     | 
    
         
            +
                        identifier = @base_graph.id_to_identifier(subtype_id)
         
     | 
| 
      
 83 
     | 
    
         
            +
                        pick_type(identifier, &)
         
     | 
| 
      
 84 
     | 
    
         
            +
                      end
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    def apply_property(property_identifier, many: false, required: false)
         
     | 
| 
      
 88 
     | 
    
         
            +
                      property = @base_graph.find_property(property_identifier)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      raise ArgumentError, "Property not found: #{property_identifier}" unless property
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                      new_property = @new_graph.add_property(property_identifier)
         
     | 
| 
      
 92 
     | 
    
         
            +
                      new_property.range = property.range.map do |p|
         
     | 
| 
      
 93 
     | 
    
         
            +
                        base_range_type = @base_graph.find_type_by_id(p)
         
     | 
| 
      
 94 
     | 
    
         
            +
                        @new_graph.identifier_to_uri(base_range_type.identifier)
         
     | 
| 
      
 95 
     | 
    
         
            +
                      end
         
     | 
| 
      
 96 
     | 
    
         
            +
                      new_property.comment = property.comment
         
     | 
| 
      
 97 
     | 
    
         
            +
                      new_property.many = many
         
     | 
| 
      
 98 
     | 
    
         
            +
                      new_property.required = required
         
     | 
| 
      
 99 
     | 
    
         
            +
                      new_property.domain += [@current_parent.id]
         
     | 
| 
      
 100 
     | 
    
         
            +
                      new_property.see_also = property.id
         
     | 
| 
      
 101 
     | 
    
         
            +
                    end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                    def add_property(property_identifier, many: false, required: false, comment: nil)
         
     | 
| 
      
 104 
     | 
    
         
            +
                      new_property = @new_graph.add_property(property_identifier)
         
     | 
| 
      
 105 
     | 
    
         
            +
                      new_property.domain += [@current_parent.id]
         
     | 
| 
      
 106 
     | 
    
         
            +
                      # TODO: expand this to handle multiple ranges
         
     | 
| 
      
 107 
     | 
    
         
            +
                      new_property.range = [@new_graph.identifier_to_uri(:Text)]
         
     | 
| 
      
 108 
     | 
    
         
            +
                      new_property.comment = comment
         
     | 
| 
      
 109 
     | 
    
         
            +
                      new_property.many = many
         
     | 
| 
      
 110 
     | 
    
         
            +
                      new_property.required = required
         
     | 
| 
      
 111 
     | 
    
         
            +
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                    def pick_all_properties
         
     | 
| 
      
 114 
     | 
    
         
            +
                      @base_graph.find_type_by_id(@current_parent.id).properties.each do |property|
         
     | 
| 
      
 115 
     | 
    
         
            +
                        identifier = @base_graph.id_to_identifier(property.id)
         
     | 
| 
      
 116 
     | 
    
         
            +
                        apply_property(identifier)
         
     | 
| 
      
 117 
     | 
    
         
            +
                      end
         
     | 
| 
      
 118 
     | 
    
         
            +
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
                  end
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
              end
         
     | 
| 
      
 122 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,138 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rdf/turtle'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Chronicle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Schema
         
     | 
| 
      
 5 
     | 
    
         
            +
                module RDFParsing
         
     | 
| 
      
 6 
     | 
    
         
            +
                  PREFIXES = {
         
     | 
| 
      
 7 
     | 
    
         
            +
                    schemaorg: 'https://schema.org/',
         
     | 
| 
      
 8 
     | 
    
         
            +
                    owl: 'http://www.w3.org/2002/07/owl#',
         
     | 
| 
      
 9 
     | 
    
         
            +
                    dc: 'http://purl.org/dc/terms/',
         
     | 
| 
      
 10 
     | 
    
         
            +
                    rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
         
     | 
| 
      
 11 
     | 
    
         
            +
                    rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
         
     | 
| 
      
 12 
     | 
    
         
            +
                    xml: 'http://www.w3.org/XML/1998/namespace',
         
     | 
| 
      
 13 
     | 
    
         
            +
                    xsd: 'http://www.w3.org/2001/XMLSchema#'
         
     | 
| 
      
 14 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  # Take a graph and serialize it as a ttl string
         
     | 
| 
      
 17 
     | 
    
         
            +
                  class RDFSerializer
         
     | 
| 
      
 18 
     | 
    
         
            +
                    attr_reader :graph
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    def initialize(graph)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      raise ArgumentError, 'graph must be a SchemaGraph' unless graph.is_a?(Chronicle::Schema::SchemaGraph)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      @graph = graph
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    def self.serialize(graph, include_generator_comment: true)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      new(graph).serialize(include_generator_comment:)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    def serialize(include_generator_comment: true)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      schema_graph = RDF::Graph.new
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                      schema_graph << ontology_triple
         
     | 
| 
      
 34 
     | 
    
         
            +
                      schema_graph << version_triple
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                      graph.types.each do |klass|
         
     | 
| 
      
 37 
     | 
    
         
            +
                        serialize_class(klass).each do |triple|
         
     | 
| 
      
 38 
     | 
    
         
            +
                          # binding.pry
         
     | 
| 
      
 39 
     | 
    
         
            +
                          schema_graph << triple
         
     | 
| 
      
 40 
     | 
    
         
            +
                        end
         
     | 
| 
      
 41 
     | 
    
         
            +
                      end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                      graph.properties.each do |property|
         
     | 
| 
      
 44 
     | 
    
         
            +
                        serialize_property(property).each do |triple|
         
     | 
| 
      
 45 
     | 
    
         
            +
                          schema_graph << triple
         
     | 
| 
      
 46 
     | 
    
         
            +
                        end
         
     | 
| 
      
 47 
     | 
    
         
            +
                      end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                      prefixes = {
         
     | 
| 
      
 50 
     | 
    
         
            +
                        '': default_namespace
         
     | 
| 
      
 51 
     | 
    
         
            +
                      }.merge(PREFIXES)
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                      output_str = ''
         
     | 
| 
      
 54 
     | 
    
         
            +
                      output_str += generation_header if include_generator_comment
         
     | 
| 
      
 55 
     | 
    
         
            +
                      output_str + schema_graph.dump(:ttl, prefixes:)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    private
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    def default_namespace
         
     | 
| 
      
 61 
     | 
    
         
            +
                      @graph.default_namespace
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    def generation_header
         
     | 
| 
      
 65 
     | 
    
         
            +
                      <<~TTL
         
     | 
| 
      
 66 
     | 
    
         
            +
                        # This file was generated from schema/chronicle_schema_v#{graph.version}.rb.
         
     | 
| 
      
 67 
     | 
    
         
            +
                        #
         
     | 
| 
      
 68 
     | 
    
         
            +
                        # Do not edit this file directly, as it will be overwritten.
         
     | 
| 
      
 69 
     | 
    
         
            +
                        #
         
     | 
| 
      
 70 
     | 
    
         
            +
                        # To generate a new version, run `rake generate`
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                      TTL
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    def ontology_triple
         
     | 
| 
      
 76 
     | 
    
         
            +
                      RDF::Statement(RDF::URI.new('https://schema.chronicle.app'), RDF.type, RDF::OWL.Ontology)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    def version_triple
         
     | 
| 
      
 80 
     | 
    
         
            +
                      RDF::Statement(RDF::URI.new('https://schema.chronicle.app'), RDF::OWL.versionInfo, graph.version.to_s)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                    def serialize_class(klass)
         
     | 
| 
      
 84 
     | 
    
         
            +
                      statements = []
         
     | 
| 
      
 85 
     | 
    
         
            +
                      statements << RDF::Statement(RDF::URI.new(klass.id), RDF.type, RDF::RDFS.Class)
         
     | 
| 
      
 86 
     | 
    
         
            +
                      statements << RDF::Statement(RDF::URI.new(klass.id), RDF::RDFS.comment, klass.comment) if klass.comment
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                      klass.subtype_ids.each do |subtype_id|
         
     | 
| 
      
 89 
     | 
    
         
            +
                        statements << RDF::Statement(RDF::URI.new(subtype_id), RDF::RDFS.subClassOf, RDF::URI.new(klass.id))
         
     | 
| 
      
 90 
     | 
    
         
            +
                      end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                      if klass.see_also
         
     | 
| 
      
 93 
     | 
    
         
            +
                        statements << RDF::Statement(RDF::URI.new(klass.id), RDF::RDFS.seeAlso, RDF::URI.new(klass.see_also))
         
     | 
| 
      
 94 
     | 
    
         
            +
                      end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                      statements
         
     | 
| 
      
 97 
     | 
    
         
            +
                    end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    def serialize_property(property)
         
     | 
| 
      
 100 
     | 
    
         
            +
                      statements = []
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                      statements << RDF::Statement(RDF::URI.new(property.id), RDF.type, RDF::RDFV.Property)
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                      property.range.each do |range|
         
     | 
| 
      
 105 
     | 
    
         
            +
                        statements << RDF::Statement(RDF::URI.new(property.id), RDF::URI.new("#{@graph.default_namespace}rangeIncludes"),
         
     | 
| 
      
 106 
     | 
    
         
            +
                          RDF::URI.new(range))
         
     | 
| 
      
 107 
     | 
    
         
            +
                      end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                      property.domain.each do |domain|
         
     | 
| 
      
 110 
     | 
    
         
            +
                        statements << RDF::Statement(RDF::URI.new(property.id), RDF::URI.new("#{@graph.default_namespace}domainIncludes"),
         
     | 
| 
      
 111 
     | 
    
         
            +
                          RDF::URI.new(domain))
         
     | 
| 
      
 112 
     | 
    
         
            +
                      end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                      if property.comment
         
     | 
| 
      
 115 
     | 
    
         
            +
                        statements << RDF::Statement(RDF::URI.new(property.id), RDF::RDFS.comment,
         
     | 
| 
      
 116 
     | 
    
         
            +
                          property.comment)
         
     | 
| 
      
 117 
     | 
    
         
            +
                      end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                      if property.see_also
         
     | 
| 
      
 120 
     | 
    
         
            +
                        statements << RDF::Statement(RDF::URI.new(property.id), RDF::RDFS.seeAlso, RDF::URI.new(property.see_also))
         
     | 
| 
      
 121 
     | 
    
         
            +
                      end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                      if property.required?
         
     | 
| 
      
 124 
     | 
    
         
            +
                        statements << RDF::Statement(RDF::URI.new(property.id), RDF::OWL.minCardinality,
         
     | 
| 
      
 125 
     | 
    
         
            +
                          RDF::Literal.new(1, datatype: RDF::XSD.integer))
         
     | 
| 
      
 126 
     | 
    
         
            +
                      end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                      unless property.many
         
     | 
| 
      
 129 
     | 
    
         
            +
                        statements << RDF::Statement(RDF::URI.new(property.id), RDF::OWL.maxCardinality,
         
     | 
| 
      
 130 
     | 
    
         
            +
                          RDF::Literal.new(1, datatype: RDF::XSD.integer))
         
     | 
| 
      
 131 
     | 
    
         
            +
                      end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                      statements
         
     | 
| 
      
 134 
     | 
    
         
            +
                    end
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
              end
         
     | 
| 
      
 138 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'open-uri'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'net/http'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Chronicle
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Schema
         
     | 
| 
      
 6 
     | 
    
         
            +
                module RDFParsing
         
     | 
| 
      
 7 
     | 
    
         
            +
                  module Schemaorg
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @memoized_graphs = {}
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    DEFAULT_NAMESPACE = 'https://schema.org/'.freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    def self.graph_for_version(version)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      @memoized_graphs[version] ||= build_graph(version)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    def self.build_graph(version)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      ttl = ttl_for_version(version)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      Chronicle::Schema::RDFParsing::TTLGraphBuilder.build_from_ttl(ttl, default_namespace: DEFAULT_NAMESPACE)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    def self.ttl_for_version(version)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      url = url_for_version(version)
         
     | 
| 
      
 23 
     | 
    
         
            +
                      ttl_via_download(url)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    def self.ttl_via_download(url)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      uri = URI(url)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      response = Net::HTTP.get_response(uri)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      raise "Error: #{response.message}" unless response.is_a?(Net::HTTPSuccess)
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                      response.body
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    def self.seed_graph_from_file(version, file_path)
         
     | 
| 
      
 35 
     | 
    
         
            +
                      ttl = File.read(file_path)
         
     | 
| 
      
 36 
     | 
    
         
            +
                      graph = Chronicle::Schema::RDFParsing::TTLGraphBuilder.build_from_ttl(ttl,
         
     | 
| 
      
 37 
     | 
    
         
            +
                        default_namespace: DEFAULT_NAMESPACE)
         
     | 
| 
      
 38 
     | 
    
         
            +
                      @memoized_graphs[version] = graph
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    def self.url_for_version(version)
         
     | 
| 
      
 42 
     | 
    
         
            +
                      # Previously just used this one but it's missing ontologies
         
     | 
| 
      
 43 
     | 
    
         
            +
                      # 'https://raw.githubusercontent.com/schemaorg/schemaorg/main/data/schema.ttl'
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                      "https://raw.githubusercontent.com/schemaorg/schemaorg/main/data/releases/#{version}/schemaorg-all-https.ttl"
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,142 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'tsort'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rdf/turtle'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative '../schema_graph'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative '../schema_type'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative '../schema_property'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module Chronicle
         
     | 
| 
      
 8 
     | 
    
         
            +
              module Schema
         
     | 
| 
      
 9 
     | 
    
         
            +
                module RDFParsing
         
     | 
| 
      
 10 
     | 
    
         
            +
                  class TTLGraphBuilder
         
     | 
| 
      
 11 
     | 
    
         
            +
                    attr_reader :ttl_str, :ttl_graph, :graph
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    def initialize(ttl_str, default_namespace: 'https://schema.org/')
         
     | 
| 
      
 14 
     | 
    
         
            +
                      @ttl_str = ttl_str
         
     | 
| 
      
 15 
     | 
    
         
            +
                      @default_namespace = default_namespace
         
     | 
| 
      
 16 
     | 
    
         
            +
                      @graph = Chronicle::Schema::SchemaGraph.new(default_namespace:)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    def build
         
     | 
| 
      
 20 
     | 
    
         
            +
                      reader = RDF::Reader.for(:ttl).new(@ttl_str)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      @ttl_graph = RDF::Graph.new << reader
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      @graph.version = get_version
         
     | 
| 
      
 24 
     | 
    
         
            +
                      @graph.types = build_type_graph
         
     | 
| 
      
 25 
     | 
    
         
            +
                      @graph.properties = build_property_graph
         
     | 
| 
      
 26 
     | 
    
         
            +
                      # build_datatype_graph
         
     | 
| 
      
 27 
     | 
    
         
            +
                      @graph.build_references!
         
     | 
| 
      
 28 
     | 
    
         
            +
                      @graph
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    def self.build_from_file(file_path, default_namespace:)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      new(File.read(file_path), default_namespace:).build
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    def self.build_from_ttl(ttl_str, default_namespace:)
         
     | 
| 
      
 36 
     | 
    
         
            +
                      new(ttl_str, default_namespace:).build
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    private
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    # TODO: make this use proper schema
         
     | 
| 
      
 42 
     | 
    
         
            +
                    def get_version
         
     | 
| 
      
 43 
     | 
    
         
            +
                      @ttl_graph.query([nil, RDF::OWL.versionInfo, nil]).map(&:object).map(&:to_s).first
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    def build_type_graph
         
     | 
| 
      
 47 
     | 
    
         
            +
                      types = all_types.map do |type_id|
         
     | 
| 
      
 48 
     | 
    
         
            +
                        comment = comment_of_class(type_id)
         
     | 
| 
      
 49 
     | 
    
         
            +
                        Chronicle::Schema::SchemaType.new(type_id) do |t|
         
     | 
| 
      
 50 
     | 
    
         
            +
                          t.comment = comment
         
     | 
| 
      
 51 
     | 
    
         
            +
                          t.namespace = @default_namespace
         
     | 
| 
      
 52 
     | 
    
         
            +
                          t.see_also = see_also(type_id)
         
     | 
| 
      
 53 
     | 
    
         
            +
                        end
         
     | 
| 
      
 54 
     | 
    
         
            +
                      end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                      types.each do |schema_type|
         
     | 
| 
      
 57 
     | 
    
         
            +
                        schema_type.subtype_ids = subtypes_of_class(schema_type.id)
         
     | 
| 
      
 58 
     | 
    
         
            +
                      end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                      types
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    def build_property_graph
         
     | 
| 
      
 64 
     | 
    
         
            +
                      all_properties.map do |property_id|
         
     | 
| 
      
 65 
     | 
    
         
            +
                        Chronicle::Schema::SchemaProperty.new(property_id) do |p|
         
     | 
| 
      
 66 
     | 
    
         
            +
                          p.range = range_of_property(property_id)
         
     | 
| 
      
 67 
     | 
    
         
            +
                          p.domain = domain_of_property(property_id)
         
     | 
| 
      
 68 
     | 
    
         
            +
                          p.comment = comment_of_property(property_id)
         
     | 
| 
      
 69 
     | 
    
         
            +
                          p.required = property_required?(property_id)
         
     | 
| 
      
 70 
     | 
    
         
            +
                          p.many = property_many?(property_id)
         
     | 
| 
      
 71 
     | 
    
         
            +
                          p.namespace = @default_namespace
         
     | 
| 
      
 72 
     | 
    
         
            +
                          p.see_also = see_also(property_id)
         
     | 
| 
      
 73 
     | 
    
         
            +
                        end
         
     | 
| 
      
 74 
     | 
    
         
            +
                      end
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    def all_types
         
     | 
| 
      
 78 
     | 
    
         
            +
                      @ttl_graph.query([nil, RDF.type, RDF::RDFS.Class])
         
     | 
| 
      
 79 
     | 
    
         
            +
                        .map(&:subject)
         
     | 
| 
      
 80 
     | 
    
         
            +
                        .map(&:to_s)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                    def all_properties
         
     | 
| 
      
 84 
     | 
    
         
            +
                      @ttl_graph.query([nil, RDF.type, RDF::RDFV.Property])
         
     | 
| 
      
 85 
     | 
    
         
            +
                        .map(&:subject)
         
     | 
| 
      
 86 
     | 
    
         
            +
                        .map(&:to_s)
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    def all_datatypes; end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                    def subtypes_of_class(type_id)
         
     | 
| 
      
 92 
     | 
    
         
            +
                      @ttl_graph.query([nil, RDF::RDFS.subClassOf, RDF::URI.new(type_id)]).map(&:subject).map(&:to_s)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    def parents_of_class(type_id)
         
     | 
| 
      
 96 
     | 
    
         
            +
                      @ttl_graph.query([RDF::URI.new(type_id), RDF::RDFS.subClassOf, nil]).map(&:object).map(&:to_s)
         
     | 
| 
      
 97 
     | 
    
         
            +
                    end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    def comment_of_class(type_id)
         
     | 
| 
      
 100 
     | 
    
         
            +
                      @ttl_graph.query([RDF::URI.new(type_id), RDF::RDFS.comment, nil]).map(&:object).map(&:to_s).first
         
     | 
| 
      
 101 
     | 
    
         
            +
                    end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                    def comment_of_property(property_id)
         
     | 
| 
      
 104 
     | 
    
         
            +
                      @ttl_graph.query([RDF::URI.new(property_id), RDF::RDFS.comment, nil]).map(&:object).map(&:to_s).first
         
     | 
| 
      
 105 
     | 
    
         
            +
                    end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                    def range_of_property(property_id)
         
     | 
| 
      
 108 
     | 
    
         
            +
                      @ttl_graph.query([
         
     | 
| 
      
 109 
     | 
    
         
            +
                                         RDF::URI.new(property_id),
         
     | 
| 
      
 110 
     | 
    
         
            +
                                         RDF::URI.new("#{@default_namespace}rangeIncludes"),
         
     | 
| 
      
 111 
     | 
    
         
            +
                                         nil
         
     | 
| 
      
 112 
     | 
    
         
            +
                                       ]).map(&:object).map(&:to_s)
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    def domain_of_property(property_id)
         
     | 
| 
      
 116 
     | 
    
         
            +
                      @ttl_graph.query([
         
     | 
| 
      
 117 
     | 
    
         
            +
                                         RDF::URI.new(property_id),
         
     | 
| 
      
 118 
     | 
    
         
            +
                                         RDF::URI.new("#{@default_namespace}domainIncludes"),
         
     | 
| 
      
 119 
     | 
    
         
            +
                                         nil
         
     | 
| 
      
 120 
     | 
    
         
            +
                                       ]).map(&:object).map(&:to_s)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                    def property_required?(property_id)
         
     | 
| 
      
 124 
     | 
    
         
            +
                      min_cardinalities = @ttl_graph.query([RDF::URI.new(property_id), RDF::OWL.minCardinality,
         
     | 
| 
      
 125 
     | 
    
         
            +
                                                            nil]).map(&:object).map(&:to_i)
         
     | 
| 
      
 126 
     | 
    
         
            +
                      min_cardinalities.empty? ? false : min_cardinalities.first.positive?
         
     | 
| 
      
 127 
     | 
    
         
            +
                    end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                    def property_many?(property_id)
         
     | 
| 
      
 130 
     | 
    
         
            +
                      max_cardinalities = @ttl_graph.query([RDF::URI.new(property_id), RDF::OWL.maxCardinality,
         
     | 
| 
      
 131 
     | 
    
         
            +
                                                            nil]).map(&:object)
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                      max_cardinalities.empty? ? true : max_cardinalities.map(&:to_i).first > 1
         
     | 
| 
      
 134 
     | 
    
         
            +
                    end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                    def see_also(id)
         
     | 
| 
      
 137 
     | 
    
         
            +
                      @ttl_graph.query([RDF::URI.new(id), RDF::RDFS.seeAlso, nil]).map(&:object).map(&:to_s).first
         
     | 
| 
      
 138 
     | 
    
         
            +
                    end
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
              end
         
     | 
| 
      
 142 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Chronicle
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Schema
         
     | 
| 
      
 3 
     | 
    
         
            +
                module RDFParsing
         
     | 
| 
      
 4 
     | 
    
         
            +
                end
         
     | 
| 
      
 5 
     | 
    
         
            +
              end
         
     | 
| 
      
 6 
     | 
    
         
            +
            end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require_relative 'rdf_parsing/ttl_graph_builder'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require_relative 'rdf_parsing/graph_transformer'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require_relative 'rdf_parsing/rdf_serializer'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require_relative 'rdf_parsing/schemaorg'
         
     | 
| 
         @@ -0,0 +1,145 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'tsort'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Chronicle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Schema
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Represents a RDF graph as a DAG of types and their properties, built
         
     | 
| 
      
 6 
     | 
    
         
            +
                # from a TTL string
         
     | 
| 
      
 7 
     | 
    
         
            +
                class SchemaGraph
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include TSort
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  attr_accessor :types, :properties, :datatypes, :version, :default_namespace
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def initialize(default_namespace: 'https://schema.chronicle.app/')
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @default_namespace = default_namespace
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @types = []
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @properties = []
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @datatypes = []
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def self.build_from_json(json)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    graph = new
         
     | 
| 
      
 21 
     | 
    
         
            +
                    graph.version = json['version']
         
     | 
| 
      
 22 
     | 
    
         
            +
                    json['types'].each do |type_data|
         
     | 
| 
      
 23 
     | 
    
         
            +
                      id = graph.id_to_identifier(type_data['id'])
         
     | 
| 
      
 24 
     | 
    
         
            +
                      graph.add_type(id).tap do |klass|
         
     | 
| 
      
 25 
     | 
    
         
            +
                        klass.comment = type_data['comment']
         
     | 
| 
      
 26 
     | 
    
         
            +
                        klass.subtype_ids = type_data['subtype_ids']
         
     | 
| 
      
 27 
     | 
    
         
            +
                      end
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                    json['properties'].each do |property_data|
         
     | 
| 
      
 30 
     | 
    
         
            +
                      id = graph.id_to_identifier(property_data['id'])
         
     | 
| 
      
 31 
     | 
    
         
            +
                      graph.add_property(id).tap do |property|
         
     | 
| 
      
 32 
     | 
    
         
            +
                        property.comment = property_data['comment']
         
     | 
| 
      
 33 
     | 
    
         
            +
                        property.domain = property_data['domain']
         
     | 
| 
      
 34 
     | 
    
         
            +
                        property.range = property_data['range']
         
     | 
| 
      
 35 
     | 
    
         
            +
                        property.many = property_data['many']
         
     | 
| 
      
 36 
     | 
    
         
            +
                        property.required = property_data['required']
         
     | 
| 
      
 37 
     | 
    
         
            +
                      end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                    graph.build_references!
         
     | 
| 
      
 40 
     | 
    
         
            +
                    graph
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def pretty_print(pp)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    pp.text('SchemaGraph')
         
     | 
| 
      
 45 
     | 
    
         
            +
                    pp.nest(2) do
         
     | 
| 
      
 46 
     | 
    
         
            +
                      pp.breakable
         
     | 
| 
      
 47 
     | 
    
         
            +
                      pp.text("Num types: #{types.size}")
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def inspect
         
     | 
| 
      
 52 
     | 
    
         
            +
                    "#<SchemaGraph:#{object_id}>"
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def to_h
         
     | 
| 
      
 56 
     | 
    
         
            +
                    {
         
     | 
| 
      
 57 
     | 
    
         
            +
                      version: @version,
         
     | 
| 
      
 58 
     | 
    
         
            +
                      types: types.map(&:to_h),
         
     | 
| 
      
 59 
     | 
    
         
            +
                      properties: properties.map(&:to_h)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    }
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def build_references!
         
     | 
| 
      
 64 
     | 
    
         
            +
                    @types.each do |klass|
         
     | 
| 
      
 65 
     | 
    
         
            +
                      klass.subtypes = klass.subtype_ids.map { |id| find_type_by_id(id) }
         
     | 
| 
      
 66 
     | 
    
         
            +
                      klass.supertypes = @types.select { |c| c.subtype_ids.include?(klass.id) }
         
     | 
| 
      
 67 
     | 
    
         
            +
                    end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    @properties.each do |property|
         
     | 
| 
      
 70 
     | 
    
         
            +
                      property.domain.each do |type_id|
         
     | 
| 
      
 71 
     | 
    
         
            +
                        klass = find_type_by_id(type_id)
         
     | 
| 
      
 72 
     | 
    
         
            +
                        klass.properties << property if klass
         
     | 
| 
      
 73 
     | 
    
         
            +
                      end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                      # prune unknown range values from property
         
     | 
| 
      
 76 
     | 
    
         
            +
                      property.range = property.range.select do |range|
         
     | 
| 
      
 77 
     | 
    
         
            +
                        find_type_by_id(range)
         
     | 
| 
      
 78 
     | 
    
         
            +
                      end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                      property.range_types = property.range.map { |id| find_type_by_id(id) }
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  def find_type_by_id(id)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    @types.find { |c| c.id == id }
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  def find_type(identifier)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @types.find { |c| c.id == identifier_to_uri(identifier) }
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  def find_property(identifier)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    @properties.find { |p| p.id == identifier_to_uri(identifier) }
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                  def find_property_by_id(id)
         
     | 
| 
      
 97 
     | 
    
         
            +
                    @properties.find { |p| p.id == id }
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                  def add_type(identifier)
         
     | 
| 
      
 101 
     | 
    
         
            +
                    find_type(identifier) || add_new_type(identifier)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                  def add_property(identifier)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    find_property(identifier) || add_new_property(identifier)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  def id_to_identifier(id)
         
     | 
| 
      
 109 
     | 
    
         
            +
                    id.gsub(@default_namespace, '')
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                  def identifier_to_uri(identifier)
         
     | 
| 
      
 113 
     | 
    
         
            +
                    "#{@default_namespace}#{identifier}"
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  private
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  def add_new_type(identifier)
         
     | 
| 
      
 119 
     | 
    
         
            +
                    new_type = SchemaType.new(identifier_to_uri(identifier)) do |t|
         
     | 
| 
      
 120 
     | 
    
         
            +
                      t.namespace = @default_namespace
         
     | 
| 
      
 121 
     | 
    
         
            +
                    end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                    @types << new_type unless @types.include?(new_type)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    new_type
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  def add_new_property(identifier)
         
     | 
| 
      
 128 
     | 
    
         
            +
                    new_property = SchemaProperty.new(identifier_to_uri(identifier))
         
     | 
| 
      
 129 
     | 
    
         
            +
                    @properties << new_property unless @properties.include?(new_property)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    new_property
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                  def tsort_each_node(&block)
         
     | 
| 
      
 134 
     | 
    
         
            +
                    @types.each do |node|
         
     | 
| 
      
 135 
     | 
    
         
            +
                      block.call(node)
         
     | 
| 
      
 136 
     | 
    
         
            +
                    end
         
     | 
| 
      
 137 
     | 
    
         
            +
                  end
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                  def tsort_each_child(node, &)
         
     | 
| 
      
 140 
     | 
    
         
            +
                    puts "tsort_each_child called with node: #{node&.id}"
         
     | 
| 
      
 141 
     | 
    
         
            +
                    node&.subtypes&.each(&)
         
     | 
| 
      
 142 
     | 
    
         
            +
                  end
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
              end
         
     | 
| 
      
 145 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,81 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Chronicle
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Schema
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Represents a property in the RDF graph
         
     | 
| 
      
 4 
     | 
    
         
            +
                class SchemaProperty
         
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_reader :id
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_accessor :domain,
         
     | 
| 
      
 7 
     | 
    
         
            +
                    :range,
         
     | 
| 
      
 8 
     | 
    
         
            +
                    :comment,
         
     | 
| 
      
 9 
     | 
    
         
            +
                    :many,
         
     | 
| 
      
 10 
     | 
    
         
            +
                    :required,
         
     | 
| 
      
 11 
     | 
    
         
            +
                    :namespace,
         
     | 
| 
      
 12 
     | 
    
         
            +
                    :range_types, # FIXME
         
     | 
| 
      
 13 
     | 
    
         
            +
                    :see_also
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def initialize(id)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @id = id
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @domain = []
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @range = []
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @many = false
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @required = false
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    yield self if block_given?
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def pretty_print(pp)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    pp.text("SchemaProperty: #{id}")
         
     | 
| 
      
 27 
     | 
    
         
            +
                    pp.nest(2) do
         
     | 
| 
      
 28 
     | 
    
         
            +
                      pp.breakable
         
     | 
| 
      
 29 
     | 
    
         
            +
                      pp.text("Range: #{range}")
         
     | 
| 
      
 30 
     | 
    
         
            +
                      pp.breakable
         
     | 
| 
      
 31 
     | 
    
         
            +
                      pp.text("Domain: #{domain}")
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def to_h
         
     | 
| 
      
 36 
     | 
    
         
            +
                    output = {
         
     | 
| 
      
 37 
     | 
    
         
            +
                      id:,
         
     | 
| 
      
 38 
     | 
    
         
            +
                      domain:,
         
     | 
| 
      
 39 
     | 
    
         
            +
                      range:,
         
     | 
| 
      
 40 
     | 
    
         
            +
                      many: @many,
         
     | 
| 
      
 41 
     | 
    
         
            +
                      required: @required
         
     | 
| 
      
 42 
     | 
    
         
            +
                    }
         
     | 
| 
      
 43 
     | 
    
         
            +
                    output[:comment] = @comment if @comment
         
     | 
| 
      
 44 
     | 
    
         
            +
                    output[:see_also] = @see_also if @see_also
         
     | 
| 
      
 45 
     | 
    
         
            +
                    output
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  def ==(other)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    id == other.id
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  def required?
         
     | 
| 
      
 53 
     | 
    
         
            +
                    @required
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  def many?
         
     | 
| 
      
 57 
     | 
    
         
            +
                    @many
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def identifier
         
     | 
| 
      
 61 
     | 
    
         
            +
                    @id.split('/').last&.to_sym
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  # FIXME
         
     | 
| 
      
 65 
     | 
    
         
            +
                  def id_snakecase
         
     | 
| 
      
 66 
     | 
    
         
            +
                    @id.split('/').last.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase.to_sym
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  # FIXME: refactor this and the next
         
     | 
| 
      
 70 
     | 
    
         
            +
                  def range_identifiers
         
     | 
| 
      
 71 
     | 
    
         
            +
                    range.map do |r|
         
     | 
| 
      
 72 
     | 
    
         
            +
                      r.split('/').last&.to_sym
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  def full_range_identifiers
         
     | 
| 
      
 77 
     | 
    
         
            +
                    range_types.map(&:descendants).flatten.map { |x| x.id.split('/').last&.to_sym } + range_identifiers
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
            end
         
     |