has_metrics 0.0.1 → 0.0.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.
data/has_metrics.gemspec CHANGED
@@ -4,7 +4,7 @@ require File.expand_path('../lib/has_metrics/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Allan Grant"]
6
6
  gem.email = ["allan@allangrant.net"]
7
- gem.description = %q{Memoization into activerecord.}
7
+ gem.description = %q{Calculate metrics and store them in the DB.}
8
8
  gem.summary = %q{Calculate "metrics" (any expensive methods) on ActiveRecord entries and memoize them to an automagical table.}
9
9
  gem.homepage = "http://github.com/allangrant/has_metrics"
10
10
 
@@ -15,9 +15,9 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = HasMetrics::VERSION
17
17
 
18
- gem.add_dependency("activerecord")
19
- gem.add_development_dependency("rake")
20
- gem.add_development_dependency("shoulda")
21
- # gem.add_development_dependency("mocha")
22
- gem.add_development_dependency("sqlite3")
18
+ gem.add_dependency "activerecord"
19
+ gem.add_development_dependency "rake"
20
+ gem.add_development_dependency "shoulda"
21
+ gem.add_development_dependency "sqlite3"
22
+ gem.add_development_dependency "pry"
23
23
  end
@@ -53,9 +53,14 @@ module Metrics
53
53
  result = instance_exec(&block)
54
54
  result = nil if result.is_a?(Float) && !result.finite?
55
55
  begin
56
- metrics.update_attributes(name => result, datestamp_column => Time.zone.now)
57
- rescue NoMethodError
58
- # This happens if the migrations haven't run yet. We should still calculate & return the metric.
56
+ metrics.send "#{name}=", result
57
+ metrics.send "#{datestamp_column}=", Time.now
58
+ rescue NoMethodError => e
59
+ raise e unless e.name == "#{name}=".to_sym
60
+ # This happens if the migrations haven't run yet for this metric. We should still calculate & return the metric.
61
+ end
62
+ unless changed?
63
+ metrics.save
59
64
  end
60
65
  result
61
66
  end
@@ -88,7 +93,8 @@ module Metrics
88
93
  :datetime
89
94
  when @float_metrics && @float_metrics.include?(column)
90
95
  :float
91
- else :integer
96
+ else
97
+ :integer
92
98
  end
93
99
  end
94
100
 
@@ -145,7 +151,7 @@ module Metrics
145
151
  raise "Cannot determine if there were extra columns for has_metric when using the table itself for storing the metric! Remove any columns manually"
146
152
  [] # We wont know what columns are excessive if the source changed
147
153
  else
148
- (columns.map(&:name) - %w(id created_at updated_at)).map - required_columns
154
+ (columns.map(&:name) - %w(id created_at updated_at)) - required_columns
149
155
  end
150
156
 
151
157
  end
@@ -0,0 +1,36 @@
1
+ # include Segmentation
2
+ module Segmentation
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ # CLASS METHODS ADDED
8
+ module ClassMethods
9
+ def segment_by category, &definition
10
+ (@segment_categories ||= []) << category.to_sym
11
+ define_method("segment_by_#{category}", definition)
12
+ if respond_to?(:has_metric)
13
+ has_metric "by_#{category}" do
14
+ send("segment_by_#{category}")
15
+ end
16
+ end
17
+ end
18
+ def segment_categories
19
+ @segment_categories
20
+ end
21
+ def update_segments!
22
+ puts "Updating all segments on #{name}: #{segment_categories.join(', ')}"
23
+ all.each do |object|
24
+ segment_categories.each do |category|
25
+ object.update_segment!(category)
26
+ end
27
+ end
28
+ end
29
+ end # END OF CLASS METHODS
30
+
31
+ # INSTANCE METHODS ADDED
32
+ def update_segment!(category)
33
+ update_attribute("by_#{category}", send("segment_by_#{category}"))
34
+ end
35
+ # END OF INSTANCE METHODS
36
+ end
@@ -1,3 +1,3 @@
1
1
  module HasMetrics
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/has_metrics.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  require "has_metrics/version"
2
2
  require "has_metrics/metrics"
3
+ require "has_metrics/segmentation"
3
4
 
4
5
  module HasMetrics
5
-
6
+ def self.included(base)
7
+ base.send :include, Metrics
8
+ base.send :include, Segmentation
9
+ end
6
10
  end
@@ -1,23 +1,5 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
2
 
3
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
4
- :database => File.expand_path(File.dirname(__FILE__) + "/../test.db"))
5
-
6
- class CreateTestTables < ActiveRecord::Migration
7
- def self.up
8
- create_table "users", :force => true do |t|
9
- t.string "name"
10
- end
11
- create_table "user_metrics", :force => true
12
- end
13
-
14
- def self.down
15
- drop_table "users"
16
- drop_table "user_metrics"
17
- end
18
- end
19
-
20
-
21
3
  class User < ActiveRecord::Base
22
4
  include Metrics
23
5
  has_metric :name_length do
@@ -25,16 +7,12 @@ class User < ActiveRecord::Base
25
7
  end
26
8
  end
27
9
 
28
-
29
10
  class MetricsTest < Test::Unit::TestCase
30
11
  context "when defining metrics" do
31
12
  setup do
32
- root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
33
-
34
- CreateTestTables.up
35
- User.update_all_metrics!
36
- @user_name =
13
+ CreateTestTables.up(:user)
37
14
  @user = User.create(:name => "Fuzz")
15
+ User.update_all_metrics!
38
16
  end
39
17
 
40
18
  should "create rows for the metrics" do
@@ -47,12 +25,27 @@ class MetricsTest < Test::Unit::TestCase
47
25
  assert_equal 16, @user.name_length_squared
48
26
  end
49
27
 
50
- should "they should calculate their block when called" do
28
+ should "calculate their block when called" do
51
29
  assert_equal "Fuzz", @user.name
52
30
  assert_equal 4, @user.name_length
31
+
53
32
  @user.name = "Bib"
54
- assert_equal 3, @user.name_length
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
55
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))
56
49
  end
57
50
  end
58
51
  end
@@ -0,0 +1,34 @@
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 CHANGED
@@ -5,6 +5,24 @@ $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
5
5
 
6
6
  require 'shoulda'
7
7
  # require 'mocha'
8
- require 'activerecord'
8
+ require 'active_record'
9
9
  require 'sqlite3'
10
- require 'has_metrics/metrics'
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 ADDED
Binary file
metadata CHANGED
@@ -1,89 +1,103 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: has_metrics
3
- version: !ruby/object:Gem::Version
4
- hash: 29
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 0
9
- - 1
10
- version: 0.0.1
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Allan Grant
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-04-07 00:00:00 -07:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
22
- version_requirements: &id001 !ruby/object:Gem::Requirement
23
- none: false
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- hash: 3
28
- segments:
29
- - 0
30
- version: "0"
31
- requirement: *id001
32
- prerelease: false
12
+ date: 2012-11-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
33
15
  name: activerecord
34
- type: :runtime
35
- - !ruby/object:Gem::Dependency
36
- version_requirements: &id002 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
37
17
  none: false
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- hash: 3
42
- segments:
43
- - 0
44
- version: "0"
45
- requirement: *id002
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
46
23
  prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
47
31
  name: rake
48
- type: :development
49
- - !ruby/object:Gem::Dependency
50
- version_requirements: &id003 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
51
33
  none: false
52
- requirements:
53
- - - ">="
54
- - !ruby/object:Gem::Version
55
- hash: 3
56
- segments:
57
- - 0
58
- version: "0"
59
- requirement: *id003
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
60
39
  prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
61
47
  name: shoulda
62
- type: :development
63
- - !ruby/object:Gem::Dependency
64
- version_requirements: &id004 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
65
49
  none: false
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- hash: 3
70
- segments:
71
- - 0
72
- version: "0"
73
- requirement: *id004
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
74
55
  prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
75
63
  name: sqlite3
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
76
70
  type: :development
77
- description: Memoization into activerecord.
78
- email:
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
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Calculate metrics and store them in the DB.
95
+ email:
79
96
  - allan@allangrant.net
80
97
  executables: []
81
-
82
98
  extensions: []
83
-
84
99
  extra_rdoc_files: []
85
-
86
- files:
100
+ files:
87
101
  - .gitignore
88
102
  - Gemfile
89
103
  - LICENSE
@@ -92,43 +106,44 @@ files:
92
106
  - has_metrics.gemspec
93
107
  - lib/has_metrics.rb
94
108
  - lib/has_metrics/metrics.rb
109
+ - lib/has_metrics/segmentation.rb
95
110
  - lib/has_metrics/version.rb
111
+ - test.db
96
112
  - test/functional/metrics_test.rb
113
+ - test/functional/segmentation_test.rb
97
114
  - test/test_helper.rb
98
- has_rdoc: true
99
115
  homepage: http://github.com/allangrant/has_metrics
100
116
  licenses: []
101
-
102
117
  post_install_message:
103
118
  rdoc_options: []
104
-
105
- require_paths:
119
+ require_paths:
106
120
  - lib
107
- required_ruby_version: !ruby/object:Gem::Requirement
121
+ required_ruby_version: !ruby/object:Gem::Requirement
108
122
  none: false
109
- requirements:
110
- - - ">="
111
- - !ruby/object:Gem::Version
112
- hash: 3
113
- segments:
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ segments:
114
128
  - 0
115
- version: "0"
116
- required_rubygems_version: !ruby/object:Gem::Requirement
129
+ hash: -764161358984080823
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
131
  none: false
118
- requirements:
119
- - - ">="
120
- - !ruby/object:Gem::Version
121
- hash: 3
122
- segments:
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ segments:
123
137
  - 0
124
- version: "0"
138
+ hash: -764161358984080823
125
139
  requirements: []
126
-
127
140
  rubyforge_project:
128
- rubygems_version: 1.5.3
141
+ rubygems_version: 1.8.24
129
142
  signing_key:
130
143
  specification_version: 3
131
- summary: Calculate "metrics" (any expensive methods) on ActiveRecord entries and memoize them to an automagical table.
132
- test_files:
144
+ summary: Calculate "metrics" (any expensive methods) on ActiveRecord entries and memoize
145
+ them to an automagical table.
146
+ test_files:
133
147
  - test/functional/metrics_test.rb
148
+ - test/functional/segmentation_test.rb
134
149
  - test/test_helper.rb