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
@@ -5,6 +5,7 @@ module Formbuilder
5
5
  @serialized = true
6
6
  @sort_as_numeric = true
7
7
  @field_type = 'price'
8
+ @search_type = 'number'
8
9
  }
9
10
 
10
11
  def render_input(value, opts = {})
@@ -13,7 +14,7 @@ module Formbuilder
13
14
  <span class='above-line'>$</span>
14
15
 
15
16
  <span class='dollars'>
16
- <input type='text' name='response_fields[#{self[:id]}][dollars]' value='#{value['dollars']}' />
17
+ <input type='text' name='response_fields[#{self[:id]}][dollars]' id='response_fields_#{self[:id]}' value='#{value['dollars']}' />
17
18
  <label>Dollars</label>
18
19
  </span>
19
20
 
@@ -34,13 +35,16 @@ module Formbuilder
34
35
  # format: [dollars] [cents]
35
36
  # only one is required, and it must consist only of numbers
36
37
  def validate_response(value)
37
- value.select { |k, v| k.in?(['dollars', 'cents']) && v.present? }.each do |k, v|
38
- unless (Float(v) rescue 0) > 0
39
- return "isn't a valid price."
40
- end
38
+ if value.select { |k, v| k.in?(['dollars', 'cents']) && v.present? }
39
+ .find { |k, v| (Float(v) rescue nil).nil? }
40
+ .present?
41
+
42
+ "isn't a valid price."
41
43
  end
44
+ end
42
45
 
43
- return nil
46
+ def sortable_value(value)
47
+ "#{value['dollars'] || '0'}.#{value['cents'] || '0'}".to_f
44
48
  end
45
49
 
46
50
  end
@@ -4,6 +4,7 @@ module Formbuilder
4
4
  after_initialize -> {
5
5
  @options_field = true
6
6
  @field_type = 'radio'
7
+ @search_type = 'one_option'
7
8
  }
8
9
 
9
10
  def render_input(value, opts = {})
@@ -30,7 +31,7 @@ module Formbuilder
30
31
  </label>
31
32
 
32
33
  <input type='text' name='response_fields[#{self[:id]}_other]'
33
- value='#{opts.try(:[], :entry).try(:responses).try(:[], "#{self[:id]}_other")}' />
34
+ value='#{opts.try(:[], :entry).try(:get_responses).try(:[], "#{self[:id]}_other")}' />
34
35
  </div>
35
36
  """
36
37
  end
@@ -41,12 +42,20 @@ module Formbuilder
41
42
  def render_entry(value, opts = {})
42
43
  str = value
43
44
 
44
- if (other = opts.try(:[], :entry).try(:responses).try(:[], "#{self.id}_other"))
45
+ if (other = opts.try(:[], :entry).try(:get_responses).try(:[], "#{self.id}_other"))
45
46
  str += " (#{other})"
46
47
  end
47
48
 
48
49
  str
49
50
  end
50
51
 
52
+ def transform_raw_value(raw_value, entry, opts = {})
53
+ entry.get_responses["#{self.id}_other"] = (raw_value == 'Other') ?
54
+ opts[:response_field_params]["#{self.id}_other"] :
55
+ nil
56
+
57
+ raw_value
58
+ end
59
+
51
60
  end
52
61
  end
@@ -7,10 +7,20 @@ module Formbuilder
7
7
  }
8
8
 
9
9
  def render_input(value, opts = {})
10
+ str = """
11
+ <div class='section-break-inner section-break-size-#{self[:field_options]['size']}'>
12
+ <div class='section-name'>#{self[:label]}</div>
10
13
  """
11
- <label class='section-name'>#{self[:label]}</label>
12
- <p>#{self[:field_options]['description']}</p>
14
+
15
+ if self[:field_options]['description'].present?
16
+ str += """<p>#{self[:field_options]['description']}</p>"""
17
+ end
18
+
19
+ str += """
20
+ </div>
13
21
  """
22
+
23
+ str
14
24
  end
15
25
 
16
26
  end
@@ -0,0 +1,222 @@
1
+ module Formbuilder
2
+ class ResponseFieldTable < ResponseField
3
+
4
+ include ActionView::Helpers::TagHelper
5
+ include ActionView::Context
6
+
7
+ after_initialize -> {
8
+ @serialized = true
9
+ @field_type = 'table'
10
+ @search_type = 'table'
11
+ }
12
+
13
+ def columns_array
14
+ Array(self[:field_options]['columns']).map { |column| column['label'] }
15
+ end
16
+
17
+ def render_input(value, opts = {})
18
+ value ||= {}
19
+
20
+ str = """
21
+ <table class='border_all'>
22
+ <thead>
23
+ <tr>
24
+ """
25
+
26
+ columns_array.each_with_index do |column, i|
27
+ str += """<th>#{column}</th>"""
28
+ end
29
+
30
+ str += """
31
+ </tr>
32
+ </thead>
33
+ <tbody>
34
+ """
35
+
36
+ [self[:field_options]['minrows'].to_i, 1, number_of_saved_rows(value)].max.times do |i|
37
+ str += """
38
+ <tr>
39
+ """
40
+
41
+ columns_array.each_with_index do |column, j|
42
+ preset_val = Hash(self.field_options['preset_values']).try(:[], column).try(:[], i)
43
+ val = preset_val.presence || value[column].try(:[], i)
44
+
45
+ str += """
46
+ <td>
47
+ <textarea name='response_fields[#{self[:id]}][#{j}][]'
48
+ #{preset_val.present? ? 'readonly' : ''}
49
+ data-col='#{j}'
50
+ data-row='#{i}'
51
+ rows='1'>#{val}</textarea>
52
+ </td>
53
+ """
54
+ end
55
+
56
+ str += """
57
+ </tr>
58
+ """
59
+ end
60
+
61
+ str += """
62
+ <script class='template' type='text/template'>
63
+ <tr>
64
+ """
65
+
66
+ columns_array.each_with_index do |_, j|
67
+ str += """
68
+ <td>
69
+ <textarea name='response_fields[#{self[:id]}][#{j}][]' rows='1'></textarea>
70
+ </td>
71
+ """
72
+ end
73
+
74
+ str += """
75
+ </tr>
76
+ </script>
77
+ """
78
+
79
+ str += """
80
+ </tbody>
81
+ """
82
+
83
+ if self[:field_options]['column_totals']
84
+ str += """
85
+ <tfoot>
86
+ """
87
+
88
+ columns_array.each_with_index do |column, j|
89
+ str += """
90
+ <td><span></span></td>
91
+ """
92
+ end
93
+
94
+ str += """
95
+ </tfoot>
96
+ """
97
+ end
98
+
99
+ str += """
100
+ </table>
101
+ """
102
+
103
+ str += """
104
+ <div class='margin_th margin_bh'>
105
+ <a class='uppercase add_another_row'><i class='icon-plus-sign'></i> Add another row</a>
106
+ </div>
107
+ """
108
+
109
+ str
110
+ end
111
+
112
+ def render_entry(value, opts = {})
113
+ value ||= {}
114
+
115
+ str = """
116
+ <table class='border_all'>
117
+ <thead>
118
+ <tr>
119
+ """
120
+
121
+ columns_array.each_with_index do |column, i|
122
+ str += """<th>#{column}</th>"""
123
+ end
124
+
125
+ str += """
126
+ </tr>
127
+ </thead>
128
+ <tbody>
129
+ """
130
+
131
+ number_of_saved_rows(value).times do |i|
132
+ str += """
133
+ <tr>
134
+ """
135
+
136
+ columns_array.each_with_index do |column, j|
137
+ str += """
138
+ <td>#{value[column].try(:[], i)}</td>
139
+ """
140
+ end
141
+
142
+ str += """
143
+ </tr>
144
+ """
145
+ end
146
+
147
+ str += """
148
+ </tbody>
149
+ """
150
+
151
+ if self[:field_options]['column_totals']
152
+ str += """
153
+ <tfoot>
154
+ """
155
+
156
+ columns_array.each_with_index do |column, j|
157
+ total = opts.try(:[], :entry).try(:get_responses).try(:[], "#{self.id}_sum_#{column}")
158
+
159
+ str += """
160
+ <td><span>#{total.to_f > 0 ? total : ''}</span></td>
161
+ """
162
+ end
163
+
164
+ str += """
165
+ </tfoot>
166
+ """
167
+ end
168
+
169
+ str += """
170
+ </table>
171
+ """
172
+
173
+ str
174
+ end
175
+
176
+ def render_entry_text(value, opts = {})
177
+ (value || {}).map do |k, v|
178
+ "#{k}: " + Array(v).join(', ')
179
+ end.join("; ")
180
+ end
181
+
182
+ def sortable_value(value)
183
+ nil # see :normalize_response for override
184
+ end
185
+
186
+ def number_of_saved_rows(value)
187
+ value.try(:first).try(:[], 1).try(:length) || 0
188
+ end
189
+
190
+ def normalize_response(value, all_responses)
191
+ # Calculate sums and store in the hstore
192
+ columns_array.each do |column|
193
+ all_responses["#{self.id}_sum_#{column}"] = Array(value[column]).map do |x|
194
+ x.gsub(/\$?,?/, '').to_f
195
+ end.inject(:+)
196
+ end
197
+ end
198
+
199
+ def transform_raw_value(raw_value, entry, opts = {})
200
+ raw_value ||= {}
201
+
202
+ {}.tap do |h|
203
+ columns_array.each_with_index do |column, index|
204
+ h[column] = raw_value[index.to_s]
205
+ end
206
+
207
+ # Iterate through each row, and remove ones without any values.
208
+ i = Array(h.first[1]).length - 1
209
+ while i >= 0
210
+ unless h.find { |k, v| v[i].present? }
211
+ h.each do |k, v|
212
+ v.delete_at(i)
213
+ end
214
+ end
215
+
216
+ i = i - 1
217
+ end
218
+ end
219
+ end
220
+
221
+ end
222
+ end
@@ -5,11 +5,18 @@ module Formbuilder
5
5
 
6
6
  after_initialize -> {
7
7
  @field_type = 'text'
8
+ @search_type = 'text'
8
9
  }
9
10
 
10
11
  def render_input(value, opts = {})
11
- tag(:input, type: 'text', name: "response_fields[#{self.id}]", class: "rf-size-#{self[:field_options]['size']}",
12
- data: self.length_validations, value: value)
12
+ tag(
13
+ :input,
14
+ type: 'text',
15
+ name: "response_fields[#{self.id}]",
16
+ id: "response_fields_#{self.id}",
17
+ class: "rf-size-#{self[:field_options]['size']}",
18
+ data: self.length_validations, value: value
19
+ )
13
20
  end
14
21
 
15
22
  end
@@ -5,13 +5,14 @@ module Formbuilder
5
5
  @serialized = true
6
6
  @sort_as_numeric = true
7
7
  @field_type = 'time'
8
+ @search_type = 'time'
8
9
  }
9
10
 
10
11
  def render_input(value, opts = {})
11
12
  """
12
13
  <div class='input-line'>
13
14
  <span class='hours'>
14
- <input type='text' name='response_fields[#{self[:id]}][hours]' value='#{value['hours']}' maxlength='2' />
15
+ <input type='text' name='response_fields[#{self[:id]}][hours]' id='response_fields_#{self[:id]}' value='#{value['hours']}' maxlength='2' />
15
16
  <label>HH</label>
16
17
  </span>
17
18
 
@@ -49,5 +50,11 @@ module Formbuilder
49
50
  end
50
51
  end
51
52
 
53
+ def sortable_value(value)
54
+ hours = value['hours'].to_i
55
+ hours += 12 if value['am_pm'] && value['am_pm'] == 'PM'
56
+ (hours*60*60) + (value['minutes'].to_i * 60) + value['seconds'].to_i
57
+ end
58
+
52
59
  end
53
60
  end
@@ -5,17 +5,29 @@ module Formbuilder
5
5
 
6
6
  after_initialize -> {
7
7
  @field_type = 'website'
8
+ @search_type = 'text'
8
9
  }
9
10
 
10
11
  def render_input(value, opts = {})
11
- tag(:input, type: 'text', name: "response_fields[#{self.id}]", class: "rf-size-#{self[:field_options]['size']}",
12
- value: value, placeholder: 'http://')
12
+ tag(
13
+ :input,
14
+ type: 'text',
15
+ name: "response_fields[#{self.id}]",
16
+ id: "response_fields_#{self.id}",
17
+ class: "rf-size-#{self[:field_options]['size']}",
18
+ value: value,
19
+ placeholder: 'http://'
20
+ )
13
21
  end
14
22
 
15
23
  def render_entry(value, opts = {})
16
24
  "<a href='#{value}' target='_blank' rel='nofollow'>#{value}</a>"
17
25
  end
18
26
 
27
+ def render_entry_text(value, opts = {})
28
+ value
29
+ end
30
+
19
31
  def validate_response(value)
20
32
  require 'uri'
21
33
 
@@ -27,5 +39,17 @@ module Formbuilder
27
39
  end
28
40
  end
29
41
 
42
+ def normalize_response(value, all_responses)
43
+ return if value.blank?
44
+
45
+ unless value[/^http:\/\//] || value[/^https:\/\//]
46
+ all_responses[self.id.to_s] = "http://#{value}"
47
+ end
48
+ end
49
+
50
+ def sortable_value(value)
51
+ value[0..20]
52
+ end
53
+
30
54
  end
31
55
  end
@@ -8,6 +8,7 @@ module Formbuilder
8
8
  @fog_public = false
9
9
 
10
10
  process :set_content_type
11
+ process :save_content_type_and_size_in_model
11
12
 
12
13
  version :thumb, :if => :image? do
13
14
  process resize_to_limit: [250, 250]
@@ -17,10 +18,19 @@ module Formbuilder
17
18
  "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
18
19
  end
19
20
 
21
+ def raw_filename
22
+ @model.read_attribute(:upload)
23
+ end
24
+
20
25
  protected
21
26
  def image?(file)
22
- content_type = file.content_type || model.try(:content_type)
27
+ content_type = file.try(:content_type) || model.try(:content_type)
23
28
  content_type && content_type.include?('image')
24
29
  end
30
+
31
+ def save_content_type_and_size_in_model
32
+ model.content_type = file.try(:content_type)
33
+ model.file_size = file.try(:size)
34
+ end
25
35
  end
26
36
  end