module DataAccessible
  module DataAccessors
    module_function

    def accessor_for_obj(obj)
      obj.to_h.keys.each do |key|
        define_accessor(obj, key)
      end
    end

    def accessor_for_data(data)
      HashMethods.each_hash(data) do |hash|
        hash.each do |key, value|
          define_accessor(hash, key)
          accessor_for_data(value)
        end
      end
    end

    def define_accessor(obj, key)
      define_getter(obj, key)
      define_setter(obj, key)
    end

    def define_getter(obj, key)
      obj.define_singleton_method(key) do
        obj.to_h.fetch(key)
      end
    end

    def define_setter(obj, key)
      obj.define_singleton_method("#{key}=") do |value|
        obj.to_h[key] = DataAccessible::DataAccessors.accessor_for_data(value)
      end
    end
  end
end