Class: ORB::Temple::AttributesCompiler

Inherits:
Object
  • Object
show all
Defined in:
lib/orb/temple/attributes_compiler.rb

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ AttributesCompiler

Returns a new instance of AttributesCompiler.



6
7
8
# File 'lib/orb/temple/attributes_compiler.rb', line 6

def initialize(options = {})
  @options = options
end

Instance Method Details

#compile_attribute(attribute) ⇒ Object

Compile a single attribute into Temple core abstraction an attribute can be a static string, a dynamic expression, or a boolean attribute (an attribute without a value, e.g. disabled, checked, etc.)

Compile a single attribute into Temple core abstraction. Boolean attributes emit [:static, “”] directly instead of [:dynamic, “nil”] to avoid unnecessary Ripper analysis in Temple’s StaticAnalyzer filter.



87
88
89
90
91
92
93
94
95
# File 'lib/orb/temple/attributes_compiler.rb', line 87

def compile_attribute(attribute)
  if attribute.string?
    [:html, :attr, attribute.name, [:static, attribute.value]]
  elsif attribute.bool?
    [:html, :attr, attribute.name, [:static, ""]]
  elsif attribute.expression?
    [:html, :attr, attribute.name, [:escape, true, [:dynamic, attribute.value]]]
  end
end

#compile_attributes(attributes) ⇒ Object

Compile the attributes of a node into a Temple core abstraction



61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/orb/temple/attributes_compiler.rb', line 61

def compile_attributes(attributes)
  temple = [:html, :attrs]

  attributes.each do |attribute|
    # Ignore splat attributes
    next if attribute.splat?

    temple << compile_attribute(attribute)
  end

  temple
end

#compile_captures(attributes, prefix) ⇒ Object

Compile the given array of AST::Attribute objects into Temple capture expressions using the given prefix in variable names



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/orb/temple/attributes_compiler.rb', line 12

def compile_captures(attributes, prefix)
  result = []

  attributes.each do |attribute|
    # TODO: handle splat attributes
    next if attribute.splat?

    # generate a unique variable name for the attribute
    var_name = prefixed_variable_name(attribute.name, prefix)

    # inject a code expression for the attribute value and assign to the variable
    if attribute.string?
      result << [:code, "#{var_name} = \"#{attribute.value}\""]
    elsif attribute.bool?
      result << [:code, "#{var_name} = true"]
    elsif attribute.expression?
      result << [:code, "#{var_name} = #{attribute.value}"]
    end
  end

  result
end

#compile_captures_and_args(attributes, prefix) ⇒ Object

Combined single-pass compilation of captures and komponent args Returns [captures_array, args_string]



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/orb/temple/attributes_compiler.rb', line 126

def compile_captures_and_args(attributes, prefix)
  captures = []
  args = {}
  splats = []

  attributes.each do |attribute|
    if attribute.splat?
      splats << attribute.value
      next
    end

    var_name = prefixed_variable_name(attribute.name, prefix)

    # Build capture
    if attribute.string?
      captures << [:code, "#{var_name} = \"#{attribute.value}\""]
    elsif attribute.bool?
      captures << [:code, "#{var_name} = true"]
    elsif attribute.expression?
      captures << [:code, "#{var_name} = #{attribute.value}"]
    end

    # Build args hash
    args = args.deep_merge(dash_to_hash(attribute.name, var_name))
  end

  # Build the argument list
  result_parts = []
  result_parts << hash_to_args_list(args) unless args.empty?
  result_parts += splats

  [captures, result_parts.join(', ')]
end

#compile_komponent_args(attributes, prefix) ⇒ Object

Compile the given array of AST::Attribute objects into a string of arguments the can be used in a ViewComponent constructor call, as long as the compiled captures are available in the same scope.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/orb/temple/attributes_compiler.rb', line 38

def compile_komponent_args(attributes, prefix)
  args = {}
  splats = []

  attributes.each do |attribute|
    if attribute.splat?
      # Splat attribute values already include the ** prefix
      splats << attribute.value
    else
      var_name = prefixed_variable_name(attribute.name, prefix)
      args = args.deep_merge(dash_to_hash(attribute.name, var_name))
    end
  end

  # Build the argument list
  result_parts = []
  result_parts << hash_to_args_list(args) unless args.empty?
  result_parts += splats

  result_parts.join(', ')
end

#compile_splat_attributes(attributes) ⇒ Object

Compile splat attributes to a code string



76
77
78
# File 'lib/orb/temple/attributes_compiler.rb', line 76

def compile_splat_attributes(attributes)
  attributes.map(&:value).join(',')
end

#dash_to_hash(name, value) ⇒ Object



97
98
99
100
# File 'lib/orb/temple/attributes_compiler.rb', line 97

def dash_to_hash(name, value)
  parts = name.split('-')
  parts.reverse.inject(value) { |a, n| { n => a } }
end

#hash_to_args_list(obj, level = -1)) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/orb/temple/attributes_compiler.rb', line 102

def hash_to_args_list(obj, level = -1)
  case obj
  when String
    obj
  when Array
    obj.map { |v| hash_to_args_list(v, level + 1) }.join(", ")
  when Hash
    down_the_rabbit_hole = obj.map { |k, v| "#{k}: #{hash_to_args_list(v, level + 1)}" }.join(", ")
    return down_the_rabbit_hole if level.negative?

    "{#{down_the_rabbit_hole}}"

  else
    raise "Invalid argument passed to hash_to_args_list: #{obj.inspect}"
  end
end

#prefixed_variable_name(name, prefix) ⇒ Object

Pre-build the variable name string with a single interpolation



120
121
122
# File 'lib/orb/temple/attributes_compiler.rb', line 120

def prefixed_variable_name(name, prefix)
  "#{prefix}_arg_#{name.underscore}"
end