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