Class: Kenji::Controller

Inherits:
Object
  • Object
show all
Defined in:
lib/kenji/controller.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#kenjiObject

use the reader freely to grab the kenji object



6
7
8
# File 'lib/kenji/controller.rb', line 6

def kenji
  @kenji
end

Class Method Details

.all(path, &block) ⇒ Object

Route all methods for given path



41
42
43
# File 'lib/kenji/controller.rb', line 41

def self.all(path, &block)
  route(:get, :post, :put, :delete, path, &block)
end

.before(&block) ⇒ Object

This lets you define before blocks.

class MyController < Kenji::Controller
  before do
    # eg. ensure authentication, you can use kenji.respond in here.
  end
end


101
102
103
104
105
106
# File 'lib/kenji/controller.rb', line 101

def self.before(&block)
  define_method(:_tmp_before_action, &block)
  block = instance_method(:_tmp_before_action)
  remove_method(:_tmp_before_action)
  (@befores ||= []) << block
end

.delete(path, &block) ⇒ Object

Route DELETE



34
35
36
# File 'lib/kenji/controller.rb', line 34

def self.delete(path, &block)
  route(:delete, path, &block)
end

.fallback(&block) ⇒ Object



45
46
47
48
# File 'lib/kenji/controller.rb', line 45

def self.fallback(&block)
  define_method(:fallback, &block)
  nil # void method
end

.get(path, &block) ⇒ Object

Route GET



19
20
21
# File 'lib/kenji/controller.rb', line 19

def self.get(path, &block)
  route(:get, path, &block)
end

.pass(path, controller) ⇒ Object

This lets us pass the routing down to another controller for a sub-path.

class MyController < Kenji::Controller
  pass '/admin/*', AdminController
  # regular routes
end


81
82
83
84
85
86
87
88
89
90
# File 'lib/kenji/controller.rb', line 81

def self.pass(path, controller)
  node = (@passes ||= {})
  segments = path.split('/')
  segments = segments.drop(1) if segments.first == ''     # discard leading /'s empty segment
  segments.each do |segment|
    node = (node[segment.to_sym] ||= {})
    break if segment == '*'
  end
  node[:@controller] = controller
end

.post(path, &block) ⇒ Object

Route POST



24
25
26
# File 'lib/kenji/controller.rb', line 24

def self.post(path, &block)
  route(:post, path, &block)
end

.put(path, &block) ⇒ Object

Route PUT



29
30
31
# File 'lib/kenji/controller.rb', line 29

def self.put(path, &block)
  route(:put, path, &block)
end

.route(*methods, path, &block) ⇒ Object

Route a given path to the correct block, for any given methods

Note: this works by building a tree for the path, each node being a path segment or variable segment, and the leaf @action being the block



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/kenji/controller.rb', line 55

def self.route(*methods, path, &block)
  # bind the block to self as an instance method, so its context is correct
  define_method(:_tmp_route_action, &block)
  block = instance_method(:_tmp_route_action)
  remove_method(:_tmp_route_action)
  # store the block for each method
  methods.each do |method|
    node = ((@routes ||= {})[method] ||= {})
    segments = path.split('/')
    segments = segments.drop(1) if segments.first == ''     # discard leading /'s empty segment
    segments.each do |segment|                              # lazily create tree
      segment = ':' if segment =~ /^:/                      # discard :variable name
      node = (node[segment.to_sym] ||= {})
    end
    node[:@action] = block                                  # store block as leaf in @action
  end
  nil # void method
end

Instance Method Details

#attempt_fallback(path) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
# File 'lib/kenji/controller.rb', line 155

def attempt_fallback(path)
  if respond_to? :fallback
    if self.class.instance_method(:fallback).arity == 1
      return fallback(path)
    else
      return fallback
    end
  else
    kenji.respond(404, 'Not found!')
  end
end

#call(method, path) ⇒ Object

Most likely only used by Kenji itself. Override to implement your own routing, if you’d like.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/kenji/controller.rb', line 112

def call(method, path)

  self.class.befores.each {|b| b.bind(self).call }

  segments = path.split('/')
  segments = segments.drop(1) if segments.first == ''     # discard leading /'s empty segment

  # check for passes
  node = self.class.passes
  remaining_segments = segments.dup

  f = fetch_passes(node, remaining_segments)

  if f[:match] && f[:controller]
    instance = f[:controller].new
    instance.kenji = kenji if instance.respond_to?(:kenji=)
    f[:variables].each do |k, v|
      instance.instance_variable_set(:"@#{k}", v)
    end
    return instance.call(method, f[:remaining_segments].join('/'))
  end

  # regular routing
  node = self.class.routes[method] || {}
  variables = []
  searching = true
  segments.each do |segment|                              # traverse tree to find
    if searching && node[segment.to_sym]
      node = node[segment.to_sym]                         # attempt to move down to the plain text segment
    elsif searching && node[:':']
      node = node[:':']                                   # attempt to find a variable segment
      variables << segment                                # either we've found a variable, or the `unless` below will trigger
    else
      return attempt_fallback(path)                       # route failed to match variable or segment node so attempt fallback
    end
  end
  if node && action = node[:@action]                      # the block is stored in the @action leaf
    return action.bind(self).call(*variables)
  else                                                    # or, fallback if necessary store the block for each method
    return attempt_fallback(path)
  end
end

#log(*args) ⇒ Object

Utility method: this can be used to log to stderr cleanly.



170
171
172
# File 'lib/kenji/controller.rb', line 170

def log(*args)
  kenji.stderr.puts(*args)
end