groupdate 2.0.4 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 30c3377f6edb6fe33f5d0ac0aab691a6a7a6c198
4
- data.tar.gz: d725a9a605553a390055a4bbf55a132233aade70
3
+ metadata.gz: 08308d10dc780963998b4ebcb906f802dd15f6a7
4
+ data.tar.gz: 4e2a7f3a352b94f22287e2e84e4e8830625914ca
5
5
  SHA512:
6
- metadata.gz: d121c447a7c6c4c6b9be3e28942c640ea4f7bb5da420314eb863d7ff9a1a648c77129afe726ae3311a61cef6a32d386fbf76e97948adc0313f0a87935f4fe181
7
- data.tar.gz: 13e3ba1f7530160f450cb48aa1e5e67f65af979602e2c3ff0d392cc436079db1c12cd457c122e30d63e1c5cac75001af3dafb4b3839520402d1b115015200613
6
+ metadata.gz: d302a57517c823e80b7192a42153bc4a594af23bd1647f6cd7f3eab77a57252882204e22d40e4a6a3a80253be4adce6577cb9fa723d52342f7f8d8cf4a725d77
7
+ data.tar.gz: 0fdeb289ba29919be219e0dc1cdaf040552ffdc2ffa1ed5c180178ba8c8f4bb8aa00d8ffb0862eb5030af8996144098b2d03380c96d0a5f97ce248362b148578
@@ -1,3 +1,8 @@
1
+ # 2.1.0
2
+
3
+ - added last option
4
+ - added format option
5
+
1
6
  # 2.0.4
2
7
 
3
8
  - added multiple groups
data/README.md CHANGED
@@ -107,6 +107,12 @@ To get a specific time range, use:
107
107
  User.group_by_day(:created_at, range: 2.weeks.ago.midnight..Time.now).count
108
108
  ```
109
109
 
110
+ To get the most recent time periods, use:
111
+
112
+ ```ruby
113
+ User.group_by_week(:created_at, last: 8).count # last 8 weeks
114
+ ```
115
+
110
116
  ### Order
111
117
 
112
118
  You can order in descending order with:
@@ -121,6 +127,16 @@ or
121
127
  User.group_by_day(:created_at).order("day desc").count
122
128
  ```
123
129
 
130
+ ### Pretty Keys
131
+
132
+ To get keys in a different format, use:
133
+
134
+ ```ruby
135
+ User.group_by_hour_of_day(:created_at, format: "%l %P").count.keys.first # 12 am
136
+ ```
137
+
138
+ Takes a `String`, which is passed to [strftime](http://strfti.me/), or a `Proc`
139
+
124
140
  ## Installation
125
141
 
126
142
  Add this line to your application’s Gemfile:
@@ -76,7 +76,7 @@ module Groupdate
76
76
  group = group(Groupdate::OrderHack.new(sanitize_sql_array(query), field, time_zone))
77
77
  range = args[2] || options[:range] || true
78
78
  unless options[:series] == false
79
- Series.new(group, field, column, time_zone_object, range, week_start, day_start, group.group_values.size - 1)
79
+ Series.new(group, field, column, time_zone_object, range, week_start, day_start, group.group_values.size - 1, options)
80
80
  else
81
81
  group
82
82
  end
@@ -1,7 +1,8 @@
1
1
  module Groupdate
2
2
  class Series
3
+ attr_accessor :relation
3
4
 
4
- def initialize(relation, field, column, time_zone, time_range, week_start, day_start, group_index)
5
+ def initialize(relation, field, column, time_zone, time_range, week_start, day_start, group_index, options)
5
6
  @relation = relation
6
7
  @field = field
7
8
  @column = column
@@ -10,20 +11,42 @@ module Groupdate
10
11
  @week_start = week_start
11
12
  @day_start = day_start
12
13
  @group_index = group_index
14
+ @options = options
13
15
  end
14
16
 
15
- def build_series(count)
17
+ def perform(method, *args, &block)
16
18
  utc = ActiveSupport::TimeZone["UTC"]
17
19
 
18
- reverse = @reverse
19
- order = @relation.order_values.first
20
+ time_range = @time_range
21
+ if !time_range.is_a?(Range) and @options[:last]
22
+ step = 1.send(@field) if 1.respond_to?(@field)
23
+ if step
24
+ now = Time.now
25
+ time_range = round_time(now - (@options[:last].to_i - 1).send(@field))..now
26
+ end
27
+ end
28
+
29
+ relation =
30
+ if time_range.is_a?(Range)
31
+ # doesn't matter whether we include the end of a ... range - it will be excluded later
32
+ @relation.where("#{@column} >= ? AND #{@column} <= ?", time_range.first, time_range.last)
33
+ else
34
+ @relation.where("#{@column} IS NOT NULL")
35
+ end
36
+
37
+ # undo reverse since we do not want this to appear in the query
38
+ reverse = relation.reverse_order_value
39
+ if reverse
40
+ relation = relation.reverse_order
41
+ end
42
+ order = relation.order_values.first
20
43
  if order.is_a?(String)
21
44
  parts = order.split(" ")
22
45
  reverse_order = (parts.size == 2 && parts[0] == @field && parts[1].to_s.downcase == "desc")
23
46
  reverse = !reverse if reverse_order
24
47
  end
25
48
 
26
- multiple_groups = @relation.group_values.size > 1
49
+ multiple_groups = relation.group_values.size > 1
27
50
 
28
51
  cast_method =
29
52
  case @field
@@ -33,7 +56,7 @@ module Groupdate
33
56
  lambda{|k| (k.is_a?(String) ? utc.parse(k) : k.to_time).in_time_zone(@time_zone) }
34
57
  end
35
58
 
36
- count = Hash[ count.map{|k, v| [multiple_groups ? k[0...@group_index] + [cast_method.call(k[@group_index])] + k[(@group_index + 1)..-1] : cast_method.call(k), v] } ]
59
+ count = Hash[ relation.send(method, *args, &block).map{|k, v| [multiple_groups ? k[0...@group_index] + [cast_method.call(k[@group_index])] + k[(@group_index + 1)..-1] : cast_method.call(k), v] } ]
37
60
 
38
61
  series =
39
62
  case @field
@@ -43,8 +66,8 @@ module Groupdate
43
66
  0..23
44
67
  else
45
68
  time_range =
46
- if @time_range.is_a?(Range)
47
- @time_range
69
+ if time_range.is_a?(Range)
70
+ time_range
48
71
  else
49
72
  # use first and last values
50
73
  sorted_keys =
@@ -57,30 +80,7 @@ module Groupdate
57
80
  end
58
81
 
59
82
  if time_range.first
60
- # determine start time
61
- time = time_range.first.to_time.in_time_zone(@time_zone) - @day_start.hours
62
- starts_at =
63
- case @field
64
- when "second"
65
- time.change(:usec => 0)
66
- when "minute"
67
- time.change(:sec => 0)
68
- when "hour"
69
- time.change(:min => 0)
70
- when "day"
71
- time.beginning_of_day
72
- when "week"
73
- # same logic as MySQL group
74
- weekday = (time.wday - 1) % 7
75
- (time - ((7 - @week_start + weekday) % 7).days).midnight
76
- when "month"
77
- time.beginning_of_month
78
- else # year
79
- time.beginning_of_year
80
- end
81
-
82
- starts_at += @day_start.hours
83
- series = [starts_at]
83
+ series = [round_time(time_range.first)]
84
84
 
85
85
  step = 1.send(@field)
86
86
 
@@ -107,31 +107,70 @@ module Groupdate
107
107
  series = series.to_a.reverse
108
108
  end
109
109
 
110
+ key_format =
111
+ if @options[:format]
112
+ if @options[:format].respond_to?(:call)
113
+ @options[:format]
114
+ else
115
+ sunday = @time_zone.parse("2014-03-02 00:00:00")
116
+ lambda do |key|
117
+ case @field
118
+ when "hour_of_day"
119
+ key = sunday + key.hours + @day_start.hours
120
+ when "day_of_week"
121
+ key = sunday + key.days
122
+ end
123
+ key.strftime(@options[:format].to_s)
124
+ end
125
+ end
126
+ else
127
+ lambda{|k| k }
128
+ end
129
+
110
130
  Hash[series.map do |k|
111
- [k, count[k] || 0]
131
+ [key_format.call(k), count[k] || 0]
112
132
  end]
113
133
  end
114
134
 
115
- def method_missing(method, *args, &block)
116
- # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/calculations.rb
117
- if ActiveRecord::Calculations.method_defined?(method)
118
- relation =
119
- if @time_range.is_a?(Range)
120
- # doesn't matter whether we include the end of a ... range - it will be excluded later
121
- @relation.where("#{@column} >= ? AND #{@column} <= ?", @time_range.first, @time_range.last)
122
- else
123
- @relation.where("#{@column} IS NOT NULL")
124
- end
135
+ def round_time(time)
136
+ time = time.to_time.in_time_zone(@time_zone) - @day_start.hours
125
137
 
126
- # undo reverse since we do not want this to appear in the query
127
- if relation.reverse_order_value
128
- @reverse = true
129
- relation = relation.reverse_order
138
+ time =
139
+ case @field
140
+ when "second"
141
+ time.change(:usec => 0)
142
+ when "minute"
143
+ time.change(:sec => 0)
144
+ when "hour"
145
+ time.change(:min => 0)
146
+ when "day"
147
+ time.beginning_of_day
148
+ when "week"
149
+ # same logic as MySQL group
150
+ weekday = (time.wday - 1) % 7
151
+ (time - ((7 - @week_start + weekday) % 7).days).midnight
152
+ when "month"
153
+ time.beginning_of_month
154
+ else # year
155
+ time.beginning_of_year
130
156
  end
131
157
 
132
- build_series(relation.send(method, *args, &block))
158
+ time + @day_start.hours
159
+ end
160
+
161
+ def clone
162
+ Groupdate::Series.new(@relation, @field, @column, @time_zone, @time_range, @week_start, @day_start, @group_index, @options)
163
+ end
164
+
165
+ # clone to prevent modifying original variables
166
+ def method_missing(method, *args, &block)
167
+ # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/calculations.rb
168
+ if ActiveRecord::Calculations.method_defined?(method)
169
+ clone.perform(method, *args, &block)
133
170
  elsif @relation.respond_to?(method)
134
- Groupdate::Series.new(@relation.send(method, *args, &block), @field, @column, @time_zone, @time_range, @week_start, @day_start, @group_index)
171
+ series = clone
172
+ series.relation = @relation.send(method, *args, &block)
173
+ series
135
174
  else
136
175
  super
137
176
  end
@@ -1,3 +1,3 @@
1
1
  module Groupdate
2
- VERSION = "2.0.4"
2
+ VERSION = "2.1.0"
3
3
  end
@@ -549,8 +549,58 @@ module TestGroupdate
549
549
  assert !User.group_by_day(:created_at).respond_to?(:no_such_method)
550
550
  end
551
551
 
552
+ def test_last
553
+ create_user "2011-05-01 00:00:00 UTC"
554
+ create_user "2013-05-01 00:00:00 UTC"
555
+ expected = {
556
+ utc.parse("2012-01-01 00:00:00 UTC") => 0,
557
+ utc.parse("2013-01-01 00:00:00 UTC") => 1,
558
+ utc.parse("2014-01-01 00:00:00 UTC") => 0
559
+ }
560
+ assert_equal expected, User.group_by_year(:created_at, last: 3).count
561
+ end
562
+
563
+ def test_format_day
564
+ create_user "2014-03-01 00:00:00 UTC"
565
+ assert_format :day, "March 1, 2014", "%B %-e, %Y"
566
+ end
567
+
568
+ def test_format_month
569
+ create_user "2014-03-01 00:00:00 UTC"
570
+ assert_format :month, "March 2014", "%B %Y"
571
+ end
572
+
573
+ def test_format_year
574
+ create_user "2014-03-01 00:00:00 UTC"
575
+ assert_format :year, "2014", "%Y"
576
+ end
577
+
578
+ def test_format_hour_of_day
579
+ create_user "2014-03-01 00:00:00 UTC"
580
+ assert_format :hour_of_day, "12 am", "%-l %P"
581
+ end
582
+
583
+ def test_format_hour_of_day_day_start
584
+ create_user "2014-03-01 00:00:00 UTC"
585
+ assert_format :hour_of_day, "2 am", "%-l %P", day_start: 2
586
+ end
587
+
588
+ def test_format_day_of_week
589
+ create_user "2014-03-01 00:00:00 UTC"
590
+ assert_format :day_of_week, "Sun", "%a"
591
+ end
592
+
593
+ def test_format_day_of_week_week_start
594
+ create_user "2014-03-01 00:00:00 UTC"
595
+ assert_format :day_of_week, "Sun", "%a", week_start: :sat
596
+ end
597
+
552
598
  # helpers
553
599
 
600
+ def assert_format(method, expected, format, options = {})
601
+ assert_equal expected, User.send(:"group_by_#{method}", :created_at, options.merge(format: format)).count.keys.first
602
+ end
603
+
554
604
  def assert_result_time(method, expected, time_str, time_zone = false, options = {})
555
605
  expected = {utc.parse(expected).in_time_zone(time_zone ? "Pacific Time (US & Canada)" : utc) => 1}
556
606
  assert_equal expected, result(method, time_str, time_zone, options)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: groupdate
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.4
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-13 00:00:00.000000000 Z
11
+ date: 2014-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord