interlock 1.0 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG +2 -0
  2. data/Manifest +8 -15
  3. data/README +16 -73
  4. data/TODO +3 -0
  5. data/interlock.gemspec +16 -9
  6. data/lib/interlock/action_controller.rb +135 -36
  7. data/lib/interlock/action_view.rb +85 -18
  8. data/lib/interlock/active_record.rb +1 -1
  9. data/lib/interlock/config.rb +1 -1
  10. data/lib/interlock/interlock.rb +22 -3
  11. data/tasks/interlock.rake +10 -0
  12. data/test/integration/app/app/controllers/application.rb +2 -2
  13. data/test/integration/app/app/controllers/items_controller.rb +19 -1
  14. data/test/integration/app/app/views/items/detail.rhtml +20 -0
  15. data/test/integration/app/app/views/items/{list.html.erb → list.rhtml} +6 -0
  16. data/test/integration/app/app/views/items/{recent.html.erb → recent.rhtml} +0 -0
  17. data/test/integration/app/app/views/items/{show.html.erb → show.rhtml} +1 -1
  18. data/test/integration/app/app/views/layouts/application.html.erb +9 -0
  19. data/test/integration/app/app/views/shared/{_related.html.erb → _related.rhtml} +0 -0
  20. data/test/integration/app/config/boot.rb +2 -0
  21. data/test/integration/app/config/environment.rb +1 -3
  22. data/test/integration/server_test.rb +53 -6
  23. data/test/setup.rb +5 -0
  24. data/test/teardown.rb +0 -4
  25. data/test/test_helper.rb +9 -2
  26. data/test/unit/interlock_test.rb +21 -0
  27. data.tar.gz.sig +0 -0
  28. metadata +14 -20
  29. metadata.gz.sig +0 -0
  30. data/test/integration/app/coverage/cache-43041 +0 -0
  31. data/test/integration/app/coverage/index.html +0 -414
  32. data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-action_controller_rb.html +0 -733
  33. data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-action_view_rb.html +0 -644
  34. data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-active_record_rb.html +0 -639
  35. data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-config_rb.html +0 -688
  36. data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-core_extensions_rb.html +0 -674
  37. data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-interlock_rb.html +0 -722
  38. data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock-memcached_rb.html +0 -640
  39. data/test/integration/app/coverage/vendor-plugins-interlock-lib-interlock_rb.html +0 -635
  40. data/test/integration/app/db/schema.rb +0 -21
@@ -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
- # cache_fu.
22
+ # Cache_fu.
23
23
  #
24
24
  def run!
25
25
  if File.exist?(CONFIG_FILE)
@@ -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, "Both controller and action must be specified" unless controller and action
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
  #
@@ -0,0 +1,10 @@
1
+
2
+ namespace :interlock do
3
+ desc "Watch the Rails log for Interlock-specific messages"
4
+ task :tail do
5
+ Dir.chdir RAILS_ROOT do
6
+ exec("tail -f log/#{RAILS_ENV}.log | grep interlock")
7
+ end
8
+ end
9
+ end
10
+
@@ -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' %>
@@ -1,6 +1,6 @@
1
1
 
2
2
  <h1>Show</h1>
3
- <% view_cache Item => :id do %>
3
+ <% view_cache :perform => @perform do %>
4
4
  <h3><%= @item.name %></h3>
5
5
  <p><%= @item.description %></p>
6
6
  <% end %>
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <title>Interlock Test: <%= yield :title %></title>
4
+ </head>
5
+ <body>
6
+ <%= yield %>
7
+ </body>
8
+ </html>
9
+
@@ -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 => '22cde4d5c1a61ba69a817953'}
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
- sleep(3)
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
- remote_eval("Item.find(1).save!")
88
- assert_match(/Artichoke/, browse("items/recent?seconds=3"))
89
- assert_match(/recent:all:3 is running the controller block/, log)
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
@@ -1,4 +0,0 @@
1
-
2
- `ps awx`.split("\n").grep(/4304[1-3]/).map do |process|
3
- system("kill -9 #{process.to_i}")
4
- end
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
- HERE = File.expand_path(File.dirname(__FILE__))
11
- $LOAD_PATH << HERE
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.0"
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: 2007-12-13 00:00:00 -05:00
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: An optimal-efficiency caching plugin for Rails.
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/list.html.erb
76
- - test/integration/app/app/views/items/recent.html.erb
77
- - test/integration/app/app/views/items/show.html.erb
78
- - test/integration/app/app/views/shared/_related.html.erb
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.9.5
159
+ rubygems_version: 1.0.1
167
160
  signing_key:
168
161
  specification_version: 2
169
- summary: An optimal-efficiency caching plugin for Rails.
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