Class: FlavourSaver::Runtime

Inherits:
Object
  • Object
show all
Defined in:
lib/flavour_saver/runtime.rb

Defined Under Namespace

Classes: BlockRuntime

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ast, context = nil, locals = {}, helpers = [], parent = nil) ⇒ Runtime

Returns a new instance of Runtime.



17
18
19
20
21
22
23
24
# File 'lib/flavour_saver/runtime.rb', line 17

def initialize(ast, context=nil, locals={}, helpers=[],parent=nil)
  @ast = ast
  @locals = locals
  @helpers = helpers
  @context = context
  @parent = parent
  @privates = parent ? parent.privates.dup : { 'root' => context }
end

Instance Attribute Details

#astObject

Returns the value of attribute ast.



11
12
13
# File 'lib/flavour_saver/runtime.rb', line 11

def ast
  @ast
end

#contextObject

Returns the value of attribute context.



11
12
13
# File 'lib/flavour_saver/runtime.rb', line 11

def context
  @context
end

#parentObject

Returns the value of attribute parent.



11
12
13
# File 'lib/flavour_saver/runtime.rb', line 11

def parent
  @parent
end

#privatesObject

Returns the value of attribute privates.



11
12
13
# File 'lib/flavour_saver/runtime.rb', line 11

def privates
  @privates
end

Class Method Details

.run(ast, context, locals = {}, helpers = []) ⇒ Object



13
14
15
# File 'lib/flavour_saver/runtime.rb', line 13

def self.run(ast, context, locals={}, helpers=[])
  self.new(ast, context, locals, helpers).to_s
end

Instance Method Details

#create_child_runtime(body = []) ⇒ Object



200
201
202
203
# File 'lib/flavour_saver/runtime.rb', line 200

def create_child_runtime(body=[])
  node = body.is_a?(TemplateNode) ? body : TemplateNode.new(body)
  Runtime.new(node,nil,@locals,@helpers,self)
end

#evaluate_argument(arg) ⇒ Object



138
139
140
141
142
143
144
# File 'lib/flavour_saver/runtime.rb', line 138

def evaluate_argument(arg)
  if arg.is_a? Array
    evaluate_object_path(arg)
  else
    evaluate_node(arg)
  end
end

#evaluate_block(node, block_context = @context) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
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
194
195
196
197
198
# File 'lib/flavour_saver/runtime.rb', line 156

def evaluate_block(node,block_context=@context)
  call = node.method.first
  content_runtime = create_child_runtime(node.contents)
  alternate_runtime = create_child_runtime(node.alternate) if node.respond_to? :alternate
  block_runtime = BlockRuntime.new(block_context,content_runtime,alternate_runtime)

  result = evaluate_call(call, block_context) { block_runtime }

  # If the helper fails to call it's provided block then all
  # sorts of wacky default behaviour kicks in. I don't like it,
  # but that's the spec.
  if !block_runtime.rendered?

    # If the result is collectiony then act as an implicit
    # "each"
    if result && result.respond_to?(:each)
      if result.respond_to?(:size) && (result.size > 0)
        r = []
        # Not using #each_with_index because the object might
        # not actually be an Enumerable
        count = 0
        result.each do |e|
          r << block_runtime.contents(e, {'index' => count})
          count += 1
        end
        result = r.join('')
      else
        result = block_runtime.inverse
      end

    # Otherwise it behaves as an implicit "if"
    elsif result
      result = block_runtime.contents result
    else
      if block_runtime.has_inverse?
        result = block_runtime.inverse
      else
        result = ''
      end
    end
  end
  result
end

#evaluate_call(call, context = self.context, &block) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/flavour_saver/runtime.rb', line 118

def evaluate_call(call, context=self.context, &block)
  context = Helpers.decorate_with(context,@helpers,@locals) unless context.is_a? Helpers::Decorator
  case call
  when ParentCallNode
    depth = call.depth
    (2..depth).inject(parent) { |p| p.parent }.evaluate_call(call.to_callnode,&block)
  when LiteralCallNode
    result = context.send(:[], call.name)
    result = result.call(*call.arguments.map { |a| evaluate_argument(a) },&block) if result.respond_to? :call
    result
  when LocalVarNode
    result = private_variable_get(call.name)
  else
    if call.parent.is_a? BlockExpressionNode and !context.respond_to? call.name
      raise UnknownHelperException, "Template context doesn't respond to method #{call.name.inspect}."
    end
    context.public_send(call.name, *call.arguments.map { |a| evaluate_argument(a) }, &block)
  end
end

#evaluate_expression(node, &block) ⇒ Object



152
153
154
# File 'lib/flavour_saver/runtime.rb', line 152

def evaluate_expression(node, &block)
  evaluate_object_path(node.method)
end

#evaluate_node(node) ⇒ Object



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
# File 'lib/flavour_saver/runtime.rb', line 58

def evaluate_node(node)
  case node
  when TemplateNode
    node.items.map { |n| evaluate_node(n) }.join('')
  when BlockExpressionNode
    evaluate_block(node).to_s
  when OutputNode
    node.value
  when NumberNode
    if node.value =~ /\./
      node.value.to_f
    else
      node.value.to_i
    end
  when ValueNode
    node.value
  when SafeExpressionNode
    evaluate_expression(node).to_s
  when ExpressionNode
    escape(evaluate_expression(node).to_s)
  when CallNode
    evaluate_call(node)
  when Hash
    node.each do |key,value|
      node[key] = evaluate_argument(value)
    end
    node
  when CommentNode
    ''
  when PartialNode
    evaluate_partial(node)
  else
    raise UnknownNodeTypeException, "Don't know how to deal with a node of type #{node.class.to_s.inspect}."
  end
end

#evaluate_object_path(path, &block) ⇒ Object



146
147
148
149
150
# File 'lib/flavour_saver/runtime.rb', line 146

def evaluate_object_path(path, &block)
  path.inject(context) do |context,call|
    context = evaluate_call(call, context, &block)
  end
end

#evaluate_partial(node) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/flavour_saver/runtime.rb', line 103

def evaluate_partial(node)
  _context = context
  _context = evaluate_argument(node.context) if node.context
  if defined?(::Rails)
    context.send(:render, :partial => node.name, :object => _context)
  else
    partial = Partial.fetch(node.name)
    if partial.respond_to? :call
      partial.call(_context)
    else
      create_child_runtime(partial).to_s(_context)
    end
  end
end

#inspectObject



205
206
207
# File 'lib/flavour_saver/runtime.rb', line 205

def inspect
  "#<FlavourSaver::Runtime contents=#{@ast.inspect}>"
end

#parent?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/flavour_saver/runtime.rb', line 99

def parent?
  !!@parent
end

#private_variable_get(name) ⇒ Object



46
47
48
49
50
51
52
# File 'lib/flavour_saver/runtime.rb', line 46

def private_variable_get(name)
  begin
    @privates.fetch(name)
  rescue KeyError
    raise UndefinedPrivateVariableException, "private variable not found @#{name}"
  end
end

#private_variable_set(name, value) ⇒ Object



42
43
44
# File 'lib/flavour_saver/runtime.rb', line 42

def private_variable_set(name,value)
  @privates[name.to_s] = value
end

#strip(tmp_context = nil) ⇒ Object



54
55
56
# File 'lib/flavour_saver/runtime.rb', line 54

def strip(tmp_context = nil)
  self.to_s(tmp_context).gsub(/[\s]+/,' ').strip
end

#to_s(tmp_context = nil, privates = {}) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/flavour_saver/runtime.rb', line 26

def to_s(tmp_context = nil,privates={})
  result = nil
  if tmp_context
    old_context = @context
    @context = tmp_context
    old_privates = @privates
    @privates = @privates.dup.merge(privates) if privates.any?
    result = evaluate_node(@ast)
    @privates = old_privates
    @context = old_context
  else
    result = evaluate_node(@ast).to_s.force_encoding(Encoding::default_external)
  end
  result
end