has_metrics 0.0.3 → 0.0.6

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 290cd9839b6158645e6952a21045665372b5d2fe
4
+ data.tar.gz: f26872bf500a652a99816716a8cbfbe3be85afc9
5
+ SHA512:
6
+ metadata.gz: 4ab5e73e945e0698c8fd82e620c64be6cb780d5b6c6ef46bce90d2b5539f252487cfa2f3ffb71fc801934c9715fd8dd5ac02255458fd781a337b07e525f6ebcb
7
+ data.tar.gz: aa6fb41dfd66fcaf7b541f3861b2237cc7e4d82faf9f0e5a92736cc41b334598ed399e3e0c3004c970686fe3a57c7f3261b52b60d40a351a53f13ffef99a7ddc
data/.gitignore CHANGED
@@ -4,6 +4,8 @@
4
4
  .config
5
5
  .yardoc
6
6
  .rvmrc
7
+ .ruby-version
8
+ .ruby-gemset
7
9
  Gemfile.lock
8
10
  InstalledFiles
9
11
  _yardoc
@@ -16,4 +18,4 @@ spec/reports
16
18
  test/tmp
17
19
  test/version_tmp
18
20
  tmp
19
- test/test.db
21
+ test/test.db
data/Gemfile CHANGED
@@ -2,3 +2,9 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in has_metrics.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'pry'
8
+ gem 'rspec'
9
+ gem 'sqlite3'
10
+ end
data/Rakefile CHANGED
@@ -1,11 +1,7 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
3
4
 
4
- require 'rake/testtask'
5
- Rake::TestTask.new(:test) do |test|
6
- test.libs << 'lib' << 'test'
7
- test.pattern = 'test/{functional,unit}/**/*_test.rb'
8
- test.verbose = true
9
- end
5
+ RSpec::Core::RakeTask.new(:spec)
10
6
 
11
- task :default => :test
7
+ task :default => :spec
data/has_metrics.gemspec CHANGED
@@ -2,22 +2,24 @@
2
2
  require File.expand_path('../lib/has_metrics/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
+ gem.name = "has_metrics"
6
+ gem.version = HasMetrics::VERSION
5
7
  gem.authors = ["Allan Grant"]
6
8
  gem.email = ["allan@allangrant.net"]
7
9
  gem.description = %q{Calculate metrics and store them in the DB.}
8
10
  gem.summary = %q{Calculate "metrics" (any expensive methods) on ActiveRecord entries and memoize them to an automagical table.}
9
11
  gem.homepage = "http://github.com/allangrant/has_metrics"
12
+ gem.license = "MIT"
10
13
 
11
14
  gem.files = `git ls-files`.split($\)
12
15
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
16
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "has_metrics"
15
17
  gem.require_paths = ["lib"]
16
- gem.version = HasMetrics::VERSION
17
18
 
18
19
  gem.add_dependency "activerecord"
19
20
  gem.add_development_dependency "rake"
20
- gem.add_development_dependency "shoulda"
21
- gem.add_development_dependency "sqlite3"
22
- gem.add_development_dependency "pry"
21
+ gem.add_development_dependency "bundler"
22
+ # gem.add_development_dependency "shoulda"
23
+ # gem.add_development_dependency "sqlite3"
24
+ # gem.add_development_dependency "pry"
23
25
  end
@@ -13,7 +13,7 @@ module Metrics
13
13
  belongs_to base.to_s.underscore.to_sym, :foreign_key => 'id'
14
14
  @object_class = base
15
15
  end
16
-
16
+
17
17
  base.class_eval do
18
18
  if klass.table_exists?
19
19
  @metrics_class = klass
@@ -23,7 +23,7 @@ module Metrics
23
23
  @metrics_class = base
24
24
  base.extend(Metrics::MetricsClass)
25
25
  end
26
-
26
+
27
27
  def metrics
28
28
  @metrics ||= self.class.metrics_class.find_or_create_by_id(id)
29
29
  end
@@ -54,7 +54,7 @@ module Metrics
54
54
  result = nil if result.is_a?(Float) && !result.finite?
55
55
  begin
56
56
  metrics.send "#{name}=", result
57
- metrics.send "#{datestamp_column}=", Time.now
57
+ metrics.send "#{datestamp_column}=", Time.current
58
58
  rescue NoMethodError => e
59
59
  raise e unless e.name == "#{name}=".to_sym
60
60
  # This happens if the migrations haven't run yet for this metric. We should still calculate & return the metric.
@@ -67,6 +67,7 @@ module Metrics
67
67
  end
68
68
 
69
69
  (@metrics ||= []) << name.to_sym
70
+ @metrics.uniq!
70
71
 
71
72
  if respond_to?(:has_custom_order_by) # TODO: carve out has_custom_order_by functionality into this gem
72
73
  unless metrics_class == self
@@ -75,7 +76,7 @@ module Metrics
75
76
  end
76
77
  end
77
78
  end
78
-
79
+
79
80
  if options[:type] && (options[:type].to_sym == :float)
80
81
  (@float_metrics ||= []) << name.to_sym
81
82
  end
@@ -84,7 +85,7 @@ module Metrics
84
85
  def metrics
85
86
  @metrics
86
87
  end
87
-
88
+
88
89
  def metrics_column_type(column)
89
90
  case
90
91
  when (column.to_s =~ /^by_(.+)$/) && respond_to?(:segment_categories) && segment_categories.include?($1.to_sym) # TODO: carve out segementation functionality into this gem
@@ -97,7 +98,7 @@ module Metrics
97
98
  :integer
98
99
  end
99
100
  end
100
-
101
+
101
102
  def update_all_metrics!(*args)
102
103
  metrics_class.migrate!
103
104
  # start_time = Time.zone.now
@@ -107,8 +108,13 @@ module Metrics
107
108
  # puts "Updating #{total} records."
108
109
  # progress_bar = ProgressBar.new("Progress", total)
109
110
  # end
110
- for record in all(:order => "id desc")
111
- record.update_metrics!(*args)
111
+ find_in_batches do |batch|
112
+ metrics_class.transaction do
113
+ batch.each do |record|
114
+ # puts "Updating record ##{record.id}: #{record}"
115
+ record.update_metrics!(*args)
116
+ end
117
+ end
112
118
  # progress_bar.inc if progress_bar
113
119
  end
114
120
  # progress_bar.finish if progress_bar
@@ -118,14 +124,14 @@ module Metrics
118
124
  end
119
125
  end
120
126
  ### END CLASS METHODS, START INSTANCE METHODS
121
-
127
+
122
128
  def update_metrics!(*args)
123
129
  self.class.metrics.each do |metric|
124
130
  send(metric, *args)
125
131
  end
126
132
  end
127
133
  ### END INSTANCE METHODS
128
-
134
+
129
135
  ### Sets up a class like "SiteMetrics". These are all CLASS methods:
130
136
  module MetricsClass
131
137
  def object_class
@@ -137,7 +143,7 @@ module Metrics
137
143
  end
138
144
 
139
145
  def required_columns
140
- @object_class.metrics.map(&:to_s) + metrics_updated_at_columns
146
+ @object_class.metrics.map(&:to_s) + metrics_updated_at_columns
141
147
  end
142
148
 
143
149
  def missing_columns
@@ -151,9 +157,9 @@ module Metrics
151
157
  raise "Cannot determine if there were extra columns for has_metric when using the table itself for storing the metric! Remove any columns manually"
152
158
  [] # We wont know what columns are excessive if the source changed
153
159
  else
154
- (columns.map(&:name) - %w(id created_at updated_at)) - required_columns
160
+ (columns.map(&:name) - %w(id created_at updated_at)) - required_columns
155
161
  end
156
-
162
+
157
163
  end
158
164
 
159
165
  class Metrics::Migration < ActiveRecord::Migration
@@ -177,15 +183,18 @@ module Metrics
177
183
  old_metrics = @object_class.metrics
178
184
  @object_class.class_eval { @metrics = [] }
179
185
  migrate!
180
- @object_class.class_eval { @metrics = old_metrics }
186
+ @object_class.class_eval { @metrics = old_metrics }
181
187
  migrate!
182
188
  end
183
189
 
184
190
  def migrate!
191
+ # don't migrate if metrics are kept in current class
192
+ return if @object_class == self
193
+
185
194
  Metrics::Migration.setup(self)
186
- Metrics::Migration.down unless @object_class == self || extra_columns.empty?
195
+ Metrics::Migration.down unless extra_columns.empty?
187
196
  Metrics::Migration.up unless missing_columns.empty?
188
197
  reset_column_information
189
198
  end
190
199
  end
191
- end
200
+ end
@@ -1,3 +1,3 @@
1
1
  module HasMetrics
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metrics do
4
+ describe "defining metrics" do
5
+ let(:user) { User.create(:name => "Fuzz") }
6
+
7
+ before do
8
+ create_tables_for(:user)
9
+
10
+ class User < ActiveRecord::Base
11
+ include Metrics
12
+ has_metric :name_length do
13
+ name.length
14
+ end
15
+ end
16
+
17
+ User.update_all_metrics!
18
+ end
19
+
20
+ it "creates rows for the metrics" do
21
+ UserMetrics.columns.count.should == 3
22
+ User.has_metric :name_length_squared do
23
+ name_length * name_length
24
+ end
25
+ User.update_all_metrics!
26
+ UserMetrics.columns.count.should == 5
27
+ user.name_length_squared.should == 16
28
+ end
29
+
30
+ it "calculates their block when called" do
31
+ user.name.should == "Fuzz"
32
+ user.name_length.should == 4
33
+
34
+ user.name = "Bib"
35
+
36
+ # since 20 hours hasn't passed, the value is pulled from cache, not recalculated
37
+ user.name_length.should == 4
38
+ # (true) forces it to recalculate right away
39
+ user.name_length(true).should == 3
40
+
41
+ # since it wasn't saved, it's the same in the DB
42
+ User.find_by_name("Fuzz").name_length.should == 4
43
+
44
+ user.save
45
+ user.name_length(true).should == 3
46
+ User.find_by_name("Bib").name_length.should == 3
47
+ end
48
+
49
+ it "has their values precomputed" do
50
+ user
51
+ User.update_all_metrics!
52
+ UserMetrics.count(:group => :name_length).should == {4=>1}
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Segmentation do
4
+ before do
5
+ create_tables_for(:post)
6
+
7
+ class Post < ActiveRecord::Base
8
+ include Metrics
9
+ include Segmentation
10
+
11
+ segment_by :name_length do
12
+ case name.length
13
+ when 0..6
14
+ "short"
15
+ when 7
16
+ "seven"
17
+ else
18
+ "long"
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "defining segments" do
25
+ before do
26
+ Post.create(:name => "Shorty")
27
+ Post.create(:name => "Seven!!")
28
+ Post.create(:name => "Really long")
29
+ Post.update_all_metrics!
30
+ end
31
+
32
+ it "segments properly" do
33
+ PostMetrics.count(:group => :by_name_length).should == {'short'=>1, 'seven'=>1, 'long'=>1}
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'pry'
4
+ require 'active_record'
5
+ require 'support/active_record'
6
+ require 'has_metrics'
7
+
8
+ RSpec.configure do |config|
9
+ # some (optional) config here
10
+ end
@@ -0,0 +1,10 @@
1
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
2
+
3
+ # ActiveRecord::Migrator.up "db/migrate"
4
+
5
+ def create_tables_for(model = :user)
6
+ ActiveRecord::Migration.create_table "#{model}s", :force => true do |t|
7
+ t.string "name"
8
+ end
9
+ ActiveRecord::Migration.create_table "#{model}_metrics", :force => true
10
+ end
metadata CHANGED
@@ -1,94 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_metrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
5
- prerelease:
4
+ version: 0.0.6
6
5
  platform: ruby
7
6
  authors:
8
7
  - Allan Grant
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-03-13 00:00:00.000000000 Z
11
+ date: 2013-05-01 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: activerecord
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
- name: shoulda
42
+ name: bundler
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- - !ruby/object:Gem::Dependency
63
- name: sqlite3
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: '0'
70
- type: :development
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: '0'
78
- - !ruby/object:Gem::Dependency
79
- name: pry
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ! '>='
52
+ - - '>='
92
53
  - !ruby/object:Gem::Version
93
54
  version: '0'
94
55
  description: Calculate metrics and store them in the DB.
@@ -108,42 +69,37 @@ files:
108
69
  - lib/has_metrics/metrics.rb
109
70
  - lib/has_metrics/segmentation.rb
110
71
  - lib/has_metrics/version.rb
111
- - test.db
112
- - test/functional/metrics_test.rb
113
- - test/functional/segmentation_test.rb
114
- - test/test_helper.rb
72
+ - spec/metrics_spec.rb
73
+ - spec/segmentation_spec.rb
74
+ - spec/spec_helper.rb
75
+ - spec/support/active_record.rb
115
76
  homepage: http://github.com/allangrant/has_metrics
116
- licenses: []
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
117
80
  post_install_message:
118
81
  rdoc_options: []
119
82
  require_paths:
120
83
  - lib
121
84
  required_ruby_version: !ruby/object:Gem::Requirement
122
- none: false
123
85
  requirements:
124
- - - ! '>='
86
+ - - '>='
125
87
  - !ruby/object:Gem::Version
126
88
  version: '0'
127
- segments:
128
- - 0
129
- hash: 3863609123876362244
130
89
  required_rubygems_version: !ruby/object:Gem::Requirement
131
- none: false
132
90
  requirements:
133
- - - ! '>='
91
+ - - '>='
134
92
  - !ruby/object:Gem::Version
135
93
  version: '0'
136
- segments:
137
- - 0
138
- hash: 3863609123876362244
139
94
  requirements: []
140
95
  rubyforge_project:
141
- rubygems_version: 1.8.24
96
+ rubygems_version: 2.0.3
142
97
  signing_key:
143
- specification_version: 3
98
+ specification_version: 4
144
99
  summary: Calculate "metrics" (any expensive methods) on ActiveRecord entries and memoize
145
100
  them to an automagical table.
146
101
  test_files:
147
- - test/functional/metrics_test.rb
148
- - test/functional/segmentation_test.rb
149
- - test/test_helper.rb
102
+ - spec/metrics_spec.rb
103
+ - spec/segmentation_spec.rb
104
+ - spec/spec_helper.rb
105
+ - spec/support/active_record.rb
@@ -1,51 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
-
3
- class User < ActiveRecord::Base
4
- include Metrics
5
- has_metric :name_length do
6
- name.length
7
- end
8
- end
9
-
10
- class MetricsTest < Test::Unit::TestCase
11
- context "when defining metrics" do
12
- setup do
13
- CreateTestTables.up(:user)
14
- @user = User.create(:name => "Fuzz")
15
- User.update_all_metrics!
16
- end
17
-
18
- should "create rows for the metrics" do
19
- assert_equal 3, UserMetrics.columns.count
20
- User.has_metric :name_length_squared do
21
- name_length * name_length
22
- end
23
- User.update_all_metrics!
24
- assert_equal 5, UserMetrics.columns.count
25
- assert_equal 16, @user.name_length_squared
26
- end
27
-
28
- should "calculate their block when called" do
29
- assert_equal "Fuzz", @user.name
30
- assert_equal 4, @user.name_length
31
-
32
- @user.name = "Bib"
33
-
34
- # since 20 hours hasn't passed, the value is pulled from cache, not recalculated
35
- assert_equal 4, @user.name_length
36
- # (true) forces it to recalculate right away
37
- assert_equal 3, @user.name_length(true)
38
-
39
- # since it wasn't saved, it's the same in the DB
40
- assert_equal 4, User.find_by_name("Fuzz").name_length
41
-
42
- @user.save
43
- assert_equal 3, @user.name_length(true)
44
- assert_equal 3, User.find_by_name("Bib").name_length
45
- end
46
-
47
- should "have their values precomputed" do
48
- assert_equal({4=>1}, UserMetrics.count(:group => :name_length))
49
- end
50
- end
51
- end
@@ -1,34 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
-
3
- class Post < ActiveRecord::Base
4
- include Metrics
5
- include Segmentation
6
-
7
- segment_by :name_length do
8
- case name.length
9
- when 0..6
10
- "short"
11
- when 7
12
- "seven"
13
- else
14
- "long"
15
- end
16
- end
17
- end
18
-
19
- class SegmentationTest < Test::Unit::TestCase
20
- context "when defining segments" do
21
- setup do
22
- CreateTestTables.up(:post)
23
-
24
- Post.create(:name => "Shorty")
25
- Post.create(:name => "Seven!!")
26
- Post.create(:name => "Really long")
27
- Post.update_all_metrics!
28
- end
29
-
30
- should "segment properly" do
31
- assert_equal({'short'=>1, 'seven'=>1, 'long'=>1}, PostMetrics.count(:group => :by_name_length))
32
- end
33
- end
34
- end
data/test/test_helper.rb DELETED
@@ -1,28 +0,0 @@
1
- require 'rubygems'
2
-
3
- # Want to test the files here, in lib, not in an installed version of the gem.
4
- $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
5
-
6
- require 'shoulda'
7
- # require 'mocha'
8
- require 'active_record'
9
- require 'sqlite3'
10
- require 'has_metrics'
11
- require 'pry'
12
-
13
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => File.expand_path(File.dirname(__FILE__) + "/../test.db"))
14
-
15
-
16
- class CreateTestTables < ActiveRecord::Migration
17
- def self.up(model = :user)
18
- create_table "#{model}s", :force => true do |t|
19
- t.string "name"
20
- end
21
- create_table "#{model}_metrics", :force => true
22
- end
23
-
24
- def self.down(model = :user)
25
- drop_table "#{model}s"
26
- drop_table "#{model}_metrics"
27
- end
28
- end
data/test.db DELETED
Binary file