coffee_table 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@coffee_table
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in coffee_table.gemspec
4
+ gemspec
5
+
6
+ gem "redis"
7
+ gem "awesome_print"
8
+
9
+ group :test do
10
+ gem "rspec"
11
+ gem "mock_redis"
12
+ gem "spork"
13
+ end
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "coffee_table/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "coffee_table"
7
+ s.version = CoffeeTable::VERSION
8
+ s.authors = ["Stewart McKee"]
9
+ s.email = ["stewart@theizone.co.uk"]
10
+ s.homepage = ""
11
+ s.summary = "Gem to manage cache stored in redis"
12
+ s.description = "rails cache gem to fragment cache with smart cache key management"
13
+
14
+ s.rubyforge_project = "coffee_table"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
@@ -0,0 +1,3 @@
1
+ module CoffeeTable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,128 @@
1
+ require "coffee_table/version"
2
+ require "utility"
3
+ require "redis"
4
+
5
+ module CoffeeTable
6
+ class Cache
7
+
8
+ include CoffeeTable::Utility
9
+
10
+ def initialize(options={})
11
+ @options = options
12
+
13
+ default_enable_cache_to true
14
+ default_redis_namespace_to :coffee_table
15
+ default_redis_server_to "127.0.0.1"
16
+ default_redis_port_to 6789
17
+
18
+ end
19
+
20
+ def get_cache(initial_key, *related_objects, &block)
21
+ setup_redis
22
+
23
+ # check objects are valid
24
+ related_objects.map{|o| raise "Objects passed in must have an id method" unless o.respond_to? "id"}
25
+
26
+ # if first related_object is integer or fixnum it is used as an expiry time for the cache object
27
+ # TODO change this to use hash and be at the end as convention
28
+ if related_objects.empty?
29
+ key = "#{initial_key}"
30
+ else
31
+ key = "#{initial_key}_#{related_objects.flatten.map{|o| "#{underscore(o.class.to_s)}[#{o.id}]"}.join("_")}"
32
+ end
33
+
34
+ if @options[:enable_cache]
35
+ # if first element is an integer or fixnum then use that as an expiry for this key
36
+ expiry = nil
37
+ if !related_objects.nil? and !related_objects.empty? and !related_objects.first.nil? and (related_objects.first.instance_of? Integer or related_objects.first.instance_of? Fixnum)
38
+ expiry = related_objects[0].to_i
39
+ related_objects = related_objects[1..-1]
40
+ end
41
+
42
+ @redis.sadd "cache_keys", key unless @redis.sismember "cache_keys", key
43
+ if @redis.exists(key)
44
+ result = marshal_value(@redis.get(key))
45
+ else
46
+ result = yield
47
+ # if its a relation, call all to get an array to cache the result
48
+ #if result.class == ActiveRecord::Relation
49
+ # @logger.debug "Expanding ActiveRecord::Relation..."
50
+ # result = result.all
51
+ #end
52
+ @redis.set key, Marshal.dump(result)
53
+ @redis.expire key expiry unless expiry.nil?
54
+ end
55
+ else
56
+ unless @options[:enable_cache] && block_given?
57
+ raise "cache is disabled and no block is given"
58
+ else
59
+ result = yield
60
+ end
61
+ end
62
+ result
63
+ end
64
+
65
+ def expire_key(key)
66
+ setup_redis
67
+ @redis.del(key)
68
+ @redis.srem "cache_keys", key
69
+ end
70
+
71
+ def expire_all
72
+ keys.map{|key| expire_key(key)}
73
+ end
74
+
75
+ def keys
76
+ setup_redis
77
+ @redis.smembers "cache_keys"
78
+ end
79
+
80
+ def expire_for(*objects)
81
+ perform_caching = Rails.application.configure do
82
+ config.action_controller.perform_caching
83
+ end
84
+
85
+ if perform_caching
86
+ deleted_keys = []
87
+ unless objects.empty?
88
+ self.keys.each do |key|
89
+ expire = true
90
+ objects.each do |object|
91
+ mod_key = "_#{key}_"
92
+ if object.class == String
93
+ unless mod_key.include?("_#{object}_") or mod_key.include?("_#{object.pluralize}_")
94
+ expire = false
95
+ end
96
+ else
97
+ object_type = object.class.to_s.underscore
98
+ unless mod_key.include?("_#{object_type.to_sym}[#{object.id}]_") or mod_key.include?("_#{object_type.pluralize}_")
99
+ expire = false
100
+ end
101
+ end
102
+ end
103
+ if expire
104
+ expire_key(key)
105
+ deleted_keys << key
106
+ end
107
+ end
108
+ end
109
+ deleted_keys
110
+ end
111
+ end
112
+
113
+ private
114
+ def marshal_value(value)
115
+ begin
116
+ result = Marshal.load(value)
117
+ rescue ArgumentError => e
118
+ puts "Attempting to load class/module #{e.message.split(" ")[-1]}"
119
+ e.message.split(" ")[-1].constantize
120
+ result = marshal_value(value)
121
+ end
122
+ result
123
+ end
124
+ def setup_redis
125
+ @redis = Redis.new #::Namespace.new(options[:redis_namespace], :redis => Redis.new({:server => @options[:redis_server], :port => @options[:redis_port]}))
126
+ end
127
+ end
128
+ end
data/lib/utility.rb ADDED
@@ -0,0 +1,23 @@
1
+ module CoffeeTable
2
+ module Utility
3
+
4
+
5
+ # used for setting default options
6
+ def method_missing(method_sym, *arguments, &block)
7
+ if method_sym.to_s =~ /^default_(.*)_to$/
8
+ tag_name = method_sym.to_s.split("_")[1..-2].join("_").to_sym
9
+ @options[tag_name] = arguments[0] unless @options.has_key?(tag_name)
10
+ else
11
+ super
12
+ end
13
+ end
14
+
15
+ def underscore(text)
16
+ text.gsub(/::/, '/').
17
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
18
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
19
+ tr("-", "_").
20
+ downcase
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,117 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe CoffeeTable do
4
+
5
+ before(:each) do
6
+ @coffee_table = CoffeeTable::Cache.new
7
+ end
8
+
9
+ specify { CoffeeTable::Cache.should respond_to :new}
10
+ specify { @coffee_table.should respond_to :get_cache}
11
+ specify { @coffee_table.should respond_to :expire_key}
12
+ specify { @coffee_table.should respond_to :expire_all}
13
+ specify { @coffee_table.should respond_to :keys}
14
+ specify { @coffee_table.should respond_to :expire_for}
15
+
16
+ describe "config" do
17
+ it "should take a hash for config" do
18
+ CoffeeTable::Cache.new({:test => "asdf"})
19
+ end
20
+ it "should not raise exception when hash not given" do
21
+ lambda{CoffeeTable::Cache.new}.should_not raise_exception
22
+ end
23
+ end
24
+
25
+ describe "get_cache" do
26
+ it "should raise an exception when block not given" do
27
+ lambda{@coffee_table.get_cache("asdf")}.should raise_exception "no block given (yield)"
28
+ end
29
+ it "should not raise an exception if cache value is available and no block given" do
30
+ result = @coffee_table.get_cache("test_key") do
31
+ "this is a valid result"
32
+ end
33
+
34
+ @coffee_table.get_cache("test_key").should == "this is a valid result"
35
+ end
36
+ it "should execute block when cache value not available" do
37
+ result = @coffee_table.get_cache("asdf") do
38
+ "this is a value"
39
+ end
40
+
41
+ result.should == "this is a value"
42
+ end
43
+ it "should return cached value when cache available" do
44
+ @coffee_table.get_cache("asdf") do
45
+ "this is a value"
46
+ end
47
+ result = @coffee_table.get_cache("asdf") do
48
+ "this is a changed value"
49
+ end
50
+
51
+ result.should == "this is a value"
52
+
53
+ end
54
+ it "should execute block when store not available" do
55
+ @coffee_table.get_cache(:test_key) do
56
+ TESTVAR = "testvar"
57
+ "this is a value"
58
+ end
59
+ @coffee_table.get_cache(:test_key) do
60
+ TESTVAR = "testvar2"
61
+ "this is a value"
62
+ end
63
+
64
+ TESTVAR.should == "testvar"
65
+
66
+ end
67
+ context "without related objects" do
68
+ it "should create a key with just the initial key" do
69
+ result = @coffee_table.get_cache(:test_key) do
70
+ "this is a changed value"
71
+ end
72
+
73
+ @coffee_table.keys.should include "test_key"
74
+
75
+ end
76
+ end
77
+ context "with related objects" do
78
+ it "should create a key from the id's of the related objects" do
79
+ test_object = SampleClass.new
80
+ result = @coffee_table.get_cache(:test_key, test_object) do
81
+ "this is a changed value"
82
+ end
83
+
84
+ puts @coffee_table.keys
85
+ @coffee_table.keys.should include "test_key_sample_class[9939]"
86
+
87
+ end
88
+ it "should raise an exception if a related object does not respond_to id" do
89
+ test_object = SampleClassWithoutId.new
90
+
91
+ lambda {
92
+ result = @coffee_table.get_cache(:test_key, test_object) do
93
+ "this is a changed value"
94
+ end
95
+ }.should raise_exception "Objects passed in must have an id method"
96
+
97
+ end
98
+ end
99
+ context "with expiry" do
100
+ it "should not execute block when cache available and not expired"
101
+ it "should execute block and return value when cache has expired"
102
+ end
103
+ end
104
+
105
+ describe "expire_key" do
106
+ end
107
+
108
+ describe "expire_all" do
109
+ end
110
+
111
+ describe "keys" do
112
+ end
113
+
114
+ describe "expire_for" do
115
+ end
116
+
117
+ end
@@ -0,0 +1,5 @@
1
+ class SampleClass
2
+ def id
3
+ 9939
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ class SampleClassWithoutId
2
+ end
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'spork'
3
+ require 'mock_redis'
4
+ require File.expand_path(File.dirname(__FILE__) + '/../../coffee_table/spec/lib/sample_class')
5
+ require File.expand_path(File.dirname(__FILE__) + '/../../coffee_table/spec/lib/sample_class_without_id')
6
+
7
+ Spork.prefork do
8
+ require File.expand_path(File.dirname(__FILE__) + '/../../coffee_table/lib/coffee_table.rb')
9
+ end
10
+
11
+ Spork.each_run do
12
+ RSpec.configure do |config|
13
+ config.before(:each) {
14
+
15
+
16
+ redis = mock(:redis)
17
+ Redis.stub!(:new).and_return(MockRedis.new)
18
+ CoffeeTable::Cache.new.expire_all
19
+
20
+ }
21
+
22
+ config.after(:each) {
23
+ }
24
+ end
25
+ end
26
+
27
+
28
+
29
+ def load_sample(filename)
30
+ File.open(File.dirname(__FILE__) + "/samples/" + filename).map { |line| line}.join("\n")
31
+ end
32
+ def load_binary_sample(filename)
33
+ File.open(File.dirname(__FILE__) + "/samples/" + filename, 'rb')
34
+ end
35
+
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coffee_table
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stewart McKee
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-16 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: rails cache gem to fragment cache with smart cache key management
15
+ email:
16
+ - stewart@theizone.co.uk
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - .rspec
23
+ - .rvmrc
24
+ - Gemfile
25
+ - Rakefile
26
+ - coffee_table.gemspec
27
+ - lib/coffee_table.rb
28
+ - lib/coffee_table/version.rb
29
+ - lib/utility.rb
30
+ - spec/lib/coffee_table_spec.rb
31
+ - spec/lib/sample_class.rb
32
+ - spec/lib/sample_class_without_id.rb
33
+ - spec/spec_helper.rb
34
+ homepage: ''
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project: coffee_table
54
+ rubygems_version: 1.8.10
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Gem to manage cache stored in redis
58
+ test_files:
59
+ - spec/lib/coffee_table_spec.rb
60
+ - spec/lib/sample_class.rb
61
+ - spec/lib/sample_class_without_id.rb
62
+ - spec/spec_helper.rb