diff --git a/lib/trackoid/tracker.rb b/lib/trackoid/tracker.rb index 7e19cd9..e3fd7d9 100644 --- a/lib/trackoid/tracker.rb +++ b/lib/trackoid/tracker.rb @@ -76,8 +76,7 @@ def set(how_much, date = Date.today) raise Errors::ModelNotSaved, "Can't update a new record" if @owner.new_record? update_data(how_much, date) @owner.collection.update( - @owner._selector, - { "$set" => update_hash(how_much, date) }, + @owner._selector, { "$set" => update_hash(how_much, date) }, :upsert => true ) return unless @owner.aggregated? @@ -87,13 +86,55 @@ def set(how_much, date = Date.today) fk = @owner.class.name.to_s.foreign_key.to_sym selector = { fk => @owner.id, :ns => k, :key => token.to_s } @owner.aggregate_klass.collection.update( - selector, - { "$set" => update_hash(how_much, date) }, + selector, { "$set" => update_hash(how_much, date) }, :upsert => true ) end end + def reset(how_much, date = Date.today) + return erase(date) if how_much.nil? + + # First, we use the default "set" for the tracking field + # This will also update one aggregate but... oh well... + set(how_much, date) + + # Need to iterate over all aggregates and send an update or delete + # operations over all mongo records for this aggregate field + @owner.aggregate_fields.each do |(k,v)| + fk = @owner.class.name.to_s.foreign_key.to_sym + selector = { fk => @owner.id, :ns => k } + @owner.aggregate_klass.collection.update( + selector, { "$set" => update_hash(how_much, date) }, + :upsert => true, :multi => true + ) + end + end + + def erase(date = Date.today) + raise Errors::ModelNotSaved, "Can't update a new record" if @owner.new_record? + + # For the in memory data, we just need to set it to nil + update_data(nil, date) + @owner.collection.update( + @owner._selector, + { "$unset" => update_hash(1, date) }, + :upsert => true + ) + return unless @owner.aggregated? + + # Need to iterate over all aggregates and send an update or delete + # operations over all mongo records + @owner.aggregate_fields.each do |(k,v)| + fk = @owner.class.name.to_s.foreign_key.to_sym + selector = { fk => @owner.id, :ns => k } + @owner.aggregate_klass.collection.update( + selector, { "$unset" => update_hash(1, date) }, + :upsert => true, :multi => true + ) + end + end + private def data_for(date) return nil if date.nil? diff --git a/spec/aggregates_spec.rb b/spec/aggregates_spec.rb index 955908d..00bd652 100644 --- a/spec/aggregates_spec.rb +++ b/spec/aggregates_spec.rb @@ -158,7 +158,7 @@ class MockTest3 @object_id = SecondTestModel.first.id @mock = SecondTestModel.find(@object_id) end - + it "should correctly save all aggregation keys as strings (inc)" do @mock.something("test").inc @mock.something.aggregate_one.first.key.is_a?(String).should be_true @@ -174,7 +174,6 @@ class MockTest3 @mock.something.aggregate_three.first.key.is_a?(String).should be_true @mock.something.aggregate_four.first.key.is_a?(String).should be_true end - end describe "when tracking a model with aggregation data" do @@ -233,8 +232,8 @@ class MockTest3 it "should work adding 1 visit with different aggregation data" do @mock.visits("Google Chrome").inc - @mock.visits.browsers.today.should == [["mozilla", 1], ["google", 1]] - @mock.visits.referers.today.should == [["firefox", 1], ["chrome", 1]] + @mock.visits.browsers.today.should =~ [["mozilla", 1], ["google", 1]] + @mock.visits.referers.today.should =~ [["firefox", 1], ["chrome", 1]] # Just for testing array manipulations @mock.visits.browsers.today.inject(0) {|total, c| total + c.last }.should == 2 @@ -246,22 +245,22 @@ class MockTest3 it "should work also with set" do @mock.visits("Google Chrome").set(5) - @mock.visits.browsers.today.should == [["mozilla", 1], ["google", 5]] - @mock.visits.referers.today.should == [["firefox", 1], ["chrome", 5]] + @mock.visits.browsers.today.should =~ [["mozilla", 1], ["google", 5]] + @mock.visits.referers.today.should =~ [["firefox", 1], ["chrome", 5]] @mock.visits.today.should == 5 end it "let's check what happens when sorting the best browser..." do @mock.visits("Google Chrome").inc - @mock.visits.browsers.today.should == [["mozilla", 1], ["google", 6]] + @mock.visits.browsers.today.should =~ [["mozilla", 1], ["google", 6]] @mock.visits.browsers.today.max {|a,b| a.second <=> b.second }.should == ["google", 6] end it "should work without aggregation information" do @mock.visits.inc - @mock.visits.browsers.today.should == [["mozilla", 1], ["google", 6]] - @mock.visits.referers.today.should == [["firefox", 1], ["chrome", 6]] - + @mock.visits.browsers.today.should =~ [["mozilla", 1], ["google", 6]] + @mock.visits.referers.today.should =~ [["firefox", 1], ["chrome", 6]] + # A more throughout test would check totals... visits_today = @mock.visits.today visits_today_with_browser = @mock.visits.browsers.today.inject(0) {|total, c| total + c.last } @@ -269,6 +268,107 @@ class MockTest3 end end + describe "When using reset method for aggregates" do + before do + TestModel.all.map(&:destroy) + TestModel.create(:name => "test") + + @object_id = TestModel.first.id + @mock = TestModel.first + + @mock.visits("Mozilla Firefox").set(1, "2010-07-11") + @mock.visits("Google Chrome").set(2, "2010-07-11") + @mock.visits("Internet Explorer").set(3, "2010-07-11") + + @mock.visits("Mozilla Firefox").set(4, "2010-07-14") + @mock.visits("Google Chrome").set(5, "2010-07-14") + @mock.visits("Internet Explorer").set(6, "2010-07-14") + + @mock.uniques("Mozilla Firefox").set(1, "2010-07-11") + @mock.uniques("Google Chrome").set(2, "2010-07-11") + @mock.uniques("Internet Explorer").set(3, "2010-07-11") + + @mock.uniques("Mozilla Firefox").set(4, "2010-07-14") + @mock.uniques("Google Chrome").set(5, "2010-07-14") + @mock.uniques("Internet Explorer").set(6, "2010-07-14") + end + + it "should have the correct values when using a value" do + @mock.visits.reset(99, "2010-07-14") + + @mock.visits.on("2010-07-14").should == 99 + @mock.visits.browsers.all_values.should =~ [ + ["mozilla", [1, 0, 0, 99]], + ["google", [2, 0, 0, 99]], + ["internet", [3, 0, 0, 99]] + ] + @mock.visits.referers.all_values.should =~ [ + ["firefox", [1, 0, 0, 99]], + ["chrome", [2, 0, 0, 99]], + ["explorer", [3, 0, 0, 99]] + ] + end + + it "should delete the values when using nil" do + @mock.visits.reset(nil, "2010-07-14") + + @mock.visits.on("2010-07-14").should == 0 + @mock.visits.browsers.all_values.should =~ [ + ["mozilla", [1]], + ["google", [2]], + ["internet", [3]] + ] + @mock.visits.referers.all_values.should =~ [ + ["firefox", [1]], + ["chrome", [2]], + ["explorer", [3]] + ] + end + + it "erase method sould also work" do + @mock.visits.erase("2010-07-14") + + @mock.visits.on("2010-07-14").should == 0 + @mock.visits.browsers.all_values.should =~ [ + ["mozilla", [1]], + ["google", [2]], + ["internet", [3]] + ] + end + + it "should reset the correct tracking fields" do + @mock.visits.reset(99, "2010-07-14") + + @mock.uniques.on("2010-07-14").should == 6 + @mock.uniques.browsers.all_values.should =~ [ + ["mozilla", [1, 0, 0, 4]], + ["google", [2, 0, 0, 5]], + ["internet", [3, 0, 0, 6]] + ] + @mock.uniques.referers.all_values.should =~ [ + ["firefox", [1, 0, 0, 4]], + ["chrome", [2, 0, 0, 5]], + ["explorer", [3, 0, 0, 6]] + ] + end + + it "should erase the correct tracking fields" do + @mock.visits.erase("2010-07-14") + + @mock.uniques.on("2010-07-14").should == 6 + @mock.uniques.browsers.all_values.should =~ [ + ["mozilla", [1, 0, 0, 4]], + ["google", [2, 0, 0, 5]], + ["internet", [3, 0, 0, 6]] + ] + @mock.uniques.referers.all_values.should =~ [ + ["firefox", [1, 0, 0, 4]], + ["chrome", [2, 0, 0, 5]], + ["explorer", [3, 0, 0, 6]] + ] + end + end + describe "Testing all accessors" do before do TestModel.all.map(&:destroy) @@ -280,7 +380,7 @@ class MockTest3 @mock.visits("Mozilla Firefox").set(1, "2010-07-11") @mock.visits("Google Chrome").set(2, "2010-07-12") @mock.visits("Internet Explorer").set(3, "2010-07-13") - + # For 'last' values @mock.visits("Mozilla Firefox").set(4, "2010-07-14") @mock.visits("Google Chrome").set(5, "2010-07-15") @@ -290,17 +390,17 @@ class MockTest3 it "should return the correct values for .all_values" do @mock.visits.all_values.should == [1, 2, 3, 4, 5, 6] end - + it "should return the all values for every aggregate" do - @mock.visits.browsers.all_values.should == [ + @mock.visits.browsers.all_values.should =~ [ ["mozilla", [1, 0, 0, 4]], ["google", [2, 0, 0, 5]], ["internet", [3, 0, 0, 6]] ] end - + it "should return the correct first_date for every aggregate" do - @mock.visits.browsers.first_date.should == [ + @mock.visits.browsers.first_date.should =~ [ ["mozilla", Date.parse("2010-07-11")], ["google", Date.parse("2010-07-12")], ["internet", Date.parse("2010-07-13")] @@ -308,7 +408,7 @@ class MockTest3 end it "should return the correct last_date for every aggregate" do - @mock.visits.browsers.last_date.should == [ + @mock.visits.browsers.last_date.should =~ [ ["mozilla", Date.parse("2010-07-14")], ["google", Date.parse("2010-07-15")], ["internet", Date.parse("2010-07-16")] @@ -316,7 +416,7 @@ class MockTest3 end it "should return the first value for aggregates" do - @mock.visits.browsers.first_value.should == [ + @mock.visits.browsers.first_value.should =~ [ ["mozilla", 1], ["google", 2], ["internet", 3] @@ -324,12 +424,11 @@ class MockTest3 end it "should return the last value for aggregates" do - @mock.visits.browsers.last_value.should == [ + @mock.visits.browsers.last_value.should =~ [ ["mozilla", 4], ["google", 5], ["internet", 6] ] end - end end