Class: Ruty::Context

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/ruty/context.rb

Overview

represents the internal namespace used by ruty It basically works like a hash just that it has multiple layers which can be pushed and popped. That feature is used by the template engine in loops, blocks and other block elements which set variables.

Instance Method Summary collapse

Constructor Details

#initialize(initial = nil) ⇒ Context

create a new context instance. initial can be a hash which represents the initial root stack which cannot be popped.



24
25
26
# File 'lib/ruty/context.rb', line 24

def initialize initial=nil
  @stack = [initial || {}]
end

Instance Method Details

#[](name) ⇒ Object

start a recursive lookup for name.



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

def [] name
  @stack.each do |hash|
    val = hash[name]
    return val if not val.nil?
  end
  nil
end

#[]=(name, value) ⇒ Object

manipulate the outermost hash.



41
42
43
# File 'lib/ruty/context.rb', line 41

def []= name, value
  @stack[-1][name] = value
end

#apply_filters(value, filters) ⇒ Object

apply filters on a value



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/ruty/context.rb', line 131

def apply_filters value, filters
  filters.each do |filter|
    name, args = filter[0], filter[1..-1]
    filter = Filters[name]
    raise TemplateRuntimeError, "filter '#{name}' missing" if filter.nil?
    args.map! do |arg|
      if arg.kind_of?(Symbol)
        resolve(arg)
      else
        arg
      end
    end
    value = filter.call(self, value, *args)
  end
  value
end

#each(&block) ⇒ Object

call a block for each item in the context. if an item exists in two layers, only the item from the higher layer is yielded.



62
63
64
65
66
67
68
69
70
71
# File 'lib/ruty/context.rb', line 62

def each &block
  found = {}
  @stack.reverse_each do |hash|
    hash.each do |key, value|
      next if found.include?(key)
      found[key] = hash
      block.call(key, value)
    end
  end
end

#has_key?(key) ⇒ Boolean

checks if the key exists in one of the hashes.

Returns:

  • (Boolean)


55
56
57
# File 'lib/ruty/context.rb', line 55

def has_key? key
  not send(:[], key).nil?
end

#popObject

pop the outermost hash from the stack and return it. The root hash is never popped, in that case the method returns nil.



36
37
38
# File 'lib/ruty/context.rb', line 36

def pop
  @stack.pop if @stack.size > 1
end

#pretty_print(q) ⇒ Object

overrides pretty print so that the output for the debug tag looks nicer



75
76
77
78
79
80
81
# File 'lib/ruty/context.rb', line 75

def pretty_print q
  t = {}
  each do |key, value|
    t[key] = value
  end
  q.pp_hash(t)
end

#push(hash = nil) ⇒ Object

push a new empty or given hash to the stack.



29
30
31
# File 'lib/ruty/context.rb', line 29

def push hash=nil
  @stack << (hash or {})
end

#resolve(path) ⇒ Object

method that resolves dotted names. Internal it first tries to access hash keys, later array indices and if this also does not work it looks for a ruty_safe? function on the object, calls it with the current part of the dotted name, and if it returns true it calls it without arguments and uses the output as new object for the next part.

{{ foo.bar.blah.42 }}

could for example resolve this:

{{ foo['bar']['blah'][42] }}

call this method only with symbols, numbers and strings are meant to be catched somewhere first.



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/ruty/context.rb', line 99

def resolve path
  # start a recursive lookup#
  current = self
  path.to_s.split(/\./).each do |part|
    part_sym = part.to_sym
    # try hash like objects (with has_key? and [])
    if current.respond_to?(:has_key?) and tmp = current[part_sym]
      current = tmp
    # try hash like objects with integers and array. If this
    # fails we don't try any longer because method names which
    # start with numbers are illegal.
    elsif part =~ /^-?\d+$/
      if current.respond_to?(:fetch) or current.respond_to?(:has_key?) \
         and tmp = current[part.to_i]
        current = tmp
      else
        return nil
      end
    # try method calls on objects with ruty_safe? methods
    elsif current.respond_to?(:ruty_safe?) and
          current.ruty_safe?(part_sym)
      current = current.send(part_sym)
    # fail with nil in all other cases.
    else
      return nil
    end
  end
  
  current
end