Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
hstore_radio_buttons
Advanced tools
So, you need a bunch of radio buttons on a form. But there's no particular reason for you to save each question in its own database field. And, even better, you have access to the Hstore data type in postgres. hstore_radio_buttons is the library you are looking for. Define a set of radio buttons, display them in your form and then gather them all up and save the data in an hstore field.
This might work with Rails 4, which has native support for hstore, but I haven't tried that yet.
Add this line to your application's Gemfile:
gem 'hstore_radio_buttons'
And then execute:
$ bundle
Or install it yourself as:
$ gem install hstore_radio_buttons
hstore_radio_buttons will save all of your hstores in a single table. To generate that table, do:
$ rails generate hstore_radio_buttons:migration
$ rake db:migrate
The migration does two things:
If you don't need to set up the hstore extension, just remove that part of the migration before running it.
For every collection of radio button questions, you'll need to define their set of values as well as a label for the set of buttons. Say you want something like this:
Gender:
o Male
o Female
o Other
Favorite Barn Animal:
o Cow
o Sheep
o Pig
In above example we have two sets of buttons, one for the Gender question and one for the Barn Animal question. By default, this yaml file is stored in config/hstore_radio_button_sets.yml
. But you can override that and put the file elsewhere, if you want.
---
person:
gender:
- male
- female
- other
'favorite barn animal':
- cow
- sheep
- pig
The above defines two sets of buttons that can be used by the person model. For a model to generate/save radio button data, the set must be defined for the model.
Then set up your model so that it knows it has hstore_radio_buttons.
class Person < ActiveRecord::Base
include HstoreRadioButtons
hstore_radio_buttons
...
end
Changing the location of the file is done by passing in the path of your configuration file:
class Person < ActiveRecord::Base
include HstoreRadioButtons
hstore_radio_buttons './config/yamls/person_hstore_buttons.yml'
...
end
Instead of using the yaml file, you can define your buttons macro-style within the model itself.
class Report < ActiveRecord::Base
include HstoreRadioButtons
hstore_radio_button Hash[viewed: ['true', 'false']]
hstore_radio_button Hash['written by' => %w(monkeys interns milton)]
end
Adding the button sets gives you getters and setters for those sets:
>> p = Person.find(1)
>> p.gender
=> 'female'
>> p.favorite_barn_animal
=> 'sheep'
>> p.favorite_barn_animal = 'pig'
=> 'pig'
Keep in mind that the returned data will always be strings. So boolean values aren't true and false, they are 'true' and 'false'. That's just how hstore works.
And, for the sake of security, you can't set a value to something that's not in your button definition. So if someone changes their form submission values to include malicious data that should not be a problem.*
>> p = Person.find(1)
>> p.gender = 'something hackerish'
>> p.gender = nil
* Notice the 'should' part there. I am not a security expert and I welcome any pull requests that make this gem more secure.
Validations from ActiveModel work as well. So you can do:
class Person < ActiveRecord::Base
...
validates_presence_of :gender
end
Or whatever other validations you need.
To display the radio button set on the form, you have three options:
<%= form_for @person do |f|>
<%= f.hstore_radio_button('gender') %>
...
<%= f.hstore_radio_button(:favorite_barn_animal) %>
<% end %>
You can use the string or a symbolized form. These are synonymous:
<%= f.hstore_radio_button('gender') %>
<%= f.hstore_radio_button(:gender) %>
or
<%= f.hstore_radio_button('Favorite Barn Animal') %>
<%= f.hstore_radio_button(:favorite_barn_animal) %>
<%= form_for @person do |f|>
<%= f.hstore_radio_buttons %>
<% end %>
<%= form_for @person do |f|>
<%= f.label(:gender) %>
<%= f.radio_button(:gender, 'male') %>
<%= f.label(:gender, "Male", :value => 'male') %>
...
<%= f.radio_button(:favorite_barn_animal, 'sheep') %>
<%= f.label(:favorite_barn_animal, "Sheep", :value => 'sheep') %>
...
<% end %>
If you want to avoid the duplication that the above introduces, you can use the _options method that is added to your model:
<%= form_for @person do |f|>
<%= f.label(:gender) %>
<% @person.gender_options.each do |option| %>
<%= f.radio_button(:gender, option) %>
<%= f.label(:gender, option.titleize, :value => option) %>
<% end %>
<% end %>
If you use the Rails helpers, you'll need to use the correct name for the attribute. Use the lowercase, underscored version of your button set name:
<%= f.radio_button(:favorite_barn_animal, 'sheep') %> #works!
<%= f.radio_button('favorite_barn_animal', 'cow') %> #works!
<%= f.radio_button('Favorite Barn Animal', 'pig') %> #fail
By default the hstore_radio_button
or hstore_radio_buttons
helpers
will give you output like this:
<div>
<label for='person_gender'>Gender</label><br />
<input id='person_gender_male' name 'person[gender]' type='radio'
value='male'>
<label for='person_gender_male'>Male</label><br />
</div>
You can swap those <br />
tags for a different separator by passing in
your own:
<%= f.hstore_radio_button('gender', separator: " - " %>
If you need more control, you should probably just use the Rails helpers approach.
If you define a button set like this:
person:
gender:
- male
- female
- other
And then do a render like:
<%= f.label(:gender) %>
or
<%= f.hstore_radio_button(:gender) %>
Then your button set will start with the label "Gender". But what if you want something else? Use the Rails translations api. So in your en.yml file, you could put:
en:
activerecord:
attributes:
person:
gender: "Please pick a gender"
And your radio button set will render as:
Please pick a gender
o Male
o Female
o Other
The conversion of your data into an hstore is handled by [activerecord-postgres-store] (https://github.com/engageis/activerecord-postgres-hstore) so refer to their documentation.
Any model that includes HstoreRadioButtons will have a has_one
associaton with hstore_radio_data
. This relationship will have
:autosave => true
As you might guess from that association, your data will be stored in the hstore_radio_data table. If you saved data for a Person with the id of 1, it will be saved as
model_id model_type hstore_data
1 Person {'gender' => 'other'....}
But it's easiest to just work with the getters and setters in the Person model than dealing directly with this table.
And, of course, this perisisted data is used to mark the correct radio buttons as 'selected' when the form is loaded later.
TODO: It'd be nice to have default values for a set.
TODO: Formtastic integration
Many code ideas were 'borrowed' from Vestal Versions and [LRD] (https://github.com/LRDesign). Testing and design ideas were also contributed by Davin Lagerroos.
git checkout -b my-new-feature
)git commit -am 'Add some feature'
)git push origin my-new-feature
)FAQs
Unknown package
We found that hstore_radio_buttons demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.