Commit 360f80bc865139f31c14a75c680c9893eb6d2771
0 parents
Exists in
master
and in
17 other branches
First commit
Showing
8 changed files
with
185 additions
and
0 deletions
Show diff stats
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. |
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 |
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 |
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 |