makandra / active_type

Make any Ruby object quack like ActiveRecord
MIT License
1.09k stars 74 forks source link

Expose ActiveRecord::Base.attribute #98

Closed Looooong closed 5 years ago

Looooong commented 7 years ago

I want to have custom serialization for my column type in Postgres, so I do this

class Quiz < ActiveRecord::Base
  # Convert attribute type to Array of Quiz::Choice instead of Array of JSONB
  attribute :choices, ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new(ChoiceType.new)
end

However, if I switch to ActiveType::Record, ::attribute behaves differently and doesn't serialize the column. I think the ::atttribute methods from ActiveRecord::Base and ActiveType::Record must be differentiate from each other and the later must not override the former with different behaviour.

Also, I would love to see that nests_many work in conjunction with custom column serializer in this example:

class Quiz < ActiveType::Record
  # Convert attribute type to Array of Quiz::Choice instead of Array of JSONB
  attribute :choices, ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new(ChoiceType.new)
  nests_many :choices
end

class Quiz::Choice < ActiveType::Object
  [...]
end

quiz = Quiz.new
quiz.validate # Should also validate quiz.choices
quiz.choices_attributes = [...] # Assign array of Choice to `choices` column
kratob commented 7 years ago

I wasn't aware of this use case for attribute (having a database column, but overriding the serializer). It might be possible to only do include ActiveType::NestedAttributes instead of inheriting from ActiveType::Record, in order to get nests_many, but not the new behaviour of attribute. I have not tested that, though.

I could see three ways to address this

@triskweline Any thoughts?

triskweline commented 7 years ago

@kratob Can we support custom serializers in our attribute ?

Looooong commented 7 years ago

I'm using Rails 4.2.8

@kratob I tested include ActiveType::NestedAttributes and it doesn't work :(

@triskweline Having custom serializer for your attribute is a good idea. I'd like to also have an ability to persist defined attribute to a database column (just assign attribute and let Rails handles the abstraction?).

kratob commented 7 years ago

I can look into supporting custom serializers. It would probably require a significant rewrite though, since our attribute is not designed to do any serialization at all.

triskweline commented 7 years ago

OK, so unless someone wants to attempt a PR with tests, I don't see us implementing this any time soon.

Just so this doesn't trip up people in the future, how about we expose the original method as ar_attribute as @kratob suggested. When someone passes a non-symbol type to our attribute we could raise an error with instructions to use ar_attribute instead.

Looooong commented 7 years ago

Sounds good to me. Be aware, in Rails 5, we can use both symbol and type object for custom type, reference.

kratob commented 5 years ago

ActiveRecord.attribute is now exposed as .ar_attribute. Unfortunately I cannot print any warnings, since an ActiveModel::Type is valid for the second arguments to ActiveType.attribute, it just does not work to override behavior for database-backed columns.

kratob commented 5 years ago

(Released as 1.1.0).