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,72 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'caring_form/action_view_extensions/form_helper'
4
+
5
+ unless defined?(CaringForm::FormBuilder)
6
+ module CaringForm
7
+ FormBuilder = Class.new
8
+ end
9
+ end
10
+
11
+ class FormHelperTester < SimpleDelegator
12
+ include CaringForm::ActionViewExtensions::FormHelper
13
+ def mock
14
+ __getobj__
15
+ end
16
+ end
17
+
18
+ describe CaringForm::ActionViewExtensions::FormHelper do
19
+ let(:helper_mock) { MiniTest::Mock.new }
20
+ let(:helper) { FormHelperTester.new(helper_mock) }
21
+ let(:form) { MiniTest::Mock.new }
22
+
23
+ context "#caring_form_for" do
24
+ before(:each) do
25
+ [:classes, :id].each { |meth| form.expect(meth, nil) }
26
+ end
27
+
28
+ it "instantiates a caring form builder and delegates to #form_for" do
29
+ def helper_mock.form_for(form, options = {})
30
+ form.form_object?.must_equal true
31
+ options[:builder].must_equal CaringForm::FormBuilder
32
+ "html content"
33
+ end
34
+ form.expect(:form_object?, true)
35
+ helper.caring_form_for(form, :url => '/').must_equal "html content"
36
+ end
37
+
38
+ it "adds classes to the form tag" do
39
+ def helper_mock.form_for(form, options = {})
40
+ ['caring-form', 'form-text', 'simple_form'].each do |name|
41
+ options[:html][:class].must_include name
42
+ end
43
+ end
44
+ options = {:url => '/'}
45
+ helper.caring_form_for(form, options)
46
+ end
47
+
48
+ it "allows to add more classes to the form tag" do
49
+ def helper_mock.form_for(form, options = {})
50
+ options[:html][:class].must_include 'my-class'
51
+ options[:html][:class].must_include 'my-other-class'
52
+ end
53
+ options = {:url => '/', :html => {:class => "my-class my-other-class"}}
54
+ helper.caring_form_for(form, options)
55
+ end
56
+
57
+ it "allows to set a custom URL" do
58
+ def helper_mock.form_for(form, options = {})
59
+ options[:url].must_equal '/my/custom/url'
60
+ end
61
+ helper.caring_form_for(form, :url => '/my/custom/url')
62
+ end
63
+
64
+ it "asks the form object for its URL if not specified" do
65
+ def helper_mock.form_for(form, options = {})
66
+ options[:url].must_equal '/form/url'
67
+ end
68
+ 2.times { form.expect(:url, '/form/url') }
69
+ helper.caring_form_for(form)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ require 'caring_form'
4
+
5
+ describe CaringForm::Dsl 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 "#attributes" do
15
+ it "returns a hash of non nil attribute values" do
16
+ form_class.instance_eval { string :first; string :last }
17
+ form = form_class.new(:first => "John")
18
+ form.attributes.must_equal(:first => "John")
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,204 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'caring_form'
4
+
5
+ class TestFormBase < CaringForm::Model
6
+ def self.model_name
7
+ ActiveModel::Name.new(self, nil, 'ZeForm')
8
+ end
9
+
10
+ def self.inherited(klass)
11
+ super
12
+ klass.url '/'
13
+ end
14
+ end
15
+
16
+ MiniTest::Spec.register_spec_type(/CaringForm::Field::Base$/, ControllerSpec)
17
+
18
+ describe CaringForm::Field::Base do
19
+ include SimpleForm::ActionViewExtensions::FormHelper
20
+ include CaringForm::ActionViewExtensions::FormHelper
21
+
22
+ context "basics" do
23
+ def build_form(*args)
24
+ klass = Class.new(TestFormBase) do
25
+ string :age, :hint => "Enter your age"
26
+ string :name
27
+ end
28
+ klass.new(*args)
29
+ end
30
+
31
+ it "renders a field as a label, an input field and a hint" do
32
+ view = caring_form_for(build_form(:age => 41)) do |f|
33
+ f.field :age
34
+ end
35
+ html_matcher('li.field').must_be :matching, view
36
+ html_matcher(
37
+ 'li > label[for="ze_form_age"]', :text => "Age"
38
+ ).must_be :matching, view
39
+ html_matcher(
40
+ 'li > input', :with => {:name => 'ze_form[age]', :value => "41"}
41
+ ).must_be :matching, view
42
+ html_matcher(
43
+ 'li > input + p.hint', :text => "Enter your age"
44
+ ).must_be :matching, view
45
+ end
46
+
47
+ it "can overide the label" do
48
+ view = caring_form_for(build_form) do |f|
49
+ f.field :age, :label => "Dude, your age"
50
+ end
51
+ html_matcher(
52
+ 'li > label[for="ze_form_age"]', :text => "Dude, your age"
53
+ ).must_be :matching, view
54
+ end
55
+
56
+ it "marks the field wrapper state as error if the field has errors" do
57
+ form = build_form(:age => "42")
58
+ form.valid?
59
+ view = caring_form_for(form) { |f| f.field :age, :name }
60
+ html_matcher(
61
+ 'li.field.field-with-errors > input#ze_form_age'
62
+ ).wont_be :matching, view
63
+ html_matcher(
64
+ 'li.field.field-with-errors > input#ze_form_name'
65
+ ).must_be :matching, view
66
+ end
67
+ end
68
+
69
+ context "conditionals" do
70
+ it "can specify a label as a symbol so it is rendered dynamically" do
71
+ klass = Class.new(TestFormBase) do
72
+ text :notes, :label => :dynamic
73
+ attr_accessor :custom_label
74
+ def dynamic
75
+ custom_label || 'Default Label'
76
+ end
77
+ end
78
+ form = klass.new
79
+ view = caring_form_for(form) do |f|
80
+ f.field :notes
81
+ end
82
+ html_matcher(
83
+ 'li > label[for="ze_form_notes"]', :text => "Default Label"
84
+ ).must_be :matching, view
85
+
86
+ form.custom_label = "Changed"
87
+ view = caring_form_for(form) do |f|
88
+ f.field :notes
89
+ end
90
+ html_matcher(
91
+ 'li > label[for="ze_form_notes"]', :text => "Changed"
92
+ ).must_be :matching, view
93
+ end
94
+
95
+ it "can specify a collection as a symbol so it is queried dynamically" do
96
+ klass = Class.new(TestFormBase) do
97
+ collection :answer, :collection => :alternatives, :optional => true
98
+ def alternatives
99
+ %w(maybe likely)
100
+ end
101
+ end
102
+ form = klass.new
103
+ view = caring_form_for(form) do |f|
104
+ f.field :answer
105
+ end
106
+ html_matcher(
107
+ 'li > select > option[value="maybe"]', :text => "maybe"
108
+ ).must_be :matching, view
109
+ html_matcher(
110
+ 'li > select > option[value="likely"]', :text => "likely"
111
+ ).must_be :matching, view
112
+ end
113
+
114
+ it "can generate a field or not using the :if option" do
115
+ klass = Class.new(TestFormBase) do
116
+ us_zip_code :zip_code, :if => :zip_code_is_relevant?
117
+ def zip_code_is_relevant?
118
+ zip_code != 'nope'
119
+ end
120
+ end
121
+ form = klass.new
122
+ view = caring_form_for(form) do |f|
123
+ f.field :zip_code
124
+ end
125
+ html_matcher('li.field > label[for="ze_form_zip_code"]').must_be :matching, view
126
+ html_matcher('input#ze_form_zip_code').must_be :matching, view
127
+
128
+ form.zip_code = 'nope'
129
+ view = caring_form_for(form) do |f|
130
+ f.field :zip_code
131
+ end
132
+ html_matcher('li.field > label[for="ze_form_zip_code"]').wont_be :matching, view
133
+ html_matcher('input#ze_form_zip_code').wont_be :matching, view
134
+ end
135
+ end
136
+
137
+ context "optional fields" do
138
+ it "renders an (optional) label when the field is optional" do
139
+ klass = Class.new(TestFormBase) do
140
+ collection :gender, :collection => %w(female male), :optional => true
141
+ end
142
+ form = klass.new
143
+ view = caring_form_for(form) do |f|
144
+ f.field :gender
145
+ end
146
+ html_matcher(
147
+ 'li > label[for="ze_form_gender"] > span.opt', :text => '(optional)'
148
+ ).must_be :matching, view
149
+ html_matcher(
150
+ 'li > label[for="ze_form_gender"]', :text => 'Gender (optional)'
151
+ ).must_be :matching, view
152
+ end
153
+
154
+ it "doesn't render an (optional) label when the field is required and uses :if" do
155
+ klass = Class.new(TestFormBase) do
156
+ us_zip_code :zip_code, label: "Your Zip", if: :use_zip_code?
157
+ def use_zip_code?
158
+ true
159
+ end
160
+ end
161
+ form = klass.new
162
+ view = caring_form_for(form) do |f|
163
+ f.field :zip_code
164
+ end
165
+ html_matcher(
166
+ 'li > label[for="ze_form_zip_code"]', :text => 'Your Zip'
167
+ ).must_be :matching, view
168
+ html_matcher(
169
+ 'li > label[for="ze_form_zip_code"] > span.opt', :text => '(optional)'
170
+ ).wont_be :matching, view
171
+ end
172
+
173
+ it "can conditionally mark a field as optional using a condition hash" do
174
+ klass = Class.new(TestFormBase) do
175
+ string :date, :input_html => {:id => 'ze_date'}
176
+ string :time, :optional => {:absent => :date}
177
+ end
178
+ form = klass.new(:value => false)
179
+ view = caring_form_for(form) do |f|
180
+ f.field :time
181
+ end
182
+ html_matcher(
183
+ 'input#ze_form_time[data-optional-if-absent="#ze_date"]'
184
+ ).must_be :matching, view
185
+ end
186
+
187
+ it "can have 2 fields with interdependent optional rule conditions" do
188
+ klass = Class.new(TestFormBase) do
189
+ string :date, :input_html => {:id => 'ze_date'}, :optional => {:absent => :time}
190
+ string :time, :input_html => {:id => 'ze_time'}, :optional => {:absent => :date}
191
+ end
192
+ form = klass.new(:value => false)
193
+ view = caring_form_for(form) do |f|
194
+ f.field(:date) + f.field(:time)
195
+ end
196
+ html_matcher(
197
+ 'input#ze_time[data-optional-if-absent="#ze_date"]'
198
+ ).must_be :matching, view
199
+ html_matcher(
200
+ 'input#ze_date[data-optional-if-absent="#ze_time"]'
201
+ ).must_be :matching, view
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'caring_form'
4
+
5
+ class CheckBoxesTestForm < CaringForm::Model
6
+ url '/'
7
+ check_boxes :languages, :collection => %w(English Italian French)
8
+ end
9
+
10
+ MiniTest::Spec.register_spec_type(/CaringForm::Field::CheckBoxes$/, ControllerSpec)
11
+
12
+ describe CaringForm::Field::CheckBoxes do
13
+ include SimpleForm::ActionViewExtensions::FormHelper
14
+ include CaringForm::ActionViewExtensions::FormHelper
15
+
16
+ it "renders a collection as check boxes" do
17
+ form = CheckBoxesTestForm.new(:languages => ['English', 'French'])
18
+ view = caring_form_for(form) do |f|
19
+ f.field :languages
20
+ end
21
+ html_matcher(
22
+ 'input[value="English"][checked]', :with => {:name => 'check_boxes_test_form[languages][]', :type => 'checkbox'}
23
+ ).must_be :matching, view
24
+ html_matcher(
25
+ 'input[value="Italian"]', :with => {:name => 'check_boxes_test_form[languages][]', :type => 'checkbox'}
26
+ ).must_be :matching, view
27
+ html_matcher('input[value="Italian"][checked]').wont_be :matching, view
28
+ html_matcher(
29
+ 'input[value="French"][checked]', :with => {:name => 'check_boxes_test_form[languages][]', :type => 'checkbox'}
30
+ ).must_be :matching, view
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'caring_form'
4
+
5
+ class CollectionTestForm < CaringForm::Model
6
+ url '/'
7
+ collection :answer, :collection => :alternatives, :optional => true
8
+ end
9
+
10
+ MiniTest::Spec.register_spec_type(/CaringForm::Field::Collection$/, ControllerSpec)
11
+
12
+ describe CaringForm::Field::Collection do
13
+ include SimpleForm::ActionViewExtensions::FormHelper
14
+ include CaringForm::ActionViewExtensions::FormHelper
15
+
16
+ it "renders a collection as a drop-down box" do
17
+ form = CollectionTestForm.new(:answer => 'non')
18
+ view = caring_form_for(form) do |f|
19
+ f.field :answer, :collection => %w(oui non)
20
+ end
21
+ html_matcher(
22
+ 'li > select > option[value="oui"]', :text => "oui"
23
+ ).must_be :matching, view
24
+ html_matcher(
25
+ 'li > select > option[value="non"][selected]', :text => "non"
26
+ ).must_be :matching, view
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'caring_form'
4
+
5
+ class RadioButtonsTestForm < CaringForm::Model
6
+ url '/'
7
+ radio_buttons :choice, :collection => %w(Yes No)
8
+ end
9
+
10
+ MiniTest::Spec.register_spec_type(/CaringForm::Field::RadioButtons$/, ControllerSpec)
11
+
12
+ describe CaringForm::Field::RadioButtons do
13
+ include SimpleForm::ActionViewExtensions::FormHelper
14
+ include CaringForm::ActionViewExtensions::FormHelper
15
+
16
+ it "renders a collection as caring radio buttons" do
17
+ form = RadioButtonsTestForm.new(:choice => 'No')
18
+ view = caring_form_for(form) do |f|
19
+ f.field :choice
20
+ end
21
+ html_matcher(
22
+ 'input[value="Yes"]', :with => {:name => 'radio_buttons_test_form[choice]', :type => 'radio'}
23
+ ).must_be :matching, view
24
+ html_matcher('input[value="Yes"][checked]').wont_be :matching, view
25
+ html_matcher(
26
+ 'input[value="No"][checked]', :with => {:name => 'radio_buttons_test_form[choice]', :type => 'radio'}
27
+ ).must_be :matching, view
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'caring_form'
4
+
5
+ MiniTest::Spec.register_spec_type(/CaringForm::Field::Submit$/, ControllerSpec)
6
+
7
+ describe CaringForm::Field::Submit do
8
+ include SimpleForm::ActionViewExtensions::FormHelper
9
+ include CaringForm::ActionViewExtensions::FormHelper
10
+
11
+ let(:form_class) do
12
+ Class.new(CaringForm::Model) do
13
+ def self.model_name
14
+ ActiveModel::Name.new(self, nil, 'ZeForm')
15
+ end
16
+ url '/'
17
+ end
18
+ end
19
+
20
+ it "renders an input of type submit by default" do
21
+ form_class.instance_eval { submit }
22
+ view = caring_form_for(form_class.new) { |f| f.field :submit }
23
+ html_matcher('li input[type="submit"]').must_be :matching, view
24
+ end
25
+
26
+ it "can wrap the submit into a different tag with :wrapper_tag" do
27
+ form_class.instance_eval { submit :wrapper_tag => :p }
28
+ view = caring_form_for(form_class.new) { |f| f.field :submit }
29
+ html_matcher('p input[type="submit"]').must_be :matching, view
30
+ end
31
+
32
+ it "can use a different type of submit with :input_tag option" do
33
+ form_class.instance_eval { submit :input_tag => :button }
34
+ view = caring_form_for(form_class.new) { |f| f.field :submit }
35
+ html_matcher('li button[type="submit"]').must_be :matching, view
36
+ end
37
+
38
+ it "adds the default 'input-width' and 'trackable' classes for an input" do
39
+ form_class.instance_eval { submit }
40
+ view = caring_form_for(form_class.new) { |f| f.field :submit }
41
+ html_matcher('input.input-width.trackable[type="submit"]').must_be :matching, view
42
+ end
43
+
44
+ it "can add a single property to the button" do
45
+ form_class.instance_eval { submit :marketing, :input_tag => :button }
46
+ view = caring_form_for(form_class.new) { |f| f.field :submit }
47
+ html_matcher('button.marketing-button[type="submit"]').must_be :matching, view
48
+ end
49
+
50
+ it "can add multiple properties to the button" do
51
+ form_class.instance_eval { submit [:secondary, :add, :huge] }
52
+ view = caring_form_for(form_class.new) { |f| f.field :submit }
53
+ html_matcher(
54
+ 'input.secondary-button.add-button.huge-button[type="submit"]'
55
+ ).must_be :matching, view
56
+ end
57
+ end