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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 | ... | ... |