cachet 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,50 @@
1
+ Just initialize the cache, by configuring your store.
2
+ Just add the configuration to your config initializers
3
+
4
+ require 'cachet'
5
+
6
+ Cachet.setup do |config|
7
+ config.logger = Rails.logger
8
+ config.storage = Cachet::FileStore.new("/pal_storage/path")
9
+
10
+ # The followings are the configurations that you can do on file store
11
+ # The given values are the default ones, if you are OK with them you don't need
12
+ # to configure at all.
13
+ # You can set whether you want directory optimization or not
14
+ # file_store.optimize = TRUE
15
+ # The depth of directories you want
16
+ # file_store.dir_levels = 3
17
+ # Number of directories within a directory, it is better if you use a prime number
18
+ # but something other than 31, that is the one we use for hashing :)
19
+ # file_store.dir_count = 19
20
+ end
21
+
22
+ And from this point forward , if you want return values of your methods to be cached;
23
+
24
+ #include cacheable module , which will add two class macros to mark a method as cacheable and also as cache invalidator.
25
+ You should pass blocks to these marcos which will return cache keys. Use exact signature of the method that you are referring in the block that returns the key.
26
+
27
+
28
+ class Sample
29
+ include Cachet::Cacheable
30
+
31
+ def first (param1, param2)
32
+ puts "First is running"
33
+ return "First"
34
+ end
35
+
36
+
37
+ def second (param1, param2)
38
+ puts "Second is running and invalidating for #{param1}"
39
+ return "First"
40
+ end
41
+
42
+ cacheable :first, :car do |param1, param2|
43
+ param1
44
+ end
45
+
46
+ cache_invalidator :second, :car do |param1, param2|
47
+ param1
48
+ end
49
+
50
+ end
data/lib/cachet.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'rails'
2
+
3
+ module Cachet
4
+ mattr_accessor :logger
5
+ mattr_accessor :storage
6
+
7
+ class << self
8
+ def setup
9
+ yield self
10
+ end
11
+
12
+ def cache (entity, key)
13
+ cached_item = storage.read(entity, key)
14
+ if not cached_item
15
+ cached_item = yield
16
+ storage.write(entity, key, cached_item)
17
+ Cachet.logger.info "#{entity} with key:#{key} could not found in cache. Evaluated and written to cache"
18
+ else
19
+ Cachet.logger.info "#{entity} with key:#{key} found in cache."
20
+ end
21
+ cached_item
22
+ end
23
+
24
+ def invalidate (entity, key)
25
+ storage.purge(entity, key)
26
+ Cachet.logger.info "#{entity} with key:#{key} invalidated."
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ require 'cachet/file_store'
33
+ require 'cachet/cacheable'
34
+
35
+
@@ -0,0 +1,31 @@
1
+ module Cachet
2
+ module Cacheable
3
+ def self.included(base)
4
+ Cachet.logger.debug "Including Cacheable in #{base}"
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def cacheable (cached_method, entity)
10
+ define_method("#{cached_method}_with_cache") { |*args|
11
+ cache_key = block_given? ? yield(*args) : cached_method.to_s
12
+ Cachet.cache(entity, cache_key) do
13
+ method("#{cached_method}_without_cache".to_sym).call(*args)
14
+ end
15
+ }
16
+ alias_method_chain "#{cached_method}".to_sym, :cache
17
+ Cachet.logger.debug "Aliasing method : #{cached_method} to cache the result as #{entity} entities"
18
+ end
19
+
20
+ def cache_invalidator (cached_method, entity)
21
+ define_method("#{cached_method}_with_cache_invalidation") { |*args|
22
+ cache_key = block_given? ? yield(*args) : cached_method.to_s
23
+ method("#{cached_method}_without_cache_invalidation".to_sym).call(*args)
24
+ Cachet.invalidate(entity, cache_key)
25
+ }
26
+ alias_method_chain "#{cached_method}".to_sym, :cache_invalidation
27
+ Cachet.logger.debug "Aliasing method : #{cached_method} to invalidate the #{entity} entities"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ require 'digest/md5'
2
+
3
+ module Cachet
4
+ class FileStore
5
+ attr_reader :root
6
+ attr_accessor :optimize
7
+ attr_accessor :dir_level
8
+ attr_accessor :dir_count
9
+
10
+ def initialize(root)
11
+ @root = root
12
+ FileUtils.mkdir_p root, :mode=>0755
13
+ @optimize = TRUE
14
+ @dir_level = 3
15
+ @dir_count = 19
16
+ end
17
+
18
+ def read(entity, key)
19
+ file_name = storage_path(entity, key)
20
+ File.open(file_name, 'rb') { |f| Marshal.load f.read } if File.exist?(file_name)
21
+ end
22
+
23
+ def write(entity, key, data_to_be_stored)
24
+ file_name = storage_path(entity, key)
25
+ temp_file_name = file_name + '.' + Thread.current.object_id.to_s
26
+ FileUtils.mkdir_p File.dirname(file_name), :mode => 755
27
+ File.open(temp_file_name, 'wb') { |f| f.write(Marshal.dump(data_to_be_stored)) }
28
+ if File.exist?(file_name)
29
+ File.unlink temp_file_name
30
+ else
31
+ FileUtils.mv temp_file_name, file_name
32
+ end
33
+ Cachet.logger.info "A new #{entity} entity stored in the cache with key:#{key} to path #{file_name}."
34
+ end
35
+
36
+ def purge(entity, key)
37
+ file_name = storage_path(entity, key)
38
+ begin
39
+ File.unlink file_name if File.exist?(file_name)
40
+ rescue
41
+ Cachet.logger.warn "An element of #{entity} in the cache with key:#{key} to path #{file_name}entity has could not be deleted."
42
+ end
43
+ Cachet.logger.info "An element of #{entity} in the cache with key:#{key} to path #{file_name}entity has been removed from the cache."
44
+ end
45
+
46
+ def storage_path(entity, key)
47
+ file_name = Digest::MD5.hexdigest(key)
48
+ if optimize
49
+ dirs = (1..dir_level).inject({:hash=>key.sum, :dirs=>[]}) do |result, level|
50
+ result[:dirs] << (result[:hash] % dir_count).abs.to_s
51
+ result[:hash] += (result[:hash] * 31)
52
+ result
53
+ end
54
+ file_name = File.join dirs[:dirs], file_name
55
+ end
56
+ File.join root, entity.to_s, file_name
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ require 'test_helper'
2
+
3
+ class FileStoreTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ FakeFS.activate!
7
+ FakeFS::FileSystem.clear
8
+ @test_store = Cachet::FileStore.new("~/testroot/")
9
+ end
10
+
11
+ def teardown
12
+ FakeFS.deactivate!
13
+ end
14
+
15
+ def test_storage_path_creation
16
+ assert_equal "~/testroot/test/2/7/15/25f9e794323b453885f5181f1b624d0b", @test_store.storage_path(:test, "123456789"), "Check storage path, hashing seems to be not working."
17
+ end
18
+
19
+ def test_idempotent_storage_paths
20
+ assert_equal @test_store.storage_path(:test, "123456789"), @test_store.storage_path(:test, "123456789"), "Should always create same path for same entry."
21
+ end
22
+
23
+ def test_storage_options
24
+ @test_store.dir_level = 10
25
+ assert_equal "~/testroot/test/2/7/15/5/8/9/3/1/13/17/25f9e794323b453885f5181f1b624d0b", @test_store.storage_path(:test, "123456789"), "Should nest resourced under 10 recursive directories."
26
+ @test_store.dir_count = 100
27
+ assert_equal "~/testroot/test/77/64/48/36/52/64/48/36/52/64/25f9e794323b453885f5181f1b624d0b", @test_store.storage_path(:test, "123456789"), "Should allow 100 hundred directories under any directory."
28
+ @test_store.optimize = FALSE
29
+ assert_equal "~/testroot/test/25f9e794323b453885f5181f1b624d0b", @test_store.storage_path(:test, "123456789"), "Should not optimize paths since optimize is disabled."
30
+ end
31
+
32
+ def test_purging_entries
33
+ file_name = @test_store.storage_path(:test, 'test_item_1')
34
+ FileUtils.mkdir_p File.dirname(file_name), :mode => 755
35
+ File.open(file_name, 'wb') { |f| f.write("Data") }
36
+ assert_equal(true, File.exist?(file_name))
37
+ @test_store.purge(:test, 'test_item_1')
38
+ assert_equal(false, File.exist?(file_name))
39
+ end
40
+
41
+ def test_purging_nonexisting_entries
42
+ file_name = @test_store.storage_path(:test, 'test_item_2')
43
+ assert_equal(false, File.exist?(file_name))
44
+ assert_nothing_thrown("FileStore should be slince while purging nonexisting entities") {
45
+ @test_store.purge(:test, 'test_item_2')
46
+ }
47
+ assert_equal(false, File.exist?(file_name))
48
+ end
49
+
50
+ def test_reading_existing_entries
51
+ @test_store.write(:test, 'test_item_3', "Some test data")
52
+ assert_equal("Some test data", @test_store.read(:test, 'test_item_3'))
53
+ end
54
+
55
+ def test_reading_nonexisting_entries
56
+ @test_store.purge(:test, 'test_item_4')
57
+ assert_nil @test_store.read(:test, 'test_item_4')
58
+ end
59
+ end
@@ -0,0 +1,7 @@
1
+ require 'test/unit'
2
+ require 'fakefs/safe'
3
+ require 'cachet'
4
+
5
+ Cachet.setup do |config|
6
+ config.logger = Logger.new(STDOUT)
7
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cachet
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Meltem Atesalp
9
+ - Umut Utkan
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2012-02-07 00:00:00 +02:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: rails
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 3.0.0
26
+ type: :runtime
27
+ version_requirements: *id001
28
+ - !ruby/object:Gem::Dependency
29
+ name: fakefs
30
+ prerelease: false
31
+ requirement: &id002 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: "0"
37
+ type: :development
38
+ version_requirements: *id002
39
+ description: Provides a way to cache and invalidate return values of your time consuming methods.
40
+ email: meltem.atesalp@gmail.com
41
+ executables: []
42
+
43
+ extensions: []
44
+
45
+ extra_rdoc_files: []
46
+
47
+ files:
48
+ - lib/cachet.rb
49
+ - lib/cachet/cacheable.rb
50
+ - lib/cachet/file_store.rb
51
+ - README
52
+ - test/test_file_store.rb
53
+ - test/test_helper.rb
54
+ has_rdoc: true
55
+ homepage: https://github.com/meltem/cachet
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.6.2
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Caches method responses to have a better performance!
82
+ test_files:
83
+ - test/test_file_store.rb
84
+ - test/test_helper.rb