<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>dm-aggregates/lib/dm-aggregates/aggregate_functions.rb</filename>
    </added>
    <added>
      <filename>dm-aggregates/lib/dm-aggregates/support/symbol.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -5,8 +5,9 @@ require 'dm-core'
 
 dir = Pathname(__FILE__).dirname.expand_path / 'dm-aggregates'
 
-require dir / 'functions'
+require dir / 'aggregate_functions'
 require dir / 'model'
 require dir / 'repository'
 require dir / 'collection'
 require dir / 'adapters' / 'data_objects_adapter'
+require dir / 'support' / 'symbol'</diff>
      <filename>dm-aggregates/lib/dm-aggregates.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,51 +1,66 @@
 module DataMapper
   module Adapters
     class DataObjectsAdapter
-      def count(property, query)
-        query(aggregate_read_statement(:count, property, query), *query.bind_values).first
+      def aggregate(query)
+        with_reader(read_statement(query), query.bind_values) do |reader|
+          results = []
+
+          while(reader.next!) do
+            row = query.fields.zip(reader.values).map do |field,value|
+              if field.respond_to?(:operator)
+                send(field.operator, field.target, value)
+              else
+                field.typecast(value)
+              end
+            end
+
+            results &lt;&lt; (query.fields.size &gt; 1 ? row : row[0])
+          end
+
+          results
+        end
       end
 
-      def min(property, query)
-        min = query(aggregate_read_statement(:min, property, query), *query.bind_values).first
-        property.typecast(min)
+      private
+
+      def count(property, value)
+        value.to_i
+      end
+
+      def min(property, value)
+        property.typecast(value)
       end
 
-      def max(property, query)
-        max = query(aggregate_read_statement(:max, property, query), *query.bind_values).first
-        property.typecast(max)
+      def max(property, value)
+        property.typecast(value)
       end
 
-      def avg(property, query)
-        avg = query(aggregate_read_statement(:avg, property, query), *query.bind_values).first
-        property.type == Integer ? avg.to_f : property.typecast(avg)
+      def avg(property, value)
+        property.type == Integer ? value.to_f : property.typecast(value)
       end
 
-      def sum(property, query)
-        sum = query(aggregate_read_statement(:sum, property, query), *query.bind_values).first
-        property.typecast(sum)
+      def sum(property, value)
+        property.typecast(value)
       end
 
       module SQL
         private
 
-        def aggregate_read_statement(aggregate_function, property, query)
-          statement = &quot;SELECT #{aggregate_field_statement(query.repository, aggregate_function, property, query.links.any?)}&quot;
-          statement &lt;&lt; &quot;, #{fields_statement(query)}&quot;                unless query.fields.empty?
-          statement &lt;&lt; &quot; FROM #{quote_table_name(query.model.storage_name(query.repository.name))}&quot;
-          statement &lt;&lt; links_statement(query)                        if query.links.any?
-          statement &lt;&lt; &quot; WHERE #{conditions_statement(query)}&quot;       if query.conditions.any?
-          statement &lt;&lt; &quot; GROUP BY #{fields_statement(query)}&quot;        if query.unique?
-          statement &lt;&lt; &quot; ORDER BY #{order_statement(query)}&quot;         if query.order.any?
-          statement &lt;&lt; &quot; LIMIT #{quote_column_value(query.limit)}&quot;   if query.limit
-          statement &lt;&lt; &quot; OFFSET #{quote_column_value(query.offset)}&quot; if query.offset &amp;&amp; query.offset &gt; 0
-          statement
-        rescue =&gt; e
-          DataMapper.logger.error(&quot;QUERY INVALID: #{query.inspect} (#{e})&quot;)
-          raise e
+        alias original_property_to_column_name property_to_column_name
+
+        def property_to_column_name(repository, property, qualify)
+          case property
+            when Query::Operator
+              aggregate_field_statement(repository, property.operator, property.target, qualify)
+            when Property
+              original_property_to_column_name(repository, property, qualify)
+            else
+              raise ArgumentError, &quot;+property+ must be a DataMapper::Query::Operator or a DataMapper::Property, but was a #{property.class} (#{property.inspect})&quot;
+          end
         end
 
         def aggregate_field_statement(repository, aggregate_function, property, qualify)
-          column_name  = if aggregate_function == :count &amp;&amp; property.nil?
+          column_name = if aggregate_function == :count &amp;&amp; property == :all
             '*'
           else
             property_to_column_name(repository, property, qualify)</diff>
      <filename>dm-aggregates/lib/dm-aggregates/adapters/data_objects_adapter.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,28 +1,11 @@
 module DataMapper
   class Collection
-    include Aggregates
+    include AggregateFunctions
 
     private
 
-    def with_repository_and_property(*args, &amp;block)
-      query = args.last.respond_to?(:merge) ? args.pop : {}
-
-      if query.kind_of?(Hash)
-        if query.has_key?(:fields) &amp;&amp; query[:fields].any?
-          query[:unique] = true
-          query[:order] ||= query[:fields]
-        else
-          query[:fields] = []
-        end
-      end
-
-      property_name = args.first
-
-      query      = scoped_query(query)
-      repository = query.repository
-      property   = properties[property_name] if property_name
-
-      yield repository, property, query
+    def property_by_name(property_name)
+      properties[property_name]
     end
   end
 end</diff>
      <filename>dm-aggregates/lib/dm-aggregates/collection.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,28 +1,11 @@
 module DataMapper
   module Model
-    include Aggregates
+    include AggregateFunctions
 
     private
 
-    def with_repository_and_property(*args, &amp;block)
-      query = args.last.respond_to?(:merge) ? args.pop : {}
-
-      if query.kind_of?(Hash)
-        if query.has_key?(:fields) &amp;&amp; query[:fields].any?
-          query[:unique] = true
-          query[:order] ||= query[:fields]
-        else
-          query[:fields] = []
-        end
-      end
-
-      property_name = args.first
-
-      query      = scoped_query(query)
-      repository = query.repository
-      property   = properties(repository.name)[property_name] if property_name
-
-      yield repository, property, query
+    def property_by_name(property_name)
+      properties(repository.name)[property_name]
     end
   end
 end</diff>
      <filename>dm-aggregates/lib/dm-aggregates/model.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,23 +1,7 @@
 module DataMapper
   class Repository
-    def count(property, query)
-      adapter.count(property, query)
-    end
-
-    def min(property, query)
-      adapter.min(property, query)
-    end
-
-    def max(property, query)
-      adapter.max(property, query)
-    end
-
-    def avg(property, query)
-      adapter.avg(property, query)
-    end
-
-    def sum(property, query)
-      adapter.sum(property, query)
+    def aggregate(query)
+      adapter.aggregate(query)
     end
   end
 end</diff>
      <filename>dm-aggregates/lib/dm-aggregates/repository.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,33 +7,40 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
       # A simplistic example, using with an Integer property
       class Dragon
         include DataMapper::Resource
-        property :id, Serial
-        property :name, String
-        property :is_fire_breathing, TrueClass
-        property :toes_on_claw, Integer
 
-        auto_migrate!(:default)
+        property :id,                Serial
+        property :name,              String
+        property :is_fire_breathing, TrueClass
+        property :toes_on_claw,      Integer
+        property :birth_at,          DateTime
+        property :birth_on,          Date
+        property :birth_time,        Time
       end
 
-      Dragon.create(:name =&gt; 'George', :is_fire_breathing =&gt; false, :toes_on_claw =&gt; 3)
-      Dragon.create(:name =&gt; 'Puff',   :is_fire_breathing =&gt; true,  :toes_on_claw =&gt; 4)
-      Dragon.create(:name =&gt; nil,      :is_fire_breathing =&gt; true,  :toes_on_claw =&gt; 5)
       # A more complex example, with BigDecimal and Float properties
       # Statistics taken from CIA World Factbook:
       # https://www.cia.gov/library/publications/the-world-factbook/
       class Country
         include DataMapper::Resource
 
-        property :id,                  Integer, :serial =&gt; true
-        property :name,                String,  :nullable =&gt; false
+        property :id,                  Serial
+        property :name,                String,     :nullable =&gt; false
         property :population,          Integer
         property :birth_rate,          Float,      :precision =&gt; 4,  :scale =&gt; 2
         property :gold_reserve_tonnes, Float,      :precision =&gt; 6,  :scale =&gt; 2
         property :gold_reserve_value,  BigDecimal, :precision =&gt; 15, :scale =&gt; 1  # approx. value in USD
-
-        auto_migrate!(:default)
       end
 
+      [ Dragon, Country ].each { |m| m.auto_migrate! }
+
+      @birth_at   = DateTime.now
+      @birth_on   = Date.parse(@birth_at.to_s)
+      @birth_time = Time.parse(@birth_at.to_s)
+
+      Dragon.create(:name =&gt; 'George', :is_fire_breathing =&gt; false, :toes_on_claw =&gt; 3, :birth_at =&gt; @birth_at, :birth_on =&gt; @birth_on, :birth_time =&gt; @birth_time)
+      Dragon.create(:name =&gt; 'Puff',   :is_fire_breathing =&gt; true,  :toes_on_claw =&gt; 4, :birth_at =&gt; @birth_at, :birth_on =&gt; @birth_on, :birth_time =&gt; @birth_time)
+      Dragon.create(:name =&gt; nil,      :is_fire_breathing =&gt; true,  :toes_on_claw =&gt; 5, :birth_at =&gt; nil,       :birth_on =&gt; nil,       :birth_time =&gt; nil)
+
       gold_kilo_price  = 277738.70
       @gold_tonne_price = gold_kilo_price * 10000
 
@@ -144,6 +151,21 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
             target(Country, target_type).min(:gold_reserve_value).should == BigDecimal('1217050983400.0')
           end
 
+          it 'should provide the lowest value of a DateTime property' do
+            target(Dragon, target_type).min(:birth_at).should be_kind_of(DateTime)
+            target(Dragon, target_type).min(:birth_at).to_s.should == @birth_at.to_s
+          end
+
+          it 'should provide the lowest value of a Date property' do
+            target(Dragon, target_type).min(:birth_on).should be_kind_of(Date)
+            target(Dragon, target_type).min(:birth_on).to_s.should == @birth_on.to_s
+          end
+
+          it 'should provide the lowest value of a Time property' do
+            target(Dragon, target_type).min(:birth_time).should be_kind_of(Time)
+            target(Dragon, target_type).min(:birth_time).to_s.should == @birth_time.to_s
+          end
+
           it 'should provide the lowest value when conditions provided' do
             target(Dragon, target_type).min(:toes_on_claw, :is_fire_breathing =&gt; true).should  == 4
             target(Dragon, target_type).min(:toes_on_claw, :is_fire_breathing =&gt; false).should == 3
@@ -174,6 +196,21 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
             target(Country, target_type).max(:gold_reserve_value).should == BigDecimal('22589877164500.0')
           end
 
+          it 'should provide the highest value of a DateTime property' do
+            target(Dragon, target_type).min(:birth_at).should be_kind_of(DateTime)
+            target(Dragon, target_type).min(:birth_at).to_s.should == @birth_at.to_s
+          end
+
+          it 'should provide the highest value of a Date property' do
+            target(Dragon, target_type).min(:birth_on).should be_kind_of(Date)
+            target(Dragon, target_type).min(:birth_on).to_s.should == @birth_on.to_s
+          end
+
+          it 'should provide the highest value of a Time property' do
+            target(Dragon, target_type).min(:birth_time).should be_kind_of(Time)
+            target(Dragon, target_type).min(:birth_time).to_s.should == @birth_time.to_s
+          end
+
           it 'should provide the highest value when conditions provided' do
             target(Dragon, target_type).max(:toes_on_claw, :is_fire_breathing =&gt; true).should  == 5
             target(Dragon, target_type).max(:toes_on_claw, :is_fire_breathing =&gt; false).should == 3
@@ -247,6 +284,28 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
           end
         end
       end
+
+      describe &quot;.aggregate on a #{target_type}&quot; do
+        describe 'with no arguments' do
+          it 'should raise an error' do
+            lambda { target(Dragon, target_type).aggregate }.should raise_error(ArgumentError)
+          end
+        end
+
+        describe 'with only aggregate fields specified' do
+          it 'should provide aggregate results' do
+            results = target(Dragon, target_type).aggregate(:all.count, :name.count, :toes_on_claw.min, :toes_on_claw.max, :toes_on_claw.avg, :toes_on_claw.sum)
+            results.should == [ 3, 2, 3, 5, 4.0, 12 ]
+          end
+        end
+
+        describe 'with aggregate fields and a property to group by' do
+          it 'should provide aggregate results' do
+            results = target(Dragon, target_type).aggregate(:all.count, :name.count, :toes_on_claw.min, :toes_on_claw.max, :toes_on_claw.avg, :toes_on_claw.sum, :is_fire_breathing)
+            results.should == [ [ 1, 1, 3, 3, 3.0, 3, false ], [ 2, 1, 4, 5, 4.5, 9, true ] ]
+          end
+        end
+      end
     end
   end
 end</diff>
      <filename>dm-aggregates/spec/integration/aggregates_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>dm-aggregates/lib/dm-aggregates/functions.rb</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>c366fee263c578c2363b2242d61b935d8b556094</id>
    </parent>
  </parents>
  <author>
    <name>Dan Kubb</name>
    <email>dan.kubb@autopilotmarketing.com</email>
  </author>
  <url>http://github.com/sam/dm-more/commit/06a9599d5ed86b70441ce9fc15cc296470b42a93</url>
  <id>06a9599d5ed86b70441ce9fc15cc296470b42a93</id>
  <committed-date>2008-07-15T12:37:10-07:00</committed-date>
  <authored-date>2008-07-15T12:37:10-07:00</authored-date>
  <message>Added Model#aggregate and Collection#aggregate methods

* New aggregate method allows mixing aggregate function calls in a
  single query.  All other aggregate methods have been refactored to
  use it under the hood.  If :fields option is specified it will
  automatically GROUP BY the specified fields.  If no :order option
  specified, it will automatically ORDER BY the :fields.
* Updated documentation to match latest YARD standards (although unsure
  about the @example tags, just following convention in dm-core)</message>
  <tree>ee05ee97d681ab2ecdf55e22046114ce03c8b083</tree>
  <committer>
    <name>Dan Kubb</name>
    <email>dan.kubb@autopilotmarketing.com</email>
  </committer>
</commit>
