data_mapper, attr_accessor и сериализация только сериализуют свойства, а не атрибуты attr_accessor
Я использую data_mapper/sinatra и пытаюсь создать некоторые атрибуты с помощью attr_accessor. Следующий пример кода:
require 'json'
class Person
include DataMapper::Resource
property :id, Serial
property :first_name, String
attr_accessor :last_name
end
ps = Person.new
ps.first_name = "Mike"
ps.last_name = "Smith"
p ps.to_json
производит этот вывод:
"{\"id\":null,\"first_name\":\"Mike\"}"
Очевидно, я бы хотел, чтобы он дал мне как имя, так и фамилию. Любые идеи о том, как заставить это работать так, как можно было бы ожидать, чтобы мой json имел все атрибуты?
Кроме того, не стесняйтесь объяснить, почему мои ожидания (что я получу все атрибуты) неверны. Я предполагаю, что в некоторый внутренний список атрибутов не добавляются переменные экземпляра attr_accessor или что-то в этом роде. Но даже так, почему?
2 ответа
Благодаря Мэтту я немного покопался и нашел параметр:method для метода to_json в dm-serializer. Их метод to_json был довольно приличным и был просто оболочкой для вспомогательного метода as_json, поэтому я переписал его, просто добавив несколько строк:
if options[:include_attributes]
options[:methods] = [] if options[:methods].nil?
options[:methods].concat(model.attributes).uniq!
end
Завершенное переопределение метода выглядит так:
module DataMapper
module Serializer
def to_json(*args)
options = args.first
options = {} unless options.kind_of?(Hash)
if options[:include_attributes]
options[:methods] = [] if options[:methods].nil?
options[:methods].concat(model.attributes).uniq!
end
result = as_json(options)
# default to making JSON
if options.fetch(:to_json, true)
MultiJson.dump(result)
else
result
end
end
end
end
Это работает вместе с методом атрибутов, который я добавил в базовый модуль, который я использую с моими моделями. Соответствующий раздел ниже:
module Base
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def attr_accessor(*vars)
@attributes ||= []
@attributes.concat vars
super(*vars)
end
def attributes
@attributes || []
end
end
def attributes
self.class.attributes
end
end
теперь мой оригинальный пример:
require 'json'
class Person
include DataMapper::Resource
include Base
property :id, Serial
property :first_name, String
attr_accessor :last_name
end
ps = Person.new
ps.first_name = "Mike"
ps.last_name = "Smith"
p ps.to_json :include_attributes => true
Работает, как и ожидалось, с новым параметром option.
То, что я мог бы сделать, чтобы выборочно получить нужные атрибуты без необходимости выполнять дополнительную работу, - просто передать имена атрибутов в параметр:method.
p ps.to_json :methods => [:last_name]
Или, так как у меня уже был мой Base
учебный класс:
p ps.to_json :methods => Person.attributes
Теперь мне просто нужно выяснить, как я хочу поддерживать коллекции.
Datamapper имеет свою собственную библиотеку сериализации, dm-serializer
, что обеспечивает to_json
метод для любого ресурса Datamapper. Если вам требуется Datamapper с require 'data_mapper'
в вашем коде вы используете data_mapper
meta-gem, который требует dm-serializer как часть его настройки.
to_json
метод предоставлен dm-serializer
только сериализует свойства Datamapper вашего объекта (то есть те, которые вы указали с property
а не "нормальные" свойства (которые вы определили с attr_accessor
). Вот почему вы получаете id
а также first_name
но нет last_name
,
Во избежание использования dm-serializer
вам нужно явно требовать те библиотеки, которые вам нужны, а не полагаться на data_mapper
, Вам понадобится как минимум dm-core
и, возможно, другие.
Нормальный" json
библиотека не содержит никаких атрибутов по умолчанию to_json
вызвать объект, он просто использует объекты to_s
метод. Так что в этом случае, если вы замените require 'data_mapper'
с require 'dm-core'
, вы получите что-то вроде "\"#<Person:0x000001013a0320>\""
,
Для создания представлений JSON ваших собственных объектов вам нужно создать свои собственные to_json
метод. Простым примером будет просто жестко закодировать атрибуты, которые вы хотите в json:
def to_json
{:id => id, :first_name => first_name, :last_name => last_name}.to_json
end
Вы можете создать метод, который просматривает атрибуты и свойства объекта и создает соответствующий json из этого, вместо того, чтобы жестко их кодировать таким образом.
Обратите внимание, что если вы создаете свой собственный to_json
метод, который вы все еще можете вызвать require 'data_mapper'
, ваш to_json
заменит предоставленный dm-serializer
, по факту dm-serializer
также добавляет as_json
метод, который вы могли бы использовать для создания комбинированного to_json
метод, например:
def to_json
as_json.merge({:last_name => last_name}).to_json
end