interlock 1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
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