formbuilder-rb 0.0.4 → 0.2.0

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +14 -0
  7. data/Gemfile.lock +219 -0
  8. data/Guardfile +17 -0
  9. data/{MIT-LICENSE → LICENSE.md} +1 -1
  10. data/README.md +86 -0
  11. data/app/controllers/formbuilder/concerns/forms_controller.rb +59 -0
  12. data/app/models/formbuilder/entry_attachment.rb +2 -8
  13. data/app/models/formbuilder/form.rb +53 -0
  14. data/app/models/formbuilder/response_field.rb +23 -10
  15. data/app/models/formbuilder/response_field_address.rb +12 -1
  16. data/app/models/formbuilder/response_field_checkboxes.rb +38 -0
  17. data/app/models/formbuilder/response_field_date.rb +7 -1
  18. data/app/models/formbuilder/response_field_dropdown.rb +2 -1
  19. data/app/models/formbuilder/response_field_email.rb +14 -2
  20. data/app/models/formbuilder/response_field_file.rb +75 -20
  21. data/app/models/formbuilder/response_field_number.rb +9 -2
  22. data/app/models/formbuilder/response_field_page_break.rb +13 -0
  23. data/app/models/formbuilder/response_field_paragraph.rb +19 -3
  24. data/app/models/formbuilder/response_field_price.rb +10 -6
  25. data/app/models/formbuilder/response_field_radio.rb +11 -2
  26. data/app/models/formbuilder/response_field_section_break.rb +12 -2
  27. data/app/models/formbuilder/response_field_table.rb +222 -0
  28. data/app/models/formbuilder/response_field_text.rb +9 -2
  29. data/app/models/formbuilder/response_field_time.rb +8 -1
  30. data/app/models/formbuilder/response_field_website.rb +26 -2
  31. data/app/uploaders/formbuilder/entry_attachment_uploader.rb +11 -1
  32. data/bin/rails +8 -0
  33. data/circle.yml +9 -0
  34. data/config/spring.rb +1 -0
  35. data/db/migrate/20130924185815_create_formbuilder_entry_attachments.rb +1 -0
  36. data/formbuilder-rb.gemspec +41 -0
  37. data/formbuilder-rb.sublime-project +20 -0
  38. data/lib/formbuilder.rb +8 -3
  39. data/lib/formbuilder/entry.rb +102 -159
  40. data/lib/formbuilder/entry_validator.rb +13 -6
  41. data/lib/formbuilder/version.rb +1 -1
  42. data/lib/formbuilder/views/entry_dl.rb +56 -0
  43. data/lib/formbuilder/views/form.rb +81 -0
  44. data/lib/formbuilder/views/form_field.rb +45 -0
  45. data/spec/controllers/formbuilder/forms_controller_spec.rb +40 -0
  46. data/spec/dummy/.ruby-gemset +1 -0
  47. data/spec/dummy/.ruby-version +1 -0
  48. data/spec/dummy/README.rdoc +28 -0
  49. data/spec/dummy/Rakefile +6 -0
  50. data/spec/dummy/app/assets/images/.keep +0 -0
  51. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  52. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  53. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  54. data/spec/dummy/app/controllers/forms_controller.rb +3 -0
  55. data/spec/dummy/app/controllers/test_controller.rb +57 -0
  56. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  57. data/spec/dummy/app/mailers/.keep +0 -0
  58. data/spec/dummy/app/models/concerns/.keep +0 -0
  59. data/spec/dummy/app/models/entry.rb +7 -0
  60. data/spec/dummy/app/models/entry_with_alternate_column_name.rb +11 -0
  61. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  62. data/spec/dummy/app/views/test/_form.rb +2 -0
  63. data/spec/dummy/app/views/test/render_entry.html.erb +1 -0
  64. data/spec/dummy/app/views/test/show_form.html.erb +1 -0
  65. data/spec/dummy/bin/bundle +3 -0
  66. data/spec/dummy/bin/rails +4 -0
  67. data/spec/dummy/bin/rake +4 -0
  68. data/spec/dummy/config.ru +4 -0
  69. data/spec/dummy/config/application.rb +25 -0
  70. data/spec/dummy/config/boot.rb +5 -0
  71. data/spec/dummy/config/database.yml.ci +15 -0
  72. data/spec/dummy/config/database.yml.example +15 -0
  73. data/spec/dummy/config/environment.rb +5 -0
  74. data/spec/dummy/config/environments/development.rb +29 -0
  75. data/spec/dummy/config/environments/production.rb +80 -0
  76. data/spec/dummy/config/environments/test.rb +52 -0
  77. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  78. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  79. data/spec/dummy/config/initializers/inflections.rb +16 -0
  80. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  81. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  82. data/spec/dummy/config/initializers/session_store.rb +3 -0
  83. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  84. data/spec/dummy/config/locales/en.yml +23 -0
  85. data/spec/dummy/config/routes.rb +12 -0
  86. data/spec/dummy/db/migrate/20130924182939_create_entries.rb +13 -0
  87. data/spec/dummy/db/migrate/20130924192151_create_formbuilder_forms.formbuilder.rb +11 -0
  88. data/spec/dummy/db/migrate/20130924192152_create_formbuilder_response_fields.formbuilder.rb +17 -0
  89. data/spec/dummy/db/migrate/20130926205845_create_formbuilder_entry_attachments.formbuilder.rb +12 -0
  90. data/spec/dummy/db/migrate/20140128000000_create_entry_with_alternate_column_names.rb +13 -0
  91. data/spec/dummy/db/schema.rb +64 -0
  92. data/spec/dummy/lib/assets/.keep +0 -0
  93. data/spec/dummy/log/.keep +0 -0
  94. data/spec/dummy/public/404.html +58 -0
  95. data/spec/dummy/public/422.html +58 -0
  96. data/spec/dummy/public/500.html +57 -0
  97. data/spec/dummy/public/favicon.ico +0 -0
  98. data/spec/dummy/test/fixtures/entries.yml +9 -0
  99. data/spec/dummy/test/fixtures/response_fields.yml +19 -0
  100. data/spec/dummy/test/models/entry_test.rb +7 -0
  101. data/spec/dummy/test/models/response_field_test.rb +7 -0
  102. data/spec/factories/formbuilder_forms.rb +45 -0
  103. data/spec/features/form_renderer_spec.rb +30 -0
  104. data/spec/features/submitting_an_entry_spec.rb +79 -0
  105. data/spec/fixtures/test_files/text.txt +1 -0
  106. data/spec/fixtures/test_files/text2.txt +1 -0
  107. data/spec/lib/formbuilder/entry_spec.rb +199 -0
  108. data/spec/lib/formbuilder/entry_validator_spec.rb +215 -0
  109. data/spec/lib/formbuilder/entry_with_alternate_column_name_spec.rb +37 -0
  110. data/spec/lib/formbuilder/views/entry_dl_spec.rb +38 -0
  111. data/spec/models/formbuilder/entry_attachment_spec.rb +17 -0
  112. data/spec/models/formbuilder/form_spec.rb +100 -0
  113. data/spec/models/formbuilder/response_field_address_spec.rb +25 -0
  114. data/spec/models/formbuilder/response_field_checkboxes_spec.rb +57 -0
  115. data/spec/models/formbuilder/response_field_file_spec.rb +80 -0
  116. data/spec/models/formbuilder/response_field_paragraph_spec.rb +22 -0
  117. data/spec/models/formbuilder/response_field_radio_spec.rb +30 -0
  118. data/spec/models/formbuilder/response_field_spec.rb +21 -0
  119. data/spec/models/formbuilder/response_field_table_spec.rb +124 -0
  120. data/spec/spec_helper.rb +25 -0
  121. data/spec/support/database_cleaner.rb +21 -0
  122. data/spec/support/submitting_an_entry_spec_helper.rb +151 -0
  123. metadata +286 -44
  124. data/app/controllers/formbuilder/forms_controller.rb +0 -53
  125. data/lib/formbuilder/entry_renderer.rb +0 -47
  126. data/lib/formbuilder/entry_table_renderer.rb +0 -58
  127. data/lib/formbuilder/form_renderer.rb +0 -102
data/bin/rails ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/formbuilder/engine', __FILE__)
6
+
7
+ require 'rails/all'
8
+ require 'rails/engine/commands'
data/circle.yml ADDED
@@ -0,0 +1,9 @@
1
+ machine:
2
+ environment:
3
+ COVERALLS_REPO_TOKEN: ppRfiNa1YEa2M9VtGpXw0AOzVqYEUEkFR
4
+
5
+ database:
6
+ override:
7
+ - cp spec/dummy/config/database.yml.ci spec/dummy/config/database.yml
8
+ - bundle exec rake db:create db:schema:load
9
+
data/config/spring.rb ADDED
@@ -0,0 +1 @@
1
+ Spring.application_root = './spec/dummy'
@@ -3,6 +3,7 @@ class CreateFormbuilderEntryAttachments < ActiveRecord::Migration
3
3
  create_table :formbuilder_entry_attachments do |t|
4
4
  t.string :upload
5
5
  t.string :content_type
6
+ t.integer :file_size
6
7
 
7
8
  t.timestamps
8
9
  end
@@ -0,0 +1,41 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ # Maintain your gem's version:
4
+ require "formbuilder/version"
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |s|
8
+ s.name = "formbuilder-rb"
9
+ s.version = Formbuilder::VERSION
10
+ s.authors = ["Adam Becker"]
11
+ s.email = ["adam@dobt.co"]
12
+ s.homepage = "https://github.com/dobtco/formbuilder-rb"
13
+ s.summary = "Build and save custom forms."
14
+ s.description = "This is a Rails Engine for https://github.com/dobtco/formbuilder."
15
+ s.license = "MIT"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {features,spec}/*`.split("\n")
19
+
20
+ s.add_dependency "rails", "4.1.0.rc1"
21
+
22
+ s.add_dependency 'carrierwave'
23
+ s.add_dependency 'erector-rails4'
24
+ s.add_dependency 'geocoder'
25
+ s.add_dependency 'pg'
26
+ s.add_dependency 'rinku'
27
+ s.add_dependency 'rmagick'
28
+
29
+ s.add_development_dependency 'capybara'
30
+ s.add_development_dependency 'database_cleaner'
31
+ s.add_development_dependency 'factory_girl_rails'
32
+ s.add_development_dependency 'guard-rspec'
33
+ s.add_development_dependency 'launchy'
34
+ s.add_development_dependency 'rspec-rails'
35
+ s.add_development_dependency 'spring'
36
+ s.add_development_dependency 'spring-commands-rspec'
37
+ s.add_development_dependency 'terminal-notifier-guard'
38
+ s.add_development_dependency 'thin'
39
+ s.add_development_dependency 'coveralls'
40
+
41
+ end
@@ -0,0 +1,20 @@
1
+ {
2
+ "folders":
3
+ [
4
+ {
5
+ "path": ".",
6
+ "folder_exclude_patterns": ["coverage", "log", "tmp"],
7
+ "file_exclude_patterns": ["*.sublime-workspace", "*.sql"]
8
+ }
9
+ ],
10
+
11
+ "settings":
12
+ {
13
+ // indentation
14
+ "tab_size": 2,
15
+ "translate_tabs_to_spaces": true,
16
+ "trim_trailing_white_space_on_save": true,
17
+ // ensure line endings is linux style (even when on Windows)
18
+ "default_line_ending": "LF"
19
+ }
20
+ }
data/lib/formbuilder.rb CHANGED
@@ -1,9 +1,14 @@
1
+ # Require dependencies explicitly
2
+ require 'carrierwave'
3
+ require 'erector'
4
+ require 'geocoder'
5
+
1
6
  require "formbuilder/engine"
2
7
  require "formbuilder/entry"
3
- require "formbuilder/entry_renderer"
4
- require "formbuilder/entry_table_renderer"
5
8
  require "formbuilder/entry_validator"
6
- require "formbuilder/form_renderer"
9
+ require "formbuilder/views/form"
10
+ require "formbuilder/views/form_field"
11
+ require "formbuilder/views/entry_dl"
7
12
 
8
13
  module Formbuilder
9
14
  def self.root
@@ -3,33 +3,82 @@ module Formbuilder
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- attr_accessor :old_responses
7
- attr_accessor :skip_validation
6
+ attr_accessor :skip_validation, :only_validate_page
7
+
8
+ before_validation :normalize_responses
8
9
  validates_with Formbuilder::EntryValidator
9
- before_save :calculate_responses_text, if: :responses_changed?
10
- include ActionView::Helpers::TextHelper
10
+ before_save :calculate_responses_text, if: :responses_column_changed?
11
+
12
+ scope :order_by_response_field_value, -> (response_field, direction) {
13
+ if response_field.sort_as_numeric
14
+ order("(responses -> '#{response_field.id}_sortable_value') ::numeric #{direction} NULLS LAST")
15
+ else
16
+ order("LOWER(responses -> '#{response_field.id}_sortable_value') #{direction} NULLS LAST")
17
+ end
18
+ }
19
+
20
+ scope :order_by_response_field_checkbox_value, -> (response_field, option, direction) {
21
+ order("(CASE WHEN (responses -> '#{response_field.id}_sortable_values_#{option}') = 'true' THEN
22
+ 1
23
+ ELSE
24
+ 0
25
+ END) #{direction}".squish)
26
+ }
27
+
28
+ scope :order_by_response_field_table_sum, -> (response_field, column, direction) {
29
+ order("(responses -> '#{response_field.id}_sum_#{column}') ::numeric #{direction}")
30
+ }
31
+ end
32
+
33
+ def valid_page?(x)
34
+ self.valid?
35
+ !self.errors_on_page?(x)
36
+ end
37
+
38
+ def first_page_with_errors
39
+ if (i = pages_with_errors.find_index { |x| x == true })
40
+ i + 1
41
+ else
42
+ nil
43
+ end
44
+ end
45
+
46
+ def pages_with_errors
47
+ form.response_fields_by_page.map do |page|
48
+ page.find { |response_field| self.error_for(response_field).present? } ? true : false
49
+ end
50
+ end
51
+
52
+ def errors_on_page?(x)
53
+ pages_with_errors[x - 1] # 0-indexed
54
+ end
55
+
56
+ def responses_column
57
+ @responses_column || 'responses'
58
+ end
59
+
60
+ def responses_column=(x)
61
+ @responses_column = x
11
62
  end
12
63
 
13
- def submit!(skip_validation = false)
14
- return false if !skip_validation && !valid?
64
+ def get_responses
65
+ send(responses_column)
66
+ end
15
67
 
16
- self.audit_responses
68
+ def set_responses(x)
69
+ send("#{responses_column}=", x)
70
+ end
17
71
 
18
- self.update_attributes(
19
- submitted_at: Time.now,
20
- skip_validation: true # don't validate twice
21
- )
72
+ def mark_responses_as_changed!
73
+ send("#{responses_column}_will_change!")
22
74
  end
23
75
 
24
- def unsubmit!
25
- self.update_attributes(
26
- submitted_at: nil,
27
- skip_validation: true
28
- )
76
+ def responses_column_changed?
77
+ send("#{responses_column}_changed?")
29
78
  end
30
79
 
31
- def submitted?
32
- self.submitted_at.present?
80
+ def responses_column_was
81
+ send("#{responses_column}_was")
33
82
  end
34
83
 
35
84
  def value_present?(response_field)
@@ -49,13 +98,8 @@ module Formbuilder
49
98
  return false
50
99
  end
51
100
 
52
- # checkboxes can have no values, yet still need to show up as unchecked
53
- def value_present_or_checkboxes?(response_field)
54
- response_field.field_type == 'checkboxes' || value_present?(response_field)
55
- end
56
-
57
101
  def response_value(response_field)
58
- value = responses && responses[response_field.id.to_s]
102
+ value = get_responses[response_field.id.to_s]
59
103
 
60
104
  if value
61
105
  response_field.serialized ? YAML::load(value) : value
@@ -66,176 +110,75 @@ module Formbuilder
66
110
  end
67
111
  end
68
112
 
69
- def save_responses(response_field_params, response_fields)
70
- self.old_responses = self.responses.try(:clone) || {}
71
- self.responses = {}
113
+ def save_responses(response_field_params, response_fields, opts = {})
114
+ set_responses({}) unless opts[:partial_update]
72
115
 
73
- response_fields.reject { |rf| !rf.input_field }.each do |response_field|
74
- self.save_response(response_field_params.try(:[], response_field.id.to_s), response_field, response_field_params)
116
+ response_fields.select { |rf| rf.input_field }.each do |response_field|
117
+ self.save_response(response_field_params[response_field.id.to_s], response_field, response_field_params)
75
118
  end
76
119
  end
77
120
 
78
121
  def save_response(raw_value, response_field, response_field_params = {})
79
- value = case response_field.field_type
80
- when "checkboxes"
81
- # transform checkboxes into {label => on/off} pairs
82
- values = {}
83
-
84
- (response_field[:field_options]["options"] || []).each_with_index do |option, index|
85
- label = response_field.field_options["options"][index]["label"]
86
- values[option["label"]] = raw_value && raw_value[index.to_s] == "on"
87
- end
88
-
89
- if raw_value && raw_value['other_checkbox'] == 'on'
90
- values['Other'] = raw_value['other']
91
- else
92
- values.delete('Other') # @todo this might cause unexpected behavior to the user. we should hide/show the other field in the frontend, too
93
- end
94
-
95
- # Save 'other' value
96
- responses["#{response_field.id}_other"] = raw_value && raw_value['other_checkbox'] == 'on' ?
97
- true :
98
- nil
99
-
100
- values
101
-
102
- when "file"
103
- # if the file is already uploaded and we're not uploading another,
104
- # be sure to keep it
105
- if raw_value.blank?
106
- if old_responses && old_responses[response_field.id.to_s]
107
- old_responses[response_field.id.to_s]
108
- end
109
- else
110
- remove_entry_attachment(responses[response_field.id.to_s]) if responses
111
- attachment = EntryAttachment.create(upload: raw_value)
112
- attachment.id
113
- end
114
- when "radio"
115
- # Save 'other' value
116
- responses["#{response_field.id}_other"] = raw_value == 'Other' ?
117
- response_field_params["#{response_field.id}_other"] :
118
- nil
119
-
120
- raw_value
121
- else
122
- raw_value
123
- end
124
-
125
- self.responses ||= {}
122
+ value = response_field.transform_raw_value(raw_value, self, response_field_params: response_field_params)
126
123
 
127
124
  if value.present?
128
- self.responses["#{response_field.id}"] = response_field.serialized ? value.to_yaml : value
129
- calculate_sortable_value(response_field, value)
125
+ get_responses["#{response_field.id}"] = response_field.serialized ? value.to_yaml : value
126
+ get_responses["#{response_field.id}_sortable_value"] = response_field.sortable_value(value)
127
+ else
128
+ get_responses.delete("#{response_field.id}")
129
+ get_responses.delete("#{response_field.id}_sortable_value")
130
130
  end
131
131
 
132
- self.responses_will_change! # hack to make sure column is marked as dirty
132
+ mark_responses_as_changed!
133
133
  end
134
134
 
135
135
  def destroy_response(response_field)
136
- case response_field.field_type
137
- when "file"
138
- self.remove_entry_attachment(responses[response_field.id.to_s])
139
- end
140
-
136
+ response_field.before_response_destroyed(self)
141
137
  id = response_field.id.to_s
142
- new_responses = self.responses.reject { |k, v| k.in?([id, "#{id}_sortable_value"]) }
143
- self.responses = new_responses
144
-
145
- self.responses_will_change! # hack to make sure column is marked as dirty
146
- end
147
-
148
- def remove_entry_attachment(entry_attachment_id)
149
- return unless entry_attachment_id.present?
150
- EntryAttachment.where(id: entry_attachment_id).first.try(:destroy)
138
+ set_responses get_responses.reject { |k, v| k.in?([id, "#{id}_sortable_value"]) }
139
+ mark_responses_as_changed!
151
140
  end
152
141
 
153
142
  def error_for(response_field)
154
- (self.errors.messages[:"responses_#{response_field.id}"] || [])[0]
143
+ Array(self.errors.messages[:"#{responses_column}_#{response_field.id}"]).first
155
144
  end
156
145
 
157
146
  def calculate_responses_text
158
- return unless self.respond_to?(:"responses_text=")
159
- selected_responses = self.responses.select { |k, v| Integer(k) rescue nil }
160
- self.responses_text = selected_responses.values.join(' ')
147
+ return unless self.respond_to?(:"#{responses_column}_text=")
148
+ self.send(:"#{responses_column}_text=", get_responses.select { |k, v| Integer(k) rescue nil }.values.join(' '))
161
149
  end
162
150
 
163
- # useful when migrating
151
+ # for manual use, maybe when migrating
164
152
  def calculate_sortable_values
165
- response_fieldable.response_fields.reject { |rf| !rf.input_field }.each do |response_field|
166
- calculate_sortable_value(response_field, response_value(response_field))
153
+ response_fieldable.input_fields.each do |response_field|
154
+ if (x = response_value(response_field)).present?
155
+ get_responses["#{response_field.id}_sortable_value"] = response_field.sortable_value(x)
156
+ end
167
157
  end
168
158
 
169
- self.responses_will_change! # hack to make sure column is marked as dirty
159
+ mark_responses_as_changed!
170
160
  end
171
161
 
172
- def calculate_additional_info
173
- response_fieldable.response_fields.reject { |rf| !rf.input_field }.each do |response_field|
174
- value = response_value(response_field)
175
- next unless value.present?
162
+ # Normalizations get run before validation.
163
+ def normalize_responses
164
+ return if form.blank?
176
165
 
177
- case response_field.field_type
178
- when 'address'
179
- begin
180
- coords = Geocoder.coordinates("#{value['street']} #{value['city']} #{value['state']} #{value['zipcode']} #{value['country']}")
181
- self.responses["#{response_field.id}_x"] = coords[0]
182
- self.responses["#{response_field.id}_y"] = coords[1]
183
- rescue
184
- self.responses["#{response_field.id}_x"] = nil
185
- self.responses["#{response_field.id}_y"] = nil
186
- end
166
+ form.response_fields.each do |response_field|
167
+ if (x = self.response_value(response_field))
168
+ response_field.normalize_response(x, get_responses)
187
169
  end
188
170
  end
189
- end
190
-
191
- # def normalize_responses
192
- # response_fieldable.response_fields.reject { |rf| !rf.input_field }.each do |response_field|
193
- # value = response_value(response_field)
194
- # next unless value.present?
195
171
 
196
- # case response_field.field_type
197
- # when 'website'
198
- # unless value[/^http:\/\//] || value[/^https:\/\//]
199
- # save_response("http://#{value}", response_field)
200
- # end
201
- # end
202
- # end
203
- # end
172
+ mark_responses_as_changed!
173
+ end
204
174
 
175
+ # Audits get run explicitly.
205
176
  def audit_responses
206
177
  form.response_fields.each do |response_field|
207
- response_field.audit_response(self.response_value(response_field), self.responses)
178
+ response_field.audit_response(self.response_value(response_field), get_responses)
208
179
  end
209
- end
210
180
 
211
- def calculate_sortable_value(response_field, value)
212
- return unless value.present?
213
-
214
- self.responses["#{response_field.id}_sortable_value"] = case response_field.field_type
215
- when "date"
216
- ['year', 'month', 'day'].each { |x| return 0 unless value[x] && !value[x].blank? }
217
- DateTime.new(value['year'].to_i, value['month'].to_i, value['day'].to_i).to_i rescue 0
218
- when "time"
219
- hours = value['hours'].to_i
220
- hours += 12 if value['am_pm'] && value['am_pm'] == 'PM'
221
- (hours*60*60) + (value['minutes'].to_i * 60) + value['seconds'].to_i
222
- when "file"
223
- value ? 1 : 0
224
- when "checkboxes"
225
- calculate_sortable_value_for_checkboxes(response_field, value)
226
- return nil
227
- when "price"
228
- "#{value['dollars'] || '0'}.#{value['cents'] || '0'}".to_f
229
- else
230
- # do we really need to sort more than the first 10 characters of a string?
231
- value[0..10]
232
- end
233
- end
234
-
235
- def calculate_sortable_value_for_checkboxes(response_field, value)
236
- (response_field.field_options['options'] || []).each do |option|
237
- self.responses["#{response_field.id}_sortable_values_#{option['label']}"] = value[option['label']]
238
- end
181
+ mark_responses_as_changed!
239
182
  end
240
183
 
241
184
  end
@@ -6,15 +6,22 @@ module Formbuilder
6
6
  @record = record
7
7
 
8
8
  # I guess it's valid if there's no form?
9
- return unless record.form
10
-
11
- # It's also valid if it has already been submitted
12
- return if record.submitted_at_was
9
+ return if record.form.blank?
13
10
 
14
11
  # we can also skip validation by setting this flag
15
12
  return if record.skip_validation
16
13
 
17
- record.form.response_fields.not_admin_only.reject { |rf| !rf.input_field }.each do |response_field|
14
+ record.form.show_blind = true
15
+
16
+ if record.only_validate_page
17
+ query = record.form.response_fields_for_page(record.only_validate_page)
18
+ else
19
+ query = record.form.filtered_response_fields
20
+ end
21
+
22
+ query = query.reject { |rf| !rf.input_field }
23
+
24
+ query.each do |response_field|
18
25
  @response_field = response_field
19
26
  @value = @record.response_value(@response_field)
20
27
 
@@ -37,7 +44,7 @@ module Formbuilder
37
44
  private
38
45
  def add_error(msg)
39
46
  return unless msg.present?
40
- @record.errors["responses_#{@response_field.id}"] << msg
47
+ @record.errors["#{@record.responses_column}_#{@response_field.id}"] << msg
41
48
  end
42
49
 
43
50
  def run_validation(method_name)