-
Notifications
You must be signed in to change notification settings - Fork 130
Description
example: #439
--
The body of an HTTP response is often JSON. Currently, after slightly modifying the JSON keys, we use class.new(**json)
(which calls class#initialize
) to assign the values to the class attributes.
Line::Bot::V2::ManageAudience::GetAudienceGroupsResponse.new(**json)
This approach works only when there are no nested keys. For example, the following JSON structure is acceptable:
{
"phase": "waiting"
}
We can access the value using response.phase
.
However, when there are nested keys, we cannot access those keys by directly traversing the attributes.
{
"audienceGroup": {
"audienceGroupId": 12345
}
}
response.audienceGroup
exists, but response.audienceGroup.audienceGroupId
results in a NoMethodError
.(the test failed due to this reason)
This occurs because the initialize
method of the class handling the response does not instantiate another class for the values of JSON objects. Instead, it directly assigns them to the attributes of its own class. Consequently, response.audienceGroup
remains a Hash or JSON object.
The current class implementation is the cause of this issue:
class ClassForAudienceGroupResponse
attr_accessor :audience_group
def initialize(
audience_group # This is a Hash/JSON containing { "audienceGroupId": 12345 }
)
@audience_group = audience_group # Simply assign it
end
end
(actual code:
line-bot-sdk-ruby/lib/line/bot/v2/manage_audience/model/get_audience_groups_response.rb
Lines 16 to 38 in da44c44
class GetAudienceGroupsResponse | |
attr_accessor :audience_groups # An array of audience data. If there are no audiences that match the specified filter, an empty array will be returned. | |
attr_accessor :has_next_page # true when this is not the last page. | |
attr_accessor :total_count # The total number of audiences that can be returned with the specified filter. | |
attr_accessor :read_write_audience_group_total_count # Of the audiences you can get with the specified filter, the number of audiences with the update permission set to READ_WRITE. | |
attr_accessor :page # The current page number. | |
attr_accessor :size # The maximum number of audiences on the current page. | |
def initialize( | |
audience_groups: nil, | |
has_next_page: nil, | |
total_count: nil, | |
read_write_audience_group_total_count: nil, | |
page: nil, | |
size: nil | |
) | |
@audience_groups = audience_groups | |
@has_next_page = has_next_page | |
@total_count = total_count | |
@read_write_audience_group_total_count = read_write_audience_group_total_count | |
@page = page | |
@size = size |
)
Therefore, i think it would be better to implement the following change as of now:
class ClassForAudienceGroupResponse
attr_accessor :audience_group
def initialize(
audience_group # This is a Hash/JSON containing { "audienceGroupId": 12345 }
)
@audience_group = AudienceGroup.new(**audience_group) # Instantiate AudienceGroup with the provided data
end
end
Advantages and Disadvantages:
Advantages:
- We can access nested values by traversing attributes.
- Handling response models and webhook models becomes consistent, eliminating the need for manual webhook parsing.
- We can remove the dependency on OpenStruct.
Disadvantages:
- When dealing with polymorphism (e.g.,
type: text
,type: image
), we will need to implement logic to determine the appropriate class for deserialization (this is already implemented in Python and PHP, so it should be achievable). - When dealing with polymorphism, we will need to implement a fallback mechanism to an
UnknownHogeClass
if an older SDK receives a new type (e.g.,type: newType
) that is released in Messaging API newly. This is already implemented in Python, PHP, and Java.