interlock 1.1 → 1.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/CHANGELOG CHANGED
@@ -1,4 +1,6 @@
1
1
 
2
+ v1.2. Add compatibility with Memcached client.
3
+
2
4
  v1.1. Add :perform => false option. Cache :content_for. Test nested view_cache blocks. Don't assign conventional dependency when called from the view, because we don't know what the controller might have already specified.
3
5
 
4
6
  v1.0. First release.
data/Manifest CHANGED
@@ -7,7 +7,7 @@ lib/interlock/active_record.rb
7
7
  lib/interlock/config.rb
8
8
  lib/interlock/core_extensions.rb
9
9
  lib/interlock/interlock.rb
10
- lib/interlock/memcached.rb
10
+ lib/interlock/lock.rb
11
11
  lib/interlock.rb
12
12
  LICENSE
13
13
  Manifest
@@ -78,5 +78,5 @@ test/setup.rb
78
78
  test/teardown.rb
79
79
  test/test_helper.rb
80
80
  test/unit/interlock_test.rb
81
- test/unit/memcached_test.rb
81
+ test/unit/lock_test.rb
82
82
  TODO
data/README CHANGED
@@ -5,9 +5,11 @@ A Rails plugin for maintainable and high-efficiency caching.
5
5
 
6
6
  == License
7
7
 
8
- Copyright 2007 Cloudburst, LLC. Licensed under the AFL 3; see the included LICENSE file. Portions copyright 2006 Chris Wanstrath and used with permission.
8
+ Copyright 2007, 2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file. Portions copyright 2006 Chris Wanstrath and used with permission.
9
9
 
10
- The public certificate for the gem is at http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem.
10
+ The public certificate for the gem is here[http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem].
11
+
12
+ If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.
11
13
 
12
14
  == Requirements
13
15
 
@@ -27,7 +29,7 @@ Interlock automatically tracks invalidation dependencies based on the model life
27
29
 
28
30
  First, compile and install memcached itself. Get a memcached server running.
29
31
 
30
- You also need the <tt>memcache-client</tt> gem:
32
+ You also need either <tt>memcache-client</tt> or {memcached}[http://blog.evanweaver.com/files/doc/fauna/memcached]:
31
33
  sudo gem install memcache-client
32
34
 
33
35
  Then, install the plugin:
@@ -38,15 +40,18 @@ Lastly, configure your Rails app for memcached by creating a <tt>config/memcache
38
40
  defaults:
39
41
  namespace: myapp
40
42
  sessions: false
43
+ client: memcache-client
41
44
  development:
42
45
  servers:
43
- - localhost:11211 # Default port
46
+ - 127.0.0.1:11211 # Default host and port
44
47
  production:
45
48
  servers
46
49
  - 10.12.128.1
47
50
  - 10.12.128.2
48
51
 
49
52
  Now you're ready to go.
53
+
54
+ Note that if you have {memcached 0.7}[http://blog.evanweaver.com/files/doc/fauna/memcached], you can use <tt>client: memcached</tt> for better performance.
50
55
 
51
56
  == Usage
52
57
 
@@ -106,6 +111,6 @@ Also, Interlock obeys the <tt>ENV['RAILS_ASSET_ID']</tt> setting, so if you need
106
111
 
107
112
  == Reporting problems
108
113
 
109
- * http://rubyforge.org/forum/forum.php?forum_id=19835
114
+ The support forum is here[http://rubyforge.org/forum/forum.php?forum_id=19835].
110
115
 
111
116
  Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
@@ -1,9 +1,10 @@
1
1
  defaults:
2
2
  namespace: myapp
3
3
  sessions: false
4
+ client: memcache-client
4
5
  development:
5
6
  servers:
6
- - localhost:11211
7
+ - 127.0.0.1:11211
7
8
  production:
8
9
  servers
9
10
  - 10.12.128.1
data/interlock.gemspec CHANGED
@@ -1,28 +1,26 @@
1
1
 
2
- # Gem::Specification for Interlock-1.1
2
+ # Gem::Specification for Interlock-1.2
3
3
  # Originally generated by Echoe
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = %q{interlock}
7
- s.version = "1.1"
7
+ s.version = "1.2"
8
8
 
9
9
  s.specification_version = 2 if s.respond_to? :specification_version=
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.authors = [""]
13
- s.date = %q{2008-01-15}
13
+ s.date = %q{2008-02-04}
14
14
  s.description = %q{A Rails plugin for maintainable and high-efficiency caching.}
15
15
  s.email = %q{}
16
- s.files = ["CHANGELOG", "examples/memcached.yml", "init.rb", "lib/interlock/action_controller.rb", "lib/interlock/action_view.rb", "lib/interlock/active_record.rb", "lib/interlock/config.rb", "lib/interlock/core_extensions.rb", "lib/interlock/interlock.rb", "lib/interlock/memcached.rb", "lib/interlock.rb", "LICENSE", "Manifest", "README", "tasks/interlock.rake", "test/integration/app/app/controllers/application.rb", "test/integration/app/app/controllers/eval_controller.rb", "test/integration/app/app/controllers/items_controller.rb", "test/integration/app/app/helpers/application_helper.rb", "test/integration/app/app/helpers/eval_helper.rb", "test/integration/app/app/helpers/items_helper.rb", "test/integration/app/app/models/item.rb", "test/integration/app/app/views/items/detail.rhtml", "test/integration/app/app/views/items/list.rhtml", "test/integration/app/app/views/items/recent.rhtml", "test/integration/app/app/views/items/show.rhtml", "test/integration/app/app/views/layouts/application.html.erb", "test/integration/app/app/views/shared/_related.rhtml", "test/integration/app/config/boot.rb", "test/integration/app/config/database.yml", "test/integration/app/config/environment.rb", "test/integration/app/config/environments/development.rb", "test/integration/app/config/environments/production.rb", "test/integration/app/config/environments/test.rb", "test/integration/app/config/initializers/inflections.rb", "test/integration/app/config/initializers/mime_types.rb", "test/integration/app/config/memcached.yml", "test/integration/app/config/routes.rb", "test/integration/app/db/migrate/001_create_items.rb", "test/integration/app/doc/README_FOR_APP", "test/integration/app/public/404.html", "test/integration/app/public/422.html", "test/integration/app/public/500.html", "test/integration/app/public/dispatch.cgi", "test/integration/app/public/dispatch.fcgi", "test/integration/app/public/dispatch.rb", "test/integration/app/public/favicon.ico", "test/integration/app/public/images/rails.png", "test/integration/app/public/index.html", "test/integration/app/public/javascripts/application.js", "test/integration/app/public/javascripts/controls.js", "test/integration/app/public/javascripts/dragdrop.js", "test/integration/app/public/javascripts/effects.js", "test/integration/app/public/javascripts/prototype.js", "test/integration/app/public/robots.txt", "test/integration/app/Rakefile", "test/integration/app/README", "test/integration/app/script/about", "test/integration/app/script/console", "test/integration/app/script/destroy", "test/integration/app/script/generate", "test/integration/app/script/performance/benchmarker", "test/integration/app/script/performance/profiler", "test/integration/app/script/performance/request", "test/integration/app/script/plugin", "test/integration/app/script/process/inspector", "test/integration/app/script/process/reaper", "test/integration/app/script/process/spawner", "test/integration/app/script/runner", "test/integration/app/script/server", "test/integration/app/test/fixtures/items.yml", "test/integration/app/test/functional/eval_controller_test.rb", "test/integration/app/test/functional/items_controller_test.rb", "test/integration/app/test/test_helper.rb", "test/integration/app/test/unit/item_test.rb", "test/integration/server_test.rb", "test/setup.rb", "test/teardown.rb", "test/test_helper.rb", "test/unit/interlock_test.rb", "test/unit/memcached_test.rb", "TODO", "interlock.gemspec"]
16
+ s.files = ["CHANGELOG", "examples/memcached.yml", "init.rb", "lib/interlock/action_controller.rb", "lib/interlock/action_view.rb", "lib/interlock/active_record.rb", "lib/interlock/config.rb", "lib/interlock/core_extensions.rb", "lib/interlock/interlock.rb", "lib/interlock/lock.rb", "lib/interlock.rb", "LICENSE", "Manifest", "README", "tasks/interlock.rake", "test/integration/app/app/controllers/application.rb", "test/integration/app/app/controllers/eval_controller.rb", "test/integration/app/app/controllers/items_controller.rb", "test/integration/app/app/helpers/application_helper.rb", "test/integration/app/app/helpers/eval_helper.rb", "test/integration/app/app/helpers/items_helper.rb", "test/integration/app/app/models/item.rb", "test/integration/app/app/views/items/detail.rhtml", "test/integration/app/app/views/items/list.rhtml", "test/integration/app/app/views/items/recent.rhtml", "test/integration/app/app/views/items/show.rhtml", "test/integration/app/app/views/layouts/application.html.erb", "test/integration/app/app/views/shared/_related.rhtml", "test/integration/app/config/boot.rb", "test/integration/app/config/database.yml", "test/integration/app/config/environment.rb", "test/integration/app/config/environments/development.rb", "test/integration/app/config/environments/production.rb", "test/integration/app/config/environments/test.rb", "test/integration/app/config/initializers/inflections.rb", "test/integration/app/config/initializers/mime_types.rb", "test/integration/app/config/memcached.yml", "test/integration/app/config/routes.rb", "test/integration/app/db/migrate/001_create_items.rb", "test/integration/app/doc/README_FOR_APP", "test/integration/app/public/404.html", "test/integration/app/public/422.html", "test/integration/app/public/500.html", "test/integration/app/public/dispatch.cgi", "test/integration/app/public/dispatch.fcgi", "test/integration/app/public/dispatch.rb", "test/integration/app/public/favicon.ico", "test/integration/app/public/images/rails.png", "test/integration/app/public/index.html", "test/integration/app/public/javascripts/application.js", "test/integration/app/public/javascripts/controls.js", "test/integration/app/public/javascripts/dragdrop.js", "test/integration/app/public/javascripts/effects.js", "test/integration/app/public/javascripts/prototype.js", "test/integration/app/public/robots.txt", "test/integration/app/Rakefile", "test/integration/app/README", "test/integration/app/script/about", "test/integration/app/script/console", "test/integration/app/script/destroy", "test/integration/app/script/generate", "test/integration/app/script/performance/benchmarker", "test/integration/app/script/performance/profiler", "test/integration/app/script/performance/request", "test/integration/app/script/plugin", "test/integration/app/script/process/inspector", "test/integration/app/script/process/reaper", "test/integration/app/script/process/spawner", "test/integration/app/script/runner", "test/integration/app/script/server", "test/integration/app/test/fixtures/items.yml", "test/integration/app/test/functional/eval_controller_test.rb", "test/integration/app/test/functional/items_controller_test.rb", "test/integration/app/test/test_helper.rb", "test/integration/app/test/unit/item_test.rb", "test/integration/server_test.rb", "test/setup.rb", "test/teardown.rb", "test/test_helper.rb", "test/unit/interlock_test.rb", "test/unit/lock_test.rb", "TODO", "interlock.gemspec"]
17
17
  s.has_rdoc = true
18
18
  s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/interlock/}
19
19
  s.require_paths = ["lib"]
20
20
  s.rubyforge_project = %q{fauna}
21
21
  s.rubygems_version = %q{1.0.1}
22
22
  s.summary = %q{A Rails plugin for maintainable and high-efficiency caching.}
23
- s.test_files = ["test/integration/server_test.rb", "test/unit/interlock_test.rb", "test/unit/memcached_test.rb"]
24
-
25
- s.add_dependency(%q<memcache_client>, [">= 1.5.0"])
23
+ s.test_files = ["test/integration/server_test.rb", "test/unit/interlock_test.rb", "test/unit/lock_test.rb"]
26
24
  end
27
25
 
28
26
 
@@ -36,7 +34,6 @@ end
36
34
  # p.summary = "A Rails plugin for maintainable and high-efficiency caching."
37
35
  # p.url = "http://blog.evanweaver.com/files/doc/fauna/interlock/"
38
36
  # p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
39
- # p.dependencies = "memcache_client >=1.5.0"
40
37
  # p.test_pattern = ["test/integration/*.rb", "test/unit/*.rb"]
41
38
  # p.rdoc_pattern = ["README", "CHANGELOG", "TODO", "LICENSE", "lib/interlock/memcached.rb", "lib/interlock/interlock.rb", "lib/interlock/action_controller.rb", "lib/interlock/active_record.rb", "lib/interlock/action_view.rb", "lib/interlock/config.rb"]
42
39
  # p.clean_pattern += ['test/integration/app/coverage', 'test/integration/app/db/schema.rb',
@@ -44,13 +41,17 @@ end
44
41
  # end
45
42
  #
46
43
  # desc "Run all the tests in production and development mode both"
47
- # task "test_all" do
48
- # STDERR.puts "#{'='*80}\nDevelopment mode\n#{'='*80}"
49
- # system("rake test:multi_rails:all")
50
- #
51
- # ENV['PRODUCTION'] = '1'
52
- # STDERR.puts "#{'='*80}\nProduction mode\n#{'='*80}"
53
- # system("rake test:multi_rails:all")
44
+ # task "test_all" do
45
+ # ['memcache-client', 'memcached'].each do |client|
46
+ # ENV['CLIENT'] = client
47
+ # ENV['PRODUCTION'] = 'false'
48
+ # STDERR.puts "#{'='*80}\nDevelopment mode: #{client}\n#{'='*80}"
49
+ # system("rake test:multi_rails:all")
50
+ #
51
+ # ENV['PRODUCTION'] = 'true'
52
+ # STDERR.puts "#{'='*80}\nProduction mode: #{client}\n#{'='*80}"
53
+ # system("rake test:multi_rails:all")
54
+ # end
54
55
  # end
55
56
  #
56
57
  # task "tail" do
@@ -189,33 +189,41 @@ And in the <tt>show.html.erb</tt> view:
189
189
  #
190
190
  def read_fragment(key, options = nil)
191
191
  return unless perform_caching
192
-
193
- if content = Interlock.local_cache.read(key, options)
194
- # Interlock.say key, "read from local cache"
195
- elsif content = fragment_cache_store.read(key, options)
196
- raise FragmentConsistencyError, "#{key} is not an Array" unless content.is_a? Array
197
- Interlock.say key, "read from memcached"
198
- Interlock.local_cache.write(key, content, options)
199
- else
200
- # Not found
201
- return nil
202
- end
203
192
 
204
- raise FragmentConsistencyError, "#{key}::content is not a String" unless content.first.is_a? String
205
-
206
- options ||= {}
207
- # Note that 'nil' is considered true for :assign_content_for
208
- if options[:assign_content_for] != false and content.last
209
- # Extract content_for variables
210
- content.last.each do |name, value|
211
- raise FragmentConsistencyError, "#{key}::content_for(:#{name}) is not a String" unless value.is_a? String
212
- # We'll just call the helper because that will handle nested view_caches properly.
213
- @template.send(:content_for, name, value)
193
+ begin
194
+ if content = Interlock.local_cache.read(key, options)
195
+ # Interlock.say key, "read from local cache"
196
+ elsif content = fragment_cache_store.read(key, options)
197
+ raise Interlock::FragmentConsistencyError, "#{key} expected Array but got #{content.class}" unless content.is_a? Array
198
+ Interlock.say key, "read from memcached"
199
+ Interlock.local_cache.write(key, content, options)
200
+ else
201
+ # Not found
202
+ return nil
214
203
  end
215
- end
216
-
217
- content.first
218
- end
204
+
205
+ raise Interlock::FragmentConsistencyError, "#{key}::content expected String but got #{content.first.class}" unless content.first.is_a? String
206
+
207
+ options ||= {}
208
+ # Note that 'nil' is considered true for :assign_content_for
209
+ if options[:assign_content_for] != false and content.last
210
+ # Extract content_for variables
211
+ content.last.each do |name, value|
212
+ raise Interlock::FragmentConsistencyError, "#{key}::content_for(:#{name}) expected String but got #{value.class}" unless value.is_a? String
213
+ # We'll just call the helper because that will handle nested view_caches properly.
214
+ @template.send(:content_for, name, value)
215
+ end
216
+ end
217
+
218
+ content.first
219
+ rescue Interlock::FragmentConsistencyError => e
220
+ # XXX Needs test coverage
221
+ # Delete the bogus key
222
+ begin; CACHE.delete key; rescue; end
223
+ # Reraise the error
224
+ raise e
225
+ end
226
+ end
219
227
 
220
228
  end
221
229
  end
@@ -4,8 +4,19 @@ module Interlock
4
4
  DEFAULTS = {
5
5
  :ttl => 1.day,
6
6
  :namespace => 'app',
7
- :servers => ['localhost:11211']
7
+ :servers => ['127.0.0.1:11211'],
8
+ :client => 'memcache-client'
8
9
  }
10
+
11
+ CLIENT_KEYS = [ #:nodoc:
12
+ :hash,
13
+ :no_block,
14
+ :buffer_requests,
15
+ :support_cas,
16
+ :tcp_nodelay,
17
+ :distribution,
18
+ :namespace
19
+ ]
9
20
 
10
21
  mattr_accessor :config
11
22
  @@config = DEFAULTS
@@ -23,7 +34,8 @@ module Interlock
23
34
  #
24
35
  def run!
25
36
  if File.exist?(CONFIG_FILE)
26
- config = YAML.load_file(CONFIG_FILE)
37
+ template = ERB.new(File.open(CONFIG_FILE) {|f| f.read})
38
+ config = YAML.load(template.result(binding))
27
39
  config.deep_symbolize_keys!
28
40
 
29
41
  Interlock.config.merge!(config[:defaults] || {})
@@ -41,12 +53,47 @@ module Interlock
41
53
  Interlock.config[:namespace] << "-#{RAILS_ENV}"
42
54
 
43
55
  unless defined? Object::CACHE
44
- klass = MemCacheWithConsistentHashing rescue MemCache
45
- Object.const_set('CACHE', klass.new(Interlock.config))
46
- CACHE.servers = Array(Interlock.config[:servers])
56
+
57
+ # Give people a choice of client, even though I don't like conditional dependencies.
58
+ klass = case Interlock.config[:client]
59
+ when 'memcached'
60
+ Memcached::Rails
61
+ when 'memcache-client'
62
+ raise ConfigurationError, "You have the Ruby-MemCache gem installed. Please uninstall Ruby-MemCache, or otherwise guarantee that memcache-client will load instead." if MemCache.constants.include?('SVNURL')
63
+ MemCache
64
+ else
65
+ raise ConfigurationError, "Invalid client name '#{Interlock.config[:client]}'"
66
+ end
67
+
68
+ Object.const_set('CACHE',
69
+ klass.new(
70
+ Interlock.config[:servers],
71
+ Interlock.config.slice(*CLIENT_KEYS)
72
+ )
73
+ )
74
+
75
+ # Mark that we're the ones who did it.
76
+ class << CACHE
77
+ def installed_by_interlock; true; end
78
+ end
79
+
80
+ else
81
+ begin
82
+ CACHE.installed_by_interlock
83
+ rescue NoMethodError
84
+ RAILS_DEFAULT_LOGGER.warn "** interlock: Object::CACHE already defined; will not install a new one"
85
+ # Mark that somebody else installed this CACHE.
86
+ class << CACHE
87
+ def installed_by_interlock; false; end
88
+ end
89
+ end
47
90
  end
48
91
 
92
+ # Add the fragment cache and lock APIs to the cache singleton. This happens no matter
93
+ # who installed the singleton.
49
94
  class << CACHE
95
+ include Interlock::Lock
96
+
50
97
  def read(*args)
51
98
  get args.first
52
99
  end
@@ -64,9 +111,17 @@ module Interlock
64
111
  # Configure Rails to use the memcached store for fragments, and optionally, sessions.
65
112
  #
66
113
  def rails!
67
- # Memcached fragment caching is mandatory
114
+ # Memcached fragment caching is mandatory
115
+ ActionView::Helpers::CacheHelper.class_eval do
116
+ def cache(name, options = nil, &block)
117
+ # Things explode if options does not default to nil
118
+ RAILS_DEFAULT_LOGGER.debug "** fragment #{name} stored via obsolete cache() call"
119
+ @controller.cache_erb_fragment(block, name, options)
120
+ end
121
+ end
68
122
  ActionController::Base.fragment_cache_store = CACHE
69
123
 
124
+ # Sessions are optional
70
125
  if Interlock.config[:sessions]
71
126
  ActionController::Base.session_store = :mem_cache_store
72
127
  ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.update 'cache' => CACHE
@@ -40,16 +40,26 @@ class Hash
40
40
  end
41
41
  end
42
42
 
43
+ # Compatibility method for Rails 1.2.6. It's also faster.
44
+ unless Hash.instance_methods.include? "slice"
45
+ def slice(*keys)
46
+ hash = {}
47
+ keys.each do |key|
48
+ hash[key] = self[key] if self[key]
49
+ end
50
+ hash
51
+ end
52
+ end
53
+
43
54
  end
44
55
 
45
56
  class Array
46
- unless Array.instance_methods.include? "extract_options!"
47
-
57
+ # Compatibility method for Rails 1.2.6.
58
+ unless Array.instance_methods.include? "extract_options!"
48
59
  def extract_options!
49
60
  # Method added in Rails rev 7217
50
61
  last.is_a?(Hash) ? pop : {}
51
- end
52
-
62
+ end
53
63
  end
54
64
  end
55
65
 
@@ -5,8 +5,12 @@ module Interlock
5
5
  end
6
6
  class DependencyError < InterlockError #:nodoc:
7
7
  end
8
+ class ConfigurationError < InterlockError #:nodoc:
9
+ end
8
10
  class UsageError < InterlockError #:nodoc:
9
11
  end
12
+ class LockAcquisitionError < InterlockError #:nodoc:
13
+ end
10
14
  class FragmentConsistencyError < InterlockError #:nodoc:
11
15
  end
12
16
 
@@ -0,0 +1,43 @@
1
+
2
+ module Interlock
3
+ module Lock
4
+
5
+ #
6
+ # Try to acquire a global lock from memcached for a particular key.
7
+ # If successful, yield and set the key to the return value, then release
8
+ # the lock.
9
+ #
10
+ # Based on http://rubyurl.com/Sw7 , which I partially wrote.
11
+ #
12
+
13
+ def lock(key, lock_expiry = 30, retries = 5)
14
+ retries.times do |count|
15
+
16
+ # We have to be compatible with both client APIs. Eventually we can use Memcached#cas
17
+ # for this.
18
+ begin
19
+ response = CACHE.add("lock:#{key}", "Locked by #{Process.pid}", lock_expiry)
20
+ # Nil is a successful response for Memcached, so we'll simulate the MemCache
21
+ # API.
22
+ response ||= "STORED\r\n"
23
+ rescue Object => e
24
+ # Catch exceptions from Memcached without setting response.
25
+ end
26
+
27
+ if response == "STORED\r\n"
28
+ begin
29
+ value = yield(CACHE.get(key))
30
+ CACHE.set(key, value)
31
+ return value
32
+ ensure
33
+ CACHE.delete("lock:#{key}")
34
+ end
35
+ else
36
+ sleep((2**count) / 2.0)
37
+ end
38
+ end
39
+ raise ::Interlock::LockAcquisitionError, "Couldn't acquire lock for #{key}"
40
+ end
41
+
42
+ end
43
+ end
data/lib/interlock.rb CHANGED
@@ -2,22 +2,19 @@
2
2
  module Interlock
3
3
  end
4
4
 
5
- unless defined? MemCache or defined? MemCacheWithConsistentHashing
6
- raise "Interlock requires the memcache-client gem"
7
- end
8
-
9
- if MemCache.constants.include?('SVNURL')
10
- raise "You have the Ruby-MemCache gem installed. Interlock uses memcache-client. Please uninstall Ruby-MemCache, or otherwise guarantee that memcache-client will load instead."
11
- end
12
-
13
5
  require 'interlock/core_extensions'
14
6
  require 'interlock/config'
15
7
  require 'interlock/interlock'
16
- require 'interlock/memcached'
8
+ require 'interlock/lock'
17
9
  require 'interlock/action_controller'
18
10
  require 'interlock/action_view'
19
11
  require 'interlock/active_record'
20
12
 
13
+ begin
14
+ require 'memcached'
15
+ rescue LoadError
16
+ end
17
+
21
18
  unless ActionController::Base.perform_caching
22
19
  RAILS_DEFAULT_LOGGER.warn "** interlock warning; config.perform_caching == false"
23
20
  end
@@ -1,8 +1,10 @@
1
1
 
2
- config.cache_classes = ENV['PRODUCTION']
2
+ production = (ENV['PRODUCTION'] == "true")
3
+
4
+ config.cache_classes = production
3
5
  config.whiny_nils = true
4
- config.action_controller.consider_all_requests_local = !ENV['PRODUCTION']
6
+ config.action_controller.consider_all_requests_local = !production
5
7
  config.action_controller.perform_caching = true
6
- config.action_view.cache_template_extensions = ENV['PRODUCTION']
7
- config.action_view.debug_rjs = !ENV['PRODUCTION']
8
+ config.action_view.cache_template_extensions = production
9
+ config.action_view.debug_rjs = !production
8
10
  config.action_mailer.raise_delivery_errors = false
@@ -2,8 +2,8 @@ defaults:
2
2
  ttl: 30
3
3
  namespace: interlock
4
4
  sessions: true
5
-
5
+ client: <%= ENV['CLIENT'] || 'memcached' %>
6
6
  development:
7
7
  servers:
8
- - localhost:43042
9
- - localhost:43043
8
+ - 127.0.0.1:43042
9
+ - 127.0.0.1:43043
@@ -1,7 +1,7 @@
1
1
 
2
2
  require "#{File.dirname(__FILE__)}/../test_helper"
3
3
 
4
- class MemcachedTest < Test::Unit::TestCase
4
+ class LockTest < Test::Unit::TestCase
5
5
 
6
6
  KEY = "memcached_test"
7
7
  LOCK = "lock:#{KEY}"
@@ -26,7 +26,7 @@ class MemcachedTest < Test::Unit::TestCase
26
26
  def test_locked
27
27
  CACHE.set LOCK, "Bogus"
28
28
 
29
- assert_raises MemCache::MemCacheError do
29
+ assert_raises Interlock::LockAcquisitionError do
30
30
  CACHE.lock(KEY, 30, 2) { "A" }
31
31
  end
32
32
 
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.1"
4
+ version: "1.2"
5
5
  platform: ruby
6
6
  authors:
7
7
  - ""
@@ -30,18 +30,10 @@ cert_chain:
30
30
  yZ0=
31
31
  -----END CERTIFICATE-----
32
32
 
33
- date: 2008-01-15 00:00:00 -05:00
33
+ date: 2008-02-04 00:00:00 -05:00
34
34
  default_executable:
35
- dependencies:
36
- - !ruby/object:Gem::Dependency
37
- name: memcache_client
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: 1.5.0
44
- version:
35
+ dependencies: []
36
+
45
37
  description: A Rails plugin for maintainable and high-efficiency caching.
46
38
  email: ""
47
39
  executables: []
@@ -60,7 +52,7 @@ files:
60
52
  - lib/interlock/config.rb
61
53
  - lib/interlock/core_extensions.rb
62
54
  - lib/interlock/interlock.rb
63
- - lib/interlock/memcached.rb
55
+ - lib/interlock/lock.rb
64
56
  - lib/interlock.rb
65
57
  - LICENSE
66
58
  - Manifest
@@ -131,7 +123,7 @@ files:
131
123
  - test/teardown.rb
132
124
  - test/test_helper.rb
133
125
  - test/unit/interlock_test.rb
134
- - test/unit/memcached_test.rb
126
+ - test/unit/lock_test.rb
135
127
  - TODO
136
128
  - interlock.gemspec
137
129
  has_rdoc: true
@@ -163,4 +155,4 @@ summary: A Rails plugin for maintainable and high-efficiency caching.
163
155
  test_files:
164
156
  - test/integration/server_test.rb
165
157
  - test/unit/interlock_test.rb
166
- - test/unit/memcached_test.rb
158
+ - test/unit/lock_test.rb
metadata.gz.sig CHANGED
Binary file
@@ -1,30 +0,0 @@
1
-
2
- class MemCache
3
-
4
- #
5
- # Try to acquire a global lock from memcached for a particular key.
6
- # If successful, yield and set the key to the return value, then release
7
- # the lock.
8
- #
9
- # Based on http://rubyurl.com/Sw7 , which I partially wrote.
10
- #
11
-
12
- def lock(key, lock_expiry = 30, retries = 5)
13
- retries.times do |count|
14
- response = CACHE.add("lock:#{key}", "Locked by #{Process.pid}", lock_expiry)
15
- if response == "STORED\r\n"
16
- begin
17
- value = yield(CACHE.get(key))
18
- CACHE.set(key, value)
19
- return value
20
- ensure
21
- CACHE.delete("lock:#{key}")
22
- end
23
- else
24
- sleep((2**count) / 2.0)
25
- end
26
- end
27
- raise MemCacheError, "Couldn't acquire lock for #{key}"
28
- end
29
-
30
- end