Asbru

Asbru is meant as a bridge between Rails and React that (hopefully) allows us to enjoy the perks of both ecosystems without having to write huge amounts of boilerplate. Meanwhile there is some added safety in the form of raises on loading non-existing components, and syntactic sugar(less curlies and parens) in view code AND there are some solutions for patterns of code use we notice.

Installation

gem 'asbru'

And then execute:

$ bundle

Or install it yourself as:

$ gem install asbru

Usage

Require 'asbru/ragna' and the C and CML modules are available everywhere. Your application helper is a great place for the require as most code that works in tandem with Asbru should go there.

in app/helpers/application_helper.rb

require 'asbru/ragna'

module ApplicationHelper
  include Asbru::RailsHelpers
  ...
end

Now you can use components in your rails views in Savage mode:

<%= C.layout_banner(
    steps: [],
    title: { color: 'light', text: t('dossier.overview.title') },
    color: 'energized-500') %>
<%= C.ui_divider(
    height: 60,
    color: 'transparent',
    hideMobileS: true) %>

<%= CML.vertical_spacer 10 %>

or in Sage mode:

<%= C::Layout.banner(color: 'energized-500',
    title: { color: 'light', text: t('dossier.overview.title') }) %>
<%= C::Ui.divider(
    height: 60,
    color: 'transparent',
    hideMobileS: true) %>

And you can write CML

<%= CML.h1 'Hello world!' %>
<%= CML.vertical_spacer 10 %>
<%= CML.p 'In the land where I was born.' %>
<%= CML.p 'Lives a man beneath the sea.' %>
<%= CML.p '...', color: 'stable-500' %>

Lets take a deeper dive

When we open the rails console we can see that asbru has some benefits over loading components just via react rails:

2.7.1 :001 > require 'asbru/components/sage'
2.7.1 :002 > C = Asbru::Components::Sage
2.7.1 :003 > C.constants
 => []
2.7.1 :004 > C.setup
2.7.1 :005 > C.constants
 => [:Layout, :Modal, :Progress, :Form, :Styles, :Ui, :Badge, :Button, :Table, :Text, :Card]
2.7.1 :006 >C::Ui.methods - Module.methods
 => [:image, :logo, :action_sheet, :icon, :divider, :icon_phone, :snackbar, :tooltip, :helper_implementation_class, :helper_implementation_class=, :react_component]
2.7.1 :007 > C::Ui.divider margin: 20
 => "<div data-react-class=\"ui.Divider\" data-react-props=\"{&quot;margin&quot;:20}\" data-react-cache-id=\"ui.Divider-0\"></div>"
2.7.1 :008 > C::iU.divider margin: 20
Traceback (most recent call last):
        2: from (irb):26
        1: from (irb):27:in `rescue in irb_binding'
NameError (uninitialized constant Asbru::Components::Sage::Iu)
2.7.1 :009 > C::Ui.blarg margin: 20
Traceback (most recent call last):
        1: from (irb):26
NoMethodError (undefined method `blarg' for Asbru::Components::Sage::Ui:Class)

We have a list of available folders and componentent available in Ruby and we raise errors when we use the wrong wrong module or method names.

To use Sage a json file names /app/javascript/components/components.json is required. This file can be generated with the following script. We have no cannonical way of solving this yet and it requires certain best practices to be followed during component development.

#!/usr/bin/env node

const dirTree = require('directory-tree');
const fs = require('fs');
const tree = dirTree('./app/javascript/components', { extensions: /\.js$/ });

const components =
  tree.children.length > 0
    ? tree.children
        .map((child) => {
          return {
            folder: child.name,
            components:
              child.children.length > 0
                ? child.children
                    .filter((c) => c.type == 'directory')
                    .map((c) =>
                      c.name
                        .split('-')
                        .map((c) => c.charAt(0).toUpperCase() + c.slice(1))
                        .join('')
                    )
                : {},
          };
        })
        .filter((f) => f.components.length > 0)
    : {};
fs.writeFile(
  './app/javascript/components/components.json',
  JSON.stringify(components),
  'utf8',
  () => {
    console.log('Component names have been collected.');
  }
);

Modules:

  • Component::Savage Components rendered via method_missing with no extra safety.

  • Component::Sage Components loaded with additional safety. Requires a bit of additional wiring.

  • CML Compose HTML pages with web components. Makes writing a page with Components more similar to writing html.

  • LinkHelper a Rails link_to with webcomponents

  • FormBuilder a Rails form builder using webcomponents

  • IMG A rails image_tag clone

Groups

  • Ragna Load all Asbru in a omakasa way(with sage components).

  • RailsHelpers Load all Asbru Rails helpers

Opinions of the author

  1. Writing Plain old HTML is fun simple and quite universal. Lets make writing components intuitive.
  2. Less noise makes code more readable.
  3. Less configuration makes working on projects smoother.

Design goals

If there is such a thing as "The Ivaldi Way" we will stick as close to it as we can.

TODO:

  • Make the helpers available using both Sage and Savage.
  • Raise an error when incorrect attributes have been supplied to a component.
  • Add image tag support.
  • Tie in with rails load cycle.
  • Run the js script from the gem(on rails startup in production and on ).
  • Remove the react-rails dependency(or get a better idea about its function).

Contributing

Contribution directions go here.

License

The gem is available as open source under the terms of the MIT License.