Commit 360f80bc865139f31c14a75c680c9893eb6d2771

Authored by Andrew Kane
0 parents

First commit

.gitignore 0 → 100644
  1 +++ a/.gitignore
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +*.gem
  2 +*.rbc
  3 +.bundle
  4 +.config
  5 +.yardoc
  6 +Gemfile.lock
  7 +InstalledFiles
  8 +_yardoc
  9 +coverage
  10 +doc/
  11 +lib/bundler/man
  12 +pkg
  13 +rdoc
  14 +spec/reports
  15 +test/tmp
  16 +test/version_tmp
  17 +tmp
Gemfile 0 → 100644
  1 +++ a/Gemfile
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +# Specify your gem's dependencies in groupdate.gemspec
  4 +gemspec
LICENSE.txt 0 → 100644
  1 +++ a/LICENSE.txt
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +Copyright (c) 2013 Andrew Kane
  2 +
  3 +MIT License
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining
  6 +a copy of this software and associated documentation files (the
  7 +"Software"), to deal in the Software without restriction, including
  8 +without limitation the rights to use, copy, modify, merge, publish,
  9 +distribute, sublicense, and/or sell copies of the Software, and to
  10 +permit persons to whom the Software is furnished to do so, subject to
  11 +the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be
  14 +included in all copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
README.md 0 → 100644
  1 +++ a/README.md
@@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
  1 +# Groupdate
  2 +
  3 +The simplest way to group by:
  4 +
  5 +- day
  6 +- week
  7 +- month
  8 +- year
  9 +- hour
  10 +- microseconds
  11 +- milliseconds
  12 +- second
  13 +- minute
  14 +- quarter
  15 +- decade
  16 +- century
  17 +- millennium
  18 +
  19 +:tada: Time zones supported!!
  20 +
  21 +PostgreSQL only at the moment - support for other datastores coming soon
  22 +
  23 +## Usage
  24 +
  25 +```ruby
  26 +User.group_by_day(:created_at).count
  27 +# => {2013-04-16 00:00:00 UTC=>50,2013-04-17 00:00:00 UTC=>100}
  28 +
  29 +Task.group_by_month(:updated_at).count
  30 +# => {2013-04-01 00:00:00 UTC=>23,2013-04-01 00:00:00 UTC=>44}
  31 +
  32 +Goal.group_by_year(:accomplished_at).count
  33 +# => {2012-01-01 00:00:00 UTC=>11,2013-01-01 00:00:00 UTC=>3}
  34 +```
  35 +
  36 +The default time zone is `Time.zone`. Pass a time zone as the second argument.
  37 +
  38 +```ruby
  39 +User.group_by_day(:created_at, ActiveSupport::TimeZone["Pacific Time (US & Canada)"]).count
  40 +# => {2013-04-16 00:00:00 UTC=>80,2013-04-17 00:00:00 UTC=>70}
  41 +```
  42 +
  43 +Use it with anything that you can use `group` with:
  44 +
  45 +```ruby
  46 +User.group_by_week(:created_at).sum(:tasks_count)
  47 +
  48 +User.group_by_hour(:created_at).average(:tasks_count)
  49 +
  50 +User.group_by_quarter(:created_at).maximum(:tasks_count)
  51 +
  52 +User.group_by_second(:created_at).average(:tasks_count)
  53 +```
  54 +
  55 +## Installation
  56 +
  57 +Add this line to your application's Gemfile:
  58 +
  59 +```ruby
  60 +gem 'groupdate'
  61 +```
  62 +
  63 +## Contributing
  64 +
  65 +1. Fork it
  66 +2. Create your feature branch (`git checkout -b my-new-feature`)
  67 +3. Commit your changes (`git commit -am 'Add some feature'`)
  68 +4. Push to the branch (`git push origin my-new-feature`)
  69 +5. Create new Pull Request
Rakefile 0 → 100644
  1 +++ a/Rakefile
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +require "bundler/gem_tasks"
groupdate.gemspec 0 → 100644
  1 +++ a/groupdate.gemspec
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +# coding: utf-8
  2 +lib = File.expand_path('../lib', __FILE__)
  3 +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
  4 +require 'groupdate/version'
  5 +
  6 +Gem::Specification.new do |spec|
  7 + spec.name = "groupdate"
  8 + spec.version = Groupdate::VERSION
  9 + spec.authors = ["Andrew Kane"]
  10 + spec.email = ["acekane1@gmail.com"]
  11 + spec.description = %q{The simplest way to group temporal data}
  12 + spec.summary = %q{The simplest way to group temporal data}
  13 + spec.homepage = ""
  14 + spec.license = "MIT"
  15 +
  16 + spec.files = `git ls-files`.split($/)
  17 + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  18 + spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
  19 + spec.require_paths = ["lib"]
  20 +
  21 + spec.add_dependency "activerecord", ">= 3.0.0"
  22 +
  23 + spec.add_development_dependency "bundler", "~> 1.3"
  24 + spec.add_development_dependency "rake"
  25 +end
lib/groupdate.rb 0 → 100644
  1 +++ a/lib/groupdate.rb
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
  1 +require "groupdate/version"
  2 +
  3 +module Groupdate
  4 + extend ActiveSupport::Concern
  5 +
  6 + # Pattern from kaminari
  7 + # https://github.com/amatsuda/kaminari/blob/master/lib/kaminari/models/active_record_extension.rb
  8 + included do
  9 + # Future subclasses will pick up the model extension
  10 + class << self
  11 + def inherited_with_groupdate(kls) #:nodoc:
  12 + inherited_without_groupdate kls
  13 + kls.send(:include, ClassMethods) if kls.superclass == ActiveRecord::Base
  14 + end
  15 + alias_method_chain :inherited, :groupdate
  16 + end
  17 +
  18 + # Existing subclasses pick up the model extension as well
  19 + self.descendants.each do |kls|
  20 + kls.send(:include, ClassMethods) if kls.superclass == ActiveRecord::Base
  21 + end
  22 + end
  23 +
  24 + module ClassMethods
  25 + extend ActiveSupport::Concern
  26 +
  27 + included do
  28 + # Field list from
  29 + # http://www.postgresql.org/docs/9.1/static/functions-datetime.html
  30 + %w(microseconds milliseconds second minute hour day week month quarter year decade century millennium).each do |field|
  31 + self.scope :"group_by_#{field}", lambda {|column, time_zone = Time.zone|
  32 + if time_zone.is_a?(ActiveSupport::TimeZone)
  33 + time_zone = time_zone.tzinfo.name
  34 + end
  35 + sql = "DATE_TRUNC('#{field}', #{column}::timestamptz AT TIME ZONE ?) AT TIME ZONE ?"
  36 + group(sanitize_sql_array([sql, time_zone, time_zone]))
  37 + }
  38 + end
  39 + end
  40 + end
  41 +
  42 +end
  43 +
  44 +ActiveRecord::Base.send :include, Groupdate
lib/groupdate/version.rb 0 → 100644
  1 +++ a/lib/groupdate/version.rb
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +module Groupdate
  2 + VERSION = "0.0.1"
  3 +end