Module: MendixBridge::AlterPageBuilder

Defined in:
lib/mendix_bridge/alter_page_builder.rb

Overview

Builds ALTER PAGE / ALTER SNIPPET MDL statements from a structured list of operations. Each operation is a Hash with an "op" key and operation- specific fields:

{ "op" => "set",          "widget" => "btnSave",
"props" => { "Caption" => "Save & Close", "ButtonStyle" => "Success" } }

{ "op" => "insert_after", "widget" => "tbEmail",
"body" => "textbox tbPhone (Label: 'Phone', Attribute: Phone)" }

{ "op" => "insert_before","widget" => "btnCancel",
"body" => "actionbutton btnBack (Caption: 'Back', Action: close_page)" }

{ "op" => "drop",         "widgets" => ["tbUnused", "lblOld"] }

{ "op" => "replace",      "widget" => "tbName",
"body" => "textarea taName (Label: 'Name', Attribute: Name)" }

{ "op" => "set_layout",   "layout" => "Atlas_Core.Atlas_Sidebar_Full" }

{ "op" => "add_variable", "name" => "counter",
"type" => "Integer",    "default" => "0" }

{ "op" => "drop_variable","name" => "counter" }

Validation errors are returned as an array of strings from validate. build raises ArgumentError when there are errors.

Constant Summary collapse

VALID_OPS =
%w[
  set insert_after insert_before drop replace set_layout add_variable drop_variable
].freeze
IDENTIFIER =
/\A[A-Za-z_]\w*\z/
QN_PATTERN =
/\A[A-Za-z_]\w*\.[A-Za-z_]\w*\z/
LAYOUT_QN =
/\A[A-Za-z_]\w*\.[A-Za-z_]\w*\z/

Class Method Summary collapse

Class Method Details

.build(qn:, operations:, known_names: nil) ⇒ Object

Build an ALTER PAGE MDL string from qn and an array of operations. Raises ArgumentError if any operation is invalid. Pass known_names: (array of widget name strings from the page tree) to enable widget-name existence checks. Omit it to skip those checks.

Raises:

  • (ArgumentError)


44
45
46
47
48
49
50
# File 'lib/mendix_bridge/alter_page_builder.rb', line 44

def self.build(qn:, operations:, known_names: nil)
  errors = validate(operations, known_names: known_names)
  raise ArgumentError, errors.join("; ") if errors.any?

  lines = operations.map { |op| build_operation(op) }.compact
  "ALTER PAGE #{qn} {\n#{lines.join("\n")}\n};\n"
end

.validate(operations, known_names: nil) ⇒ Object

Returns an array of human-readable error strings, or [] if valid.



53
54
55
56
57
58
59
60
61
62
63
64
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/mendix_bridge/alter_page_builder.rb', line 53

def self.validate(operations, known_names: nil)
  return ["operations must be an array"] unless operations.is_a?(Array)

  errors = []
  operations.each_with_index do |op, i|
    prefix = "operation[#{i}]"
    unless op.is_a?(Hash)
      errors << "#{prefix}: must be a hash"
      next
    end

    kind = op["op"].to_s
    unless VALID_OPS.include?(kind)
      errors << "#{prefix}: unknown op '#{kind}' (valid: #{VALID_OPS.join(", ")})"
      next
    end

    case kind
    when "set"
      errors << "#{prefix}: 'widget' required" unless op["widget"].is_a?(String)
      errors << "#{prefix}: 'props' must be a non-empty hash" unless
        op["props"].is_a?(Hash) && op["props"].any?
      check_widget_exists(prefix, op["widget"], known_names, errors)

    when "insert_after", "insert_before"
      errors << "#{prefix}: 'widget' required" unless op["widget"].is_a?(String)
      errors << "#{prefix}: 'body' required (MDL widget definition)" unless
        op["body"].is_a?(String) && !op["body"].strip.empty?
      check_widget_exists(prefix, op["widget"], known_names, errors)

    when "drop"
      unless op["widgets"].is_a?(Array) && op["widgets"].all? { |w| w.is_a?(String) }
        errors << "#{prefix}: 'widgets' must be an array of strings"
        next
      end
      op["widgets"].each { |w| check_widget_exists(prefix, w, known_names, errors) }

    when "replace"
      errors << "#{prefix}: 'widget' required" unless op["widget"].is_a?(String)
      errors << "#{prefix}: 'body' required (MDL widget definition)" unless
        op["body"].is_a?(String) && !op["body"].strip.empty?
      check_widget_exists(prefix, op["widget"], known_names, errors)

    when "set_layout"
      unless op["layout"].is_a?(String) && op["layout"].match?(LAYOUT_QN)
        errors << "#{prefix}: 'layout' must be a qualified name (Module.Layout)"
      end

    when "add_variable"
      errors << "#{prefix}: 'name' must be a valid identifier" unless
        op["name"].is_a?(String) && op["name"].match?(IDENTIFIER)
      errors << "#{prefix}: 'type' required" unless op["type"].is_a?(String)

    when "drop_variable"
      errors << "#{prefix}: 'name' must be a valid identifier" unless
        op["name"].is_a?(String) && op["name"].match?(IDENTIFIER)
    end
  end

  errors
end