lutaml-model 0.8.3 → 0.8.5
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/dependent-tests.yml +3 -1
- data/.rubocop.yml +18 -0
- data/.rubocop_todo.yml +16 -22
- data/Gemfile +2 -0
- data/README.adoc +327 -3
- data/docs/_guides/document-validation.adoc +303 -0
- data/docs/_guides/index.adoc +19 -0
- data/docs/_guides/jsonld-serialization.adoc +217 -0
- data/docs/_guides/rdf-serialization.adoc +344 -0
- data/docs/_guides/turtle-serialization.adoc +224 -0
- data/docs/_guides/xml-mapping.adoc +9 -1
- data/docs/_guides/xml_mappings/07_best_practices.adoc +36 -0
- data/docs/_guides/xml_mappings/08_troubleshooting.adoc +89 -0
- data/docs/_pages/serialization_adapters.adoc +31 -0
- data/docs/_references/index.adoc +1 -0
- data/docs/_references/rdf-namespaces.adoc +243 -0
- data/docs/_tutorials/lutaml-xml-architecture.adoc +6 -1
- data/docs/index.adoc +3 -2
- data/lib/lutaml/jsonld/adapter.rb +23 -0
- data/lib/lutaml/jsonld/context.rb +69 -0
- data/lib/lutaml/jsonld/term_definition.rb +39 -0
- data/lib/lutaml/jsonld/transform.rb +174 -0
- data/lib/lutaml/jsonld.rb +23 -0
- data/lib/lutaml/model/attribute.rb +19 -1
- data/lib/lutaml/model/error/liquid_drop_already_registered_error.rb +11 -0
- data/lib/lutaml/model/error/ordered_content_mapping_error.rb +17 -0
- data/lib/lutaml/model/format_registry.rb +10 -1
- data/lib/lutaml/model/global_context.rb +1 -0
- data/lib/lutaml/model/liquefiable.rb +12 -15
- data/lib/lutaml/model/mapping/mapping_rule.rb +10 -2
- data/lib/lutaml/model/mapping_hash.rb +1 -1
- data/lib/lutaml/model/serialize/format_conversion.rb +17 -1
- data/lib/lutaml/model/services/transformer.rb +67 -32
- data/lib/lutaml/model/transform.rb +41 -4
- data/lib/lutaml/model/uninitialized_class.rb +11 -5
- data/lib/lutaml/model/validation/concerns/has_issues.rb +27 -0
- data/lib/lutaml/model/validation/context.rb +36 -0
- data/lib/lutaml/model/validation/issue.rb +62 -0
- data/lib/lutaml/model/validation/layer_result.rb +34 -0
- data/lib/lutaml/model/validation/profile.rb +66 -0
- data/lib/lutaml/model/validation/registry.rb +60 -0
- data/lib/lutaml/model/validation/remediation.rb +33 -0
- data/lib/lutaml/model/validation/remediation_result.rb +20 -0
- data/lib/lutaml/model/validation/report.rb +39 -0
- data/lib/lutaml/model/validation/rule.rb +59 -0
- data/lib/lutaml/model/validation.rb +2 -1
- data/lib/lutaml/model/validation_framework.rb +77 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +10 -0
- data/lib/lutaml/rdf/error.rb +7 -0
- data/lib/lutaml/rdf/iri.rb +44 -0
- data/lib/lutaml/rdf/language_tagged.rb +11 -0
- data/lib/lutaml/rdf/literal.rb +62 -0
- data/lib/lutaml/rdf/mapping.rb +71 -0
- data/lib/lutaml/rdf/mapping_rule.rb +35 -0
- data/lib/lutaml/rdf/member_rule.rb +13 -0
- data/lib/lutaml/rdf/namespace.rb +58 -0
- data/lib/lutaml/rdf/namespace_set.rb +69 -0
- data/lib/lutaml/rdf/namespaces/dcterms_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/owl_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/rdf_namespace.rb +14 -0
- data/lib/lutaml/rdf/namespaces/rdfs_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/skos_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/xsd_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces.rb +14 -0
- data/lib/lutaml/rdf/transform.rb +36 -0
- data/lib/lutaml/rdf.rb +19 -0
- data/lib/lutaml/turtle/adapter.rb +35 -0
- data/lib/lutaml/turtle/mapping.rb +7 -0
- data/lib/lutaml/turtle/transform.rb +158 -0
- data/lib/lutaml/turtle.rb +22 -0
- data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +9 -2
- data/lib/lutaml/xml/adapter/oga_adapter.rb +11 -3
- data/lib/lutaml/xml/adapter/ox_adapter.rb +5 -2
- data/lib/lutaml/xml/adapter/rexml_adapter.rb +10 -3
- data/lib/lutaml/xml/adapter_element.rb +26 -2
- data/lib/lutaml/xml/data_model.rb +14 -0
- data/lib/lutaml/xml/document.rb +3 -0
- data/lib/lutaml/xml/element.rb +8 -2
- data/lib/lutaml/xml/mapping.rb +9 -0
- data/lib/lutaml/xml/model_transform.rb +42 -0
- data/lib/lutaml/xml/schema/xsd/base.rb +4 -1
- data/lib/lutaml/xml/serialization/instance_methods.rb +3 -1
- data/lib/lutaml/xml/transformation/ordered_applier.rb +46 -2
- data/lib/lutaml/xml/transformation.rb +40 -1
- data/lib/lutaml/xml/xml_element.rb +8 -7
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/integration/edge_cases_spec.rb +109 -0
- data/spec/lutaml/integration/multi_format_spec.rb +106 -0
- data/spec/lutaml/integration/round_trip_spec.rb +170 -0
- data/spec/lutaml/jsonld/adapter_spec.rb +46 -0
- data/spec/lutaml/jsonld/context_spec.rb +114 -0
- data/spec/lutaml/jsonld/term_definition_spec.rb +55 -0
- data/spec/lutaml/jsonld/transform_spec.rb +211 -0
- data/spec/lutaml/model/attribute_default_cache_spec.rb +58 -0
- data/spec/lutaml/model/liquefiable_spec.rb +22 -6
- data/spec/lutaml/model/liquid_compatibility_spec.rb +442 -0
- data/spec/lutaml/model/ordered_content_spec.rb +5 -5
- data/spec/lutaml/model/services/transformer_spec.rb +43 -0
- data/spec/lutaml/model/transform_cache_spec.rb +62 -0
- data/spec/lutaml/model/transform_dynamic_attributes_spec.rb +41 -0
- data/spec/lutaml/model/uninitialized_class_deep_dup_spec.rb +39 -0
- data/spec/lutaml/model/uninitialized_class_spec.rb +14 -2
- data/spec/lutaml/model/validation/concerns/has_issues_spec.rb +76 -0
- data/spec/lutaml/model/validation/context_spec.rb +60 -0
- data/spec/lutaml/model/validation/issue_spec.rb +77 -0
- data/spec/lutaml/model/validation/layer_result_spec.rb +66 -0
- data/spec/lutaml/model/validation/profile_spec.rb +134 -0
- data/spec/lutaml/model/validation/registry_spec.rb +94 -0
- data/spec/lutaml/model/validation/remediation_result_spec.rb +23 -0
- data/spec/lutaml/model/validation/remediation_spec.rb +72 -0
- data/spec/lutaml/model/validation/report_spec.rb +58 -0
- data/spec/lutaml/model/validation/rule_spec.rb +134 -0
- data/spec/lutaml/model/validation/uninitialized_class_validate_spec.rb +29 -0
- data/spec/lutaml/model/validation/validation_error_spec.rb +29 -0
- data/spec/lutaml/model/validation/validation_framework_spec.rb +110 -0
- data/spec/lutaml/rdf/graph_serialization_spec.rb +137 -0
- data/spec/lutaml/rdf/iri_spec.rb +73 -0
- data/spec/lutaml/rdf/literal_spec.rb +98 -0
- data/spec/lutaml/rdf/mapping_spec.rb +164 -0
- data/spec/lutaml/rdf/member_rule_spec.rb +17 -0
- data/spec/lutaml/rdf/namespace_set_spec.rb +115 -0
- data/spec/lutaml/rdf/namespace_spec.rb +241 -0
- data/spec/lutaml/rdf/rdf_transform_spec.rb +82 -0
- data/spec/lutaml/turtle/adapter_spec.rb +47 -0
- data/spec/lutaml/turtle/mapping_spec.rb +123 -0
- data/spec/lutaml/turtle/transform_spec.rb +273 -0
- data/spec/lutaml/xml/content_model_validation_spec.rb +157 -0
- data/spec/lutaml/xml/mapping_spec.rb +12 -7
- metadata +95 -7
- data/spec/fixtures/liquid_templates/_ceramics.liquid +0 -3
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "lutaml/jsonld"
|
|
5
|
+
|
|
6
|
+
RSpec.describe Lutaml::JsonLd::Transform do
|
|
7
|
+
before do
|
|
8
|
+
stub_const("TestSkosNs", Class.new(Lutaml::Rdf::Namespace) do
|
|
9
|
+
uri "http://www.w3.org/2004/02/skos/core#"
|
|
10
|
+
prefix "skos"
|
|
11
|
+
end)
|
|
12
|
+
|
|
13
|
+
stub_const("TestExNs", Class.new(Lutaml::Rdf::Namespace) do
|
|
14
|
+
uri "http://example.org/"
|
|
15
|
+
prefix "ex"
|
|
16
|
+
end)
|
|
17
|
+
stub_const("JsonLdTestModel", Class.new(Lutaml::Model::Serializable) do
|
|
18
|
+
attribute :name, :string
|
|
19
|
+
attribute :description, :string
|
|
20
|
+
|
|
21
|
+
rdf do
|
|
22
|
+
namespace TestSkosNs, TestExNs
|
|
23
|
+
|
|
24
|
+
subject { |m| "http://example.org/#{m.name}" } # rubocop:disable RSpec/NamedSubject
|
|
25
|
+
|
|
26
|
+
type "skos:Concept"
|
|
27
|
+
|
|
28
|
+
predicate :name, namespace: TestExNs, to: :name
|
|
29
|
+
predicate :description, namespace: TestExNs, to: :description
|
|
30
|
+
end
|
|
31
|
+
end)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
let(:instance) do
|
|
35
|
+
JsonLdTestModel.new(name: "test", description: "A test concept")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "model_to_data" do
|
|
39
|
+
let(:result) { instance.to_jsonld }
|
|
40
|
+
|
|
41
|
+
it "generates @context with namespace prefixes" do
|
|
42
|
+
parsed = JSON.parse(result)
|
|
43
|
+
expect(parsed).to have_key("@context")
|
|
44
|
+
expect(parsed["@context"]["skos"]).to eq("http://www.w3.org/2004/02/skos/core#")
|
|
45
|
+
expect(parsed["@context"]["ex"]).to eq("http://example.org/")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "generates @type as compact IRI" do
|
|
49
|
+
parsed = JSON.parse(result)
|
|
50
|
+
expect(parsed["@type"]).to eq("skos:Concept")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "generates @id from subject block" do
|
|
54
|
+
parsed = JSON.parse(result)
|
|
55
|
+
expect(parsed["@id"]).to eq("http://example.org/test")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "includes predicate data" do
|
|
59
|
+
parsed = JSON.parse(result)
|
|
60
|
+
expect(parsed["name"]).to eq("test")
|
|
61
|
+
expect(parsed["description"]).to eq("A test concept")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe "model_to_data without type and subject" do
|
|
66
|
+
before do
|
|
67
|
+
stub_const("MinimalJsonLdModel", Class.new(Lutaml::Model::Serializable) do
|
|
68
|
+
attribute :value, :string
|
|
69
|
+
|
|
70
|
+
rdf do
|
|
71
|
+
namespace TestSkosNs
|
|
72
|
+
|
|
73
|
+
predicate :notation, namespace: TestSkosNs, to: :value
|
|
74
|
+
end
|
|
75
|
+
end)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "omits @type and @id when not defined" do
|
|
79
|
+
instance = MinimalJsonLdModel.new(value: "x")
|
|
80
|
+
parsed = JSON.parse(instance.to_jsonld)
|
|
81
|
+
expect(parsed).not_to have_key("@type")
|
|
82
|
+
expect(parsed).not_to have_key("@id")
|
|
83
|
+
expect(parsed["notation"]).to eq("x")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe "data_to_model" do
|
|
88
|
+
let(:jsonld_input) do
|
|
89
|
+
{
|
|
90
|
+
"@context" => { "ex" => "http://example.org/" },
|
|
91
|
+
"@type" => "skos:Concept",
|
|
92
|
+
"@id" => "http://example.org/test",
|
|
93
|
+
"name" => "from_jsonld",
|
|
94
|
+
"description" => "Loaded from JSON-LD",
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "parses JSON-LD back to model" do
|
|
99
|
+
model = JsonLdTestModel.from_jsonld(JSON.generate(jsonld_input))
|
|
100
|
+
expect(model.name).to eq("from_jsonld")
|
|
101
|
+
expect(model.description).to eq("Loaded from JSON-LD")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "strips JSON-LD keywords before attribute mapping" do
|
|
105
|
+
model = JsonLdTestModel.from_jsonld(JSON.generate(jsonld_input))
|
|
106
|
+
expect(model).to be_a(JsonLdTestModel)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
describe "deserialization ignores JSON-LD keywords" do
|
|
111
|
+
let(:jsonld_input) do
|
|
112
|
+
{
|
|
113
|
+
"@context" => { "ex" => "http://example.org/" },
|
|
114
|
+
"@type" => "skos:Concept",
|
|
115
|
+
"@id" => "http://example.org/test",
|
|
116
|
+
"@graph" => [],
|
|
117
|
+
"name" => "value_only",
|
|
118
|
+
}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "strips all @-prefixed keys" do
|
|
122
|
+
model = JsonLdTestModel.from_jsonld(JSON.generate(jsonld_input))
|
|
123
|
+
expect(model.name).to eq("value_only")
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
describe "round-trip" do
|
|
128
|
+
it "preserves data through model → JSON-LD → model" do
|
|
129
|
+
json = instance.to_jsonld
|
|
130
|
+
restored = JsonLdTestModel.from_jsonld(json)
|
|
131
|
+
expect(restored.name).to eq("test")
|
|
132
|
+
expect(restored.description).to eq("A test concept")
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "produces consistent @context through round-trip" do
|
|
136
|
+
json = instance.to_jsonld
|
|
137
|
+
parsed = JSON.parse(json)
|
|
138
|
+
expect(parsed["@context"]["skos"]).to eq("http://www.w3.org/2004/02/skos/core#")
|
|
139
|
+
expect(parsed["@context"]["ex"]).to eq("http://example.org/")
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe "nil attribute values" do
|
|
144
|
+
let(:instance) { JsonLdTestModel.new(name: "test", description: nil) }
|
|
145
|
+
|
|
146
|
+
it "omits nil values from output" do
|
|
147
|
+
parsed = JSON.parse(instance.to_jsonld)
|
|
148
|
+
expect(parsed["name"]).to eq("test")
|
|
149
|
+
expect(parsed).not_to have_key("description")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe "model with integer and boolean attributes" do
|
|
154
|
+
before do
|
|
155
|
+
stub_const("TypedJsonLdModel", Class.new(Lutaml::Model::Serializable) do
|
|
156
|
+
attribute :label, :string
|
|
157
|
+
attribute :count, :integer
|
|
158
|
+
attribute :active, :boolean
|
|
159
|
+
|
|
160
|
+
rdf do
|
|
161
|
+
namespace TestSkosNs
|
|
162
|
+
|
|
163
|
+
subject { |_| "http://example.org/1" } # rubocop:disable RSpec/NamedSubject
|
|
164
|
+
|
|
165
|
+
type "skos:Concept"
|
|
166
|
+
|
|
167
|
+
predicate :prefLabel, namespace: TestSkosNs, to: :label
|
|
168
|
+
predicate :notation, namespace: TestSkosNs, to: :count
|
|
169
|
+
predicate :note, namespace: TestSkosNs, to: :active
|
|
170
|
+
end
|
|
171
|
+
end)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "serializes and deserializes typed values" do
|
|
175
|
+
instance = TypedJsonLdModel.new(label: "test", count: 42, active: true)
|
|
176
|
+
json = instance.to_jsonld
|
|
177
|
+
parsed = JSON.parse(json)
|
|
178
|
+
expect(parsed["notation"]).to eq(42)
|
|
179
|
+
expect(parsed["note"]).to be(true)
|
|
180
|
+
|
|
181
|
+
restored = TypedJsonLdModel.from_jsonld(json)
|
|
182
|
+
expect(restored.count).to eq(42)
|
|
183
|
+
expect(restored.active).to be(true)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
describe "collection attributes" do
|
|
188
|
+
before do
|
|
189
|
+
stub_const("CollectionJsonLdModel", Class.new(Lutaml::Model::Serializable) do
|
|
190
|
+
attribute :tags, :string, collection: true
|
|
191
|
+
|
|
192
|
+
rdf do
|
|
193
|
+
namespace TestSkosNs
|
|
194
|
+
|
|
195
|
+
subject { |_| "http://example.org/1" } # rubocop:disable RSpec/NamedSubject
|
|
196
|
+
|
|
197
|
+
type "skos:Concept"
|
|
198
|
+
|
|
199
|
+
predicate :notation, namespace: TestSkosNs, to: :tags
|
|
200
|
+
end
|
|
201
|
+
end)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "round-trips collection values" do
|
|
205
|
+
instance = CollectionJsonLdModel.new(tags: ["en", "fr"])
|
|
206
|
+
json = instance.to_jsonld
|
|
207
|
+
restored = CollectionJsonLdModel.from_jsonld(json)
|
|
208
|
+
expect(restored.tags).to eq(["en", "fr"])
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Attribute default caching" do
|
|
6
|
+
describe "immutable defaults" do
|
|
7
|
+
let(:klass) do
|
|
8
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
9
|
+
attribute :name, :string, default: -> { "default_name" }
|
|
10
|
+
attribute :count, :integer, default: -> { 42 }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "returns same default object for immutable types" do
|
|
15
|
+
attr = klass.attributes[:name]
|
|
16
|
+
default1 = attr.default(:default)
|
|
17
|
+
default2 = attr.default(:default)
|
|
18
|
+
expect(default1).to equal(default2)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "mutable defaults" do
|
|
23
|
+
let(:klass) do
|
|
24
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
25
|
+
attribute :prefs, :hash, default: -> { { theme: "dark" } }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "does not share mutable default between calls" do
|
|
30
|
+
attr = klass.attributes[:prefs]
|
|
31
|
+
default1 = attr.default(:default)
|
|
32
|
+
default2 = attr.default(:default)
|
|
33
|
+
|
|
34
|
+
# If cached, they'd be the same object. Mutating one would affect the other.
|
|
35
|
+
# With the immutable_value? guard, Hashes are NOT cached, so each call
|
|
36
|
+
# re-evaluates the default proc, producing independent objects.
|
|
37
|
+
if default1.equal?(default2)
|
|
38
|
+
# If they happen to be the same (cached), mutation should NOT propagate
|
|
39
|
+
pending "mutable default caching needs fixing"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "instance-aware defaults" do
|
|
45
|
+
let(:klass) do
|
|
46
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
47
|
+
attribute :label, :string, default: -> { "default" }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "passes instance_object to default_value when provided" do
|
|
52
|
+
attr = klass.attributes[:label]
|
|
53
|
+
instance = klass.new
|
|
54
|
+
result = attr.default(:default, instance)
|
|
55
|
+
expect(result).to eq("default")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -81,8 +81,10 @@ RSpec.describe Lutaml::Model::Liquefiable do
|
|
|
81
81
|
it "raises an error" do
|
|
82
82
|
expect do
|
|
83
83
|
dummy.class.register_liquid_drop_class
|
|
84
|
-
end.to raise_error(
|
|
85
|
-
|
|
84
|
+
end.to raise_error(
|
|
85
|
+
Lutaml::Model::LiquidDropAlreadyRegisteredError,
|
|
86
|
+
"Liquid drop class 'Drop' is already registered.",
|
|
87
|
+
)
|
|
86
88
|
end
|
|
87
89
|
end
|
|
88
90
|
|
|
@@ -94,8 +96,10 @@ RSpec.describe Lutaml::Model::Liquefiable do
|
|
|
94
96
|
it "raises an error" do
|
|
95
97
|
expect do
|
|
96
98
|
EmptyModel.register_liquid_drop_class
|
|
97
|
-
end.to raise_error(
|
|
98
|
-
|
|
99
|
+
end.to raise_error(
|
|
100
|
+
Lutaml::Model::LiquidDropAlreadyRegisteredError,
|
|
101
|
+
"Liquid drop class 'Drop' is already registered.",
|
|
102
|
+
)
|
|
99
103
|
end
|
|
100
104
|
end
|
|
101
105
|
end
|
|
@@ -254,14 +258,26 @@ RSpec.describe Lutaml::Model::Liquefiable do
|
|
|
254
258
|
end
|
|
255
259
|
|
|
256
260
|
it "renders" do
|
|
261
|
+
# Probe which partial tag the installed Liquid supports (render ≥5, include ≤4)
|
|
262
|
+
partial_tag = begin
|
|
263
|
+
Liquid::Template.parse("{% render 'probe' %}")
|
|
264
|
+
"render"
|
|
265
|
+
rescue Liquid::SyntaxError
|
|
266
|
+
"include"
|
|
267
|
+
end
|
|
268
|
+
|
|
257
269
|
template = Liquid::Template.new
|
|
258
270
|
file_system = Liquid::LocalFileSystem.new(liquid_template_dir)
|
|
259
271
|
template.registers[:file_system] = file_system
|
|
260
|
-
|
|
272
|
+
parent_template = <<~LIQUID
|
|
273
|
+
{% for ceramic in ceramic_collection.ceramics %}
|
|
274
|
+
{% #{partial_tag} 'ceramic' ceramic: ceramic %}
|
|
275
|
+
{%- endfor %}
|
|
276
|
+
LIQUID
|
|
277
|
+
template.parse(parent_template)
|
|
261
278
|
|
|
262
279
|
ceramic_collection = LiquefiableSpec::CeramicCollection.from_yaml(yaml)
|
|
263
280
|
output = template.render("ceramic_collection" => ceramic_collection)
|
|
264
|
-
# puts output
|
|
265
281
|
|
|
266
282
|
expected_output = <<~OUTPUT
|
|
267
283
|
* Name: "Celadon Bowl"
|