SpontaneousCMS/spontaneous

View on GitHub
lib/spontaneous/data_mapper/dataset.rb

Summary

Maintainability
B
5 hrs
Test Coverage
module Spontaneous
  module DataMapper
    class Dataset
      include Enumerable

      def initialize(dataset, schema, identity_map)
        @dataset, @schema, @identity_map = dataset, schema, identity_map
      end

      def count
        @dataset.count
      end

      def get(id)
        return get_all(id) if id.is_a?(Array)
        if (instance = @identity_map[id])
          instance
        else
          primary_key_lookup(id)
        end
      end

      # Gets all the instances for the list of ids and returns them
      # in the id order of the list, rather than the db order.
      def get_all(id_list)
        # This pure-db solution is Postgresql only unfortunately...
        #
        # values = []
        # id_list.each_with_index do |id, index|
        #   values << ("(" << [id, index].join(",") << ")")
        # end
        # ds = @dataset.filter(:id => id_list).qualify.
        # join(Sequel::LiteralString.new("(VALUES #{values.join(",")}) AS id_order (id, pos)"), {:id_order__id => :id}, {:qualify => true}).
        # order(:id_order__pos)
        order = Hash[id_list.each_with_index.map { |id, index| [id, index] }]
        filter(:id => id_list).all.sort { |i1, i2|
          order[i1.id] <=> order[i2.id]
        }
      end

      def primary_key_lookup(pk)
        first(id: pk)
      end

      def first(*args, &block)
        load_instance @dataset.first(*args, &block)
      end

      def insert(*values, &block)
        @dataset.insert(*values, &block)
      end

      def save(instance)
        attributes = instance.modified_attributes
        id = instance.id
        return insert(instance) if id.nil?
        return instance if attributes.empty?
        @dataset.where(id: id).update(attributes)
        instance
      end

      def update(columns)
        @dataset.update(columns)
      end

      def create(instance)
        id = @dataset.insert(attributes_for_insert(instance))
        instance.set_attributes_after_save!(get_unfiltered_raw(id))
        instance
      end

      def delete
        @dataset.delete
      end

      def delete_instance(instance)
        @dataset.unfiltered.filter(id: instance.id).delete
      end

      def destroy
        @dataset.each do |row|
          load_instance(row).destroy
        end
      end

      def filter(*cond, &block)
        @dataset.filter!(*cond, &block)
        self
      end

      def exclude(*cond, &block)
        @dataset.exclude!(*cond, &block)
        self
      end

      def where(*cond, &block)
        @dataset.where!(*cond, &block)
        self
      end

      def map(column = nil, &block)
        if column
          @dataset.map(column)
        else
          super(&block)
        end
      end

      def all
        load_instances @dataset.all
      end

      def reload(instance)
        attrs = get_raw(instance.id)
        instance.set_attributes!(attrs)
      end

      def and(*cond, &block)
        @dataset.and!(*cond, &block)
        self
      end

      def or(*cond, &block)
        @dataset.or!(*cond, &block)
        self
      end

      def each
        return enum_for(:each) unless block_given?
        @dataset.each do |r|
          yield load_instance(r)
        end
      end

      def for_update
        @dataset.for_update!
        self
      end

      def order(*columns, &block)
        @dataset.order!(*columns, &block)
        self
      end

      def limit(l, o = (_no_offset = true; nil))
        @dataset.limit!(l, o)
        self
      end

      def offset(o)
        @dataset.offset!(o)
        self
      end

      def select(*columns, &block)
        @dataset.select!(*columns, &block)
        self
      end

      def invert
        @dataset.invert!
        self
      end

      class PreparedStatement
        def initialize(name, ds, ps)
          @name = name
          @ds = ds
          @ps = ps
        end

        def call(vars = {})
          results = @ps.call(vars)
          case results
          when Array
            @ds.load_instances(results)
          else
            @ds.load_instance(results)
          end
        end
      end

      def prepare(type, name, *values)
        ps = @dataset.prepare(type, name, *values)
        PreparedStatement.new(name, self, ps)
      end

      def db
        @dataset.db
      end

      def qualify_to_first_source
        @dataset = @dataset.qualify_to_first_source
        self
      end

      def qualify(col)
        SQL::QualifiedIdentifier.new(table, k)
      end

      def to_sql
        @dataset.sql
      end

      def unfiltered
        @dataset.unfiltered
      end

      def ds
        @dataset
      end

      alias_method :sql, :to_sql

      def get_raw(id)
        @dataset.first(id: id)
      end

      def get_unfiltered_raw(id)
        @dataset.unfiltered.first(id: id)
      end

      def attributes_for_insert(instance)
        attributes_with_type_sid(instance.class, instance.attributes)
      end

      def attributes_with_type_sid(model, attributes)
        attributes = attributes.dup
        restricted = model.restricted_columns
        restricted.each { |col| attributes.delete(col)}
        attributes.update({
          type_sid: @schema.to_id(model)
        })
      end

      def load_instances(rows)
        rows.map { |row| load_instance(row) }
      end

      def load_instance(attributes)
        return nil if attributes.nil?
        allocate_instance(attributes)
      end

      def allocate_instance(attributes)
        return attributes if attributes[:type_sid].nil?
        if (instance = @identity_map[attributes[:id]])
          instance
        else
          model = @schema.to_class(attributes[:type_sid])
          return nil if model.nil?
          instance = model.new(attributes, true)
          @identity_map[instance.id] = instance
        end
      end

      def inspect
        %(#<Spontaneous::DataMapper::Dataset:#{object_id.to_s(16)} dataset=#{@dataset.sql}>)
      end
    end
  end
end