Skip to content

amireh/eskimo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Eskimo

Declarative text formatting for Ruby. If you find yourself munging strings and logic to come up with something renderable by a machine and readable by a fellow human being, please don't. At least, let the language help you.

Eskimo can be used anywhere text-formatting is done -- whether you're targeting the terminal or HTML through ERB, it doesn't matter. The idea is to move the low-level string logic to dedicated objects, components, and use a rendering method to compose them into the final rendered target.

class Banner
  include Eskimo::ASCII

  def render(**)
    Indent.new(width: 4) do
      Style.new(style: [:bold, :green]) do
        'Welcome!'
      end
    end
  end
end

Eskimo::Core::Renderer.new.apply do
  Banner.new
end
# => "    Welcome!"

Pass properties to the renderer and use them in components:

class FormattedError
  include Eskimo::ASCII

  def render(error:, **)
    [
      Style.new(:red, :bold) do
        "error: "
      end,

      Indent.new(width: 'error: '.length) do
        error.message
      end
    ]
  end
end

Eskimo::Core::Renderer.new(error: StandardError.new("welp!")).apply do
  FormattedError.new
end # => "error:
    #            welp!"

Customize components with parameters (they're Ruby objects after-all):

class FormattedError
  include Eskimo::ASCII

  def initialize(style: [:red])
    @style = style
  end

  def render(error:, **)
    [
      Style.new(*@style) { 'oops! ' },
      Style.new(:bold) { error.message }
    ]
  end
end

Eskimo::Core::Renderer.new(error: StandardError.new).apply do
  FormattedError.new(style: [:red, :bold])
  #                  ^^^^^^^^^^^^^^^^^^^^
end

Compose everything:

include Eskimo::ASCII

Eskimo::Core::Renderer.new(error: StandardError.new).apply do
  Indent.new(width: 4) do
    [
      ErrorHeader.new(style: [:red, :bold]),
      
      LineBreak.new,

      Indent.new(width: 4) do
        Wrap.new(width: 72) do |error:, **|
          error.message
        end
      end
    ]
  end
end

class ErrorHeader
  ...
end

It's all Ruby, you choose:

as_a_proc = lambda { |name:, **| "Hello, #{name!}" }
as_a_duck = Indent.new(width: 4) { |name:, **| "Hello, #{name}!" }
as_a_string = 'Who is this?'

Eskimo::Core::Renderer.new(name: 'Kiksi').apply do
  [ as_a_proc, ' ', as_a_duck, ' ', as_a_string ]
end
# => "Hello, Kiksi! Hello, Kiksi! Who is this?"

Installation

gem install eskimo

The eskimo [meta] gem gives you eskimo-core plus eskimo-ascii. If you only need the HTML components, install the eskimo-html gem instead:

gem install eskimo-core &&
gem install eskimo-html

API

See the accompanying API documentation for the gruesome details but we'll go over the main API here.

Renderer.new(?props: Hash): Renderer

Create an instance of the Renderer with the properties you want to expose to components (if any).

Renderer#apply(&component): String

Convert a component into a String. A component may be:

  • A string
  • An array of components
  • A lambda that accepts a Hash and returns a component
  • An object that responds to render, accepting a Hash and returning a component

A component may render others. This is done by means of calling the special render lambda that is passed to components:

component = lambda do |render:, **|
  string = render['   a', 'b'] # => " ab"
  string.strip
end

render { component } # => "ab"

Although a more useful example would be when your component accepts children:

def component(&children)
  lambda do |render:, **|
    string = render[children]
    string.strip
  end
end

render { component { ['   a', 'b'] } } # => "ab"

Even though you're not technically tied to accepting children in a block, it's recommended to do so for consistency with the base Eskimo components.

Component.new(&children: Proc): Component

A convenience base class that allows you to render children with a mere call to super in your render routine:

class MyCompositeComponent < Eskimo::Component
  def render(**)
    string = super
    string.strip
  end
end

render { MyCompositeComponent.new { ['   a', 'b'] } } # => "ab"

Refer to the implementation of the existing Eskimo components for guidance.

Components

See the accompanying API documentation.

License

MIT

About

Declarative text formatting for Ruby.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published