Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveModel::Model Support #40

Open
pinzonjulian opened this issue May 22, 2024 · 3 comments
Open

ActiveModel::Model Support #40

pinzonjulian opened this issue May 22, 2024 · 3 comments
Labels
enhancement New feature or request

Comments

@pinzonjulian
Copy link

pinzonjulian commented May 22, 2024

Hey! I've managed to add ActiveModel::Model support like so:

# config/initializers/universal_id.rb

UniversalID::MessagePackFactory.register(
  type: ActiveModel::Model,
  packer: -> (model, packer) {
    unless model.respond_to?(:attributes)
      error = <<~STR
        #{model.class} must respond to #attributes. You can do this in two ways:
        Using ActiveModel::Serializers to define a custom `attributes` method
        https://guides.rubyonrails.org/active_model_basics.html#serialization
        
        Using ActiveModel::Attributes which provides `attributes` automatically
        https://api.rubyonrails.org/classes/ActiveModel/Attributes.html
      STR
      raise ArgumentError, error
    end
    packer.write(model.class)
    packer.write(model.attributes)
  },
  unpacker: -> (unpacker) {
    model_name = unpacker.read
    model_name.new(unpacker.read)
  }
)

With this, a class including ActiveModel that responds to attributes now works.

I wonder if this is the right way to do it though. What do you think @hopsoft ?

@hopsoft
Copy link
Owner

hopsoft commented May 27, 2024

This looks like a clean solution. Will consider getting this into the library soon. Thanks.

@pinzonjulian
Copy link
Author

pinzonjulian commented Jul 30, 2024

I'm extending the feature I built with this and I believe there's an edge case when an Active Record class is a child of an Active Model object. This proposal doesn't cover and I'm not sure how to extend it to make it work.

class Journey
  include ActiveModel::Model
  attribute :itinerary # active record object goes here
end

# schema: [name]
class Itinerary < ApplicationRecord
end

journey = Journey.new
journey.itinerary = Itinerary.new(name: "Hello World")
journey.itinerary.name # => "Hello World"

payload = URI::UID.build(journey, { include_changes: true }).payload
decoded_journey = URI::UID.from_payload(payload).decode

decoded_journey.itinerary.name # => nil

I tried using include_changes, include_descendants and descendants_depth with no luck.

Any ideas?

@pinzonjulian
Copy link
Author

I also tried registering the settings like so:

UniversalID::Settings.register :changed, { prepack: { include_blank: false}, database: { include_changes: true, include_descendants: true, descendant_depth: 2} }

This didn't work either.

I've traced it down to this line:
UniversalID::Extensions::ActiveRecordBasePacker#prepack_database_options

reject_unsaved_changes! attrs if prepack_database_options.exclude_changes?

For some reason, it's not understanding that I want to include changes.

@hopsoft hopsoft added the enhancement New feature or request label Dec 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants