Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/sergeypedan/formtastic-tristate-radio

Have 3-state radiobuttons instead of a 2-state checkbox for your Boolean columns which can store NULLs
https://github.com/sergeypedan/formtastic-tristate-radio

activeadmin formtastic gem rails ruby

Last synced: about 1 month ago
JSON representation

Have 3-state radiobuttons instead of a 2-state checkbox for your Boolean columns which can store NULLs

Awesome Lists containing this project

README

        

# Formtastic tri-state radio

[![Gem Version](https://badge.fury.io/rb/formtastic_tristate_radio.svg)](https://badge.fury.io/rb/formtastic_tristate_radio)
[![Maintainability](https://api.codeclimate.com/v1/badges/c2508d7f23238fb2b87f/maintainability)](https://codeclimate.com/github/sergeypedan/formtastic-tristate-radio/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/c2508d7f23238fb2b87f/test_coverage)](https://codeclimate.com/github/sergeypedan/formtastic-tristate-radio/test_coverage)

## What is “tri-state”?

— that which has 3 states.

By defenition Boolean values have 2 states: True & False.

However, if you store a Boolean value in a database column with no `NOT NULL` restriction, it aquires a 3d possible state: `null`.

Some may consider this practice questionable — I don’t think so. In real life you always have a case when the answer to your question may be only “yes” or “no”, but you don’t know the answer yet. Using a string type column, storing there `"yes"`, `"no"` and `"unset"` + using a state machine + validations — feels overkill to me.

## What the gem does

1. Provides a custom Formtastic input type `:tristate_radio` which renders 3 radios (“Yes”, “No”, “Unset”) instead of a checkbox (only where you put it).
1. Teaches Rails recognize `"null"` and `"nil"` param values as `nil`. See “[How it works](#how-it-works)” ☟ section for technical details on this.
1. Encourages you to add translations for ActiveAdmin “status tag” so that `nil` be correctly translated as “Unset” instead of “False”.

## Usage

For a Boolean column with 3 possible states:

```ruby
f.input :am_i_awake, as: :tristate_radio
```

You get (HTML is simplified, actually there are more classes etc.):

```html

Am i awake?
Yes
No
Unset

```

## Installation

### Gem

```ruby
gem "formtastic_tristate_radio"
```

### Translations

Add translation for the new “unset” option:

```yaml
ru:
formtastic:
# :yes: Да # <- these two fall back to translations
# :no: Нет # in Formtastic gem but have only English
null: Неизвестно # <- this you must provide youself
```

You can override individual translations like so:

```ruby
f.input :attribute, as: :tristate_radio, null: "Your text"
```

### ActiveAdmin translations

ActiveAdmin will automatically translate `nil` as “No”, so if you use ActiveAdmin, add translation like so:

```yaml
ru:
active_admin:
status_tag:
:yes: Да
:no: Нет
unset: Неизвестно
```

Notice that the key ActiveAdmin uses is “unset”, not “null”.

## Configuration

It’s difficult to come up with a reasonable use case for that, but you can configure what will be used as inputs value:

```ruby
# config/initializers/formtastic.rb
FormtasticTristateRadio.configure do |config|
config.unset_key = "__unset" # default is :null
end
```

which will result in:

```html

```

Mind that for your custom value to work, you also need to configure `ActiveModel` to recognize that value as `nil`. Currently that is done [like so](https://github.com/sergeypedan/formtastic-tristate-radio/blob/master/config/initializers/activemodel_type_boolean.rb#L9).

## Documentation

Low-level methods are properly documented in RubyDoc [here](https://www.rubydoc.info/gems/formtastic_tristate_radio/TristateRadioInput).

## Dependencies

Now the gem depends on [Formtastic](https://github.com/formtastic/formtastic) (naturally) and Rails. Frankly I am not sure whether I will have time to make it work with other frameworks.

## How it works

In Ruby any String is cast to `true`:

```ruby
!!"" #=> true
!!"false" #=> true
!!"nil" #=> true
!!"no" #=> true
!!"null" #=> true
```

Web form params are passed as plain text and are interpreted as String by Rack.

So how are Boolean values transfered as strings if a `"no"` or `"0"` and even `""` is truthy in Ruby?

Frameworks just have a list of string values to be recognized and mapped to Boolean values:

```ruby
ActiveModel::Type::Boolean::FALSE_VALUES
#=> [
0, "0", :"0",
"f", :f, "F", :F,
false, "false", :false, "FALSE", :FALSE,
"off", :off, "OFF", :OFF,
]
```

so that

```ruby
ActiveModel::Type::Boolean.new.cast("0") #=> false
ActiveModel::Type::Boolean.new.cast("f") #=> false
ActiveModel::Type::Boolean.new.cast(:FALSE) #=> false
ActiveModel::Type::Boolean.new.cast("off") #=> false
# etc
```

So what [I do in this gem](https://github.com/sergeypedan/formtastic_tristate_radio/blob/master/config/initializers/activemodel_type_boolean.rb) is extend `ActiveModel::Type::Boolean` in a consistent way to teach it recognize null-ish values as `nil`:

```ruby
module ActiveModel
module Type
class Boolean < Value

NULL_VALUES = [nil, "", "null", :null, "nil", :nil].to_set.freeze

private def cast_value(value)
NULL_VALUES.include?(value) ? nil : !FALSE_VALUES.include?(value)
end

end
end
end
```

And voila!

```ruby
ActiveModel::Type::Boolean.new.cast("") #=> nil
ActiveModel::Type::Boolean.new.cast("null") #=> nil
ActiveModel::Type::Boolean.new.cast(:null) #=> nil
ActiveModel::Type::Boolean.new.cast("nil") #=> nil
ActiveModel::Type::Boolean.new.cast(:nil) #=> nil
```

**Warning**: as you might have noticed, default Rails behavior is changed. If you rely on Rails’ automatic conversion of strings with value `"null"` into `true`, this gem might not be for you (and you are definitely doing something weird).

## Roadmap

- [ ] Remove `require_relative "../app/models/active_record/base"` from main file
- [x] Make the gem configurable
- [x] Pull the key used for “unset” choice value into configuration
- [x] Add translations into most popular languages
- [ ] Load translations from gem
- [ ] Rgister `:tristate_radio` for Boolean columns with `null`
- [ ] Decouple `ActiveModel::Type::Boolean` thing from Formtastic things, maybe into a separate gem
- [ ] Decouple from Rails

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).