aggtive_record 0.0.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NzY2MmY5YWYwNGY3NTQ2OGZjOWFkNmQ0OGMzMGY0OTBhYTI5YWU5ZQ==
4
+ YzUxOTY3OTI2MWVmYjg0ZWU0YzVmOTUyMjA0NzhmNGEzMTU5ODhhMg==
5
5
  data.tar.gz: !binary |-
6
- OGMyYzBiZTRiYWI1NjBhYzA2YTFmYzAwY2U0YzI1MWU0MzYyYmM5Yg==
6
+ YmIzZWJjOGMzMzEzYTc0NzQwZjc0ODQ1YjUyNTVjZWU4ZTFhMmNmZA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- MzFjZDIyMzk5MjVlOTNjYjA0ZWFlZjZhM2Q1MzcxMmUzZjQxNWQ2NTZmODRj
10
- ZDFkMGIwZjNiODNhODk1NWU1Zjg2Y2UwNzFlM2E5ZDM0YTgwM2UzODA1NzA3
11
- MmJkN2FiNjFjNGU1NTRkYWNiMzBmNTgyYWE1ODM3MTU2OWI2NGM=
9
+ ZDBhZGYwOThkN2U5NjQ4NjNiOTA1ZDdkMTA1MTE4YmNmYjI3M2VjZWQxZDg0
10
+ NWZkNDE2NDYxNGRiM2QxYjMwMTJmNDIwMDM2MGFhYTAzNDBhM2Y5MGFmMjQw
11
+ MzI3NGRiYjkzZjMwNWU5NjdiYzhkMjA4OTA5YjBkMmZhNzExYzg=
12
12
  data.tar.gz: !binary |-
13
- OWYzYjlhNjgwMWZmZjc2MjBmYTUxMjEzYThmOTY0NDQ0M2VhMTg2ZTlmMzYz
14
- M2FlZDQ5NzlmMGJmYTc2ZTczZmY0MDFmNGExNWQwNmUxOTE2NzFmMWUwZDRi
15
- MjMwMzZlYjEzOTIyZGFiMzZkYmRmNWYyYTZiMzI2NzJmODE5NWQ=
13
+ ZWQyMjNiMzE3Y2ZlYzZiYmE5MWJkYjdhOWQ1NGM3ODY4YzhmYmUyZDJhNWJl
14
+ MTg4MWUxOWRlZThkNTYyMWM5ZWUyNWZkN2QwN2MyNzQwNmRjNDhjYzM1ZDc0
15
+ NzdhMGU3OWVhNGVmMDRhZjNmMDkyY2Q4Yzk4NTVjODlkYzc1ZjY=
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ aggtive_record
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-head
data/Gemfile CHANGED
@@ -6,6 +6,10 @@ group :development do
6
6
  gem "bundler"
7
7
  gem "jeweler", "~> 1.8.4"
8
8
  gem 'pry'
9
+ gem 'database_cleaner', '~> 1.0.1'
10
+ gem 'mysql2'
11
+ gem 'activerecord', '~> 3.2.14'
9
12
  end
10
13
 
14
+ gem 'groupdate'
11
15
  gem 'activesupport', '~> 3.2.14'
data/Gemfile.lock CHANGED
@@ -1,12 +1,22 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ activemodel (3.2.14)
5
+ activesupport (= 3.2.14)
6
+ builder (~> 3.0.0)
7
+ activerecord (3.2.14)
8
+ activemodel (= 3.2.14)
9
+ activesupport (= 3.2.14)
10
+ arel (~> 3.0.2)
11
+ tzinfo (~> 0.3.29)
4
12
  activesupport (3.2.14)
5
13
  i18n (~> 0.6, >= 0.6.4)
6
14
  multi_json (~> 1.0)
7
15
  addressable (2.3.5)
8
- builder (3.2.2)
16
+ arel (3.0.2)
17
+ builder (3.0.4)
9
18
  coderay (1.0.9)
19
+ database_cleaner (1.0.1)
10
20
  diff-lcs (1.2.4)
11
21
  faraday (0.8.8)
12
22
  multipart-post (~> 1.2.0)
@@ -18,6 +28,8 @@ GEM
18
28
  multi_json (~> 1.4)
19
29
  nokogiri (~> 1.5.2)
20
30
  oauth2
31
+ groupdate (1.0.4)
32
+ activerecord (>= 3.0.0)
21
33
  hashie (2.0.5)
22
34
  highline (1.6.19)
23
35
  httpauth (0.2.0)
@@ -35,9 +47,10 @@ GEM
35
47
  jwt (0.1.8)
36
48
  multi_json (>= 1.5)
37
49
  method_source (0.8.2)
38
- multi_json (1.7.9)
50
+ multi_json (1.8.0)
39
51
  multi_xml (0.5.5)
40
52
  multipart-post (1.2.0)
53
+ mysql2 (0.3.13)
41
54
  nokogiri (1.5.10)
42
55
  oauth2 (0.9.2)
43
56
  faraday (~> 0.8)
@@ -59,17 +72,22 @@ GEM
59
72
  rspec-expectations (~> 2.14.0)
60
73
  rspec-mocks (~> 2.14.0)
61
74
  rspec-core (2.14.5)
62
- rspec-expectations (2.14.2)
75
+ rspec-expectations (2.14.3)
63
76
  diff-lcs (>= 1.1.3, < 2.0)
64
77
  rspec-mocks (2.14.3)
65
78
  slop (3.4.6)
79
+ tzinfo (0.3.37)
66
80
 
67
81
  PLATFORMS
68
82
  ruby
69
83
 
70
84
  DEPENDENCIES
85
+ activerecord (~> 3.2.14)
71
86
  activesupport (~> 3.2.14)
72
87
  bundler
88
+ database_cleaner (~> 1.0.1)
89
+ groupdate
73
90
  jeweler (~> 1.8.4)
91
+ mysql2
74
92
  pry
75
93
  rspec (~> 2.14.1)
data/README.md CHANGED
@@ -61,31 +61,31 @@ This gem takes some inspiration from the highly useful groupdate.
61
61
 
62
62
  @person.messages_count_during_past_year_per_day
63
63
 
64
- @person.rate_word_count_from_messages_during_past_year_per_day
64
+ @person.messages_rate_per_day_during_past_year
65
65
 
66
- @person.count_messages_during_past_year_per_day
67
- @person.messages_count_during_past_year_per_day
66
+ @person.messages_count_in_past_year_by_day
67
+ @person.messages_count_in_past_year_by_day
68
68
 
69
69
  Message.count_during_past_year
70
70
 
71
71
 
72
72
  A standard verbose call:
73
73
 
74
- @person.sum_word_count_from_messages_during_past_year_per_day
74
+ @person.sum_word_count_from_messages_during_past_year_by_day
75
75
 
76
76
 
77
77
  *Under the hood query:*
78
78
 
79
- @person.egg.sum(:word_count).
79
+ @person.egg.
80
+ collate(:sum, :word_count).
80
81
  from(:messages).
81
- during(:past_year).
82
- per(:day)
83
-
82
+ in(:past_year).
83
+ by(:day)
84
84
  *Which translates to this in ActiveRecord:*
85
85
 
86
86
  @person.messages.
87
87
  where('sent_at >= ?', 1.year.ago).
88
- group('DAY(sent_at)').
88
+ group_by_day(:sent_at).
89
89
  sum(:word_count)
90
90
 
91
91
 
@@ -94,6 +94,46 @@ A standard verbose call:
94
94
 
95
95
 
96
96
 
97
- ### Scalars and arrays
97
+ ### Scalars
98
+
99
+ @messages.count_during_past_year
100
+ @messages.rate_per_day_during_past_year
101
+ @messages.average_per_day_of_word_count_during_past_year
102
+ @messages.sum_of_word_count_during_past_year
103
+
104
+ ### Arrays
105
+ *similar to groupdate*
106
+
107
+ @messages.count_in_past_year_by_day
108
+ @messages.count_in_past_year_by_weekday
109
+ @messages.count_in_past_year_by_dayhour
110
+ @messages.count_in_past_year_by_hour
111
+
112
+ @messages.rate_per_hour_in_past_year_by_day
113
+ @messages.average_per_day_of_word_count_during_past_year_by_month
114
+ @messages.sum_of_word_count_during_past_year_by_day
115
+
116
+
117
+
118
+ ### Custom time periods
119
+
120
+ @messages.count_during_year(year: 2010)
121
+ @messages.count_during_years(years: 2008..2012)
122
+ @messages.count_during_day(day: '2012-12-01')
123
+ @messages.count_during_year_by_week(year: 2010, timezone: 'PST', first_day: 'Sunday')
124
+
125
+
126
+
127
+
128
+ count
129
+
130
+ rate_per
131
+ average_per, of_numeric_value
132
+
133
+ sum, of_numeric_value
134
+
135
+ list_of_proper_nouns_during_past_year
136
+
137
+ counted_list_of_proper_nouns_during_past_year
98
138
 
99
139
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.1.2
@@ -0,0 +1,93 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "aggtive_record"
8
+ s.version = "0.1.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Dan Nguyen"]
12
+ s.date = "2013-09-23"
13
+ s.description = "This is not even remotely finished or even started on. Please don't download."
14
+ s.email = "dansonguyen@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ ".ruby-gemset",
23
+ ".ruby-version",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE.txt",
27
+ "README.md",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "aggtive_record.gemspec",
31
+ "lib/aggtive_record.rb",
32
+ "lib/aggtive_record/adapter.rb",
33
+ "lib/aggtive_record/adapter/mysql.rb",
34
+ "lib/aggtive_record/aggable.rb",
35
+ "lib/aggtive_record/egg_scopes.rb",
36
+ "lib/aggtive_record/egg_scopes/collation.rb",
37
+ "lib/aggtive_record/egg_scopes/collation/count_by.rb",
38
+ "lib/aggtive_record/egg_scopes/collation/rate.rb",
39
+ "lib/aggtive_record/egg_scopes/time_bucket.rb",
40
+ "lib/aggtive_record/egg_scopes/time_span.rb",
41
+ "lib/aggtive_record/memo.rb",
42
+ "lib/aggtive_record/time.rb",
43
+ "spec/functional/basic_object_run_spec.rb",
44
+ "spec/lib/aggable_spec.rb",
45
+ "spec/lib/count_by_spec.rb",
46
+ "spec/lib/rate_spec.rb",
47
+ "spec/lib/time_bucket_spec.rb",
48
+ "spec/lib/time_span_spec.rb",
49
+ "spec/spec_helper.rb"
50
+ ]
51
+ s.homepage = "http://github.com/dannguyen/aggtive_record"
52
+ s.licenses = ["MIT"]
53
+ s.require_paths = ["lib"]
54
+ s.rubygems_version = "2.0.5"
55
+ s.summary = "A convoluted way to describe aggregations of ActiveRecords over a datetime attribute"
56
+
57
+ if s.respond_to? :specification_version then
58
+ s.specification_version = 4
59
+
60
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
61
+ s.add_runtime_dependency(%q<groupdate>, [">= 0"])
62
+ s.add_runtime_dependency(%q<activesupport>, ["~> 3.2.14"])
63
+ s.add_development_dependency(%q<rspec>, ["~> 2.14.1"])
64
+ s.add_development_dependency(%q<bundler>, [">= 0"])
65
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
66
+ s.add_development_dependency(%q<pry>, [">= 0"])
67
+ s.add_development_dependency(%q<database_cleaner>, ["~> 1.0.1"])
68
+ s.add_development_dependency(%q<mysql2>, [">= 0"])
69
+ s.add_development_dependency(%q<activerecord>, ["~> 3.2.14"])
70
+ else
71
+ s.add_dependency(%q<groupdate>, [">= 0"])
72
+ s.add_dependency(%q<activesupport>, ["~> 3.2.14"])
73
+ s.add_dependency(%q<rspec>, ["~> 2.14.1"])
74
+ s.add_dependency(%q<bundler>, [">= 0"])
75
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
76
+ s.add_dependency(%q<pry>, [">= 0"])
77
+ s.add_dependency(%q<database_cleaner>, ["~> 1.0.1"])
78
+ s.add_dependency(%q<mysql2>, [">= 0"])
79
+ s.add_dependency(%q<activerecord>, ["~> 3.2.14"])
80
+ end
81
+ else
82
+ s.add_dependency(%q<groupdate>, [">= 0"])
83
+ s.add_dependency(%q<activesupport>, ["~> 3.2.14"])
84
+ s.add_dependency(%q<rspec>, ["~> 2.14.1"])
85
+ s.add_dependency(%q<bundler>, [">= 0"])
86
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
87
+ s.add_dependency(%q<pry>, [">= 0"])
88
+ s.add_dependency(%q<database_cleaner>, ["~> 1.0.1"])
89
+ s.add_dependency(%q<mysql2>, [">= 0"])
90
+ s.add_dependency(%q<activerecord>, ["~> 3.2.14"])
91
+ end
92
+ end
93
+
@@ -4,4 +4,6 @@ module AggtiveRecord
4
4
  end
5
5
 
6
6
 
7
-
7
+ require_relative 'aggtive_record/aggable'
8
+ require_relative 'aggtive_record/time'
9
+ require_relative 'aggtive_record/egg_scopes'
@@ -2,13 +2,18 @@ module AggtiveRecord
2
2
  module Aggable
3
3
  extend ActiveSupport::Concern
4
4
 
5
+
5
6
  included do
7
+ include AggtiveRecord::EggScopes::TimeBucket
8
+ include AggtiveRecord::EggScopes::TimeSpan
9
+ include AggtiveRecord::EggScopes::Collation
10
+
6
11
  class_attribute :datetime_attribute
7
12
  end
8
13
 
9
14
  module ClassMethods
10
- def attr_timestamp(attname)
11
- raise ArgumentError unless is_a_datetime?(att)
15
+ def attr_datetime(attname)
16
+ raise ArgumentError unless is_a_datetime?(attname)
12
17
  self.datetime_attribute = attname
13
18
  end
14
19
 
@@ -18,12 +23,5 @@ module AggtiveRecord
18
23
  end
19
24
 
20
25
 
21
- def method_missing
22
-
23
- end
24
-
25
- def respond_to
26
-
27
- end
28
26
  end
29
27
  end
@@ -0,0 +1,10 @@
1
+ module AggtiveRecord
2
+ module EggScopes
3
+
4
+ end
5
+ end
6
+
7
+
8
+ require_relative 'egg_scopes/time_bucket'
9
+ require_relative 'egg_scopes/time_span'
10
+ require_relative 'egg_scopes/collation'
@@ -0,0 +1,47 @@
1
+ require_relative 'collation/count_by'
2
+ require_relative 'collation/rate'
3
+
4
+ module AggtiveRecord
5
+ module EggScopes
6
+ module Collation
7
+ extend ActiveSupport::Concern
8
+ include CountBy
9
+ include Rate
10
+
11
+ module ClassMethods
12
+ # Public: a helper
13
+ # sorts the records after ActiveRecord query, ascending
14
+ #
15
+ # Returns a mapped array of timestamps
16
+ def enumerable_asc_sort(records)
17
+ records.map{|r| r.send(self.datetime_attribute)}.sort
18
+ end
19
+
20
+ def earliest_time_of(records)
21
+ enumerable_asc_sort(records).first
22
+ end
23
+
24
+ def latest_time_of(records)
25
+ enumerable_asc_sort(records).last
26
+ end
27
+
28
+ # Public:
29
+ # records: An ActiveRecord collection
30
+ #
31
+ # Returns the number of seconds
32
+ # TODO - eliminate use of helper methods
33
+ def timespan_of(records)
34
+ latest_time_of(records) - earliest_time_of(records)
35
+ end
36
+
37
+ def timespan_to_now(records)
38
+ ::Time.now - earliest_time_of(records)
39
+ end
40
+
41
+ end
42
+
43
+
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,20 @@
1
+ module AggtiveRecord
2
+ module EggScopes
3
+ module Collation
4
+ module CountBy
5
+ extend ActiveSupport::Concern
6
+
7
+ # Public: convenience for #by_:time_period and #count
8
+ # self is a ActiveRecord scope
9
+
10
+ module ClassMethods
11
+ def count_by(time_period)
12
+ span_foo = "by_#{time_period}"
13
+ self.send(span_foo).count
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ module AggtiveRecord
2
+ module EggScopes
3
+ module Collation
4
+ module Rate
5
+ extend ActiveSupport::Concern
6
+
7
+ # Public: a rate
8
+ # expects that ActiveRelation has a grouping
9
+ # self is a ActiveRecord scope
10
+
11
+ module ClassMethods
12
+
13
+ # Public
14
+ #
15
+ # Returns float indicating rate of records per given time period
16
+ def rate_per(time_period)
17
+ time_period_secs = AggtiveRecord::Time.to_seconds(time_period)
18
+ records = self.scoped.to_a
19
+
20
+ return records.size.to_f * time_period_secs / self.timespan_to_now(records)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ =begin
30
+
31
+ Record.rate_per :hour
32
+ .rate_per :day
33
+
34
+
35
+ =end
@@ -0,0 +1,30 @@
1
+ # defines named_scopes
2
+
3
+ require 'groupdate'
4
+ module AggtiveRecord
5
+ module EggScopes
6
+ module TimeBucket
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ #TODO: refactor
11
+
12
+ # create a method for each type of groupdate
13
+ # e.g. by_year == group_by_year(datetime_attribute)
14
+ AggtiveRecord::Time.periods.each do |bucket_name|
15
+ agg_foo_name = "by_#{bucket_name}".to_sym
16
+ groupdate_foo_name = "group_#{agg_foo_name}".to_sym
17
+
18
+ # defining scope here, dynamically
19
+ scope agg_foo_name, ->(*args){
20
+ send( groupdate_foo_name, self.datetime_attribute )
21
+ }
22
+ end
23
+
24
+
25
+ end
26
+
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ # defines named_scopes
2
+ module AggtiveRecord
3
+ module EggScopes
4
+ module TimeSpan
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+
9
+ included do
10
+
11
+
12
+ scope :past_time_periods, ->(num, period_name){ where("#{self.datetime_attribute} >= ?", num.send(period_name).send(:ago) ) }
13
+
14
+
15
+ # singular time periods
16
+ # e.g. scope :past_year, ->{ past_time_periods(1, :year)}
17
+ AggtiveRecord::Time.periods.each do |period_name|
18
+ foo_name = "past_#{period_name}".to_sym
19
+ # defining scope here, dynamically
20
+ scope foo_name, ->{
21
+ send( :past_time_periods, 1, period_name )
22
+ }
23
+ end
24
+
25
+
26
+ # some custom time periods
27
+ # e.g. :past_14_days, :past_30_days
28
+
29
+ [[14, 'day'], [30, 'day'], [6, 'month']].each do |p_arr|
30
+ num, period = p_arr
31
+ foo_name = "past_#{num}_#{period.pluralize}"
32
+ scope foo_name, ->{
33
+ send( :past_time_periods, num, period)
34
+ }
35
+
36
+ end
37
+
38
+
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,35 +1,45 @@
1
1
  module AggtiveRecord
2
2
  module Time
3
3
 
4
+ # A week is always 7 * 24 * 60 * 60, for instance
4
5
  UNIFORM_PERIODS = [:week, :day, :hour, :minute, :second]
5
- CALENDAR_PERIODS = [:year, :month]
6
-
7
- PERIODS = CALENDAR_PERIODS + UNIFORM_PERIODS
8
-
9
- TIMESTAMP_QUANTITIES = [
10
- year: {
11
- str: '%Y'
12
- },
13
- month:{
14
- str: '%m'
15
- },
16
- day: {
17
- str: '%d'
18
- }
19
-
20
- hour: {
21
- str: '%H'
22
- },
23
-
24
- minute: {
25
- str: '%i'
26
- },
27
-
28
- second: {
29
- str: '%S'
30
- }
31
-
32
- ]
6
+
7
+ # e.g. A year may have 365 days or 366
8
+ NON_UNIFORM_PERIODS = [:year, :month]
9
+
10
+ PERIODS = NON_UNIFORM_PERIODS + UNIFORM_PERIODS
11
+
12
+ OTHER_PERIODS = [:day_of_week, :hour_of_day]
13
+
14
+ # todo: better naming convention
15
+
16
+
17
+ def self.periods
18
+ PERIODS + OTHER_PERIODS
19
+ end
20
+
21
+ SECONDS_PER_DAY = 60 * 60 * 24
22
+
23
+ # probably reinventing the wheel here...
24
+ def self.to_seconds(sym)
25
+ case sym.to_sym
26
+ when :year
27
+ SECONDS_PER_DAY * 365
28
+ when :month
29
+ SECONDS_PER_DAY * 30
30
+ when :week
31
+ SECONDS_PER_DAY * 7
32
+ when :day
33
+ SECONDS_PER_DAY
34
+ when :hour
35
+ 60 * 60
36
+ when :minute
37
+ 60
38
+ when :second
39
+ 1
40
+ end
41
+ end
42
+
33
43
 
34
44
  end
35
45
  end
@@ -1,6 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  require 'hashie'
4
+ =begin
5
+
6
+ IGNORE ALL OF THIS
4
7
 
5
8
  class AnObject < ActiveRecord::Base
6
9
  include AggtiveRecord
@@ -57,4 +60,5 @@ describe 'AnObject with AggtiveRecord mixedin', skip: true do
57
60
  end
58
61
  end
59
62
 
60
- end
63
+ end
64
+ =end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe "AggtiveRecord::Aggable" do
4
+
5
+ context 'class methods' do
6
+
7
+ describe '#is_a_datetime?' do
8
+ it 'returns true if named column exists and is date/time' do
9
+ expect(MusicRecord.is_a_datetime?(:published_at)).to be_true
10
+ end
11
+
12
+ it 'returns false if named column does not exist' do
13
+ expect(MusicRecord.is_a_datetime?(:not_a_column)).to be_false
14
+ end
15
+
16
+
17
+ it 'returns false if named column is not date/time' do
18
+ expect(MusicRecord.is_a_datetime?(:title)).to be_false
19
+ end
20
+ end
21
+
22
+
23
+ describe '#attr_datetime' do
24
+ it 'sets @@datetime_attribute' do
25
+ # defined in spec helper already
26
+ # MusicRecord.attr_datetime :published_at
27
+ expect(MusicRecord.datetime_attribute).to eq :published_at
28
+ end
29
+
30
+ it 'raises ArgumentError if not a valid datetime column' do
31
+ expect{MusicRecord.attr_datetime :title}.to raise_error ArgumentError
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe AggtiveRecord::EggScopes::Collation::CountBy do
4
+ describe '#count_by' do
5
+
6
+ before(:each) do
7
+ @a = MusicRecord.create(
8
+ published_at: "2010-01-20"
9
+ )
10
+
11
+ @b = MusicRecord.create(
12
+ published_at: "2010-03-12"
13
+ )
14
+
15
+ @c = MusicRecord.create(
16
+ published_at: "2012-05-12"
17
+ )
18
+
19
+ @d = MusicRecord.create(
20
+ published_at: "2012-05-12 15:00"
21
+ )
22
+ end
23
+
24
+ context 'basic groupdate usage' do
25
+ it 'can #count_by_year' do
26
+ a = MusicRecord.by_year.count.select{|k,v| k.year == 2010}.first
27
+ b = MusicRecord.count_by(:year).select{|k,v| k.year == 2010}.first
28
+ expect(a).to eq b
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+
35
+ it 'should raise error if :time_period is erroneous'
36
+
37
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe "AggtiveRecord::EggScopes -- .rate" do
4
+
5
+
6
+ describe '#rate_for' do
7
+ context 'singular periods' do
8
+ it 'should get about 1 a year' do
9
+ @record = MusicRecord.create published_at: 12.months.ago
10
+ expect(MusicRecord.scoped.rate_per(:year) ).to be_within(0.1).of 1
11
+ end
12
+ end
13
+
14
+ context 'has two optional parameters' do
15
+
16
+ before(:each) do
17
+ MusicRecord.create(published_at: 1.day.ago )
18
+ end
19
+
20
+ let(:records){ MusicRecord.scoped }
21
+
22
+ context 'first parameter is the :start_time' do
23
+ it 'bounds range with :start_time and Time.now' do
24
+ pending 'rethink'
25
+ expect(records.rate_per(:day, 2.days.ago)).to be_within(0.1).of 0.5
26
+ end
27
+ end
28
+
29
+ context 'second parameter is the end_time' do
30
+ it 'bounds range with :start_time and :end_time' do
31
+ pending 'rethink'
32
+ expect(records.rate_per(:day, 2.days.ago)).to be_within(0.1).of 0.5
33
+ end
34
+
35
+ it 'has no effect on :where' do
36
+ pending('This is troublesome. What if a user calls:
37
+ records.past_year.rate_per(:day, 10.days.ago, 3.days.ago)
38
+ -- too many surprises here!
39
+ ')
40
+ end
41
+ end
42
+
43
+
44
+ it 'should raise an error when a non-time period is passed in'
45
+ it 'should play nicely with other methods'
46
+ end
47
+ end
48
+
49
+ describe '#timespan_of' do
50
+ before(:each) do
51
+ @latest = 1.months.ago
52
+ @earliest = 14.months.ago
53
+
54
+ MusicRecord.create published_at: @latest
55
+ MusicRecord.create published_at: 2.months.ago
56
+ MusicRecord.create published_at: @earliest
57
+
58
+ @collection = MusicRecord.all
59
+ end
60
+
61
+ it 'should return a range' do
62
+ expect(MusicRecord.timespan_of(@collection)).to be_within(0.1).of @latest - @earliest
63
+ end
64
+
65
+ it 'should get #latest record in a set without re-querying database'
66
+ end
67
+
68
+
69
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe AggtiveRecord::EggScopes::TimeBucket do
4
+
5
+
6
+ describe '#by' do
7
+
8
+ before(:each) do
9
+ @a = MusicRecord.create(
10
+ published_at: "2010-01-20"
11
+ )
12
+
13
+ @b = MusicRecord.create(
14
+ published_at: "2010-03-12"
15
+ )
16
+
17
+ @c = MusicRecord.create(
18
+ published_at: "2012-05-12"
19
+ )
20
+
21
+ @d = MusicRecord.create(
22
+ published_at: "2012-05-12 15:00"
23
+ )
24
+ end
25
+
26
+ context 'basic groupdate usage' do
27
+ it 'groups #by_year' do
28
+ @group = MusicRecord.by_year.
29
+ count.select{|k,v| k.year == 2010}.first
30
+ expect(@group[1]).to eq 2
31
+ end
32
+
33
+ it 'groups #by_month' do
34
+ @group = MusicRecord.by_month.
35
+ count.select{|k,v| k.year == 2012 && k.month == 5}.first
36
+ expect(@group[1]).to eq 2
37
+ end
38
+
39
+ it 'groups #by_hour' do
40
+ @group = MusicRecord.by_hour.
41
+ count.
42
+ select{|k,v| k.hour == 15}.first
43
+ expect(@group[1]).to eq 1
44
+ end
45
+
46
+
47
+ context 'groupdate options' do
48
+ describe '#continuous' do
49
+ it 'should adopt the usage of groupdate series' do
50
+ pending %q{May make more sense to apply this operation at the end, as something separate from
51
+ groupdate as it's not related to ActiveRecord
52
+ }
53
+ end
54
+
55
+ it 'populates default range between two endpoints'
56
+ it 'populates a specified range'
57
+
58
+ end
59
+ end
60
+ end
61
+
62
+
63
+
64
+ end
65
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe AggtiveRecord::EggScopes::TimeSpan do
4
+
5
+
6
+ describe '#in...' do
7
+ before(:each) do
8
+
9
+ end
10
+
11
+
12
+ describe '#past...' do
13
+ context 'singular periods' do
14
+ it 'should span a #year' do
15
+ @record = MusicRecord.create published_at: 4.months.ago
16
+ MusicRecord.create published_at: 2.years.ago
17
+ expect(MusicRecord.past_year.count).to eq 1
18
+ end
19
+
20
+ it 'should span a #month' do
21
+ @record = MusicRecord.create published_at: 4.months.ago
22
+ MusicRecord.create published_at: 2.days.ago
23
+ expect(MusicRecord.past_month.count).to eq 1
24
+ end
25
+
26
+ it 'should span an #hour' do
27
+ @record = MusicRecord.create published_at: 2.hours.ago
28
+ MusicRecord.create published_at: 1.minute.ago
29
+ expect(MusicRecord.past_hour.count).to eq 1
30
+ end
31
+ end
32
+
33
+ context 'plural periods' do
34
+ it 'has a few of these: 14 days, 30 days, 6 months, etc' do
35
+ [2.years.ago, 4.months.ago, 15.days.ago, 1.minute.ago].each do |t|
36
+ MusicRecord.create published_at: t
37
+ end
38
+
39
+ expect(MusicRecord.past_6_months.count).to eq 3
40
+ expect(MusicRecord.past_30_days.count).to eq 2
41
+ expect(MusicRecord.past_14_days.count).to eq 1
42
+ end
43
+ end
44
+ end
45
+
46
+
47
+ describe 'just period name' do
48
+ context 'single argument' do
49
+ it 'spans #year(1999)'
50
+ end
51
+ end
52
+
53
+ end
54
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,13 @@
1
1
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
3
  require 'rspec'
4
- require 'aggtive_record'
4
+ require 'mysql2'
5
+
6
+ require 'active_record'
7
+ require 'database_cleaner'
5
8
 
9
+ require 'aggtive_record'
10
+ require 'pry'
6
11
 
7
12
  # Requires supporting files with custom matchers and macros, etc,
8
13
  # in ./support/ and its subdirectories.
@@ -10,6 +15,34 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
15
 
11
16
 
12
17
 
18
+ ActiveRecord::Base.establish_connection(
19
+ :adapter => "mysql2",
20
+ :database => "test_aggtive_record",
21
+ username: 'root',
22
+ password: ''
23
+
24
+ )
25
+
26
+ ActiveRecord::Migration.verbose = false
27
+
28
+ ActiveRecord::Schema.define do
29
+ create_table :music_records , force: true do |t|
30
+ t.string "title"
31
+ t.string "genre"
32
+ t.datetime 'published_at'
33
+ t.string 'description'
34
+ end
35
+ end
36
+
37
+ class MusicRecord < ActiveRecord::Base
38
+ include AggtiveRecord::Aggable
39
+
40
+ attr_datetime :published_at
41
+ end
42
+
43
+ DatabaseCleaner.strategy = :truncation
44
+
45
+
13
46
 
14
47
  RSpec.configure do |config|
15
48
  config.color_enabled = true
@@ -17,9 +50,13 @@ RSpec.configure do |config|
17
50
  config.formatter = :documentation # :progress, :html, :textmate
18
51
 
19
52
  config.before(:each) do
53
+ DatabaseCleaner.start
20
54
  end
21
55
 
22
56
  config.after(:each) do
57
+ DatabaseCleaner.clean
23
58
  end
24
59
  end
25
60
 
61
+
62
+
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aggtive_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Nguyen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-20 00:00:00.000000000 Z
11
+ date: 2013-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: groupdate
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: activesupport
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,48 @@ dependencies:
80
94
  - - ! '>='
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: database_cleaner
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 1.0.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.0.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: mysql2
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: activerecord
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ~>
130
+ - !ruby/object:Gem::Version
131
+ version: 3.2.14
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: 3.2.14
83
139
  description: This is not even remotely finished or even started on. Please don't download.
84
140
  email: dansonguyen@gmail.com
85
141
  executables: []
@@ -90,25 +146,34 @@ extra_rdoc_files:
90
146
  files:
91
147
  - .document
92
148
  - .rspec
149
+ - .ruby-gemset
150
+ - .ruby-version
93
151
  - Gemfile
94
152
  - Gemfile.lock
95
153
  - LICENSE.txt
96
154
  - README.md
97
155
  - Rakefile
98
156
  - VERSION
157
+ - aggtive_record.gemspec
99
158
  - lib/aggtive_record.rb
100
159
  - lib/aggtive_record/adapter.rb
101
160
  - lib/aggtive_record/adapter/mysql.rb
102
161
  - lib/aggtive_record/aggable.rb
162
+ - lib/aggtive_record/egg_scopes.rb
163
+ - lib/aggtive_record/egg_scopes/collation.rb
164
+ - lib/aggtive_record/egg_scopes/collation/count_by.rb
165
+ - lib/aggtive_record/egg_scopes/collation/rate.rb
166
+ - lib/aggtive_record/egg_scopes/time_bucket.rb
167
+ - lib/aggtive_record/egg_scopes/time_span.rb
103
168
  - lib/aggtive_record/memo.rb
104
- - lib/aggtive_record/scopes.rb
105
- - lib/aggtive_record/scopes/collation.rb
106
- - lib/aggtive_record/scopes/time_bucket.rb
107
- - lib/aggtive_record/scopes/time_span.rb
108
169
  - lib/aggtive_record/time.rb
109
170
  - spec/functional/basic_object_run_spec.rb
171
+ - spec/lib/aggable_spec.rb
172
+ - spec/lib/count_by_spec.rb
173
+ - spec/lib/rate_spec.rb
174
+ - spec/lib/time_bucket_spec.rb
175
+ - spec/lib/time_span_spec.rb
110
176
  - spec/spec_helper.rb
111
- - spec/unit/aggable.rb
112
177
  homepage: http://github.com/dannguyen/aggtive_record
113
178
  licenses:
114
179
  - MIT
@@ -1,5 +0,0 @@
1
- module AggtiveRecord
2
- module Scopes
3
-
4
- end
5
- end
@@ -1,7 +0,0 @@
1
- module AggtiveRecord
2
- module Scopes
3
- module Collation
4
- extend ActiveRecord::Concern
5
- end
6
- end
7
- end
@@ -1,12 +0,0 @@
1
- module AggtiveRecord
2
- module Scopes
3
- module TimeBucket
4
- extend ActiveRecord::Concern
5
-
6
- mattr_reader :prefix
7
- self.prefix = 'by'
8
-
9
-
10
- end
11
- end
12
- end
@@ -1,22 +0,0 @@
1
- module AggtiveRecord
2
- module Scopes
3
- module TimeSpan
4
-
5
- extend ActiveRecord::Concern
6
-
7
- # where("#{}" => 1.year.ago..Time.now)
8
- #past_year
9
-
10
- mattr_reader :prefix
11
- self.prefix = 'during'
12
-
13
- =begin
14
-
15
-
16
-
17
- =end
18
-
19
-
20
- end
21
- end
22
- end
data/spec/unit/aggable.rb DELETED
@@ -1,24 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "AggtiveRecord::Aggable" do
4
-
5
- context 'class methods' do
6
-
7
- describe '#is_a_datetime?' do
8
- it 'returns true if named column exists and is date/time' do
9
- @klass.is_a_datetime?
10
- end
11
-
12
- it 'returns false if named column does not exist'
13
- it 'returns false if named column is not date/time'
14
- end
15
-
16
-
17
- describe '#attr_datetime' do
18
- it 'sets @@datetime_attribute'
19
- it 'raises ArgumentError if not a valid datetime column'
20
- end
21
-
22
- end
23
-
24
- end