interlock 1.0 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/Manifest +8 -15
- data/README +16 -73
- data/TODO +3 -0
- data/interlock.gemspec +16 -9
- data/lib/interlock/action_controller.rb +135 -36
- data/lib/interlock/action_view.rb +85 -18
- data/lib/interlock/active_record.rb +1 -1
- data/lib/interlock/config.rb +1 -1
- data/lib/interlock/interlock.rb +22 -3
- data/tasks/interlock.rake +10 -0
- data/test/integration/app/app/controllers/application.rb +2 -2
- data/test/integration/app/app/controllers/items_controller.rb +19 -1
- data/test/integration/app/app/views/items/detail.rhtml +20 -0
- data/test/integration/app/app/views/items/{list.html.erb → list.rhtml} +6 -0
- data/test/integration/app/app/views/items/{recent.html.erb → recent.rhtml} +0 -0
- data/test/integration/app/app/views/items/{show.html.erb → show.rhtml} +1 -1
- data/test/integration/app/app/views/layouts/application.html.erb +9 -0
- data/test/integration/app/app/views/shared/{_related.html.erb → _related.rhtml} +0 -0
- data/test/integration/app/config/boot.rb +2 -0
- data/test/integration/app/config/environment.rb +1 -3
- data/test/integration/server_test.rb +53 -6
- data/test/setup.rb +5 -0
- data/test/teardown.rb +0 -4
- data/test/test_helper.rb +9 -2
- data/test/unit/interlock_test.rb +21 -0
- data.tar.gz.sig +0 -0
- metadata +14 -20
- metadata.gz.sig +0 -0
- data/test/integration/app/coverage/cache-43041 +0 -0
- data/test/integration/app/coverage/index.html +0 -414
- data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-action_controller_rb.html +0 -733
- data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-action_view_rb.html +0 -644
- data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-active_record_rb.html +0 -639
- data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-config_rb.html +0 -688
- data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-core_extensions_rb.html +0 -674
- data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-interlock_rb.html +0 -722
- data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-memcached_rb.html +0 -640
- data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock_rb.html +0 -635
- data/test/integration/app/db/schema.rb +0 -21
data/lib/interlock/config.rb
CHANGED
@@ -19,7 +19,7 @@ module Interlock
|
|
19
19
|
#
|
20
20
|
# Load the configuration file, if available, and then set up the Memcached instance,
|
21
21
|
# Rails settings, and CACHE constants. Should be more or less compatible with
|
22
|
-
#
|
22
|
+
# Cache_fu.
|
23
23
|
#
|
24
24
|
def run!
|
25
25
|
if File.exist?(CONFIG_FILE)
|
data/lib/interlock/interlock.rb
CHANGED
@@ -7,9 +7,20 @@ module Interlock
|
|
7
7
|
end
|
8
8
|
class UsageError < InterlockError #:nodoc:
|
9
9
|
end
|
10
|
+
class FragmentConsistencyError < InterlockError #:nodoc:
|
11
|
+
end
|
10
12
|
|
11
13
|
SCOPE_KEYS = [:controller, :action, :id]
|
12
14
|
|
15
|
+
# Buried value extracted from memcache.rb in the memcache-client gem.
|
16
|
+
# If one tries to request a key that is too long, the client throws an error.
|
17
|
+
# In practice, it seems better to simply avoid ever setting such long keys,
|
18
|
+
# so we use this value to achieve such for keys generated by Interlock.
|
19
|
+
KEY_LENGTH_LIMIT = 250
|
20
|
+
|
21
|
+
# Similarly buried and useful: no whitespace allowed in keys.
|
22
|
+
ILLEGAL_KEY_CHARACTERS_PATTERN = /\s/
|
23
|
+
|
13
24
|
mattr_accessor :local_cache
|
14
25
|
|
15
26
|
class << self
|
@@ -93,12 +104,20 @@ module Interlock
|
|
93
104
|
# rolling your own.
|
94
105
|
#
|
95
106
|
def caching_key(controller, action, id, tag)
|
96
|
-
raise ArgumentError,
|
107
|
+
raise ArgumentError, 'Both controller and action must be specified' unless controller and action
|
97
108
|
|
98
109
|
id = (id or 'all').to_interlock_tag
|
99
110
|
tag = tag.to_interlock_tag
|
100
|
-
|
101
|
-
"interlock:#{ENV['RAILS_ASSET_ID']}:#{controller}:#{action}:#{id}:#{tag}"
|
111
|
+
|
112
|
+
key = "interlock:#{ENV['RAILS_ASSET_ID']}:#{controller}:#{action}:#{id}:#{tag}"
|
113
|
+
|
114
|
+
if key.length > KEY_LENGTH_LIMIT
|
115
|
+
old_key = key
|
116
|
+
key = key[0..KEY_LENGTH_LIMIT-1]
|
117
|
+
say old_key, "truncated to #{key}"
|
118
|
+
end
|
119
|
+
|
120
|
+
key.gsub ILLEGAL_KEY_CHARACTERS_PATTERN, ''
|
102
121
|
end
|
103
122
|
|
104
123
|
#
|
@@ -2,9 +2,9 @@
|
|
2
2
|
# Likewise, all the methods added will be available for all controllers.
|
3
3
|
|
4
4
|
class ApplicationController < ActionController::Base
|
5
|
-
helper :all # include all helpers, all the time
|
5
|
+
# helper :all # include all helpers, all the time
|
6
6
|
|
7
7
|
# See ActionController::RequestForgeryProtection for details
|
8
8
|
# Uncomment the :secret if you're not using the cookie session store
|
9
|
-
protect_from_forgery # :secret => '491bb7f9ad07a91046fcc3756839524b'
|
9
|
+
# protect_from_forgery # :secret => '491bb7f9ad07a91046fcc3756839524b'
|
10
10
|
end
|
@@ -9,6 +9,16 @@ class ItemsController < ApplicationController
|
|
9
9
|
render :action => 'list'
|
10
10
|
end
|
11
11
|
|
12
|
+
def detail
|
13
|
+
# Nesting is theoretically useful when the outer view block invalidates faster than the inner one
|
14
|
+
behavior_cache :tag => :outer do
|
15
|
+
@items = Item.find(:all)
|
16
|
+
end
|
17
|
+
behavior_cache Item => :id, :tag => :inner do
|
18
|
+
@item = Item.find(params[:id])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
12
22
|
def show
|
13
23
|
behavior_cache Item => :id do
|
14
24
|
@item = Item.find(params['id'])
|
@@ -21,6 +31,14 @@ class ItemsController < ApplicationController
|
|
21
31
|
end
|
22
32
|
end
|
23
33
|
|
34
|
+
def preview
|
35
|
+
@perform = false
|
36
|
+
behavior_cache Item => :id, :perform => @perform do
|
37
|
+
@item = Item.find(params['id'])
|
38
|
+
end
|
39
|
+
render :action => 'show'
|
40
|
+
end
|
41
|
+
|
24
42
|
private
|
25
43
|
|
26
44
|
def related
|
@@ -28,5 +46,5 @@ class ItemsController < ApplicationController
|
|
28
46
|
@related = "Delicious cake"
|
29
47
|
end
|
30
48
|
end
|
31
|
-
|
49
|
+
|
32
50
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
<% view_cache :tag => :outer do %>
|
3
|
+
<div style="border: 1px solid black; padding: 5px;">
|
4
|
+
<p>I invalidate quickly.</p>
|
5
|
+
<h1><%= @items.size %> total items</h1>
|
6
|
+
|
7
|
+
<% content_for :title do %>Outer: <% end %>
|
8
|
+
|
9
|
+
<% view_cache :tag => :inner do %>
|
10
|
+
<div style="margin: 20px; padding: 5px; border: 1px solid black;">
|
11
|
+
<p>I invalidate slowly.</p>
|
12
|
+
<h2>Item <%= @item.id %> is <%= @item.name %>.</h2>
|
13
|
+
|
14
|
+
<% content_for :title do %>Inner<% end %>
|
15
|
+
|
16
|
+
</div>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
</div>
|
20
|
+
<% end %>
|
@@ -1,10 +1,16 @@
|
|
1
1
|
|
2
2
|
<h1>List</h1>
|
3
3
|
<% view_cache do %>
|
4
|
+
|
5
|
+
<% content_for :title do %>
|
6
|
+
<%= @items.size %> Items
|
7
|
+
<% end %>
|
8
|
+
|
4
9
|
<% @items.each do |item| %>
|
5
10
|
<h3><%= item.name %></h3>
|
6
11
|
<p><%= item.description %></p>
|
7
12
|
<% end %>
|
13
|
+
|
8
14
|
<% end %>
|
9
15
|
|
10
16
|
<%= render :partial => 'shared/related' %>
|
File without changes
|
File without changes
|
@@ -55,8 +55,10 @@ module Rails
|
|
55
55
|
|
56
56
|
def load_rails_gem
|
57
57
|
if version = self.class.gem_version
|
58
|
+
STDERR.puts "Boot.rb loading version #{version}"
|
58
59
|
gem 'rails', version
|
59
60
|
else
|
61
|
+
STDERR.puts "Boot.rb loading latest available version"
|
60
62
|
gem 'rails'
|
61
63
|
end
|
62
64
|
rescue Gem::LoadError => load_error
|
@@ -1,13 +1,11 @@
|
|
1
1
|
|
2
|
-
RAILS_GEM_VERSION = ENV['MULTIRAILS_RAILS_VERSION'] if ENV['MULTIRAILS_RAILS_VERSION']
|
3
|
-
|
4
2
|
require File.join(File.dirname(__FILE__), 'boot')
|
5
3
|
require 'action_controller'
|
6
4
|
|
7
5
|
Rails::Initializer.run do |config|
|
8
6
|
|
9
7
|
if ActionController::Base.respond_to? 'session='
|
10
|
-
config.action_controller.session = {:session_key => '_app_session', :secret => '
|
8
|
+
config.action_controller.session = {:session_key => '_app_session', :secret => '22cde4d5c1a61ba69a81795322cde4d5c1a61ba69a817953'}
|
11
9
|
end
|
12
10
|
|
13
11
|
# config.to_prepare do
|
@@ -70,7 +70,8 @@ class ServerTest < Test::Unit::TestCase
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def test_caching_with_tag
|
73
|
-
|
73
|
+
# This test is a little over-complicated
|
74
|
+
sleep(4)
|
74
75
|
assert_no_match(/Artichoke/, browse("items/recent?seconds=3"))
|
75
76
|
assert_match(/recent:all:3 is running the controller block/, log)
|
76
77
|
|
@@ -79,15 +80,26 @@ class ServerTest < Test::Unit::TestCase
|
|
79
80
|
assert_match(/recent:all:2 is running the controller block/, log)
|
80
81
|
assert_no_match(/recent:all:3 is running the controller block/, log)
|
81
82
|
|
83
|
+
truncate
|
84
|
+
remote_eval("Item.find(1).save!")
|
85
|
+
assert_match(/Artichoke/, browse("items/recent?seconds=4"))
|
86
|
+
assert_match(/recent:all:4 is running the controller block/, log)
|
87
|
+
|
82
88
|
truncate
|
83
89
|
assert_no_match(/Artichoke/, browse("items/recent?seconds=3"))
|
84
|
-
assert_no_match(/recent:all:3 is running the controller block/, log)
|
90
|
+
assert_no_match(/recent:all:3 is running the controller block/, log)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_caching_with_perform_false
|
94
|
+
browse("items/preview/1")
|
95
|
+
assert_no_match(/preview:1:untagged registered a dependency/, log)
|
96
|
+
assert_match(/preview:1:untagged is not cached/, log)
|
85
97
|
|
86
98
|
truncate
|
87
|
-
|
88
|
-
|
89
|
-
assert_match(/
|
90
|
-
end
|
99
|
+
browse("items/preview/1")
|
100
|
+
assert_no_match(/preview:1:untagged registered a dependency/, log)
|
101
|
+
assert_match(/preview:1:untagged is not cached/, log)
|
102
|
+
end
|
91
103
|
|
92
104
|
def test_caching_with_ignore
|
93
105
|
assert_match(/Delicious cake/, browse('items'))
|
@@ -104,6 +116,41 @@ class ServerTest < Test::Unit::TestCase
|
|
104
116
|
assert_match(/any:any:all:related is running the controller block/, log)
|
105
117
|
end
|
106
118
|
|
119
|
+
def test_caching_of_content_for
|
120
|
+
assert_match(/Interlock Test:\s*\d\s*Items/m, browse("items"))
|
121
|
+
assert_match(/all:untagged is running the controller block/, log)
|
122
|
+
assert_match(/all:untagged wrote/, log)
|
123
|
+
|
124
|
+
truncate
|
125
|
+
assert_match(/Interlock Test:\s*\d\s*Items/m, browse("items"))
|
126
|
+
# Make sure we didn't copy the content_for too many times
|
127
|
+
assert_no_match(/Interlock Test:\s*\d\s*Items\s*\d\s*Items/m, browse("items"))
|
128
|
+
assert_no_match(/all:untagged is running the controller block/, log)
|
129
|
+
assert_match(/all:untagged read from memcached/, log)
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_nested_view_caches
|
133
|
+
assert_match(/Outer: Inner<.*2 total items.*Artichoke/m, browse("items/detail/1"))
|
134
|
+
assert_match(/detail:1:outer is running the controller block/, log)
|
135
|
+
assert_match(/detail:1:inner is running the controller block/, log)
|
136
|
+
|
137
|
+
truncate
|
138
|
+
assert_match(/Outer: Inner<.*2 total items.*Artichoke/m, browse("items/detail/1"))
|
139
|
+
assert_no_match(/detail:1:outer is running the controller block/, log)
|
140
|
+
assert_no_match(/detail:1:inner is running the controller block/, log)
|
141
|
+
|
142
|
+
truncate
|
143
|
+
remote_eval("Item.find(2).save!")
|
144
|
+
assert_match(/Outer: Inner<.*2 total items.*Artichoke/m, browse("items/detail/1"))
|
145
|
+
assert_match(/detail:1:outer is running the controller block/, log)
|
146
|
+
assert_no_match(/detail:1:inner is running the controller block/, log)
|
147
|
+
|
148
|
+
truncate
|
149
|
+
remote_eval("Item.find(1).save!")
|
150
|
+
assert_match(/Outer: Inner<.*2 total items.*Artichoke/m, browse("items/detail/1"))
|
151
|
+
assert_match(/detail:1:outer is running the controller block/, log)
|
152
|
+
assert_match(/detail:1:inner is running the controller block/, log)
|
153
|
+
end
|
107
154
|
|
108
155
|
### Support methods
|
109
156
|
|
data/test/setup.rb
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
# Setup integration system for the integration suite
|
3
3
|
|
4
4
|
Dir.chdir "#{File.dirname(__FILE__)}/integration/app/" do
|
5
|
+
|
6
|
+
`ps awx`.split("\n").grep(/4304[1-3]/).map do |process|
|
7
|
+
system("kill -9 #{process.to_i}")
|
8
|
+
end
|
9
|
+
|
5
10
|
system "memcached -p 43042 &"
|
6
11
|
system "memcached -p 43043 &"
|
7
12
|
|
data/test/teardown.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -2,12 +2,19 @@
|
|
2
2
|
$VERBOSE = nil
|
3
3
|
require 'rubygems'
|
4
4
|
require 'test/unit'
|
5
|
+
require 'echoe'
|
5
6
|
require 'multi_rails_init'
|
6
7
|
|
8
|
+
if defined? ENV['MULTIRAILS_RAILS_VERSION']
|
9
|
+
ENV['RAILS_GEM_VERSION'] = ENV['MULTIRAILS_RAILS_VERSION']
|
10
|
+
end
|
11
|
+
|
7
12
|
$rcov = ENV['RCOV']
|
8
13
|
require 'ruby-debug' unless $rcov
|
9
14
|
|
10
|
-
|
11
|
-
|
15
|
+
Echoe.silence do
|
16
|
+
HERE = File.expand_path(File.dirname(__FILE__))
|
17
|
+
$LOAD_PATH << HERE
|
18
|
+
end
|
12
19
|
|
13
20
|
require 'integration/app/config/environment'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/../test_helper"
|
2
|
+
|
3
|
+
class InterlockTest < Test::Unit::TestCase
|
4
|
+
def test_caching_key_requires_controller_and_action
|
5
|
+
assert_raises ArgumentError do
|
6
|
+
Interlock.caching_key nil, nil, nil, nil
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_caching_key_prevents_too_long_keys
|
11
|
+
assert_equal Interlock::KEY_LENGTH_LIMIT,
|
12
|
+
Interlock.caching_key('controller', 'action', 'id', 'x'*Interlock::KEY_LENGTH_LIMIT).size,
|
13
|
+
"keys longer than #{Interlock::KEY_LENGTH_LIMIT} will result in errors from memcache-client"
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_caching_key_strips_whitespace
|
17
|
+
assert_no_match Interlock::ILLEGAL_KEY_CHARACTERS_PATTERN,
|
18
|
+
Interlock.caching_key('controller', 'action', 'id', 'tag with illegal characters')
|
19
|
+
'generated keys should not contain illegal characters'
|
20
|
+
end
|
21
|
+
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: interlock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "1.
|
4
|
+
version: "1.1"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ""
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
yZ0=
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date:
|
33
|
+
date: 2008-01-15 00:00:00 -05:00
|
34
34
|
default_executable:
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -42,7 +42,7 @@ dependencies:
|
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: 1.5.0
|
44
44
|
version:
|
45
|
-
description:
|
45
|
+
description: A Rails plugin for maintainable and high-efficiency caching.
|
46
46
|
email: ""
|
47
47
|
executables: []
|
48
48
|
|
@@ -65,6 +65,7 @@ files:
|
|
65
65
|
- LICENSE
|
66
66
|
- Manifest
|
67
67
|
- README
|
68
|
+
- tasks/interlock.rake
|
68
69
|
- test/integration/app/app/controllers/application.rb
|
69
70
|
- test/integration/app/app/controllers/eval_controller.rb
|
70
71
|
- test/integration/app/app/controllers/items_controller.rb
|
@@ -72,10 +73,12 @@ files:
|
|
72
73
|
- test/integration/app/app/helpers/eval_helper.rb
|
73
74
|
- test/integration/app/app/helpers/items_helper.rb
|
74
75
|
- test/integration/app/app/models/item.rb
|
75
|
-
- test/integration/app/app/views/items/
|
76
|
-
- test/integration/app/app/views/items/
|
77
|
-
- test/integration/app/app/views/items/
|
78
|
-
- test/integration/app/app/views/
|
76
|
+
- test/integration/app/app/views/items/detail.rhtml
|
77
|
+
- test/integration/app/app/views/items/list.rhtml
|
78
|
+
- test/integration/app/app/views/items/recent.rhtml
|
79
|
+
- test/integration/app/app/views/items/show.rhtml
|
80
|
+
- test/integration/app/app/views/layouts/application.html.erb
|
81
|
+
- test/integration/app/app/views/shared/_related.rhtml
|
79
82
|
- test/integration/app/config/boot.rb
|
80
83
|
- test/integration/app/config/database.yml
|
81
84
|
- test/integration/app/config/environment.rb
|
@@ -86,18 +89,7 @@ files:
|
|
86
89
|
- test/integration/app/config/initializers/mime_types.rb
|
87
90
|
- test/integration/app/config/memcached.yml
|
88
91
|
- test/integration/app/config/routes.rb
|
89
|
-
- test/integration/app/coverage/cache-43041
|
90
|
-
- test/integration/app/coverage/index.html
|
91
|
-
- test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-action_controller_rb.html
|
92
|
-
- test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-action_view_rb.html
|
93
|
-
- test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-active_record_rb.html
|
94
|
-
- test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-config_rb.html
|
95
|
-
- test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-core_extensions_rb.html
|
96
|
-
- test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-interlock_rb.html
|
97
|
-
- test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-memcached_rb.html
|
98
|
-
- test/integration/app/coverage/vendor-plugins-interlock-lib-interlock_rb.html
|
99
92
|
- test/integration/app/db/migrate/001_create_items.rb
|
100
|
-
- test/integration/app/db/schema.rb
|
101
93
|
- test/integration/app/doc/README_FOR_APP
|
102
94
|
- test/integration/app/public/404.html
|
103
95
|
- test/integration/app/public/422.html
|
@@ -138,6 +130,7 @@ files:
|
|
138
130
|
- test/setup.rb
|
139
131
|
- test/teardown.rb
|
140
132
|
- test/test_helper.rb
|
133
|
+
- test/unit/interlock_test.rb
|
141
134
|
- test/unit/memcached_test.rb
|
142
135
|
- TODO
|
143
136
|
- interlock.gemspec
|
@@ -163,10 +156,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
156
|
requirements: []
|
164
157
|
|
165
158
|
rubyforge_project: fauna
|
166
|
-
rubygems_version: 0.
|
159
|
+
rubygems_version: 1.0.1
|
167
160
|
signing_key:
|
168
161
|
specification_version: 2
|
169
|
-
summary:
|
162
|
+
summary: A Rails plugin for maintainable and high-efficiency caching.
|
170
163
|
test_files:
|
171
164
|
- test/integration/server_test.rb
|
165
|
+
- test/unit/interlock_test.rb
|
172
166
|
- test/unit/memcached_test.rb
|
metadata.gz.sig
CHANGED
Binary file
|
Binary file
|