Class: ORB::Temple::Filters
- Inherits:
-
Temple::Filter
- Object
- Temple::Filter
- ORB::Temple::Filters
- Defined in:
- lib/orb/temple/filters.rb
Constant Summary collapse
- VALID_HTML_TAG_NAME =
Valid HTML tag names: starts with a letter, followed by letters, digits, or hyphens. Used to validate dynamic tag names before interpolation into generated Ruby code.
/\A[a-zA-Z][a-zA-Z0-9-]*\z/- VALID_COMPONENT_NAME =
Valid Ruby constant path: one or more segments like Foo, Foo::Bar, Foo::Bar::Baz. Each segment starts with an uppercase letter followed by word characters. Used to validate component names before interpolation into generated Ruby code.
/\A[A-Z]\w*(::[A-Z]\w*)*\z/- VALID_SLOT_NAME =
Valid Ruby identifier for use as a method name suffix in with_ slot calls. Used to validate slot names before interpolation into generated Ruby code.
/\A[a-z_]\w*\z/
Instance Method Summary collapse
-
#initialize(options = {}) ⇒ Filters
constructor
A new instance of Filters.
-
#on_orb_component(node, content = []) ⇒ Array
Handle a component tag expression ‘[:orb, :component, name, attributes, content]`.
-
#on_orb_dynamic(node, content) ⇒ Object
Handle a dynamic node expression ‘[:orb, :dynamic, node, content]`.
-
#on_orb_for(expression, content) ⇒ Array
Handle a for block expression ‘[:orb, :for, expression, content]`.
-
#on_orb_if(condition, yes, no = nil) ⇒ Array
Handle an if block expression ‘[:orb, :if, condition, yes, no]`.
-
#on_orb_slot(node, content = []) ⇒ Array
Handle a component slot tag expression ‘[:orb, :slot, name, attributes, content]`.
-
#on_orb_tag(name, attributes, content = nil) ⇒ Array
Handle an HTML tag expression ‘[:orb, :tag, name, attributes, content]`.
Constructor Details
#initialize(options = {}) ⇒ Filters
Returns a new instance of Filters.
19 20 21 22 |
# File 'lib/orb/temple/filters.rb', line 19 def initialize( = {}) @options = @attributes_compiler = AttributesCompiler.new end |
Instance Method Details
#on_orb_component(node, content = []) ⇒ Array
Handle a component tag expression ‘[:orb, :component, name, attributes, content]`
40 41 42 43 44 45 46 47 48 49 50 51 52 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 |
# File 'lib/orb/temple/filters.rb', line 40 def on_orb_component(node, content = []) tmp = unique_name # Lookup the component class name using the ORB lookup mechanism # that traverses the configured namespaces name = node.tag.gsub('.', '::') komponent = ORB.lookup_component(name) komponent_name = komponent || name unless komponent_name.match?(VALID_COMPONENT_NAME) raise ORB::SyntaxError.new("Invalid component name: #{komponent_name.inspect}", 0) end block_name = "__orb__#{komponent_name.rpartition('::').last.underscore}" block_name = node.directives.fetch(:with, block_name) unless block_name.match?(/\A[a-z_]\w*\z/) raise ORB::SyntaxError.new("Invalid :with directive value: must be a valid Ruby identifier", 0) end # We need to compile the attributes into a set of captures and a set of arguments # since arguments passed to the view component constructor may be defined as # dynamic expressions in our template, and we need to first capture their results. arg_captures = @attributes_compiler.compile_captures(node.attributes, tmp) args = @attributes_compiler.compile_komponent_args(node.attributes, tmp) # Construct the render call for the view component code = "render #{komponent_name}.new(#{args}) do |#{block_name}|" # Return a compiled Temple expression that captures the component render call # and then evaluates the result into the OutputBuffer. [:multi, *arg_captures, # Capture the result of the component render call into a variable # we can't do :dynamic here because it's probably not a complete expression [:block, "#{tmp} = #{code}", # Capture the content of the block into a separate buffer # [:capture, unique_name, compile(content)] compile(content)], # Output the content [:escape, true, [:dynamic, tmp.to_s]]] end |
#on_orb_dynamic(node, content) ⇒ Object
Handle a dynamic node expression ‘[:orb, :dynamic, node, content]`
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/orb/temple/filters.rb', line 155 def on_orb_dynamic(node, content) # Determine whether the node is an html_tag, component, or slot node if node.component_tag? on_orb_component(node, content) elsif node.component_slot_tag? on_orb_slot(node, content) else # It's a dynamic HTML tag unless node.tag.match?(VALID_HTML_TAG_NAME) raise ORB::SyntaxError.new("Invalid tag name: #{node.tag.inspect}", 0) end tmp = unique_name splats = @attributes_compiler.compile_splat_attributes(node.splat_attributes) code = "content_tag('#{node.tag}', #{splats}) do" [:multi, [:block, "#{tmp} = #{code}", compile(content)], [:escape, true, [:dynamic, tmp]]] end end |
#on_orb_for(expression, content) ⇒ Array
Handle a for block expression ‘[:orb, :for, expression, content]`
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/orb/temple/filters.rb', line 134 def on_orb_for(expression, content) match = expression.match(/\A\s*([a-z_]\w*)\s+in\s+(.+)\z/m) raise ORB::SyntaxError.new("Invalid :for expression: enumerator must be a valid Ruby identifier", 0) unless match enumerator, collection = match[1], match[2] # Reject semicolons in the collection expression to prevent statement injection. # Legitimate complex expressions should be assigned in a {%...%} block first. if collection.include?(';') raise ORB::SyntaxError.new("Invalid :for collection expression: semicolons are not allowed", 0) end code = "#{collection}.each do |#{enumerator}|" [:multi, [:code, code], compile(content), [:code, "end"]] end |
#on_orb_if(condition, yes, no = nil) ⇒ Array
Handle an if block expression ‘[:orb, :if, condition, yes, no]`
123 124 125 126 127 |
# File 'lib/orb/temple/filters.rb', line 123 def on_orb_if(condition, yes, no = nil) result = [:if, condition, compile(yes)] result << compile(no) if no result end |
#on_orb_slot(node, content = []) ⇒ Array
Handle a component slot tag expression ‘[:orb, :slot, name, attributes, content]`
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 114 115 |
# File 'lib/orb/temple/filters.rb', line 87 def on_orb_slot(node, content = []) tmp = unique_name # We need to compile the attributes into a set of captures and a set of arguments # since arguments passed to the view component constructor may be defined as # dynamic expressions in our template, and we need to first capture their results. arg_captures = @attributes_compiler.compile_captures(node.attributes, tmp) args = @attributes_compiler.compile_komponent_args(node.attributes, tmp) # Prepare the slot name, parent name, and block name slot_name = node.slot unless slot_name.match?(VALID_SLOT_NAME) raise ORB::SyntaxError.new("Invalid slot name: #{slot_name.inspect}", 0) end parent_name = "__orb__#{node.component.underscore}" block_name = node.directives.fetch(:with, "__orb__#{slot_name}") unless block_name.match?(/\A[a-z_]\w*\z/) raise ORB::SyntaxError.new("Invalid :with directive value: must be a valid Ruby identifier", 0) end # Construct the code to call the slot on the parent component code = "#{parent_name}.with_#{slot_name}(#{args}) do |#{block_name}|" # Return a compiled Temple expression that captures the slot call [:multi, *arg_captures, [:code, code], compile(content), [:code, "end"]] end |
#on_orb_tag(name, attributes, content = nil) ⇒ Array
Handle an HTML tag expression ‘[:orb, :tag, name, attributes, content]`
30 31 32 |
# File 'lib/orb/temple/filters.rb', line 30 def on_orb_tag(name, attributes, content = nil) [:html, :tag, name, @attributes_compiler.compile_attributes(attributes), compile(content)] end |