Class: FileSystemRouting

Inherits:
Object
  • Object
show all
Extended by:
StringUtils
Defined in:
lib/routing/file_system.rb

Overview

A file-system based router, like Next.js

Class Method Summary collapse

Instance Method Summary collapse

Methods included from StringUtils

snake_of_pascal

Class Method Details

.regexp_of_name(const_name) ⇒ Object

::Hello => ^/hello$ ::Hello::X => ^/hello/(\w+)$



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/routing/file_system.rb', line 35

def regexp_of_name(const_name)
  # namespace
  inner = const_name.split('::').map do |part|
    case part
    when 'Index' then ''
    when 'X' then '(\w+)'
    when 'N' then '(\d+)'
    else snake_of_pascal(part)
    end
  end.join('/')
  Regexp.new("^/#{inner}$")
end

.register_route!(cpath, value) ⇒ Object



48
49
50
51
52
53
54
# File 'lib/routing/file_system.rb', line 48

def register_route!(cpath, value)
  return unless value.method_defined? :call

  routes << { method: 'GET',
              pattern: regexp_of_name(cpath),
              module: value }
end

.root(dir) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/routing/file_system.rb', line 56

def root(dir)
  from_dir = File.dirname(caller.first.split(':').first)
  @root = File.join(from_dir, dir)
  loader = Zeitwerk::Loader.new
  loader.on_load do |cpath, value, _abspath|
    register_route! cpath, value
  end
  loader.push_dir(@root)
  loader.setup

  loader.eager_load
end

.routesObject



28
29
30
# File 'lib/routing/file_system.rb', line 28

def routes
  @routes ||= []
end

Instance Method Details

#call(env) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/routing/file_system.rb', line 10

def call(env)
  route = self.class.routes.find do |route|
    route[:method] == env['REQUEST_METHOD'] and route[:pattern] =~ env['PATH_INFO']
  end

  if route
    # Call the module's `call` method
    obj = Object.new.extend route[:module]
    result = obj.call env, *Regexp.last_match.captures
    [200, { 'content-type' => 'text/html' }, [result]]
  else
    [404, { 'content-type' => 'text/html' }, ['404 Not Found']]
  end
end