catche 0.2.1 → 0.2.2

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 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