Отсутствует и неточная полезная нагрузка в Rails с ActiveModel и Forms - PullRequest
0 голосов
/ 08 марта 2020

У меня есть модель для Organisation, как

class Organisation
  include ActiveModel::Model

  attr_accessor :orguid,
                :title, :firstname, :lastname, :role, :telephone, :extension, :email,
                :name, :branch, :address1, :address2, :address3, :city, :state, :country, :zip

end

В моем контроллере у меня есть следующие действия:

# frozen_string_literal: true

require 'cgi'
require 'json'

class OrganisationsController < ApplicationController
  include Secured

  before_action :set_api, only: %i[dashboard create]
  before_action :user_info, only: %i[dashboard register]

  def dashboard
    @registration = @api.registered?
  end

  def register
    @organisation = Organisation.new
  end

  def create
    organisation_params
    register_data = params[:organisation].to_h
    register_data['oruid'] = org_uid
    @api.register(register_data)
  end

  private

  def set_api
    @api = CoreApi.new(org_uid)
  end

  def user_info
    @user_info = session[:userinfo].to_h
  end

  def org_uid
    CGI.escape(user_info['uid'])
  end

  def organisation_params
    params.require(:organisation).permit!
  end

end

в моем register.html.erb У меня есть:

<h1> Register Your Organisation</h1>


<%= form_with model: @organisation, url: org_register_path do |f| %>
  <div class="container">
    <h2>Your Details</h2>
    <div class="form-row">
      <div class="form-group col-md-2">
        <%= f.label :title %>
        <%= f.text_field :title, class: 'form-control' %>
      </div>
      <div class="form-group col-md-5">
        <%= f.label :first_name %>
        <%= f.text_field :firstname, class: 'form-control' %>
      </div>
      <div class="form-group col-md-5">
        <%= f.label :last_name %>
        <%= f.text_field :lastname, class: 'form-control' %>
      </div>
    </div>
    <div class="form-row">
      <div class="form-group col-md-12">
        <%= f.label :role %>
        <%= f.text_field :role, class: 'form-control' %>
      </div>
    </div>
    <div class="form-row">
      <div class="form-group col-md-4">
        <%= f.label :telephone %>
        <%= f.telephone_field :telephone, class: 'form-control' %>
      </div>
      <div class="form-group col-md-2">
        <%= f.label :extension %>
        <%= f.text_field :extension, class: 'form-control' %>
      </div>
      <div class="form-group col-md-6">
        <%= f.label :email %>
        <%= f.email_field :email, class: 'form-control', readonly:'', value: @user_info['info']['name'] %>
      </div>
    </div>
  </div>

  <div class="container">
    <h2>Organisation Details</h2>
    <div class="form-row">
      <div class="form-group col-md-6">
        <%= f.label :name %>
        <%= f.text_field :name, class: 'form-control' %>
      </div>
      <div class="form-group col-md-6">
        <%= f.label :branch %>
        <%= f.text_field :branch, class: 'form-control' %>
      </div>
    </div>
    <div class="form-row">
      <div class="form-group col-md-12">
        <%= f.label :address_line_1 %>
        <%= f.text_field :address1, class: 'form-control' %>
      </div>
    </div>
    <div class="form-row">
      <div class="form-group col-md-12">
        <%= f.label :address_line_2 %>
        <%= f.text_field :address2, class: 'form-control' %>
      </div>
    </div>
    <div class="form-row">
      <div class="form-group col-md-12">
        <%= f.label :address_line_3 %>
        <%= f.text_field :address3, class: 'form-control' %>
      </div>
    </div>
    <div class="form-row">
      <div class="form-group col-md-4">
        <%= f.label :city %>
        <%= f.text_field :city, class: 'form-control' %>
      </div>
      <div class="form-group col-md-4">
        <%= f.label :state %>
        <%= f.text_field :state, class: 'form-control' %>
      </div>
      <div class="form-group col-md-4">
        <%= f.label :country %>
        <%= f.text_field :country, class: 'form-control' %>
      </div>
    </div>
    <div class="form-row">
      <div class="form-group col-md-2">
        <%= f.label :zip %>
        <%= f.text_field :zip, class: 'form-control' %>
      </div>
    </div>
  </div>

  <div class="container">
    <div class="form-row">
      <div class="form-group col-md-12">
        <%= f.button :Register, class: 'btn btn-primary' %>
      </div>
    </div>
  </div>
<% end %>

и, наконец, метод register в моем core_api.rb таков:

  def register(data)
    body = data.to_json
    puts ">> >> >> >> #{body.class} :: #{body}"
    options = { headers: { 'Content-Type' => 'application/json' }, body: body }
    response = self.class.post('/organisations', options)
    #puts ">>>>>>>>>>>> #{response}"
  end

и, наконец, мой routes.rb файл содержит:

Rails.application.routes.draw do

  get '/' => 'home#show'

  get '/auth/auth0/callback' => 'auth0#callback'
  get '/auth/failure' => 'auth0#failure'

  get '/logout',                    to: 'logout#logout',                as: 'logout'

  get '/organisations/dashboard',   to: 'organisations#dashboard',      as: 'org_dashboard'
  get '/organisations/register',    to: 'organisations#register',       as: 'org_register'
  post '/organisations/register',   to: 'organisations#create'

  root 'home#show'
end

теперь, когда Я запускаю сервер и отправляю форму в журналах, которые я получаю:

>> >> >> >> String :: {"title":"","firstname":"","lastname":"","role":"","telephone":"","extension":"","email":"alijy3@yahoo.com","name":"we","branch":"we","address1":"we","address2":"","address3":"","city":"we","state":"","country":"we","zip":"","oruid":"auth0%7C5e5388493d670c11be833bca","contact_id":0}

, которая для меня выглядит как правильная json. Но, так как ответ API был постоянно безуспешным, я перехватил исходящее сообщение с Почтальоном , чтобы посмотреть, какая полезная нагрузка отправляется. К моему удивлению, полезная нагрузка не плоская json, но выглядит так: Postman screenshot

У меня есть 2 проблемы с этим:

  • API принимает такие элементы, как address1, address2, city, et c. Я считаю, что мне следует отправлять их, а не показывать organisation[address1], organisation[address2] и т. Д. c.
  • Вторая проблема заключается в том, что я добавляю orguid после отправки формы и перед вызовом / отправкой сообщений в api. Но, хотя я вижу это в сообщениях журнала, я не вижу orguid в полезной нагрузке почтальона в любой форме.

У меня нет базы данных на сервере. Все выборки / размещены / сохранены через API. Некоторое время я читал о том, как работать с Activemodel и формами, и мне пока не удалось решить эту проблему. Любая помощь или объяснение будет высоко ценится.

1 Ответ

1 голос
/ 08 марта 2020

Без обид, но это крушение поезда. Вам не нужно нарушать каждое соглашение по rails только потому, что вы не используете ActiveRecord в этом конкретном случае c.

Начните с использования ActiveModel::Attributes#attribute вместо встроенного Ruby attr_accessor.

class Organisation
  include ActiveModel::Model
  include ActiveModel::Attributes
  [:orguid, :title, :firstname, :lastname, :role, :telephone, 
   :extension, :email, :name, :branch, 
   :address1, :address2, :address3, :city, :state, :country, :zip]
  .each do |name|
     attribute name
  end

  # @todo write validations!
end

Это создает атрибуты, которые действуют как атрибуты ActiveRecord, и вы можете правильно сериализовать модель с помощью @organization.as_json.

Затем давайте просто запустим fre sh на этом контроллере, так как есть только слишком много запаха, чтобы стоить спасения.

# routes.rb
resources :organisations, only: [:new, :create]


class OganizationsController < ApplicationController
  # GET /organizations/new
  def new
     @organization = Organization.new
  end

  # POST /organizations
  def create
    # You never manually parse out incoming params - thats Rack's job.
    # also since you have a model - USE IT!
    @organization = Organization.new(organization_params) do |o|
      o.orguid = org_uid
    end
    # validate the user input before you send it to an external API
    if @organization.valid? && @api.register(@organization)
      redirect_to '/somewhere'
    else
      render :new
    end
  end

  private

  # use monads here instead of callbacks!
  def user_info
    # Rails will serialize/deserialize hashes automatically 
    # from the session
    session[:userinfo] 
  end

  def org_uid
    # Have no clue what the heck you're doing with CGI escape. 
    @org_uid ||= user_info['uid']
  end 

  def api
    @api ||= CoreApi.new(org_uid)
  end

  def organization_params
    # You don't have any reason to use 'permit!' and give 
    # yourself a potential mass assignment vunerablity
    params.require(:organization)
          .permit(
             :title, :firstname, :lastname, :role, :telephone, 
             :extension, :email, :name, :branch, 
             :address1, :address2, :address3, :city, 
             :state, :country, :zip
           )
  end
end

Переименуйте представление /organizations/new.html.rb. На этом этапе вы сможете заглушить API и выполнить интеграционный тест с допустимым и недействительным вводом.

Вся эта вещь session[:userinfo] все еще пахнет очень плохо - если вы принимаете ответ от OAuth и толкаете Во время сеанса вы настраиваете себя на действительно плохое время, так как это может вызвать переполнение cook ie. Кроме того, в целом в Rails, если вы когда-либо выполняете приведение / сериализацию вручную, это действительно хороший признак того, что вы делаете что-то очень неправильно.

Понятия не имею, что на самом деле происходит в вашем классе CoreApi, но если вы используете HTTParty Вы не должны делать ЛЮБУЮ ручную кодировку JSON.

# @fixme name is way to generic. 
class CoreApi
  include HTTParty
  format :json # sets content type and encodes the content
  # ...
  def register(organization)
    response = self.class.post('/organisations', @organization.as_json)
    if response.success?
      true
    else
      @organization.errors.add(:base, 'Could not be registered')
      false
    end
  end
end
...