Module: Volt::AttributeScope

Included in:
ViewScope
Defined in:
lib/volt/server/html_parser/attribute_scope.rb

Overview

Included into ViewScope to provide processing for attributes

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



24
25
26
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 24

def self.included(base)
  base.send :extend, ClassMethods
end

Instance Method Details

#add_asset_url_attribute(getter, attributes) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 135

def add_asset_url_attribute(getter, attributes)
  # Asset url helper binding
  asset_url_parts = getter.split(/[\s\(\)\'\"]/).reject(&:blank?)
  url = asset_url_parts[1]

  unless url
    raise "the `asset_url` helper requries a url argument ```{{ asset_url 'pic.png' }}```"
  end

  link_url = @handler.link_asset(url, false)

  attributes['src'] = link_url
end

#add_id_to_attributes(attributes) ⇒ Object



185
186
187
188
189
190
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 185

def add_id_to_attributes(attributes)
  id              = attributes['id'] ||= "id#{@binding_number}"
  @binding_number += 1

  id.to_s
end

#add_multiple_attribute(tag_name, id, attribute_name, parts, content) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 149

def add_multiple_attribute(tag_name, id, attribute_name, parts, content)
  case attribute_name
    when 'checked', 'value'
      if parts.size > 1
        if tag_name == 'textarea'
          fail "The content of text area's can not be bound to multiple bindings."
        else
          # Multiple values can not be passed to value or checked attributes.
          fail "Multiple bindings can not be passed to a #{attribute_name} binding: #{parts.inspect}"
        end
      end
  end

  string_template_renderer_path = add_string_template_renderer(content)

  save_binding(id, "lambda { |__p, __t, __c, __id| Volt::AttributeBinding.new(__p, __t, __c, __id, #{attribute_name.inspect}, Proc.new { Volt::StringTemplateRenderer.new(__p, __c, #{string_template_renderer_path.inspect}) }) }")
end

#add_single_attribute(id, attribute_name, getter) ⇒ Object

Add an attribute binding on the tag, bind directly to the getter in the binding



129
130
131
132
133
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 129

def add_single_attribute(id, attribute_name, getter)
  setter = getter_to_setter(getter)

  save_binding(id, "lambda { |__p, __t, __c, __id| Volt::AttributeBinding.new(__p, __t, __c, __id, #{attribute_name.inspect}, Proc.new { #{getter} }, Proc.new { |val| #{setter} }) }")
end

#add_string_template_renderer(content) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 167

def add_string_template_renderer(content)
  path            = @path + "/_rv#{@binding_number}"
  new_handler     = ViewHandler.new(path, nil, false)
  @binding_number += 1

  SandlebarsParser.new(content, new_handler)

  # Close out the last scope
  new_handler.scope.last.close_scope

  # Copy in the templates from the new handler
  new_handler.templates.each_pair do |key, value|
    @handler.templates[key] = value
  end

  path
end

#attribute_string(attributes) ⇒ Object



192
193
194
195
196
197
198
199
200
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 192

def attribute_string(attributes)
  attr_str = attributes.map { |v| "#{v[0]}=\"#{v[1]}\"" }.join(' ')
  if attr_str.size > 0
    # extra space
    attr_str = ' ' + attr_str
  end

  attr_str
end

#binding_parts_and_count(value) ⇒ Object

Takes a string and splits on bindings, returns the string split on bindings and the number of bindings.



63
64
65
66
67
68
69
70
71
72
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 63

def binding_parts_and_count(value)
  if value.is_a?(String)
    parts = value.split(/(\{\{[^\}]+\}\})/).reject(&:blank?)
  else
    parts = ['']
  end
  binding_count = parts.count { |p| p[0] == '{' && p[1] == '{' && p[-2] == '}' && p[-1] == '}' }

  [parts, binding_count]
end

#getter_to_setter(getter) ⇒ Object

TODO: We should use a real parser for this



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 104

def getter_to_setter(getter)
  getter = getter.strip.gsub(/\(\s*\)/, '')

  # Check to see if this can be converted to a setter
  if getter[0] =~ /^[A-Z]/ && getter[-1] != ')'
    if getter.index('.')
      "#{getter}=(val)"
    else
      "raise \"could not auto generate setter for `#{getter}`\""
    end
  elsif getter[0] =~ /^[a-z_]/ && getter[-1] != ')'
    # Convert a getter into a setter
    if getter.index('.') || getter.index('@')
      prefix = ''
    else
      prefix = 'self.'
    end

    "#{prefix}#{getter}=(val)"
  else
    "raise \"could not auto generate setter for `#{getter}`\""
  end
end

#process_attribute(tag_name, attributes, attribute_name, value) ⇒ Object



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
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 74

def process_attribute(tag_name, attributes, attribute_name, value)
  parts, binding_count = binding_parts_and_count(value)

  # if this attribute has bindings
  if binding_count > 0
    # Setup an id
    id = add_id_to_attributes(attributes)

    if parts.size > 1
      # Multiple bindings
      add_multiple_attribute(tag_name, id, attribute_name, parts, value)
    elsif parts.size == 1 && binding_count == 1
      getter = parts[0][2...-2].strip

      if getter =~ /^asset_url[ \(]/
        # asset url helper
        add_asset_url_attribute(getter, attributes)
        return # don't delete attr
      else
        # A single binding
        add_single_attribute(id, attribute_name, getter)
      end
    end

    # Remove the attribute
    attributes.delete(attribute_name)
  end
end

#process_attributes(tag_name, attributes) ⇒ Object

Take the attributes and create any bindings



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 29

def process_attributes(tag_name, attributes)
  new_attributes = attributes.dup

  attributes.each_pair do |name, value|
    if name[0..1] == 'e-'
      process_event_binding(tag_name, new_attributes, name, value)
    else
      process_attribute(tag_name, new_attributes, name, value)
    end
  end

  new_attributes
end

#process_event_binding(tag_name, attributes, name, value) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/volt/server/html_parser/attribute_scope.rb', line 43

def process_event_binding(tag_name, attributes, name, value)
  id = add_id_to_attributes(attributes)

  event = name[2..-1]

  if tag_name == 'a'
    # For links, we need to add blank href to make it clickable.
    attributes['href'] ||= ''
  end

  # Remove the e- attribute
  attributes.delete(name)

  value = self.class.methodize_string(value)

  save_binding(id, "lambda { |__p, __t, __c, __id| Volt::EventBinding.new(__p, __t, __c, __id, #{event.inspect}, Proc.new {|event| #{value} })}")
end