multirow_counter 0.0.2 → 0.0.3
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/generators/templates/multirow_counter_migration.rb +8 -19
- data/lib/multirow_counter.rb +0 -1
- data/lib/multirow_counter/extension.rb +36 -7
- data/test/multirow_counter_test.rb +18 -6
- metadata +74 -21
- metadata.gz.sig +3 -0
- data/lib/multirow_counter/counter_model_creator.rb +0 -9
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 250181796c25350c1166b39ab8d0ff56f0f4ed2b
|
4
|
+
data.tar.gz: 878c3644f5bccacb41e4c006f1ab80613284ae69
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ddbbb2213381c12d5251289070ddb23b4981b7dfc60d2c5fcc2ee62df5d632309ed27aa0c6b6d226c945546d144c8bbe57206c593ddbfb114cf64e96e7ecabc8
|
7
|
+
data.tar.gz: f4cf08244e0f71a4ae5fd0c5bbf7c2677353e822dc77ff939cb19ed3aa59c697a31d86086313427732f93e1367b442d639eb9ffe3bbab080553fdd781b174d94
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
Binary file
|
@@ -1,25 +1,14 @@
|
|
1
1
|
class Add<%= counter_name.classify %>CounterTo<%= model_name.classify %> < ActiveRecord::Migration
|
2
2
|
|
3
3
|
def self.up
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# You may want to consider moving this into a background task if it takes too long
|
14
|
-
<%= model_name.classify %>.find_each do |<%= model_name %>|
|
15
|
-
1.upto(<%= number_of_counter_rows %>) do |num|
|
16
|
-
MultirowCounter::<%= model_name.classify %><%= counter_name.classify %>.create! do |row|
|
17
|
-
row.<%= model_name %>_id = <%= model_name %>.id
|
18
|
-
row.counter_id = num
|
19
|
-
row.value = 0
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
4
|
+
execute <<-SQL
|
5
|
+
CREATE TABLE <%= [model_name, counter_name].join('_').tableize %> (
|
6
|
+
<%= model_name %>_id INT NOT NULL,
|
7
|
+
counter_id TINYINT NOT NULL,
|
8
|
+
value INT NOT NULL,
|
9
|
+
PRIMARY KEY (<%= model_name %>_id, counter_id)
|
10
|
+
)
|
11
|
+
SQL
|
23
12
|
end
|
24
13
|
|
25
14
|
def self.down
|
data/lib/multirow_counter.rb
CHANGED
@@ -4,25 +4,54 @@ module MultirowCounter
|
|
4
4
|
num_rows = options[:rows] || raise(ArgumentError, "You need to specify how many rows will be used eg. :rows => 3")
|
5
5
|
class_name = self.name
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
assoc = class_name.foreign_key
|
8
|
+
table_name = [class_name, counter_name.to_s.camelize].join.tableize
|
9
9
|
|
10
10
|
# define getter method
|
11
11
|
getter = lambda do
|
12
|
-
|
13
|
-
|
12
|
+
ivar_name = "@#{counter_name}"
|
13
|
+
if x = instance_variable_get(ivar_name)
|
14
|
+
return x
|
15
|
+
else
|
16
|
+
val = ActiveRecord::Base.connection.select_value <<-SQL
|
17
|
+
SELECT IFNULL(SUM(value),0) FROM #{table_name} WHERE #{assoc}=#{self.id}
|
18
|
+
SQL
|
19
|
+
instance_variable_set(ivar_name, val.to_i)
|
20
|
+
end
|
14
21
|
end
|
15
22
|
|
16
23
|
define_method(counter_name, &getter)
|
17
24
|
define_method("multirow_counter_#{counter_name}", &getter)
|
18
25
|
|
19
26
|
# define increment method
|
20
|
-
define_method("
|
21
|
-
|
27
|
+
define_method("new_#{counter_name}!") do |incr = 1|
|
28
|
+
ivar_name = "@#{counter_name}"
|
29
|
+
if x = instance_variable_get(ivar_name)
|
30
|
+
instance_variable_set(ivar_name, x + incr)
|
31
|
+
end
|
32
|
+
|
22
33
|
randomly_selected_counter_row = rand(num_rows) + 1
|
23
34
|
|
24
|
-
|
35
|
+
num_changed = ActiveRecord::Base.connection.update <<-SQL
|
36
|
+
UPDATE #{table_name} SET value=value+#{Integer(incr)}
|
37
|
+
WHERE #{assoc}=#{self.id} AND counter_id=#{randomly_selected_counter_row}
|
38
|
+
SQL
|
39
|
+
|
40
|
+
if num_changed == 0 # row doesn't exist. We create it instead.
|
41
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
42
|
+
INSERT INTO #{table_name} (#{assoc}, counter_id, value)
|
43
|
+
VALUES (#{self.id}, #{randomly_selected_counter_row}, #{Integer(incr)})
|
44
|
+
ON DUPLICATE KEY UPDATE value=value+#{Integer(incr)};
|
45
|
+
SQL
|
46
|
+
end
|
25
47
|
end
|
48
|
+
|
49
|
+
define_method("reload_with_#{counter_name}_reset") do
|
50
|
+
instance_variable_set("@#{counter_name}", nil)
|
51
|
+
send("reload_without_#{counter_name}_reset")
|
52
|
+
end
|
53
|
+
|
54
|
+
alias_method_chain :reload, "#{counter_name}_reset".to_sym
|
26
55
|
end
|
27
56
|
end
|
28
57
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
describe MultirowCounter do
|
4
|
+
include MultirowCounterTables
|
5
|
+
|
4
6
|
before do
|
5
7
|
reset_tables
|
6
8
|
end
|
@@ -14,30 +16,30 @@ describe MultirowCounter do
|
|
14
16
|
end
|
15
17
|
|
16
18
|
it "should allow incrementing" do
|
17
|
-
@shop.
|
19
|
+
@shop.new_version!
|
18
20
|
assert_equal 1, @shop.version
|
19
21
|
end
|
20
22
|
|
21
23
|
it "supports many increments" do
|
22
|
-
10.times { @shop.
|
24
|
+
10.times { @shop.new_version! }
|
23
25
|
assert_equal 10, @shop.version
|
24
26
|
end
|
25
27
|
|
26
28
|
it "supports batch increments" do
|
27
|
-
@shop.
|
29
|
+
@shop.new_version!(10)
|
28
30
|
assert_equal 10, @shop.version
|
29
31
|
end
|
30
32
|
|
31
33
|
it "should not send all increments to the same row" do
|
32
|
-
10.times { @shop.
|
34
|
+
10.times { @shop.new_version! }
|
33
35
|
|
34
|
-
shop_versions =
|
36
|
+
shop_versions = ShopVersion.where(:shop_id => @shop.id)
|
35
37
|
refute shop_versions.any? { |v| v.value == 10 }
|
36
38
|
end
|
37
39
|
|
38
40
|
it "requires the number of rows to be specified" do
|
39
41
|
class Foo < ActiveRecord::Base
|
40
|
-
lambda {
|
42
|
+
lambda {
|
41
43
|
multirow_counter :the_count, :random => 'oops'
|
42
44
|
}.must_raise ArgumentError
|
43
45
|
end
|
@@ -46,5 +48,15 @@ describe MultirowCounter do
|
|
46
48
|
it "defines a second obscure getter" do
|
47
49
|
@shop.multirow_counter_version.must_equal @shop.version
|
48
50
|
end
|
51
|
+
|
52
|
+
it "resets the memoized value when reloading" do
|
53
|
+
@shop.new_version!
|
54
|
+
@other_instance = Shop.find(@shop.id)
|
55
|
+
@other_instance.new_version!
|
56
|
+
|
57
|
+
@shop.reload
|
58
|
+
|
59
|
+
assert_equal @shop.version, @other_instance.version
|
60
|
+
end
|
49
61
|
end
|
50
62
|
|
metadata
CHANGED
@@ -1,38 +1,93 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multirow_counter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.3
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jesse Storimer
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
|
-
cert_chain:
|
12
|
-
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQ8wDQYDVQQDDAZhZG1p
|
14
|
+
bnMxFzAVBgoJkiaJk/IsZAEZFgdzaG9waWZ5MRMwEQYKCZImiZPyLGQBGRYDY29t
|
15
|
+
MB4XDTE0MDUxNTIwMzM0OFoXDTE1MDUxNTIwMzM0OFowPzEPMA0GA1UEAwwGYWRt
|
16
|
+
aW5zMRcwFQYKCZImiZPyLGQBGRYHc2hvcGlmeTETMBEGCgmSJomT8ixkARkWA2Nv
|
17
|
+
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL0/81O3e1vh5smcwp2G
|
18
|
+
MpLQ6q0kejQLa65bPYPxdzWA1SYOKyGfw+yR9LdFzsuKpwWzKq6zX35lj1IckWS4
|
19
|
+
bNBEQzxmufUxU0XPM02haFB8fOfDJzdXsWte9Ge4IFwahwn68gpMqN+BvxL+KMYz
|
20
|
+
Iut9YmN44d4LZdsENEIO5vmybuG2vYDz7R56qB0PA+Q2P2CdhymsBad2DQs69FBo
|
21
|
+
uico9V6VMYYctL9lCYdzu9IXrOYNTt88suKIVzzAlHOKeN0Ng5qdztFoTR8sfxDr
|
22
|
+
Ydg3KHl5n47wlpgd8R0f/4b5gGxW+v9pyJCgQnLlRu7DedVSvv7+GMtj3g9r3nhJ
|
23
|
+
KqECAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFI/o
|
24
|
+
maf34HXbUOQsdoLHacEKQgunMB0GA1UdEQQWMBSBEmFkbWluc0BzaG9waWZ5LmNv
|
25
|
+
bTAdBgNVHRIEFjAUgRJhZG1pbnNAc2hvcGlmeS5jb20wDQYJKoZIhvcNAQEFBQAD
|
26
|
+
ggEBADkK9aj5T0HPExsov4EoMWFnO+G7RQ28C30VAfKxnL2UxG6i4XMHVs6Xi94h
|
27
|
+
qXFw1ec9Y2eDUqaolT3bviOk9BB197+A8Vz/k7MC6ci2NE+yDDB7HAC8zU6LAx8Y
|
28
|
+
Iqvw7B/PSZ/pz4bUVFlTATif4mi1vO3lidRkdHRtM7UePSn2rUpOi0gtXBP3bLu5
|
29
|
+
YjHJN7wx5cugMEyroKITG5gL0Nxtu21qtOlHX4Hc4KdE2JqzCPOsS4zsZGhgwhPs
|
30
|
+
fl3hbtVFTqbOlwL9vy1fudXcolIE/ZTcxQ+er07ZFZdKCXayR9PPs64heamfn0fp
|
31
|
+
TConQSX2BnZdhIEYW+cKzEC/bLc=
|
32
|
+
-----END CERTIFICATE-----
|
33
|
+
date: 2014-06-23 00:00:00.000000000 Z
|
13
34
|
dependencies:
|
14
35
|
- !ruby/object:Gem::Dependency
|
15
36
|
name: activerecord
|
16
|
-
requirement:
|
17
|
-
none: false
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
18
38
|
requirements:
|
19
|
-
- -
|
39
|
+
- - ">="
|
20
40
|
- !ruby/object:Gem::Version
|
21
41
|
version: '0'
|
22
42
|
type: :runtime
|
23
43
|
prerelease: false
|
24
|
-
version_requirements:
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
25
49
|
- !ruby/object:Gem::Dependency
|
26
50
|
name: mysql2
|
27
|
-
requirement:
|
28
|
-
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rake
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
73
|
requirements:
|
30
|
-
- -
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: minitest
|
79
|
+
requirement: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
31
82
|
- !ruby/object:Gem::Version
|
32
83
|
version: '0'
|
33
84
|
type: :development
|
34
85
|
prerelease: false
|
35
|
-
version_requirements:
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
36
91
|
description: Typically SQL is not a great place to store a counter that is incremented
|
37
92
|
often. For instance if you're counting the number of visits to a page by incrementing
|
38
93
|
a SQL column and that page gets popular then there's a good chance that the SQL
|
@@ -47,34 +102,32 @@ extra_rdoc_files: []
|
|
47
102
|
files:
|
48
103
|
- lib/generators/multirow_counter_generator.rb
|
49
104
|
- lib/generators/templates/multirow_counter_migration.rb
|
50
|
-
- lib/multirow_counter
|
105
|
+
- lib/multirow_counter.rb
|
51
106
|
- lib/multirow_counter/extension.rb
|
52
107
|
- lib/multirow_counter/railtie.rb
|
53
|
-
- lib/multirow_counter.rb
|
54
108
|
- test/multirow_counter_test.rb
|
55
|
-
homepage: http://github.com/Shopify/
|
109
|
+
homepage: http://github.com/Shopify/multirow_counter
|
56
110
|
licenses: []
|
111
|
+
metadata: {}
|
57
112
|
post_install_message:
|
58
113
|
rdoc_options: []
|
59
114
|
require_paths:
|
60
115
|
- lib
|
61
116
|
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
-
none: false
|
63
117
|
requirements:
|
64
|
-
- -
|
118
|
+
- - ">="
|
65
119
|
- !ruby/object:Gem::Version
|
66
120
|
version: '0'
|
67
121
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
-
none: false
|
69
122
|
requirements:
|
70
|
-
- -
|
123
|
+
- - ">="
|
71
124
|
- !ruby/object:Gem::Version
|
72
125
|
version: '0'
|
73
126
|
requirements: []
|
74
127
|
rubyforge_project:
|
75
|
-
rubygems_version:
|
128
|
+
rubygems_version: 2.2.2
|
76
129
|
signing_key:
|
77
|
-
specification_version:
|
130
|
+
specification_version: 4
|
78
131
|
summary: Encapsulates a multi-row counter for SQL.
|
79
132
|
test_files:
|
80
133
|
- test/multirow_counter_test.rb
|
metadata.gz.sig
ADDED