Class: Ferro::Router

Inherits:
Object
  • Object
show all
Defined in:
opal/opal-ferro/ferro_router.js.rb

Overview

Wrapper for the web browsers history API. Note that there are no private methods in Opal. Methods that should be private are marked in the docs with ‘Internal method’.

Instance Method Summary collapse

Constructor Details

#initialize(page404) ⇒ Router

Create the router. Do not create a router directly, instead call the ‘router’ method that is available in all Ferro classes That method points to the router instance that is attached to the FerroDocument.

Parameters:

  • page404 (Method)

    Method that is called when the web browser navigates and no route can be found. Override the page404 method in FerroDocument.



18
19
20
21
22
# File 'opal/opal-ferro/ferro_router.js.rb', line 18

def initialize(page404)
  @routes  = []
  @page404 = page404
  setup_navigation_listener
end

Instance Method Details

#add_route(path, callback) ⇒ Object

Add a new route to the router.

Examples: when the following routes are created and the web browser navigates to a url matching the route, the callback method will be called with parameters:

add_route('/ferro/page1', my_cb) # '/ferro/page1'  => my_cb({})
add_route('/user/:id',    my_cb) # '/user/1'       => my_cb({id: 1})
add_route('/ferro',       my_cb) # '/ferro?page=1' => my_cb({page: 1})

Parameters:

  • path (String)

    Relative url (without protocol and host)

  • callback (Method)

    Method that is called when the web browser navigates and the path is matched. The callback method should accept one parameter [Hash] containing the parameters found in the matched url



38
39
40
# File 'opal/opal-ferro/ferro_router.js.rb', line 38

def add_route(path, callback)
  @routes << { parts: path_to_parts(path), callback: callback }
end

#add_search_to_params(search) ⇒ Object

Internal method to split search parameters

Parameters:

  • search (String)

    Url search parameters



167
168
169
170
171
172
173
174
175
176
# File 'opal/opal-ferro/ferro_router.js.rb', line 167

def add_search_to_params(search)
  if !search.empty?
    pars = search[1..-1].split('&')

    pars.each do |par|
      pair = par.split('=')
      @params[ pair[0] ] = pair[1] if pair.length == 2
    end
  end
end

#decode(value) ⇒ Object

URI decode a value

Parameters:

  • value (String)

    Value to decode



91
92
93
# File 'opal/opal-ferro/ferro_router.js.rb', line 91

def decode(value)
  `decodeURI(#{value})`
end

#get_locationObject

Internal method to get the new location



75
76
77
# File 'opal/opal-ferro/ferro_router.js.rb', line 75

def get_location
  Native(`new URL(window.location.href)`)
end

#get_matches(path) ⇒ Object

Internal method to match a path to possible routes

Parameters:

  • path (String)

    Url to match



131
132
133
134
135
136
137
138
139
140
# File 'opal/opal-ferro/ferro_router.js.rb', line 131

def get_matches(path)
  matches = []

  @routes.each_with_index do |route, i|
    score, pars = score_route(route[:parts], path)
    matches << [i, score, pars] if score > 0
  end

  matches
end

#go_backObject

Navigate back



70
71
72
# File 'opal/opal-ferro/ferro_router.js.rb', line 70

def go_back
  `history.back()`
end

#go_to(url) ⇒ Object

Navigate to url

Parameters:

  • url (String)

    Relative url (without protocol and host)



64
65
66
67
# File 'opal/opal-ferro/ferro_router.js.rb', line 64

def go_to(url)
  push_state(url)
  navigated
end

#match(path, search) ⇒ Object

Internal method to match a path to the most likely route

Parameters:

  • path (String)

    Url to match

  • search (String)

    Url search parameters



113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'opal/opal-ferro/ferro_router.js.rb', line 113

def match(path, search)
  matches = get_matches(path)

  if matches.length > 0
    match = matches.sort { |m| m[1] }.first

    @params = match[2]
    add_search_to_params(search)

    match[0]
  else
    nil
  end
end

Internal method called when the web browser navigates



96
97
98
99
100
101
102
103
104
105
106
107
# File 'opal/opal-ferro/ferro_router.js.rb', line 96

def navigated
  url = get_location
  @params = []

  idx = match(path_to_parts(decode(url.pathname)), decode(url.search))

  if idx
    @routes[idx][:callback].call(@params)
  else
    @page404.call(url.pathname)
  end
end

#path_to_parts(path) ⇒ Object

Internal method to split a path into components



80
81
82
83
84
85
86
# File 'opal/opal-ferro/ferro_router.js.rb', line 80

def path_to_parts(path)
  path.
    downcase.
    split('/').
    map { |part| part.empty? ? nil : part.strip }.
    compact
end

#push_state(url) ⇒ Object

Add a location to the web browsers history

Parameters:

  • url (String)

    Relative url (without protocol and host)



57
58
59
# File 'opal/opal-ferro/ferro_router.js.rb', line 57

def push_state(url)
  `history.pushState(null,null,#{url})`
end

#replace_state(url) ⇒ Object

Replace the current location in the web browsers history

Parameters:

  • url (String)

    Relative url (without protocol and host)



50
51
52
# File 'opal/opal-ferro/ferro_router.js.rb', line 50

def replace_state(url)
  `history.replaceState(null,null,#{url})`
end

#score_route(parts, path) ⇒ Object

Internal method to add a match score

Parameters:

  • parts (String)

    Parts of a route

  • path (String)

    Url to match



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'opal/opal-ferro/ferro_router.js.rb', line 146

def score_route(parts, path)
  score = 0
  pars  = {}

  if parts.length == path.length
    parts.each_with_index do |part, i|
      if part[0] == ':'
        score += 1
        pars["#{part[1..-1]}"] = path[i]
      elsif part == path[i].downcase
        score += 2
      end
    end
  end

  return score, pars
end

#setup_navigation_listenerObject

Internal method to set ‘onpopstate’



43
44
45
# File 'opal/opal-ferro/ferro_router.js.rb', line 43

def setup_navigation_listener
  `window.onpopstate = function(e){#{navigated}}`
end