ReactiveRouter

ReactiveRouter allows you write and use the React Router in Ruby through Opal.

Installation

Add this line to your application's Gemfile:

gem 'reactive-router'

And then execute:

$ bundle

Or install it yourself as:

$ gem install reactor-router

Usage

The router is a React component that loads other components depending on the current URL.

Unlike other compnents there can only be one router on a page.

To get you started here is a sample router.

module Components
  module Accounts

    class Show

      include React::Router  # instead of React::Component, you use React::Router

      # the routes macro creates the mapping between URLs and components to display

      routes(path: "/account/:user_id") do  # i.e. we expect to see something like /account/12345
        # routes can be nested  the dashboard will be at /account/12345/dashboard
        # the DashboardRoute component will be mounted
        route(name: "Dashboard", path: "dashboard", handler: Components::Accounts::DashboardRoute)
        route(path: "orders", name: "Orders", handler: Components::Accounts::OrdersRoute)
        # when displaying an order we need the order order as well as the user_id
        route(path: "orders/:order_id", name: "Order", handler: Components::Accounts::OrderRoute)
        route(path: "statement", name: "Statement", handler: Components::Accounts::StatementRoute)
        # the special redirect route 
        redirect(from: "/account/:user_id", to: "Dashboard")
      end

      # you grab the url params and preprocess them using the router_param macro.
      # when Router is mounted it will receive the :user_id from the url.  In this case we grab
      # the corresponding active_record model.

      router_param :user_id do |id|
        User.find(id)
      end

      # like any component routers can have params that are passed in when the router is mounted

      required_param :user_param, type: User
      required_param :user_orders_param, type: [Order]
      required_param :production_center_address_param, type: Address
      required_param :open_invoices_param
      required_param :user_profiles_param, type: [PaymentProfile]
      required_param :user_addresses_param, type: [Address]

      # because the underlying javascript router has no provisions to pass params we
      # will export states and copy the params to the states so the lower components can read them
      # expect this get fixed in the near future

      export_state :user
      export_state :production_center_address
      export_state :open_invoices
      export_state :payment_profiles
      export_state :addresses

      # the router also makes a good place for other top level states to be housed (i.e. the flux architecture)
      export_state :order_count

      before_mount do
        # before mounting the router we copy the incoming params that the lower level components will need
        user! user_param
        production_center_address! production_center_address_param
        open_invoices! open_invoices_param
        payment_profiles! user_profiles_param
        addresses! user_addresses_param

        order_count! user.orders.count  # grab our top level state info and save it away

      end

      # For routers you define a show method instead of a render method
      def show
        div do
          div. do

            # link is a special router component that generates an on page link, that will maintain history etc.
            # basically an intelligent anchor tag.  When a user clicks a link, it will rerender the router, update
            # the history etc.
            # So for example when "My Statement" is clicked. The route changes to /account/:id/statement

            link(to: "Dashboard", class: "no-underline btn btn-default", params: { user_id: user.id }) { "Account Dashboard" }
            link(to: "Orders", class: "no-underline btn btn-default", params: { user_id: user.id }) { "My Quotes & Orders" }
            link(to: "Statement", class: "no-underline btn btn-default", params: { user_id: user.id }) { "My Statement" }

          end
        # someplace in the router show method you will have route_handler component which mounts and renders the component
        # indicated by the current route.
        route_handler   
        end
      end
    end

    # We can't pass parameters to the routed components, so we set up these mini components
    # which grab the state from router and send it along to the actual component

    class DashboardRoute

      include React::Component

      def render
        AccountDashboard user: Show.user, addresses: Show.addresses, payment_profiles: Show.payment_profiles
      end

    end

    class StatementRoute

      include React::Component

      def render
        Statement production_center_address: Show.production_center_address,
        open_invoices: Show.open_invoices, current_invoices: Show.open_invoices[:invoices],
        mailing_address: Show.open_invoices[:mailing_address]
      end

    end

    class OrdersRoute

      include React::Component

      def render
        AccountOrders user: Show.user #, orders: Show.orders
      end

    end

    class OrderRoute

      include React::Component

      router_param :order_id do |id|
        Order.find(id)
      end

      def render
        OrderShow(order: order_id, referrer: "account")
      end

    end

  end
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release to create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

  1. Fork it ( https://github.com/catprintlabs/reactor-router/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request