cached_attr 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/README.md ADDED
@@ -0,0 +1,4 @@
1
+ cache_attr
2
+ ==========
3
+
4
+ Make a ruby object attribute cacheable
@@ -0,0 +1,115 @@
1
+ # This module implements attributes whose reader method executes once, and the returned value is cached for later use.
2
+ # This might be handy for expensive operations.
3
+ #
4
+ # An example, given the following method:
5
+ #
6
+ # def object_to_get
7
+ # if @object_to_get.nil?
8
+ # @object_to_get = [code_to_retrieve_object]
9
+ # end
10
+ # @object_to_get
11
+ # end
12
+ #
13
+ # ...the cached attribute format would be:
14
+ #
15
+ # cache_attr :object_to_get do
16
+ # [code_to_retrieve_object]
17
+ # end
18
+ module CachedAttr
19
+
20
+ def self.included(base)
21
+ base.extend(ClassMethods)
22
+ end
23
+
24
+ module ClassMethods
25
+
26
+ # Defines an attribute on the including class.
27
+ #
28
+ # @param name [Symbol] the name of the attribute
29
+ # @param options [Hash] options
30
+ # @option options :writer if set to true, also define a writer method for the property
31
+ # @option options :ttl the time to live for a value. Ignored if nil or false
32
+ # @option options :method the method to call if no block is given
33
+ def cached_attr(name, options = {}, &block)
34
+ default_opts = {
35
+ :writer => false,
36
+ :method => false,
37
+ :ttl => false
38
+ }
39
+ opts = default_opts.merge(options)
40
+
41
+ class_eval do
42
+ internal_retrieve_method = "__internal_retrieve_#{name}".to_sym
43
+ define_method(internal_retrieve_method) do
44
+ if block.nil?
45
+ val = self.send(opts[:method])
46
+ else
47
+ val = self.instance_exec(&block)
48
+ end
49
+ cached_attributes_store[name][:invoke_count] += 1
50
+ val
51
+ end
52
+ internal_init_method = "__internal_init_#{name}".to_sym
53
+ define_method(internal_init_method) do |val = nil|
54
+ unless cached_attributes_store.has_key?(name)
55
+ cached_attributes_store[name] = {:call_count => 0, :invoke_count => 0}
56
+ if val.nil?
57
+ cached_attributes_store[name][:value] = self.send(internal_retrieve_method)
58
+ cached_attributes_store[name][:expires] = Time.now + opts[:ttl] if opts[:ttl]
59
+
60
+ else
61
+ cached_attributes_store[name][:value] = val
62
+ end
63
+ end
64
+ cached_attributes_store[name]
65
+ end
66
+
67
+
68
+ define_method("_#{name}_reset!".to_sym) do
69
+ cached_attributes_store.delete(name)
70
+ end
71
+
72
+ define_method("_#{name}_expires".to_sym) do
73
+ self.send(internal_init_method)[:expires]
74
+ end
75
+
76
+ define_method("_#{name}_call_count".to_sym) do
77
+ self.send(internal_init_method)[:call_count]
78
+ end
79
+
80
+ define_method("_#{name}_invoke_count".to_sym) do
81
+ self.send(internal_init_method)[:invoke_count]
82
+ end
83
+
84
+ define_method(name) do
85
+
86
+ cache_val = self.send(internal_init_method)
87
+
88
+ if opts[:ttl] && cache_val[:expires] && Time.now > cache_val[:expires]
89
+ cache_val[:value] = self.send(internal_retrieve_method)
90
+ cache_val[:expires] = Time.now + opts[:ttl]
91
+ end
92
+
93
+ cache_val[:call_count] = cached_attributes_store[name][:call_count] + 1
94
+ cache_val[:value]
95
+ end
96
+
97
+ if opts[:writer]
98
+ define_method("#{name}=") do |val|
99
+ self.send(internal_init_method, val)
100
+ end
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ #private
109
+
110
+ # The store of cached properties
111
+ def cached_attributes_store
112
+ @cached_attributes_store ||= {}
113
+ end
114
+
115
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe CachedAttr do
4
+
5
+ it "simple case" do
6
+ t = Test.new
7
+ 5.times do
8
+ t.one.should == "1"
9
+ end
10
+ t._one_call_count.should == 5
11
+ t._one_invoke_count.should == 1
12
+ end
13
+
14
+ it "using TTL" do
15
+ t = Test.new
16
+ t.two.should == "2"
17
+ t._two_invoke_count.should == 1
18
+ sleep(3)
19
+ t.two.should == "2"
20
+ t._two_invoke_count.should == 2
21
+ end
22
+
23
+ it "using method call" do
24
+ t = Test.new
25
+ t.three.should == "3"
26
+ end
27
+
28
+ class Test
29
+ include CachedAttr
30
+
31
+ cached_attr :one do
32
+ "1"
33
+ end
34
+
35
+ cached_attr :two, :ttl => 2 do
36
+ "2"
37
+ end
38
+
39
+ cached_attr :three, :method => :three_impl
40
+
41
+ def three_impl
42
+ "3"
43
+ end
44
+ end
45
+
46
+
47
+ end
@@ -0,0 +1,12 @@
1
+ require 'bundler/setup'
2
+ require 'rspec/autorun'
3
+ require 'simplecov'
4
+
5
+ unless ENV['COVERAGE'] == 'no'
6
+ require 'simplecov'
7
+ SimpleCov.start do
8
+ add_filter "/spec/"
9
+ end
10
+ end
11
+
12
+ require 'cached_attr'
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cached_attr
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jerphaes van Blijenburgh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: simplecov
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.7.1
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.7.1
62
+ description: Cacheable Ruby attributes
63
+ email:
64
+ - jerphaes@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - lib/cached_attr.rb
70
+ - README.md
71
+ - spec/cached_attr_spec.rb
72
+ - spec/spec_helper.rb
73
+ homepage: http://github.com/jerphaes/cache_attr
74
+ licenses:
75
+ - Apache License 2.0
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 1.9.2
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 1.8.25
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: Cacheable Ruby attributes
98
+ test_files:
99
+ - spec/cached_attr_spec.rb
100
+ - spec/spec_helper.rb