Class: Document::Embedded
- Inherits:
-
Object
- Object
- Document::Embedded
- Extended by:
- Enumerize
- Includes:
- ActiveModel::Model, ActiveModel::Validations::Callbacks
- Defined in:
- lib/document/embedded.rb
Overview
This concern wraps the logic for embedding document in an active record object. The document fields are stored in a jsonb column. The database migration should look like this:
class CreateRenalwareTransplantRecipientWorkups < ActiveRecord::Migration
def change
create_table :transplants_recipient_workups do |t|
t.belongs_to :patient, index: true, foreign_key: true
t. :performed_at
t.jsonb :document
t.text :notes
t. null: false
end
add_index :transplants_recipient_workups, :document, using: :gin
end
end
You then have to create a class for the document under /app/documents and provide a list of the attributes following the Virtus conventions.
The class also includes the ActiveModel::Model module so you can use validations.
Here’s an example:
module Renalware
module Transplants
class RecipientWorkupDocument < Document::Embedded
attribute :hx_tb, Boolean
attribute :hx_dvt, Boolean
attribute :pregnancies_no, Integer
attribute :cervical_result, String
attribute :cervical_date, Date
class Consent < Document::Embedded
attribute :consent, Boolean
attribute :consent_date, Date
validates_presence_of :consent_date, if: :consent
end
validates_presence_of :cervical_date
end
end
end
You then include the Base module in the parent ActiveRecord and provide the document class to use.
For instance:
module Renalware
module Transplants
class RecipientWorkup < ApplicationRecord
include Document::Base
has_document class_name: "Renalware::Transplants::RecipientWorkupDocument"
end
end
end
The document attributes can be localized, but a special convention must be followed due to the namespaces, especially for simple_form. Simply create a file under config/locales and provide the fields localization:
en:
activemodel:
attributes:
renalware/transplants/recipient_workup_document:
hx_tb: History of TB?
hx_dvt: History of DVT?
pregnancies_no: Number of pregnancies
cervical_result: Cervical smear result
cervical_date: Cervical smear date
renalware/transplants/recipient_workup_document/consent:
consent: Tx consent?
consent_date: Tx consent date
simple_form:
hints:
transplants_recipient_workup:
cervical_date: The date and time of the cervical
document:
cervical_date: Just a date
placeholders:
transplants_recipient_workup:
document:
pregnancies_no: "0"
Then in a form, you simply use the form builder fields_for helper:
= f.simple_fields_for :document, f.object.document do |fd|
= fd.input :hx_tb, as: :boolean
= fd.input :hx_dvt, as: :boolean
= fd.input :cervical_date, as: :date
= fd.simple_fields_for :consent, fd.object.consent do |fdd|
= fdd.input :consent
= fdd.input :consent_date, as: :date
Direct Known Subclasses
Renalware::Accesses::AssessmentDocument, Renalware::Accesses::AssessmentDocument::Admin, Renalware::Accesses::AssessmentDocument::Results, Renalware::Events::Biopsy::Document, Renalware::Events::Investigation::Document, Renalware::Events::Swab::Document, Renalware::HD::ProfileDocument, Renalware::HD::ProfileDocument::Anticoagulant, Renalware::HD::ProfileDocument::CareLevel, Renalware::HD::ProfileDocument::Dialysis, Renalware::HD::ProfileDocument::Drugs, Renalware::HD::ProfileDocument::Transport, Renalware::HD::Session::DNA::Document, Renalware::HD::SessionDocument, Renalware::HD::SessionDocument::Complications, Renalware::HD::SessionDocument::Dialysis, Renalware::HD::SessionDocument::HDF, Renalware::HD::SessionDocument::Info, Renalware::HD::SessionDocument::Observations, Renalware::LowClearance::ProfileDocument, Renalware::NestedAttribute, Renalware::PD::Assessment::Document, Renalware::PD::TrainingSession::Document, Renalware::PatientDocument, Renalware::PatientDocument::History, Renalware::PatientDocument::Psychosocial, Renalware::PatientDocument::Referral, Renalware::Renal::ProfileDocument, Renalware::Renal::ProfileDocument::Comorbidities, Renalware::Transplants::DonorOperationDocument, Renalware::Transplants::DonorOperationDocument::Complications, Renalware::Transplants::DonorOperationDocument::Outcome, Renalware::Transplants::DonorWorkupDocument, Renalware::Transplants::DonorWorkupDocument::Comorbidities, Renalware::Transplants::DonorWorkupDocument::CreatinineClearance, Renalware::Transplants::DonorWorkupDocument::GlomerularFiltrationRate, Renalware::Transplants::DonorWorkupDocument::ImagingAndScans, Renalware::Transplants::DonorWorkupDocument::Infections, Renalware::Transplants::DonorWorkupDocument::OtherInvestigations, Renalware::Transplants::DonorWorkupDocument::UrineDipsticks, Renalware::Transplants::RecipientFollowupDocument, Renalware::Transplants::RecipientFollowupDocument::CardiovascularComplication, Renalware::Transplants::RecipientOperationDocument, Renalware::Transplants::RecipientOperationDocument::BKVirus, Renalware::Transplants::RecipientOperationDocument::CadavericDonor, Renalware::Transplants::RecipientOperationDocument::Donor, Renalware::Transplants::RecipientOperationDocument::DonorSpecificAntibodies, Renalware::Transplants::RecipientOperationDocument::Recipient, Renalware::Transplants::RecipientWorkupDocument, Renalware::Transplants::RecipientWorkupDocument::BaseConsent, Renalware::Transplants::RecipientWorkupDocument::CervicalSmear, Renalware::Transplants::RecipientWorkupDocument::Education, Renalware::Transplants::RecipientWorkupDocument::Examination, Renalware::Transplants::RecipientWorkupDocument::Historicals, Renalware::Transplants::RecipientWorkupDocument::ObstetricsAndgynaecology, Renalware::Transplants::RecipientWorkupDocument::Scores, Renalware::Transplants::RegistrationDocument, Renalware::Transplants::RegistrationDocument::CRF, Renalware::Transplants::RegistrationDocument::Codes, Renalware::Transplants::RegistrationDocument::Consent, Renalware::Transplants::RegistrationDocument::HLA, Renalware::Transplants::RegistrationDocument::Organs, Renalware::Transplants::RegistrationDocument::Transplant, Renalware::Transplants::RegistrationDocument::UKTransplantCentre, Renalware::Virology::ProfileDocument, Renalware::Virology::Vaccination::Document
Constant Summary collapse
- STRIPPABLE_TYPES =
%w(Float Integer).freeze
- @@methods_to_ignore =
rubocop:disable Style/ClassVars
[]
Class Method Summary collapse
-
.attribute(*args) ⇒ Object
Assign a default value to the attributes using a custom type.
-
.attributes_list ⇒ Object
Returns a list of the Virtus attributes in the model.
-
.old_attribute(attribute) ⇒ Object
Flag an old attribute to be ignored when the document is deserialized from the database.
-
.old_attributes(*list) ⇒ Object
Flag a list of old attribtues to be ignored when the document is deserialized from the database.
Instance Method Summary collapse
-
#method_missing(method_sym, *arguments, &block) ⇒ Object
Don’t raise exception if known missing attribute.
- #strip_leading_trailing_whitespace_from_numbers ⇒ Object
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_sym, *arguments, &block) ⇒ Object
Don’t raise exception if known missing attribute
186 187 188 |
# File 'lib/document/embedded.rb', line 186 def method_missing(method_sym, *arguments, &block) super unless @@methods_to_ignore.include? method_sym end |
Class Method Details
.attribute(*args) ⇒ Object
Assign a default value to the attributes using a custom type. Set a validation on nested object.
You can specify an enum attribute by passing the ‘enums` options:
attribute :gender, enums: %i(male female)
144 145 146 147 148 149 150 151 152 153 |
# File 'lib/document/embedded.rb', line 144 def self.attribute(*args) = args. attr_name, attr_type = *args AttributeInitializer .determine_initializer(self, attr_name, attr_type, ) .call do |name, type, | super(name, type, ) end end |
.attributes_list ⇒ Object
Returns a list of the Virtus attributes in the model
156 157 158 |
# File 'lib/document/embedded.rb', line 156 def self.attributes_list attribute_set.entries.map(&:name) end |
.old_attribute(attribute) ⇒ Object
170 171 172 173 |
# File 'lib/document/embedded.rb', line 170 def self.old_attribute(attribute) @@methods_to_ignore << attribute @@methods_to_ignore << "#{attribute}=".to_sym end |
.old_attributes(*list) ⇒ Object
181 182 183 |
# File 'lib/document/embedded.rb', line 181 def self.old_attributes(*list) list.each { |item| old_attribute(item) } end |
Instance Method Details
#strip_leading_trailing_whitespace_from_numbers ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/document/embedded.rb', line 114 def strip_leading_trailing_whitespace_from_numbers attributes.keys.each do |att| # Find the type defined in the document definition eg `attribute :weight, Integer`` # Note that primitive could be a string or class, hence :to_s primitive = self.class.attribute_set[att].type.primitive.to_s # If the type is in STRIPPABLE_TYPES ie its a numeric type, # and it has arrived as a string (which responds to :strip) then # ensure there are no leading or trailing spaces, otherwise Virtus cannot # coerce it into the correct type. For example Virtus won't corece # " 1" into 1 but will coerce "1" into 1 (FYI the Dry::Types gem (the successor to Virtus) # rectifies this). # Note also that here in this before_validation callback, the act of assignment in # `self[att] =` prompts Virtus to re-attempt to coerce the value, which now, if space # has prevented it from doing so before, it will do successfully. next unless STRIPPABLE_TYPES.include?(primitive) if self[att].respond_to?(:strip) self[att] = self[att].strip end end end |