I'm building an application where users are part of an Organisation
. An organisation has many List
s, which in turn have many ListItem
s.
Now, I would like for admin users to be able to specify which attributes are available on list items, based on the organisation they belong to (or rather, on the organisation their list belongs to), without having to touch any code.
So far, when defining attributes that are not bound to a specific column in the database, I have used document_serializable, a nifty little gem (based on virtus) which serializes virtual attributes to a JSONB column in the db. I like this approach, because I get all of virtus' goodies (types, coercion, validations, etc.), and because data ends up sitting in a JSONB column, meaning it can be loaded quickly, indexed, and searched through with relative ease.
I would like to keep using this approach when adding these user-defined attributes on the fly. So I'd like to do something like:
class ListItem < ApplicationRecord
belongs_to :list
delegate :organisation, to: :list
organisation.list_attributes.each do |a, t|
attribute a, t
end
end
Where Organisation#list_attributes
returns the user-defined hash of attribute names and their associated types, which, for example, might look like:
{
name: String,
age: Integer
}
As you might have guessed, this does not work, because organisation.list_attributes.each
actually runs in the context of ListItem
, which is an instance of Class
, and Class
doesn't have an #organisation
method. I hope that's worded in a way that makes sense1.
I've tried using after_initialize
, but at that point in the object's lifecycle, #attribute
is owned by ActiveRecord::AttributeMethods::Read
and not DocumentSerializable::ClassMethods
, so it's an entirely different method and I can't figure out wether I can still access the one I need, and wether that would even work.
Another alternative would be to find the organisation in question in some explicit way, Organisation#find
-style, but I honestly don't know where I should store the information necessary to do so.
So, my question: at the moment of instantiating (initializing or loading2) a record, is there a way I can retrieve a hash stored in a database column of one of its relations? Or am I trying to build this in a completely misguided way, and if so, how else should I go about it?
1 To clarify, if I were to use the hash directly like so:
class ListItem < ApplicationRecord
belongs_to :list
delegate :organisation, to: :list
{
name: String,
age: Integer
}.each do |a, t|
attribute a, t
end
end
it would work, my issue is solely with getting a record's relation at this earlier point in time.
2 My understanding is that Rails runs a model's code whenever a record of that type is created or loaded from the database, meaning the virtual attributes are defined anew every time this happens, which is why I'm asking how to do this in both cases.
Yes. This is fairly trivial as long as your relations are setup correctly / simply. Lets say we have these three models:
We can instantiate a
ListItem
and then retrieve data from anyone of its parents.And if we wanted to do this at every instance of a
ListItem
, we can use Active Record Callbacks like this:But
after_initialize
feels like a strange usage for this kind of thing. Maybe a helper method would be a better option!