Storage of different data formats in Rails dynamically defined by admin
-
26-09-2019 - |
Question
I'm facing a problem where I cannot permanently decide which columns one of my models will have. A use case will be this:
An admin creates a new dataset, he wants users to answer. In the dataset the admin defines several data points of different format and units.
I could imagine the classes to look similar to this:
class Dataset < ActiveRecord::Base
has_many :measurements
has_many :profiles, :through => :measurements
has_many :datapoints, :through => :dataset_datapoint
end
# Join table
class Dataset_datapoint < ActiveRecord::Base
belongs_to :dataset
belongs_to :datapoint
end
class Datapoint < ActiveRecord::Base
has_many :dataset, :through => :dataset_datapoint
has_many :data
# create_table "datapoints" do |t|
# t.string :name
# t.string :format # e.g. string, decimal etc.
# t.string :unit # e.g. CM, pounds etc.
end
class Data < ActiveRecord::Base
belongs_to :datapoint
# create_table "data" do |t|
# t.integer :datapoint_id
# t.string :value # This column could be anything from string to decimal
end
In my head, this seems pretty dynamic, but still quite easy to implement. What I'm worried about, is how to do the validation on every Data model that is created? Since I cannot hardcode the validation in the model? And to make it even more complicated, what if some datapoints require extra validations, such as minimum and maximum value?
Thanks in advance, Jonas
Solution
You'll have to enumerate the list of available validations.
Then you can create a validation model and table (and maybe a join table if you want users to be able to reuse their validations - depends on your use cases):
class Validation < ActiveRecord::Base
belongs_to :dataset
# create_table 'validations' do |t|
# t.references :dataset
# t.string :type
# ... and columns for each restriction you could apply, ie:
# t.integer :max_value
# t.integer :min_value
# t.string :regexp
# ...etc...
end
Then, in your data
model, add a before_save
filter to call your custom validation method:
class Data < ActiveRecord::Base
belongs_to :datapoint
has_many :validations, :through => :datapoint
before_save :custom_validation
private
def custom_validation
validations.each do |validation|
if validation.type == 'integer_range'
unless value < validation.max_value and value > validation.min_value
# return false, or add an error on the value attribute, or whatever
end
# More validations here - use a case statement probably
end
end
end
Not sure if I've got your relationships exactly figured out, but something like that should give you a starting point.