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?"
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
See the accompanying API documentation for the gruesome details but we'll go over the main API here.
Create an instance of the Renderer with the properties you want to expose to components (if any).
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.
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.
See the accompanying API documentation.
MIT