From d14700c51d692335f001a93c2f6b13b135783206 Mon Sep 17 00:00:00 2001 From: IN COMMON Collective Date: Thu, 8 Apr 2021 16:34:15 +0200 Subject: [FIX] Use form model to create/edit resources (fixes #4, fixes #5, refs #3) Since we must associate other models (e.g., classifications) to a Resource, we use a composite model to save all changes inside a database transaction. This approach makes it simpler to handle resources and their associations. Work remains to fix the geolocation and reverse geolocation to ensure these are in sync. --- app/models/resource_form.rb | 138 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 app/models/resource_form.rb (limited to 'app/models/resource_form.rb') diff --git a/app/models/resource_form.rb b/app/models/resource_form.rb new file mode 100644 index 0000000..14275a4 --- /dev/null +++ b/app/models/resource_form.rb @@ -0,0 +1,138 @@ +class ResourceForm + include ActiveModel::Model + + attr_accessor :agent, :resource + attr_accessor :agent_id, :uuid, :name, :summary, :description, :email, + :website, :phone_number, :address, :postal_code, :city, + :entry_number, :latitude, :longitude, :section_ids, :source + + with_options presence: true do + validates :name + validates :email, allow_blank: true +# validates :source + validates :phone_number, allow_blank: true + validates :website, allow_blank: true + end + + validate :validate_models + + def initialize(agent, attributes = nil) + @agent = agent + @attributes = attributes || default_attributes + + raise ArgumentError "Attributes must be permitted" unless @attributes.permitted? + + # We use an empty hidden field to ensure this is always set, even if no + # section is selected, so let's remove the empty value + @section_ids = @attributes.extract!(:section_ids)[:section_ids].filter_map { |x| x.presence } rescue [] + # We may have an existing and correct source, but anyway we must have one + @resource = @agent.resources.build({ source: @agent.name }.merge(@attributes)) + end + + def new_record? + resource&.new_record? || true + end + + # New forms can provide an existing classification, + # and updates can prefer passed arguments to ensure correct changes + def section_ids + resource.present? && @section_ids.empty? ? resource.section_ids : @section_ids + end + + # Ensure field values stick around + # OMG this is ugly + # TODO: use delegator or metaprog + def name + @name ||= resource&.name + end + def summary + @summary ||= resource&.summary + end + def description + @description ||= resource&.description + end + def email + @email ||= resource&.email + end + def website + @website ||= resource&.website + end + def phone_number + @phone_number ||= resource&.phone_number + end + def address + @address ||= resource&.address + end + def postal_code + @postal_code ||= resource&.postal_code + end + def city + @city ||= resource&.city + end + def longitude + @longitude ||= resource&.longitude + end + def latitude + @latitude ||= resource&.latitude + end + + + def save + Rails.logger.info "--- Calling SAVE ---" + return false if invalid? + + ActiveRecord::Base.transaction do + resource.save! && \ + @section_ids.each do |sec_id| + Rails.logger.info("section " + sec_id) + resource.categories << resource.classifications.find_or_create_by!(section_id: sec_id) + end + + true + rescue ActiveRecord::StatementInvalid => e + errors.add(:base, e.message) + false + end + + rescue Exception => e + Rails.logger.info "Unhandled Exception #{e.class}: #{e.message}" + false + end + + private + + def default_attributes + ActionController::Parameters.new( + { + agent_id: @agent.id, + uuid: '', + name: '', + summary: '', + description: '', + email: '', + website: '', + phone_number: '', + address: '', + postal_code: '', + city: '', + entry_number: nil, + latitude: 0.0, + longitude: 0.0, + source: @agent.name, + section_ids: [] + }).permit! + end + + # Validate underlying models + def validate_models + Rails.logger.info('--- validate_models ---') + resource.valid? || promote_errors(resource.errors) + end + + # Promote an error from the Model to the Form + def promote_errors(model_errors) + model_errors.each do |attribute, error| + errors.add(attribute, error.full_message) + end + end +end -- cgit v1.2.3