Lab42::TagCloud
Creating Tag Clouds with gamma correct color values and styles from a simple DSL.
This is a port of Elixir's tag_cloud to Ruby
Installation:
With bundler
gem 'lab42_tag_cloud'
or simply
gem install lab42_tag_cloud
Usage:
A very simple DSL allows you to describe the size, color and thickness of your tag and each line of this DSL will be transformed into some CSS and the text of the tag.
Colors
Gamma correction for scaled colors
To create 13 different shades of a color, where 0 means transparent (#ffffff) and 12 opaque (original color value or #000000 as default) which are equally spaced for the human eye we use a gamma correction of 1/2.2 which seems to work very well on modern screens.
The result for all 13 shades for some colors can be seen here
Right now the size of the scale and the gamma value cannot be modified but that could be easily implemented if desired.
Well let us describe the behavior of this DSL by means of speculations.
Context: The DSL
The DSL is a very simple string with 3 white space separated words, of which the first is a color specification which we
speculate about below, the second a font size in CSS (defaulting to pt if only a number is specified) and the third
the font weight as an optional integer with defaults to 100
This simply is translated to HTML style attributes as follows:
Given the following DSL strings
include Lab42::TagCloud
let(:blue_bold) { "blue 10 800" }
let(:dgr_shaded) { "10/darkgoldenrod 1.2em" }
let(:explicit) { "6/#2f3ab0 30px 450" }
Then the to_style method will yield the following results
expect(to_style(blue_bold)).to eq("color: #0000ff; font-size: 10pt; font-weight: 800;")
expect(to_style(dgr_shaded)).to eq("color: #995061; font-size: 1.2em;")
expect(to_style(explicit)).to eq("color: #695676; font-size: 30px; font-weight: 450;")
Context: Colors
Given we include the module:
include Lab42::TagCloud
Then we can see some transformation for colors, e.g. shade 11 of black can be expressed in different ways
gray = "525252"
expect(color_value(11)).to eq(gray)
expect(color_value("11")).to eq(gray)
expect(color_value("11/black")).to eq(gray)
expect(color_value("11/#000000")).to eq(gray)
And we can also use all web color names
expect(color_value("10/blue")).to eq("7171ff")
expect(color_value("10/lime")).to eq("71ff71")
And we can add underscores for readability to the color names
expect(color_value("4/medium_slate_blue")).to eq("0d16e0")
Context: Convenience Helpers
Now we have already everything we need to create nice tag clouds, e.g. with ERB, however
with the API described so far we would need to write code like the following:
<% somd_data_source.each do |datum| %>
...
<span style="<%= Lab42::TagCloud.to_style(datum.dsl) %>"><%= datum.tag %></span>
However if we had, say a helper, that would operate on objects that respond to dsl and tag, or [:dsl] and [:tag],
then we could do very nice things like
<%= some_data_source.map { Lab42::TagCloud.tag_from_object(_1, tag: "span") } %>
would that not be great?
Well guess what, it is great, and the helper is called tag_from_object indeed
Given an OpenStruct and a Hash instance of the required format
let(:ostruct) { OpenStruct.new(tag: "Ruby", dsl: "10/red 1.2em") }
let(:hash) { {tag: "Elixir", dsl: "blue 1.5em 800"} }
let(:elixir_style) { %{ style="color: #0000ff; font-size: 1.5em; font-weight: 800;"} }
let(:ruby_style) { %{ style="color: #ff7171; font-size: 1.2em;"} }
let(:two) { [ostruct, hash] }
Then we can obtain tags from these objects
expect(tag_from_object(ostruct)).to eq(%{<span#{ruby_style}>Ruby</span>})
expect(tag_from_object(hash, tag: :div, class: "some-class"))
.to eq(%{<div class="some-class"#{elixir_style}>Elixir</div>})
And we can map them together
expected =
%{<span#{ruby_style}>Ruby</span> <span#{elixir_style}>Elixir</span>}
expect(two.map {tag_from_object(_1)}.join(" "))
.to eq(expected)
But that is cumbersome and we want a more flexible approach: Enter tags_from_collction
Then with this we can do things like:
expected =
%{<li#{ruby_style}><i>Ruby</i></li> - <li#{elixir_style}><i>Elixir</i></li>}
expect((two, tag: :li, before: "<i>", after: "</i>", join: " - "))
.to eq(expected)
Typically such tag clouds can than be easily constructed from external data sources like JSON or YAML
LICENSE
Copyright 2022 Robert Dober [email protected]
Apache-2.0 c.f LICENSE