catche 0.1.2 → 0.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
@@ -1,6 +1,6 @@
1
1
  # Catche [![Build Status](https://secure.travis-ci.org/Arjeno/catche.png?branch=master)](http://travis-ci.org/Arjeno/catche)
2
2
 
3
- Catche is a caching library for easy and automated resource and collection caching. It basically tags cached outputs and expires them based on configuration.
3
+ Catche is a caching library for Ruby on Rails. It automates automated resource and collection caching/expiration. It basically tags cached outputs and expires those tags based on configuration.
4
4
 
5
5
  ## Installation
6
6
 
@@ -23,21 +23,63 @@ end
23
23
 
24
24
  ### Associative caching
25
25
 
26
+ For advanced usage such as advanced caching you need to configure this in the model.
27
+
28
+ ```ruby
29
+ class Task < ActiveRecord::Base
30
+ catche :through => :project
31
+ end
32
+ ```
33
+
26
34
  ```ruby
27
35
  class TasksController < ApplicationController
28
- catche Task, :index, :show, :through => :project
36
+ catche Task, :index, :show
29
37
  end
30
38
  ```
31
39
 
32
- On resource change this will expire:
40
+ On resource `update` and `destroy` this will expire:
41
+
42
+ * Resource: `tasks_1`
43
+ * Collection: `tasks`
44
+ * Collection: `projects_1_tasks_1`
45
+
46
+ On resource `create` this will expire:
47
+
48
+ * Collection: `tasks`
49
+ * Collection: `projects_1_tasks_1`
50
+
51
+ You can use as many associations as you would like;
52
+
53
+ ```ruby
54
+ class Task < ActiveRecord::Base
55
+ catche :through => [:user, :project]
56
+ end
57
+ ```
33
58
 
34
- * Resource task
35
- * Resource task within specific project
59
+ ### Advanced configuration
36
60
 
37
- On resource or collection change this will expire:
61
+ ```ruby
62
+ class TasksController < ApplicationController
63
+ catche(
64
+ Task, # Configured cached model
65
+ :index, :show, # Actions
66
+ {
67
+ :resource_name => :task, # Name of your resource, defaults to your model name
68
+ }
69
+ )
70
+ end
71
+ ```
38
72
 
39
- * Collection tasks
40
- * Collection tasks within specific project
73
+ ```ruby
74
+ class Task < ActiveRecord::Base
75
+ catche(
76
+ :through => [:user, :project], # Associations
77
+ :tag_identifier => :id, # Unique identifier for the resource
78
+ :class => Task, # Class to use as tag scope
79
+ :collection_tag => 'tasks', # Name of the tag scope for this model,
80
+ )
81
+ end
82
+ ```
41
83
 
42
84
  ## License
43
85
 
@@ -3,6 +3,7 @@ require 'catche/adapter'
3
3
  require 'catche/controller'
4
4
  require 'catche/model'
5
5
  require 'catche/tag'
6
+ require 'catche/resource_loader'
6
7
 
7
8
  module Catche
8
9
 
@@ -4,46 +4,47 @@ module Catche
4
4
 
5
5
  extend ActiveSupport::Concern
6
6
 
7
+ included do
8
+
9
+ class_attribute :catche_model,
10
+ :catche_resource_name
11
+
12
+ end
13
+
7
14
  module ClassMethods
8
15
 
9
16
  # Caches a controller action by tagging it.
10
17
  # Supports any option parameters caches_action supports.
11
18
  #
12
- # catche Project, :index
13
- # catche Task, :through => :project
19
+ # catche Project, :index, :resource_name => :project
14
20
  def catche(model, *args)
15
21
  options = args.extract_options!
16
- object = Catche::Tag::Object.register!(model, self, options)
17
- tag = Proc.new { |controller| object }
22
+
23
+ self.catche_model = model
24
+ self.catche_resource_name = options[:resource_name] || self.catche_model.name.downcase.to_sym
18
25
 
19
26
  # Use Rails caches_action to pass along the tag
20
- caches_action(*args, { :tag => tag }.merge(options))
27
+ caches_action(*args, options.merge({ :catche => true }))
21
28
  end
22
29
 
23
30
  end
24
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
+
25
44
  def _save_fragment(name, options={})
26
- key = fragment_cache_key(name)
27
- object = options[:tag]
28
- tags = []
29
-
30
- if object.present?
31
- if object.respond_to?(:call)
32
- object = self.instance_exec(self, &object)
33
-
34
- if object.respond_to?(:tags)
35
- tags = object.tags(self)
36
- else
37
- tags = Array.new(object)
38
- end
39
- else
40
- tags = Array.new(object)
41
- end
42
-
43
- Catche::Tag.tag! key, *tags
44
-
45
- # Store for future reference
46
- @catche_cache_object = object
45
+ if options[:catche]
46
+ key = fragment_cache_key(name)
47
+ Catche::Tag.tag! key, *catche_tags
47
48
  end
48
49
 
49
50
  super
@@ -4,32 +4,72 @@ module Catche
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
+ class_attribute :catche_class,
8
+ :catche_collection_tag,
9
+ :catche_tag_identifier,
10
+ :catche_associations
11
+
12
+ # Expiration callbacks
13
+
7
14
  after_update :expire_resource_and_collection!
8
15
  after_destroy :expire_resource_and_collection!
9
16
 
10
17
  after_create :expire_collection!
11
18
  end
12
19
 
20
+ module ClassMethods
21
+
22
+ # Configures catche
23
+ #
24
+ # catche :through => :project, :catche_class => Task
25
+ def catche(options={})
26
+ options = {
27
+ :class => self,
28
+ :tag_identifier => :id,
29
+ :collection_tag => nil,
30
+ :associations => [options[:through]].flatten.compact
31
+ }.merge(options)
32
+
33
+ options.each do |key, value|
34
+ self.send("catche_#{key}=", value) if self.respond_to?("catche_#{key}")
35
+ end
36
+
37
+ self.catche_collection_tag ||= self.catche_class.name.downcase.pluralize
38
+ end
39
+
40
+ def catche_reset!
41
+ catche {}
42
+ end
43
+
44
+ def catche_tag
45
+ self.catche_collection_tag || self.name.downcase.pluralize
46
+ end
47
+
48
+ def catche_tag=(value)
49
+ self.catche_collection_tag = value
50
+ end
51
+
52
+ def catche_tag_identifier
53
+ super || :id
54
+ end
55
+
56
+ end
57
+
58
+ def catche_tag
59
+ Tag.join self.class.catche_tag, self.send(:id)
60
+ end
61
+
13
62
  def expire_resource_and_collection!
14
63
  expire_collection!
15
64
  expire_resource!
16
65
  end
17
66
 
18
67
  def expire_collection!
19
- expire_cache! false
68
+ Catche::Tag.expire! *Catche::Tag::Collect.collection(self, self.class)[:expire]
20
69
  end
21
70
 
22
71
  def expire_resource!
23
- expire_cache!
24
- end
25
-
26
- def expire_cache!(set_instance=true)
27
- tags = Catche::Tag::Object.find_by_model(self.class).collect do |obj|
28
- self.instance_variable_set("@#{obj.options[:resource_name]}", self) if set_instance
29
- obj.expiration_tags(self)
30
- end.flatten.compact.uniq
31
-
32
- Catche::Tag.expire! *tags
72
+ Catche::Tag.expire! *Catche::Tag::Collect.resource(self)[:expire]
33
73
  end
34
74
 
35
75
  end
@@ -0,0 +1,36 @@
1
+ module Catche
2
+ class ResourceLoader
3
+
4
+ class << self
5
+
6
+ # Fetches resources in the given context, for example a controller
7
+ #
8
+ # Catche::Tag::ResourceLoader.fetch(controller, :task)
9
+ def fetch(context, *names)
10
+ names.collect do |name|
11
+ if resource = context.instance_variable_get("@#{name}")
12
+ resource
13
+ elsif context.respond_to?(name)
14
+ begin
15
+ context.send(name)
16
+ rescue
17
+ nil
18
+ end
19
+ else
20
+ nil
21
+ end
22
+ end.compact
23
+ end
24
+
25
+ def fetch_one(context, name)
26
+ fetch(context, name).first
27
+ end
28
+
29
+ def exists?(context, name)
30
+ fetch_one(context, name).present?
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
@@ -1,5 +1,4 @@
1
- require 'catche/tag/object'
2
- require 'catche/tag/resource'
1
+ require 'catche/tag/collect'
3
2
 
4
3
  module Catche
5
4
  module Tag
@@ -0,0 +1,55 @@
1
+ module Catche
2
+ module Tag
3
+ class Collect
4
+
5
+ class << self
6
+
7
+ # Collects resource tags for a given resource
8
+ #
9
+ # Catche::Tag::Tagger.resource_tags(@task)
10
+ # => { :set => ["tasks_1"], :expire => ["tasks_1"] }
11
+ def resource(resource)
12
+ set_tags = []
13
+ expire_tags = []
14
+
15
+ set_tags << resource.catche_tag
16
+ expire_tags << resource.catche_tag
17
+
18
+ {
19
+ :set => set_tags.uniq,
20
+ :expire => expire_tags.uniq
21
+ }
22
+ end
23
+
24
+ # Collects collection tags for a given context, for example a controller
25
+ #
26
+ # Catche::Tag::Tagger.collection_tags(controller, Task)
27
+ # => { :set => ["projects_1_tasks"], :expire => ["tasks", "projects_1_tasks"] }
28
+ def collection(context, resource_class, include_base=true)
29
+ associations = Catche::ResourceLoader.fetch(context, *resource_class.catche_associations)
30
+ association_tags = associations.map { |a| a.catche_tag }
31
+
32
+ set_tags = []
33
+ expire_tags = []
34
+
35
+ if association_tags.any?
36
+ set_tags << Tag.join(*association_tags, resource_class.catche_tag)
37
+ else
38
+ # Without any associations, add just the collection tag
39
+ set_tags << resource_class.catche_tag
40
+ end
41
+
42
+ expire_tags << resource_class.catche_tag if include_base
43
+ expire_tags += set_tags
44
+
45
+ {
46
+ :set => set_tags.uniq,
47
+ :expire => expire_tags.uniq
48
+ }
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -1,3 +1,3 @@
1
1
  module Catche
2
- VERSION = "0.1.2"
2
+ VERSION = "0.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.1.2
4
+ version: '0.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-20 00:00:00.000000000Z
12
+ date: 2012-06-22 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &2160688340 !ruby/object:Gem::Requirement
16
+ requirement: &70299120119960 !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: *2160688340
24
+ version_requirements: *70299120119960
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sqlite3
27
- requirement: &2160687900 !ruby/object:Gem::Requirement
27
+ requirement: &70299120118620 !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: *2160687900
35
+ version_requirements: *70299120118620
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec-rails
38
- requirement: &2160687400 !ruby/object:Gem::Requirement
38
+ requirement: &70299120117540 !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: *2160687400
46
+ version_requirements: *70299120117540
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: capybara
49
- requirement: &2160686880 !ruby/object:Gem::Requirement
49
+ requirement: &70299120115880 !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: *2160686880
57
+ version_requirements: *70299120115880
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: guard-rspec
60
- requirement: &2160686040 !ruby/object:Gem::Requirement
60
+ requirement: &70299120113480 !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: *2160686040
68
+ version_requirements: *70299120113480
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: guard-spork
71
- requirement: &2160685320 !ruby/object:Gem::Requirement
71
+ requirement: &70299120112920 !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: *2160685320
79
+ version_requirements: *70299120112920
80
80
  description: ''
81
81
  email:
82
82
  - mail@arjen.me
@@ -91,8 +91,8 @@ files:
91
91
  - lib/catche/model/base.rb
92
92
  - lib/catche/model.rb
93
93
  - lib/catche/railtie.rb
94
- - lib/catche/tag/object.rb
95
- - lib/catche/tag/resource.rb
94
+ - lib/catche/resource_loader.rb
95
+ - lib/catche/tag/collect.rb
96
96
  - lib/catche/tag.rb
97
97
  - lib/catche/version.rb
98
98
  - lib/catche.rb
@@ -120,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
120
  version: '0'
121
121
  requirements: []
122
122
  rubyforge_project:
123
- rubygems_version: 1.8.15
123
+ rubygems_version: 1.8.10
124
124
  signing_key:
125
125
  specification_version: 3
126
126
  summary: Smart collection and resource caching
@@ -1,138 +0,0 @@
1
- module Catche
2
- module Tag
3
- class Object
4
-
5
- class << self
6
-
7
- @@objects = []
8
-
9
- # Returns an existing (duplicate) or new tag object for the given arguments.
10
- #
11
- # Catche::Tag::Object.register!(Project, ProjectsController)
12
- def register!(model, object, options={})
13
- tag_object = find_or_initialize(model, object, options)
14
-
15
- objects << tag_object
16
- objects.uniq!
17
-
18
- tag_object
19
- end
20
- alias :for :register!
21
-
22
- # Finds a previously declared (same) tag object or returns a new one.
23
- def find_or_initialize(model, object, options={})
24
- new_object = self.new(model, object, options)
25
-
26
- objects.each do |tag_object|
27
- return tag_object if tag_object.same?(new_object)
28
- end
29
-
30
- new_object
31
- end
32
-
33
- def find_by_model(model)
34
- objects.select { |tag_object| tag_object.model == model }
35
- end
36
-
37
- def find_by_association(association)
38
- objects.select { |tag_object| tag_object.associations.include?(association) }
39
- end
40
-
41
- def clear
42
- @@objects = []
43
- end
44
-
45
- def objects
46
- @@objects
47
- end
48
-
49
- end
50
-
51
- attr_reader :model, :object, :options
52
- attr_accessor :associations
53
-
54
- def initialize(model, object, options={})
55
- @model = model
56
- @object = object
57
-
58
- association = options[:through]
59
-
60
- @options = {
61
- :resource_name => Catche::Tag::Resource.singularize(@model),
62
- :collection_name => Catche::Tag::Resource.pluralize(@model),
63
- :associations => [association].flatten.compact,
64
- :bubble => false,
65
- :expire_collection => true
66
- }.merge(options)
67
-
68
- @associations = @options[:associations]
69
- end
70
-
71
- # Returns the tags for the given object.
72
- # If `bubble` is set to true it will pass along the separate tag for each association.
73
- # This means the collection or resource is expired as soon as the association changes.
74
- #
75
- # object = Catche::Tag::Object.new(Task, TasksController, :through => [:user, :project])
76
- # object.tags(controller.new) => ['users_1_projects_1_tasks_1']
77
- def tags(initialized_object)
78
- tags = []
79
-
80
- tags += association_tags(initialized_object) if bubble?
81
- tags += expiration_tags(initialized_object)
82
-
83
- tags
84
- end
85
-
86
- # The tags that should expire as soon as the resource or collection changes.
87
- def expiration_tags(initialized_object)
88
- resource = Catche::Tag::Resource.resource(initialized_object, options[:resource_name])
89
- tags = []
90
-
91
- # Add collection tags when enabled
92
- tags << Catche::Tag.join(
93
- association_tags(initialized_object),
94
- options[:collection_name]
95
- ) if resource.nil? && options[:expire_collection]
96
-
97
- tags << Catche::Tag.join(
98
- association_tags(initialized_object),
99
- identifier_tags(initialized_object)
100
- )
101
-
102
- tags.uniq
103
- end
104
-
105
- # Identifying tag for the current resource or collection.
106
- #
107
- # object = Catche::Tag::Object.new(Task, TasksController)
108
- # object.identifier_tags(controller) => ['tasks', 1]
109
- def identifier_tags(initialized_object)
110
- Catche::Tag.join options[:collection_name], Catche::Tag::Resource.resource(initialized_object, options[:resource_name]).try(:id)
111
- end
112
-
113
- # Maps the given resources names to tags by fetching the resources from the given object.
114
- def resource_tags(*resources)
115
- resources.map { |resource| Catche::Tag.join(Catche::Tag::Resource.pluralize(resource.class), resource.id) }
116
- end
117
-
118
- # Maps association tags.
119
- #
120
- # object = Catche::Tag::Object.new(Task, TasksController, :through => [:user, :project])
121
- # object.association_tags(controller) => ['users_1', 'projects_1']
122
- def association_tags(initialized_object)
123
- resource_tags(*Catche::Tag::Resource.associations(initialized_object, associations))
124
- end
125
-
126
- def bubble?
127
- options[:bubble]
128
- end
129
-
130
- def same?(object)
131
- self.model == object.model &&
132
- self.object == object.object &&
133
- self.options == object.options
134
- end
135
-
136
- end
137
- end
138
- end
@@ -1,35 +0,0 @@
1
- module Catche
2
- module Tag
3
- class Resource
4
-
5
- class << self
6
-
7
- def pluralize(object)
8
- object.name.to_s.pluralize.downcase
9
- end
10
-
11
- def singularize(object)
12
- object.name.to_s.singularize.downcase
13
- end
14
-
15
- def resource(object, name)
16
- if resource = object.instance_variable_get("@#{name}")
17
- return resource
18
- elsif object.respond_to?(name)
19
- begin
20
- return object.send(name)
21
- rescue; end
22
- end
23
-
24
- nil
25
- end
26
-
27
- def associations(object, associations)
28
- associations.map { |association| resource(object, association) }.compact
29
- end
30
-
31
- end
32
-
33
- end
34
- end
35
- end