vtambourine
vtambourine

Reputation: 2149

Type restriction for class members in case/when

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

Answers (2)

Johannes Müller
Johannes Müller

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

Oleh Prypin
Oleh Prypin

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

Related Questions