gem_of_thrones 0.2.1

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.
@@ -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
+