Closed sander-deryckere closed 2 years ago
Hi @sander-deryckere . Sorry I missed this for so long. I didn't realize I was getting so far behind.
Correct me if I'm wrong, but you could I believe what you're looking to do can already be achieved with
serialize :address, through: :address, from: :full_format
Also, I'm not a fan of the term map
here because it makes me think that you would get some sort of array in return.
@adamcrown , no worries, thanks for your reply.
You can indeed use through
, combined with from
, but that syntax repeats the serializing name, and looks quite confusing to me. On top of that, the map
suggestion I made enables you to combine multiple values for serialization.
Say you have some metrics, modelled as values with units and a formatting display_value
function. Then you can map the models to the display_value
function to get the formatted value. Something like
serialize :cpu_temp, :cpu_usage, :ram_usage, :disk_usage, map: :display_value
With the existing options, you end up with a serializer that's a lot more verbose since you can't pass the same through
option to all serialized values:
serialize :cpu_temp, through: :cup_temp, from: :display_value
serialize :ram_usage, through: :ram_usage, from: :display_value
serialize :disk_usage, through: :disk_usage, from: :display_value
I don't think map
is a strange name for this, but maybe it's because I'm from a math background. A map
is the abbreviation of a mapping, and used as a synonym to a function
. When used as a verb, it means "apply the function to these values", which is what the code is doing.
But if you don't like the name map
, what about format
? Every example I seem to come up with involves formatting a model into some string: formatting that address, formatting metrics, ...
I'm with @adamcrown here. map
is a rather misleading name, from a rubyist and from a functional programming perspective one would expect this thing to iterate over a collection and transform it.
The repetition
serialize :cpu_temp, through: :cup_temp, from: :display_value
serialize :ram_usage, through: :ram_usage, from: :display_value
serialize :disk_usage, through: :disk_usage, from: :display_value
probably this case can be put into its own serializer and then be merged. Example:
class DisplayValueSerializer < CacheCrispies::Base
serialize :cpu_temp, :cpu_usage, :ram_usage
end
class MyModelSerializer < CacheCrispies::Base
serialize :another_attribute
merge :display_value, with: DisplayValueSerializer
end
display_value = OpenStruct.new(cpu_temp: '100', cpu_usage: 50, ram_usage: 25)
my_model = OpenStruct.new(another_attribute: 'test', display_value: display_value)
MyModelSerializer.new(my_model).as_json
# => {:another_attribute=>"test", :cpu_temp=>"100", :cpu_usage=>50, :ram_usage=>25}
I'm sorry if my example wasn't clear, but I meant it more like this, where I have a model with metrics (consisting of a value and a unit, and with extra operations)
class MetricSerializer < CacheCrispies::Base
serialize :display_value
end
class SystemMetricsSerializer < CacheCrispies::Base
merge :cpu_temp, :cpu_usage, :ram_usage, with: MetricSerializer
end
cpu_temp = OpenStruct.new(value: 75, unit: "°C", display_value: "75 °C")
cpu_usage = OpenStruct.new(value: 15, unit: "%", display_value: "15 %")
ram_usage = OpenStruct.new(value: 80, unit: "%", display_value: "80 %")
# This isn't modeled as an OpenStruct obviously, but rather in a model as offered by UnitWise
metrics = OpenStruct.new(cpu_temp: cpu_temp, cpu_usage: cpu_usage, ram_usage: ram_usage)
SystemMetricsSerializer.new(metrics).as_json
# => {cpu_temp: "75 °C", cpu_usage: "15 %", ram_usage: "80 %"}
Sadly, this isn't how merge
works. Instead of the json, I just get an error message saying merge
only accepts one argument.
So, unless I missed something, merge doesn't with the given model.
What I want is a function that applies to all arguments given to the serialize
call. Passing a list of arguments to a function is almost like passing a collection (it's just a splatted collection), hence why I thought the map
option would be a fitting name. But as said before, I'm not fixed on the name, I just would want some easy way to apply a common formatting function to a list of values.
Another option I'm thinking about is having a raw
serializer. That serializer shouldn't wrap itself in a sub-hash, but rather just return a value to be used as serialisation.
# Inherit from a different kind of base class
class MetricSerializer < CacheCrispies::Raw
# Serialize returns the complete object or value to be serialized without it having to be wrapped
def serialize(model)
model.display_value
end
end
class SystemMetricsSerializer < CacheCrispies::Base
serialize :cpu_temp, :cpu_usage, :ram_usage, with: MetricSerializer
end
cpu_temp = OpenStruct.new(value: 75, unit: "°C", display_value: "75 °C")
cpu_usage = OpenStruct.new(value: 15, unit: "%", display_value: "15 %")
ram_usage = OpenStruct.new(value: 80, unit: "%", display_value: "80 %")
# This isn't modeled as an OpenStruct obviously, but rather in a model as offered by UnitWise
metrics = OpenStruct.new(cpu_temp: cpu_temp, cpu_usage: cpu_usage, ram_usage: ram_usage)
SystemMetricsSerializer.new(metrics).as_json
# => {cpu_temp: "75 °C", cpu_usage: "15 %", ram_usage: "80 %"}
But I don't think something like that exists either. And it follows a more functional programming style, while cache-crispies is more declarative in nature.
Any opinions on the name format
, or on implementing a raw serializer?
This PR adds a
map
option to the serialize calls.It is similar to the
through
option, with the difference that thethrough
option uses the name of the sub-call, while themap
option uses the name of the parent call.In the given example, there can be an address having all discrete fields (streetname, city, ...), and some methods for formatting it (full address, just city and state, ...). Then you can map the address to one of the formatters for serialization, while it keeps the
address
key.