Module: LazyGraph::Builder::DSL

Included in:
LazyGraph::Builder
Defined in:
lib/lazy_graph/builder/dsl.rb

Overview

This module defines the DSL for building Lazy Graph JSON schemas. Supported helpers

  • object :name, **opts, &blk

  • object_conditional :name, **opts, &blk ⌙ matches &blk

  • array :name, **opts, &blk ⌙ items &blk

  • <primitive> :name, **opts, &blk

  • date :name, **opts, &blk

  • decimal :name, **opts, &blk

  • timestamp :name, **opts, &blk

  • time :name, **opts, &blk

Instance Method Summary collapse

Instance Method Details

#additional_properties(value) ⇒ Object



19
20
21
22
# File 'lib/lazy_graph/builder/dsl.rb', line 19

def additional_properties(value)
  schema[:additionalProperties] = value
  self
end

#any_of(any_of) ⇒ Object



252
253
254
# File 'lib/lazy_graph/builder/dsl.rb', line 252

def any_of(any_of)
  schema[:anyOf] = (schema[:any_of] || []).concat(any_of).uniq
end

#array(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, type: :object, **opts, &blk) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/lazy_graph/builder/dsl.rb', line 256

def array(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
          type: :object, **opts, &blk)
  new_array = {
    type: :array,
    **(!default.nil? ? { default: default } : {}),
    **(description ? { description: description } : {}),
    **(rule ? { rule: rule } : {}),
    **opts,
    items: { properties: {} }.tap do |items|
      yields(items) do
        send(type, :items, &blk)
      end
    end[:properties][:items]
  }
  required(name) if required && default.nil? && rule.nil?
  pattern_property ? set_pattern_property(name, new_array) : set_property(name, new_array)
end

#date(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, **opts, &blk) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/lazy_graph/builder/dsl.rb', line 211

def date(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
         **opts, &blk)
  new_date = {
    anyOf: [
      {
        type: :string,
        # Matches ISO 8601 date format
        pattern: '^\\d{4}-\\d{2}-\\d{2}$'
      }
    ],
    type: :date, # Custom extended type
    **(!default.nil? ? { default: default } : {}),
    **(description ? { description: description } : {}),
    **(rule ? { rule: rule } : {}),
    **opts
  }
  yields(new_date, &blk)
  required(name) if required && default.nil? && rule.nil?
  pattern_property ? set_pattern_property(name, new_date) : set_property(name, new_date)
end

#decimal(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, **opts, &blk) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/lazy_graph/builder/dsl.rb', line 138

def decimal(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
            **opts, &blk)
  # Define the decimal schema supporting multiple formats
  new_decimal = {
    anyOf: [
      {
        type: :string,
        # Matches valid decimals with optional exponentials
        pattern: '^-?(\\d+\\.\\d+|\\d+|\\d+e[+-]?\\d+|\\d+\\.\\d+e[+-]?\\d+)$'

      },
      # Allows both float and int
      {
        type: :number #
      },
      {
        type: :integer
      }
    ],
    type: :decimal,
    **(!default.nil? ? { default: default } : {}),
    **(description ? { description: description } : {}),
    **(rule ? { rule: rule } : {}),
    **opts
  }
  yields(new_decimal, &blk)
  required(name) if required && default.nil? && rule.nil?
  pattern_property ? set_pattern_property(name, new_decimal) : set_property(name, new_decimal)
end

#default(value) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/lazy_graph/builder/dsl.rb', line 29

def default(value)
  schema[:default] = \
    if value.is_a?(Hash)
      HashUtils.deep_merge(schema.fetch(:default, {}), value)
    elsif value.is_a?(Array)
      schema.fetch(:default, []).concat(value).uniq!
    else
      value
    end
  self
end

#dependencies(dependencies) ⇒ Object



244
245
246
# File 'lib/lazy_graph/builder/dsl.rb', line 244

def dependencies(dependencies)
  schema[:dependencies] = HashUtils.deep_merge(schema[:dependencies] || {}, dependencies)
end

#depends_on(*dependencies) ⇒ Object



306
307
308
309
310
311
312
# File 'lib/lazy_graph/builder/dsl.rb', line 306

def depends_on(*dependencies)
  @resolved_dependencies ||= Hash.new do |h, k|
    send(k) # Load dependency once
    h[k] = true
  end
  dependencies.each(&@resolved_dependencies.method(:[]))
end

#items(&blk) ⇒ Object



274
275
276
# File 'lib/lazy_graph/builder/dsl.rb', line 274

def items(&blk)
  yields(schema[:items], &blk)
end

#matches(name, invisible: true, **when_clause, &blk) ⇒ Object



94
95
96
97
# File 'lib/lazy_graph/builder/dsl.rb', line 94

def matches(name, invisible: true, **when_clause, &blk)
  @match_cases << { name: name, when_clause: when_clause, schema: { invisible: invisible } }
  yields(@match_cases.last[:schema], &blk)
end

#object(name = nil, required: false, pattern_property: false, rule: nil, default: nil, description: nil, **opts, &blk) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/lazy_graph/builder/dsl.rb', line 99

def object(
  name = nil, required: false, pattern_property: false, rule: nil,
  default: nil, description: nil, **opts, &blk
)
  rule ||= rule_from_when(opts.delete(:when)) if opts[:when]
  rule ||= rule_from_first_of(opts.delete(:first_of)) if opts[:first_of]
  new_object = {
    type: :object,
    properties: {},
    additionalProperties: false,

    **(!default.nil? ? { default: default } : {}),
    **(description ? { description: description } : {}),
    **(rule ? { rule: rule } : {}),
    **opts
  }
  yields(new_object, &blk)
  required(name) if required && default.nil? && rule.nil?
  pattern_property ? set_pattern_property(name, new_object) : set_property(name, new_object)
end

#object_conditional(name = nil, required: false, pattern_property: false, rule: nil, default: nil, description: nil, **opts, &blk) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/lazy_graph/builder/dsl.rb', line 65

def object_conditional(
  name = nil, required: false, pattern_property: false, rule: nil,
  default: nil, description: nil, **opts, &blk
)
  new_object = {
    type: :object,
    properties: {},
    additionalProperties: false,
    **(!default.nil? ? { default: default } : {}),
    **(description ? { description: description } : {}),
    **opts
  }
  @prev_match_cases = @match_cases
  @match_cases = []

  yields(new_object, &blk)

  object_names = @match_cases.map do |match_case|
    rule = rule_from_when(match_case[:when_clause])
    set_property(match_case[:name], { type: :object, rule: rule, **match_case[:schema] })
    match_case[:name]
  end

  new_object[:rule] = rule_from_first_of(object_names)
  @match_cases = @prev_match_cases
  required(name) if required && default.nil? && rule.nil?
  pattern_property ? set_pattern_property(name, new_object) : set_property(name, new_object)
end

#one_of(one_of) ⇒ Object



248
249
250
# File 'lib/lazy_graph/builder/dsl.rb', line 248

def one_of(one_of)
  schema[:oneOf] = (schema[:one_of] || []).concat(one_of).uniq
end

#primitive(name = nil, type, required: false, pattern_property: false, default: nil, description: nil, enum: nil, rule: nil, **additional_options, &blk) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/lazy_graph/builder/dsl.rb', line 120

def primitive(
  name = nil, type, required: false, pattern_property: false,
  default: nil, description: nil, enum: nil,
  rule: nil, **additional_options, &blk
)
  new_primitive = {
    type: type,
    **(enum ? { enum: enum } : {}),
    **(!default.nil? ? { default: default } : {}),
    **(description ? { description: description } : {}),
    **(rule ? { rule: rule } : {}),
    **additional_options
  }
  yields(new_primitive, &blk)
  required(name) if required && default.nil? && rule.nil?
  pattern_property ? set_pattern_property(name, new_primitive) : set_property(name, new_primitive)
end

#required(*keys) ⇒ Object



24
25
26
27
# File 'lib/lazy_graph/builder/dsl.rb', line 24

def required(*keys)
  (schema[:required] ||= []).concat(keys.map(&:to_s)).uniq!
  self
end

#rule_from_first_of(prop_list) ⇒ Object



299
300
301
302
303
304
# File 'lib/lazy_graph/builder/dsl.rb', line 299

def rule_from_first_of(prop_list)
  {
    inputs: prop_list,
    calc: "itself.get_first_of(:#{prop_list.join(', :')})"
  }
end

#rule_from_when(when_clause) ⇒ Object



287
288
289
290
291
292
293
294
295
296
297
# File 'lib/lazy_graph/builder/dsl.rb', line 287

def rule_from_when(when_clause)
  inputs = when_clause.keys
  conditions = when_clause
  calc = "{#{when_clause.keys.map { |k| "#{k}: #{k}}" }.join(', ')}"
  {
    inputs: inputs,
    conditions: conditions,
    fixed_result: when_clause,
    calc: calc
  }
end

#set_pattern_property(pattern, value) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
# File 'lib/lazy_graph/builder/dsl.rb', line 41

def set_pattern_property(pattern, value)
  pattern = pattern.to_sym
  properties = schema[:patternProperties] ||= {}
  properties[pattern] = \
    if properties.key?(pattern) && %i[object array].include?(properties[pattern][:type])
      HashUtils.deep_merge(properties[pattern], value, key)
    else
      value
    end
  self
end

#set_property(key, value) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/lazy_graph/builder/dsl.rb', line 53

def set_property(key, value)
  key = key.to_sym
  properties = schema[:properties] ||= {}
  properties[key] = \
    if properties.key?(key) && %i[object array].include?(properties[key][:type])
      HashUtils.deep_merge(properties[key], value, key)
    else
      value
    end
  self
end

#time(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, **opts, &blk) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/lazy_graph/builder/dsl.rb', line 195

def time(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
         **opts, &blk)
  new_time = {
    type: :time, # Custom extended type
    # Matches HH:mm[:ss[.SSS]]
    pattern: '^\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,3})?)?$',
    **(!default.nil? ? { default: default } : {}),
    **(description ? { description: description } : {}),
    **(rule ? { rule: rule } : {}),
    **opts
  }
  yields(new_time, &blk)
  required(name) if required && default.nil? && rule.nil?
  pattern_property ? set_pattern_property(name, new_time) : set_property(name, new_time)
end

#timestamp(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, **opts, &blk) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/lazy_graph/builder/dsl.rb', line 168

def timestamp(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
              **opts, &blk)
  new_timestamp = {
    anyOf: [
      {
        type: :string,
        # Matches ISO 8601 timestamp without timezone
        pattern: '^\d{4}-\d{2}-\d{2}(T\d{2}(:\d{2}(:\d{2}(\.\d{1,3})?)?)?(Z|[+-]\d{2}(:\d{2})?)?)?$'
      },
      {
        type: :number # Allows numeric epoch timestamps
      },
      {
        type: :integer # Allows both float and int
      }
    ],
    type: :timestamp, # Custom extended type
    **(!default.nil? ? { default: default } : {}),
    **(description ? { description: description } : {}),
    **(rule ? { rule: rule } : {}),
    **opts
  }
  yields(new_timestamp, &blk)
  required(name) if required && default.nil? && rule.nil?
  pattern_property ? set_pattern_property(name, new_timestamp) : set_property(name, new_timestamp)
end

#yields(other) ⇒ Object



278
279
280
281
282
283
284
285
# File 'lib/lazy_graph/builder/dsl.rb', line 278

def yields(other)
  return unless block_given?

  prev_schema = schema
  self.schema = other
  yield
  self.schema = prev_schema
end