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.
- checksums.yaml +7 -0
 - data/MIT-LICENSE +20 -0
 - data/Rakefile +24 -0
 - data/app/controllers/formbuilder/forms_controller.rb +53 -0
 - data/app/models/formbuilder/entry_attachment.rb +15 -0
 - data/app/models/formbuilder/form.rb +12 -0
 - data/app/models/formbuilder/response_field.rb +85 -0
 - data/app/models/formbuilder/response_field_address.rb +349 -0
 - data/app/models/formbuilder/response_field_checkboxes.rb +68 -0
 - data/app/models/formbuilder/response_field_date.rb +46 -0
 - data/app/models/formbuilder/response_field_dropdown.rb +30 -0
 - data/app/models/formbuilder/response_field_email.rb +26 -0
 - data/app/models/formbuilder/response_field_file.rb +44 -0
 - data/app/models/formbuilder/response_field_number.rb +33 -0
 - data/app/models/formbuilder/response_field_paragraph.rb +21 -0
 - data/app/models/formbuilder/response_field_price.rb +47 -0
 - data/app/models/formbuilder/response_field_radio.rb +52 -0
 - data/app/models/formbuilder/response_field_section_break.rb +17 -0
 - data/app/models/formbuilder/response_field_text.rb +16 -0
 - data/app/models/formbuilder/response_field_time.rb +53 -0
 - data/app/models/formbuilder/response_field_website.rb +31 -0
 - data/app/uploaders/formbuilder/entry_attachment_uploader.rb +26 -0
 - data/db/migrate/20130924185726_create_formbuilder_forms.rb +10 -0
 - data/db/migrate/20130924185814_create_formbuilder_response_fields.rb +16 -0
 - data/db/migrate/20130924185815_create_formbuilder_entry_attachments.rb +10 -0
 - data/lib/formbuilder/engine.rb +5 -0
 - data/lib/formbuilder/entry.rb +242 -0
 - data/lib/formbuilder/entry_renderer.rb +47 -0
 - data/lib/formbuilder/entry_table_renderer.rb +58 -0
 - data/lib/formbuilder/entry_validator.rb +107 -0
 - data/lib/formbuilder/form_renderer.rb +102 -0
 - data/lib/formbuilder/version.rb +3 -0
 - data/lib/formbuilder.rb +12 -0
 - data/lib/tasks/formbuilder_tasks.rake +4 -0
 - metadata +258 -0
 
| 
         @@ -0,0 +1,242 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Formbuilder
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Entry
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                included do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_accessor :old_responses
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_accessor :skip_validation
         
     | 
| 
      
 8 
     | 
    
         
            +
                  validates_with Formbuilder::EntryValidator
         
     | 
| 
      
 9 
     | 
    
         
            +
                  before_save :calculate_responses_text, if: :responses_changed?
         
     | 
| 
      
 10 
     | 
    
         
            +
                  include ActionView::Helpers::TextHelper
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def submit!(skip_validation = false)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  return false if !skip_validation && !valid?
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  self.audit_responses
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  self.update_attributes(
         
     | 
| 
      
 19 
     | 
    
         
            +
                    submitted_at: Time.now,
         
     | 
| 
      
 20 
     | 
    
         
            +
                    skip_validation: true # don't validate twice
         
     | 
| 
      
 21 
     | 
    
         
            +
                  )
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def unsubmit!
         
     | 
| 
      
 25 
     | 
    
         
            +
                  self.update_attributes(
         
     | 
| 
      
 26 
     | 
    
         
            +
                    submitted_at: nil,
         
     | 
| 
      
 27 
     | 
    
         
            +
                    skip_validation: true
         
     | 
| 
      
 28 
     | 
    
         
            +
                  )
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def submitted?
         
     | 
| 
      
 32 
     | 
    
         
            +
                  self.submitted_at.present?
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def value_present?(response_field)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  value = self.response_value(response_field)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  # value isn't blank (ignore hashes)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  return true if (value && value.present? && !value.is_a?(Hash))
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  # no options are available
         
     | 
| 
      
 42 
     | 
    
         
            +
                  return true if (response_field.options_field && Array(response_field.field_options["options"]).empty?)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # there is at least one value (for hashes)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # reject select fields
         
     | 
| 
      
 46 
     | 
    
         
            +
                  return true if (value.is_a?(Hash) && value.reject { |k, v| k.in? ['am_pm', 'country'] }.find { |k, v| v.present? })
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  # otherwise, it's not present
         
     | 
| 
      
 49 
     | 
    
         
            +
                  return false
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 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 
     | 
    
         
            +
                def response_value(response_field)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  value = responses && responses[response_field.id.to_s]
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  if value
         
     | 
| 
      
 61 
     | 
    
         
            +
                    response_field.serialized ? YAML::load(value) : value
         
     | 
| 
      
 62 
     | 
    
         
            +
                  elsif !value && response_field.serialized && response_field.field_type != 'checkboxes'
         
     | 
| 
      
 63 
     | 
    
         
            +
                    {}
         
     | 
| 
      
 64 
     | 
    
         
            +
                  else # for checkboxes, we need to know the difference between no value and none selected
         
     | 
| 
      
 65 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                def save_responses(response_field_params, response_fields)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  self.old_responses = self.responses.try(:clone) || {}
         
     | 
| 
      
 71 
     | 
    
         
            +
                  self.responses = {}
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 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)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                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 ||= {}
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  if value.present?
         
     | 
| 
      
 128 
     | 
    
         
            +
                    self.responses["#{response_field.id}"] = response_field.serialized ? value.to_yaml : value
         
     | 
| 
      
 129 
     | 
    
         
            +
                    calculate_sortable_value(response_field, value)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                  self.responses_will_change! # hack to make sure column is marked as dirty
         
     | 
| 
      
 133 
     | 
    
         
            +
                end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 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 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                  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)
         
     | 
| 
      
 151 
     | 
    
         
            +
                end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                def error_for(response_field)
         
     | 
| 
      
 154 
     | 
    
         
            +
                  (self.errors.messages[:"responses_#{response_field.id}"] || [])[0]
         
     | 
| 
      
 155 
     | 
    
         
            +
                end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                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(' ')
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                # useful when migrating
         
     | 
| 
      
 164 
     | 
    
         
            +
                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))
         
     | 
| 
      
 167 
     | 
    
         
            +
                  end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                  self.responses_will_change! # hack to make sure column is marked as dirty
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 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?
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 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
         
     | 
| 
      
 187 
     | 
    
         
            +
                    end
         
     | 
| 
      
 188 
     | 
    
         
            +
                  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 
     | 
    
         
            +
             
     | 
| 
      
 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
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                def audit_responses
         
     | 
| 
      
 206 
     | 
    
         
            +
                  form.response_fields.each do |response_field|
         
     | 
| 
      
 207 
     | 
    
         
            +
                    response_field.audit_response(self.response_value(response_field), self.responses)
         
     | 
| 
      
 208 
     | 
    
         
            +
                  end
         
     | 
| 
      
 209 
     | 
    
         
            +
                end
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 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
         
     | 
| 
      
 239 
     | 
    
         
            +
                end
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
              end
         
     | 
| 
      
 242 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Formbuilder
         
     | 
| 
      
 2 
     | 
    
         
            +
              class EntryRenderer
         
     | 
| 
      
 3 
     | 
    
         
            +
                include ActionView::Context
         
     | 
| 
      
 4 
     | 
    
         
            +
                include ActionView::Helpers::TagHelper
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(entry, form, opts = {})
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @entry, @form = entry, form
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @options = opts # merge defaults?
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def fields
         
     | 
| 
      
 12 
     | 
    
         
            +
                  return_fields = @form.response_fields.reject { |rf| !rf.input_field }
         
     | 
| 
      
 13 
     | 
    
         
            +
                  return_fields.reject! { |rf| rf.blind? } unless @options[:show_blind]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  return_fields
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def to_html
         
     | 
| 
      
 18 
     | 
    
         
            +
                  content_tag 'dl', class: 'entry-dl' do
         
     | 
| 
      
 19 
     | 
    
         
            +
                    fields.map do |rf|
         
     | 
| 
      
 20 
     | 
    
         
            +
                      """
         
     | 
| 
      
 21 
     | 
    
         
            +
                        <dt>#{field_labels(rf)}</dt>
         
     | 
| 
      
 22 
     | 
    
         
            +
                        <dd>
         
     | 
| 
      
 23 
     | 
    
         
            +
                          #{field_value(rf) || no_value}
         
     | 
| 
      
 24 
     | 
    
         
            +
                        </dd>
         
     | 
| 
      
 25 
     | 
    
         
            +
                      """
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end.join('').html_safe
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def field_labels(rf)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  """
         
     | 
| 
      
 32 
     | 
    
         
            +
                    #{rf.label}
         
     | 
| 
      
 33 
     | 
    
         
            +
                    #{rf.blind? ? '<span class="label">Blind</span>' : ''}
         
     | 
| 
      
 34 
     | 
    
         
            +
                    #{rf.admin_only? ? '<span class="label">Admin Only</span>' : ''}
         
     | 
| 
      
 35 
     | 
    
         
            +
                  """
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def no_value
         
     | 
| 
      
 39 
     | 
    
         
            +
                  "<span class='no-response'>No response</span>"
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def field_value(rf)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  value = @entry.response_value(rf)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  rf.render_entry(value, entry: @entry)
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Formbuilder
         
     | 
| 
      
 2 
     | 
    
         
            +
              class EntryTableRenderer < EntryRenderer
         
     | 
| 
      
 3 
     | 
    
         
            +
                # include ActionView::Context
         
     | 
| 
      
 4 
     | 
    
         
            +
                # include ActionView::Helpers::TagHelper
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # def initialize(entry, form, opts = {})
         
     | 
| 
      
 7 
     | 
    
         
            +
                #   @entry, @form = entry, form
         
     | 
| 
      
 8 
     | 
    
         
            +
                #   @options = opts # merge defaults?
         
     | 
| 
      
 9 
     | 
    
         
            +
                # end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # def fields
         
     | 
| 
      
 12 
     | 
    
         
            +
                #   return_fields = @form.response_fields.reject { |rf| rf.non_input_field? }
         
     | 
| 
      
 13 
     | 
    
         
            +
                #   return_fields.reject! { |rf| rf.blind? } unless @options[:show_blind]
         
     | 
| 
      
 14 
     | 
    
         
            +
                #   return_fields
         
     | 
| 
      
 15 
     | 
    
         
            +
                # end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def to_html
         
     | 
| 
      
 18 
     | 
    
         
            +
                  content_tag 'dl', class: 'entry-dl' do
         
     | 
| 
      
 19 
     | 
    
         
            +
                    fields.map do |rf|
         
     | 
| 
      
 20 
     | 
    
         
            +
                      field_value(rf)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end.join('').html_safe
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def field_value(rf)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  value = @entry.response_value(rf)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  rf.field_class.render_entry_for_table(rf, value, entry: @entry)
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                # def field_labels(rf)
         
     | 
| 
      
 31 
     | 
    
         
            +
                #   """
         
     | 
| 
      
 32 
     | 
    
         
            +
                #     #{rf.label}
         
     | 
| 
      
 33 
     | 
    
         
            +
                #     #{rf.blind? ? '<span class="label">Blind</span>' : ''}
         
     | 
| 
      
 34 
     | 
    
         
            +
                #     #{rf.admin_only? ? '<span class="label">Admin Only</span>' : ''}
         
     | 
| 
      
 35 
     | 
    
         
            +
                #   """
         
     | 
| 
      
 36 
     | 
    
         
            +
                # end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                # def no_value
         
     | 
| 
      
 39 
     | 
    
         
            +
                #   "<span class='no-response'>No response</span>"
         
     | 
| 
      
 40 
     | 
    
         
            +
                # end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                # def field_value(rf)
         
     | 
| 
      
 43 
     | 
    
         
            +
                #   value = @entry.response_value(rf)
         
     | 
| 
      
 44 
     | 
    
         
            +
                #   Formbuilder::Fields.const_get(rf.field_type.camelize + 'Field').render_entry(rf, value, entry: @entry)
         
     | 
| 
      
 45 
     | 
    
         
            +
                # end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                # def checkboxes_for_table
         
     | 
| 
      
 48 
     | 
    
         
            +
                #   (@response_field.field_options['options'] || []).map do |option|
         
     | 
| 
      
 49 
     | 
    
         
            +
                #     """
         
     | 
| 
      
 50 
     | 
    
         
            +
                #       <td data-column-id='#{@response_field.id}_sortable_values_#{option['label']}'>
         
     | 
| 
      
 51 
     | 
    
         
            +
                #         <i class='#{@value && @value[option['label']] ? 'icon-ok' : 'icon-remove'}'></i>
         
     | 
| 
      
 52 
     | 
    
         
            +
                #       </td>
         
     | 
| 
      
 53 
     | 
    
         
            +
                #     """
         
     | 
| 
      
 54 
     | 
    
         
            +
                #   end.join('')
         
     | 
| 
      
 55 
     | 
    
         
            +
                # end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,107 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Formbuilder
         
     | 
| 
      
 2 
     | 
    
         
            +
              class EntryValidator < ActiveModel::Validator
         
     | 
| 
      
 3 
     | 
    
         
            +
                SHARED_VALIDATION_METHODS = [:min_max_length, :min_max, :integer_only]
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def validate(record)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @record = record
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 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
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  # we can also skip validation by setting this flag
         
     | 
| 
      
 15 
     | 
    
         
            +
                  return if record.skip_validation
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  record.form.response_fields.not_admin_only.reject { |rf| !rf.input_field }.each do |response_field|
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @response_field = response_field
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @value = @record.response_value(@response_field)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    if @response_field.required? && !@record.value_present?(@response_field)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      add_error "can't be blank"
         
     | 
| 
      
 23 
     | 
    
         
            +
                      next
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    if @record.value_present?(@response_field)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      # Field-specific validation
         
     | 
| 
      
 28 
     | 
    
         
            +
                      add_error(@response_field.validate_response(@value))
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      SHARED_VALIDATION_METHODS.each do |method_name|
         
     | 
| 
      
 31 
     | 
    
         
            +
                        run_validation(method_name)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                private
         
     | 
| 
      
 38 
     | 
    
         
            +
                def add_error(msg)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  return unless msg.present?
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @record.errors["responses_#{@response_field.id}"] << msg
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def run_validation(method_name)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  if (error_message = send(method_name))
         
     | 
| 
      
 45 
     | 
    
         
            +
                    add_error(error_message)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def min_max_length
         
     | 
| 
      
 50 
     | 
    
         
            +
                  return unless @response_field.field_options["minlength"].present? ||
         
     | 
| 
      
 51 
     | 
    
         
            +
                                @response_field.field_options["maxlength"].present?
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  if @response_field.field_options["min_max_length_units"] == 'words'
         
     | 
| 
      
 54 
     | 
    
         
            +
                    min_max_length_words
         
     | 
| 
      
 55 
     | 
    
         
            +
                  else
         
     | 
| 
      
 56 
     | 
    
         
            +
                    min_max_length_characters
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                def min_max_length_characters
         
     | 
| 
      
 61 
     | 
    
         
            +
                  if @response_field.field_options["minlength"].present? && (@value.length < @response_field.field_options["minlength"].to_i)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    return "is too short. It should be #{@response_field.field_options["minlength"]} characters or more."
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  if @response_field.field_options["maxlength"].present? && (@value.length > @response_field.field_options["maxlength"].to_i)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    return "is too long. It should be #{@response_field.field_options["maxlength"]} characters or less."
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def min_max_length_words
         
     | 
| 
      
 71 
     | 
    
         
            +
                  if @response_field.field_options["minlength"].present? && (@value.scan(/\w+/).count < @response_field.field_options["minlength"].to_i)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    return "is too short. It should be #{@response_field.field_options["minlength"]} words or more."
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  if @response_field.field_options["maxlength"].present? && (@value.scan(/\w+/).count > @response_field.field_options["maxlength"].to_i)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    return "is too long. It should be #{@response_field.field_options["maxlength"]} words or less."
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def min_max
         
     | 
| 
      
 81 
     | 
    
         
            +
                  return unless @response_field.field_options["min"].present? ||
         
     | 
| 
      
 82 
     | 
    
         
            +
                                @response_field.field_options["max"].present?
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  value_for_comparison = case @response_field.field_type
         
     | 
| 
      
 85 
     | 
    
         
            +
                  when 'price'
         
     | 
| 
      
 86 
     | 
    
         
            +
                   "#{@value['dollars'] || 0}.#{@value['cents'] || 0}".to_f
         
     | 
| 
      
 87 
     | 
    
         
            +
                  else
         
     | 
| 
      
 88 
     | 
    
         
            +
                    @value.to_f
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  if @response_field.field_options["min"].present? && (value_for_comparison < @response_field.field_options["min"].to_f)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    return "is too small. It should be #{@response_field.field_options["min"]} or more."
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  if @response_field.field_options["max"].present? && (value_for_comparison > @response_field.field_options["max"].to_f)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    return "is too large. It should be #{@response_field.field_options["max"]} or less."
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                def integer_only
         
     | 
| 
      
 101 
     | 
    
         
            +
                  if @response_field.field_options["integer_only"] && !(Integer(@value) rescue false)
         
     | 
| 
      
 102 
     | 
    
         
            +
                    "only integers allowed."
         
     | 
| 
      
 103 
     | 
    
         
            +
                  end
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
              end
         
     | 
| 
      
 107 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,102 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Formbuilder
         
     | 
| 
      
 2 
     | 
    
         
            +
              class FormRenderer
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                include ActionView::Helpers::UrlHelper
         
     | 
| 
      
 5 
     | 
    
         
            +
                include ActionView::Helpers::FormTagHelper
         
     | 
| 
      
 6 
     | 
    
         
            +
                include ActionView::Context
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def protect_against_forgery?; false; end;
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                DEFAULT_OPTIONS = {
         
     | 
| 
      
 11 
     | 
    
         
            +
                  action: '',
         
     | 
| 
      
 12 
     | 
    
         
            +
                  method: 'POST'
         
     | 
| 
      
 13 
     | 
    
         
            +
                }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def initialize(form, entry, options = {})
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @form, @entry = form, entry
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @options = self.class::DEFAULT_OPTIONS.merge(options)
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def to_html
         
     | 
| 
      
 21 
     | 
    
         
            +
                  form_tag @options[:action], method: @options[:method], class: 'formbuilder-form', multipart: true do
         
     | 
| 
      
 22 
     | 
    
         
            +
                    hidden_fields +
         
     | 
| 
      
 23 
     | 
    
         
            +
                    render_fields +
         
     | 
| 
      
 24 
     | 
    
         
            +
                    render_actions
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def hidden_fields
         
     | 
| 
      
 29 
     | 
    
         
            +
                  """
         
     | 
| 
      
 30 
     | 
    
         
            +
                    <input type='hidden' name='draft_only' />
         
     | 
| 
      
 31 
     | 
    
         
            +
                  """.html_safe
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def render_fields
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @form.response_fields.map do |field|
         
     | 
| 
      
 36 
     | 
    
         
            +
                    @field = field
         
     | 
| 
      
 37 
     | 
    
         
            +
                    render_field
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end.join('').html_safe
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def render_field
         
     | 
| 
      
 42 
     | 
    
         
            +
                  value = @entry.try(:response_value, @field)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  """
         
     | 
| 
      
 45 
     | 
    
         
            +
                    <div class='response-field-wrapper response-field-#{@field.field_type} #{@entry.try(:error_for, @field) && 'error'}'>
         
     | 
| 
      
 46 
     | 
    
         
            +
                      #{render_label}
         
     | 
| 
      
 47 
     | 
    
         
            +
                      #{@field.render_input(value, entry: @entry)}
         
     | 
| 
      
 48 
     | 
    
         
            +
                      <div class='cf'></div>
         
     | 
| 
      
 49 
     | 
    
         
            +
                      #{render_error}
         
     | 
| 
      
 50 
     | 
    
         
            +
                      #{render_description}
         
     | 
| 
      
 51 
     | 
    
         
            +
                      #{render_length_validations}
         
     | 
| 
      
 52 
     | 
    
         
            +
                      #{render_min_max_validations}
         
     | 
| 
      
 53 
     | 
    
         
            +
                    </div>
         
     | 
| 
      
 54 
     | 
    
         
            +
                  """
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def render_label
         
     | 
| 
      
 58 
     | 
    
         
            +
                  return unless @field.input_field
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  """
         
     | 
| 
      
 61 
     | 
    
         
            +
                    <label>
         
     | 
| 
      
 62 
     | 
    
         
            +
                      #{@field[:label]}
         
     | 
| 
      
 63 
     | 
    
         
            +
                      #{render_label_required if @field.required?}
         
     | 
| 
      
 64 
     | 
    
         
            +
                    </label>
         
     | 
| 
      
 65 
     | 
    
         
            +
                  """
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                def render_label_required
         
     | 
| 
      
 69 
     | 
    
         
            +
                  "<abbr title='required'>*</abbr>"
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def render_error
         
     | 
| 
      
 73 
     | 
    
         
            +
                  return unless @field.input_field
         
     | 
| 
      
 74 
     | 
    
         
            +
                  return unless @entry.error_for(@field)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  "<span class='help-block validation-message-wrapper'>#{@entry.error_for(@field)}</span>"
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                def render_description
         
     | 
| 
      
 79 
     | 
    
         
            +
                  return unless @field.input_field
         
     | 
| 
      
 80 
     | 
    
         
            +
                  return if @field[:field_options]["description"].blank?
         
     | 
| 
      
 81 
     | 
    
         
            +
                  "<span class='help-block'>#{simple_format(@field[:field_options]["description"])}</span>"
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def render_length_validations
         
     | 
| 
      
 85 
     | 
    
         
            +
                  return unless @field.input_field
         
     | 
| 
      
 86 
     | 
    
         
            +
                  return unless !@field.has_length_validations?
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                def render_min_max_validations
         
     | 
| 
      
 90 
     | 
    
         
            +
                  return unless @field.input_field
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                def render_actions
         
     | 
| 
      
 94 
     | 
    
         
            +
                  """
         
     | 
| 
      
 95 
     | 
    
         
            +
                    <div class='form-actions'>
         
     | 
| 
      
 96 
     | 
    
         
            +
                      <button class='button'>Submit</button>
         
     | 
| 
      
 97 
     | 
    
         
            +
                      <a class='button save-draft-button' data-loading-text='All changes saved'>Save draft</a>
         
     | 
| 
      
 98 
     | 
    
         
            +
                    </div>
         
     | 
| 
      
 99 
     | 
    
         
            +
                  """.html_safe
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
              end
         
     | 
| 
      
 102 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/formbuilder.rb
    ADDED
    
    | 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "formbuilder/engine"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "formbuilder/entry"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "formbuilder/entry_renderer"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "formbuilder/entry_table_renderer"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "formbuilder/entry_validator"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "formbuilder/form_renderer"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module Formbuilder
         
     | 
| 
      
 9 
     | 
    
         
            +
              def self.root
         
     | 
| 
      
 10 
     | 
    
         
            +
                File.expand_path('../..', __FILE__)
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     |