formbuilder-rb 0.0.1

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 (35) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +24 -0
  4. data/app/controllers/formbuilder/forms_controller.rb +53 -0
  5. data/app/models/formbuilder/entry_attachment.rb +15 -0
  6. data/app/models/formbuilder/form.rb +12 -0
  7. data/app/models/formbuilder/response_field.rb +85 -0
  8. data/app/models/formbuilder/response_field_address.rb +349 -0
  9. data/app/models/formbuilder/response_field_checkboxes.rb +68 -0
  10. data/app/models/formbuilder/response_field_date.rb +46 -0
  11. data/app/models/formbuilder/response_field_dropdown.rb +30 -0
  12. data/app/models/formbuilder/response_field_email.rb +26 -0
  13. data/app/models/formbuilder/response_field_file.rb +44 -0
  14. data/app/models/formbuilder/response_field_number.rb +33 -0
  15. data/app/models/formbuilder/response_field_paragraph.rb +21 -0
  16. data/app/models/formbuilder/response_field_price.rb +47 -0
  17. data/app/models/formbuilder/response_field_radio.rb +52 -0
  18. data/app/models/formbuilder/response_field_section_break.rb +17 -0
  19. data/app/models/formbuilder/response_field_text.rb +16 -0
  20. data/app/models/formbuilder/response_field_time.rb +53 -0
  21. data/app/models/formbuilder/response_field_website.rb +31 -0
  22. data/app/uploaders/formbuilder/entry_attachment_uploader.rb +26 -0
  23. data/db/migrate/20130924185726_create_formbuilder_forms.rb +10 -0
  24. data/db/migrate/20130924185814_create_formbuilder_response_fields.rb +16 -0
  25. data/db/migrate/20130924185815_create_formbuilder_entry_attachments.rb +10 -0
  26. data/lib/formbuilder/engine.rb +5 -0
  27. data/lib/formbuilder/entry.rb +242 -0
  28. data/lib/formbuilder/entry_renderer.rb +47 -0
  29. data/lib/formbuilder/entry_table_renderer.rb +58 -0
  30. data/lib/formbuilder/entry_validator.rb +107 -0
  31. data/lib/formbuilder/form_renderer.rb +102 -0
  32. data/lib/formbuilder/version.rb +3 -0
  33. data/lib/formbuilder.rb +12 -0
  34. data/lib/tasks/formbuilder_tasks.rake +4 -0
  35. metadata +258 -0
@@ -0,0 +1,30 @@
1
+ module Formbuilder
2
+ class ResponseFieldDropdown < ResponseField
3
+
4
+ after_initialize -> {
5
+ @options_field = true
6
+ @field_type = 'dropdown'
7
+ }
8
+
9
+ def render_input(value, opts = {})
10
+ options = ""
11
+ options += "<option></option>" if self[:field_options]['include_blank_option']
12
+
13
+ Array(self[:field_options]['options']).each_with_index do |option, i|
14
+ selected = (value == option["label"]) || (value.blank? && option["checked"])
15
+ options += "<option value='#{option["label"]}' #{selected ? 'selected' : ''}>#{option['label']}</option>"
16
+ end
17
+
18
+ """
19
+ <select name='response_fields[#{self[:id]}]'>
20
+ #{options}
21
+ </select>
22
+ """
23
+ end
24
+
25
+ def render_entry(value, opts = {})
26
+ value
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ module Formbuilder
2
+ class ResponseFieldEmail < ResponseField
3
+
4
+ include ActionView::Helpers::TagHelper
5
+
6
+ after_initialize -> {
7
+ @field_type = 'email'
8
+ }
9
+
10
+ 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)
13
+ end
14
+
15
+ def render_entry(value, opts = {})
16
+ "<a href='mailto:#{value}'>#{value}</a>"
17
+ end
18
+
19
+ def validate_response(value)
20
+ unless value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
21
+ "isn't a valid email address."
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,44 @@
1
+ module Formbuilder
2
+ class ResponseFieldFile < ResponseField
3
+
4
+ after_initialize -> {
5
+ @field_type = 'file'
6
+ }
7
+
8
+ # @todo dropzone?
9
+ def render_input(value, opts = {})
10
+ attachment = value && EntryAttachment.find(value)
11
+
12
+ """
13
+ <span class='existing-filename'>#{attachment.try(:upload).try(:file).try(:filename).try(:gsub, /\?.*$/, '')}</span>
14
+ <input type='file' name='response_fields[#{self[:id]}]' />
15
+ """
16
+ end
17
+
18
+ def render_entry(value, opts = {})
19
+ attachment = EntryAttachment.find(value)
20
+
21
+ return unless attachment
22
+
23
+ str = ""
24
+
25
+ str += """
26
+ <a href='#{attachment.upload.url}' target='_blank'>
27
+ """
28
+
29
+ if attachment.upload.send(:active_versions).include?(:thumb)
30
+ str += """
31
+ <img src='#{attachment.upload.thumb.url}' /><br />
32
+ """
33
+ end
34
+
35
+ str +="""
36
+ #{attachment.upload.try(:file).try(:filename).try(:gsub, /\?.*$/, '')}
37
+ </a>
38
+ """
39
+
40
+ str
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ module Formbuilder
2
+ class ResponseFieldNumber < ResponseField
3
+
4
+ include ActionView::Helpers::TagHelper
5
+
6
+ after_initialize -> {
7
+ @sort_as_numeric = true
8
+ @field_type = 'number'
9
+ }
10
+
11
+ def render_input(value, opts = {})
12
+ str = tag(:input, type: 'text', name: "response_fields[#{self.id}]",
13
+ class: "rf-size-#{self[:field_options]['size']}", value: value)
14
+
15
+ if (units = self[:field_options]['units'].presence)
16
+ str += "<span class='units'>#{units}</span>".html_safe
17
+ end
18
+
19
+ str
20
+ end
21
+
22
+ def render_entry(value, opts = {})
23
+ value
24
+ end
25
+
26
+ def validate_response(value)
27
+ unless (Float(value) rescue nil)
28
+ "isn't a valid number."
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ module Formbuilder
2
+ class ResponseFieldParagraph < ResponseField
3
+
4
+ include ActionView::Helpers::TagHelper
5
+ include ActionView::Context
6
+
7
+ after_initialize -> {
8
+ @field_type = 'paragraph'
9
+ }
10
+
11
+ def render_input(value, opts = {})
12
+ content_tag(:textarea, name: "response_fields[#{self.id}]", class: "rf-size-#{self[:field_options]['size']}",
13
+ data: self.length_validations) { value }
14
+ end
15
+
16
+ def render_entry(value, opts = {})
17
+ ActionController::Base.helpers.simple_format(value)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ module Formbuilder
2
+ class ResponseFieldPrice < ResponseField
3
+
4
+ after_initialize -> {
5
+ @serialized = true
6
+ @sort_as_numeric = true
7
+ @field_type = 'price'
8
+ }
9
+
10
+ def render_input(value, opts = {})
11
+ """
12
+ <div class='input-line'>
13
+ <span class='above-line'>$</span>
14
+
15
+ <span class='dollars'>
16
+ <input type='text' name='response_fields[#{self[:id]}][dollars]' value='#{value['dollars']}' />
17
+ <label>Dollars</label>
18
+ </span>
19
+
20
+ <span class='above-line'>.</span>
21
+
22
+ <span class='cents'>
23
+ <input type='text' name='response_fields[#{self[:id]}][cents]' value='#{value['cents']}' maxlength='2' />
24
+ <label>Cents</label>
25
+ </span>
26
+ </div>
27
+ """
28
+ end
29
+
30
+ def render_entry(value, opts = {})
31
+ "$#{sprintf('%.2f', "#{value['dollars']}.#{value['cents']}".to_f)}"
32
+ end
33
+
34
+ # format: [dollars] [cents]
35
+ # only one is required, and it must consist only of numbers
36
+ 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
41
+ end
42
+
43
+ return nil
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,52 @@
1
+ module Formbuilder
2
+ class ResponseFieldRadio < ResponseField
3
+
4
+ after_initialize -> {
5
+ @options_field = true
6
+ @field_type = 'radio'
7
+ }
8
+
9
+ def render_input(value, opts = {})
10
+ value ||= {}
11
+
12
+ str = (self[:field_options]["options"] || []).each_with_index.map do |option, i|
13
+
14
+ checked = (value == option["label"]) || (value.blank? && option["checked"])
15
+
16
+ """
17
+ <label>
18
+ <input type='radio' name='response_fields[#{self[:id]}]' #{checked ? 'checked' : ''} value='#{option['label']}' />
19
+ #{option['label']}
20
+ </label>
21
+ """
22
+ end.join('')
23
+
24
+ if self[:field_options]['include_other_option']
25
+ str += """
26
+ <div class='other-option'>
27
+ <label>
28
+ <input type='radio' name='response_fields[#{self[:id]}]' #{value == 'Other' ? 'checked' : ''} value='Other' />
29
+ Other
30
+ </label>
31
+
32
+ <input type='text' name='response_fields[#{self[:id]}_other]'
33
+ value='#{opts.try(:[], :entry).try(:responses).try(:[], "#{self[:id]}_other")}' />
34
+ </div>
35
+ """
36
+ end
37
+
38
+ str
39
+ end
40
+
41
+ def render_entry(value, opts = {})
42
+ str = value
43
+
44
+ if (other = opts.try(:[], :entry).try(:responses).try(:[], "#{self.id}_other"))
45
+ str += " (#{other})"
46
+ end
47
+
48
+ str
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,17 @@
1
+ module Formbuilder
2
+ class ResponseFieldSectionBreak < ResponseField
3
+
4
+ after_initialize -> {
5
+ @input_field = false
6
+ @field_type = 'section_break'
7
+ }
8
+
9
+ def render_input(value, opts = {})
10
+ """
11
+ <label class='section-name'>#{self[:label]}</label>
12
+ <p>#{self[:field_options]['description']}</p>
13
+ """
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Formbuilder
2
+ class ResponseFieldText < ResponseField
3
+
4
+ include ActionView::Helpers::TagHelper
5
+
6
+ after_initialize -> {
7
+ @field_type = 'text'
8
+ }
9
+
10
+ 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)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,53 @@
1
+ module Formbuilder
2
+ class ResponseFieldTime < ResponseField
3
+
4
+ after_initialize -> {
5
+ @serialized = true
6
+ @sort_as_numeric = true
7
+ @field_type = 'time'
8
+ }
9
+
10
+ def render_input(value, opts = {})
11
+ """
12
+ <div class='input-line'>
13
+ <span class='hours'>
14
+ <input type='text' name='response_fields[#{self[:id]}][hours]' value='#{value['hours']}' maxlength='2' />
15
+ <label>HH</label>
16
+ </span>
17
+
18
+ <span class='above-line'>:</span>
19
+
20
+ <span class='minutes'>
21
+ <input type='text' name='response_fields[#{self[:id]}][minutes]' value='#{value['minutes']}' maxlength='2' />
22
+ <label>MM</label>
23
+ </span>
24
+
25
+ <span class='above-line'>:</span>
26
+
27
+ <span class='seconds'>
28
+ <input type='text' name='response_fields[#{self[:id]}][seconds]' value='#{value['seconds']}' maxlength='2' />
29
+ <label>SS</label>
30
+ </span>
31
+
32
+ <span class='am_pm'>
33
+ <select name='response_fields[#{self[:id]}][am_pm]'>
34
+ <option value='AM' #{value['am_pm'] == "AM" ? 'selected' : ''}>AM</option>
35
+ <option value='PM' #{value['am_pm'] == "PM" ? 'selected' : ''}>PM</option>
36
+ </select>
37
+ </span>
38
+ </div>
39
+ """
40
+ end
41
+
42
+ def render_entry(value, opts = {})
43
+ "#{value['hours']}:#{value['minutes']}#{if !value['seconds'].blank? then ':'+value['seconds'] end} #{value['am_pm']}"
44
+ end
45
+
46
+ def validate_response(value)
47
+ if !value['hours'].to_i.between?(1, 12) || !value['minutes'].to_i.between?(0, 60) || !value['seconds'].to_i.between?(0, 60)
48
+ "isn't a valid time."
49
+ end
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,31 @@
1
+ module Formbuilder
2
+ class ResponseFieldWebsite < ResponseField
3
+
4
+ include ActionView::Helpers::TagHelper
5
+
6
+ after_initialize -> {
7
+ @field_type = 'website'
8
+ }
9
+
10
+ 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://')
13
+ end
14
+
15
+ def render_entry(value, opts = {})
16
+ "<a href='#{value}' target='_blank' rel='nofollow'>#{value}</a>"
17
+ end
18
+
19
+ def validate_response(value)
20
+ require 'uri'
21
+
22
+ # add http if not present
23
+ value = "http://#{value}" unless value[/^http:\/\//] || value[/^https:\/\//]
24
+
25
+ if !(value =~ URI::regexp) # this doesn't really validate, since almost *anything* matches
26
+ "isn't a valid URL."
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ require 'carrierwave/processing/mime_types'
2
+
3
+ module Formbuilder
4
+ class EntryAttachmentUploader < CarrierWave::Uploader::Base
5
+ include CarrierWave::MimeTypes
6
+ include CarrierWave::RMagick
7
+
8
+ @fog_public = false
9
+
10
+ process :set_content_type
11
+
12
+ version :thumb, :if => :image? do
13
+ process resize_to_limit: [250, 250]
14
+ end
15
+
16
+ def store_dir
17
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
18
+ end
19
+
20
+ protected
21
+ def image?(file)
22
+ content_type = file.content_type || model.try(:content_type)
23
+ content_type && content_type.include?('image')
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ class CreateFormbuilderForms < ActiveRecord::Migration
2
+ def change
3
+ create_table :formbuilder_forms do |t|
4
+ t.integer :formable_id
5
+ t.string :formable_type
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ class CreateFormbuilderResponseFields < ActiveRecord::Migration
2
+ def change
3
+ create_table :formbuilder_response_fields do |t|
4
+ t.integer :form_id
5
+ t.text :label
6
+ t.string :type
7
+ t.text :field_options
8
+ t.integer :sort_order
9
+ t.boolean :required, default: false
10
+ t.boolean :blind, default: false
11
+ t.boolean :admin_only, default: false
12
+
13
+ t.timestamps
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ class CreateFormbuilderEntryAttachments < ActiveRecord::Migration
2
+ def change
3
+ create_table :formbuilder_entry_attachments do |t|
4
+ t.string :upload
5
+ t.string :content_type
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ module Formbuilder
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Formbuilder
4
+ end
5
+ end