lutaml-model 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +4 -5
- data/Gemfile +1 -0
- data/README.adoc +243 -40
- data/lib/lutaml/model/error/liquid_not_enabled_error.rb +9 -0
- data/lib/lutaml/model/error.rb +1 -0
- data/lib/lutaml/model/liquefiable.rb +15 -2
- data/lib/lutaml/model/serialize.rb +0 -2
- data/lib/lutaml/model/version.rb +1 -1
- data/spec/fixtures/liquid_templates/_ceramic.liquid +6 -0
- data/spec/fixtures/liquid_templates/_ceramics.liquid +3 -0
- data/spec/fixtures/liquid_templates/_ceramics_in_one.liquid +4 -0
- data/spec/lutaml/model/liquefiable_spec.rb +152 -14
- data/spec/spec_helper.rb +1 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16b8569b5f2b450dfa8218c1c55a2175f154790220a54536bc9acbf6bc718312
|
4
|
+
data.tar.gz: d9d62e793690a72856e2663a67eb4eeb9cd8b909a861a91b093d8b849674a983
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e491d76d913445b9929b4e1939becc31c4b1ccb656aeaaa3e83caa20bf3c5e53142c3cc45f1a82b0a85d4b7111ab6994d5a5c03588c4f42dd8f76d101c95db0
|
7
|
+
data.tar.gz: e18a44f0047929b93c2bc66c82f8942d3cf48818c6597722bba0b78107c2630aa59f84b3b4034a105155d889e64fa7e8f11881fbc9eca076b3103c21f8f189cf
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2025-02-
|
3
|
+
# on 2025-02-15 02:54:02 UTC using RuboCop version 1.71.2.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -138,7 +138,7 @@ RSpec/DescribedClass:
|
|
138
138
|
Exclude:
|
139
139
|
- 'spec/lutaml/model/xml_mapping_spec.rb'
|
140
140
|
|
141
|
-
# Offense count:
|
141
|
+
# Offense count: 157
|
142
142
|
# Configuration parameters: CountAsOne.
|
143
143
|
RSpec/ExampleLength:
|
144
144
|
Max: 54
|
@@ -149,7 +149,7 @@ RSpec/LeakyConstantDeclaration:
|
|
149
149
|
- 'spec/benchmarks/xml_parsing_benchmark_spec.rb'
|
150
150
|
- 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
|
151
151
|
|
152
|
-
# Offense count:
|
152
|
+
# Offense count: 208
|
153
153
|
RSpec/MultipleExpectations:
|
154
154
|
Max: 14
|
155
155
|
|
@@ -163,13 +163,12 @@ RSpec/MultipleMemoizedHelpers:
|
|
163
163
|
RSpec/NestedGroups:
|
164
164
|
Max: 4
|
165
165
|
|
166
|
-
# Offense count:
|
166
|
+
# Offense count: 5
|
167
167
|
RSpec/PendingWithoutReason:
|
168
168
|
Exclude:
|
169
169
|
- 'spec/lutaml/model/type/date_time_spec.rb'
|
170
170
|
- 'spec/lutaml/model/type/time_spec.rb'
|
171
171
|
- 'spec/lutaml/model/type/time_without_date_spec.rb'
|
172
|
-
- 'spec/lutaml/model/validation_spec.rb'
|
173
172
|
|
174
173
|
# Offense count: 3
|
175
174
|
# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata.
|
data/Gemfile
CHANGED
data/README.adoc
CHANGED
@@ -413,9 +413,11 @@ end
|
|
413
413
|
|
414
414
|
==== Decimal type
|
415
415
|
|
416
|
-
|
416
|
+
WARNING: Decimal is an optional feature.
|
417
417
|
|
418
|
-
|
418
|
+
The Decimal type is a value type that is disabled by default.
|
419
|
+
|
420
|
+
NOTE: The reason why the Decimal type is disabled by default is that the
|
419
421
|
`BigDecimal` class became optional to the standard Ruby library from Ruby 3.4
|
420
422
|
onwards. The `Decimal` type is only enabled when the `bigdecimal` library is
|
421
423
|
loaded.
|
@@ -693,7 +695,7 @@ end
|
|
693
695
|
Lutaml lets you create reusable element and attribute collections using `no_root`. These can be imported into other models using:
|
694
696
|
|
695
697
|
- `import_model`: imports both attributes and mappings
|
696
|
-
- `import_model_attributes`: imports only attributes
|
698
|
+
- `import_model_attributes`: imports only attributes
|
697
699
|
- `import_model_mappings`: imports only mappings
|
698
700
|
|
699
701
|
NOTE: This feature works with XML. Import order determines how elements and attributes are overwritten.
|
@@ -3898,7 +3900,7 @@ class Person < Lutaml::Model::Serializable
|
|
3898
3900
|
}
|
3899
3901
|
end
|
3900
3902
|
|
3901
|
-
# Mapping-level transformation in XML format
|
3903
|
+
# Mapping-level transformation in XML format
|
3902
3904
|
xml do
|
3903
3905
|
map "full-name", to: :name, transform: {
|
3904
3906
|
export: ->(value) { "Dr. #{value}" },
|
@@ -4098,39 +4100,39 @@ NOTE: For `NokogiriAdapter`, we can also call `to_xml` on `value.node.adapter_no
|
|
4098
4100
|
|
4099
4101
|
# Nokogiri Adapter Node
|
4100
4102
|
|
4101
|
-
#<Lutaml::Model::XmlAdapter::NokogiriElement:0x0000000107656ed8
|
4102
|
-
# @attributes={},
|
4103
|
-
# @children=
|
4103
|
+
#<Lutaml::Model::XmlAdapter::NokogiriElement:0x0000000107656ed8
|
4104
|
+
# @attributes={},
|
4105
|
+
# @children=
|
4104
4106
|
# [#<Lutaml::Model::XmlAdapter::NokogiriElement:0x0000000107656cd0 @attributes={}, @children=[], @default_namespace=nil, @name="text", @namespace_prefix=nil, @text="\n ">,
|
4105
|
-
# #<Lutaml::Model::XmlAdapter::NokogiriElement:0x00000001076569b0
|
4106
|
-
# @attributes={},
|
4107
|
-
# @children=
|
4107
|
+
# #<Lutaml::Model::XmlAdapter::NokogiriElement:0x00000001076569b0
|
4108
|
+
# @attributes={},
|
4109
|
+
# @children=
|
4108
4110
|
# [#<Lutaml::Model::XmlAdapter::NokogiriElement:0x00000001076567f8 @attributes={}, @children=[], @default_namespace=nil, @name="text", @namespace_prefix=nil, @text="Metadata">],
|
4109
|
-
# @default_namespace=nil,
|
4110
|
-
# @name="category",
|
4111
|
-
# @namespace_prefix=nil,
|
4112
|
-
# @text="Metadata">,
|
4111
|
+
# @default_namespace=nil,
|
4112
|
+
# @name="category",
|
4113
|
+
# @namespace_prefix=nil,
|
4114
|
+
# @text="Metadata">,
|
4113
4115
|
# #<Lutaml::Model::XmlAdapter::NokogiriElement:0x0000000107656028 @attributes={}, @children=[], @default_namespace=nil, @name="text", @namespace_prefix=nil, @text="\n ">],
|
4114
|
-
# @default_namespace=nil,
|
4116
|
+
# @default_namespace=nil,
|
4115
4117
|
# @name="metadata",
|
4116
4118
|
# @namespace_prefix=nil,
|
4117
4119
|
# @text="\n Metadata\n ">
|
4118
4120
|
|
4119
4121
|
# Ox Adapter Node
|
4120
4122
|
|
4121
|
-
#<Lutaml::Model::XmlAdapter::OxElement:0x0000000107584f78
|
4122
|
-
# @attributes={},
|
4123
|
-
# @children=
|
4124
|
-
# [#<Lutaml::Model::XmlAdapter::OxElement:0x0000000107584e60
|
4125
|
-
# @attributes={},
|
4123
|
+
#<Lutaml::Model::XmlAdapter::OxElement:0x0000000107584f78
|
4124
|
+
# @attributes={},
|
4125
|
+
# @children=
|
4126
|
+
# [#<Lutaml::Model::XmlAdapter::OxElement:0x0000000107584e60
|
4127
|
+
# @attributes={},
|
4126
4128
|
# @children=[#<Lutaml::Model::XmlAdapter::OxElement:0x0000000107584d48 @attributes={}, @children=[], @default_namespace=nil, @name="text", @namespace_prefix=nil, @text="Metadata">],
|
4127
|
-
# @default_namespace=nil,
|
4128
|
-
# @name="category",
|
4129
|
-
# @namespace_prefix=nil,
|
4130
|
-
# @text="Metadata">],
|
4131
|
-
# @default_namespace=nil,
|
4132
|
-
# @name="metadata",
|
4133
|
-
# @namespace_prefix=nil,
|
4129
|
+
# @default_namespace=nil,
|
4130
|
+
# @name="category",
|
4131
|
+
# @namespace_prefix=nil,
|
4132
|
+
# @text="Metadata">],
|
4133
|
+
# @default_namespace=nil,
|
4134
|
+
# @name="metadata",
|
4135
|
+
# @namespace_prefix=nil,
|
4134
4136
|
# @text=nil>
|
4135
4137
|
|
4136
4138
|
# Oga Adapter Node
|
@@ -4190,7 +4192,7 @@ class CustomModelParentMapper < Lutaml::Model::Serializable
|
|
4190
4192
|
map_element :CustomModelChild,
|
4191
4193
|
with: { to: :child_to_xml, from: :child_from_xml }
|
4192
4194
|
end
|
4193
|
-
|
4195
|
+
|
4194
4196
|
def child_to_xml(model, parent, doc)
|
4195
4197
|
child_el = doc.create_element("CustomModelChild")
|
4196
4198
|
street_el = doc.create_element("street")
|
@@ -4227,7 +4229,7 @@ end
|
|
4227
4229
|
[source,ruby]
|
4228
4230
|
----
|
4229
4231
|
> instance = CustomModelParentMapper.from_xml(xml)
|
4230
|
-
> #<CustomModelParent:0x0000000107c9ca68 @child_mapper=#<CustomModelChild:0x0000000107c95218 @city="London", @street="Oxford Street">, @first_name="John">
|
4232
|
+
> #<CustomModelParent:0x0000000107c9ca68 @child_mapper=#<CustomModelChild:0x0000000107c95218 @city="London", @street="Oxford Street">, @first_name="John">
|
4231
4233
|
> CustomModelParentMapper.to_xml(instance)
|
4232
4234
|
> #<CustomModelParent><first_name>John</first_name><CustomModelChild><street>Oxford Street</street><city>London</city></CustomModelChild></CustomModelParent>
|
4233
4235
|
----
|
@@ -4595,27 +4597,228 @@ klin.validate
|
|
4595
4597
|
====
|
4596
4598
|
|
4597
4599
|
|
4598
|
-
== Liquid
|
4600
|
+
== Liquid template access
|
4601
|
+
|
4602
|
+
WARNING: The Liquid template feature is optional. To enable it, please
|
4603
|
+
explicitly require the `liquid` gem.
|
4604
|
+
|
4605
|
+
The https://shopify.github.io/liquid/[Liquid template language] is an
|
4606
|
+
open-source template language developed by Shopify and written in Ruby.
|
4607
|
+
|
4608
|
+
`Lutaml::Model::Serializable` objects can be safely accessed within Liquid
|
4609
|
+
templates through a `to_liquid` method that converts the objects into
|
4610
|
+
`Liquid::Drop` instances.
|
4599
4611
|
|
4600
|
-
|
4612
|
+
* All attributes are accessible in the Liquid template by their names.
|
4613
|
+
* Nested attributes are also converted into `Liquid::Drop` objects so
|
4614
|
+
inner attributes can be accessed using the Liquid dot notation.
|
4615
|
+
|
4616
|
+
NOTE: Every `Lutaml::Model::Serializable` class extends the `Liquefiable` module
|
4617
|
+
which generates a corresponding `Liquid::Drop` class.
|
4618
|
+
|
4619
|
+
NOTE: Methods defined in the `Lutaml::Model::Serializable` class are not
|
4620
|
+
accessible in the Liquid template.
|
4621
|
+
|
4622
|
+
.Using `to_liquid` to convert model instances into corresponding Liquid drop instances
|
4601
4623
|
|
4602
4624
|
[example]
|
4603
4625
|
====
|
4604
4626
|
[source,ruby]
|
4605
4627
|
----
|
4606
|
-
class
|
4628
|
+
class Ceramic < Lutaml::Model::Serializable
|
4629
|
+
attribute :name, :string
|
4630
|
+
attribute :temperature, :integer
|
4631
|
+
end
|
4632
|
+
|
4633
|
+
ceramic = Ceramic.new({ name: "Porcelain Vase", temperature: 1200 })
|
4634
|
+
ceramic_drop = ceramic.to_liquid
|
4635
|
+
# Ceramic::CeramicDrop
|
4636
|
+
|
4637
|
+
puts ceramic_drop.name
|
4638
|
+
# "Porcelain Vase"
|
4639
|
+
puts ceramic_drop.temperature
|
4640
|
+
# 1200
|
4641
|
+
----
|
4642
|
+
====
|
4643
|
+
|
4644
|
+
.Accessing LutaML::Model objects within a Liquid template
|
4645
|
+
[example]
|
4646
|
+
====
|
4647
|
+
[source,ruby]
|
4648
|
+
----
|
4649
|
+
class Ceramic < Lutaml::Model::Serializable
|
4650
|
+
attribute :name, :string
|
4651
|
+
attribute :temperature, :integer
|
4652
|
+
end
|
4653
|
+
|
4654
|
+
class CeramicCollection < Lutaml::Model::Serializable
|
4655
|
+
attribute :ceramics, Ceramic, collection: true
|
4656
|
+
end
|
4657
|
+
----
|
4658
|
+
|
4659
|
+
`sample.yml`:
|
4660
|
+
|
4661
|
+
[source,yaml]
|
4662
|
+
----
|
4663
|
+
---
|
4664
|
+
ceramics:
|
4665
|
+
- name: Porcelain Vase
|
4666
|
+
temperature: 1200
|
4667
|
+
- name: Earthenware Pot
|
4668
|
+
temperature: 950
|
4669
|
+
- name: Stoneware Jug
|
4670
|
+
temperature: 1200
|
4671
|
+
----
|
4672
|
+
|
4673
|
+
`template.liquid`:
|
4674
|
+
|
4675
|
+
[source,liquid]
|
4676
|
+
----
|
4677
|
+
{% for ceramic in ceramic_collection.ceramics %}
|
4678
|
+
* Name: "{{ ceramic.name }}"
|
4679
|
+
** Temperature: {{ ceramic.temperature }}
|
4680
|
+
{%- endfor %}
|
4681
|
+
----
|
4682
|
+
|
4683
|
+
[source,ruby]
|
4684
|
+
----
|
4685
|
+
# Load the Lutaml::Model collection
|
4686
|
+
ceramic_collection = CeramicCollection.from_yaml(File.read("sample.yml"))
|
4687
|
+
|
4688
|
+
# Load the Liquid template
|
4689
|
+
template = Liquid::Template.parse(File.read("template.liquid"))
|
4690
|
+
|
4691
|
+
# Pass the Lutaml::Model collection to the Liquid template and render
|
4692
|
+
output = template.render("ceramic_collection" => ceramic_collection)
|
4693
|
+
puts output
|
4694
|
+
# >
|
4695
|
+
# * Name: "Porcelain Vase"
|
4696
|
+
# ** Temperature: 1200
|
4697
|
+
# * Name: "Earthenware Pot"
|
4698
|
+
# ** Temperature: 950
|
4699
|
+
# * Name: "Stoneware Jug"
|
4700
|
+
# ** Temperature: 1200
|
4701
|
+
----
|
4702
|
+
====
|
4703
|
+
|
4704
|
+
.Accessing nested LutaML::Model objects within nested Liquid templates
|
4705
|
+
[example]
|
4706
|
+
====
|
4707
|
+
[source,ruby]
|
4708
|
+
----
|
4709
|
+
class Glaze < Lutaml::Model::Serializable
|
4710
|
+
attribute :color, :string
|
4711
|
+
attribute :opacity, :string
|
4712
|
+
end
|
4713
|
+
|
4714
|
+
class CeramicWork < Lutaml::Model::Serializable
|
4607
4715
|
attribute :name, :string
|
4608
|
-
attribute :
|
4716
|
+
attribute :glaze, Glaze
|
4717
|
+
end
|
4718
|
+
|
4719
|
+
class CeramicCollection < Lutaml::Model::Serializable
|
4720
|
+
attribute :ceramics, Ceramic, collection: true
|
4609
4721
|
end
|
4610
4722
|
|
4611
|
-
|
4612
|
-
|
4613
|
-
|
4723
|
+
ceramic_work = CeramicWork.new({
|
4724
|
+
name: "Celadon Bowl",
|
4725
|
+
glaze: Glaze.new({
|
4726
|
+
color: "Jade Green",
|
4727
|
+
opacity: "Translucent"
|
4728
|
+
})
|
4729
|
+
})
|
4730
|
+
ceramic_work_drop = ceramic_work.to_liquid
|
4731
|
+
# CeramicWork::CeramicWorkDrop
|
4732
|
+
|
4733
|
+
puts ceramic_work_drop.name
|
4734
|
+
# "Celadon Bowl"
|
4735
|
+
puts ceramic_work_drop.glaze.color
|
4736
|
+
# "Jade Green"
|
4737
|
+
puts ceramic_work_drop.glaze.opacity
|
4738
|
+
# "Translucent"
|
4739
|
+
----
|
4740
|
+
|
4741
|
+
`ceramics.yml`:
|
4742
|
+
|
4743
|
+
[source,yaml]
|
4744
|
+
----
|
4745
|
+
---
|
4746
|
+
ceramics:
|
4747
|
+
- name: Celadon Bowl
|
4748
|
+
glaze:
|
4749
|
+
color: Jade Green
|
4750
|
+
opacity: Translucent
|
4751
|
+
- name: Earthenware Pot
|
4752
|
+
glaze:
|
4753
|
+
color: Rust Red
|
4754
|
+
opacity: Opaque
|
4755
|
+
- name: Stoneware Jug
|
4756
|
+
glaze:
|
4757
|
+
color: Cobalt Blue
|
4758
|
+
opacity: Transparent
|
4759
|
+
----
|
4760
|
+
|
4761
|
+
|
4762
|
+
`templates/_ceramics.liquid`:
|
4763
|
+
|
4764
|
+
[source,liquid]
|
4765
|
+
----
|
4766
|
+
{% for ceramic in ceramic_collection.ceramics %}
|
4767
|
+
{% render 'ceramic' ceramic: ceramic %}
|
4768
|
+
{%- endfor %}
|
4769
|
+
----
|
4770
|
+
|
4771
|
+
NOTE: `render` is a Liquid tag that renders a partial template, by default
|
4772
|
+
Liquid uses the pattern `_%s.liquid` to find the partial template. Here
|
4773
|
+
`ceramic` refers to the file at `templates/_ceramic.liquid`.
|
4774
|
+
|
4775
|
+
`templates/_ceramic.liquid`:
|
4776
|
+
|
4777
|
+
[source,liquid]
|
4778
|
+
----
|
4779
|
+
* Name: "{{ ceramic.name }}"
|
4780
|
+
** Temperature: {{ ceramic.temperature }}
|
4781
|
+
{%- if ceramic.glaze %}
|
4782
|
+
** Glaze (color): {{ ceramic.glaze.color }}
|
4783
|
+
** Glaze (opacity): {{ ceramic.glaze.opacity }}
|
4784
|
+
{%- endif %}
|
4785
|
+
----
|
4786
|
+
|
4787
|
+
[source,ruby]
|
4788
|
+
----
|
4789
|
+
require 'liquid'
|
4790
|
+
|
4791
|
+
# Create a Liquid template object that supports dynamic loading
|
4792
|
+
template = Liquid::Template.new
|
4793
|
+
|
4794
|
+
# Link the Liquid template object to a "local file system" (directory)
|
4795
|
+
file_system = Liquid::LocalFileSystem.new('templates/')
|
4796
|
+
template.registers[:file_system] = file_system
|
4797
|
+
|
4798
|
+
# Load the partial template, this is necessary.
|
4799
|
+
# This will also allow Liquid to load any inner partials from the file system
|
4800
|
+
# dynamically (see `file_system.pattern` to see what it loads)
|
4801
|
+
template.parse(file_system.read_template_file('ceramics'))
|
4802
|
+
|
4803
|
+
# Read the lutaml-model collection
|
4804
|
+
ceramic_collection = CeramicCollection.from_yaml(File.read("ceramics.yml"))
|
4614
4805
|
|
4615
|
-
|
4616
|
-
|
4617
|
-
puts
|
4618
|
-
#
|
4806
|
+
# Render the template with the collection
|
4807
|
+
output = template.render("ceramic_collection" => ceramic_collection)
|
4808
|
+
puts output
|
4809
|
+
# >
|
4810
|
+
# * Name: "Celadon Bowl"
|
4811
|
+
# ** Temperature: 1200
|
4812
|
+
# ** Glaze (color): Jade Green
|
4813
|
+
# ** Glaze (finish): Translucent
|
4814
|
+
# * Name: "Earthenware Pot"
|
4815
|
+
# ** Temperature: 950
|
4816
|
+
# ** Glaze (color): Rust Red
|
4817
|
+
# ** Glaze (finish): Opaque
|
4818
|
+
# * Name: "Stoneware Jug"
|
4819
|
+
# ** Temperature: 1200
|
4820
|
+
# ** Glaze (color): Cobalt Blue
|
4821
|
+
# ** Glaze (finish): Transparent
|
4619
4822
|
----
|
4620
4823
|
====
|
4621
4824
|
|
data/lib/lutaml/model/error.rb
CHANGED
@@ -6,6 +6,7 @@ module Lutaml
|
|
6
6
|
end
|
7
7
|
|
8
8
|
require_relative "error/invalid_value_error"
|
9
|
+
require_relative "error/liquid_not_enabled_error"
|
9
10
|
require_relative "error/incorrect_mapping_argument_error"
|
10
11
|
require_relative "error/pattern_not_matched_error"
|
11
12
|
require_relative "error/unknown_adapter_type_error"
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require "liquid"
|
2
|
-
|
3
1
|
module Lutaml
|
4
2
|
module Model
|
5
3
|
module Liquefiable
|
@@ -9,6 +7,7 @@ module Lutaml
|
|
9
7
|
|
10
8
|
module ClassMethods
|
11
9
|
def register_liquid_drop_class
|
10
|
+
validate_liquid!
|
12
11
|
if drop_class
|
13
12
|
raise "#{drop_class_name} Already exists!"
|
14
13
|
end
|
@@ -38,6 +37,7 @@ module Lutaml
|
|
38
37
|
|
39
38
|
def register_drop_method(method_name)
|
40
39
|
register_liquid_drop_class unless drop_class
|
40
|
+
return if drop_class.method_defined?(method_name)
|
41
41
|
|
42
42
|
drop_class.define_method(method_name) do
|
43
43
|
value = @object.public_send(method_name)
|
@@ -49,9 +49,22 @@ module Lutaml
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
|
+
|
53
|
+
def validate_liquid!
|
54
|
+
return if Object.const_defined?(:Liquid)
|
55
|
+
|
56
|
+
raise Lutaml::Model::LiquidNotEnabledError
|
57
|
+
end
|
52
58
|
end
|
53
59
|
|
54
60
|
def to_liquid
|
61
|
+
self.class.validate_liquid!
|
62
|
+
|
63
|
+
if is_a?(Lutaml::Model::Serializable)
|
64
|
+
self.class.attributes.each_key do |attr_name|
|
65
|
+
self.class.register_drop_method(attr_name)
|
66
|
+
end
|
67
|
+
end
|
55
68
|
self.class.drop_class.new(self)
|
56
69
|
end
|
57
70
|
end
|
data/lib/lutaml/model/version.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require "liquid"
|
2
3
|
require_relative "../../fixtures/address"
|
3
4
|
|
4
5
|
class LiquefiableClass
|
@@ -16,6 +17,23 @@ class LiquefiableClass
|
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
20
|
+
module LiquefiableSpec
|
21
|
+
class Glaze < Lutaml::Model::Serializable
|
22
|
+
attribute :color, :string
|
23
|
+
attribute :opacity, :string
|
24
|
+
end
|
25
|
+
|
26
|
+
class Ceramic < Lutaml::Model::Serializable
|
27
|
+
attribute :name, :string
|
28
|
+
attribute :temperature, :integer
|
29
|
+
attribute :glaze, Glaze
|
30
|
+
end
|
31
|
+
|
32
|
+
class CeramicCollection < Lutaml::Model::Serializable
|
33
|
+
attribute :ceramics, Ceramic, collection: true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
19
37
|
RSpec.describe Lutaml::Model::Liquefiable do
|
20
38
|
before do
|
21
39
|
stub_const("DummyModel", Class.new(LiquefiableClass))
|
@@ -26,14 +44,27 @@ RSpec.describe Lutaml::Model::Liquefiable do
|
|
26
44
|
describe ".register_liquid_drop_class" do
|
27
45
|
context "when drop class does not exist" do
|
28
46
|
it "creates a new drop class" do
|
29
|
-
expect
|
30
|
-
dummy.class.
|
31
|
-
|
47
|
+
expect do
|
48
|
+
dummy.class.register_liquid_drop_class
|
49
|
+
end.to change {
|
50
|
+
dummy.class.const_defined?(:DummyModelDrop)
|
51
|
+
}
|
32
52
|
.from(false)
|
33
53
|
.to(true)
|
34
54
|
end
|
35
55
|
end
|
36
56
|
|
57
|
+
context "when 'liquid' is not available" do
|
58
|
+
before { allow(Object).to receive(:const_defined?).with(:Liquid).and_return(false) }
|
59
|
+
|
60
|
+
it "raises an error" do
|
61
|
+
expect { dummy.class.register_liquid_drop_class }.to raise_error(
|
62
|
+
Lutaml::Model::LiquidNotEnabledError,
|
63
|
+
"Liquid functionality is not available by default; please install and require `liquid` gem to use this functionality",
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
37
68
|
context "when drop class already exists" do
|
38
69
|
it "raises an error" do
|
39
70
|
dummy.class.register_liquid_drop_class
|
@@ -69,26 +100,41 @@ RSpec.describe Lutaml::Model::Liquefiable do
|
|
69
100
|
end
|
70
101
|
|
71
102
|
it "defines a method on the drop class" do
|
72
|
-
expect
|
73
|
-
dummy.
|
74
|
-
|
103
|
+
expect do
|
104
|
+
dummy.class.register_drop_method(:display_name)
|
105
|
+
end.to change {
|
106
|
+
dummy.to_liquid.respond_to?(:display_name)
|
107
|
+
}
|
75
108
|
.from(false)
|
76
109
|
.to(true)
|
77
110
|
end
|
78
111
|
end
|
79
112
|
|
80
113
|
describe ".to_liquid" do
|
81
|
-
|
82
|
-
|
83
|
-
dummy.class.register_drop_method(:display_name)
|
84
|
-
end
|
114
|
+
context "when liquid is not enabled" do
|
115
|
+
before { allow(Object).to receive(:const_defined?).with(:Liquid).and_return(false) }
|
85
116
|
|
86
|
-
|
87
|
-
|
117
|
+
it "raises an error" do
|
118
|
+
expect { dummy.to_liquid }.to raise_error(
|
119
|
+
Lutaml::Model::LiquidNotEnabledError,
|
120
|
+
"Liquid functionality is not available by default; please install and require `liquid` gem to use this functionality",
|
121
|
+
)
|
122
|
+
end
|
88
123
|
end
|
89
124
|
|
90
|
-
|
91
|
-
|
125
|
+
context "when liquid is enabled" do
|
126
|
+
before do
|
127
|
+
dummy.class.register_liquid_drop_class
|
128
|
+
dummy.class.register_drop_method(:display_name)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "returns an instance of the drop class" do
|
132
|
+
expect(dummy.to_liquid).to be_a(dummy.class.drop_class)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "allows access to registered methods via the drop class" do
|
136
|
+
expect(dummy.to_liquid.display_name).to eq("TestName (42)")
|
137
|
+
end
|
92
138
|
end
|
93
139
|
end
|
94
140
|
|
@@ -118,4 +164,96 @@ RSpec.describe Lutaml::Model::Liquefiable do
|
|
118
164
|
end
|
119
165
|
end
|
120
166
|
end
|
167
|
+
|
168
|
+
describe "working with liquid templates" do
|
169
|
+
let(:liquid_template_dir) do
|
170
|
+
File.join(File.dirname(__FILE__), "../../fixtures/liquid_templates")
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "rendering simple models with liquid templates" do
|
174
|
+
let :yaml do
|
175
|
+
<<~YAML
|
176
|
+
---
|
177
|
+
ceramics:
|
178
|
+
- name: Porcelain Vase
|
179
|
+
temperature: 1200
|
180
|
+
- name: Earthenware Pot
|
181
|
+
temperature: 950
|
182
|
+
- name: Stoneware Jug
|
183
|
+
temperature: 1200
|
184
|
+
YAML
|
185
|
+
end
|
186
|
+
let :template_path do
|
187
|
+
File.join(liquid_template_dir, "_ceramics_in_one.liquid")
|
188
|
+
end
|
189
|
+
|
190
|
+
it "renders" do
|
191
|
+
template = Liquid::Template.parse(File.read(template_path))
|
192
|
+
ceramic_collection = LiquefiableSpec::CeramicCollection.from_yaml(yaml)
|
193
|
+
output = template.render("ceramic_collection" => ceramic_collection)
|
194
|
+
|
195
|
+
expected_output = <<~OUTPUT
|
196
|
+
* Name: "Porcelain Vase"
|
197
|
+
** Temperature: 1200
|
198
|
+
* Name: "Earthenware Pot"
|
199
|
+
** Temperature: 950
|
200
|
+
* Name: "Stoneware Jug"
|
201
|
+
** Temperature: 1200
|
202
|
+
OUTPUT
|
203
|
+
|
204
|
+
expect(output.strip).to eq(expected_output.strip)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "rendering nested models with liquid templates from file system" do
|
209
|
+
let :yaml do
|
210
|
+
<<~YAML
|
211
|
+
---
|
212
|
+
ceramics:
|
213
|
+
- name: Celadon Bowl
|
214
|
+
temperature: 1200
|
215
|
+
glaze:
|
216
|
+
color: Jade Green
|
217
|
+
opacity: Translucent
|
218
|
+
- name: Earthenware Pot
|
219
|
+
temperature: 950
|
220
|
+
glaze:
|
221
|
+
color: Rust Red
|
222
|
+
opacity: Opaque
|
223
|
+
- name: Stoneware Jug
|
224
|
+
temperature: 1200
|
225
|
+
glaze:
|
226
|
+
color: Cobalt Blue
|
227
|
+
opacity: Transparent
|
228
|
+
YAML
|
229
|
+
end
|
230
|
+
|
231
|
+
it "renders" do
|
232
|
+
template = Liquid::Template.new
|
233
|
+
file_system = Liquid::LocalFileSystem.new(liquid_template_dir)
|
234
|
+
template.registers[:file_system] = file_system
|
235
|
+
template.parse(file_system.read_template_file("ceramics"))
|
236
|
+
|
237
|
+
ceramic_collection = LiquefiableSpec::CeramicCollection.from_yaml(yaml)
|
238
|
+
output = template.render("ceramic_collection" => ceramic_collection)
|
239
|
+
# puts output
|
240
|
+
|
241
|
+
expected_output = <<~OUTPUT
|
242
|
+
* Name: "Celadon Bowl"
|
243
|
+
** Temperature: 1200
|
244
|
+
** Glaze (color): Jade Green
|
245
|
+
** Glaze (opacity): Translucent
|
246
|
+
* Name: "Earthenware Pot"
|
247
|
+
** Temperature: 950
|
248
|
+
** Glaze (color): Rust Red
|
249
|
+
** Glaze (opacity): Opaque
|
250
|
+
* Name: "Stoneware Jug"
|
251
|
+
** Temperature: 1200
|
252
|
+
** Glaze (color): Cobalt Blue
|
253
|
+
** Glaze (opacity): Transparent
|
254
|
+
OUTPUT
|
255
|
+
expect(output.strip).to eq(expected_output.strip)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
121
259
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lutaml-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -111,6 +111,7 @@ files:
|
|
111
111
|
- lib/lutaml/model/error/incorrect_sequence_error.rb
|
112
112
|
- lib/lutaml/model/error/invalid_choice_range_error.rb
|
113
113
|
- lib/lutaml/model/error/invalid_value_error.rb
|
114
|
+
- lib/lutaml/model/error/liquid_not_enabled_error.rb
|
114
115
|
- lib/lutaml/model/error/multiple_mappings_error.rb
|
115
116
|
- lib/lutaml/model/error/no_root_mapping_error.rb
|
116
117
|
- lib/lutaml/model/error/no_root_namespace_error.rb
|
@@ -192,6 +193,9 @@ files:
|
|
192
193
|
- spec/ceramic_spec.rb
|
193
194
|
- spec/fixtures/address.rb
|
194
195
|
- spec/fixtures/ceramic.rb
|
196
|
+
- spec/fixtures/liquid_templates/_ceramic.liquid
|
197
|
+
- spec/fixtures/liquid_templates/_ceramics.liquid
|
198
|
+
- spec/fixtures/liquid_templates/_ceramics_in_one.liquid
|
195
199
|
- spec/fixtures/person.rb
|
196
200
|
- spec/fixtures/sample_model.rb
|
197
201
|
- spec/fixtures/vase.rb
|