visit-counter 0.1.1 → 0.1.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/README.md +2 -0
- data/Rakefile +8 -0
- data/lib/visit-counter/store/redis_store.rb +3 -3
- data/lib/visit-counter/version.rb +1 -1
- data/lib/visit-counter/visit_counter.rb +7 -2
- data/spec/lib/visit_counter_spec.rb +18 -4
- metadata +13 -13
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# VisitCounter
|
2
2
|
|
3
|
+
[](https://secure.travis-ci.org/KensoDev/visit-counter)
|
4
|
+
|
3
5
|
VisitCounter is a gem which solves the annoying problem of counting visits and displaying them in real time. In an SQL database, for a site with a lot of hits, this can cause quite a lot of overhead. VisitCounter aims to solve this by using a quick key-value store to keep a delta, and only persist to the SQL DB when the delta crosses a certain percent of the saved counter.
|
4
6
|
It can be used transparently, by overriding the accessor to the counter, or simply by using the helper functions it defines - incr_counter, read_counter, get_counter_delta and nullify_counter.
|
5
7
|
|
data/Rakefile
CHANGED
@@ -38,7 +38,7 @@ module VisitCounter
|
|
38
38
|
redis.decrby(key, by)
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
41
|
+
def acquire_lock(object)
|
42
42
|
redis.setnx(lock_key(object), 1)
|
43
43
|
end
|
44
44
|
|
@@ -51,7 +51,7 @@ module VisitCounter
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def with_lock(object, &block)
|
54
|
-
if
|
54
|
+
if acquire_lock(object)
|
55
55
|
begin
|
56
56
|
yield
|
57
57
|
ensure
|
@@ -63,4 +63,4 @@ module VisitCounter
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
66
|
-
end
|
66
|
+
end
|
@@ -4,7 +4,7 @@ module VisitCounter
|
|
4
4
|
base.class_eval do
|
5
5
|
class << self
|
6
6
|
#defining class instance attributes
|
7
|
-
attr_accessor :visit_counter_threshold, :visit_counter_threshold_method
|
7
|
+
attr_accessor :visit_counter_threshold, :visit_counter_threshold_method, :persist_with_callbacks
|
8
8
|
end
|
9
9
|
end
|
10
10
|
base.send(:include, InstanceMethods)
|
@@ -73,7 +73,12 @@ module VisitCounter
|
|
73
73
|
|
74
74
|
def persist(object, staged_count, diff, name)
|
75
75
|
VisitCounter::Store.engine.with_lock(object) do
|
76
|
-
object.
|
76
|
+
if object.class.persist_with_callbacks
|
77
|
+
object.send("#{name}=", staged_count + diff )
|
78
|
+
object.save
|
79
|
+
else
|
80
|
+
object.update_attribute(name, staged_count + diff)
|
81
|
+
end
|
77
82
|
object.nullify_counter_cache(name, diff)
|
78
83
|
end
|
79
84
|
end
|
@@ -3,12 +3,16 @@ require "spec_helper"
|
|
3
3
|
class DummyObject
|
4
4
|
include VisitCounter
|
5
5
|
|
6
|
-
attr_accessor :counter
|
6
|
+
attr_accessor :counter, :persist_with_callbacks
|
7
7
|
|
8
8
|
def update_attribute(attribute, value)
|
9
9
|
self.send("#{attribute}=", value)
|
10
10
|
end
|
11
11
|
|
12
|
+
def save
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
12
16
|
def read_attribute(name)
|
13
17
|
#yeah, evals are evil, but it works and it's for testing purposes only. we assume read_attribute is defined the same as in AR wherever we include this module
|
14
18
|
eval("@#{name}")
|
@@ -124,13 +128,13 @@ describe VisitCounter do
|
|
124
128
|
end
|
125
129
|
|
126
130
|
it "should not persist if object is locked" do
|
127
|
-
VisitCounter::Store.engine.stub!(:
|
131
|
+
VisitCounter::Store.engine.stub!(:acquire_lock).and_return(false)
|
128
132
|
@d.should_not_receive(:update_attribute)
|
129
133
|
VisitCounter::Helper.persist(@d, 1, 1, :counter)
|
130
134
|
end
|
131
135
|
|
132
136
|
it "should persist if object is locked" do
|
133
|
-
VisitCounter::Store.engine.stub!(:
|
137
|
+
VisitCounter::Store.engine.stub!(:acquire_lock).and_return(true)
|
134
138
|
@d.should_receive(:update_attribute)
|
135
139
|
VisitCounter::Helper.persist(@d, 1, 1, :counter)
|
136
140
|
end
|
@@ -157,5 +161,15 @@ describe VisitCounter do
|
|
157
161
|
@d.counter.should == 11
|
158
162
|
end
|
159
163
|
end
|
164
|
+
|
165
|
+
describe "persist with callbacks" do
|
166
|
+
it "should use save method" do
|
167
|
+
@d = DummyObject.new
|
168
|
+
@d.class.persist_with_callbacks = true
|
169
|
+
@d.stub!(:id).and_return(1)
|
170
|
+
@d.should_receive(:save)
|
171
|
+
@d.incr_counter :counter
|
172
|
+
end
|
173
|
+
end
|
160
174
|
|
161
|
-
end
|
175
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: visit-counter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,56 +9,56 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-04-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
+
prerelease: false
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '0'
|
22
|
+
none: false
|
22
23
|
type: :development
|
23
|
-
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
25
|
requirements:
|
27
26
|
- - ! '>='
|
28
27
|
- !ruby/object:Gem::Version
|
29
28
|
version: '0'
|
29
|
+
none: false
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: rake
|
32
|
+
prerelease: false
|
32
33
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
34
|
requirements:
|
35
35
|
- - ~>
|
36
36
|
- !ruby/object:Gem::Version
|
37
37
|
version: 0.9.2
|
38
|
+
none: false
|
38
39
|
type: :development
|
39
|
-
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
41
|
requirements:
|
43
42
|
- - ~>
|
44
43
|
- !ruby/object:Gem::Version
|
45
44
|
version: 0.9.2
|
45
|
+
none: false
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
name: redis
|
48
|
+
prerelease: false
|
48
49
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
50
|
requirements:
|
51
51
|
- - ! '>='
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '0'
|
54
|
+
none: false
|
54
55
|
type: :runtime
|
55
|
-
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
57
|
requirements:
|
59
58
|
- - ! '>='
|
60
59
|
- !ruby/object:Gem::Version
|
61
60
|
version: '0'
|
61
|
+
none: false
|
62
62
|
description: Simple counter increment which only writes to DB once in a while
|
63
63
|
email:
|
64
64
|
- tcaspy@gmail.com
|
@@ -90,17 +90,17 @@ rdoc_options: []
|
|
90
90
|
require_paths:
|
91
91
|
- lib
|
92
92
|
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
-
none: false
|
94
93
|
requirements:
|
95
94
|
- - ! '>='
|
96
95
|
- !ruby/object:Gem::Version
|
97
96
|
version: '0'
|
98
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
97
|
none: false
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
99
|
requirements:
|
101
100
|
- - ! '>='
|
102
101
|
- !ruby/object:Gem::Version
|
103
102
|
version: '0'
|
103
|
+
none: false
|
104
104
|
requirements: []
|
105
105
|
rubyforge_project:
|
106
106
|
rubygems_version: 1.8.24
|