formtastic_tristate_radio 0.1.0 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +162 -0
- data/MIT-LICENSE +20 -0
- data/README.md +77 -26
- data/app/inputs/tristate_radio_input.rb +58 -116
- data/app/models/active_record/base.rb +5 -1
- data/config/initializers/activemodel_type_boolean.rb +1 -1
- data/config/locales/active_admin.yml +60 -0
- data/config/locales/formtastic.yml +48 -0
- data/formtastic_tristate_radio.gemspec +60 -0
- data/lib/formtastic_tristate_radio/configuration.rb +25 -0
- data/lib/formtastic_tristate_radio/engine.rb +7 -0
- data/lib/formtastic_tristate_radio/missing_translation_error.rb +6 -0
- data/lib/formtastic_tristate_radio/version.rb +3 -1
- data/lib/formtastic_tristate_radio.rb +4 -3
- metadata +43 -47
- data/config/locales/active_admin.en.yml +0 -5
- data/config/locales/active_admin.ru.yml +0 -7
- data/config/locales/formtastic.en.yml +0 -4
- data/config/locales/formtastic.ru.yml +0 -6
- data/lib/tasks/formtastic_tristate_radio_tasks.rake +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8fdf4d000b01b98679b7b2e4eb9bbb8ac43e2bdec9dfe79aba6e07ed580f0cbf
|
4
|
+
data.tar.gz: 7734d39327b809b599b058362e091df2e6153749ff6cfad5a7cb7ac91756e1a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e67a6dfe2440683e483d9d2bb677b66a87d335625a1ca7622888e641b3b0c36d8602ef502708d2098e78b4d3f15985ca59b6105d17f661adb4e72ea5661f07a1
|
7
|
+
data.tar.gz: 9fcd6fe99ddfba81237d17305abcc6e1de6febcc05765c442c265bdb5293adaea88bd9364affac0c374929a4ebc1113cba8c98ad7eb82dd11440cde462fa7606
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [0.2.2] - 2021-11-05
|
4
|
+
|
5
|
+
- Make the gem configurable
|
6
|
+
- Pull the key used for “unset” choice value into configuration
|
7
|
+
|
8
|
+
## [0.2.1] - 2021-11-04
|
9
|
+
|
10
|
+
- Updates docs URL in gemspec
|
11
|
+
- Updates change-log URL in gemspec
|
12
|
+
- Removes development gems
|
13
|
+
- Adds roadmap items
|
14
|
+
|
15
|
+
## [0.2.0] - 2021-11-04
|
16
|
+
|
17
|
+
- Custom translation override from form via options
|
18
|
+
- Custom error class
|
19
|
+
- YARD documentation for everything
|
20
|
+
- Inherits from `Formtastic::Inputs::RadioInput` and patches only necessary methods
|
21
|
+
- Error YAML example for ActiveAdmin included only if ActiveAdmin is detected
|
22
|
+
|
23
|
+
## [0.1.0] - 2021-11-01
|
24
|
+
|
25
|
+
Initial release
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
formtastic_tristate_radio (0.2.3)
|
5
|
+
formtastic (>= 3, < 5)
|
6
|
+
rails (>= 4, < 7)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actioncable (6.1.4.1)
|
12
|
+
actionpack (= 6.1.4.1)
|
13
|
+
activesupport (= 6.1.4.1)
|
14
|
+
nio4r (~> 2.0)
|
15
|
+
websocket-driver (>= 0.6.1)
|
16
|
+
actionmailbox (6.1.4.1)
|
17
|
+
actionpack (= 6.1.4.1)
|
18
|
+
activejob (= 6.1.4.1)
|
19
|
+
activerecord (= 6.1.4.1)
|
20
|
+
activestorage (= 6.1.4.1)
|
21
|
+
activesupport (= 6.1.4.1)
|
22
|
+
mail (>= 2.7.1)
|
23
|
+
actionmailer (6.1.4.1)
|
24
|
+
actionpack (= 6.1.4.1)
|
25
|
+
actionview (= 6.1.4.1)
|
26
|
+
activejob (= 6.1.4.1)
|
27
|
+
activesupport (= 6.1.4.1)
|
28
|
+
mail (~> 2.5, >= 2.5.4)
|
29
|
+
rails-dom-testing (~> 2.0)
|
30
|
+
actionpack (6.1.4.1)
|
31
|
+
actionview (= 6.1.4.1)
|
32
|
+
activesupport (= 6.1.4.1)
|
33
|
+
rack (~> 2.0, >= 2.0.9)
|
34
|
+
rack-test (>= 0.6.3)
|
35
|
+
rails-dom-testing (~> 2.0)
|
36
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
37
|
+
actiontext (6.1.4.1)
|
38
|
+
actionpack (= 6.1.4.1)
|
39
|
+
activerecord (= 6.1.4.1)
|
40
|
+
activestorage (= 6.1.4.1)
|
41
|
+
activesupport (= 6.1.4.1)
|
42
|
+
nokogiri (>= 1.8.5)
|
43
|
+
actionview (6.1.4.1)
|
44
|
+
activesupport (= 6.1.4.1)
|
45
|
+
builder (~> 3.1)
|
46
|
+
erubi (~> 1.4)
|
47
|
+
rails-dom-testing (~> 2.0)
|
48
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
49
|
+
activejob (6.1.4.1)
|
50
|
+
activesupport (= 6.1.4.1)
|
51
|
+
globalid (>= 0.3.6)
|
52
|
+
activemodel (6.1.4.1)
|
53
|
+
activesupport (= 6.1.4.1)
|
54
|
+
activerecord (6.1.4.1)
|
55
|
+
activemodel (= 6.1.4.1)
|
56
|
+
activesupport (= 6.1.4.1)
|
57
|
+
activestorage (6.1.4.1)
|
58
|
+
actionpack (= 6.1.4.1)
|
59
|
+
activejob (= 6.1.4.1)
|
60
|
+
activerecord (= 6.1.4.1)
|
61
|
+
activesupport (= 6.1.4.1)
|
62
|
+
marcel (~> 1.0.0)
|
63
|
+
mini_mime (>= 1.1.0)
|
64
|
+
activesupport (6.1.4.1)
|
65
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
66
|
+
i18n (>= 1.6, < 2)
|
67
|
+
minitest (>= 5.1)
|
68
|
+
tzinfo (~> 2.0)
|
69
|
+
zeitwerk (~> 2.3)
|
70
|
+
builder (3.2.4)
|
71
|
+
concurrent-ruby (1.1.9)
|
72
|
+
crass (1.0.6)
|
73
|
+
diff-lcs (1.4.4)
|
74
|
+
erubi (1.10.0)
|
75
|
+
formtastic (4.0.0)
|
76
|
+
actionpack (>= 5.2.0)
|
77
|
+
globalid (0.5.2)
|
78
|
+
activesupport (>= 5.0)
|
79
|
+
i18n (1.8.11)
|
80
|
+
concurrent-ruby (~> 1.0)
|
81
|
+
loofah (2.12.0)
|
82
|
+
crass (~> 1.0.2)
|
83
|
+
nokogiri (>= 1.5.9)
|
84
|
+
mail (2.7.1)
|
85
|
+
mini_mime (>= 0.1.1)
|
86
|
+
marcel (1.0.2)
|
87
|
+
method_source (1.0.0)
|
88
|
+
mini_mime (1.1.2)
|
89
|
+
minitest (5.14.4)
|
90
|
+
nio4r (2.5.8)
|
91
|
+
nokogiri (1.12.5-x86_64-darwin)
|
92
|
+
racc (~> 1.4)
|
93
|
+
racc (1.6.0)
|
94
|
+
rack (2.2.3)
|
95
|
+
rack-test (1.1.0)
|
96
|
+
rack (>= 1.0, < 3)
|
97
|
+
rails (6.1.4.1)
|
98
|
+
actioncable (= 6.1.4.1)
|
99
|
+
actionmailbox (= 6.1.4.1)
|
100
|
+
actionmailer (= 6.1.4.1)
|
101
|
+
actionpack (= 6.1.4.1)
|
102
|
+
actiontext (= 6.1.4.1)
|
103
|
+
actionview (= 6.1.4.1)
|
104
|
+
activejob (= 6.1.4.1)
|
105
|
+
activemodel (= 6.1.4.1)
|
106
|
+
activerecord (= 6.1.4.1)
|
107
|
+
activestorage (= 6.1.4.1)
|
108
|
+
activesupport (= 6.1.4.1)
|
109
|
+
bundler (>= 1.15.0)
|
110
|
+
railties (= 6.1.4.1)
|
111
|
+
sprockets-rails (>= 2.0.0)
|
112
|
+
rails-dom-testing (2.0.3)
|
113
|
+
activesupport (>= 4.2.0)
|
114
|
+
nokogiri (>= 1.6)
|
115
|
+
rails-html-sanitizer (1.4.2)
|
116
|
+
loofah (~> 2.3)
|
117
|
+
railties (6.1.4.1)
|
118
|
+
actionpack (= 6.1.4.1)
|
119
|
+
activesupport (= 6.1.4.1)
|
120
|
+
method_source
|
121
|
+
rake (>= 0.13)
|
122
|
+
thor (~> 1.0)
|
123
|
+
rake (13.0.6)
|
124
|
+
rspec (3.10.0)
|
125
|
+
rspec-core (~> 3.10.0)
|
126
|
+
rspec-expectations (~> 3.10.0)
|
127
|
+
rspec-mocks (~> 3.10.0)
|
128
|
+
rspec-core (3.10.1)
|
129
|
+
rspec-support (~> 3.10.0)
|
130
|
+
rspec-expectations (3.10.1)
|
131
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
132
|
+
rspec-support (~> 3.10.0)
|
133
|
+
rspec-mocks (3.10.2)
|
134
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
135
|
+
rspec-support (~> 3.10.0)
|
136
|
+
rspec-support (3.10.3)
|
137
|
+
sprockets (4.0.2)
|
138
|
+
concurrent-ruby (~> 1.0)
|
139
|
+
rack (> 1, < 3)
|
140
|
+
sprockets-rails (3.2.2)
|
141
|
+
actionpack (>= 4.0)
|
142
|
+
activesupport (>= 4.0)
|
143
|
+
sprockets (>= 3.0.0)
|
144
|
+
thor (1.1.0)
|
145
|
+
tzinfo (2.0.4)
|
146
|
+
concurrent-ruby (~> 1.0)
|
147
|
+
websocket-driver (0.7.5)
|
148
|
+
websocket-extensions (>= 0.1.0)
|
149
|
+
websocket-extensions (0.1.5)
|
150
|
+
yard (0.9.26)
|
151
|
+
zeitwerk (2.5.1)
|
152
|
+
|
153
|
+
PLATFORMS
|
154
|
+
x86_64-darwin-17
|
155
|
+
|
156
|
+
DEPENDENCIES
|
157
|
+
formtastic_tristate_radio!
|
158
|
+
rspec (~> 3)
|
159
|
+
yard (>= 0.9.20, < 1)
|
160
|
+
|
161
|
+
BUNDLED WITH
|
162
|
+
2.2.27
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 Sergey Pedan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,50 +1,57 @@
|
|
1
1
|
# Formtastic tri-state radio
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/formtastic_tristate_radio.svg)](https://badge.fury.io/rb/formtastic_tristate_radio)
|
4
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/c2508d7f23238fb2b87f/maintainability)](https://codeclimate.com/github/sergeypedan/formtastic-tristate-radio/maintainability)
|
5
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/c2508d7f23238fb2b87f/test_coverage)](https://codeclimate.com/github/sergeypedan/formtastic-tristate-radio/test_coverage)
|
6
|
+
|
3
7
|
## What is “tri-state”?
|
4
8
|
|
5
|
-
— that which has 3
|
9
|
+
— that which has 3 states.
|
6
10
|
|
7
|
-
By defenition Boolean values have 2
|
11
|
+
By defenition Boolean values have 2 states: True & False.
|
8
12
|
|
9
|
-
However, if
|
13
|
+
However, if you store a Boolean value in a database column with no `NOT NULL` restriction, it aquires a 3<sup>d</sup> possible state: `null`.
|
10
14
|
|
11
|
-
Some may
|
15
|
+
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.
|
12
16
|
|
13
17
|
|
14
18
|
## What the gem does
|
15
19
|
|
16
|
-
1. Provides a
|
17
|
-
1. Teaches Rails recognize `"null"` and `"nil"` param values as
|
18
|
-
1. Encourages you to
|
20
|
+
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).
|
21
|
+
1. Teaches Rails recognize `"null"` and `"nil"` param values as `nil`. See “[How it works](#how-it-works)” ☟ section for technical details on this.
|
22
|
+
1. Encourages you to add translations for ActiveAdmin “status tag” so that `nil` be correctly translated as “Unset” instead of “False”.
|
19
23
|
|
20
24
|
|
21
25
|
## Usage
|
22
26
|
|
23
|
-
For a
|
27
|
+
For a Boolean column with 3 possible states:
|
24
28
|
|
25
29
|
```ruby
|
26
|
-
f.input :
|
30
|
+
f.input :am_i_awake, as: :tristate_radio
|
27
31
|
```
|
28
32
|
|
29
|
-
You get (HTML is simplified):
|
33
|
+
You get (HTML is simplified, actually there are more classes etc.):
|
30
34
|
|
31
35
|
```html
|
32
36
|
<fieldset>
|
33
|
-
<
|
34
|
-
<input name="
|
35
|
-
<input name="
|
37
|
+
<legend>Am i awake?</legend>
|
38
|
+
<input name="am_i_awake" type="radio" value="true"> <label>Yes</label>
|
39
|
+
<input name="am_i_awake" type="radio" value="false"> <label>No</label>
|
40
|
+
<input name="am_i_awake" type="radio" value="null"> <label>Unset</label>
|
36
41
|
</fieldset>
|
37
42
|
```
|
38
43
|
|
39
|
-
In the future `:tristate_radio` will be registered for Boolean columns with `null` by default. Until then you have to assign it manually.
|
40
|
-
|
41
44
|
|
42
45
|
## Installation
|
43
46
|
|
47
|
+
### Gem
|
48
|
+
|
44
49
|
```ruby
|
45
50
|
gem "formtastic_tristate_radio"
|
46
51
|
```
|
47
52
|
|
53
|
+
### Translations
|
54
|
+
|
48
55
|
Add translation for the new “unset” option:
|
49
56
|
|
50
57
|
```yaml
|
@@ -55,7 +62,15 @@ ru:
|
|
55
62
|
null: Неизвестно # <- this you must provide youself
|
56
63
|
```
|
57
64
|
|
58
|
-
|
65
|
+
As noted in [Usage](#usage), you can override individual translations like so:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
f.input :attribute, as: :tristate_radio, null: "Your text"
|
69
|
+
```
|
70
|
+
|
71
|
+
### ActiveAdmin translations
|
72
|
+
|
73
|
+
ActiveAdmin will automatically translate `nil` as “No”, so if you use ActiveAdmin, add translation like so:
|
59
74
|
|
60
75
|
```yaml
|
61
76
|
ru:
|
@@ -63,23 +78,47 @@ ru:
|
|
63
78
|
status_tag:
|
64
79
|
:yes: Да
|
65
80
|
:no: Нет
|
66
|
-
|
81
|
+
unset: Неизвестно
|
67
82
|
```
|
68
83
|
|
84
|
+
Notice that the key ActiveAdmin uses is “unset”, not “null”.
|
85
|
+
|
69
86
|
|
70
87
|
## Configuration
|
71
88
|
|
72
|
-
|
89
|
+
It’s difficult to come up with a reasonable use case for that, but you can configure what will be used as inputs value:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# config/initializers/formtastic.rb
|
93
|
+
FormtasticTristateRadio.configure do |config|
|
94
|
+
config.unset_key = "__unset" # default is :null
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
which will result in:
|
99
|
+
|
100
|
+
```html
|
101
|
+
<input type="radio" name="am_i_awake" value="true">
|
102
|
+
<input type="radio" name="am_i_awake" value="false">
|
103
|
+
<input type="radio" name="am_i_awake" value="__unset">
|
104
|
+
```
|
105
|
+
|
106
|
+
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).
|
107
|
+
|
108
|
+
|
109
|
+
## Documentation
|
110
|
+
|
111
|
+
Low-level methods are properly documented in RubyDoc [here](https://www.rubydoc.info/gems/formtastic_tristate_radio/TristateRadioInput).
|
73
112
|
|
74
113
|
|
75
114
|
## Dependencies
|
76
115
|
|
77
|
-
Now the gem depends on
|
116
|
+
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.
|
78
117
|
|
79
118
|
|
80
119
|
## How it works
|
81
120
|
|
82
|
-
In Ruby any String is cast to
|
121
|
+
In Ruby any String is cast to `true`:
|
83
122
|
|
84
123
|
```ruby
|
85
124
|
!!"" #=> true
|
@@ -89,11 +128,11 @@ In Ruby any String is cast to `true`:
|
|
89
128
|
!!"null" #=> true
|
90
129
|
```
|
91
130
|
|
92
|
-
Web form params are passed as
|
131
|
+
Web form params are passed as plain text and are interpreted as String by Rack.
|
93
132
|
|
94
|
-
So how Boolean values
|
133
|
+
So how are Boolean values transfered as strings if a `"no"` or `"0"` and even `""` is truthy in Ruby?
|
95
134
|
|
96
|
-
Frameworks just have a
|
135
|
+
Frameworks just have a list of string values to be recognized and mapped to Boolean values:
|
97
136
|
|
98
137
|
```ruby
|
99
138
|
ActiveModel::Type::Boolean::FALSE_VALUES
|
@@ -115,7 +154,7 @@ ActiveModel::Type::Boolean.new.cast("off") #=> false
|
|
115
154
|
# etc
|
116
155
|
```
|
117
156
|
|
118
|
-
So what [I do in
|
157
|
+
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`:
|
119
158
|
|
120
159
|
```ruby
|
121
160
|
module ActiveModel
|
@@ -143,9 +182,21 @@ ActiveModel::Type::Boolean.new.cast("nil") #=> nil
|
|
143
182
|
ActiveModel::Type::Boolean.new.cast(:nil) #=> nil
|
144
183
|
```
|
145
184
|
|
146
|
-
**Warning**: as
|
185
|
+
**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).
|
186
|
+
|
187
|
+
|
188
|
+
## Roadmap
|
189
|
+
|
190
|
+
- [ ] Remove `require_relative "../app/models/active_record/base"` from main file
|
191
|
+
- [x] Make the gem configurable
|
192
|
+
- [x] Pull the key used for “unset” choice value into configuration
|
193
|
+
- [ ] Load translations from gem
|
194
|
+
- [ ] Add translations into most popular languages
|
195
|
+
- [ ] Rgister `:tristate_radio` for Boolean columns with `null`
|
196
|
+
- [ ] Decouple `ActiveModel::Type::Boolean` thing from Formtastic things, maybe into a separate gem
|
197
|
+
- [ ] Decouple from Rails
|
147
198
|
|
148
199
|
|
149
200
|
## License
|
150
201
|
|
151
|
-
The gem is available as
|
202
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -3,168 +3,110 @@
|
|
3
3
|
require "formtastic"
|
4
4
|
|
5
5
|
# It may also be appropriate to put this file in `app/inputs`
|
6
|
-
class TristateRadioInput
|
6
|
+
class TristateRadioInput < Formtastic::Inputs::RadioInput
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
include Formtastic::Inputs::Base::Choices
|
11
|
-
|
12
|
-
|
13
|
-
# UNSET_KEY = :null
|
14
|
-
UNSET_KEY = ActiveModel::Type::Boolean::NULL_VALUES.reject(&:blank?).first
|
8
|
+
# No equals `:null`.
|
9
|
+
# Should equal one of `ActiveModel::Type::Boolean::NULL_VALUES`
|
15
10
|
#
|
16
|
-
# Mind ActiveAdmin status resolving logic:
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
|
21
|
-
MISSING_TRANSLATION_ERROR_MSG = <<~HEREDOC
|
22
|
-
|
11
|
+
# Mind ActiveAdmin [status resolving logic](https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/views/components/status_tag.rb#L51):
|
12
|
+
# in status tag builder the value is lowercased before casting into Boolean, and the keyword for nil is `"unset"`.
|
13
|
+
# So if we have lowercase `"unset"`, translations from `ru.formtastic.unset` will be overriden by `ru.active_admin.status_tag.unset`.
|
14
|
+
#
|
15
|
+
UNSET_KEY = FormtasticTristateRadio.config.unset_key
|
23
16
|
|
24
|
-
|
17
|
+
I18N_EXAMPLE_ACTIVEADMIN = <<~YAML.chomp
|
25
18
|
ru:
|
26
19
|
active_admin:
|
27
20
|
status_tag:
|
28
21
|
:yes: Да
|
29
22
|
:no: Нет
|
30
23
|
:#{UNSET_KEY}: Неизвестно
|
24
|
+
YAML
|
31
25
|
|
32
|
-
|
26
|
+
I18N_EXAMPLE_FORMTASTIC = <<~YAML.chomp
|
33
27
|
ru:
|
34
28
|
formtastic:
|
35
29
|
:yes: Да
|
36
30
|
:no: Нет
|
37
31
|
:#{UNSET_KEY}: Неизвестно
|
38
|
-
|
39
|
-
Note: “yes”, “no”, “null” and some other words are reserved, converted into Boolean values in YAML, so you need to quote or symbolize them.
|
40
|
-
HEREDOC
|
32
|
+
YAML
|
41
33
|
|
42
34
|
|
43
|
-
#
|
35
|
+
# @note In you have ActiveAdmin installed, it will give you YAML example for ActiveAdmin as well, otherwise only for Formtastic
|
44
36
|
#
|
45
|
-
# @
|
37
|
+
# @return [String] error message with YAML examples for the “unset” label translation lookup error
|
46
38
|
#
|
47
|
-
def
|
48
|
-
|
39
|
+
def self.missing_i18n_error_msg
|
40
|
+
msg = []
|
41
|
+
msg << "Add translations for the “unset” radio label"
|
42
|
+
msg << ["For radiobutton labels in forms:", I18N_EXAMPLE_FORMTASTIC].join("\n")
|
43
|
+
msg << "Note: “yes”, “no” and some other reserved words are converted into Boolean values in YAML, so you need to quote or symbolize them."
|
44
|
+
msg << ["For ActiveAdmin status tags in index & view tables:", I18N_EXAMPLE_ACTIVEADMIN].join("\n") if !!defined?(ActiveAdmin)
|
45
|
+
msg.join("\n\n")
|
49
46
|
end
|
50
47
|
|
51
48
|
|
52
|
-
#
|
49
|
+
# @see https://github.com/formtastic/formtastic/blob/35dc806964403cb2bb0a6074b951ceef906c8581/lib/formtastic/inputs/base/choices.rb#L59 Original Formtastic method
|
53
50
|
#
|
54
|
-
# @return [
|
51
|
+
# @return [Hash] HTML options for the `<input type="radio" />` tag
|
55
52
|
#
|
56
|
-
|
57
|
-
|
53
|
+
# Adds `{ selected: true }` to the original options Hash if the choice value equals attribute value (to ultimately set for `checked="checked"`)
|
54
|
+
#
|
55
|
+
def choice_html_options(choice)
|
56
|
+
super.merge({ checked: selected?(choice) })
|
58
57
|
end
|
59
58
|
|
60
59
|
|
61
|
-
#
|
62
|
-
#
|
60
|
+
# @example Original method
|
61
|
+
# def collection_for_boolean
|
62
|
+
# true_text = options[:true] || Formtastic::I18n.t(:yes)
|
63
|
+
# false_text = options[:false] || Formtastic::I18n.t(:no)
|
64
|
+
# [ [true_text, true], [false_text, false] ]
|
65
|
+
# end
|
63
66
|
#
|
64
|
-
#
|
65
|
-
# { for: nil, class: ["label"] }
|
67
|
+
# collection_for_boolean #=> [["Да", true], ["Нет", false]]
|
66
68
|
#
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
|
71
|
-
|
72
|
-
# choice_value(choice) => true | false | UNSET_KEY <- in our version
|
73
|
-
# choice_value(choice) => true | false | ? <- in regular radio-buttons version
|
74
|
-
# method => :status
|
75
|
-
# object => ActiveRecord::Base model subclass, `User`
|
69
|
+
# @example This patched method
|
70
|
+
# collection_for_boolean #=> [["Да", true], ["Нет", false], ["Неизвестно", :null]]
|
76
71
|
#
|
77
|
-
# @
|
72
|
+
# @return [Array<[String, (Boolean|String|Symbol)]>] an array of “choices”, each presented as an array with 2 items: HTML label text and HTML input value
|
78
73
|
#
|
79
|
-
#
|
74
|
+
# @see https://github.com/formtastic/formtastic/blob/e34baba470d2fda75bf9748cff8898ee0ed29075/lib/formtastic/inputs/base/collections.rb#L131 Original Formtastic method
|
80
75
|
#
|
81
|
-
def
|
82
|
-
|
76
|
+
def collection_for_boolean
|
77
|
+
super + [[label_text_for_unset, UNSET_KEY]]
|
83
78
|
end
|
84
79
|
|
85
80
|
|
86
|
-
#
|
87
|
-
# "<input ...> Text..."
|
88
|
-
#
|
89
|
-
# @param choice [Array], ["Completed", "completed"]
|
81
|
+
# Checks translation passed as option, then checks in locale
|
90
82
|
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
# input_html_options.merge(choice_html_options(choice)).merge({ required: false })
|
94
|
-
# => { id: "task_status_completed", required: false, autofocus: false, readonly: false }
|
95
|
-
#
|
96
|
-
# builder => an instance of ActiveAdmin::FormBuilder
|
97
|
-
# choice_label(choice) => "Completed"
|
98
|
-
# choice_html_options(choice) => { id: "task_status_completed" }
|
99
|
-
# choice_value(choice) => "completed"
|
100
|
-
# input_name => :status
|
83
|
+
# @example
|
84
|
+
# label_text_for_unset #=> "Неизвестно"
|
101
85
|
#
|
102
|
-
|
103
|
-
builder.radio_button(
|
104
|
-
input_name,
|
105
|
-
choice_value(choice),
|
106
|
-
input_html_options.merge(choice_html_options(choice)).merge({ required: false, checked: selected?(choice) })
|
107
|
-
) << choice_label(choice)
|
108
|
-
end
|
109
|
-
|
110
|
-
|
111
|
-
# choice_input_dom_id(choice) => "task_status_completed"
|
112
|
-
# label_html_options => { for: nil, class: ["label"] }
|
86
|
+
# @return [String] Label of the radio that stands for the unknown choice
|
113
87
|
#
|
114
|
-
# @
|
88
|
+
# @raise [StandardError] if the translation could not be found
|
89
|
+
# @see missing_i18n_error_msg
|
115
90
|
#
|
116
|
-
def
|
117
|
-
|
91
|
+
def label_text_for_unset
|
92
|
+
options.fetch(:null, Formtastic::I18n.t(UNSET_KEY)).presence or \
|
93
|
+
fail FormtasticTristateRadio::MissingTranslationError.new(self.class.missing_i18n_error_msg)
|
118
94
|
end
|
119
95
|
|
120
96
|
|
121
|
-
#
|
97
|
+
# @example For each item of `collection` it runs:
|
98
|
+
# selected?(["Да", true]) #=> false
|
99
|
+
# selected?(["Нет", false]) #=> false
|
100
|
+
# selected?(["Неизвестно", :null]) #=> true
|
122
101
|
#
|
123
|
-
#
|
124
|
-
# <label>Status</label>
|
125
|
-
# </legend>"
|
102
|
+
# @param choice [Array<[String, (Boolean|String|Symbol)]>]
|
126
103
|
#
|
127
|
-
#
|
128
|
-
# <input type="radio" value="completed" name="task[status]" /> Completed
|
129
|
-
# </label>"
|
104
|
+
# @return [Boolean] answer to the question “Is the passed option selected?”
|
130
105
|
#
|
131
|
-
#
|
132
|
-
# choice_wrapping({ class: "choice" }) do
|
133
|
-
# choice_html(choice)
|
134
|
-
# end
|
135
|
-
# end
|
136
|
-
# => ["<li class="choice">
|
137
|
-
# <label for="task_status_completed">
|
138
|
-
# <input type="radio" value="completed" name="task[status]" /> Completed
|
139
|
-
# </label>
|
140
|
-
# </li>",
|
141
|
-
# "<li class="choice">
|
142
|
-
# ...
|
143
|
-
# ]
|
144
|
-
#
|
145
|
-
# This method relies on ActiveAdmin
|
146
|
-
#
|
147
|
-
def to_html
|
148
|
-
choices = collection_with_unset #=> [["Completed", "completed"], ["In progress", "in_progress"], ["Unknown", "unset"]]
|
149
|
-
|
150
|
-
input_wrapping do
|
151
|
-
choices_wrapping do
|
152
|
-
legend_html << choices_group_wrapping do
|
153
|
-
choices.map { |choice|
|
154
|
-
choice_wrapping(choice_wrapping_html_options(choice)) do
|
155
|
-
choice_html(choice)
|
156
|
-
end
|
157
|
-
}.join("\n").html_safe
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
|
164
|
-
# @return [String] Label of the radio that stands for the unknown choice
|
106
|
+
# @note For this to work, `ActiveModel::Type::Boolean` must be patched to resolve `UNSET_KEY` as `nil`.
|
165
107
|
#
|
166
|
-
def
|
167
|
-
|
108
|
+
def selected?(choice)
|
109
|
+
ActiveModel::Type::Boolean.new.cast(choice_value(choice)) == object.public_send(method)
|
168
110
|
end
|
169
111
|
|
170
112
|
end
|
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
class ActiveRecord::Base
|
4
4
|
|
5
|
-
# @return [Array]
|
5
|
+
# @return [Array<Symbol>] names of Boolean columns which can store `NULL` values
|
6
|
+
# @example
|
7
|
+
# Company.tristate_column_names
|
8
|
+
# #=> [:is_profitable, :is_run_by_psychopaths, :evades_taxation, ...]
|
9
|
+
#
|
6
10
|
def self.tristate_column_names
|
7
11
|
columns.select { |col| col.type == :boolean && col.null }.map(&:name)
|
8
12
|
end
|
@@ -4,7 +4,7 @@ module ActiveModel
|
|
4
4
|
module Type
|
5
5
|
class Boolean < Value
|
6
6
|
|
7
|
-
NULL_VALUES = [nil, "", "null", :
|
7
|
+
NULL_VALUES = [nil, "", :null, "null", :nil, "nil"].to_set.freeze
|
8
8
|
|
9
9
|
private def cast_value(value)
|
10
10
|
NULL_VALUES.include?(value) ? nil : !FALSE_VALUES.include?(value)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
---
|
2
|
+
ca:
|
3
|
+
active_admin:
|
4
|
+
status_tag:
|
5
|
+
:null: No establert
|
6
|
+
---
|
7
|
+
de:
|
8
|
+
active_admin:
|
9
|
+
status_tag:
|
10
|
+
:null: Nicht eingestellt
|
11
|
+
---
|
12
|
+
fr:
|
13
|
+
active_admin:
|
14
|
+
status_tag:
|
15
|
+
:null: Pas défini
|
16
|
+
---
|
17
|
+
it:
|
18
|
+
active_admin:
|
19
|
+
status_tag:
|
20
|
+
:null: Sconosciuto
|
21
|
+
---
|
22
|
+
ja:
|
23
|
+
active_admin:
|
24
|
+
status_tag:
|
25
|
+
:null: 不明
|
26
|
+
---
|
27
|
+
en:
|
28
|
+
active_admin:
|
29
|
+
status_tag:
|
30
|
+
:null: Unset
|
31
|
+
---
|
32
|
+
es:
|
33
|
+
active_admin:
|
34
|
+
status_tag:
|
35
|
+
:null: No establecido
|
36
|
+
---
|
37
|
+
pl:
|
38
|
+
active_admin:
|
39
|
+
status_tag:
|
40
|
+
:null: Nie ustawiony
|
41
|
+
---
|
42
|
+
pt-BR:
|
43
|
+
active_admin:
|
44
|
+
status_tag:
|
45
|
+
:null: Não configurado
|
46
|
+
---
|
47
|
+
pt:
|
48
|
+
active_admin:
|
49
|
+
status_tag:
|
50
|
+
:null: Não configurado
|
51
|
+
---
|
52
|
+
ru:
|
53
|
+
active_admin:
|
54
|
+
status_tag:
|
55
|
+
:null: Неизвестно
|
56
|
+
---
|
57
|
+
tr:
|
58
|
+
active_admin:
|
59
|
+
status_tag:
|
60
|
+
:null: Ayarlanmadı
|
@@ -0,0 +1,48 @@
|
|
1
|
+
---
|
2
|
+
ca:
|
3
|
+
formtastic:
|
4
|
+
:null: No establert
|
5
|
+
---
|
6
|
+
de:
|
7
|
+
formtastic:
|
8
|
+
:null: Nicht eingestellt
|
9
|
+
---
|
10
|
+
fr:
|
11
|
+
formtastic:
|
12
|
+
:null: Pas défini
|
13
|
+
---
|
14
|
+
it:
|
15
|
+
formtastic:
|
16
|
+
:null: Sconosciuto
|
17
|
+
---
|
18
|
+
ja:
|
19
|
+
formtastic:
|
20
|
+
:null: 不明
|
21
|
+
---
|
22
|
+
en:
|
23
|
+
formtastic:
|
24
|
+
:null: Unset
|
25
|
+
---
|
26
|
+
es:
|
27
|
+
formtastic:
|
28
|
+
:null: No establecido
|
29
|
+
---
|
30
|
+
pl:
|
31
|
+
formtastic:
|
32
|
+
:null: Nie ustawiony
|
33
|
+
---
|
34
|
+
pt-BR:
|
35
|
+
formtastic:
|
36
|
+
:null: Não configurado
|
37
|
+
---
|
38
|
+
pt:
|
39
|
+
formtastic:
|
40
|
+
:null: Não configurado
|
41
|
+
---
|
42
|
+
ru:
|
43
|
+
formtastic:
|
44
|
+
:null: Неизвестно
|
45
|
+
---
|
46
|
+
tr:
|
47
|
+
formtastic:
|
48
|
+
:null: Ayarlanmadı
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# https://guides.rubygems.org/name-your-gem/
|
4
|
+
# https://bundler.io/guides/creating_gem.html
|
5
|
+
# https://guides.rubyonrails.org/engines.html
|
6
|
+
# https://guides.rubyonrails.org/plugins.html
|
7
|
+
|
8
|
+
require_relative "lib/formtastic_tristate_radio/version"
|
9
|
+
|
10
|
+
Gem::Specification.new do |spec|
|
11
|
+
spec.name = "formtastic_tristate_radio"
|
12
|
+
spec.version = FormtasticTristateRadio::VERSION
|
13
|
+
spec.authors = ["Sergey Pedan"]
|
14
|
+
spec.email = ["sergey.pedan@gmail.com"]
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.summary = "Have 3-state radiobuttons instead of a 2-state checkbox for your Boolean columns which can store NULL"
|
18
|
+
spec.description = <<~HEREDOC
|
19
|
+
#{spec.summary}.
|
20
|
+
|
21
|
+
What the gem does?
|
22
|
+
|
23
|
+
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).
|
24
|
+
1. Teaches Rails recognize `"null"` and `"nil"` param values as `nil`. See “[How it works](#how-it-works)” ☟ section for technical details on this.
|
25
|
+
1. Encourages you to add translations for ActiveAdmin “status tag” so that `nil` be correctly translated as “Unset” instead of “False”.
|
26
|
+
|
27
|
+
Does not change controls, you need to turn it on via `as: :tristate_radio` option.
|
28
|
+
|
29
|
+
By defenition Boolean values have 2 states: True & False.
|
30
|
+
|
31
|
+
However, if you store a Boolean value in a database column with no `NOT NULL` restriction, it aquires a 3<sup>d</sup> possible state: `null`.
|
32
|
+
|
33
|
+
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.
|
34
|
+
HEREDOC
|
35
|
+
|
36
|
+
spec.homepage = "https://github.com/sergeypedan/formtastic-tristate-radio"
|
37
|
+
spec.extra_rdoc_files = ["README.md", "CHANGELOG.md"]
|
38
|
+
spec.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
spec.metadata = { "changelog_uri" => "#{spec.homepage}/blob/master/CHANGELOG.md",
|
40
|
+
"documentation_uri" => "https://www.rubydoc.info/gems/#{spec.name}",
|
41
|
+
"homepage_uri" => spec.homepage,
|
42
|
+
"source_code_uri" => spec.homepage }
|
43
|
+
|
44
|
+
spec.require_paths = ["app/inputs", "app/models/active_record", "config/initializers", "config/locales", "lib"]
|
45
|
+
spec.bindir = "exe"
|
46
|
+
spec.executables = []
|
47
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
48
|
+
`git ls-files`.split("\n")
|
49
|
+
.reject { |f| %w[bin spec test].any? { |dir| f.start_with? dir } }
|
50
|
+
.reject { |f| f.start_with? "." }
|
51
|
+
end
|
52
|
+
|
53
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
|
54
|
+
|
55
|
+
spec.add_dependency "formtastic", ">= 3", "< 5"
|
56
|
+
spec.add_dependency "rails", ">= 4", "< 7"
|
57
|
+
|
58
|
+
spec.add_development_dependency "rspec", "~> 3"
|
59
|
+
spec.add_development_dependency "yard", ">= 0.9.20", "< 1"
|
60
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormtasticTristateRadio
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_writer :config
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.config
|
10
|
+
@config ||= Configuration.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.configure
|
14
|
+
yield(config)
|
15
|
+
end
|
16
|
+
|
17
|
+
class Configuration
|
18
|
+
attr_accessor :unset_key
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@unset_key = :null
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative "formtastic_tristate_radio/version"
|
2
|
+
require_relative "formtastic_tristate_radio/missing_translation_error"
|
3
|
+
require_relative "formtastic_tristate_radio/configuration"
|
4
|
+
require_relative "formtastic_tristate_radio/engine"
|
3
5
|
|
4
6
|
require_relative "../app/models/active_record/base"
|
5
|
-
# require_relative "../app/inputs/tristate_radio_input"
|
6
7
|
|
7
8
|
module FormtasticTristateRadio
|
8
9
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: formtastic_tristate_radio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergey Pedan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-11-
|
11
|
+
date: 2021-11-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: formtastic
|
@@ -51,102 +51,98 @@ dependencies:
|
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: '7'
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
54
|
+
name: rspec
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - "~>"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
59
|
+
version: '3'
|
60
60
|
type: :development
|
61
61
|
prerelease: false
|
62
62
|
version_requirements: !ruby/object:Gem::Requirement
|
63
63
|
requirements:
|
64
64
|
- - "~>"
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: '
|
66
|
+
version: '3'
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
|
-
name:
|
68
|
+
name: yard
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
70
70
|
requirements:
|
71
|
-
- - "
|
72
|
-
- !ruby/object:Gem::Version
|
73
|
-
version: '0.14'
|
74
|
-
type: :development
|
75
|
-
prerelease: false
|
76
|
-
version_requirements: !ruby/object:Gem::Requirement
|
77
|
-
requirements:
|
78
|
-
- - "~>"
|
71
|
+
- - ">="
|
79
72
|
- !ruby/object:Gem::Version
|
80
|
-
version:
|
81
|
-
-
|
82
|
-
name: rake
|
83
|
-
requirement: !ruby/object:Gem::Requirement
|
84
|
-
requirements:
|
85
|
-
- - "~>"
|
73
|
+
version: 0.9.20
|
74
|
+
- - "<"
|
86
75
|
- !ruby/object:Gem::Version
|
87
|
-
version: '
|
76
|
+
version: '1'
|
88
77
|
type: :development
|
89
78
|
prerelease: false
|
90
79
|
version_requirements: !ruby/object:Gem::Requirement
|
91
80
|
requirements:
|
92
|
-
- - "
|
93
|
-
- !ruby/object:Gem::Version
|
94
|
-
version: '13'
|
95
|
-
- !ruby/object:Gem::Dependency
|
96
|
-
name: yard
|
97
|
-
requirement: !ruby/object:Gem::Requirement
|
98
|
-
requirements:
|
99
|
-
- - "~>"
|
81
|
+
- - ">="
|
100
82
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
102
|
-
|
103
|
-
prerelease: false
|
104
|
-
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
requirements:
|
106
|
-
- - "~>"
|
83
|
+
version: 0.9.20
|
84
|
+
- - "<"
|
107
85
|
- !ruby/object:Gem::Version
|
108
|
-
version: '
|
109
|
-
description:
|
110
|
-
|
111
|
-
|
86
|
+
version: '1'
|
87
|
+
description: |
|
88
|
+
Have 3-state radiobuttons instead of a 2-state checkbox for your Boolean columns which can store NULL.
|
89
|
+
|
90
|
+
What the gem does?
|
91
|
+
|
92
|
+
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).
|
93
|
+
1. Teaches Rails recognize `"null"` and `"nil"` param values as `nil`. See “[How it works](#how-it-works)” ☟ section for technical details on this.
|
94
|
+
1. Encourages you to add translations for ActiveAdmin “status tag” so that `nil` be correctly translated as “Unset” instead of “False”.
|
95
|
+
|
96
|
+
Does not change controls, you need to turn it on via `as: :tristate_radio` option.
|
97
|
+
|
98
|
+
By defenition Boolean values have 2 states: True & False.
|
99
|
+
|
100
|
+
However, if you store a Boolean value in a database column with no `NOT NULL` restriction, it aquires a 3<sup>d</sup> possible state: `null`.
|
101
|
+
|
102
|
+
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.
|
112
103
|
email:
|
113
104
|
- sergey.pedan@gmail.com
|
114
105
|
executables: []
|
115
106
|
extensions: []
|
116
107
|
extra_rdoc_files:
|
117
108
|
- README.md
|
109
|
+
- CHANGELOG.md
|
118
110
|
files:
|
111
|
+
- CHANGELOG.md
|
112
|
+
- Gemfile
|
113
|
+
- Gemfile.lock
|
114
|
+
- MIT-LICENSE
|
119
115
|
- README.md
|
120
116
|
- Rakefile
|
121
117
|
- app/inputs/tristate_radio_input.rb
|
122
118
|
- app/models/active_record/base.rb
|
123
119
|
- config/initializers/activemodel_type_boolean.rb
|
124
|
-
- config/locales/active_admin.
|
125
|
-
- config/locales/
|
126
|
-
- config/locales/formtastic.en.yml
|
127
|
-
- config/locales/formtastic.ru.yml
|
120
|
+
- config/locales/active_admin.yml
|
121
|
+
- config/locales/formtastic.yml
|
128
122
|
- config/routes.rb
|
123
|
+
- formtastic_tristate_radio.gemspec
|
129
124
|
- lib/formtastic_tristate_radio.rb
|
125
|
+
- lib/formtastic_tristate_radio/configuration.rb
|
130
126
|
- lib/formtastic_tristate_radio/engine.rb
|
127
|
+
- lib/formtastic_tristate_radio/missing_translation_error.rb
|
131
128
|
- lib/formtastic_tristate_radio/version.rb
|
132
|
-
- lib/tasks/formtastic_tristate_radio_tasks.rake
|
133
129
|
homepage: https://github.com/sergeypedan/formtastic-tristate-radio
|
134
130
|
licenses:
|
135
131
|
- MIT
|
136
132
|
metadata:
|
137
|
-
changelog_uri: https://github.com/sergeypedan/formtastic-tristate-radio/blob/master/
|
138
|
-
documentation_uri: https://
|
133
|
+
changelog_uri: https://github.com/sergeypedan/formtastic-tristate-radio/blob/master/CHANGELOG.md
|
134
|
+
documentation_uri: https://www.rubydoc.info/gems/formtastic_tristate_radio
|
139
135
|
homepage_uri: https://github.com/sergeypedan/formtastic-tristate-radio
|
140
136
|
source_code_uri: https://github.com/sergeypedan/formtastic-tristate-radio
|
141
137
|
post_install_message:
|
142
138
|
rdoc_options:
|
143
139
|
- "--charset=UTF-8"
|
144
140
|
require_paths:
|
145
|
-
- lib
|
146
141
|
- app/inputs
|
147
142
|
- app/models/active_record
|
148
143
|
- config/initializers
|
149
144
|
- config/locales
|
145
|
+
- lib
|
150
146
|
required_ruby_version: !ruby/object:Gem::Requirement
|
151
147
|
requirements:
|
152
148
|
- - ">="
|