counter_cachier 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in counter_cachier.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ counter-cachier
2
+ ===============
3
+
4
+ usage
5
+ -------------
6
+
7
+ counter cachier is a generic counter caching solution (using redis) for stuff where ActiveRecord's counter cache just does not cut it. i.e. when the counter is on a complex association, etc.
8
+
9
+ usage is pretty simple, simply include CounterCachier in the class you wish to use it on, and define counter cachiers:
10
+
11
+ ```ruby
12
+ class User
13
+ include CounterCachier
14
+
15
+ counter_cachier :approved_posts_count do |user|
16
+ user.posts.approved.count
17
+ end
18
+ end
19
+ ```
20
+
21
+ the block's argument is the object you're making the calculations for (i.e. an instance of User), the value returned in the block will be the new counter. From this moment, two new methods are added to User - approved_posts_count and recalc_approved_posts_count.
22
+
23
+ ```ruby
24
+ user = User.first
25
+ user.approved_posts_count #=> 10
26
+ #...user adds an approved post...
27
+ user.recalc_approved_posts_count #=> 11, but it actually recalculates and pushes the number into redis.
28
+ ```
29
+
30
+ installation
31
+ -------------
32
+
33
+ in your gemfile
34
+ gem "counter_cachier"
35
+
36
+ in an initializer:
37
+ ```ruby
38
+ CounterCachier.redis = Redis.new(redis_configuration)
39
+ ```
40
+ you can skip this if you've got the $redis global.
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/counter_cachier/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Tom Caspy"]
6
+ gem.email = ["tcaspy@gmail.com"]
7
+ gem.description = %q{Counter cache on steroids - requires a bit of configuration, but then simply saves counter data.}
8
+ gem.summary = %q{simply set the counter's name and write a block for recalculation of that cache, and you're done}
9
+ gem.homepage = "https://github.com/unorthodoxgeek/counter-cachier"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "counter_cachier"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = CounterCachier::VERSION
17
+
18
+ gem.add_development_dependency(%q<rspec>, [">= 0"])
19
+ gem.add_development_dependency(%q<rake>, ["~> 0.9.2"])
20
+ gem.add_dependency(%q<redis>, [">= 0"])
21
+ end
@@ -0,0 +1,20 @@
1
+ module CounterCachier
2
+ class Cachier
3
+ attr_accessor :name
4
+
5
+ def initialize(name, &block)
6
+ @name = name
7
+ @block = block
8
+ end
9
+
10
+ def recalc(object)
11
+ @block.call(object)
12
+ end
13
+
14
+ def write(object)
15
+ new_value = recalc(object)
16
+ CounterCachier.write(object, name, new_value)
17
+ new_value
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,45 @@
1
+ module CounterCachier
2
+ class << self
3
+
4
+ def redis=(r)
5
+ @redis = r
6
+ end
7
+
8
+ def redis
9
+ @redis ||= $redis
10
+ end
11
+
12
+ def key(object, name)
13
+ "counter_cachier::#{object.class.to_s}::#{object.id}::#{name}"
14
+ end
15
+
16
+ def read(object, cachier)
17
+ value = redis.get(key(object, cachier.name))
18
+ if value.nil?
19
+ value = cachier.write(object)
20
+ end
21
+ value
22
+ end
23
+
24
+ def write(object, name, value)
25
+ redis.set key(object, name), value
26
+ end
27
+ end
28
+
29
+ def self.included(base)
30
+ base.extend ClassMethods
31
+ class << base
32
+ attr_accessor :cachiers
33
+ end
34
+ end
35
+
36
+ module ClassMethods
37
+ def counter_cachier(name, options = {}, &block)
38
+ self.cachiers ||= {}
39
+ self.cachiers[name] = Mounter.new(self, name, &block)
40
+ if options[:async]
41
+ later "recalc_#{name}", queue: options[:queue] || :long
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,27 @@
1
+ module CounterCachier
2
+ class Mounter
3
+
4
+ attr_accessor :cachier
5
+
6
+ def initialize(klass, name, &block)
7
+ @klass = klass
8
+ @name = name
9
+ self.cachier = CounterCachier::Cachier.new(name, &block)
10
+ define_methods
11
+ end
12
+
13
+ def define_methods
14
+ #these local variables are set so they can be used inside the define_method blocks
15
+ name = @name
16
+ cachier = @cachier
17
+
18
+ @klass.send :define_method, @name do
19
+ CounterCachier.read(self, name)
20
+ end
21
+
22
+ @klass.send :define_method, "recalc_#{@name}" do
23
+ cachier.write(self)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module CounterCachier
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "counter_cachier/counter_cachier"
2
+ require "counter_cachier/cachier"
3
+ require "counter_cachier/mounter"
4
+ require "counter_cachier/version"
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe CounterCachier::Cachier do
4
+
5
+ describe :cachier do
6
+
7
+ let :cachier do
8
+ CounterCachier::Cachier.new(:foo){ |bar| 10 * bar}
9
+ end
10
+
11
+ it "should save a block for recalculation" do
12
+ cachier.recalc(4).should == 40
13
+ end
14
+
15
+ it "should write the counter cache to the store" do
16
+ CounterCachier.should_receive(:write)
17
+ cachier.write(4)
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+ Redis ||= Class.new
3
+
4
+ describe CounterCachier do
5
+ let(:a){double(id: 1, class: "Foo")}
6
+ let(:b){double(id: 2, class: "Bar")}
7
+ let(:cachier){double(name: "foo")}
8
+
9
+ before :each do
10
+ CounterCachier.redis = double
11
+ end
12
+
13
+ describe :write do
14
+ it "should write the new counter cache" do
15
+ CounterCachier.redis.should_receive(:set)
16
+ CounterCachier.write(a, :b, 4)
17
+ end
18
+ end
19
+
20
+ describe :read do
21
+ it "should read the counter cache" do
22
+ CounterCachier.redis.should_receive(:get).and_return(1)
23
+ CounterCachier.read(a, cachier)
24
+ end
25
+
26
+ it "should recalc the counter cache if the read yields nil" do
27
+ CounterCachier.redis.should_receive(:get)
28
+ cachier.should_receive(:write).and_return(5)
29
+ CounterCachier.read(a, cachier).should == 5
30
+ end
31
+ end
32
+
33
+ describe :key do
34
+ it "should return a string" do
35
+ CounterCachier.key(a, :a).should be_a String
36
+ end
37
+
38
+ it "should return a valid unique key for each object" do
39
+ CounterCachier.key(a, :a).should_not == CounterCachier.key(b, :a)
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,47 @@
1
+ require "spec_helper"
2
+ class Dummy
3
+ include CounterCachier
4
+ def id
5
+ 4
6
+ end
7
+
8
+ def baz
9
+ 10
10
+ end
11
+
12
+ counter_cachier :foo do |foo|
13
+ foo.baz
14
+ end
15
+ end
16
+
17
+ describe CounterCachier::Mounter do
18
+ let(:dummy) {Dummy.new}
19
+ describe "class methods" do
20
+ it "should have cachiers accessor" do
21
+ Dummy.should respond_to :cachiers
22
+ end
23
+
24
+ it "should call later if later specified" do
25
+ Dummy.should_receive(:later).with("recalc_bar", queue: :bar)
26
+
27
+ Dummy.counter_cachier :bar, async: true, queue: :bar do
28
+ 10
29
+ end
30
+ end
31
+ end
32
+
33
+ describe "instance methods" do
34
+ it "should respond to foo" do
35
+ dummy.should respond_to(:foo)
36
+ end
37
+
38
+ it "should respond to recalc_foo" do
39
+ dummy.should respond_to(:recalc_foo)
40
+ end
41
+
42
+ it "should run the block when calling recalc_foo" do
43
+ CounterCachier.redis.should_receive :set
44
+ dummy.recalc_foo.should == 10
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require "counter_cachier"
3
+
4
+ RSpec.configure do |config|
5
+ # config.mock_with :mocha
6
+ # config.mock_with :flexmock
7
+ # config.mock_with :rr
8
+ config.mock_with :rspec
9
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: counter_cachier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tom Caspy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ prerelease: false
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ none: false
23
+ type: :development
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ! '>='
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ none: false
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ prerelease: false
33
+ requirement: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.9.2
38
+ none: false
39
+ type: :development
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 0.9.2
45
+ none: false
46
+ - !ruby/object:Gem::Dependency
47
+ name: redis
48
+ prerelease: false
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ none: false
55
+ type: :runtime
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ none: false
62
+ description: Counter cache on steroids - requires a bit of configuration, but then
63
+ simply saves counter data.
64
+ email:
65
+ - tcaspy@gmail.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - README.md
73
+ - counter_cachier.gemspec
74
+ - lib/counter_cachier.rb
75
+ - lib/counter_cachier/cachier.rb
76
+ - lib/counter_cachier/counter_cachier.rb
77
+ - lib/counter_cachier/mounter.rb
78
+ - lib/counter_cachier/version.rb
79
+ - spec/lib/cachier_spec.rb
80
+ - spec/lib/counter_cachier_spec.rb
81
+ - spec/lib/mounter_spec.rb
82
+ - spec/spec_helper.rb
83
+ homepage: https://github.com/unorthodoxgeek/counter-cachier
84
+ licenses: []
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ none: false
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ none: false
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 1.8.24
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: simply set the counter's name and write a block for recalculation of that
107
+ cache, and you're done
108
+ test_files:
109
+ - spec/lib/cachier_spec.rb
110
+ - spec/lib/counter_cachier_spec.rb
111
+ - spec/lib/mounter_spec.rb
112
+ - spec/spec_helper.rb