diff --git a/README.md b/README.md index 563829a..ee3adf1 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,36 @@ class AddUserDefinedFieldsToMyModel < ActiveRecord::Migration[7.0] end ``` +### Models +Models that include the `UserDefinedFields::Fieldable` concern will be treated as the models that store the user defined data. The will be available in the dropdown list when configuring user defined fields. + +Applications that define user defined fields at the model level should call `resolve_defineable` class method with a lambda function that returns the `defineable` model. + +```ruby +class MyModel < ApplicationRecord + include UserDefinedFields::Fieldable + + resolve_defineable -> (my_model) { my_model.parent } +end +``` + +### Serializers +To include the `user_defined_fields` array as a set of nested attributes, include the `UserDefinedFields::DefineableSerializer` in the serializer for whatever model the user defined fields belong to. This is only necessary for fields defined at the model level. + +```ruby +class ParentModelSerializer < BaseSerializer + include UserDefinedFields::DefineableSerializer +end +``` + +To include the `user_defined` hash as an attribute, include the `UserDefinedFields::FieldableSerializer` in the model serializer. + +```ruby +class MyModelSerializer < BaseSerializer + include UserDefinedFields::FieldableSerializer +end +``` + ### Components User defined fields can be configured one of two ways: At the application level, or at the model level. diff --git a/app/controllers/user_defined_fields/database_controller.rb b/app/controllers/user_defined_fields/database_controller.rb index 55fa490..ac911ae 100644 --- a/app/controllers/user_defined_fields/database_controller.rb +++ b/app/controllers/user_defined_fields/database_controller.rb @@ -1,7 +1,7 @@ module UserDefinedFields class DatabaseController < ApplicationController def data_types - data_types = %w(Boolean Date Number Select String Text) + data_types = %w(Boolean Date Number RichText Select String Text) render json: { data_types: data_types }, status: :ok end diff --git a/app/models/concerns/user_defined_fields/fieldable.rb b/app/models/concerns/user_defined_fields/fieldable.rb index 1fbf38e..3d24114 100644 --- a/app/models/concerns/user_defined_fields/fieldable.rb +++ b/app/models/concerns/user_defined_fields/fieldable.rb @@ -2,8 +2,52 @@ module UserDefinedFields module Fieldable extend ActiveSupport::Concern + class_methods do + def resolve_defineable(lambda = nil) + @resolve_defineable = lambda unless lambda.nil? + @resolve_defineable + end + end + included do + # Resourceable parameters allow_params :user_defined + + # Validations + validate :validate_user_defined_fields + + private + + def validate_user_defined_fields + defineable = self.class.resolve_defineable&.call(self) + + query = UserDefinedField.all + + if defineable + query = query.where( + defineable_id: defineable.id, + defineable_type: defineable.class.to_s + ) + end + + query.each do |field| + # Skip if this field is not required + next unless field.required? + + # Parse the user defined field and extract the value + json = JSON.parse(self.user_defined || '{}') + value = json[field.column_name] + + # Add an error if the value is "empty" + next unless value.nil? || + (value.is_a?(String) && value.blank?) || + (value.is_a?(Array) && value.length === 0) || + (value.is_a?(Hash) && value.empty?) + + message = I18n.t('errors.common.required', name: field.column_name) + self.errors.add(:user_defined, [field.column_name, message]) + end + end end end end diff --git a/app/serializers/concerns/user_defined_fields/defineable_serializer.rb b/app/serializers/concerns/user_defined_fields/defineable_serializer.rb new file mode 100644 index 0000000..929e810 --- /dev/null +++ b/app/serializers/concerns/user_defined_fields/defineable_serializer.rb @@ -0,0 +1,10 @@ +module UserDefinedFields + module DefineableSerializer + extend ActiveSupport::Concern + + included do + show_attributes user_defined_fields: UserDefinedFieldsSerializer + end + + end +end diff --git a/app/serializers/concerns/user_defined_fields/fieldable_serializer.rb b/app/serializers/concerns/user_defined_fields/fieldable_serializer.rb new file mode 100644 index 0000000..c93eefd --- /dev/null +++ b/app/serializers/concerns/user_defined_fields/fieldable_serializer.rb @@ -0,0 +1,11 @@ +module UserDefinedFields + module FieldableSerializer + extend ActiveSupport::Concern + + included do + index_attributes :user_defined + show_attributes :user_defined + end + + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..21137b6 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,35 @@ +# Files in the config/locales directory are used for internationalization +# and are automatically loaded by Rails. If you want to use locales other +# than English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t "hello" +# +# In views, this is aliased to just `t`: +# +# <%= t("hello") %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# The following keys must be escaped otherwise they will not be retrieved by +# the default I18n backend: +# +# true, false, on, off, yes, no +# +# Instead, surround them with single quotes. +# +# en: +# "true": "foo" +# +# To learn more, please read the Rails Internationalization guide +# available at https://guides.rubyonrails.org/i18n.html. + +en: + errors: + common: + required: "%{name} is required"