Reputation: 2149
Let's say I have following class Message
representing a message in a chat.
class Message
JSON.mapping({
message_id: {type: Int32},
text: {type: String, nilable: true},
photo: {type: Photo, nilable: true},
sticker: {type: Sticker, nilable: true},
})
MESSAGE_TYPES = %w( text photo sticker )
{% for type in MESSAGE_TYPES %}
def {{ type.id }}?
! {{ type.id }}.nil?
end
{% end %}
end
Every message can be either text, or photo, or sticker. But only one of those. For instance, if message is a text, then photo
and sticker
properties are nil, but text
is not nil. If message is a sticker, only sticker
is not nil. And so on. Real class has much more direct types and inferred types.
Upon receiving this message, I want to process it in a way specific for particular type. Something like:
case message
when .text?
p message.text
when .photo?
p message.photo.file_id
when .sticker?
p message.sticker.file_id
end
Of course, it won't work, because in each when
clause corresponding properties are nilable and undefined method 'file_id' for Nil
.
Is there any way to restrict variable type in each case making sure that is message.sticker?
then message.sticker
is not nil and file_id
exists?
Maybe I'm completely wrong in very approaching the problem and there better and cleaner ways to code this?
Upvotes: 1
Views: 69
Reputation: 5661
You could put all payload objects in one instance variable with a union type as Oleh Prypin suggested.
Another option, if you want to keep the current data layout, you could have the methods return the respective Type or nil
and assign its value to a variable:
if text = message.text?
p text
if photo = message.photo?
p photo.file_id
if sticker = message.sticker?
p sticker.file_id
end
Upvotes: 2
Reputation: 34116
require "json"
class Photo
JSON.mapping(url: String)
end
struct Message
JSON.mapping(id: Int32, message: String|Photo)
end
p Message.from_json(%({"id": 5, "message": {"url": "http://example.org/"}}))
Upvotes: 2