Skip to content

Instantly share code, notes, and snippets.

@jgaskins
Last active February 16, 2019 13:36
Show Gist options
  • Save jgaskins/50b0bc5f0a8cea038e24d9d29dd66129 to your computer and use it in GitHub Desktop.
Save jgaskins/50b0bc5f0a8cea038e24d9d29dd66129 to your computer and use it in GitHub Desktop.
React Router v4-style routing for Clearwater
require 'routing'
class Layout
include Clearwater::Component
include Routing
def render
div([
nav([
Link.new({ href: '/' }, 'Home'),
' ',
Link.new({ href: '/articles' }, 'Articles'),
' ',
Link.new({ href: '/about' }, 'About'),
]),
# Here's where the magic happens
route do
match('articles') { Articles.new }
match('about') { About.new }
miss { h1 'Home page' }
end,
])
end
end
class Articles
include Clearwater::Component
include Routing
def render
div([
# Display a list of links for each article in the list
ul(articles.map { |a| ArticlesListItem.new(a) }),
# Note that we don't need to include the full path here.
# We just supplement what was already matched.
route do
match(':id') { 'This does not work yet' }
match('foo') { h1 'Matched Foo' }
end,
])
end
def articles
Store.state.articles
end
end
module Routing
attr_accessor :matched_path # The part of the path that's already been matched in parent routes
def route
@matched = [] # Supports multiple route blocks
@path_matcher = PathMatcher.new(matched_path, Bowser.window.location.path)
yield
@matched
end
def match path_segment
if @path_matcher.match? path_segment
@matched << ChildRoute.new(yield, "#{@path_matcher.current_match}/#{path_segment}")
end
end
def miss
if @matched.none?
@matched << ChildRoute.new(yield, matched_path)
end
end
ChildRoute = Struct.new(:content, :current_path_match) do
include Clearwater::Component
def render
# Allow for plain JS objects but also check to see if we can use the accessor
if `!!(#{content} && #{content}.$$class) && #{content.respond_to? :matched_path=}`
content.matched_path = current_path_match
end
content
end
end
class PathMatcher
attr_reader :current_match, :current_path
def initialize current_match, current_path
@current_match = current_match.to_s
@current_path = current_path.to_s
end
def match? segment
segment = segment.sub(%r(^/), '')
match_check = "#{current_match}/#{segment}"
# TODO: handle dynamic segments here
if current_path.start_with? match_check
true
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment