hstore_radio_buttons 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .ruby-*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hstore_radio_buttons.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ian Whitney
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,232 @@
1
+ # hstore_radio_buttons
2
+
3
+ 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.
4
+
5
+ # Requirements
6
+
7
+ - Postgres. If you have the hstore extension enabled, great. If not, the
8
+ migration generated by this gem will turn it on.
9
+ - Rails 3 and the activerecord-postgres-hstore gem installed
10
+
11
+ This might work with Rails 4, which has native support for hstore, but I haven't tried that yet.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'hstore_radio_buttons'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install hstore_radio_buttons
26
+
27
+ hstore_radio_buttons will save all of your hstores in a single table. To
28
+ generate that table, do:
29
+
30
+ $ rails generate hstore_radio_buttons:migration
31
+ $ rake db:migrate
32
+
33
+ The migration does two things:
34
+
35
+ 1. Executes the command "CREATE EXTENSION IF NOT EXISTS hstore"
36
+ 2. Creates a polymorphic table hstore_radio_data, which is where all the
37
+ persisted data will be saved.
38
+
39
+ If you don't need to set up the hstore extension, just remove that part
40
+ of the migration before running it.
41
+
42
+ ## Usage
43
+
44
+ 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:
45
+
46
+ Gender:
47
+ o Male
48
+ o Female
49
+ o Other
50
+
51
+ Favorite Barn Animal:
52
+ o Cow
53
+ o Sheep
54
+ o Pig
55
+
56
+ ### Defining buttons in a YAML file
57
+
58
+ 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.
59
+
60
+ ---
61
+ person:
62
+ gender:
63
+ - male
64
+ - female
65
+ - other
66
+ 'favorite barn animal':
67
+ - cow
68
+ - sheep
69
+ - pig
70
+
71
+ 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.
72
+
73
+ Then set up your model so that it knows it has hstore_radio_buttons.
74
+
75
+ class Person < ActiveRecord::Base
76
+ include HstoreRadioButtons
77
+
78
+ hstore_radio_buttons
79
+ ...
80
+ end
81
+
82
+ Changing the location of the file is done by passing in the path of your
83
+ configuration file:
84
+
85
+ class Person < ActiveRecord::Base
86
+ include HstoreRadioButtons
87
+
88
+ hstore_radio_buttons './config/yamls/person_hstore_buttons.yml'
89
+ ...
90
+ end
91
+
92
+ ### Defining buttons with macros in the model itself
93
+
94
+ Instead of using the yaml file, you can define your buttons macro-style
95
+ within the model itself.
96
+
97
+ class Report < ActiveRecord::Base
98
+ include HstoreRadioButtons
99
+
100
+ hstore_radio_button Hash['viewed' => ['true', 'false']]
101
+ hstore_radio_button Hash['written by' => %w(monkeys interns milton)]
102
+ end
103
+
104
+ ### Getters, Setters, Security
105
+
106
+ Adding the button sets gives you getters and setters for those sets:
107
+
108
+ >> p = Person.find(1)
109
+ >> p.gender
110
+ => 'female'
111
+ >> p.favorite_barn_animal
112
+ => 'sheep'
113
+ >> p.favorite_barn_animal = 'pig'
114
+ => 'pig'
115
+
116
+ Keep in mind that the returned data will always be strings. So boolean
117
+ values aren't true and false, they are 'true' and 'false'. That's just
118
+ how hstore works.
119
+
120
+ And, for the sake of security, you can't set a value to
121
+ something that's not in your button definition. So if someone changes their
122
+ form submission values to include malicious data that should not be a
123
+ problem.<sup>*</sup>
124
+
125
+
126
+ >> p = Person.find(1)
127
+ >> p.gender = 'something hackerish'
128
+ >> p.gender = nil
129
+
130
+ <sup>*</sup> Notice the 'should' part there. I am not a security expert
131
+ and I welcome any pull requests that make this gem more secure.
132
+
133
+ Validations from ActiveModel work as well. So you can do:
134
+
135
+ class Person < ActiveRecord::Base
136
+ ...
137
+ validates_presence_of :gender
138
+ end
139
+
140
+ Or whatever other validations you need.
141
+
142
+ ### Displaying buttons in a form
143
+
144
+ To display the radio button set on the form, you have three options:
145
+
146
+ 1. Use a helper to display a single radio button
147
+ 2. Use a helper to display all the radio buttons
148
+ 3. Use the Rails form helpers
149
+
150
+ #### Use a helper to display a single radio button
151
+
152
+ <%= form_for @person do |f|>
153
+ <%= f.hstore_radio_button('gender') %>
154
+ ...
155
+ <%= f.hstore_radio_button('favorite barn animal') %>
156
+ <% end %>
157
+
158
+ #### Use a helper to display all the radio buttons
159
+
160
+ <%= form_for @person do |f|>
161
+ <%= f.hstore_radio_buttons %>
162
+ <% end %>
163
+
164
+ #### Use the Rails form helpers:
165
+
166
+ <%= form_for @person do |f|>
167
+ <%= f.label(:gender) %>
168
+ <%= f.radio_button(:gender, 'male') %>
169
+ <%= f.label(:gender, "Male", :value => 'male') %>
170
+ ...etc...
171
+ <% end %>
172
+
173
+ If you want to avoid the duplication that the above introduces, you can
174
+ use the _options method that is added to your model:
175
+
176
+ <%= form_for @person do |f|>
177
+ <%= f.label(:gender) %>
178
+ <% @person.gender_options.each do |option| %>
179
+ <%= f.radio_button(:gender, option) %>
180
+ <%= f.label(:gender, option.titleize, :value => option) %>
181
+ <% end %>
182
+ <% end %>
183
+
184
+ ### Controlling helper-created output
185
+
186
+ By default the `hstore_radio_button` or `hstore_radio_buttons` helpers
187
+ will give you output like this:
188
+
189
+ <div>
190
+ <label for='person_gender'>Gender</label><br />
191
+ <input id='person_gender_male' name 'person[gender]' type='radio'
192
+ value='male'>
193
+ <label for='person_gender_male'>Male</label><br />
194
+ </div>
195
+
196
+ You can swap those `<br />` tags for a different separator by passing in
197
+ your own:
198
+
199
+ <%= f.hstore_radio_button('gender', separator: " - " %>
200
+
201
+ If you need more control, you should probably just use the Rails helpers
202
+ approach.
203
+
204
+ ### Persistence
205
+
206
+ The conversion of your data into an hstore is handled by
207
+ [activerecord-postgres-store]
208
+ (https://github.com/engageis/activerecord-postgres-hstore) so refer to
209
+ their documentation.
210
+
211
+ Your data will be stored in the hstore_radio_data table. If you saved
212
+ data for a Person with the id of 1, it will be saved as
213
+
214
+ model_id model_type hstore_data
215
+ 1 Person {'gender' => 'other'....}
216
+
217
+ But it's easiest to just work with the getters and setters in the Person
218
+ model than dealing directly with this table.
219
+
220
+ And, of course, this perisisted data is used to mark the correct radio
221
+ buttons as 'selected' when the form is loaded later.
222
+
223
+ TODO: It'd be nice to have default values for a set.
224
+ TODO: Formtastic integration
225
+
226
+ ## Contributing
227
+
228
+ 1. Fork it
229
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
230
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
231
+ 4. Push to the branch (`git push origin my-new-feature`)
232
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.test_files = FileList['test/**/*_test.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hstore_radio_buttons/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hstore_radio_buttons"
8
+ spec.version = HstoreRadioButtons::VERSION
9
+ spec.authors = ["Ian Whitney"]
10
+ spec.email = ["ian@ianwhitney.com"]
11
+ spec.description = %q{Works with postgres's hstore data type and Rails to let you display/store a collection of radio buttons in an hstore field}
12
+ spec.summary = %q{Define a set of radio buttons for a model and then save the perisisted data for these buttons in an hstore field. Good for data that you want to persist, but that isn't important enough for its own field in the model.}
13
+ spec.homepage = "https://github.com/IanWhitney/hstore_radio_buttons"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'activerecord', '~> 3.0'
22
+ spec.add_dependency 'activesupport', '~> 3.0'
23
+ spec.add_dependency 'actionpack', '~> 3.0'
24
+ spec.add_dependency 'activerecord-postgres-hstore'
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "sqlite3"
29
+ spec.add_development_dependency "mocha"
30
+ end
@@ -0,0 +1,11 @@
1
+ require 'rails/generators/named_base'
2
+
3
+ module HstoreRadioButtons
4
+ module Generators
5
+ module Base
6
+ def source_root
7
+ @_hstore_radio_buttons_source_root ||= File.expand_path(File.join('../hstore_radio_buttons', generator_name, 'templates'), __FILE__)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ require 'generators/hstore_radio_buttons'
2
+ require 'rails/generators/active_record'
3
+
4
+ module HstoreRadioButtons
5
+ module Generators
6
+ class MigrationGenerator < ActiveRecord::Generators::Base
7
+ extend Base
8
+
9
+ argument :name, :type => :string, :default => 'create_hstore_radio_data'
10
+
11
+ def generate_files
12
+ migration_template 'migration.rb', "db/migrate/#{name}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ class CreateHstoreRadioData < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :hstore_radio_data do |t|
4
+ t.belongs_to :model, :polymorphic => true
5
+ t.hstore :hstore_data
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ drop_table :hstore_radio_data
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ require 'action_view'
2
+ require 'active_support/concern'
3
+ require 'active_support/dependencies/autoload'
4
+
5
+ require 'hstore_radio_buttons/version'
6
+ require 'hstore_radio_buttons/configuration'
7
+ require 'hstore_radio_buttons/button_options'
8
+ require 'hstore_radio_buttons/button_set'
9
+ require 'hstore_radio_buttons/helpers/form_helper'
10
+
11
+ module HstoreRadioButtons
12
+ extend ActiveSupport::Concern
13
+ extend ActiveSupport::Autoload
14
+
15
+ autoload :HstoreRadioData
16
+
17
+ module ClassMethods
18
+ def hstore_radio_buttons(yaml_file_location = './config/hstore_radio_button_sets.yml')
19
+ HstoreRadioButtons::Configuration.from_yaml(self,yaml_file_location)
20
+ end
21
+
22
+ def hstore_radio_button(button_hash)
23
+ HstoreRadioButtons::Configuration.from_hash(self, button_hash)
24
+ end
25
+ end
26
+
27
+ def hstore_data_proxy
28
+ if self.hstore_radio_data.nil?
29
+ self.build_hstore_radio_data.hstore_data
30
+ else
31
+ self.hstore_radio_data.hstore_data
32
+ end
33
+ end
34
+ private :hstore_data_proxy
35
+
36
+ included do
37
+ has_one :hstore_radio_data, :class_name => 'HstoreRadioButtons::HstoreRadioData', :as => :model
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ module HstoreRadioButtons
2
+ class ButtonOptions
3
+ attr_accessor :name, :options
4
+ def initialize(name, options)
5
+ self.name = name.gsub(/ /,"_").downcase
6
+ self.options = *options
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,32 @@
1
+ module HstoreRadioButtons
2
+ class ButtonSet
3
+ def initialize(button_definition, model)
4
+ setter_method = "#{button_definition.name}=".to_sym
5
+ getter_method = button_definition.name.to_sym
6
+ options_method = "#{button_definition.name}_options".to_sym
7
+
8
+ model.send(:define_method, getter_method) {
9
+ hstore_data_proxy[button_definition.name]
10
+ }
11
+
12
+ model.send(:define_method, setter_method) {|value|
13
+ if self.send(options_method).include?(value)
14
+ hstore_data_proxy[button_definition.name] = value
15
+ else
16
+ hstore_data_proxy[button_definition.name] = nil
17
+ end
18
+ }
19
+
20
+ model.send(:define_method, options_method) {
21
+ button_definition.options
22
+ }
23
+
24
+ if !model.instance_variable_defined?(:@hstore_button_names)
25
+ model.instance_variable_set(:@hstore_button_names, Set.new)
26
+ end
27
+ all_buttons_on_model = model.send(:instance_variable_get, :@hstore_button_names)
28
+
29
+ model.send(:instance_variable_set, :@hstore_button_names, all_buttons_on_model << button_definition.name.to_sym)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ module HstoreRadioButtons
2
+ class Configuration
3
+ def self.from_yaml(model, yaml_file_location = './config/hstore_radio_button_sets.yml')
4
+ self.new._from_yaml(model, yaml_file_location)
5
+ end
6
+
7
+ def self.from_hash(model, raw_button_options)
8
+ self.new._from_hash(model, raw_button_options)
9
+ end
10
+
11
+ def _from_yaml(model, yaml_file_location)
12
+ self.yaml_file_location = yaml_file_location
13
+ self.model = model
14
+
15
+ if yaml_exists?
16
+ button_options_from_yaml.each {|button_options| HstoreRadioButtons::ButtonSet.new(button_options,model)}
17
+ end
18
+ end
19
+
20
+ def _from_hash(model, raw_button_options)
21
+ self.model = model
22
+ button_options = button_options_from_raw(raw_button_options)
23
+ HstoreRadioButtons::ButtonSet.new(button_options,model)
24
+ end
25
+
26
+ private
27
+ attr_accessor :model, :yaml_file_location
28
+ def config_file
29
+ File.open(yaml_file_location)
30
+ end
31
+
32
+ def yaml_exists?
33
+ File.exists?(yaml_file_location)
34
+ end
35
+
36
+ def button_options_from_yaml
37
+ button_options = []
38
+ YAML.load(config_file)[model.to_s.downcase].each do |key, value|
39
+ button_options << button_options_from_raw(Hash[key,value])
40
+ end
41
+ button_options
42
+ end
43
+
44
+ def button_options_from_raw(raw_button_options)
45
+ ButtonOptions.new(raw_button_options.keys.first, raw_button_options.values.first)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,36 @@
1
+ require 'action_view'
2
+
3
+ module HstoreRadioButtons::FormHelper
4
+ def hstore_radio_button(object_name, method, tag_value, options = {})
5
+ separator = options.delete(:separator)
6
+ label = label(object_name, tag_value, options)
7
+ input = radio_button(object_name, method, tag_value, options)
8
+ "#{input}#{label}#{separator}"
9
+ end
10
+ end
11
+
12
+ module HstoreRadioButtons::FormBuilder
13
+ include ActionView::Helpers::TagHelper
14
+
15
+ def hstore_radio_button(method, options = {})
16
+ options[:separator] = (options[:separator] ? options[:separator] : "<br />")
17
+ radio_options = object.public_send("#{method}_options".to_sym)
18
+ button_set = "#{method.to_s.titleize}#{options[:separator]}"
19
+ radio_options.each do |radio_option|
20
+ button_set += @template.hstore_radio_button(@object_name, method, radio_option, objectify_options(options))
21
+ end
22
+ content_tag(:div, button_set.html_safe).html_safe
23
+ end
24
+
25
+ def hstore_radio_buttons(options = {})
26
+ all_buttons = ""
27
+ object.class.instance_variable_get(:@hstore_button_names).each do |method|
28
+ all_buttons += hstore_radio_button(method, options)
29
+ end
30
+ all_buttons.html_safe
31
+ end
32
+ end
33
+
34
+ ActionView::Helpers::FormHelper.send(:include, HstoreRadioButtons::FormHelper)
35
+ ActionView::Base.send(:include, HstoreRadioButtons::FormHelper)
36
+ ActionView::Helpers::FormBuilder.send(:include, HstoreRadioButtons::FormBuilder)
@@ -0,0 +1,7 @@
1
+ require 'activerecord-postgres-hstore'
2
+ module HstoreRadioButtons
3
+ class HstoreRadioData < ActiveRecord::Base
4
+ belongs_to :model, :polymorphic => true
5
+ serialize :hstore_data, ActiveRecord::Coders::Hstore
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module HstoreRadioButtons
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../../test_helper.rb'
2
+
3
+ describe HstoreRadioButtons::ButtonOptions do
4
+ describe "#name" do
5
+ it "replaces spaces in a name with underscores" do
6
+ it = HstoreRadioButtons::ButtonOptions.new('test name', nil)
7
+ it.name.must_equal 'test_name'
8
+ end
9
+
10
+ it "downcases the name" do
11
+ it = HstoreRadioButtons::ButtonOptions.new('Test', nil)
12
+ it.name.must_equal 'test'
13
+ end
14
+ end
15
+
16
+ describe "#options" do
17
+ it "turns a single option into an array" do
18
+ it = HstoreRadioButtons::ButtonOptions.new('test', 1)
19
+ it.options.must_equal [1]
20
+ end
21
+
22
+ it "turns a null options into an array" do
23
+ it = HstoreRadioButtons::ButtonOptions.new('test', nil)
24
+ it.options.must_equal []
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,58 @@
1
+ require_relative '../../test_helper.rb'
2
+
3
+ describe HstoreRadioButtons::ButtonSet do
4
+ describe '#initialize' do
5
+ before :each do
6
+ @test_yaml_file_location = './test/support/config/hstore_radio_button_sets.yml'
7
+ @it = Person.new
8
+ config = YAML.load(File.open(@test_yaml_file_location))
9
+ @raw_button_definitions = config[@it.class.to_s.downcase]
10
+ @button_definitions = []
11
+
12
+ @raw_button_definitions.each do |button_definition|
13
+ button_options = HstoreRadioButtons::ButtonOptions.new(button_definition[0],button_definition[1])
14
+ @button_definitions << button_options
15
+ HstoreRadioButtons::ButtonSet.new(button_options,@it.class)
16
+ end
17
+ end
18
+
19
+ it 'adds getters and setters for the button sets defined in the config file' do
20
+ @button_definitions.each do |button_definition|
21
+ @it.must_respond_to button_definition.name.to_sym
22
+ @it.must_respond_to "#{button_definition.name}=".to_sym
23
+ end
24
+ end
25
+
26
+ it "adds a method that returns the button set's options" do
27
+ @button_definitions.each do |button_definition|
28
+ @it.must_respond_to "#{button_definition.name}_options".to_sym
29
+ @it.send("#{button_definition.name}_options".to_sym).must_equal button_definition.options
30
+ end
31
+ end
32
+
33
+ it "defines the setter so that a value not in the set's option is not passed to the hstore" do
34
+ bad_value = "hackery_nonsense"
35
+ @button_definitions.each do |button_definition|
36
+ @it.send("#{button_definition.name}=".to_sym, bad_value)
37
+ @it.send(button_definition.name.to_sym).must_be_nil
38
+ end
39
+ end
40
+
41
+ it 'persists data sent to setters' do
42
+ @button_definitions.each do |button_definition|
43
+ random_value = button_definition.options.sample
44
+ @it.send("#{button_definition.name}=".to_sym, random_value)
45
+ @it.send(button_definition.name.to_sym).must_equal random_value
46
+ end
47
+ end
48
+
49
+ it 'allows the use of ActiveModel validation methods' do
50
+ @it.class.validates_presence_of(@button_definitions.sample.name)
51
+ @it.valid?.must_equal false
52
+ end
53
+
54
+ it 'creates a class instance variable that returns the names of all hstore button names' do
55
+ @it.class.instance_variable_get(:@hstore_button_names).must_equal @button_definitions.map {|d| d.name.to_sym}.to_set
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,57 @@
1
+ require_relative '../../test_helper.rb'
2
+
3
+ describe HstoreRadioButtons::Configuration do
4
+
5
+ describe "#from_yaml" do
6
+ before :each do
7
+ @test_yaml_file_location = './test/support/config/hstore_radio_button_sets.yml'
8
+ end
9
+
10
+ it "checks if a provided yaml file exists" do
11
+ File.expects(:exists?).with(@test_yaml_file_location).returns(true)
12
+ HstoreRadioButtons::Configuration.from_yaml(Person, @test_yaml_file_location)
13
+ end
14
+
15
+ it "checks if the default yaml file exists if none provided" do
16
+ yaml_file_location = './config/hstore_radio_button_sets.yml'
17
+ File.expects(:exists?).with(yaml_file_location).returns(false)
18
+ HstoreRadioButtons::Configuration.from_yaml(Person, yaml_file_location)
19
+ end
20
+
21
+ describe "has a yaml file to use" do
22
+ before :each do
23
+ @it = Person.new
24
+ config = YAML.load(File.open(@test_yaml_file_location))
25
+ @button_sets = config[@it.class.to_s.downcase]
26
+ @button_option_double = HstoreRadioButtons::ButtonOptions.new('test',[])
27
+ HstoreRadioButtons::ButtonOptions.stubs(:new).returns(@button_option_double)
28
+ end
29
+
30
+ it 'creates a button_set for each button set defined in the config file' do
31
+ @button_sets.each do |button_set|
32
+ HstoreRadioButtons::ButtonSet.expects(:new).with(@button_option_double,@it.class).returns(nil)
33
+ end
34
+ HstoreRadioButtons::Configuration.from_yaml(Person, @test_yaml_file_location)
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "#from_hash" do
40
+ describe "accepts a model and a button-defining hash to create a button" do
41
+ it "creates a button_set for each button defined by a hstore_radio_button macro" do
42
+ class Report < ActiveRecord::Base; end
43
+ @button_option_double = HstoreRadioButtons::ButtonOptions.new('test',[])
44
+ HstoreRadioButtons::ButtonOptions.stubs(:new).returns(@button_option_double)
45
+
46
+ HstoreRadioButtons::ButtonSet.expects(:new).with(@button_option_double,Report).twice.returns(nil)
47
+
48
+ class Report < ActiveRecord::Base
49
+ include HstoreRadioButtons
50
+
51
+ hstore_radio_button Hash['viewed' => ['true', 'false']]
52
+ hstore_radio_button Hash['written by' => %w(monkeys interns milton)]
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,64 @@
1
+ require_relative '../../../test_helper.rb'
2
+ require 'action_view/test_case'
3
+
4
+ class FormBuilderTest < ActionView::TestCase
5
+ include ActiveSupport::Testing::SetupAndTeardown
6
+ include ActionView::TestCase::Behavior
7
+
8
+ setup do
9
+ @person_instance = Person.new
10
+ end
11
+
12
+ test "adds my FormHelper to Rails' FormHelper" do
13
+ assert ActionView::Helpers::FormBuilder.include?(HstoreRadioButtons::FormBuilder)
14
+ end
15
+
16
+ test "FormBuilder instances can respond to hstore_radio_button" do
17
+ it = ActionView::Helpers::FormBuilder.new(:person, @person_instance, view, {}, proc {})
18
+ assert it.respond_to?(:hstore_radio_button)
19
+ end
20
+
21
+ test "FormBuilder#hstore_radio_button wraps the whole thing in a div" do
22
+ view.stubs(:hstore_radio_button).with(any_parameters).returns("")
23
+ it = ActionView::Helpers::FormBuilder.new(:person, @person_instance, view, {}, proc {})
24
+ assert_match /^<div>/, it.hstore_radio_button(:gender)
25
+ assert_match /<\/div>$/, it.hstore_radio_button(:gender)
26
+ end
27
+
28
+ test "FormBuilder#hstore_radio_button returns a set of buttons that are prefaced with the button set's name" do
29
+ view.stubs(:hstore_radio_button).with(any_parameters).returns("")
30
+ it = ActionView::Helpers::FormBuilder.new(:person, @person_instance, view, {}, proc {})
31
+ assert_match /^<div>Gender/, it.hstore_radio_button(:gender)
32
+ end
33
+
34
+ test "#hstore_radio_button accepts a separator, which will be used to separate the button-set header from the buttons" do
35
+ view.stubs(:hstore_radio_button).with(any_parameters).returns("")
36
+ it = ActionView::Helpers::FormBuilder.new(:person, @person_instance, view, {}, proc {})
37
+ assert_match /^<div>Gender-/, it.hstore_radio_button(:gender, separator: "-")
38
+ end
39
+
40
+ test "FormBuilder#hstore_radio_button gets a button from FormHelper for each value in the _options method" do
41
+ expected = "<div>Gender<br />"
42
+ @person_instance.gender_options.each do |option|
43
+ view.stubs(:hstore_radio_button).with(:person, :gender, option, {:separator => "<br />", :object => @person_instance}).returns(option)
44
+ expected += "#{option}"
45
+ end
46
+ expected += "</div>"
47
+
48
+ it = ActionView::Helpers::FormBuilder.new(:person, @person_instance, view, {}, proc {})
49
+
50
+ assert_equal expected, it.hstore_radio_button(:gender)
51
+ end
52
+
53
+ test "#hstore_radio_buttons builds a button set for each button defined in the class" do
54
+ it = ActionView::Helpers::FormBuilder.new(:person, @person_instance, view, {}, proc {})
55
+
56
+ all_buttons = Person.instance_variable_get(:@hstore_button_names)
57
+ expected = ""
58
+ all_buttons.each do |b|
59
+ expected += it.hstore_radio_button(b)
60
+ end
61
+
62
+ assert_equal expected, it.hstore_radio_buttons
63
+ end
64
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../../../test_helper.rb'
2
+ require 'action_view/test_case'
3
+
4
+ class FormHelperTest < ActionView::TestCase
5
+ include ActionView::TestCase::Behavior
6
+
7
+ test "adds my FormHelper to Rails' FormHelper" do
8
+ assert ActionView::Helpers::FormHelper.include?(HstoreRadioButtons::FormHelper)
9
+ assert ActionView::Base.include?(HstoreRadioButtons::FormHelper)
10
+ end
11
+
12
+ test "view can respond to hstore_radio_button" do
13
+ assert view.respond_to?(:hstore_radio_button)
14
+ end
15
+
16
+ test "returns a radio input, label and separator of <br />" do
17
+ it = view.hstore_radio_button(:person, :gender, 'male', separator: "<br />")
18
+ expected = ""
19
+ expected += view.radio_button(:person, :gender, 'male', {})
20
+ expected += view.label(:person, 'male', {})
21
+ expected += "<br />"
22
+ assert_equal expected, it
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ require_relative '../../test_helper.rb'
2
+
3
+ describe HstoreRadioButtons do
4
+ it 'adds a HstoreRadioData active record class' do
5
+ HstoreRadioButtons::HstoreRadioData.new.wont_be_nil
6
+ end
7
+
8
+ it 'allows a class to respond to hstore_radio_buttons' do
9
+ Person.must_respond_to :hstore_radio_buttons
10
+ end
11
+
12
+ it 'builds a has_one association with the hstore_radio_data table' do
13
+ Person.new.must_respond_to :hstore_radio_data
14
+ Person.new.must_respond_to :build_hstore_radio_data
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ require_relative '../../test_helper.rb'
2
+
3
+ describe HstoreRadioButtons do
4
+ it "must have a version" do
5
+ HstoreRadioButtons::VERSION.wont_be_nil
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ ---
2
+ person:
3
+ gender:
4
+ - male
5
+ - female
6
+ - other
7
+ 'favorite barn animal':
8
+ - cow
9
+ - sheep
10
+ - pig
11
+ report:
12
+ accepted:
13
+ - true
14
+ - false
@@ -0,0 +1,6 @@
1
+ class Person < ActiveRecord::Base
2
+ include HstoreRadioButtons
3
+
4
+ hstore_radio_buttons './test/support/config/hstore_radio_button_sets.yml'
5
+ end
6
+
@@ -0,0 +1,23 @@
1
+ ActiveRecord::Base.establish_connection(
2
+ :adapter => 'sqlite3',
3
+ :database => File.expand_path('../../test.db', __FILE__)
4
+ )
5
+
6
+ class CreateSchema < ActiveRecord::Migration
7
+ def self.up
8
+ create_table :people, :force => true do |t|
9
+ t.timestamps
10
+ end
11
+
12
+ create_table :reports, :force => true do |t|
13
+ t.timestamps
14
+ end
15
+
16
+ create_table :hstore_radio_data, :force => true do |t|
17
+ t.belongs_to :model, :polymorphic => true
18
+ t.text :hstore_data
19
+
20
+ t.timestamps
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ lib = File.expand_path('../../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'hstore_radio_buttons'
5
+
6
+ require 'minitest/autorun'
7
+ require 'mocha/setup'
8
+
9
+ require 'fileutils'
10
+ require 'active_record'
11
+ require_relative 'support/models.rb'
12
+ require_relative 'support/schema.rb'
13
+
14
+ CreateSchema.suppress_messages{ CreateSchema.migrate(:up) }
15
+
16
+ MiniTest::Unit.after_tests do
17
+ FileUtils.rm_rf(File.expand_path('../test.db', __FILE__))
18
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hstore_radio_buttons
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ian Whitney
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &70257637365360 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70257637365360
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ requirement: &70257637364800 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70257637364800
36
+ - !ruby/object:Gem::Dependency
37
+ name: actionpack
38
+ requirement: &70257637364220 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '3.0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70257637364220
47
+ - !ruby/object:Gem::Dependency
48
+ name: activerecord-postgres-hstore
49
+ requirement: &70257637363740 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70257637363740
58
+ - !ruby/object:Gem::Dependency
59
+ name: bundler
60
+ requirement: &70257637363060 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '1.3'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70257637363060
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: &70257637362480 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70257637362480
80
+ - !ruby/object:Gem::Dependency
81
+ name: sqlite3
82
+ requirement: &70257637361800 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70257637361800
91
+ - !ruby/object:Gem::Dependency
92
+ name: mocha
93
+ requirement: &70257637361240 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *70257637361240
102
+ description: Works with postgres's hstore data type and Rails to let you display/store
103
+ a collection of radio buttons in an hstore field
104
+ email:
105
+ - ian@ianwhitney.com
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - .gitignore
111
+ - Gemfile
112
+ - LICENSE.txt
113
+ - README.md
114
+ - Rakefile
115
+ - hstore_radio_buttons.gemspec
116
+ - lib/generators/hstore_radio_buttons.rb
117
+ - lib/generators/hstore_radio_buttons/migration/migration_generator.rb
118
+ - lib/generators/hstore_radio_buttons/migration/templates/migration.rb
119
+ - lib/hstore_radio_buttons.rb
120
+ - lib/hstore_radio_buttons/button_options.rb
121
+ - lib/hstore_radio_buttons/button_set.rb
122
+ - lib/hstore_radio_buttons/configuration.rb
123
+ - lib/hstore_radio_buttons/helpers/form_helper.rb
124
+ - lib/hstore_radio_buttons/hstore_radio_data.rb
125
+ - lib/hstore_radio_buttons/version.rb
126
+ - test/lib/hstore_radio_buttons/button_options_test.rb
127
+ - test/lib/hstore_radio_buttons/button_set_test.rb
128
+ - test/lib/hstore_radio_buttons/configuration_test.rb
129
+ - test/lib/hstore_radio_buttons/helpers/form_builder_test.rb
130
+ - test/lib/hstore_radio_buttons/helpers/form_helper_test.rb
131
+ - test/lib/hstore_radio_buttons/hstore_radio_buttons_test.rb
132
+ - test/lib/hstore_radio_buttons/version_test.rb
133
+ - test/support/config/hstore_radio_button_sets.yml
134
+ - test/support/models.rb
135
+ - test/support/schema.rb
136
+ - test/test_helper.rb
137
+ homepage: https://github.com/IanWhitney/hstore_radio_buttons
138
+ licenses:
139
+ - MIT
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ none: false
152
+ requirements:
153
+ - - ! '>='
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 1.8.16
159
+ signing_key:
160
+ specification_version: 3
161
+ summary: Define a set of radio buttons for a model and then save the perisisted data
162
+ for these buttons in an hstore field. Good for data that you want to persist, but
163
+ that isn't important enough for its own field in the model.
164
+ test_files:
165
+ - test/lib/hstore_radio_buttons/button_options_test.rb
166
+ - test/lib/hstore_radio_buttons/button_set_test.rb
167
+ - test/lib/hstore_radio_buttons/configuration_test.rb
168
+ - test/lib/hstore_radio_buttons/helpers/form_builder_test.rb
169
+ - test/lib/hstore_radio_buttons/helpers/form_helper_test.rb
170
+ - test/lib/hstore_radio_buttons/hstore_radio_buttons_test.rb
171
+ - test/lib/hstore_radio_buttons/version_test.rb
172
+ - test/support/config/hstore_radio_button_sets.yml
173
+ - test/support/models.rb
174
+ - test/support/schema.rb
175
+ - test/test_helper.rb