Module: Tanuki::ControllerBehavior

Extended by:
ClassMethods
Includes:
Enumerable
Included in:
I18n, Tanuki_Controller
Defined in:
lib/tanuki/behavior/controller_behavior.rb

Overview

Tanuki::ControllerBehavior contains basic methods for a framework controller. In is included in the base controller class.

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ClassMethods

arg_defs, escape, extended, extract_args, grow_link, has_arg

Class Method Details

.dispatch(ctx, klass, request_path) ⇒ Object

Dispathes route chain in context ctx on request_path, starting with controller klass.



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/tanuki/behavior/controller_behavior.rb', line 273

def dispatch(ctx, klass, request_path)
  route_parts = parse_path(request_path)
  curr = root_ctrl = klass.new(ctx, nil, nil, true)
  route_parts.each do |route_part|
    curr.instance_variable_set :@_active, true
    nxt = curr[route_part[:route], *route_part[:args]]
    curr.logical_child = nxt
    curr = nxt
  end
  while route_part = curr.default_route
    if route_part[:redirect]
      klass = curr.child_class(route_part)
      return {:type => :redirect, :location => grow_link(curr, route_part, klass.arg_defs)}
    end
    curr.instance_variable_set :@_active, true
    nxt = curr[route_part[:route], *route_part[:args]]
    curr.logical_child = nxt
    curr = nxt
  end
  curr.instance_variable_set :@_active, true
  curr.instance_variable_set :@_current, true
  type = (curr.is_a? ctx.missing_page) ? :missing_page : :page
  prev = curr
  while curr = prev.visual_parent
    curr.visual_child = prev
    prev = curr
  end
  {:type => type, :controller => prev}
end

.included(mod) ⇒ Object

Extends the including module with Tanuki::ControllerBehavior::ClassMethods.



304
305
306
# File 'lib/tanuki/behavior/controller_behavior.rb', line 304

def included(mod)
  mod.extend ClassMethods
end

Instance Method Details

#[](route, *args) ⇒ Object

Initializes and retrieves child controller on route. Searches static, dynamic, and ghost routes (in that order).



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
# File 'lib/tanuki/behavior/controller_behavior.rb', line 42

def [](route, *args)
  byname = (args.length == 1 and args[0].is_a? Hash)
  ensure_configured!
  key = [route, args.dup]
  if cached = @_cache[key]
    # Return form cache
    return cached
  elsif child_def = @_child_defs[route]
    # Search static routes
    klass = child_def[:class]
    args = klass.extract_args(args[0]) if byname
    child = klass.new(process_child_context(@_ctx, route), self, {:route => route, :args => args}, child_def[:model])
  else
    # Search dynamic routes
    found = false
    s = route.to_s
    @_child_collection_defs.each do |collection_def|
      if md = collection_def[:parse].match(s)
        a_route = md['route'].to_sym
        child_def = collection_def[:fetcher].fetch(a_route, collection_def[:format])
        if child_def
          klass = child_def[:class]
          args = klass.extract_args(args[0]) if byname
          embedded_args = klass.extract_args(md)
          args.each_index {|i| embedded_args[i] = args[i] if args[i] }
          child = klass.new(process_child_context(@_ctx, a_route), self,
            {:route => a_route, :args => embedded_args}, child_def[:model])
          found = true
          break child
        end
      end
    end
    # If still not found, search ghost routes
    child = missing_route(route, *args) unless found
  end
  @_cache[key] = child # Thread safe (possible overwrite, but within consistent state)
end

#_ctx(ctx) ⇒ Object

Returns controller context. Used internally by templates.



37
38
39
# File 'lib/tanuki/behavior/controller_behavior.rb', line 37

def _ctx(ctx)
  @_ctx
end

#active?Boolean

Returns true, if controller is active.

Returns:

  • (Boolean)


81
82
83
# File 'lib/tanuki/behavior/controller_behavior.rb', line 81

def active?
  @_active
end

#child_class(route) ⇒ Object

Retrieves child controller class on route. Searches static, dynamic, and ghost routes (in that order).



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/tanuki/behavior/controller_behavior.rb', line 86

def child_class(route)
  ensure_configured!
  args = []
  key = [route, args]
  if cached = @_cache[key]
    # Return from cache
    return cached.class
  elsif child_def = @_child_defs[route]
    # Return from static routes
    return child_def[:class]
  else
    # Search dynamic routes
    s = route.to_s
    @_child_collection_defs.each do |collection_def|
      if md = collection_def[:parse].match(s)
        a_route = md['route'].to_sym
        child_def = collection_def[:fetcher].fetch(a_route, collection_def[:format])
        return child_def[:class] if child_def
      end
    end
    # If still not found, search ghost routes
    return (@_cache[key] = missing_route(route, *args)).class
  end
end

#configureObject

Invoked when controller needs to be configured.



112
113
# File 'lib/tanuki/behavior/controller_behavior.rb', line 112

def configure
end

#current?Boolean

Returns true, if controller is current.

Returns:

  • (Boolean)


116
117
118
# File 'lib/tanuki/behavior/controller_behavior.rb', line 116

def current?
  @_current
end

#default_routeObject

If set, controller navigates to a given child route by default. Returned object should be either nil (don’t navigate), or a Hash with keys:

  • :route is the Symbol for the route

  • :args contain route arguments Hash

  • :redirect makes a 302 redirect to this route, if true (optional)



125
126
127
# File 'lib/tanuki/behavior/controller_behavior.rb', line 125

def default_route
  nil
end

#each(&block) ⇒ Object

Calls block once for each visible child controller on static or dynamic routes, passing it as a parameter.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/tanuki/behavior/controller_behavior.rb', line 130

def each(&block)
  return Enumerator.new(self) unless block_given?
  ensure_configured!
  @_child_defs.each_pair do |route, child|
    if route.is_a? Regexp
      cd = @_child_collection_defs[child]
      cd[:fetcher].fetch_all(cd[:format]) do |child_def|
        key = [child_def[:route], []]
        unless child = @_cache[key]
          child = child_def[:class].new(process_child_context(@_ctx, route), self,
            {:route => child_def[:route], :args => {}}, child_def[:model])
          @_cache[key] = child
        end
        block.call child
      end
    else
      yield self[route] unless child[:hidden]
    end
  end
  self
end

#ensure_configured!Object

Invoked when controller configuration needs to be ensured.



153
154
155
156
157
158
159
160
161
162
163
# File 'lib/tanuki/behavior/controller_behavior.rb', line 153

def ensure_configured!
  unless @_configured
    @_child_defs = {}
    @_child_collection_defs = []
    @_cache={}
    @_length = 0
    configure
    @_configured = true
  end
  nil
end

Returns the link to the current controller, switching the active controller on the respective path level to self.



166
167
168
169
170
171
# File 'lib/tanuki/behavior/controller_behavior.rb', line 166

def forward_link
  uri_parts = @_ctx.env['PATH_INFO'].split(/(?<!\$)\//)
  link_parts = link.split(/(?<!\$)\//)
  link_parts.each_index {|i| uri_parts[i] = link_parts[i] }
  uri_parts.join('/') << ((qs = @_ctx.env['QUERY_STRING']).empty? ? '' : "?#{qs}")
end

#initialize(ctx, logical_parent, route_part, model = nil) ⇒ Object

Creates new controller with context ctx, logical_parent controller, route_part definitions and a model.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/tanuki/behavior/controller_behavior.rb', line 13

def initialize(ctx, logical_parent, route_part, model=nil)
  @_configured = false
  @_ctx = ctx
  @_model = model
  @_args = {}
  if @_logical_parent = logical_parent
    @_route = route_part[:route]
    self.class.arg_defs.each_pair do |arg_name, arg_def|
      route_part[:args][arg_def[:index]] = @_args[arg_name] = arg_def[:arg].to_value(route_part[:args][arg_def[:index]])
    end
    @_link = self.class.grow_link(@_logical_parent, {:route => @_route, :args => @_args}, self.class.arg_defs)
    initialize_route(*route_part[:args])
  else
    @_link = '/'
    @_route = nil
    initialize_route
  end
end

#initialize_route(*args) ⇒ Object

Invoked with route args when current route is initialized.



33
34
# File 'lib/tanuki/behavior/controller_behavior.rb', line 33

def initialize_route(*args)
end

#lengthObject Also known as: size

Returns the number of visible child controllers on static and dynamic routes.



174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/tanuki/behavior/controller_behavior.rb', line 174

def length
  if @_child_collection_defs.length > 0
    if @_length_is_valid
      @_length
    else
      @_child_collection_defs.each {|cd| @_length += cd[:fetcher].length }
      @_length_is_valid = true
    end
  else
    @_length
  end
end

#process_child_context(ctx, route) ⇒ Object

Invoked when child controller context needs to be processed before initializing.



188
189
190
# File 'lib/tanuki/behavior/controller_behavior.rb', line 188

def process_child_context(ctx, route)
  ctx
end

#to_sObject

Returns controller string representation. Defaults to route name.



195
196
197
# File 'lib/tanuki/behavior/controller_behavior.rb', line 195

def to_s
  @_route.to_s
end

#visual_parentObject

Invoked when visual parent needs to be determined. Defaults to logical parent.



200
201
202
# File 'lib/tanuki/behavior/controller_behavior.rb', line 200

def visual_parent
  @_logical_parent
end