def process_signup(field_data:, email_opt_in: false, source: nil, subsource: nil)
      # Construct the XML to send
      builder = Builder::XmlMarkup.new
      builder.instruct! :xml, version: '1.0', encoding: 'utf-8'
      xml_body = builder.api do |api|
        api.signup_form(id: self.id) do |form|
          form_fields.each do |field|
            form.signup_form_field(field_data[field.label], id: field.id)
          end
          unless email_opt_in.nil?
            form.email_opt_in(email_opt_in ? '1' : '0')            
          end
          form.source(source) unless source.nil?
          form.subsource(subsource) unless subsource.nil?
        end
      end

      # Post it to the endpoint
      begin
        response = connection.perform_request_raw '/signup/process_signup', {}, 'POST', xml_body
        if response.status >= 200 && response.status < 300
          return true
        else
          raise "process signup failed with message: #{response.body}"
        end
      rescue BlueStateDigital::XmlErrorResponse => err
        begin
          errors = {}
          error_xmls = Nokogiri::XML(err.message).xpath('//error')
          error_xmls.each do |error_xml|
            field = form_fields.select{|field| field.id.to_s == error_xml.xpath('signup_form_field_id').text}.first
            errors[field.label] = error_xml.xpath('description').text
            raise "process_signup failed with errors: #{errors}"
          end
        end
      end
    end
def process_signup(field_data:, email_opt_in: false, source: nil, subsource: nil)
      # Construct the XML to send
      builder = Builder::XmlMarkup.new
      builder.instruct! :xml, version: '1.0', encoding: 'utf-8'
      xml_body = builder.api do |api|
        api.signup_form(id: self.id) do |form|
          form_fields.each do |field|
            form.signup_form_field(field_data[field.label], id: field.id)
          end
          unless email_opt_in.nil?
            form.email_opt_in(email_opt_in ? '1' : '0')            
          end
          form.source(source) unless source.nil?
          form.subsource(subsource) unless subsource.nil?
        end
      end

      # Post it to the endpoint
      begin
        response = connection.perform_request_raw '/signup/process_signup', {}, 'POST', xml_body
        if response.status >= 200 && response.status < 300
          return true
        else
          raise "process signup failed with message: #{response.body}"
        end
      rescue BlueStateDigital::XmlErrorResponse => err
        begin
          errors = {}
          error_xmls = Nokogiri::XML(err.message).xpath('//error')
          error_xmls.each do |error_xml|
            field = form_fields.select{|field| field.id.to_s == error_xml.xpath('signup_form_field_id').text}.first
            errors[field.label] = error_xml.xpath('description').text
            raise "process_signup failed with errors: #{errors}"
          end
        end
      end
    end
field_data is a hash mapping field labels to field values, like {'First Name' => 'Alice'}