inesita-rb/inesita-router

View on GitHub
opal/inesita-router/router.rb

Summary

Maintainability
A
1 hr
Test Coverage
module Inesita
  module Router
    include Inesita::Component

    attr_reader :params

    def initialize
      @routes = Routes.new
      raise Error, 'Add #routes method to router!' unless respond_to?(:routes)
      routes
      raise Error, 'Add #route to your #routes method!' if @routes.routes.empty?
      find_route
      parse_url_params
      add_listeners
    end

    def self.included(base)
      base.extend(Inesita::Component::ClassMethods)
      Component.module_eval do
        unless respond_to?(:__a)
          alias_method :__a, :a
          define_method(:a) do |params = {}, &block|
            params = { onclick: ->(e) { router.go_to(e.target.pathname) unless params[:target] == "_blank" } }.merge(params)
            __a(params, &block)
          end
        end
      end
    end

    def add_listeners
      Browser.on_pop_state { find_route; parse_url_params; render! }
      Browser.hash_change { find_route; parse_url_params; render! }
    end

    def route(*params, &block)
      @routes.route(*params, &block)
    end

    def find_route
      @routes.routes.each do |route|
        next unless path.match(route[:regex])
        return go_to(url_for(route[:redirect_to])) if route[:redirect_to]
        return @route = route
      end
      raise Error, "Can't find route for url"
    end

    def find_component(route)
      call_on_enter_callback(route)
      @component_props = route[:component_props]
      route[:component]
    end

    def render
      component find_component(@route), props: @component_props if @route
    end

    def call_on_enter_callback(route)
      return unless route[:on_enter]
      if route[:on_enter].respond_to?(:call)
        route[:on_enter].call
      end
    end

    def go_to(path)
      Browser.push_state(path)
      find_route
      parse_url_params
      render!
      false
    end

    def parse_url_params
      @params = compotent_url_params
      query[1..-1].split('&').each do |param|
        key, value = param.split('=')
        @params[Browser.decode_uri_component(key)] = Browser.decode_uri_component(value)
      end unless query.empty?
    end

    def compotent_url_params
      Hash[@route[:params].zip(path.match(@route[:regex])[1..-1])]
    end

    def url_for(name, params = nil)
      route = @routes.routes.find do |r|
        case name
        when String
          r[:name] == name || r[:path] == name
        when Object
          r[:component] == name
        else
          false
        end
      end
      route ? url_with_params(route, params) : raise(Error, "Route '#{name}' not found.")
    end

    def query
      Browser.query
    end

    def path
      Browser.path
    end

    def current_url?(name)
      path == url_for(name, params)
    end

    def url_with_params(route, params)
      path = route[:path]
      params.each do |key, value|
        path = path.gsub(":#{key}", "#{value}")
      end if params
      path
    end
  end
end