Генерация JSON для Синатры - PullRequest
4 голосов
/ 29 марта 2012

У меня проблема с передачей сгенерированной нотации JSON моего объекта в мое приложение Sinatra. У меня двойная проблема:

  • У меня есть 2 класса, которые сопоставлены с базой данных, используя гем Sequel. Когда они генерируют JSON, это нормально и правильно реализовано.
  • У меня есть собственный класс, который называется регистрация, который отображает один из классов с дополнительным полем. Цель состоит в том, чтобы сгенерировать JSON из этого и передать этот JSON приложению, используя cucumber (цель теста)

Код приложения, ответственный за обработку запроса, имеет следующую определенную функцию:

post '/users' do
  begin
    hash = JSON.parse(self.request.body.read)
    registration = Registration.new.from_json(@request.body.read)
    registration.user.country = Database::Alaplaya.get_country_by_iso_code(registration.user.country.iso_code)
    return 400 unless(registration.is_valid?)
    id = Database::Alaplaya.create_user(registration.user)

    # If the registration failed in our system, return a page 400.
    return 400 if id < 1
end
  • задача 1: Я не могу использовать хэш параметров. Он существует, но это просто пустой хеш. Почему?
  • проблема 2: Я не могу десериализовать JSON, сгенерированный самим классом. Почему?

Класс регистрации выглядит так:

require 'json'

class Registration
  attr_accessor :user, :project_id

  def to_json(*a)
    {
        'json_class'   => self.class.name,
        'data'         => [@user.to_json(*a), @project_id]
    }.to_json(*a)
  end

  def self.json_create(o)
    new(*o['data'])
  end

  # Creates a new instance of the class using the information provided in the
  # hash. If a field is missing in the hash, nil will be assigned to that field
  # instead.
  def initialize(params = {})
    @user = params[:user]
    @project_id = params[:project_id]
  end

  # Returns a string representing the entire Registration.
  def inspect
    "#{@user.inspect} - #{@user.country.inspect} - #{@project_id}"
  end

  # Returns a boolean valid representing whether the Registration instance is
  # considered valid for the API or not. True if the instance is considered
  # valid; otherwise false.
  def is_valid?
    return false if @user.nil? || @project_id.nil?
    return false if !@user.is_a?(User) || !@project_id.is_a?(Fixnum)
    return false if !@user.is_valid?
    true
  end
end

Мне нужно было реализовать методы для правильной генерации вывода JSON. Когда я запускаю это в консоли, я получаю следующий вывод:

irb(main):004:0> r = Registration.new(:user => u, :project_id => 1)
=> new_login - nil - 1
irb(main):005:0> r.to_json
=> "{\"json_class\":\"Registration\",\"data\":[\"{\\\"json_class\\\":\\\"User\\\
",\\\"login\\\":\\\"new_login\\\"}\",1]}"

Что для меня похоже на действительный JSON. Однако, когда я POST это на сервер приложений и пытаюсь разобрать это, JSON жалуется, что необходимо по крайней мере 2 октета и отказывается десериализовать объект.

1 Ответ

4 голосов
/ 29 марта 2012

Если вы используете Sequel в качестве ORM, попробуйте что-то вроде этого:

В вашей модели:

class Registration < Sequel::Model
  many_to_one :user
  many_to_one :project
  plugin :json_serializer
end        

Сервер:

before do
  @data = JSON.parse(request.body.read) rescue {}
end

post '/users' do
  @registration = Registration.new @data
  if @registration.valid?
    @registration.save 
    @registration.to_json #return a JSON representation of the resource
  else
    status 422 #proper status code for invalid input
    @registration.errors.to_json
  end
end

Я думаю, что вы можете усложнить процесс регистрации. Если действие HTTP POST /users, то почему бы не создать пользователя? Похоже, создание registration слишком сложно. Если ваш пользователь уже не существует, в этом случае POST /users будет неправильным. Если вы действительно собираетесь добавить пользователя в проект, тогда вам нужно PUT /projects/:project_id/users/:user_id, и действие будет выглядеть примерно так:

class User < Sequel::Model
  many_to_many :projects
end
class Project < Sequel::Model
  many_to_many :users
end
#make sure your db schema has a table called users_projects or projects_users

put '/projects/:project_id/users/:user_id' do
  #find the project
  @project = Project.find params[:project_id]
  raise Sinatra::NotFound unless @project
  #find the user
  @user = Project.find params[:project_id]
  raise Sinatra::NotFound unless @user
  #add user to project's users collection
  @project.add_user @user
  #send a new representation of the parent resource back to the client
  #i like to include the child resources as well
  #json might look something like this
  #{ 'name' : 'a project name', 'users' : ['/users/:user_id', '/users/:another_user_id'] }
  @project.to_json
end
...