caring_form 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.travis.yml +17 -0
  4. data/Appraisals +18 -0
  5. data/Gemfile +9 -0
  6. data/Guardfile +15 -0
  7. data/LICENSE +22 -0
  8. data/README.md +33 -0
  9. data/Rakefile +12 -0
  10. data/app/assets/javascripts/caring_form/form-observer.coffee +63 -0
  11. data/app/assets/javascripts/caring_form/main.coffee +15 -0
  12. data/app/assets/javascripts/caring_form/remote-validator.coffee +71 -0
  13. data/app/assets/javascripts/caring_form/rule-based-validator.coffee +37 -0
  14. data/app/assets/javascripts/caring_form/setup.coffee +5 -0
  15. data/app/assets/javascripts/caring_form/webshims-customization.coffee +25 -0
  16. data/app/assets/javascripts/caring_forms.js +7 -0
  17. data/caring_form.gemspec +31 -0
  18. data/gemfiles/rails_3_1.gemfile +8 -0
  19. data/gemfiles/rails_3_2.gemfile +8 -0
  20. data/gemfiles/rails_4.gemfile +7 -0
  21. data/gemfiles/rails_4_1.gemfile +8 -0
  22. data/lib/caring_form.rb +20 -0
  23. data/lib/caring_form/action_view_extensions/builder.rb +75 -0
  24. data/lib/caring_form/action_view_extensions/config.rb +4 -0
  25. data/lib/caring_form/action_view_extensions/flash_error_helper.rb +28 -0
  26. data/lib/caring_form/action_view_extensions/form_helper.rb +33 -0
  27. data/lib/caring_form/active_model/monkey_patching.rb +32 -0
  28. data/lib/caring_form/dsl.rb +91 -0
  29. data/lib/caring_form/engine.rb +4 -0
  30. data/lib/caring_form/field/base.rb +145 -0
  31. data/lib/caring_form/field/check_box.rb +23 -0
  32. data/lib/caring_form/field/check_boxes.rb +15 -0
  33. data/lib/caring_form/field/collection.rb +48 -0
  34. data/lib/caring_form/field/dummy.rb +30 -0
  35. data/lib/caring_form/field/email.rb +41 -0
  36. data/lib/caring_form/field/full_name.rb +40 -0
  37. data/lib/caring_form/field/looking_for.rb +37 -0
  38. data/lib/caring_form/field/metadata.rb +26 -0
  39. data/lib/caring_form/field/radio_buttons.rb +15 -0
  40. data/lib/caring_form/field/string.rb +13 -0
  41. data/lib/caring_form/field/submit.rb +83 -0
  42. data/lib/caring_form/field/tel.rb +37 -0
  43. data/lib/caring_form/field/text.rb +13 -0
  44. data/lib/caring_form/field/us_zip_code.rb +37 -0
  45. data/lib/caring_form/field_container.rb +126 -0
  46. data/lib/caring_form/form_builder.rb +51 -0
  47. data/lib/caring_form/model.rb +58 -0
  48. data/lib/caring_form/model/active_model.rb +11 -0
  49. data/lib/caring_form/model/active_record.rb +38 -0
  50. data/lib/caring_form/simple_form/initializer.rb +26 -0
  51. data/lib/caring_form/simple_form/monkey_patching.rb +51 -0
  52. data/lib/caring_form/tools/referee.rb +62 -0
  53. data/lib/caring_form/version.rb +3 -0
  54. data/lib/tasks/webshims.rake +7 -0
  55. data/spec/caring_form/action_view_extensions/flash_error_helper_spec.rb +50 -0
  56. data/spec/caring_form/action_view_extensions/form_helper_spec.rb +72 -0
  57. data/spec/caring_form/dsl_spec.rb +21 -0
  58. data/spec/caring_form/field/base_spec.rb +204 -0
  59. data/spec/caring_form/field/check_boxes_spec.rb +32 -0
  60. data/spec/caring_form/field/collection_spec.rb +28 -0
  61. data/spec/caring_form/field/radio_buttons_spec.rb +29 -0
  62. data/spec/caring_form/field/submit_spec.rb +57 -0
  63. data/spec/caring_form/field_container_spec.rb +85 -0
  64. data/spec/caring_form/form_builder_spec.rb +113 -0
  65. data/spec/caring_form/model_spec.rb +235 -0
  66. data/spec/caring_form/tools/referee_spec.rb +86 -0
  67. data/spec/spec_helper.rb +28 -0
  68. data/spec/support/nokogiri_matcher.rb +204 -0
  69. metadata +278 -0
@@ -0,0 +1,85 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ require 'caring_form'
4
+
5
+ describe CaringForm::FieldContainer do
6
+ let(:form_class) do
7
+ Class.new(CaringForm::Model) do
8
+ def self.model_name
9
+ ActiveModel::Name.new(self, nil, 'ZeForm')
10
+ end
11
+ end
12
+ end
13
+
14
+ context ".field_names" do
15
+ it "returns the list of field names" do
16
+ klass = Class.new(form_class) do
17
+ string :name
18
+ check_boxes :choices
19
+ end
20
+ klass.field_names.must_equal ['name', 'choices']
21
+ end
22
+
23
+ it "doesn't include :submit as submit being a field is just an implementation choice" do
24
+ klass = Class.new(form_class) do
25
+ string :name
26
+ submit
27
+ end
28
+ klass.field_names.must_equal ['name']
29
+ end
30
+
31
+ it "can filter the list of field names by passing a block" do
32
+ klass = Class.new(form_class) do
33
+ string :name
34
+ string :not_me, :ancillary => true
35
+ submit :primary => true
36
+ end
37
+ names = klass.field_names { |name, field| !field.ancillary }
38
+ names.must_equal ['name']
39
+ end
40
+ end
41
+
42
+ context "#field_names" do
43
+ it "delegates #field_names to its class" do
44
+ klass = Class.new(form_class) do
45
+ string :name
46
+ check_boxes :choices
47
+ end
48
+ form = klass.new
49
+ form.field_names.must_equal ['name', 'choices']
50
+ end
51
+ end
52
+
53
+ context "#field_id" do
54
+ it "returns the field id specified" do
55
+ klass = Class.new(form_class) do
56
+ string :with_id, :input_html => {:id => "defined"}
57
+ end
58
+ form = klass.new
59
+ form.field_id(:with_id).must_equal "defined"
60
+ end
61
+
62
+ it "returns the default id if none was specified" do
63
+ klass = Class.new(form_class) do
64
+ string :default
65
+ end
66
+ form = klass.new
67
+ form.field_id(:default).must_equal "ze_form_default"
68
+ end
69
+
70
+ it "returns the default id if none was specified (using form index)" do
71
+ klass = Class.new(form_class) do
72
+ index_on :foo
73
+ def foo; 'bar'; end
74
+ string :default
75
+ end
76
+ form = klass.new
77
+ form.field_id(:default).must_equal "ze_form_bar_default"
78
+ end
79
+
80
+ it "returns nil if the field doesn't exist" do
81
+ form = form_class.new
82
+ form.field_id(:doesnt_exist).must_equal nil
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,113 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ require 'caring_form'
4
+
5
+ class BuilderTestForm < CaringForm::Model
6
+ id 'my-id'
7
+ classes 'my-class your-class'
8
+ url '/my/pretty/url'
9
+
10
+ protect_from_bots
11
+
12
+ metadata :form_name
13
+ string :age, :hint => "Enter your age"
14
+ submit :label => "Continue"
15
+ end
16
+
17
+ MiniTest::Spec.register_spec_type(/CaringForm::FormBuilder$/, ControllerSpec)
18
+
19
+ describe CaringForm::FormBuilder do
20
+ include SimpleForm::ActionViewExtensions::FormHelper
21
+ include CaringForm::ActionViewExtensions::FormHelper
22
+
23
+ let(:ze_form) { BuilderTestForm.new }
24
+
25
+ context "rendering the form tag" do
26
+ it "renders a form with appropriate HTML classes" do
27
+ html_matcher(
28
+ 'form.caring-form.form-text.simple_form'
29
+ ).must_be :matching, caring_form_for(ze_form)
30
+ end
31
+
32
+ it "sets the form tag action to the form url" do
33
+ html_matcher(
34
+ 'form[action="/my/pretty/url"]'
35
+ ).must_be :matching, caring_form_for(ze_form)
36
+ end
37
+
38
+ it "sets the form tag id to the form id" do
39
+ html_matcher('form#my-id').must_be :matching, caring_form_for(ze_form)
40
+ end
41
+
42
+ it "sets the form tag class to the form classes" do
43
+ html_matcher(
44
+ 'form.my-class.your-class'
45
+ ).must_be :matching, caring_form_for(ze_form)
46
+ end
47
+ end
48
+
49
+ context "metadata: render metadata and dummy fields" do
50
+ it "renders metadata fields" do
51
+ view = caring_form_for(ze_form) do |f|
52
+ f.metadata +
53
+ f.fields do
54
+ f.field :age
55
+ end
56
+ end
57
+ html_matcher(
58
+ 'input', :with => {:name => 'builder_test_form[form_name]', :type => 'hidden'}
59
+ ).must_be :matching, view
60
+ end
61
+
62
+ it "renders dummy fields" do
63
+ view = caring_form_for(ze_form) do |f|
64
+ f.metadata
65
+ end
66
+ html_matcher(
67
+ 'div.form_add > input', :with => {:name => 'reference', :type => 'text'}
68
+ ).must_be :matching, view
69
+ end
70
+ end
71
+
72
+ context "fields: render a field wrapper" do
73
+ it "renders a ol tag wrapper by default" do
74
+ view = caring_form_for(ze_form) do |f|
75
+ f.fields do
76
+ f.field :age
77
+ end
78
+ end
79
+ html_matcher(
80
+ 'ol > li.field > input', :with => {:name => 'builder_test_form[age]', :type => 'text'}
81
+ ).must_be :matching, view
82
+ end
83
+
84
+ it "can render a customized wrapper tag through options" do
85
+ view = caring_form_for(ze_form) do |f|
86
+ f.fields :tag => :ul, :class => 'my-class', :id => 'blah' do
87
+ f.field :age
88
+ end
89
+ end
90
+ html_matcher(
91
+ 'ul#blah.my-class > li > input[name="builder_test_form[age]"]'
92
+ ).must_be :matching, view
93
+ end
94
+
95
+ it "can render all fields with :all" do
96
+ view = caring_form_for(ze_form) do |f|
97
+ f.field :all
98
+ end
99
+ html_matcher(
100
+ 'input', :with => {:name => 'builder_test_form[form_name]', :type => 'hidden'}
101
+ ).must_be :matching, view
102
+ html_matcher(
103
+ 'input', :with => {:name => 'reference', :type => 'text'}
104
+ ).must_be :matching, view
105
+ html_matcher(
106
+ 'input', :with => {:name => 'builder_test_form[age]', :type => 'text'}
107
+ ).must_be :matching, view
108
+ html_matcher(
109
+ 'input', :with => {:name => 'commit', :type => 'submit'}
110
+ ).must_be :matching, view
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,235 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'active_support/hash_with_indifferent_access'
3
+
4
+ require 'caring_form'
5
+
6
+ describe CaringForm::Model do
7
+ let(:form_class) do
8
+ Class.new(CaringForm::Model) do
9
+ def self.model_name
10
+ ActiveModel::Name.new(self, nil, 'ZeForm')
11
+ end
12
+ end
13
+ end
14
+
15
+ context ".field: common declarations" do
16
+ it "can declare fields" do
17
+ form_class.instance_eval { string :user_name }
18
+ form = form_class.new(:user_name => "John Zorn")
19
+ form.user_name.must_equal "John Zorn"
20
+ end
21
+
22
+ it "defaults to required fields" do
23
+ form_class.instance_eval { string :user_name }
24
+ form = form_class.new
25
+ form.wont_be :valid?
26
+ form.errors[:user_name].must_equal ["Enter the user name"]
27
+ end
28
+
29
+ it "can declare an optional field" do
30
+ form_class.instance_eval { string :user_lang, :optional => true }
31
+ form = form_class.new
32
+ form.must_be :valid?
33
+ end
34
+
35
+ it "can declare an optional field that uses a rule" do
36
+ form_class.instance_eval do
37
+ string :confirmation, :optional => {:absent => :require_confirmation}
38
+ string :require_confirmation, :optional => true
39
+ end
40
+ form = form_class.new
41
+ form.must_be :valid?
42
+
43
+ form.require_confirmation = 'yes'
44
+ form.wont_be :valid?
45
+
46
+ form.confirmation = 'confirmed'
47
+ form.must_be :valid?
48
+ end
49
+
50
+ it "can declare if the field should be handled for a form instance" do
51
+ form_class.instance_eval do
52
+ metadata :relevant, :optional => true
53
+ string :maybe, :if => :maybe_is_relevant
54
+ end
55
+ form_class.send(:define_method, :maybe_is_relevant) { relevant }
56
+
57
+ form = form_class.new(:relevant => false)
58
+ form.must_be :valid?
59
+ form.relevant = true
60
+ form.wont_be :valid?
61
+ end
62
+ end
63
+
64
+ context ".field: different field types" do
65
+ it "can declare a collection field" do
66
+ form_class.instance_eval { collection :gender, :collection => %w(male female) }
67
+ form = form_class.new(:gender => "female")
68
+ form.must_be :valid?
69
+ end
70
+
71
+ it "can declare a collection field represented as check boxes" do
72
+ form_class.instance_eval { check_boxes :skills }
73
+ form = form_class.new(:skills => ['code', 'design'])
74
+ form.must_be :valid?
75
+ end
76
+
77
+ it "can declare a single value field represented as a check box" do
78
+ form_class.instance_eval do
79
+ check_box :chat_source,
80
+ :optional => true,
81
+ :value => {:checked => "true", :label => "By chat"}
82
+ end
83
+ form = form_class.new(:chat_source => ['', 'true'])
84
+ form.must_be :valid?
85
+ form.chat_source.must_equal "true"
86
+ form = form_class.new(:chat_source => [''])
87
+ form.must_be :valid?
88
+ form.chat_source.must_equal ""
89
+ end
90
+
91
+ it "can declare a dummy field" do
92
+ form_class.instance_eval { dummy :reference }
93
+ form = form_class.new(:reference => "I am a spammer")
94
+ form.must_be :valid?
95
+ end
96
+
97
+ it "can declare an email field" do
98
+ form_class.instance_eval { email :email_address }
99
+ form = form_class.new(:email_address => "invalid@address")
100
+ form.wont_be :valid?
101
+ form.errors[:email_address].must_equal ["Enter a valid email address"]
102
+ form.email_address = "valid@email.com"
103
+ form.must_be :valid?
104
+ end
105
+
106
+ it "can declare a full name field" do
107
+ form_class.instance_eval { full_name :name }
108
+ form = form_class.new(:name => "M")
109
+ form.wont_be :valid?
110
+ form.errors[:name].must_equal ["Enter your first and last name"]
111
+ form.name = "John Zorn"
112
+ form.must_be :valid?
113
+ end
114
+
115
+ it "can declare a looking for field" do
116
+ form_class.instance_eval { looking_for :inquiry_for }
117
+ form = form_class.new(:inquiry_for => "Unsupported Value")
118
+ form.wont_be :valid?
119
+ form.errors[:inquiry_for].must_equal ["Tell us for whom you are seeking care"]
120
+ form.inquiry_for = "Parent(s)"
121
+ form.must_be :valid?
122
+ end
123
+
124
+ it "can declare a metadata field" do
125
+ form_class.instance_eval { metadata :form_name }
126
+ form = form_class.new(:form_name => "right_rail")
127
+ form.must_be :valid?
128
+ end
129
+
130
+ it "can declare a collection field represented as caring radio buttons" do
131
+ form_class.instance_eval { radio_buttons :choice }
132
+ form = form_class.new(:choice => 'good')
133
+ form.must_be :valid?
134
+ end
135
+
136
+ it "can declare a string field" do
137
+ form_class.instance_eval { string :user_name }
138
+ form = form_class.new(:user_name => "John Zorn")
139
+ form.must_be :valid?
140
+ end
141
+
142
+ it "can declare a submit field" do
143
+ proc {
144
+ form_class.instance_eval { submit }
145
+ form = form_class.new
146
+ }.must_be_silent
147
+ end
148
+
149
+ it "can declare a tel field" do
150
+ form_class.instance_eval { tel :number }
151
+ form = form_class.new(:number => "123")
152
+ form.wont_be :valid?
153
+ form.errors[:number].must_equal ["Enter your phone number with area code like this: XXX-XXX-XXXX"]
154
+ form.number = "650-622-9294"
155
+ form.must_be :valid?
156
+ end
157
+
158
+ it "can declare a US zip code field" do
159
+ form_class.instance_eval { us_zip_code :zip }
160
+ form = form_class.new(:zip => "3")
161
+ form.wont_be :valid?
162
+ form.errors[:zip].must_equal ["Enter a valid U.S. zip code"]
163
+ form.zip = "94002"
164
+ form.must_be :valid?
165
+ end
166
+
167
+ it "can declare a text field" do
168
+ form_class.instance_eval { text :notes }
169
+ form = form_class.new(:notes => "blah blah")
170
+ form.must_be :valid?
171
+ end
172
+ end
173
+
174
+ context ".protect_from_bots: help with protecting from spammers" do
175
+ it "declares a dummy field to trap spam bots" do
176
+ form_class.instance_eval { protect_from_bots }
177
+ form = form_class.new
178
+ form.must_be :valid?
179
+ end
180
+
181
+ it "tells if a spam bot got trapped with #spam?" do
182
+ form_class.instance_eval { protect_from_bots }
183
+ form = form_class.new(:reference => "caught...")
184
+ form.must_be :spam?
185
+ form.reference = ""
186
+ form.wont_be :spam?
187
+ end
188
+ end
189
+
190
+ context ".index_on: scope to a field value" do
191
+ before(:each) do
192
+ form_class.instance_eval do
193
+ index_on :location
194
+ string :location, :ancillary => true
195
+ string :band
196
+ end
197
+ end
198
+
199
+ it "loads attributes using the index defined" do
200
+ form = form_class.new(HashWithIndifferentAccess.new(
201
+ "location" => "right",
202
+ "ze_form" => {
203
+ "right" => {
204
+ "band" => "Nightmares on Wax"
205
+ }
206
+ }
207
+ ))
208
+ form.location.must_equal "right"
209
+ form.band.must_equal "Nightmares on Wax"
210
+ end
211
+
212
+ it "only loads ancillary attributes if no index value" do
213
+ form = form_class.new(HashWithIndifferentAccess.new(
214
+ "ze_form" => {
215
+ "right" => {
216
+ "band" => "Nightmares on Wax"
217
+ }
218
+ }
219
+ ))
220
+ form.band.must_equal nil
221
+ end
222
+
223
+ it "only loads ancillary attributes if nothing at the index value" do
224
+ form = form_class.new(HashWithIndifferentAccess.new(
225
+ "location" => "right",
226
+ "ze_form" => {
227
+ "left" => {
228
+ "band" => "Nightmares on Wax"
229
+ }
230
+ }
231
+ ))
232
+ form.band.must_equal nil
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,86 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require 'ostruct'
3
+
4
+ require 'caring_form/tools/referee'
5
+
6
+ describe CaringForm::Tools::Referee do
7
+ let(:field) { OpenStruct.new }
8
+ let(:referee) { CaringForm::Tools::Referee.new(field) }
9
+
10
+ context "#is_rule?" do
11
+ it "returns true if the delegate property is a rule" do
12
+ field.optional = {:absent => :optional_if_empty}
13
+ referee.is_rule?(:optional).must_equal true
14
+ end
15
+
16
+ it "returns false if the delegate property isn't a rule" do
17
+ field.optional = true
18
+ referee.is_rule?(:optional).must_equal false
19
+ referee.is_rule?(:doesnt_exist).must_equal false
20
+ end
21
+ end
22
+
23
+ context "#decide" do
24
+ let(:form) { OpenStruct.new }
25
+
26
+ it "returns true if the delegate property is true" do
27
+ field.optional = true
28
+ referee.decide(:optional, form).must_equal true
29
+ end
30
+
31
+ it "returns false if the delegate property is false" do
32
+ field.optional = false
33
+ referee.decide(:optional, form).must_equal false
34
+ end
35
+
36
+ it "checks if the target is absent if the delegate property an :absent rule " do
37
+ field.optional = {:absent => :optional_if_empty}
38
+
39
+ form.optional_if_empty = ''
40
+ referee.decide(:optional, form).must_equal true
41
+
42
+ form.optional_if_empty = 'present'
43
+ referee.decide(:optional, form).must_equal false
44
+ end
45
+
46
+ it "checks if the target is present if the delegate property an :present rule " do
47
+ field.mandatory = {:present => :is_required}
48
+
49
+ form.is_required = 'yes'
50
+ referee.decide(:mandatory, form).must_equal true
51
+
52
+ form.is_required = ''
53
+ referee.decide(:mandatory, form).must_equal false
54
+ end
55
+
56
+ it "defaults to false if the rule is invalid" do
57
+ def form.doesnt_exist
58
+ raise "I shouldn't be called!"
59
+ end
60
+ field.nonsense = {:blah => :doesnt_exist}
61
+ referee.decide(:nonsense, form).must_equal false
62
+ end
63
+ end
64
+
65
+ context "#rule_as_data_attribute" do
66
+ let(:form) { MiniTest::Mock.new }
67
+
68
+ it "returns an attribute key/value pair for a :present rule" do
69
+ field.mandatory = {:present => :is_required}
70
+ form.expect(:field_id, 'mandatory_id', [:is_required])
71
+ attribute = referee.rule_as_data_attribute(:mandatory, form)
72
+ attribute.must_equal(:"data-mandatory-if-present" => '#mandatory_id')
73
+ end
74
+
75
+ it "returns an attribute key/value pair for an :absent rule" do
76
+ field.optional = {:absent => :optional_if_empty}
77
+ form.expect(:field_id, 'optional_id', [:optional_if_empty])
78
+ attribute = referee.rule_as_data_attribute(:optional, form)
79
+ attribute.must_equal(:"data-optional-if-absent" => '#optional_id')
80
+ end
81
+
82
+ it "returns an empty array if the property value is not a rule" do
83
+ referee.rule_as_data_attribute(:doesnt_exist, form).must_equal({})
84
+ end
85
+ end
86
+ end