Class: Nyara::Route

Inherits:
Object show all
Defined in:
lib/nyara/route.rb

Constant Summary collapse

REQUIRED_ATTRS =
[:http_method, :scope, :prefix, :suffix, :controller, :id, :conv]
TOKEN =

private +++

/%(?:[sz]|(?>\.\d+)?[dfux])/
FORWARD_SPLIT =
/(?=#{TOKEN})/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(&p) ⇒ Route

Returns a new instance of Route.


17
18
19
# File 'lib/nyara/route.rb', line 17

def initialize &p
  instance_eval &p if p
end

Instance Attribute Details

#accept_extsObject

optional


12
13
14
# File 'lib/nyara/route.rb', line 12

def accept_exts
  @accept_exts
end

#accept_mimesObject

optional


12
13
14
# File 'lib/nyara/route.rb', line 12

def accept_mimes
  @accept_mimes
end

#classesObject

optional


12
13
14
# File 'lib/nyara/route.rb', line 12

def classes
  @classes
end

#http_method=(value) ⇒ Object (writeonly)

Sets the attribute http_method

Parameters:

  • value

    the value to set the attribute http_method to.


5
6
7
# File 'lib/nyara/route.rb', line 5

def http_method=(value)
  @http_method = value
end

#idObject

NOTE id is stored in symbol for C-side conenience, but returns as string for Ruby-side goodness


7
8
9
# File 'lib/nyara/route.rb', line 7

def id
  @id.to_s
end

Class Method Details

.canonicalize_callback_selector(selector) ⇒ Object

remove .klass and :method from selector, and validate selector format


222
223
224
225
226
227
228
229
230
231
232
# File 'lib/nyara/route.rb', line 222

def canonicalize_callback_selector selector
  /\A
    (?<id>\#\w++(?:\-\w++)*)?
    (?<klass>\.\w++(?:\-\w++)*)?
    (?<method>:\w+)?
  \z/x =~ selector
  unless id or klass or method
    raise ArgumentError, "bad selector: #{selector.inspect}", caller[1..-1]
  end
  id.presence or selector.sub(/:\w+\z/, &:upcase)
end

.clearObject


234
235
236
237
238
# File 'lib/nyara/route.rb', line 234

def clear
  # gc mark fail if wrong order?
  Ext.clear_route
  @controllers = []
end

.compileObject


184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/nyara/route.rb', line 184

def compile
  @global_path_templates = {} # "name#id" => path
  mapped_controllers = {}

  @routes = @controllers.flat_map do |scope, c|
    if c.is_a?(String)
      c = name2const c
    end
    name = c.controller_name || const2name(c)
    raise "#{c.inspect} is not a Nyara::Controller" unless Controller > c

    if mapped_controllers[c]
      raise "controller #{c.inspect} was already mapped"
    end
    mapped_controllers[c] = true

    c.nyara_compile_routes(scope).each do |e|
      @global_path_templates[name + e.id] = e.path_template
    end
  end
  @routes.sort_by! &:prefix
  @routes.reverse!

  mapped_controllers.each do |c, _|
    c.path_templates = @global_path_templates.merge c.path_templates
  end

  Ext.clear_route
  @routes.each do |e|
    Ext.register_route e
  end
end

.const2name(c) ⇒ Object

private


253
254
255
256
257
258
# File 'lib/nyara/route.rb', line 253

def const2name c
  name = c.to_s.sub /Controller$/, ''
  name.gsub!(/(?<!\b)[A-Z]/){|s| "_#{s.downcase}" }
  name.gsub!(/[A-Z]/, &:downcase)
  name
end

.global_path_template(id) ⇒ Object


217
218
219
# File 'lib/nyara/route.rb', line 217

def global_path_template id
  @global_path_templates[id]
end

.name2const(name) ⇒ Object


260
261
262
263
264
265
266
# File 'lib/nyara/route.rb', line 260

def name2const name
  return Module.const_get(name) if name[0] =~ /[A-Z]/
  name = name.gsub /(?<=\b|_)[a-z]/, &:upcase
  name.gsub! '_', ''
  name << 'Controller'
  Module.const_get name
end

240
241
242
243
244
245
246
247
248
249
# File 'lib/nyara/route.rb', line 240

def print_routes
  puts "All routes:"
  Nyara::Route.routes.each do |route|
    cname = const2name route.controller
    print "#{cname}#{route.id}".rjust(30), " "
    print route.http_method_to_s.ljust(6), " "
    print route.path_template
    puts
  end
end

.register_controller(scope, controller) ⇒ Object

Param

NOTE controller may be not defined when register_controller is called


176
177
178
179
180
181
182
# File 'lib/nyara/route.rb', line 176

def register_controller scope, controller
  unless scope.is_a?(String)
    raise ArgumentError, "route prefix should be a string"
  end
  scope = scope.dup.freeze
  (@controllers ||= []) << [scope, controller]
end

.routesObject


167
168
169
# File 'lib/nyara/route.rb', line 167

def routes
  @routes || []
end

Instance Method Details

#analyse_path(path) ⇒ Object

Split the path into 2 parts:
a fixed prefix and a variable suffix


158
159
160
161
162
# File 'lib/nyara/route.rb', line 158

def analyse_path path
  raise 'path must contain no new line' if path.index "\n"
  raise 'path must start with /' unless path.start_with? '/'
  path.split(FORWARD_SPLIT, 2)
end

#compile(controller, scope) ⇒ Object

Compute prefix, suffix, conv
NOTE routes may be inherited, so late-setting controller is necessary


63
64
65
66
67
68
69
70
71
72
73
# File 'lib/nyara/route.rb', line 63

def compile controller, scope
  @controller = controller
  @scope = scope

  path = scope.sub /\/?$/, @path
  if path.empty?
    path = '/'
  end
  @prefix, suffix = analyse_path path
  @suffix, @conv = compile_re suffix
end

#compile_re(suffix) ⇒ Object

Returns

[str_re, conv]

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
154
# File 'lib/nyara/route.rb', line 114

def compile_re suffix
  return ['', []] unless suffix
  conv = []
  segs = suffix.split(FORWARD_SPLIT).flat_map do |s|
    if (s =~ TOKEN) == 0
      part1 = s[TOKEN]
      [part1, s.slice(part1.size..-1)]
    else
      s
    end
  end
  re_segs = segs.map do |s|
    case s
    when /\A%(?>\.\d+)?([dfux])\z/
      case $1
      when 'd'
        conv << :to_i
        '(-?\d+)'
      when 'f'
        conv << :to_f
        # just copied from scanf
        '([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))'
      when 'u'
        conv << :to_i
        '(\d+)'
      when 'x'
        conv << :hex
        '(\h+)'
      end
    when '%s'
      conv << :to_s
      '([^/]+)'
    when '%z'
      conv << :to_s
      '(.*)'
    else
      Regexp.quote s
    end
  end
  ["^#{re_segs.join}$", conv]
end

#http_method_overrideObject

nil for get / post


28
29
30
31
32
33
# File 'lib/nyara/route.rb', line 28

def http_method_override
  m = http_method_to_s
  if m != 'GET' and m != 'POST'
    m
  end
end

#http_method_to_sObject

http_method in string form


22
23
24
25
# File 'lib/nyara/route.rb', line 22

def http_method_to_s
  m, _ = HTTP_METHODS.find{|k,v| v == http_method}
  m
end

#matched_lifecycle_callbacks(filters) ⇒ Object

find blocks in filters that match selectors


45
46
47
48
49
50
51
52
53
54
55
# File 'lib/nyara/route.rb', line 45

def matched_lifecycle_callbacks filters
  actions = []
  selectors = selectors()
  if selectors and filters
    # iterate with filter's order to preserve define order
    filters.each do |sel, blks|
      actions.concat blks if selectors.include?(sel)
    end
  end
  actions
end

#path_templateObject


57
58
59
# File 'lib/nyara/route.rb', line 57

def path_template
  File.join @scope, (@path.gsub '%z', '%s')
end

#selectorsObject

enum all combinations of matching selectors


36
37
38
39
40
41
42
# File 'lib/nyara/route.rb', line 36

def selectors
  if classes
    [id, *classes, *classes.map{|k| "#{k}:#{http_method_to_s}"}, ":#{http_method_to_s}"]
  else
    [id, ":#{http_method_to_s}"]
  end
end

#set_accept_exts(a) ⇒ Object

Compute accept_exts, accept_mimes


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/nyara/route.rb', line 76

def set_accept_exts a
  @accept_exts = {}
  @accept_mimes = []
  if a
    a.each do |e|
      e = e.to_s.dup.freeze
      @accept_exts[e] = true
      if MIME_TYPES[e]
        v1, v2 = MIME_TYPES[e].split('/')
        raise "bad mime type: #{MIME_TYPES[e].inspect}" if v1.nil? or v2.nil?
        @accept_mimes << [v1, v2, e]
      end
    end
  end
  @accept_mimes = nil if @accept_mimes.empty?
  @accept_exts = nil if @accept_exts.empty?
end

#validateObject

Raises:

  • (ArgumentError)

94
95
96
97
98
99
100
101
# File 'lib/nyara/route.rb', line 94

def validate
  REQUIRED_ATTRS.each do |attr|
    unless instance_variable_get("@#{attr}")
      raise ArgumentError, "missing #{attr}"
    end
  end
  raise ArgumentError, "id must be symbol" unless @id.is_a?(Symbol)
end