NAV Navbar

Inesita

Inesita

Little princess among the frontend frameworks


Star Watch Fork

About

Inesita is a simple, light, Ruby front-end framework. Yes, Ruby, it’s all about Ruby, and its ecosystem.

The main part is component. Component describes the appearance and behavior of the page. This is the core of Inesita. Components can be used alone and mounted into any DOM element. You don’t need to use the entire stack. Moreover, component supports injections. You can inject any other classes into the main component, and they will be available to all nested components. For example, you can inject a Store class to store your application data. Of course, the implementation of store depends entirely on you. Another example of the injection is router. Router takes care of all navigation stuff, like rendering component tree depending on browser URL, changing URL, etc.

Install

To install and try Inesita, you’ll need a working Ruby environment. Follow these steps to install and launch sample Inesita application:

$ gem install inesita
$ inesita new inesita_app
$ cd inesita_app
$ bundle exec inesita server

What are we doing ?

As we can see everything works awesome. Let’s take a closer look at our application.

Application

Let’s figure out what’s actually happening. Main file is app/application.rb. This is where all the magic starts.

# require Inesita
require 'inesita'
require 'inesita-router'

# require main parts of application
require 'router'
require 'store'

# require all components
require_tree './components'

# when document is ready render application to <body>

class Application
  include Inesita::Component

  inject Router
  inject Store

  def render
    div.container do
      component NavBar
      component router
    end
  end
end


Inesita::Browser.ready? do
  Application.mount_to(Inesita::Browser.body)
end

Here you can require all project files, external gems, etc.

Application is the main component. As we can see, we’re injecting Router and Store, so they will be available throughout the entire component tree as router and store.

In store we will store all application state and data, router will render our component tree depending on browser URL.

render method defines how Inesita will render our components. It’s like a layout: NavBar and router will be rendered within the div with container class.

Last part is mounting our application. When document is ready, our main component will be mounted into <body></body> tag.

Now let’s take a closer look at component.

Component

As you already know, component is at the core of Inesita. We have seen the main Application component, now let’s take a look at the Home component.

class Home
  include Inesita::Component

  def render
    div.jubmotron class: 'text-center' do
      img src: '/static/inesita-rb.png'
      h1 do
        text "Hello I'm Inesita"
      end
      component Counter, props: {header: 'This is a sample counter'}
    end
  end
end

This is simple component that renders bootstrap jumbotron with a few headers. We’re using a nice and simple DSL to render HTML. No HTML files here, only Ruby.

So, Home is a very simple component that only renders HTML.

As you can see, we can pass class to define classes of elements, but also id, onclick, and other element attributes. Notice that you can use a shortcut for element classes like div.jumbotron. In this case div will include jumbotron class and text-center class. You can also do div.example! - the exclamation mark means that example is an id instead of a class.

This component also renders a Counter child component, and passes to it some props.

Now let’s take a look at the Counter component.

class Counter
  include Inesita::Component

  def inc
    store.increase
    render!
  end

  def dec
    store.decrease
    render!
  end

  def render
    h4 do
      text props[:header]
    end
    div class: 'input-group' do
      span class: 'input-group-btn' do
        button class: 'btn btn-default', onclick: method(:dec) do
          text '-'
        end
      end
      input type: "text", class: "form-control", value: store.counter, disabled: true
      span class: 'input-group-btn' do
        button class: 'btn btn-default', onclick: -> { inc } do
          text '+'
        end
      end
    end
  end
end

Wow, there is a lot of new things. Let start with the render method. In h4 we render the header text from props - things that were passed from the parent component.

Next one is a button. In this case the dec method describes onclick behavior. This method invokes methods from store, and renders out the component tree.

The render! method re-renders the entire component tree. No worries, there is virtual-dom that will only render differences to the actual DOM. It’s fast.

Same thing happens with the inc method and the + button, but we’re using a function notation to invoke the dec method.

In the middle there is an input field that displays the counter value from store.

Of course you can store component state in instance variables, it depends on you.

Now we need to take a look at injections.

Injections

Injections are simply other classes that are included in our component tree. In our example we have Store and Router injections.

Take a look at our Store.

class Store
  include Inesita::Injection

  attr_accessor :counter

  def init
    @counter = 0
  end

  def increase
    @counter += 1
  end

  def decrease
    @counter -= 1
  end
end

This is an injection example. We are including Inesita::Injection in order to be able to inject this class into the root component.

In this example, we’re storing counter value.

The init method initializes our store. At the beginning we’re setting the counter value to 0.

The increase method increments current counter value by 1

The decrease method decrements current counter value by 1

There is also attr_accessor :counter so we can access that value with store.counter.

Simple! Right?

You can inject other stores, but also things like dispatchers depending on the architecture you want to obtain.

Router

Router is a component, but also an injection. It’s a separate gem named inesita-router.

With router you are able to render the component tree depending on current URL.

class Router
  include Inesita::Router

  def auth
    unless store.logged_in?
      go_to('/login')
    end
  end

  def routes
    route '/', to: Home
    route '/description', to: Description

    route '/secret', to: List, on_enter: method(:auth)
    route '/login', to: Login
  end
end

This class describes how our router will work.

First of all, it includes Inesita::Router.

In the routes method, we provide information about which components are rendered on what URL.

On the root path, which is /, we render the Home component. On /description URL, the router will render the Description component. On /secret URL, the router will check if the user is logged in. If not, it will redirect to /login. On /login URL, the router will render the Login component.

You can pass props to child compontents like always.

Router as an injection provides a few useful methods:

When we’re using inesita-router, onclick events are handled by the router automatically.

DOM DSL

There are few things about writing components with DSL worth to mention:

Data attributes

Sometimes you want to write some data into DOM. You can use data attribute for that.

To archive that check an example.

class Home
  include Inesita::Component

  def render
    div.jubmotron do
      ul do
        li data: { id: "1", title: "Title #1" } do
          "Title #1"
        end

        li data: { id: "2", title: "Title #1" } do
          "Title #2"
        end

        li data: { id: "3", title: "Title #1" } do
           "Title #3"
        end
      end
    end
  end
end

Hooks

If you want to manipulate a DOM element after or before its mounted you can use hooks.

There are two of them. ‘hook’ is executes just after node is attached to DOM, and unhook is executes just before node is removed from DOM.

Here is an example.

class Home
  include Inesita::Component

  def after_mount(node)
    # do something with node after mount to dom
    puts "I'm mounted #{node}"
  end

  def before_remove(node)
    # do something with node before remove
    puts "I'm removed #{node}"
  end

  def render
    div.jubmotron hook: hook(:after_mount), unhook: unhook(:before_remove) do
      "Hello"
    end
  end
end

Awesome Inesita Awesome

Addons

Applications

Contribution

Your contributions and suggestions are welcome 鈾.