okuramasafumi / alba

Alba is a JSON serializer for Ruby, JRuby and TruffleRuby.
https://okuramasafumi.github.io/alba/
MIT License
939 stars 43 forks source link

When typing instance defined attributes, you get a method_missing' error if the passed object does not have that method defined. #389

Open patvice opened 3 hours ago

patvice commented 3 hours ago

Describe the bug

Typing attributes that are defined on a resource by instance methods doesn't seem supported. This came up when trying to add https://github.com/skryukov/typelizer to our current project.

Ideally, we would be able to support adding types to all attributes, in my case, allowing us to generate the correct type definitions on the other side.

To Reproduce

Here is an example:

class Admin
  def full_name
    "The Admin's name"
  end
end

class AdminResource
  include Alba::Resource

  attributes full_name: [String, true], message: [String, true]

  def message
    "My message"
  end
end

resource = AdminResource.new(Admin.new)

Results in this error: 'method_missing': undefined method 'message' for an instance of Admin

Expected behavior

I would have expected the resource to default to call the instance method first before going to the object passed into the resource. I would have expected typing the attribute to change the resource's calling behavior.

The metadata about the resource is correct when expecting the resource object, but we're unable to serialize the data. (aka call .to_h to .to_json on the resource object.

Actual behavior

We get a method missing error on the object passed to the resource object.

Environment

Additional context

I'm happy to create a change to solve this issue if you are good with supporting this behavior. 😄

patvice commented 3 hours ago

Upon some more testing, it seems like instance defined methods on the resource aren't used when an attribute is type:

Another Example:

class Admin
  def full_name
    "The Admin's name"
  end

  def message
    "Original message"
  end
end

class AdminResource
  include Alba::Resource

  attributes full_name: [String, true], message: [String, true]

  def message(object)
    object.message + " from resource"
  end
end

resource = AdminResource.new(Admin.new).to_h

puts resource
# => {"full_name"=>"The Admin's name", "message"=>"Original message"}

These two things sound related to the same thing. Though, this seems like it could lead to some undiscovered bugs.