trackoid 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/trackoid/aggregates.rb +95 -21
- data/lib/trackoid/errors.rb +9 -0
- data/lib/trackoid/tracker.rb +30 -8
- data/lib/trackoid/tracker_aggregates.rb +29 -0
- data/lib/trackoid/tracking.rb +21 -9
- data/lib/trackoid.rb +1 -0
- data/spec/aggregates_spec.rb +98 -12
- data/spec/spec_helper.rb +2 -1
- data/spec/trackoid_spec.rb +12 -4
- data/trackoid.gemspec +3 -2
- metadata +5 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/lib/trackoid/aggregates.rb
CHANGED
@@ -1,26 +1,74 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
module Mongoid #:nodoc:
|
2
|
-
module Tracking
|
3
|
-
|
3
|
+
module Tracking #:nodoc:
|
4
4
|
module Aggregates
|
5
5
|
# This module includes aggregate data extensions to Trackoid instances
|
6
6
|
def self.included(base)
|
7
7
|
base.class_eval do
|
8
8
|
extend ClassMethods
|
9
|
-
include InstanceMethods
|
10
9
|
|
11
10
|
class_inheritable_accessor :aggregate_fields, :aggregate_klass
|
12
|
-
self.aggregate_fields =
|
11
|
+
self.aggregate_fields = {}
|
13
12
|
self.aggregate_klass = nil
|
14
|
-
|
13
|
+
delegate :aggregate_fields,
|
14
|
+
:aggregate_klass,
|
15
|
+
:aggregated?,
|
16
|
+
:to => "self.class"
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
20
|
module ClassMethods
|
19
|
-
|
21
|
+
# Defines an aggregate token to an already tracked model. It defines
|
22
|
+
# a new mongoid model named after the original model.
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
#
|
26
|
+
# <tt>class Page</tt>
|
27
|
+
# <tt> include Mongoid::Document</tt>
|
28
|
+
# <tt> include Mongoid::Document</tt>
|
29
|
+
# <tt> track :visits</tt>
|
30
|
+
# <tt> aggregate :browsers do |b|</tt>
|
31
|
+
# <tt> b.split(" ").first</tt>
|
32
|
+
# <tt> end</tt>
|
33
|
+
# <tt>end</tt>
|
34
|
+
#
|
35
|
+
# A new model is defined as <tt>class PageAggregates</tt>
|
36
|
+
#
|
37
|
+
# This model has the following structure:
|
38
|
+
#
|
39
|
+
# <tt>belongs_to :page</tt>
|
40
|
+
# <tt>field :ns, :type => String</tt>
|
41
|
+
# <tt>field :key, :type => String</tt>
|
42
|
+
# <tt>index [:ns, :key], :unique => true</tt>
|
43
|
+
# <tt>track :[original_parent_tracking_data]</tt>
|
44
|
+
# <tt>track :...</tt>
|
45
|
+
#
|
46
|
+
# :ns is the "namespace". It's the name you put along the
|
47
|
+
# "aggregate :browsers" in the original model definition.
|
48
|
+
#
|
49
|
+
# :key is your aggregation key. This is the value you are required to
|
50
|
+
# return in the "aggregate" block.
|
51
|
+
#
|
52
|
+
# With the above structure, you can always query aggregates directly
|
53
|
+
# using Mongoid this way:
|
54
|
+
#
|
55
|
+
# <tt>TestModelAggregates.where(:ns => "browsers", :key => "explorer").first</tt>
|
56
|
+
#
|
57
|
+
# But you are encouraged to use Trackoid methods whenever possible.
|
58
|
+
#
|
20
59
|
def aggregate(name, &block)
|
60
|
+
raise Errors::AggregationAlreadyDefined.new(self.name, name) if
|
61
|
+
aggregate_fields.has_key? name
|
62
|
+
|
21
63
|
define_aggregate_model if aggregate_klass.nil?
|
22
|
-
|
64
|
+
has_many_related internal_accessor_name(name), :class_name => aggregate_klass.to_s
|
23
65
|
add_aggregate_field(name, block)
|
66
|
+
create_aggregation_accessors(name)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return true if this model has aggregated data.
|
70
|
+
def aggregated?
|
71
|
+
!aggregate_klass.nil?
|
24
72
|
end
|
25
73
|
|
26
74
|
protected
|
@@ -30,37 +78,62 @@ module Mongoid #:nodoc:
|
|
30
78
|
str.camelize
|
31
79
|
end
|
32
80
|
|
81
|
+
def internal_accessor_name(name)
|
82
|
+
(name.to_s + "_accessor").to_sym
|
83
|
+
end
|
84
|
+
|
85
|
+
# Defines the aggregation model. It checks for class name conflicts
|
33
86
|
def define_aggregate_model
|
34
87
|
raise Errors::ClassAlreadyDefined.new(internal_aggregates_name) if foreign_class_defined
|
88
|
+
parent_name = self.name.underscore
|
35
89
|
define_klass do
|
36
90
|
include Mongoid::Document
|
37
91
|
include Mongoid::Tracking
|
38
|
-
|
39
|
-
#
|
92
|
+
|
93
|
+
# Make the relation to the original class
|
94
|
+
belongs_to_related parent_name.to_sym, :class_name => parent_name.camelize
|
95
|
+
|
96
|
+
# Internal fields to track aggregation token and keys
|
97
|
+
field :ns, :type => String
|
98
|
+
field :key, :type => String
|
99
|
+
index [[:ns, Mongo::ASCENDING], [:key, Mongo::ASCENDING]], :unique => true, :background => true
|
100
|
+
|
101
|
+
# Include parent tracking data.
|
102
|
+
parent_name.camelize.constantize.tracked_fields.each {|track_field| track track_field }
|
40
103
|
end
|
41
104
|
self.aggregate_klass = internal_aggregates_name.constantize
|
42
105
|
end
|
43
|
-
|
106
|
+
|
107
|
+
# Returns true if there is a class defined with the same name as our
|
108
|
+
# aggregate class.
|
44
109
|
def foreign_class_defined
|
45
110
|
Object.const_defined?(internal_aggregates_name.to_sym)
|
46
111
|
end
|
47
112
|
|
113
|
+
# Adds the aggregate field to the array of aggregated fields.
|
48
114
|
def add_aggregate_field(name, block)
|
49
|
-
aggregate_fields
|
115
|
+
aggregate_fields[name] = block
|
50
116
|
end
|
51
117
|
|
118
|
+
# Defines the aggregation external class. This class is named after
|
119
|
+
# the original class model but with "Aggregates" appended.
|
120
|
+
# Example: TestModel ==> TestModelAggregates
|
52
121
|
def define_klass(&block)
|
53
|
-
|
54
|
-
klass = Object.const_set internal_aggregates_name, Class.new
|
122
|
+
klass = Object.const_set(internal_aggregates_name, Class.new)
|
55
123
|
klass.class_eval(&block)
|
56
124
|
end
|
57
125
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
126
|
+
def create_aggregation_accessors(name)
|
127
|
+
# Aggregation accessors in the model acts like a named scope
|
128
|
+
define_method(name) do |*args|
|
129
|
+
TrackerAggregates.new(self, name, args)
|
130
|
+
end
|
131
|
+
|
132
|
+
define_method("#{name}=") do
|
133
|
+
raise NoMethodError
|
134
|
+
end
|
63
135
|
end
|
136
|
+
|
64
137
|
end
|
65
138
|
|
66
139
|
end
|
@@ -79,9 +152,10 @@ module Mongoid #:nodoc:
|
|
79
152
|
# end
|
80
153
|
#
|
81
154
|
#
|
82
|
-
# self.visits.inc
|
83
|
-
#
|
84
|
-
#
|
155
|
+
# self.visits(agg).inc
|
156
|
+
# self.visits.today
|
157
|
+
# self.visits.browsers.today
|
158
|
+
#
|
85
159
|
# users
|
86
160
|
# { _id: 32334333, name:"pepe", visits_data:{} }
|
87
161
|
#
|
data/lib/trackoid/errors.rb
CHANGED
@@ -11,6 +11,15 @@ module Mongoid #:nodoc
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
class AggregationAlreadyDefined < RuntimeError
|
15
|
+
def initialize(klass, token)
|
16
|
+
@token = token
|
17
|
+
end
|
18
|
+
def message
|
19
|
+
"Aggregation '#{@token}' already defined for model #{klass}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
14
23
|
class ModelNotSaved < RuntimeError; end
|
15
24
|
|
16
25
|
class NotMongoid < RuntimeError; end
|
data/lib/trackoid/tracker.rb
CHANGED
@@ -3,19 +3,41 @@ module Mongoid #:nodoc:
|
|
3
3
|
module Tracking
|
4
4
|
# This internal class handles all interaction for a track field.
|
5
5
|
class Tracker
|
6
|
-
|
6
|
+
|
7
|
+
def initialize(owner, field, aggregate_data)
|
7
8
|
@owner, @for = owner, field
|
8
|
-
@data = @owner.read_attribute(@for)
|
9
|
+
@data = @owner.read_attribute(@for)
|
10
|
+
|
11
|
+
# The following is needed if the "field" Mongoid definition for our
|
12
|
+
# internal tracking field does not include option ":default => {}"
|
13
|
+
if @data.nil?
|
14
|
+
@owner.write_attribute(@for, {})
|
15
|
+
@data = @owner.read_attribute(@for)
|
16
|
+
end
|
17
|
+
|
18
|
+
@aggregate_data = aggregate_data
|
9
19
|
end
|
10
20
|
|
11
21
|
# Update methods
|
12
22
|
def add(how_much = 1, date = DateTime.now)
|
13
23
|
raise Errors::ModelNotSaved, "Can't update a new record" if @owner.new_record?
|
14
|
-
|
15
24
|
update_data(data_for(date) + how_much, date)
|
25
|
+
|
16
26
|
@owner.collection.update( @owner._selector,
|
17
27
|
{ (how_much > 0 ? "$inc" : "$dec") => update_hash(how_much.abs, date) },
|
18
28
|
:upsert => true)
|
29
|
+
|
30
|
+
return unless @owner.aggregated?
|
31
|
+
|
32
|
+
@owner.aggregate_fields.each do |(k,v)|
|
33
|
+
token = v.call(@aggregate_data)
|
34
|
+
fk = @owner.class.name.to_s.foreign_key.to_sym
|
35
|
+
selector = { fk => @owner.id, :ns => k, :key => token }
|
36
|
+
|
37
|
+
@owner.aggregate_klass.collection.update( selector,
|
38
|
+
{ (how_much > 0 ? "$inc" : "$dec") => update_hash(how_much.abs, date) },
|
39
|
+
:upsert => true)
|
40
|
+
end
|
19
41
|
end
|
20
42
|
|
21
43
|
def inc(date = DateTime.now)
|
@@ -28,13 +50,13 @@ module Mongoid #:nodoc:
|
|
28
50
|
|
29
51
|
def set(how_much, date = DateTime.now)
|
30
52
|
raise Errors::ModelNotSaved, "Can't update a new record" if @owner.new_record?
|
31
|
-
|
32
53
|
update_data(how_much, date)
|
33
54
|
@owner.collection.update( @owner._selector,
|
34
55
|
{ "$set" => update_hash(how_much, date) },
|
35
56
|
:upsert => true)
|
36
57
|
end
|
37
58
|
|
59
|
+
|
38
60
|
# Access methods
|
39
61
|
def today
|
40
62
|
data_for(Date.today)
|
@@ -46,7 +68,6 @@ module Mongoid #:nodoc:
|
|
46
68
|
|
47
69
|
def last_days(how_much = 7)
|
48
70
|
return [today] unless how_much > 0
|
49
|
-
|
50
71
|
date, values = DateTime.now, []
|
51
72
|
(date - how_much.abs + 1).step(date) {|d| values << data_for(d) }
|
52
73
|
values
|
@@ -61,11 +82,13 @@ module Mongoid #:nodoc:
|
|
61
82
|
# Private methods
|
62
83
|
private
|
63
84
|
def data_for(date)
|
64
|
-
return 0 if @data
|
65
|
-
|
85
|
+
return 0 if @data.nil? ||
|
86
|
+
@data[date.year.to_s].nil? ||
|
87
|
+
@data[date.year.to_s][date.month.to_s].nil?
|
66
88
|
@data[date.year.to_s][date.month.to_s][date.day.to_s] || 0
|
67
89
|
end
|
68
90
|
|
91
|
+
# TODO: It should be a better way of updating a hash. :-)
|
69
92
|
def update_data(value, date)
|
70
93
|
if @data[date.year.to_s]
|
71
94
|
if @data[date.year.to_s][date.month.to_s]
|
@@ -94,7 +117,6 @@ module Mongoid #:nodoc:
|
|
94
117
|
}
|
95
118
|
end
|
96
119
|
|
97
|
-
|
98
120
|
# WARNING: This is +only+ for debugging pourposes (rspec y tal)
|
99
121
|
def _original_hash
|
100
122
|
@data
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Tracking
|
4
|
+
# This internal class handles all interaction of an aggregation token.
|
5
|
+
class TrackerAggregates
|
6
|
+
|
7
|
+
def initialize(owner, token, key_selector)
|
8
|
+
@owner, @token = owner, token
|
9
|
+
@key = key_selector
|
10
|
+
|
11
|
+
@accessor = @owner.class.send(:internal_accessor_name, @token)
|
12
|
+
@selector = {:ns => @token}
|
13
|
+
@selector.merge!(:key => @key.first) if @key.first
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(name, *args, &block)
|
17
|
+
@criteria ||= @owner.send(@accessor).where(@selector)
|
18
|
+
|
19
|
+
# Delegate all missing methods to the underlying Mongoid Criteria
|
20
|
+
return @criteria.send(name) unless @owner.tracked_fields.member?(name)
|
21
|
+
|
22
|
+
# Otherwise, it's a track method, so process it
|
23
|
+
@criteria.collect {|x| x.send(name, *args, &block) }
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/trackoid/tracking.rb
CHANGED
@@ -20,13 +20,14 @@ module Mongoid #:nodoc:
|
|
20
20
|
|
21
21
|
module ClassMethods
|
22
22
|
# Adds analytics tracking for +name+. Adds a +'name'_data+ mongoid
|
23
|
-
# field as a Hash for tracking this information. Additionaly,
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
23
|
+
# field as a Hash for tracking this information. Additionaly, hiddes
|
24
|
+
# the field, so that the user can not mangle with the original one.
|
25
|
+
# This is necessary so that Mongoid does not "dirty" the field
|
26
|
+
# potentially overwriting the original data.
|
27
27
|
def track(name)
|
28
28
|
set_tracking_field(name)
|
29
29
|
create_tracking_accessors(name)
|
30
|
+
update_aggregates(name) if aggregated?
|
30
31
|
end
|
31
32
|
|
32
33
|
protected
|
@@ -38,10 +39,10 @@ module Mongoid #:nodoc:
|
|
38
39
|
# Configures the internal fields for tracking. Additionally also creates
|
39
40
|
# an index for the internal tracking field.
|
40
41
|
def set_tracking_field(name)
|
41
|
-
field internal_track_name(name), :type => Hash, :default => {}
|
42
|
-
#
|
42
|
+
field internal_track_name(name), :type => Hash # , :default => {}
|
43
|
+
# Should we make an index for this field?
|
43
44
|
index internal_track_name(name)
|
44
|
-
tracked_fields <<
|
45
|
+
tracked_fields << name.to_sym
|
45
46
|
end
|
46
47
|
|
47
48
|
# Creates the tracking field accessor and also disables the original
|
@@ -49,8 +50,8 @@ module Mongoid #:nodoc:
|
|
49
50
|
# Mongoid fields ensures they doesn't get dirty, so Mongoid does not
|
50
51
|
# overwrite old data.
|
51
52
|
def create_tracking_accessors(name)
|
52
|
-
define_method("#{name}") do
|
53
|
-
Tracker.new(self, "#{name}_data".to_sym)
|
53
|
+
define_method("#{name}") do |*aggr|
|
54
|
+
Tracker.new(self, "#{name}_data".to_sym, aggr)
|
54
55
|
end
|
55
56
|
|
56
57
|
# Should we just "undef" this methods?
|
@@ -62,6 +63,17 @@ module Mongoid #:nodoc:
|
|
62
63
|
define_method("#{name}_data=") do
|
63
64
|
raise NoMethodError
|
64
65
|
end
|
66
|
+
|
67
|
+
# I think it's important to override also the #{name}_changed? so
|
68
|
+
# as to be sure Mongoid never mark this field as dirty.
|
69
|
+
define_method("#{name}_changed?") do
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Updates the aggregated class for it to include a new tracking field
|
75
|
+
def update_aggregates(name)
|
76
|
+
aggregate_klass.track name
|
65
77
|
end
|
66
78
|
|
67
79
|
end
|
data/lib/trackoid.rb
CHANGED
data/spec/aggregates_spec.rb
CHANGED
@@ -5,11 +5,15 @@ class TestModel
|
|
5
5
|
include Mongoid::Tracking
|
6
6
|
|
7
7
|
field :name # Dummy field
|
8
|
+
|
9
|
+
# Note that references to "track" and "aggregate" in this test are mixed
|
10
|
+
# for testing pourposes. Trackoid does not make any difference in the
|
11
|
+
# declaration order of tracking fields and aggregate tokens.
|
8
12
|
track :visits
|
13
|
+
aggregate :browsers do; "Mozilla".downcase; end
|
9
14
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
15
|
+
track :uniques
|
16
|
+
aggregate :referers do; "GoogleBot".downcase; end
|
13
17
|
end
|
14
18
|
|
15
19
|
class SecondTestModel
|
@@ -20,7 +24,7 @@ class SecondTestModel
|
|
20
24
|
track :visits
|
21
25
|
|
22
26
|
aggregate :browsers do
|
23
|
-
"Chrome"
|
27
|
+
"Chrome".downcase
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
@@ -39,30 +43,61 @@ describe Mongoid::Tracking::Aggregates do
|
|
39
43
|
end
|
40
44
|
|
41
45
|
it "should create a has_many relationship in the original model" do
|
42
|
-
|
46
|
+
# Note that due to ActiveSupport "class_inheritable_accessor" this method
|
47
|
+
# is available both as class method and instance method.
|
48
|
+
@mock.class.method_defined?(:browsers_accessor).should be_true
|
43
49
|
end
|
44
50
|
|
45
|
-
it "should have the aggregates klass in a instance var" do
|
46
|
-
|
51
|
+
it "should have the aggregates klass in a class/instance var" do
|
52
|
+
# Note that due to ActiveSupport "class_inheritable_accessor" this method
|
53
|
+
# is available both as class method and instance method.
|
54
|
+
@mock.class.aggregate_klass == TestModelAggregates
|
47
55
|
end
|
48
56
|
|
49
|
-
it "should create
|
50
|
-
|
57
|
+
it "should create a hash in the class with all aggregate fields" do
|
58
|
+
# Note that due to ActiveSupport "class_inheritable_accessor" this method
|
59
|
+
# is available both as class method and instance method.
|
60
|
+
@mock.class.aggregate_fields.keys.to_set.should == [ :browsers, :referers ].to_set
|
51
61
|
end
|
52
62
|
|
53
63
|
it "should create an array in the class with all aggregate fields even when monkey patching" do
|
54
64
|
class TestModel
|
55
|
-
aggregate :
|
56
|
-
"
|
65
|
+
aggregate :quarters do
|
66
|
+
"Q1"
|
57
67
|
end
|
58
68
|
end
|
59
|
-
@mock.class.aggregate_fields.
|
69
|
+
@mock.class.aggregate_fields.keys.to_set.should == [ :browsers, :referers, :quarters ].to_set
|
70
|
+
end
|
71
|
+
|
72
|
+
it "the aggregated class should have the same tracking fields as the parent class" do
|
73
|
+
TestModelAggregates.tracked_fields.should == TestModel.tracked_fields
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should raise error if we try to add an aggregation token twice" do
|
77
|
+
lambda {
|
78
|
+
class TestModel
|
79
|
+
aggregate :referers do
|
80
|
+
"(none)"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
}.should raise_error Mongoid::Errors::AggregationAlreadyDefined
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should have Mongoid accessors defined" do
|
87
|
+
tm = TestModel.create(:name => "Dummy")
|
88
|
+
tm.send(tm.class.send(:internal_accessor_name, "browsers")).class.should == Mongoid::Criteria
|
89
|
+
tm.send(tm.class.send(:internal_accessor_name, "referers")).class.should == Mongoid::Criteria
|
90
|
+
tm.send(tm.class.send(:internal_accessor_name, "quarters")).class.should == Mongoid::Criteria
|
60
91
|
end
|
61
92
|
|
62
93
|
it "should indicate this is an aggregated traking object with aggregated?" do
|
63
94
|
@mock.aggregated?.should be_true
|
64
95
|
end
|
65
96
|
|
97
|
+
it "should indicate this is an aggregated class with aggregated?" do
|
98
|
+
@mock.class.aggregated?.should be_true
|
99
|
+
end
|
100
|
+
|
66
101
|
it "should raise error if already defined class with the same aggregated klass name" do
|
67
102
|
lambda {
|
68
103
|
class MockTestAggregates
|
@@ -115,4 +150,55 @@ describe Mongoid::Tracking::Aggregates do
|
|
115
150
|
}.should raise_error Mongoid::Errors::ClassAlreadyDefined
|
116
151
|
end
|
117
152
|
|
153
|
+
describe "when tracking a model with aggregation data" do
|
154
|
+
|
155
|
+
before(:all) do
|
156
|
+
TestModel.delete_all
|
157
|
+
TestModel.create(:name => "test")
|
158
|
+
@object_id = TestModel.first.id
|
159
|
+
end
|
160
|
+
|
161
|
+
before do
|
162
|
+
@mock = TestModel.find(@object_id)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "calling an aggregation scope should return the appropiate class" do
|
166
|
+
@mock.browsers.class.should == Mongoid::Tracking::TrackerAggregates
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should increment visits for all aggregated instances" do
|
170
|
+
@mock.visits("Aggregate data").inc
|
171
|
+
@mock.browsers.count.should == 1
|
172
|
+
@mock.referers.count.should == 1
|
173
|
+
@mock.quarters.count.should == 1
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should increment visits for specific aggregation keys" do
|
177
|
+
@mock.browsers("mozilla").size.should == 1
|
178
|
+
@mock.referers("googlebot").size.should == 1
|
179
|
+
@mock.quarters("Q1").size.should == 1
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should NOT increment visits for different aggregation keys" do
|
183
|
+
@mock.browsers("internet explorer").size.should == 0
|
184
|
+
@mock.referers("yahoo slurp").size.should == 0
|
185
|
+
@mock.quarters("Q2").size.should == 0
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should have 1 in visits" do
|
189
|
+
tt = @mock.browsers.visits.collect {|v| v.last_days(7)}
|
190
|
+
tt.should == [[0, 0, 0, 0, 0, 0, 1]]
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
# it "should print methods && their classes to stdout" do
|
198
|
+
# TestModel.methods.sort.each {|x|
|
199
|
+
# puts "#{x}"
|
200
|
+
# }
|
201
|
+
# should be_true
|
202
|
+
# end
|
203
|
+
|
118
204
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -14,8 +14,9 @@ Mongoid.configure do |config|
|
|
14
14
|
name = "trackoid_test"
|
15
15
|
host = "localhost"
|
16
16
|
port = "27017"
|
17
|
-
|
17
|
+
# config.master = Mongo::Connection.new(host, port, :logger => Logger.new(STDOUT)).db(name)
|
18
18
|
config.master = Mongo::Connection.new.db(name)
|
19
|
+
config.use_object_ids = true
|
19
20
|
end
|
20
21
|
|
21
22
|
Spec::Runner.configure do |config|
|
data/spec/trackoid_spec.rb
CHANGED
@@ -49,7 +49,13 @@ describe Mongoid::Tracking do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
it "should create a method for accesing the stats" do
|
52
|
-
@mock.respond_to?(:visits).should
|
52
|
+
@mock.respond_to?(:visits).should be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should respond 'false' to field_changed? method" do
|
56
|
+
# Ok, this test is not very relevant since it will return false even
|
57
|
+
# if Trackid does not override it.
|
58
|
+
@mock.visits_changed?.should be_false
|
53
59
|
end
|
54
60
|
|
55
61
|
it "should create a method for accesing the stats of the proper class" do
|
@@ -57,14 +63,14 @@ describe Mongoid::Tracking do
|
|
57
63
|
end
|
58
64
|
|
59
65
|
it "should create an array in the class with all tracking fields" do
|
60
|
-
@mock.class.tracked_fields.should == [ :
|
66
|
+
@mock.class.tracked_fields.should == [ :visits ]
|
61
67
|
end
|
62
68
|
|
63
69
|
it "should create an array in the class with all tracking fields even when monkey patching" do
|
64
70
|
class Test
|
65
71
|
track :something_else
|
66
72
|
end
|
67
|
-
@mock.class.tracked_fields.should == [ :
|
73
|
+
@mock.class.tracked_fields.should == [ :visits, :something_else ]
|
68
74
|
end
|
69
75
|
|
70
76
|
it "should not update stats when new record" do
|
@@ -211,8 +217,10 @@ describe Mongoid::Tracking do
|
|
211
217
|
end
|
212
218
|
tm = TestModel.first
|
213
219
|
tm.something.today.should == 0
|
220
|
+
tm.something.inc
|
221
|
+
tm.something.today.should == 1
|
214
222
|
end
|
215
|
-
|
223
|
+
|
216
224
|
end
|
217
225
|
|
218
226
|
end
|
data/trackoid.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{trackoid}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jose Miguel Perez"]
|
12
|
-
s.date = %q{2010-06-
|
12
|
+
s.date = %q{2010-06-06}
|
13
13
|
s.description = %q{Trackoid uses an embeddable approach to track analytics data using the poweful features of MongoDB for scalability}
|
14
14
|
s.email = %q{josemiguel@perezruiz.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
"lib/trackoid/aggregates.rb",
|
28
28
|
"lib/trackoid/errors.rb",
|
29
29
|
"lib/trackoid/tracker.rb",
|
30
|
+
"lib/trackoid/tracker_aggregates.rb",
|
30
31
|
"lib/trackoid/tracking.rb",
|
31
32
|
"spec/aggregates_spec.rb",
|
32
33
|
"spec/spec.opts",
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trackoid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jose Miguel Perez
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-06-
|
18
|
+
date: 2010-06-06 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -54,6 +54,7 @@ files:
|
|
54
54
|
- lib/trackoid/aggregates.rb
|
55
55
|
- lib/trackoid/errors.rb
|
56
56
|
- lib/trackoid/tracker.rb
|
57
|
+
- lib/trackoid/tracker_aggregates.rb
|
57
58
|
- lib/trackoid/tracking.rb
|
58
59
|
- spec/aggregates_spec.rb
|
59
60
|
- spec/spec.opts
|