dynamic_fieldsets 0.0.3 → 0.0.4

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 (89) hide show
  1. data/CHANGELOG +7 -0
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +1 -0
  4. data/README.rdoc +15 -2
  5. data/Rakefile +24 -4
  6. data/VERSION +1 -1
  7. data/app/controllers/dynamic_fieldsets/fields_controller.rb +25 -0
  8. data/app/controllers/dynamic_fieldsets/fieldset_children_controller.rb +57 -0
  9. data/app/controllers/dynamic_fieldsets/fieldsets_controller.rb +91 -5
  10. data/app/helpers/dynamic_fieldsets/fields_helper.rb +1 -15
  11. data/app/helpers/dynamic_fieldsets/fieldset_children_helper.rb +5 -0
  12. data/app/helpers/dynamic_fieldsets/nested_model_helper.rb +18 -0
  13. data/app/helpers/dynamic_fieldsets_helper.rb +70 -44
  14. data/app/models/dynamic_fieldsets/dependency.rb +76 -0
  15. data/app/models/dynamic_fieldsets/dependency_clause.rb +32 -0
  16. data/app/models/dynamic_fieldsets/dependency_group.rb +108 -0
  17. data/app/models/dynamic_fieldsets/field.rb +17 -12
  18. data/app/models/dynamic_fieldsets/field_default.rb +27 -1
  19. data/app/models/dynamic_fieldsets/field_record.rb +19 -4
  20. data/app/models/dynamic_fieldsets/fieldset.rb +35 -30
  21. data/app/models/dynamic_fieldsets/fieldset_associator.rb +52 -8
  22. data/app/models/dynamic_fieldsets/fieldset_child.rb +148 -0
  23. data/app/views/dynamic_fieldsets/fields/_disable_field_form.html.erb +4 -0
  24. data/app/views/dynamic_fieldsets/fields/_field_default_fields.html.erb +1 -1
  25. data/app/views/dynamic_fieldsets/fields/_form.html.erb +9 -27
  26. data/app/views/dynamic_fieldsets/fields/index.html.erb +8 -5
  27. data/app/views/dynamic_fieldsets/fields/new.html.erb +5 -1
  28. data/app/views/dynamic_fieldsets/fields/show.html.erb +1 -13
  29. data/app/views/dynamic_fieldsets/fieldset_children/_dependency_clause_fields.html.erb +25 -0
  30. data/app/views/dynamic_fieldsets/fieldset_children/_dependency_fields.html.erb +12 -0
  31. data/app/views/dynamic_fieldsets/fieldset_children/_dependency_group_fields.html.erb +16 -0
  32. data/app/views/dynamic_fieldsets/fieldset_children/_form.html.erb +34 -0
  33. data/app/views/dynamic_fieldsets/fieldset_children/edit.html.erb +6 -0
  34. data/app/views/dynamic_fieldsets/fieldsets/_associate_child.html.erb +4 -0
  35. data/app/views/dynamic_fieldsets/fieldsets/_child.html.erb +41 -0
  36. data/app/views/dynamic_fieldsets/fieldsets/_form.html.erb +2 -8
  37. data/app/views/dynamic_fieldsets/fieldsets/children.html.erb +47 -39
  38. data/app/views/dynamic_fieldsets/fieldsets/index.html.erb +4 -4
  39. data/app/views/dynamic_fieldsets/fieldsets/new.html.erb +5 -1
  40. data/app/views/dynamic_fieldsets/fieldsets/reorder.html.erb +4 -0
  41. data/app/views/dynamic_fieldsets/fieldsets/show.html.erb +1 -12
  42. data/app/views/dynamic_fieldsets/shared/_javascript_watcher.html.erb +255 -0
  43. data/app/views/dynamic_fieldsets/shared/_nested_model_javascript.html.erb +35 -0
  44. data/config/.routes.rb.swp +0 -0
  45. data/config/routes.rb +11 -0
  46. data/dynamic_fieldsets.gemspec +41 -4
  47. data/lib/dynamic_fieldsets/dynamic_fieldsets_in_model.rb +141 -14
  48. data/lib/generators/dynamic_fieldsets/templates/migrations/install_migration.rb +39 -5
  49. data/spec/dummy/app/controllers/information_forms_controller.rb +2 -1
  50. data/spec/dummy/app/models/information_form.rb +1 -1
  51. data/spec/dummy/app/views/information_forms/dynamic_view.html.erb +18 -0
  52. data/spec/dummy/app/views/information_forms/show.html.erb +1 -0
  53. data/spec/dummy/app/views/layouts/application.html.erb +9 -1
  54. data/spec/dummy/config/routes.rb +1 -1
  55. data/spec/dummy/db/migrate/{20110726215814_create_dynamic_fieldsets_tables.rb → 20110809161724_create_dynamic_fieldsets_tables.rb} +39 -5
  56. data/spec/dummy/db/schema.rb +40 -11
  57. data/spec/dummy/features/field.feature +17 -2
  58. data/spec/dummy/features/fieldset.feature +1 -21
  59. data/spec/dummy/features/fieldset_children.feature +50 -0
  60. data/spec/dummy/features/javascript_tests.feature +91 -0
  61. data/spec/dummy/features/step_definitions/field_steps.rb +9 -5
  62. data/spec/dummy/features/step_definitions/fieldset_associator_steps.rb +3 -1
  63. data/spec/dummy/features/step_definitions/fieldset_children_steps.rb +65 -0
  64. data/spec/dummy/features/step_definitions/fieldset_steps.rb +0 -27
  65. data/spec/dummy/features/step_definitions/javascript_steps.rb +208 -0
  66. data/spec/dummy/features/step_definitions/web_steps.rb +5 -0
  67. data/spec/dummy/features/support/paths.rb +10 -1
  68. data/spec/dummy/features/support/selectors.rb +2 -0
  69. data/spec/dummy/public/javascripts/jquery-1.6.2.min.js +18 -0
  70. data/spec/dummy/public/javascripts/jquery-ui-1.8.15.custom.min.js +111 -0
  71. data/spec/dummy/public/javascripts/jquery-ui-nestedSortable.js +356 -0
  72. data/spec/dummy/public/stylesheets/scaffold.css +101 -0
  73. data/spec/dynamic_fieldsets_helper_spec.rb +236 -55
  74. data/spec/dynamic_fieldsets_in_model_spec.rb +122 -4
  75. data/spec/models/dependency_clause_spec.rb +55 -0
  76. data/spec/models/dependency_group_spec.rb +237 -0
  77. data/spec/models/dependency_spec.rb +173 -0
  78. data/spec/models/field_default_spec.rb +49 -0
  79. data/spec/models/field_record_spec.rb +11 -2
  80. data/spec/models/field_spec.rb +30 -6
  81. data/spec/models/fieldset_associator_spec.rb +138 -25
  82. data/spec/models/fieldset_child_spec.rb +122 -0
  83. data/spec/models/fieldset_spec.rb +78 -80
  84. data/spec/support/dependency_group_helper.rb +9 -0
  85. data/spec/support/dependency_helper.rb +13 -0
  86. data/spec/support/field_helper.rb +0 -2
  87. data/spec/support/fieldset_child_helper.rb +10 -0
  88. data/spec/support/fieldset_helper.rb +2 -18
  89. metadata +51 -5
@@ -0,0 +1,255 @@
1
+ <script type='text/javascript'>
2
+
3
+ // The only inputs we care about are visible... not hidden
4
+ var allInputs = $(":input:not(:hidden)");
5
+
6
+ /***************************************************************************
7
+
8
+ field-[group_field]-child-[group_fsc] - just want the last value from the
9
+ input's id. This function rips it.
10
+
11
+ input - called upon the input that just called the change()
12
+ returns [integer] - the group_fsc id from the end of the id
13
+
14
+ ***************************************************************************/
15
+
16
+ function grab_fsc_id(field, field_type) {
17
+ switch(field_type)
18
+ {
19
+ case 'text':
20
+ var id_array = field.attr('id').split('-');
21
+ return id_array[id_array.length-1];
22
+ case 'radio':
23
+ var id_array = field.closest("div").attr("id").split('-');
24
+ return id_array[id_array.length-1];
25
+ case 'textarea':
26
+ var id_array = field.attr('id').split('-');
27
+ return id_array[id_array.length-1];
28
+ case 'checkbox':
29
+ var id_array = field.closest("div").attr("id").split('-');
30
+ return id_array[id_array.length-1];
31
+ case 'select':
32
+ var id_array = field.attr('id').split('-');
33
+ return id_array[id_array.length-1];
34
+ case 'multi-select':
35
+ var id_array = field.attr('id').split('-');
36
+ return id_array[id_array.length-1];
37
+ default:
38
+ return ''
39
+ }
40
+ };
41
+
42
+ /***************************************************************************
43
+
44
+ evaluate() does the logical computing between two values with a given
45
+ relationship.
46
+
47
+ params [entered_value] - The current value of the input that called the
48
+ change()
49
+ params [relationship] - The relationship of the dependency that the
50
+ input that called the change() is tied to
51
+ params [value] - The value that we are testing against for the dependency
52
+ that the input that called the change() is tied to
53
+ returns [bool] - The boolean value by comparing the entered_value against
54
+ the value using the relationship.
55
+
56
+ **************************************************************************/
57
+
58
+ function evaluate(entered_value, relationship, value) {
59
+ switch(relationship)
60
+ {
61
+ /* CHANGE THIS if the model changes on which relationships
62
+ are allowed. */
63
+ case 'equals':
64
+ return entered_value == value;
65
+ case 'not equals':
66
+ return entered_value != value;
67
+ case 'includes':
68
+ return jQuery.inArray(value, entered_value) != -1;
69
+ case 'not includes':
70
+ return jQuery.inArray(value, entered_value) == -1;
71
+ case 'blank':
72
+ return entered_value == "";
73
+ case 'not blank':
74
+ return entered_value != "";
75
+ default:
76
+ return false;
77
+ }
78
+ };
79
+
80
+ /*************************************************************************
81
+
82
+ group_action() responds to the results of evaluate() to enact the
83
+ appropriate action upon the group_field of the dependency_group.
84
+
85
+ params [success_flag] - Either set to succeed or failure to indicate
86
+ the results of evaluate()
87
+ params [action] - Either set to show or enable.
88
+ params [group_field] - The field tied to the dependency_group that is
89
+ to experience the action based off of the success of evaluate()
90
+ returns - changes the group_field
91
+
92
+ *************************************************************************/
93
+
94
+ function group_action(success_flag, action, group_field) {
95
+
96
+ /* CHANGE THIS if the model change on which actions are allowed */
97
+
98
+ if (success_flag == true){
99
+ switch(action)
100
+ {
101
+ case 'show':
102
+ $('#input-' + group_field).show();
103
+ case 'enable':
104
+ $('#' + group_field + ' :input').removeAttr('disabled');
105
+ }
106
+ } else {
107
+ switch(action)
108
+ {
109
+ case 'show':
110
+ $('#input-' + group_field).hide();
111
+ case 'enable':
112
+ $('#' + group_field + ' :input').attr('disabled', true);
113
+ }
114
+ }
115
+ };
116
+
117
+ function grab_value(field, field_type) {
118
+ switch(field_type)
119
+ {
120
+ case 'text':
121
+ return field.attr('value').toLowerCase();
122
+ case 'radio':
123
+ if (field.val() == $(':checked').val())
124
+ {
125
+ field_val = field.attr('id').split('-').slice(2);
126
+ return field_val.join(" ").toLowerCase();
127
+ }
128
+ case 'textarea':
129
+ return field.val();
130
+ case 'checkbox':
131
+ var value_ray = [];
132
+ var field_ray = [];
133
+ var sibling_ray = field.closest('div').children().children();
134
+ var checked_ray = $('input:checked');
135
+ value_ray = getIntersect(sibling_ray, checked_ray)
136
+ for (var element in value_ray) {
137
+ field_ray.push(value_ray[element].id.split('-').slice(2)[0]);
138
+ }
139
+ return field_ray;
140
+ case 'select':
141
+ return $('#' + field.attr('id') + ' option:selected').text().toLowerCase();
142
+ case 'multi-select':
143
+ var value_ray = $('#' + field.attr('id') + ' option:selected');
144
+ var field_ray = []
145
+ for (var element in value_ray)
146
+ {
147
+ var temp = value_ray[element].text;
148
+ if (typeof(temp) == "string") { field_ray.push(temp.toLowerCase()); }
149
+ }
150
+ return field_ray;
151
+ default:
152
+ return '';
153
+ }
154
+ };
155
+
156
+ function grab_type(field) {
157
+ if (field.attr('type') != undefined)
158
+ {
159
+ return field.attr('type')
160
+ }
161
+ else if ($('textarea#'+field.attr('id')).length > 0)
162
+ {
163
+ return "textarea";
164
+ }
165
+ else if ($('select#'+field.attr('id')).length > 0)
166
+ {
167
+ if (field.attr('multiple') == "multiple")
168
+ {
169
+ return "multi-select";
170
+ }
171
+ else {
172
+ return "select";
173
+ }
174
+ }
175
+ else
176
+ {
177
+ return "invalid";
178
+ }
179
+ }
180
+
181
+ function getIntersect(arr1, arr2) {
182
+ var temp = [];
183
+ for(var i = 0; i < arr1.length; i++){
184
+ for(var k = 0; k < arr2.length; k++){
185
+ if(arr1[i] == arr2[k]){
186
+ temp.push( arr1[i]);
187
+ break;
188
+ }
189
+ }
190
+ }
191
+ return temp;
192
+ }
193
+
194
+ /***********************************************************************
195
+
196
+ This watches for ANY change that may take place on the page. It then
197
+ pushes the object through grab_fsc_id to get an id we can check against
198
+ the json_holder. If it's in there, it then grabs all the related data,
199
+ checks the current value against the expected value through the
200
+ evaluate() function and then goes through the needed action AFTER
201
+ checking each dependency and dependency_clause.
202
+
203
+ ***********************************************************************/
204
+
205
+ allInputs.change( function() {
206
+ var field = $(this);
207
+ var field_type = grab_type(field);
208
+ var fieldset_child_id = grab_fsc_id(field, field_type);
209
+ var entered_value = grab_value(field, field_type)
210
+ if (fieldset_child_id in json_holder) {
211
+ for (var group in json_holder[fieldset_child_id]) {
212
+ var action = json_holder[fieldset_child_id][group]['action'];
213
+ var group_fsc_id = json_holder[fieldset_child_id][group]['fieldset_child_id'];
214
+ var group_field_id = json_holder[fieldset_child_id][group]['field_id'];
215
+ var group_field = 'field-' + group_field_id + '-child-' + group_fsc_id;
216
+
217
+ // As each dependency clause is AND'd together, we'll assume it'll be
218
+ // true until short circuited into being false.`
219
+ var group_break_flag = true;
220
+ group:
221
+ for (var clause in json_holder[fieldset_child_id][group]["clause"]) {
222
+ // As each clause ORs dependencies together, we'll assume it'll be
223
+ // false until short circuited into being true.
224
+ var clause_break_flag = false;
225
+ clause:
226
+ for (var dependency in json_holder[fieldset_child_id][group]["clause"][clause])
227
+ {
228
+ var relationship = json_holder[fieldset_child_id][group]["clause"][clause][dependency]['relationship'];
229
+ var value = json_holder[fieldset_child_id][group]["clause"][clause][dependency]['value'].toLowerCase();
230
+ // Short circuit from looking at each dependency in the case
231
+ // we determine a true dependency (OR)
232
+ if (evaluate(entered_value, relationship, value)) {
233
+ clause_break_flag = true;
234
+ break clause;
235
+ }
236
+ }
237
+
238
+ // Short circuit from looking at each clause in the case we determine
239
+ // a false dependency (AND)
240
+ if (clause_break_flag == false) {
241
+ group_break_flag = false;
242
+ break group;
243
+ }
244
+ }
245
+ // If successful, group_break_flag will pass true into group_action
246
+ group_action(group_break_flag, action, group_field);
247
+
248
+ }
249
+
250
+ }
251
+
252
+ })
253
+ .change();
254
+
255
+ </script>
@@ -0,0 +1,35 @@
1
+ <script>
2
+ <%# Sorry for these duplicated functions %>
3
+ function remove_fields(link) {
4
+ $(link).prev("input[type=hidden]").val("1");
5
+ $(link).closest(".fields").hide();
6
+ $(link).parent().hide();
7
+ }
8
+
9
+ function remove_fields_and_parent_fields(link) {
10
+ $(link).prev("input[type=hidden]").val("1");
11
+ $(link).closest(".fields").hide();
12
+ $(link).parent().parent().hide();
13
+ }
14
+
15
+ function remove_fields_from_table(link) {
16
+ $(link).prev("input[type=hidden]").val("1");
17
+ $(link).closest(".fields").hide();
18
+ $(link).parent().parent().hide();
19
+ //also remove the next row
20
+ $(link).parent().parent().next().hide();
21
+ }
22
+
23
+ function add_fields(link, association, content) {
24
+ var new_id = new Date().getTime();
25
+ var regexp = new RegExp("new_" + association, "g");
26
+ $(link).parent().before(content.replace(regexp, new_id));
27
+ }
28
+
29
+ function add_fields_to_table(link, association, content) {
30
+ var new_id = new Date().getTime();
31
+ var regexp = new RegExp("new_" + association, "g");
32
+ $(link).parent().parent().before(content.replace(regexp, new_id));
33
+ }
34
+ </script>
35
+
Binary file
data/config/routes.rb CHANGED
@@ -1,9 +1,20 @@
1
1
  Rails.application.routes.draw do
2
2
  namespace :dynamic_fieldsets do
3
3
  resources :fieldset_associators
4
+
4
5
  resources :fieldsets
6
+ match "/fieldsets/roots" => "fieldsets#roots"
5
7
  match "/fieldsets/:id/children" => "fieldsets#children", :as => :children_dynamic_fieldsets_fieldset
8
+ match "/fieldsets/:id/children/reorder" => "fieldsets#reorder"
9
+ match "/fieldsets/new(/:parent)" => "fieldsets#new", :as => :new_dynamic_fieldsets_fieldset
10
+ match "fieldsets/:id/associate_child" => "fieldsets#associate_child", :as => :associate_child_to_fieldset
11
+
6
12
  resources :fields
13
+ match "/fields/new(/:parent)" => "fields#new", :as => :new_dynamic_fieldsets_field
14
+ match "/fields/:id/enable" => "fields#enable", :as => "enable_field", :method => :post
15
+
16
+ resources :fieldset_children
17
+ match "/fieldset_children/:id/remove" => "fieldset_children#remove", :as => :remove_fieldset_child, :method => :post
7
18
  end
8
19
 
9
20
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{dynamic_fieldsets}
8
- s.version = "0.0.3"
8
+ s.version = "0.0.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = [%q{Jeremiah Hemphill}, %q{Ethan Pemble}]
12
- s.date = %q{2011-07-28}
11
+ s.authors = [%q{Jeremiah Hemphill}, %q{Ethan Pemble}, %q{John Carter}]
12
+ s.date = %q{2011-08-25}
13
13
  s.description = %q{Dynamic fieldsets for rails controllers}
14
14
  s.email = %q{jeremiah@cloudspace.com}
15
15
  s.extra_rdoc_files = [
@@ -25,9 +25,15 @@ Gem::Specification.new do |s|
25
25
  "VERSION",
26
26
  "app/controllers/dynamic_fieldsets/fields_controller.rb",
27
27
  "app/controllers/dynamic_fieldsets/fieldset_associators_controller.rb",
28
+ "app/controllers/dynamic_fieldsets/fieldset_children_controller.rb",
28
29
  "app/controllers/dynamic_fieldsets/fieldsets_controller.rb",
29
30
  "app/helpers/dynamic_fieldsets/fields_helper.rb",
31
+ "app/helpers/dynamic_fieldsets/fieldset_children_helper.rb",
32
+ "app/helpers/dynamic_fieldsets/nested_model_helper.rb",
30
33
  "app/helpers/dynamic_fieldsets_helper.rb",
34
+ "app/models/dynamic_fieldsets/dependency.rb",
35
+ "app/models/dynamic_fieldsets/dependency_clause.rb",
36
+ "app/models/dynamic_fieldsets/dependency_group.rb",
31
37
  "app/models/dynamic_fieldsets/field.rb",
32
38
  "app/models/dynamic_fieldsets/field_default.rb",
33
39
  "app/models/dynamic_fieldsets/field_html_attribute.rb",
@@ -35,6 +41,8 @@ Gem::Specification.new do |s|
35
41
  "app/models/dynamic_fieldsets/field_record.rb",
36
42
  "app/models/dynamic_fieldsets/fieldset.rb",
37
43
  "app/models/dynamic_fieldsets/fieldset_associator.rb",
44
+ "app/models/dynamic_fieldsets/fieldset_child.rb",
45
+ "app/views/dynamic_fieldsets/fields/_disable_field_form.html.erb",
38
46
  "app/views/dynamic_fieldsets/fields/_field_default_fields.html.erb",
39
47
  "app/views/dynamic_fieldsets/fields/_field_html_attribute_fields.html.erb",
40
48
  "app/views/dynamic_fieldsets/fields/_field_option_fields.html.erb",
@@ -45,12 +53,23 @@ Gem::Specification.new do |s|
45
53
  "app/views/dynamic_fieldsets/fields/show.html.erb",
46
54
  "app/views/dynamic_fieldsets/fieldset_associators/index.html.erb",
47
55
  "app/views/dynamic_fieldsets/fieldset_associators/show.html.erb",
56
+ "app/views/dynamic_fieldsets/fieldset_children/_dependency_clause_fields.html.erb",
57
+ "app/views/dynamic_fieldsets/fieldset_children/_dependency_fields.html.erb",
58
+ "app/views/dynamic_fieldsets/fieldset_children/_dependency_group_fields.html.erb",
59
+ "app/views/dynamic_fieldsets/fieldset_children/_form.html.erb",
60
+ "app/views/dynamic_fieldsets/fieldset_children/edit.html.erb",
61
+ "app/views/dynamic_fieldsets/fieldsets/_associate_child.html.erb",
62
+ "app/views/dynamic_fieldsets/fieldsets/_child.html.erb",
48
63
  "app/views/dynamic_fieldsets/fieldsets/_form.html.erb",
49
64
  "app/views/dynamic_fieldsets/fieldsets/children.html.erb",
50
65
  "app/views/dynamic_fieldsets/fieldsets/edit.html.erb",
51
66
  "app/views/dynamic_fieldsets/fieldsets/index.html.erb",
52
67
  "app/views/dynamic_fieldsets/fieldsets/new.html.erb",
68
+ "app/views/dynamic_fieldsets/fieldsets/reorder.html.erb",
53
69
  "app/views/dynamic_fieldsets/fieldsets/show.html.erb",
70
+ "app/views/dynamic_fieldsets/shared/_javascript_watcher.html.erb",
71
+ "app/views/dynamic_fieldsets/shared/_nested_model_javascript.html.erb",
72
+ "config/.routes.rb.swp",
54
73
  "config/routes.rb",
55
74
  "dynamic_fieldsets.gemspec",
56
75
  "lib/dynamic_fieldsets.rb",
@@ -68,6 +87,7 @@ Gem::Specification.new do |s|
68
87
  "spec/dummy/app/helpers/information_forms_helper.rb",
69
88
  "spec/dummy/app/models/information_form.rb",
70
89
  "spec/dummy/app/views/information_forms/_form.html.erb",
90
+ "spec/dummy/app/views/information_forms/dynamic_view.html.erb",
71
91
  "spec/dummy/app/views/information_forms/edit.html.erb",
72
92
  "spec/dummy/app/views/information_forms/index.html.erb",
73
93
  "spec/dummy/app/views/information_forms/new.html.erb",
@@ -88,16 +108,20 @@ Gem::Specification.new do |s|
88
108
  "spec/dummy/config/initializers/session_store.rb",
89
109
  "spec/dummy/config/locales/en.yml",
90
110
  "spec/dummy/config/routes.rb",
91
- "spec/dummy/db/migrate/20110726215814_create_dynamic_fieldsets_tables.rb",
92
111
  "spec/dummy/db/migrate/20110727210451_create_information_forms.rb",
112
+ "spec/dummy/db/migrate/20110809161724_create_dynamic_fieldsets_tables.rb",
93
113
  "spec/dummy/db/schema.rb",
94
114
  "spec/dummy/features/field.feature",
95
115
  "spec/dummy/features/fieldset.feature",
96
116
  "spec/dummy/features/fieldset_associator.feature",
117
+ "spec/dummy/features/fieldset_children.feature",
118
+ "spec/dummy/features/javascript_tests.feature",
97
119
  "spec/dummy/features/step_definitions/debugging_steps.rb",
98
120
  "spec/dummy/features/step_definitions/field_steps.rb",
99
121
  "spec/dummy/features/step_definitions/fieldset_associator_steps.rb",
122
+ "spec/dummy/features/step_definitions/fieldset_children_steps.rb",
100
123
  "spec/dummy/features/step_definitions/fieldset_steps.rb",
124
+ "spec/dummy/features/step_definitions/javascript_steps.rb",
101
125
  "spec/dummy/features/step_definitions/web_steps.rb",
102
126
  "spec/dummy/features/support/env.rb",
103
127
  "spec/dummy/features/support/paths.rb",
@@ -107,6 +131,9 @@ Gem::Specification.new do |s|
107
131
  "spec/dummy/public/500.html",
108
132
  "spec/dummy/public/favicon.ico",
109
133
  "spec/dummy/public/javascripts/application.js",
134
+ "spec/dummy/public/javascripts/jquery-1.6.2.min.js",
135
+ "spec/dummy/public/javascripts/jquery-ui-1.8.15.custom.min.js",
136
+ "spec/dummy/public/javascripts/jquery-ui-nestedSortable.js",
110
137
  "spec/dummy/public/javascripts/jquery.min.js",
111
138
  "spec/dummy/public/stylesheets/.gitkeep",
112
139
  "spec/dummy/public/stylesheets/scaffold.css",
@@ -115,24 +142,31 @@ Gem::Specification.new do |s|
115
142
  "spec/dynamic_fieldsets_in_model_spec.rb",
116
143
  "spec/dynamic_fieldsets_spec.rb",
117
144
  "spec/integration/navigation_spec.rb",
145
+ "spec/models/dependency_clause_spec.rb",
146
+ "spec/models/dependency_group_spec.rb",
147
+ "spec/models/dependency_spec.rb",
118
148
  "spec/models/field_default_spec.rb",
119
149
  "spec/models/field_html_attribute_spec.rb",
120
150
  "spec/models/field_option_spec.rb",
121
151
  "spec/models/field_record_spec.rb",
122
152
  "spec/models/field_spec.rb",
123
153
  "spec/models/fieldset_associator_spec.rb",
154
+ "spec/models/fieldset_child_spec.rb",
124
155
  "spec/models/fieldset_spec.rb",
125
156
  "spec/reports/SPEC-ActsAsMultipartForm.xml",
126
157
  "spec/reports/SPEC-MultipartForm-InProgressForm-validations.xml",
127
158
  "spec/reports/SPEC-MultipartForm-InProgressForm.xml",
128
159
  "spec/reports/SPEC-Navigation.xml",
129
160
  "spec/spec_helper.rb",
161
+ "spec/support/dependency_group_helper.rb",
162
+ "spec/support/dependency_helper.rb",
130
163
  "spec/support/field_default_helper.rb",
131
164
  "spec/support/field_helper.rb",
132
165
  "spec/support/field_html_attribute_helper.rb",
133
166
  "spec/support/field_option_helper.rb",
134
167
  "spec/support/field_record_helper.rb",
135
168
  "spec/support/fieldset_associator_helper.rb",
169
+ "spec/support/fieldset_child_helper.rb",
136
170
  "spec/support/fieldset_helper.rb"
137
171
  ]
138
172
  s.homepage = %q{http://github.com/jeremiahishere/dynamic_fieldsets}
@@ -158,6 +192,7 @@ Gem::Specification.new do |s|
158
192
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
159
193
  s.add_development_dependency(%q<jeweler>, ["~> 1.6.3"])
160
194
  s.add_development_dependency(%q<rcov>, [">= 0"])
195
+ s.add_development_dependency(%q<json>, [">= 0"])
161
196
  s.add_development_dependency(%q<ci_reporter>, [">= 0"])
162
197
  else
163
198
  s.add_dependency(%q<rails>, ["= 3.0.7"])
@@ -173,6 +208,7 @@ Gem::Specification.new do |s|
173
208
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
174
209
  s.add_dependency(%q<jeweler>, ["~> 1.6.3"])
175
210
  s.add_dependency(%q<rcov>, [">= 0"])
211
+ s.add_dependency(%q<json>, [">= 0"])
176
212
  s.add_dependency(%q<ci_reporter>, [">= 0"])
177
213
  end
178
214
  else
@@ -189,6 +225,7 @@ Gem::Specification.new do |s|
189
225
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
190
226
  s.add_dependency(%q<jeweler>, ["~> 1.6.3"])
191
227
  s.add_dependency(%q<rcov>, [">= 0"])
228
+ s.add_dependency(%q<json>, [">= 0"])
192
229
  s.add_dependency(%q<ci_reporter>, [">= 0"])
193
230
  end
194
231
  end
@@ -39,27 +39,154 @@ module DynamicFieldsets
39
39
  end
40
40
 
41
41
  module InstanceMethods
42
+
43
+ # Overrides the ActiveModel Validations run_validations! method
44
+ # It additionally adds validations for the fields that are required
45
+ #
46
+ # I am not sure if this is the correct place to put this. Seems like a reasonable one.
47
+ #
48
+ # @return [Boolean] The result of run_validations! with the extra errors added, should be true if errors.empty?
49
+ def run_validations!
50
+ run_dynamic_fieldset_validations!
51
+ super
52
+ end
53
+
54
+ # Iterates over the fieldset associator's children and adds errors
55
+ def run_dynamic_fieldset_validations!
56
+ # for each fsa
57
+ self.dynamic_fieldsets.keys.each do |key|
58
+ fsa = self.fieldset_associator(key)
59
+ run_fieldset_child_validations!(fsa.id, fsa.fieldset)
60
+ end
61
+ end
62
+
63
+ # Checks if a fieldset child is required and adds an error if it's value is blank
64
+ # Adds errors to the sel.errors array, does not return them
65
+ #
66
+ # @param [Integer] fsa_id The id for the fieldset associator the child belongs to
67
+ # @param [Field or Fieldset] child The child of the fieldset associator
68
+ def run_fieldset_child_validations!(fsa_id, child)
69
+ if child.is_a?(DynamicFieldsets::Fieldset)
70
+ # if a fieldset, then recurse
71
+ child.children.each do |grand_child|
72
+ run_fieldset_child_validations!(fsa_id, grand_child)
73
+ end
74
+ elsif child.is_a?(DynamicFieldsets::Field)
75
+ # if a child, check if the params value is set, check if it is required, check if it satisfies condition
76
+ fsa_tag_id = "fsa-" + fsa_id.to_s
77
+ field_tag_id = "field-" + child.id.to_s
78
+ if !self.dynamic_fieldset_values.has_key?(fsa_tag_id) || !self.dynamic_fieldset_values[fsa_tag_id].has_key?(field_tag_id)
79
+ self.errors.add(:base, child.label + " is required and the input is missing")
80
+ else
81
+ # get the value
82
+ value = self.dynamic_fieldset_values[fsa_tag_id][field_tag_id]
83
+ if child.required?
84
+ # empty works on array or string, so simplifying here
85
+ self.errors.add(:base, child.label + " is required") if value.nil? || value.empty?
86
+ end
87
+ end
88
+ else
89
+ # found a major problem, not sure how to get here
90
+ puts "found a child that wasn't a field or fieldset" + child.inspect
91
+ end
92
+ end
93
+
94
+ # Stores the dynamic fieldset values
95
+ def set_fieldset_values( params )
96
+ values = params.select{ |key| key.match(/^fsa-/) }
97
+ values.keys.each do |key|
98
+ set_date_to_mysql( values[key] )
99
+ end
100
+ self.dynamic_fieldset_values = values
101
+ end
102
+
103
+ # This turns your date fields into a MySQL-happy single format. This modifies the hash.
104
+ # @param [Hash] post The post parameters that include date fields like date(1i), date(2i), ...
105
+ # @return [Hash] The modified hash containing one key-pair value in YYYY-MM-DD[ hh:mm] format.
106
+ def set_date_to_mysql( post )
107
+ # 'dates' is an array of the "field-ID"s that have multiple date fields of the format field(1i), field(2i), ...
108
+ # e.g. [ "field-4", "field-7" ]
109
+ dates = post.select{ |k| k =~ /\([0-9]i\)/ }.keys.map{ |k| k.gsub /\([0-9]i\)/, '' }.uniq
110
+ dates.each do |field|
111
+ datefield = ''
112
+ datefield += post.delete( "#{field}(1i)" ) # year
113
+ datefield += '-'
114
+ datefield += '%02d' % post.delete( "#{field}(2i)" ) # month
115
+ datefield += '-'
116
+ datefield += '%02d' % post.delete( "#{field}(3i)" ) # day
117
+ if post.keys.include? "#{field}(4i)" then
118
+ datefield += ' '
119
+ datefield += '%02d' % post.delete( "#{field}(4i)" ) # hour
120
+ datefield += ':'
121
+ datefield += '%02d' % post.delete( "#{field}(5i)" ) # minute
122
+ datefield += ':'
123
+ datefield += '00' # second
124
+ end
125
+ # adding the formatted string to the hash to be saved.
126
+ post.merge! field => datefield
127
+ end
128
+ return post
129
+ end
130
+
42
131
 
43
132
  # hacky system to save fieldset values
44
133
  # needs to be refactored and tested
45
134
  #
46
135
  # among other things, it can edit field records for random fsas if the wrong information comes from the controller
136
+
47
137
  def save_dynamic_fieldsets
48
- if !self.dynamic_fieldset_values.nil?
49
- self.dynamic_fieldset_values.keys.each do |key|
138
+ values = self.dynamic_fieldset_values
139
+ if !values.nil?
140
+ values.keys.each do |key|
50
141
  if key.start_with?("fsa-")
51
142
  key_id = key.gsub(/^fsa-/, "")
52
- fsa = DynamicFieldsets::FieldsetAssociator.find_by_id(key_id)
53
- self.dynamic_fieldset_values[key].keys.each do |sub_key|
143
+
144
+ if key_id.eql? ""
145
+ then fsa = DynamicFieldsets::FieldsetAssociator.create(
146
+ :fieldset_id => values[key][:fieldset_id],
147
+ :fieldset_model_id => self.id,
148
+ :fieldset_model_type => self.class.name,
149
+ :fieldset_model_name => values[key][:fieldset_model_name] )
150
+ else fsa = DynamicFieldsets::FieldsetAssociator.find_by_id key_id
151
+ end
152
+
153
+ values[key].keys.each do |sub_key| # EACH FIELD
54
154
  if sub_key.start_with?("field-")
55
155
  sub_key_id = sub_key.gsub(/^field-/, "")
56
- field_record = DynamicFieldsets::FieldRecord.where(:fieldset_associator_id => fsa.id, :field_id => sub_key_id).first
57
- if field_record.nil?
58
- field_record = DynamicFieldsets::FieldRecord.create(:fieldset_associator_id => fsa.id, :field_id => sub_key_id, :value => self.dynamic_fieldset_values[key][sub_key])
59
- else
60
- field_record.value = self.dynamic_fieldset_values[key][sub_key]
61
- field_record.save
156
+
157
+ this_value = values[key][sub_key]
158
+ if this_value.is_a? Array
159
+ then # multiple values
160
+ field_records = DynamicFieldsets::FieldRecord.where(:fieldset_associator_id => fsa.id, :fieldset_child_id => sub_key_id)
161
+
162
+ this_value.each do |value|
163
+ if field_records.select{ |record| record.value.eql? value }.empty? # record does not exist?
164
+ #ADD
165
+ DynamicFieldsets::FieldRecord.create( :fieldset_associator_id => fsa.id,
166
+ :fieldset_child_id => sub_key_id,
167
+ :value => value)
168
+ end
169
+ end
170
+ field_records.each do |record|
171
+ if !this_value.include? record.value then
172
+ #DELETE
173
+ record.destroy
174
+ else
175
+ #KEEP
176
+ end
177
+ end
178
+
179
+ else # single value
180
+ # retrieve record
181
+ field_record = DynamicFieldsets::FieldRecord.where(:fieldset_associator_id => fsa.id, :fieldset_child_id => sub_key_id).first
182
+ if field_record.nil? # create record
183
+ field_record = DynamicFieldsets::FieldRecord.create(:fieldset_associator_id => fsa.id, :fieldset_child_id => sub_key_id, :value => this_value)
184
+ else # update record
185
+ field_record.value = this_value
186
+ field_record.save
187
+ end
62
188
  end
189
+
63
190
  end
64
191
  end
65
192
  end
@@ -130,14 +257,14 @@ module DynamicFieldsets
130
257
  def fieldset_associator(sym)
131
258
  if match_fieldset_associator?(sym)
132
259
  fsa = DynamicFieldsets::FieldsetAssociator.find_by_fieldset_model_parameters(
133
- :fieldset_model_id => self.id,
260
+ :fieldset_model_id => self.id,
134
261
  :fieldset_model_type => self.class.name,
135
262
  :fieldset_model_name => sym,
136
263
  :fieldset => self.dynamic_fieldsets[sym][:fieldset]).first
137
264
  if fsa.nil?
138
- fsa = DynamicFieldsets::FieldsetAssociator.create(
139
- :fieldset_model_id => self.id,
140
- :fieldset_model_type => self.class.name,
265
+ fsa = DynamicFieldsets::FieldsetAssociator.new(
266
+ :fieldset_model_id => self.id,
267
+ :fieldset_model_type => self.class.name,
141
268
  :fieldset_model_name => sym.to_s,
142
269
  :fieldset => DynamicFieldsets::Fieldset.find_by_nkey(self.dynamic_fieldsets[sym][:fieldset]))
143
270
  end