Module: Causalontology::Canonical

Defined in:
lib/causalontology/canonical.rb

Constant Summary collapse

IDENTITY_FIELDS =

The identity-bearing fields of each of the twenty-one kinds (3.0.0 adds the cross_stratal_seam; the conduit gains realized_by; 4.0.0 adds the attitude, the predicted_occurrence, and the prediction_error - all additive and identity-preserving - a record that omits a new field keeps its earlier identifier byte-for-byte, and the new kinds open new identity schemes that disturb no existing record). "type" is always injected, so it is not listed here. Order does not matter (JCS sorts keys).

{
  # ---- type tier ----
  "occurrent"  => ["label", "category", "stratum"],
  "causal_relation_object" => ["causes", "effects", "mechanism", "temporal",
                               "modality", "context", "refines", "skips"],
  "continuant" => ["label", "category"],
  "realizable" => ["kind", "bearer", "label"],
  "stratum"    => ["label", "scheme", "ordinal", "unit", "governs"],
  "bridge"     => ["coarse", "fine", "relation"],
  "cross_stratal_seam" => ["source", "target", "mechanism_status", "chain"],
  "port"       => ["bearer", "label", "direction", "accepts", "realizable"],
  "conduit"    => ["label", "from", "to", "carries", "transform",
                   "realized_by"],
  "quality"    => ["label", "datatype", "unit", "stratum"],
  # ---- token tier ----
  "token_individual"   => ["instantiates", "designator", "part_of"],
  "token_occurrence"   => ["instantiates", "interval", "participants",
                           "locus", "observer"],
  "state_assertion"    => ["subject", "quality", "value", "interval"],
  "token_causal_claim" => ["causes", "effects", "covering_law",
                           "actual_delay", "counterfactual"],
  "attitude"             => ["holder", "attitude_type", "content"],
  "predicted_occurrence" => ["instantiates", "interval", "predictor",
                             "strength"],
  "prediction_error"     => ["predicted", "observed", "discrepancy"],
  # ---- provenance tier ----
  "assertion"  => ["about", "source", "evidence_type", "evidence", "strength",
                   "confidence", "timestamp", "evidenced_by"],
  "enrichment" => ["about", "field", "entry", "source", "timestamp"],
  "retraction" => ["retracts", "source", "timestamp"],
  "succession" => ["predecessor", "successor", "timestamp"],
}.freeze
PREFIX =

Whole-word re-mint (P7): the scheme IS the type value for every kind.

IDENTITY_FIELDS.keys.each_with_object({}) { |k, h| h[k] = k }.freeze
KIND_OF_PREFIX =
PREFIX.invert.freeze

Class Method Summary collapse

Class Method Details

.canonicalize(obj, kind = nil) ⇒ Object

The RFC 8785 identity-bearing bytes of an object (a UTF-8 string).



98
99
100
101
# File 'lib/causalontology/canonical.rb', line 98

def canonicalize(obj, kind = nil)
  _, ib = identity_bearing(obj, kind)
  Jcs.encode(ib).encode(Encoding::UTF_8)
end

.identify(obj, kind = nil) ⇒ Object

The content-addressed identifier: scheme + ":" + SHA-256 hex.



104
105
106
107
108
# File 'lib/causalontology/canonical.rb', line 104

def identify(obj, kind = nil)
  kind, ib = identity_bearing(obj, kind)
  digest = Digest::SHA256.hexdigest(Jcs.encode(ib).encode(Encoding::UTF_8))
  PREFIX[kind] + ":" + digest
end

.identity_bearing(obj, kind = nil) ⇒ Object

The identity-bearing subset of an object, with type always present. Returns [kind, subset].



85
86
87
88
89
90
91
92
93
94
95
# File 'lib/causalontology/canonical.rb', line 85

def identity_bearing(obj, kind = nil)
  kind ||= infer_kind(obj)
  unless IDENTITY_FIELDS.key?(kind)
    raise ArgumentError, "unknown kind: #{kind.inspect}"
  end
  out = { "type" => kind }
  IDENTITY_FIELDS[kind].each do |field|
    out[field] = obj[field] if obj.key?(field)
  end
  [kind, out]
end

.infer_kind(obj) ⇒ Object

Infer an object's kind from its type field, id prefix, or shape.

Raises:

  • (ArgumentError)


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/causalontology/canonical.rb', line 64

def infer_kind(obj)
  return obj["type"] if obj.key?("type")
  if obj.key?("id") && obj["id"].is_a?(String) && obj["id"].include?(":")
    pre = obj["id"].split(":", 2)[0]
    return KIND_OF_PREFIX[pre] if KIND_OF_PREFIX.key?(pre)
  end
  return "bridge" if obj.key?("coarse") && obj.key?("fine")
  return "causal_relation_object" if obj.key?("causes") && obj.key?("effects")
  return "retraction" if obj.key?("retracts")
  return "succession" if obj.key?("predecessor") && obj.key?("successor")
  return "enrichment" if obj.key?("field") && obj.key?("entry")
  return "assertion" if obj.key?("evidence_type") ||
                        (obj.key?("about") && obj.key?("confidence"))
  return "realizable" if obj.key?("kind") && obj.key?("bearer")
  raise ArgumentError,
        "cannot infer kind (occurrents and continuants share a shape); " \
        "pass kind explicitly"
end