Module: Lutaml::Model::Serialize::Builder
- Included in:
- Lutaml::Model::Serialize
- Defined in:
- lib/lutaml/model/serialize/builder.rb
Overview
Builder interface for constructing model instances with mixed content.
Provides two syntaxes:
NO MAGIC (Explicit Receiver)
group = Group.new do |g|
g.member(person1)
g.member_key("john")
g.description "\n He is a good man.\n"
end
MAGIC (instance_eval — No Receiver)
group = Group.new do
member(person1)
member_key("john")
description "\n He is a good man.\n"
end
Both syntaxes track the order of calls for mixed_content serialization.
Instance Method Summary collapse
-
#initialize(attrs = {}, options = {}, &block) ⇒ Object
Override initialize to support builder block syntax.
-
#mixed_content? ⇒ Boolean
Check if this model has mixed_content enabled.
-
#order_tracking_enabled? ⇒ Boolean
Whether this instance was constructed via a builder block and therefore records mutations into element_order.
-
#ordered? ⇒ Boolean
Check if this model has ordered or mixed_content enabled.
-
#record_mutation(attribute_name, value = nil) ⇒ Object
Record a singular attribute mutation in element_order.
-
#record_mutation_collection(attribute_name, value) ⇒ Object
Record a collection-attribute mutation in element_order.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object (private)
Intercept method calls to track order for mixed_content
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/lutaml/model/serialize/builder.rb', line 136 def method_missing(method_name, *args, &block) # Check if this is an attribute setter call setter_name = :"#{method_name}=" if args.length == 1 && attribute_exist?(setter_name) # Track order before calling the actual setter track_order(method_name, args.first, block) if @__order_tracking__ # Call the actual setter send(setter_name, args.first) elsif args.empty? && block # Block form: attribute { |nested| nested.attr value } # This handles nested model construction handle_nested_block(method_name, block) else super end end |
Instance Method Details
#initialize(attrs = {}, options = {}, &block) ⇒ Object
Override initialize to support builder block syntax.
When a block is passed, evaluates it in the model's context. For mixed_content models, tracks call order in element_order.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/lutaml/model/serialize/builder.rb', line 36 def initialize(attrs = {}, = {}, &block) # Call parent initialize super(attrs, ) return self unless block # Enable order tracking for ordered and mixed_content models. # `ordered?` is true for both `ordered` and `mixed_content` # (mixed_content sets @ordered = true in addition to @mixed_content). @__order_tracking__ = ordered? # Evaluate the block - use instance_eval for no-receiver style # The block's first parameter determines the style: # - |g| -> explicit receiver (block.call(g)) # - no param -> instance_eval (&block) if block.arity.zero? instance_eval(&block) else yield(self) end self end |
#mixed_content? ⇒ Boolean
Check if this model has mixed_content enabled
62 63 64 65 |
# File 'lib/lutaml/model/serialize/builder.rb', line 62 def mixed_content? mapping = self.class.mappings_for(:xml, lutaml_register) mapping&.mixed_content? || false end |
#order_tracking_enabled? ⇒ Boolean
Whether this instance was constructed via a builder block and therefore records mutations into element_order. Parsed models and instances constructed without a block do not track; their element_order (if any) comes from the parser and is treated as the complete source of truth by the serializer.
84 85 86 |
# File 'lib/lutaml/model/serialize/builder.rb', line 84 def order_tracking_enabled? @__order_tracking__ ? true : false end |
#ordered? ⇒ Boolean
Check if this model has ordered or mixed_content enabled. Both set @ordered = true on the XML mapping; mixed_content additionally sets @mixed_content = true. Order tracking is needed for either mode so the builder can emit elements in setter-call order rather than declaration order.
73 74 75 76 |
# File 'lib/lutaml/model/serialize/builder.rb', line 73 def ordered? mapping = self.class.mappings_for(:xml, lutaml_register) mapping&.ordered? || false end |
#record_mutation(attribute_name, value = nil) ⇒ Object
Record a singular attribute mutation in element_order.
No-op unless order tracking is enabled (i.e. the instance was
constructed via a builder block on an ordered/mixed_content
model). This is the single entry point for singular mutations
and is called from generated setters and getter-with-arg paths
so that direct setters (x.foo = v) and appender calls
(x.foo(v)) behave identically.
Returns value so generated setters preserve Ruby's setter
contract: obj.foo = v evaluates to v. Callers like
obj.foo || (obj.foo = []) depend on this.
105 106 107 108 109 110 |
# File 'lib/lutaml/model/serialize/builder.rb', line 105 def record_mutation(attribute_name, value = nil) return value unless @__order_tracking__ track_order(attribute_name, value, nil) value end |
#record_mutation_collection(attribute_name, value) ⇒ Object
Record a collection-attribute mutation in element_order.
Emits one entry per item so that element_order length matches the number of serialized child elements. No-op when tracking is disabled, when the value is nil/empty, or when the collection's frozen sentinel is preserved (no real data).
Returns value so generated setters preserve Ruby's setter
contract (see #record_mutation).
125 126 127 128 129 130 131 |
# File 'lib/lutaml/model/serialize/builder.rb', line 125 def record_mutation_collection(attribute_name, value) return value unless @__order_tracking__ return value if value.nil? || Lutaml::Model::Utils.uninitialized?(value) Array(value).each { |item| track_order(attribute_name, item, nil) } value end |