aggtive_record 0.0.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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