gem_of_thrones 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ script: "bundle exec rake"
2
+ rvm:
3
+ - ree
4
+ - 1.9.2
5
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ gem 'rake'
5
+ gem 'rspec', '~>2'
6
+ gem 'activesupport', '>3'
7
+ gem 'memcached'
8
+ gem 'benhutton-libmemcached_store'
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gem_of_thrones (0.2.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ activesupport (3.2.3)
10
+ i18n (~> 0.6)
11
+ multi_json (~> 1.0)
12
+ benhutton-libmemcached_store (0.3.2)
13
+ memcached
14
+ diff-lcs (1.1.3)
15
+ i18n (0.6.0)
16
+ memcached (1.3.5)
17
+ multi_json (1.3.4)
18
+ rake (0.9.2)
19
+ rspec (2.6.0)
20
+ rspec-core (~> 2.6.0)
21
+ rspec-expectations (~> 2.6.0)
22
+ rspec-mocks (~> 2.6.0)
23
+ rspec-core (2.6.4)
24
+ rspec-expectations (2.6.0)
25
+ diff-lcs (~> 1.1.2)
26
+ rspec-mocks (2.6.0)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ activesupport (> 3)
33
+ benhutton-libmemcached_store
34
+ gem_of_thrones!
35
+ memcached
36
+ rake
37
+ rspec (~> 2)
@@ -0,0 +1,22 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :default do
4
+ sh "rspec spec/"
5
+ end
6
+
7
+ # extracted from https://github.com/grosser/project_template
8
+ rule /^version:bump:.*/ do |t|
9
+ sh "git status | grep 'nothing to commit'" # ensure we are not dirty
10
+ index = ['major', 'minor','patch'].index(t.name.split(':').last)
11
+ file = 'lib/gem_of_thrones/version.rb'
12
+
13
+ version_file = File.read(file)
14
+ old_version, *version_parts = version_file.match(/(\d+)\.(\d+)\.(\d+)/).to_a
15
+ version_parts[index] = version_parts[index].to_i + 1
16
+ version_parts[2] = 0 if index < 2 # remove patch for minor
17
+ version_parts[1] = 0 if index < 1 # remove minor for major
18
+ new_version = version_parts * '.'
19
+ File.open(file,'w'){|f| f.write(version_file.sub(old_version, new_version)) }
20
+
21
+ sh "bundle && git add #{file} Gemfile.lock && git commit -m 'bump version to #{new_version}'"
22
+ end
@@ -0,0 +1,39 @@
1
+ Everybody wants to be king, but only one can win (synchronized via a distributed cache).<br/>
2
+ Update something everybody depends on without doing it multiple times or using a cron.
3
+
4
+ Cache must support the interface `write(key, value, :expires_in => xxx, :unless_exist => true)`,<br/>
5
+ which afaik only `ActiveSupport::Cache::MemCacheStore` and [ActiveSupport::Cache::LibmemcachedStore](https://github.com/benhutton/libmemcached_store) do.
6
+
7
+
8
+ Install
9
+ =======
10
+ gem install gem_of_thrones
11
+ Or
12
+
13
+ rails plugin install git://github.com/grosser/gem_of_thrones.git
14
+
15
+
16
+ Usage
17
+ =====
18
+
19
+ aspirant = GemOfThrones.new(
20
+ :cache => Rails.cache, # where to store the lock ?
21
+ :timeout => 60 # if current king does not react the next aspirant will take its place
22
+ )
23
+
24
+ Thread.new do
25
+ loop do
26
+ # if I can be king (there is no king or the old king did not do anything)
27
+ if aspirant.rise_to_power
28
+ # do something that should only be done by one
29
+ end
30
+ sleep 30 # if you choose a timeout greater then the throne timeout the king will always change
31
+ end
32
+ end
33
+
34
+ Author
35
+ ======
36
+ [Zendesk](http://zendesk.com)<br/>
37
+ michael@grosser.it<br/>
38
+ License: MIT<br/>
39
+ [![Build Status](https://secure.travis-ci.org/grosser/gem_of_thrones.png)](http://travis-ci.org/grosser/gem_of_thrones)
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
+ name = "gem_of_thrones"
3
+ require "#{name}/version"
4
+
5
+ Gem::Specification.new name, GemOfThrones::VERSION do |s|
6
+ s.summary = "Everybody wants to be king, but only one can win (synchronized via a distributed cache)"
7
+ s.authors = ["Michael Grosser"]
8
+ s.email = "michael@grosser.it"
9
+ s.homepage = "http://github.com/grosser/#{name}"
10
+ s.files = `git ls-files`.split("\n")
11
+ s.license = 'MIT'
12
+ end
@@ -0,0 +1,38 @@
1
+ require "gem_of_thrones/version"
2
+
3
+ class GemOfThrones
4
+ def initialize(options)
5
+ @options = {
6
+ :timeout => 10 * 60,
7
+ :cache_key => "GemOfThrones.",
8
+ :cache => "You have to set :cache",
9
+ }.merge(options)
10
+
11
+ raise "Only integers are supported for timeout" if options[:timeout].is_a?(Float)
12
+ end
13
+
14
+ def rise_to_power
15
+ i_am_king! :try => !in_power?
16
+ end
17
+
18
+ private
19
+
20
+ def i_am_king!(options)
21
+ @options[:cache].write(@options[:cache_key], myself,
22
+ :expires_in => @options[:timeout],
23
+ :unless_exist => options[:try]
24
+ )
25
+ end
26
+
27
+ def in_power?
28
+ current_king == myself
29
+ end
30
+
31
+ def current_king
32
+ @options[:cache].read(@options[:cache_key])
33
+ end
34
+
35
+ def myself
36
+ @myself ||= "#{Process.pid}-#{object_id}-#{Time.now.to_f}"
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ class GemOfThrones
2
+ VERSION = '0.2.1'
3
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe GemOfThrones do
4
+ let(:cache){ ActiveSupport::Cache::LibmemcachedStore.new(:namespace => "GemOfThronesTest") }
5
+
6
+ before do
7
+ cache.clear
8
+ end
9
+
10
+ def new_game(options={})
11
+ GemOfThrones.new({:cache => cache, :mutex_timeout => 0.01}.merge(options))
12
+ end
13
+
14
+ it "has a VERSION" do
15
+ GemOfThrones::VERSION.should =~ /^[\.\da-z]+$/
16
+ end
17
+
18
+ it "raises with unsupported timeouts" do
19
+ expect{
20
+ new_game(:timeout => 0.1)
21
+ }.to raise_error
22
+ end
23
+
24
+ describe "#rise_to_power" do
25
+ it "executes if there is no king" do
26
+ result = nil
27
+ if new_game.rise_to_power
28
+ result = 1
29
+ end
30
+ result.should == 1
31
+ end
32
+
33
+ it "executes if the king is alive" do
34
+ result = nil
35
+ if new_game.rise_to_power
36
+ result = 1
37
+ end
38
+ if new_game.rise_to_power
39
+ result = 2
40
+ end
41
+ result.should == 1
42
+ end
43
+
44
+ it "executes if the king stays alive" do
45
+ result = nil
46
+ game = new_game
47
+ if game.rise_to_power
48
+ result = 1
49
+ end
50
+ if game.rise_to_power
51
+ result = 2
52
+ end
53
+ result.should == 2
54
+ end
55
+
56
+ it "executes if the king died" do
57
+ result = nil
58
+ if new_game(:timeout => 1).rise_to_power
59
+ result = 1
60
+ end
61
+ sleep 1.1
62
+ if new_game.rise_to_power
63
+ result = 2
64
+ end
65
+ result.should == 2
66
+ end
67
+
68
+ # using fractions of seconds leads to randomly failing tests
69
+ it "ruling keeps the king in power" do
70
+ result = nil
71
+ old_king = new_game(:timeout => 3)
72
+ if old_king.rise_to_power
73
+ result = 1
74
+ end
75
+ sleep 2
76
+ if old_king.rise_to_power
77
+ result = 2
78
+ end
79
+ sleep 2
80
+ if new_game.rise_to_power
81
+ result = 3
82
+ end
83
+ result.should == 2
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift 'lib'
2
+ require 'gem_of_thrones'
3
+
4
+ require 'memcached'
5
+ require 'active_support/cache'
6
+ require 'active_support/cache/libmemcached_store'
7
+
8
+ # needed by ActiveSupport::Cache::Store
9
+ class String
10
+ def duplicable?
11
+ true
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gem_of_thrones
3
+ version: !ruby/object:Gem::Version
4
+ hash: 21
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 1
10
+ version: 0.2.1
11
+ platform: ruby
12
+ authors:
13
+ - Michael Grosser
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-09 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description:
22
+ email: michael@grosser.it
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - .travis.yml
31
+ - Gemfile
32
+ - Gemfile.lock
33
+ - Rakefile
34
+ - Readme.md
35
+ - gem_of_thrones.gemspec
36
+ - lib/gem_of_thrones.rb
37
+ - lib/gem_of_thrones/version.rb
38
+ - spec/gem_of_thrones_spec.rb
39
+ - spec/spec_helper.rb
40
+ homepage: http://github.com/grosser/gem_of_thrones
41
+ licenses:
42
+ - MIT
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ hash: 3
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.24
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Everybody wants to be king, but only one can win (synchronized via a distributed cache)
73
+ test_files: []
74
+