Tag Options
Simple library for manipulating options passed to the Rails tag,
content_tag, and other tag helpers.
This library provides a simple class to make authoring resuable helpers and View Components easier when you want to allow for the input of properties on HTML elements, but also need to add/set your own.
TagOptions::Hash is an object that normalizes the options passed to Rails
helper, while providing helpful methods to manipulate the values of HTML
properties:
def external_link_to(name, url, ={})
= TagOptions::Hash.new()
.at(:class).combine!("external-link")
link_to(name, url, )
end
Called with:
external_link_to("Example", "https://example.com", class: "ml-2")
Would render:
<a href="https://example.com" class="ml-2 external-link">Example</a>
Table of Contents
Installation
Add this line to your application's Gemfile:
gem "tag_options"
And then execute:
bundle install
General Usage
Instantiate a TagOptions::Hash directly or by passing an existing Hash.
TagOptions::Hash.new
=> {}
hash = {class: "flex"}
TagOptions::Hash.new(hash)
=> {:class=>"flex"}
Similar to Array(), you can also instantiate a new TagOptions::Hash by
passing a has to TagOptions::Hash().
hash = {class: "flex"}
TagOptions::Hash(hash)
=> {:class=>"flex"}
The values of the hash passed to TagOptions::Hash.new or TagOptions::Hash()
are automatically converted to strings.
hash = {disabled: true}
TagOptions::Hash.new(hash)
=> {:disabled=>"true"}
TagOptions::Hash inherits from ActiveSupport::HashWithIndifferentAccess,
implementing a hash where keys :foo and "foo" are considered to be the same.
It differs from ActiveSupport::HashWithIndifferentAccess, however, by storing
the keys as symbols instead of strings to make it easier to pass the hash as
an method argument using double splat, e.g. some_method **options.
combine!
Combine HTML attributes with an existing TagOptions::Hash by chaining at and
combine!
options = TagOptions::Hash.new(class: "flex")
options.at(:class).combine!("mt-1")
=> {:class=>"flex mt-1"}
Values can also be specified as arrays.
options = TagOptions::Hash.new(class: "flex")
options.at(:class).combine!(["mt-1", "mx-2"])
=> {:class=>"flex mt-1 mx-2"}
HTML attributes are only added if they don't already exist.
options = TagOptions::Hash.new(class: "flex")
options.at(:class).combine!("flex flex-col")
=> {:class=>"flex flex-col"}
You can also combine values on nested hashes.
options = TagOptions::Hash.new(class: "flex", data: {controller: "dropdown"})
options.at(:data, :controller).combine!("toggle")
=> {:class=>"flex", :data=>{:controller=>"dropdown toggle"}
If a nested hash doesn't already exist it will be automatically added.
options = TagOptions::Hash.new(class: "flex")
options.at(:data, :controller).combine!("dropdown")
=> {:class=>"flex", :data=>{:controller=>"dropdown"}
set!
Chaining at and set! functions nearly the same as combine! with all same
usage patterns. The major difference is that the set method will override any
existing values.
options = TagOptions::Hash.new(class: "flex")
options.at(:class).set!("block")
=> {:class=>"block"}
default!
Chaining at and default! functions nearly the same as set! with all the
same usage patterns. The only difference is that the default method will only
set the specified values if the value is not already specified.
options = TagOptions::Hash.new(class: "flex")
options.at(:class).default!("block")
options.at(:role).default!("alert")
=> {:class=>"flex", :role=>"alert"}
remove!
Remove HTML attributes from an existing TagOptions::Hash by chaining at and
remove!.
options = TagOptions::Hash.new(class: "flex ml-1 mr-1")
options.at(:class).remove!("mr-1")
=> {:class=>"flex ml-1"}
In addition to string values, you can also pass regular expression to remove!.
options = TagOptions::Hash.new(class: "flex ml-1 mr-2")
options.at(:class).remove!(/m.-\d/)
=> {:class=>"flex"}
Conditional Usage
The combine!, set!, default!, and remove! methods allow for values to be
conditionally resolved using an argument array. Where the values are passed
unconditionally and key/value pairs have their key passed IF the value is
true.
# assuming `centered?` returns `true`
options = TagOptions::Hash.new(class: "flex")
options.at(:class).combine!("mt-1", "mx-auto": centered?, "mx-2": !centered?)
=> {:class=>"flex mt-1 mx-auto"}
# assuming `centered?` returns `true`
options = TagOptions::Hash.new(class: "flex")
options.at(:class).set!("block", "mx-auto": centered?, "mx-2": !centered?)
=> {:class=>"block mx-auto"}
# assuming `centered?` returns `true`
options = TagOptions::Hash.new(role: "alert")
options.at(:class).default!("flex", "mx-auto": centered?, "mx-2": !centered?)
=> {:role=>"alert", :class=>"flex mx-auto"}
# assuming `centered?` returns `true`
options = TagOptions::Hash.new(class: "flex mx-auto mx-2")
options.at(:class).remove!("mt-1", "mx-auto": centered?, "mx-2": !centered?)
=> {:class=>"flex mx-2"}
Custom Property Resolvers
Chaining at to combine! or set! processes HTML properties similar to
class.
- Multiple are allowed
- Multiple values are seperated by a space
- Duplicate values are not added
Tag Options also ships with a special style resolver, which can be used by
pass as: :style to at.
- Multiple values are allowed
- Values must be specified as
property: value; - Duplicate
property: value;pairs are not added - The
combine!method will overwrite an existing style property if it exists, add properties that don't exist, and leave the remaining properties untouched. - The
set!method will overwrite all existing style properties.
options = TagOptions::Hash.new(style: "display: block; margin-left: 0;")
options.at(:style, as: :style).combine!("display: flex; margin-right: 0;")
=> {:style=>"display: flex; margin-left: 0; margin-right: 0;"}
A TagOptions::Resolver class is available if you wish to implement your own
custom handlers. For examples on doing so, see the built-in
handlers.
To register a custom handler:
# config/initializers/tag_options.rb
TagOptions.configure do |config|
config.register_resolver :custom, "MyCustomResolver"
end
Development
After checking out the repo, run bin/setup to install dependencies. Then, run
bin/rspec to run the tests. You can also run:
bin/consolefor an interactive prompt that will allow you to experimentbin/rubocopto run RuboCop to check the code style and formatting
To build this gem on your local machine, run bundle exec rake build. To
release a new version, update the version number in version.rb, and then run
bundle exec rake release, which will create a git tag for the version, push
git commits and the created tag, and push the .gem file to
rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/wamonroe/tag_options.
License
The gem is available as open source under the terms of the MIT License.