michaelminter
7/16/2018 - 5:22 PM

exercise_wrapper.rb

module Contents
  class ExerciseWrapper < Contents::BaseWrapper
    attribute :name, String
    attribute :type

    validate :name, presence: true

    def to_fhir
      {
        one: :attribute_name,
        two: {
          a: :attribute_name,
          b: :attribute_name
        }
      }
    end
  end
end
module Contents
  class ValidationError < StandardError
    attr_reader :object

    def initialize(object)
      @object = object
    end
  end

  class BaseWrapper
    attr_accessor :attributes, :errors

    # rescue_from ValidationError, with: :validation_error

    class << self
      def attribute(attribute_name, klass = String, options = {})
        @default_attributes[attribute_name.to_sym] = {}
        @default_attributes[attribute_name.to_sym][:klass] = klass
        @default_attributes[attribute_name.to_sym][:value] = options[:default].present? ? options[:default] : nil
      end

      def validate(attribute_name, options = {})
        @rules = []
        options.each do |option_name, option_value|
          @rules << valid_attribute?(attribute_name, option_name, option_value)
        end
      end

      def valid_attribute?(attribute_name, validation_name, validation_value)
        attribute = @default_attributes[attribute_name.to_sym]

        case validation_name.to_sym
        when :presence
          attribute[:value].present? ? true : raise(ValidationError, "#{attribute_name} must be present.")
        when :includes
          attribute[:value].includes?(validation_value) || raise(ValidationError, "#{attribute_name} must include #{validation_value}.")
        end
      end
    end

    def initialize(attributes) # TODO: accept JSON blob
      @errors = []
      @attributes = attributes
      @default_attributes = {}

      attributes.each do |attribute_name, attribute_value|
        # Manually creates methods for both getter and setter and then
        self.class.send(:define_method, "#{attribute_name}=".to_sym) do |value|
          instance_variable_set("@#{attribute_name}", value)
        end

        self.class.send(:define_method, attribute_name.to_sym) do
          instance_variable_get("@#{attribute_name}")
        end

        # Sends a message to the new setter with the attribute_value
        self.send("#{attribute_name}=".to_sym, attribute_value)
      end
    end

    def success?
      errors.empty?
    end

    def data
      # TODO: deep link to_fhir data with default attributes
      # @default_attributes.merge(attributes.map{ |k, v| { k.to_sym => valueize(v[:klass], v[:value]) } })
    end

    def valueize(klass, value)
      case klass
      when String
        value.to_s
      when Integer
        value.to_i
      when Float
        value.to_f
      end
    end

    def validation_error(error)
      errors << error
    end
  end
end