has_metrics 0.0.3 → 0.0.6

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