Class: Lutaml::Model::Choice

Inherits:
Object
  • Object
show all
Defined in:
lib/lutaml/model/choice.rb

Constant Summary collapse

INTERNAL_ATTRIBUTES =
%i[@flat_attributes].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model, min, max, format: nil) ⇒ Choice

Returns a new instance of Choice.



14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/lutaml/model/choice.rb', line 14

def initialize(model, min, max, format: nil)
  @attributes = []
  @model = model
  @min = min
  @max = max
  @format = format

  if @min.negative? || @max.negative?
    raise Lutaml::Model::InvalidChoiceRangeError.new(@min,
                                                     @max)
  end
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



6
7
8
# File 'lib/lutaml/model/choice.rb', line 6

def attributes
  @attributes
end

#formatObject

Returns the value of attribute format.



10
11
12
# File 'lib/lutaml/model/choice.rb', line 10

def format
  @format
end

#maxObject (readonly)

Returns the value of attribute max.



6
7
8
# File 'lib/lutaml/model/choice.rb', line 6

def max
  @max
end

#minObject (readonly)

Returns the value of attribute min.



6
7
8
# File 'lib/lutaml/model/choice.rb', line 6

def min
  @min
end

#modelObject (readonly)

Returns the value of attribute model.



6
7
8
# File 'lib/lutaml/model/choice.rb', line 6

def model
  @model
end

Instance Method Details

#==(other) ⇒ Object



27
28
29
30
31
32
# File 'lib/lutaml/model/choice.rb', line 27

def ==(other)
  @attributes == other.attributes &&
    @min == other.min &&
    @max == other.max &&
    @model == other.model
end

#attribute(name, type, options = {}) ⇒ Object



34
35
36
37
# File 'lib/lutaml/model/choice.rb', line 34

def attribute(name, type, options = {})
  options[:choice] = self
  @attributes << @model.attribute(name, type, options)
end

#can_render_empty?(_object) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/lutaml/model/choice.rb', line 72

def can_render_empty?(_object)
  # Check if ALL attributes in the choice have render_empty: true
  # This allows empty instances of required elements to pass validation
  return false unless @format

  mapping = @model.mappings_for(@format)
  return false unless mapping&.elements

  @attributes.all? do |attribute|
    next true if attribute.is_a?(Choice) # Nested choices handled separately

    rule = mapping.elements.find { |r| r.to == attribute.name }
    rule&.render_empty?
  end
rescue StandardError
  false
end

#choice(min: 1, max: 1, &block) ⇒ Object



39
40
41
42
43
# File 'lib/lutaml/model/choice.rb', line 39

def choice(min: 1, max: 1, &block)
  @attributes << Choice.new(@model, min, max, format: @format).tap do |c|
    c.instance_eval(&block)
  end
end

#deep_duplicate(new_model, register = nil) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/lutaml/model/choice.rb', line 122

def deep_duplicate(new_model, register = nil)
  choice = self.class.new(new_model, @min, @max, format: @format)
  attrs = @attributes.map do |attr|
    next attr.deep_duplicate(new_model, register) if attr.is_a?(Choice)

    choice_attr = new_model.attributes(register)[attr.name]
    next if choice_attr.nil?

    choice_attr.options[:choice] = choice
    choice_attr
  end

  choice.attributes.concat(attrs.compact)
  choice
end

#flat_attributesObject



45
46
47
48
49
# File 'lib/lutaml/model/choice.rb', line 45

def flat_attributes
  @flat_attributes ||= @attributes.flat_map do |attribute|
    attribute.is_a?(Choice) ? attribute.flat_attributes : attribute
  end
end

#import_model_attributes(model, register_id = nil) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/lutaml/model/choice.rb', line 90

def import_model_attributes(model, register_id = nil)
  if later_importable?(model)
    return import_model_later(model, :import_model_attributes)
  end

  root_model_error(model)
  register_id ||= Lutaml::Model::Config.default_register

  if register_id != :default
    # Non-default register: store in register-specific storage only
    current_record = @model.register_records[register_id] ||= {
      attributes: {}, choice_attributes: []
    }
    imported_attributes = Utils.deep_dup(model.attributes(register_id))
    imported_attributes.each_value do |attr|
      attr.options[:choice] = self
    end
    current_record[:attributes].merge!(imported_attributes)
    return
  end

  # Default register: store in class-level storage
  imported_attributes = Utils.deep_dup(model.attributes.values)
  imported_attributes.each do |attr|
    attr.options[:choice] = self
    @model.define_attribute_methods(attr)
  end
  @attributes.concat(imported_attributes)
  attrs_hash = imported_attributes.to_h { |attr| [attr.name, attr] }
  @model.attributes.merge!(attrs_hash)
end

#optional_empty_choice?(count) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
155
156
157
158
# File 'lib/lutaml/model/choice.rb', line 152

def optional_empty_choice?(count)
  count.zero? && @attributes.any? do |attr|
    next attr.optional_empty_choice?(count) if attr.is_a?(self.class)

    optional_attribute?(attr)
  end
end

#pretty_print_instance_variablesObject



160
161
162
# File 'lib/lutaml/model/choice.rb', line 160

def pretty_print_instance_variables
  (instance_variables - INTERNAL_ATTRIBUTES).sort
end

#validate_content!(object, register = nil) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/lutaml/model/choice.rb', line 60

def validate_content!(object, register = nil)
  validated_attributes = []
  valid = valid_attributes(object, validated_attributes, register)

  # Allow empty choice if it can render empty elements
  if valid.none? && can_render_empty?(object)
    return
  end

  validate_count_errors!(valid.count, validated_attributes)
end

#validate_count_errors!(count, attributes) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/lutaml/model/choice.rb', line 138

def validate_count_errors!(count, attributes)
  return if count.between?(@min, @max)
  return if optional_empty_choice?(count)

  if count < @min
    raise Lutaml::Model::ChoiceLowerBoundError.new(attributes,
                                                   @min)
  end
  if count > @max
    raise Lutaml::Model::ChoiceUpperBoundError.new(attributes,
                                                   @max)
  end
end

#validate_sequence_content!(elements, appearance_count = 0, register = nil) ⇒ Object



51
52
53
54
55
56
57
58
# File 'lib/lutaml/model/choice.rb', line 51

def validate_sequence_content!(elements, appearance_count = 0,
register = nil)
  choices_hash = ::Hash.new { |h, k| h[k] = 0 }
  choices_hash[self] = appearance_count
  current_index = validate_choices(elements, choices_hash, register)
  raise_errors(choices_hash)
  current_index
end