ActiveModel::Serializer 将动态接受任何属性的子类?
ActiveModel::Serializer Subclass that will accept any attribute dynamically?
我正在为现有应用构建一个 API 引擎,它将为 JSON 和 ActiveModel::Serializer
提供服务。在现有的应用程序上,有一些控制器只呈现不是任何 ActiveModel subclass 实例的常规旧散列 - 最初,这些是 AJAX 端点所以 class 响应 body 是。
我需要在 API 模块中重新创建其中一些现有端点,因此对于此类实例,我想构建一个自定义序列化程序,它将接受您向其提供的任何属性。像...
在控制器中:
def show
response = {
key: "this is a custom object and not an AM instance"
}
render json: response, serializer: Api::V1::CustomSerializer
end
和序列化器:
module Api
module V1
class CustomSerializer < ActiveModel::Serializer
def attributes
*object.keys.map(&:to_sym)
end
def read_attribute_for_serialization(attr)
object[attr.to_s]
end
end
end
end
几个问题:
a) 在控制器中对渲染的调用似乎不喜欢我传递给渲染的参数数量,据推测这需要 *args
,这表明我的覆盖我写的方法。
b) 如果我只是将 attributes *object.class.column_names.map(&:to_sym)
放在 class 的第一行,那么 object 在方法之外是未定义的。
c) 我在一个方法中调用它,生成的散列嵌套在我选择调用该方法的任何内容中。不是我想的那样。
我的问题:有没有人成功创建了一个可以接受任何属性的序列化程序?很想知道怎么做。
请注意: 如果可以的话,我想用 AMS 实现这一点 - 我们正在使用 JSON API 的适配器来进行所有响应身体。我宁愿让这项工作正常进行,然后初始化一个与我们每次响应不是 AM 实例时使用的 json api 标准相同的散列。
对于可能偶然发现相同问题的人,我最终组装了一个包罗万象的序列化程序 class 用于任何我想呈现的不是 Active 的子 class 的东西记录。像这样:
module Api
module V1
class CustomSerializer
def initialize(obj, error: false, type: nil)
@hash = error ? error_hash(obj) : success_hash(type, obj)
end
def to_h
@hash
end
private
def error_hash(obj)
{
errors: {
pointer: obj[:error] || obj[:errors]
},
detail: detail(obj)
}
end
def success_hash(type, obj)
{
id: obj.try(:id) ? obj[:id] : nil,
type: type.nil? ? 'hash' : type,
data: obj.try(:id) ? obj.except(:id) : obj,
links: ''
}
end
def detail(obj)
obj[:detail] || obj[:message] || obj[:msg]
end
end
end
end
请注意,我使用的是 JSON API 标准。然后,不要为 activemodel 序列化器做这样的事情:
render json: @device, serializer: Api::V1::DeviceSerializer
我可以这样做:
render json: Api::V1::CustomSerializer.new(@response, error: false, type: 'feed').to_h
基本上只是意味着我仍然可以为任何只是散列 class 实例的任何东西或我正在为其做外部 JSON api 的任何东西呈现标准 API 请求并存储在哈希中。希望有一天这对某人有所帮助。
我正在为现有应用构建一个 API 引擎,它将为 JSON 和 ActiveModel::Serializer
提供服务。在现有的应用程序上,有一些控制器只呈现不是任何 ActiveModel subclass 实例的常规旧散列 - 最初,这些是 AJAX 端点所以 class 响应 body 是。
我需要在 API 模块中重新创建其中一些现有端点,因此对于此类实例,我想构建一个自定义序列化程序,它将接受您向其提供的任何属性。像...
在控制器中:
def show
response = {
key: "this is a custom object and not an AM instance"
}
render json: response, serializer: Api::V1::CustomSerializer
end
和序列化器:
module Api
module V1
class CustomSerializer < ActiveModel::Serializer
def attributes
*object.keys.map(&:to_sym)
end
def read_attribute_for_serialization(attr)
object[attr.to_s]
end
end
end
end
几个问题:
a) 在控制器中对渲染的调用似乎不喜欢我传递给渲染的参数数量,据推测这需要 *args
,这表明我的覆盖我写的方法。
b) 如果我只是将 attributes *object.class.column_names.map(&:to_sym)
放在 class 的第一行,那么 object 在方法之外是未定义的。
c) 我在一个方法中调用它,生成的散列嵌套在我选择调用该方法的任何内容中。不是我想的那样。
我的问题:有没有人成功创建了一个可以接受任何属性的序列化程序?很想知道怎么做。
请注意: 如果可以的话,我想用 AMS 实现这一点 - 我们正在使用 JSON API 的适配器来进行所有响应身体。我宁愿让这项工作正常进行,然后初始化一个与我们每次响应不是 AM 实例时使用的 json api 标准相同的散列。
对于可能偶然发现相同问题的人,我最终组装了一个包罗万象的序列化程序 class 用于任何我想呈现的不是 Active 的子 class 的东西记录。像这样:
module Api
module V1
class CustomSerializer
def initialize(obj, error: false, type: nil)
@hash = error ? error_hash(obj) : success_hash(type, obj)
end
def to_h
@hash
end
private
def error_hash(obj)
{
errors: {
pointer: obj[:error] || obj[:errors]
},
detail: detail(obj)
}
end
def success_hash(type, obj)
{
id: obj.try(:id) ? obj[:id] : nil,
type: type.nil? ? 'hash' : type,
data: obj.try(:id) ? obj.except(:id) : obj,
links: ''
}
end
def detail(obj)
obj[:detail] || obj[:message] || obj[:msg]
end
end
end
end
请注意,我使用的是 JSON API 标准。然后,不要为 activemodel 序列化器做这样的事情:
render json: @device, serializer: Api::V1::DeviceSerializer
我可以这样做:
render json: Api::V1::CustomSerializer.new(@response, error: false, type: 'feed').to_h
基本上只是意味着我仍然可以为任何只是散列 class 实例的任何东西或我正在为其做外部 JSON api 的任何东西呈现标准 API 请求并存储在哈希中。希望有一天这对某人有所帮助。