catche 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -11,34 +11,54 @@ gem "catche"
11
11
 
12
12
  ## Controller caching
13
13
 
14
- Controller caching is based on `caches_action` using the method `catche`.
14
+ Catche supports both action and page caching using the Rails methods `caches_action` and `caches_page`.
15
+
16
+ ### Action caching
17
+
18
+ Catche's `catches_action` uses Rails' `caches_action` and therefore supports all options this method supports.
19
+
20
+ ```ruby
21
+ class ProjectsController < ApplicationController
22
+ catches_action Project, :index, :show
23
+ end
24
+ ```
25
+
26
+ ### Page caching
27
+
28
+ Catche's `catches_page` uses Rails' `caches_page` and therefore supports all options this method supports.
29
+
30
+ ```ruby
31
+ class ProjectsController < ApplicationController
32
+ catches_page Project, :index, :show
33
+ end
34
+ ```
15
35
 
16
36
  ### Simple caching
17
37
 
18
38
  ```ruby
19
39
  class ProjectsController < ApplicationController
20
- catche Project, :index, :show
40
+ catches_action Project, :index, :show # or catches_page
21
41
  end
22
42
  ```
23
43
 
24
- This will result in the following expirations:
44
+ This will result in the following expirations, depending on your routes configuration:
25
45
 
26
46
  ```ruby
27
47
  @project.update_attributes({ :title => 'Update!' }) # or @project.destroy
28
48
 
29
- # => Expires /projects
49
+ # => Expires: /projects
30
50
  # => Expires: /projects/1
31
51
  ```
32
52
 
33
53
  ```ruby
34
54
  @project.create
35
55
 
36
- # => Expires /projects
56
+ # => Expires: /projects
37
57
  ```
38
58
 
39
59
  ### Associative caching
40
60
 
41
- Catche supports associative (nested) caching.
61
+ Catche supports associative caching.
42
62
 
43
63
  ```ruby
44
64
  class Task < ActiveRecord::Base
@@ -48,7 +68,7 @@ end
48
68
 
49
69
  ```ruby
50
70
  class TasksController < ApplicationController
51
- catche Task, :index, :show
71
+ catches_action Task, :index, :show # or catches_page
52
72
  end
53
73
  ```
54
74
 
@@ -57,7 +77,7 @@ This will result in the following expirations:
57
77
  ```ruby
58
78
  @task.update_attributes({ :title => 'Update!' }) # or @task.destroy
59
79
 
60
- # => Expires /tasks
80
+ # => Expires: /tasks
61
81
  # => Expires: /projects/1/tasks
62
82
  # => Expires: /projects/1/tasks/1
63
83
  ```
@@ -65,7 +85,7 @@ This will result in the following expirations:
65
85
  ```ruby
66
86
  @project.tasks.create
67
87
 
68
- # => Expires /tasks
88
+ # => Expires: /tasks
69
89
  # => Expires: /projects/1/tasks
70
90
  ```
71
91
 
@@ -84,7 +104,7 @@ This will result in the following expirations:
84
104
  ```ruby
85
105
  @task.update_attributes({ :title => 'Update!' }) # or @task.destroy
86
106
 
87
- # => Expires /tasks
107
+ # => Expires: /tasks
88
108
  # => Expires: /projects/1/tasks
89
109
  # => Expires: /projects/1/tasks/1
90
110
  # => Expires: /users/1/tasks
@@ -94,7 +114,7 @@ This will result in the following expirations:
94
114
  ```ruby
95
115
  @project.tasks.create
96
116
 
97
- # => Expires /tasks
117
+ # => Expires: /tasks
98
118
  # => Expires: /projects/1/tasks
99
119
  # => Expires: /users/1/tasks
100
120
  ```
@@ -104,10 +124,11 @@ This will result in the following expirations:
104
124
  ```ruby
105
125
  class TasksController < ApplicationController
106
126
  catche(
107
- Task, # Configured cached model
108
- :index, :show, # Actions
127
+ Task, # Configured cached model
128
+ :index, :show, # Actions
109
129
  {
110
- :resource_name => :task, # Name of your resource, defaults to your model name
130
+ :resource_name => :task, # Name of your resource, defaults to your model name
131
+ :type => :action, # Type of caching, :action or :page
111
132
  }
112
133
  )
113
134
  end
@@ -124,12 +145,23 @@ class Task < ActiveRecord::Base
124
145
  end
125
146
  ```
126
147
 
148
+ ## How does it work?
149
+
150
+ Catche intercepts a cached value and tags this value using the unique identifier for the given/loaded resource or collection. Once a resource expires it will expire the tagged cached values, such as the resource itself and the collection it belongs to.
151
+
152
+ ```ruby
153
+ Catche::Tag::Collect.resource(@task) # { :set => ["tasks_1"], :expire => ["tasks_1"] }
154
+ Catche::Tag::Collect.collection_tags(@task, Task) # { :set => ["projects_1_tasks"], :expire => ["tasks", "projects_1_tasks"] }
155
+ ```
156
+
157
+ The tags will point to different cached values, for example pointing to a cached key or a cached filepath.
158
+
127
159
  ## Manually expiring a cache
128
160
 
129
161
  ```ruby
130
162
  @task.expire_resource!
131
163
  @task.expire_collection!
132
- @task.expire.expire_resource_and_collection!
164
+ @task.expire_resource_and_collection!
133
165
  ```
134
166
 
135
167
  ## Supported cache stores
@@ -144,6 +176,7 @@ Want support for more? Just fork and open up a pull request.
144
176
 
145
177
  ## Roadmap
146
178
 
179
+ * Page cache (caches_page)
147
180
  * View cache
148
181
 
149
182
  ## License
@@ -1 +1,60 @@
1
- require 'catche/controller/base'
1
+ module Catche
2
+ module Controller
3
+
4
+ extend ActiveSupport::Concern
5
+ extend ActiveSupport::Autoload
6
+
7
+ eager_autoload do
8
+ autoload :Action
9
+ autoload :Page
10
+ end
11
+
12
+ include Action, Page
13
+
14
+ included do
15
+
16
+ class_attribute :catche_model,
17
+ :catche_resource_name,
18
+ :catche_constructed_tags
19
+
20
+ end
21
+
22
+ module ClassMethods
23
+
24
+ # Caches a controller action.
25
+ #
26
+ # catche Project, :index, :resource_name => :project
27
+ def catche(model, *args)
28
+ options = args.extract_options!
29
+
30
+ self.catche_model = model
31
+ self.catche_resource_name = options[:resource_name] || self.catche_model.name.downcase.to_sym
32
+
33
+ case options[:type]
34
+ when :page then caches_page *args, options
35
+ else caches_action *args, options
36
+ end
37
+ end
38
+
39
+ def catche?
40
+ self.catche_model.present?
41
+ end
42
+
43
+ end
44
+
45
+ def catche_tags
46
+ return @catche_tags if ! @catche_tags.nil?
47
+
48
+ if resource = Catche::ResourceLoader.fetch_one(self, self.class.catche_resource_name)
49
+ tags = Catche::Tag::Collect.resource(resource)
50
+ @catche_tags = tags[:set]
51
+ else
52
+ tags = Catche::Tag::Collect.collection(self, self.class.catche_model)
53
+ @catche_tags = tags[:set]
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+ ActionController::Base.send :include, Catche::Controller
@@ -0,0 +1,30 @@
1
+ module Catche
2
+ module Controller
3
+ module Action
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ # Caches an action in Rails.cache
10
+ # See ActionController `caches_action` for more information
11
+ #
12
+ # catches_action Project, :index
13
+ def catches_action(model, *args)
14
+ catche model, *args, :type => :action
15
+ end
16
+
17
+ end
18
+
19
+ def _save_fragment(name, options={})
20
+ if self.class.catche?
21
+ key = fragment_cache_key(name)
22
+ Catche::Tag.tag_view! key, *catche_tags
23
+ end
24
+
25
+ super
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ module Catche
2
+ module Controller
3
+ module Page
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ # Caches an action by file
10
+ # See ActionController `caches_page` for more information
11
+ #
12
+ # catches_action Project, :index
13
+ def catches_page(model, *args)
14
+ catche model, *args, :type => :page
15
+ end
16
+
17
+ def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
18
+ if self.catche?
19
+ Catche::Tag.tag_page! page_cache_path(path, extension), *catche_constructed_tags
20
+ end
21
+
22
+ super
23
+ end
24
+
25
+ end
26
+
27
+ def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
28
+ self.class.catche_constructed_tags = catche_tags
29
+ super
30
+ end
31
+
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ require 'catche/expire/view'
2
+ require 'catche/expire/page'
3
+
4
+ module Catche
5
+ module Expire
6
+
7
+ class << self
8
+
9
+ def expire!(data)
10
+ Catche::Expire::View.expire! *data[:views]
11
+ Catche::Expire::Page.expire! *data[:pages]
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Catche
2
+ module Expire
3
+ module Page
4
+
5
+ class << self
6
+
7
+ # Expires cache by deleting the associated files
8
+ # Uses the same flow as defined in `expire_page` in ActionController
9
+ #
10
+ # Catche::Expire::Page.expire!('/public/projects.html')
11
+ def expire!(*paths)
12
+ paths.each do |path|
13
+ File.delete(path) if File.exist?(path)
14
+ File.delete(path + '.gz') if File.exist?(path + '.gz')
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Catche
2
+ module Expire
3
+ module View
4
+
5
+ class << self
6
+
7
+ # Expires cache by deleting the associated keys
8
+ #
9
+ # Catche::Expire::View.expire!('projects')
10
+ def expire!(*keys)
11
+ keys.each do |key|
12
+ Catche.adapter.delete key
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -12,52 +12,69 @@ module Catche
12
12
  tags.flatten.compact.uniq.join(DIVIDER)
13
13
  end
14
14
 
15
- def tag!(key, *tags)
15
+ # Tags a view
16
+ def tag_view!(view, *tags)
17
+ tag_type! :views, view, *tags
18
+ end
19
+ alias :tag_fragment! :tag_view!
20
+
21
+ # Tags a page cache
22
+ def tag_page!(page, *tags)
23
+ tag_type! :pages, page, *tags
24
+ end
25
+
26
+ # Dynamic method for tagging a type of stored cache
27
+ #
28
+ # Catche::Tag.tag_type!(:views, 'example.com/projects', 'projects')
29
+ def tag_type!(scope, value, *tags)
16
30
  tags.each do |tag|
17
- keys = fetch_tag(tag)
18
- key_tags = fetch_key(key)
31
+ data = fetch_tag(tag)
32
+
33
+ # Set up key names
19
34
  tag_key = stored_key(:tags, tag)
20
- key_key = stored_key(:keys, key)
35
+ type_key = stored_key(scope, value)
21
36
 
22
- Catche.adapter.write(tag_key, keys << key)
23
- Catche.adapter.write(key_key, key_tags << tag_key)
37
+ # Current tags
38
+ type_tags = fetch_type(scope, value)
39
+
40
+ # Append new value to scoped data
41
+ data[scope] ||= []
42
+ data[scope] << value
43
+ tag_data = data
44
+
45
+ # Append new tag key
46
+ type_data = type_tags << tag_key
47
+
48
+ Catche.adapter.write(tag_key, tag_data)
49
+ Catche.adapter.write(type_key, type_data)
24
50
  end
25
51
  end
26
52
 
27
53
  def expire!(*tags)
28
- expired_keys = []
29
-
30
54
  tags.each do |tag|
31
- keys = fetch_tag(tag)
32
- expired_keys += keys
33
-
34
- keys.each do |key|
35
- # Expires the cached value
36
- Catche.adapter.delete key
37
-
38
- # Removes the tag from the tag list in case it's never used again
39
- Catche.adapter.write(
40
- stored_key(:keys, key),
41
- fetch_key(key).delete(stored_key(:tags, tag))
42
- )
43
- end
44
-
55
+ Catche::Expire.expire! fetch_tag(tag)
45
56
  Catche.adapter.delete stored_key(:tags, tag)
46
57
  end
47
-
48
- expired_keys
49
58
  end
50
59
 
51
60
  protected
52
61
 
53
62
  def fetch_tag(tag)
54
- Catche.adapter.read stored_key(:tags, tag), []
63
+ Catche.adapter.read stored_key(:tags, tag), {}
55
64
  end
56
65
 
57
66
  def fetch_key(key)
58
67
  Catche.adapter.read stored_key(:keys, key), []
59
68
  end
60
69
 
70
+ def fetch_type(type, value)
71
+ Catche.adapter.read stored_key(type, value), []
72
+ end
73
+
74
+ def fetch_path(path)
75
+ Catche.adapter.read stored_key(:paths, path), []
76
+ end
77
+
61
78
  def stored_key(scope, value)
62
79
  join_keys KEY, scope.to_s, value.to_s
63
80
  end
@@ -6,7 +6,7 @@ module Catche
6
6
 
7
7
  # Collects resource tags for a given resource
8
8
  #
9
- # Catche::Tag::Tagger.resource_tags(@task)
9
+ # Catche::Tag::Collect.resource_tags(@task)
10
10
  # => { :set => ["tasks_1"], :expire => ["tasks_1"] }
11
11
  def resource(resource)
12
12
  set_tags = []
@@ -23,7 +23,7 @@ module Catche
23
23
 
24
24
  # Collects collection tags for a given context, for example a controller
25
25
  #
26
- # Catche::Tag::Tagger.collection_tags(controller, Task)
26
+ # Catche::Tag::Collect.collection_tags(controller, Task)
27
27
  # => { :set => ["projects_1_tasks"], :expire => ["tasks", "projects_1_tasks"] }
28
28
  def collection(context, resource_class, include_base=true)
29
29
  associations = Catche::ResourceLoader.fetch(context, *resource_class.catche_associations)
@@ -1,3 +1,3 @@
1
1
  module Catche
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: catche
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-22 00:00:00.000000000Z
12
+ date: 2012-06-23 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &70193875371180 !ruby/object:Gem::Requirement
16
+ requirement: &70222481972780 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.2.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70193875371180
24
+ version_requirements: *70222481972780
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sqlite3
27
- requirement: &70193875370760 !ruby/object:Gem::Requirement
27
+ requirement: &70222481972240 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70193875370760
35
+ version_requirements: *70222481972240
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec-rails
38
- requirement: &70193875370300 !ruby/object:Gem::Requirement
38
+ requirement: &70222481971660 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70193875370300
46
+ version_requirements: *70222481971660
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: capybara
49
- requirement: &70193875369880 !ruby/object:Gem::Requirement
49
+ requirement: &70222481971120 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70193875369880
57
+ version_requirements: *70222481971120
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: guard-rspec
60
- requirement: &70193875369460 !ruby/object:Gem::Requirement
60
+ requirement: &70222481970640 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70193875369460
68
+ version_requirements: *70222481970640
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: guard-spork
71
- requirement: &70193875369040 !ruby/object:Gem::Requirement
71
+ requirement: &70222481970160 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,7 +76,7 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70193875369040
79
+ version_requirements: *70222481970160
80
80
  description: ''
81
81
  email:
82
82
  - mail@arjen.me
@@ -86,8 +86,12 @@ extra_rdoc_files: []
86
86
  files:
87
87
  - lib/catche/adapter/base.rb
88
88
  - lib/catche/adapter.rb
89
- - lib/catche/controller/base.rb
89
+ - lib/catche/controller/action.rb
90
+ - lib/catche/controller/page.rb
90
91
  - lib/catche/controller.rb
92
+ - lib/catche/expire/page.rb
93
+ - lib/catche/expire/view.rb
94
+ - lib/catche/expire.rb
91
95
  - lib/catche/model/base.rb
92
96
  - lib/catche/model.rb
93
97
  - lib/catche/railtie.rb
@@ -1,57 +0,0 @@
1
- module Catche
2
- module Controller
3
- module Base
4
-
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
-
9
- class_attribute :catche_model,
10
- :catche_resource_name
11
-
12
- end
13
-
14
- module ClassMethods
15
-
16
- # Caches a controller action by tagging it.
17
- # Supports any option parameters caches_action supports.
18
- #
19
- # catche Project, :index, :resource_name => :project
20
- def catche(model, *args)
21
- options = args.extract_options!
22
-
23
- self.catche_model = model
24
- self.catche_resource_name = options[:resource_name] || self.catche_model.name.downcase.to_sym
25
-
26
- # Use Rails caches_action to pass along the tag
27
- caches_action(*args, options.merge({ :catche => true }))
28
- end
29
-
30
- end
31
-
32
- def catche_tags
33
- return @catche_tags if ! @catche_tags.nil?
34
-
35
- if resource = Catche::ResourceLoader.fetch_one(self, self.class.catche_resource_name)
36
- tags = Catche::Tag::Collect.resource(resource)
37
- @catche_tags = tags[:set]
38
- else
39
- tags = Catche::Tag::Collect.collection(self, self.class.catche_model)
40
- @catche_tags = tags[:set]
41
- end
42
- end
43
-
44
- def _save_fragment(name, options={})
45
- if options[:catche]
46
- key = fragment_cache_key(name)
47
- Catche::Tag.tag! key, *catche_tags
48
- end
49
-
50
- super
51
- end
52
-
53
- end
54
- end
55
- end
56
-
57
- ActionController::Base.send :include, Catche::Controller::Base