thread_so_safe 0.1.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.rdoc +122 -19
- data/Rakefile +4 -3
- data/lib/thread_so_safe.rb +106 -19
- data/spec/thread_so_safe_spec.rb +76 -30
- data/thread_so_safe.gemspec +8 -9
- metadata +14 -9
- data/.document +0 -5
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -1,36 +1,139 @@
|
|
1
1
|
= thread_so_safe
|
2
2
|
|
3
|
-
thread_so_safe is a very simple gem to help keep
|
3
|
+
+thread_so_safe+ is a very simple gem to help keep data accessed in multiple threads synced.
|
4
4
|
|
5
|
-
== Examples
|
6
5
|
|
7
|
-
|
6
|
+
== What is this thing?
|
7
|
+
|
8
|
+
Well the description pretty much says it all. +thread_so_safe+ is a gem to help keep data accessed in multiple threads synced.
|
9
|
+
|
10
|
+
|
11
|
+
== Why do I care?
|
12
|
+
|
13
|
+
That's a great question! Your data is important. If your data changes during the lifecycle of your application, your application may not run properly. Oh no! That sounds terrible! Luckily we can make our code smart enough that we don't have to worry about this.
|
14
|
+
|
15
|
+
|
16
|
+
== Are you reinventing the wheel?
|
17
|
+
|
18
|
+
Absolutely not! +thread_so_safe+ is not a wheel, but I might be reinventing a thread-safety mechanism. Some approaches to thread-safety lock the data from every other thread until the current thread is finished with the data. This has the potential to slow down your app quite a bit if you have many threads. Other approaches update the data found in each thread. This works when you know what you're looking for. +thread_so_safe+ gives you the means of checking, "has my data changed or is it still safe?" and, "I've changed my data, better send an update to any other threads that might be using this data."
|
19
|
+
|
20
|
+
|
21
|
+
== That sounds fancy, but how does it really work?
|
22
|
+
|
23
|
+
When you call +register_token+ it creates a file in +/tmp/thread_so_safe+. This file contains the +Time.now.to_i+ value at the time of creation. The time value is also stored in +ThreadSoSafe+'s memory. The +in_sync?+ method checks value stored in the +ThreadSoSafe+ object against the value in the file. If they match the thread is safe, but if they don't match +in_sync?+ will return false to indicating the thread isn't safe or synced anymore. The +update!+ and +reset!+ methods update the time value in the file.
|
24
|
+
|
25
|
+
At first I tried using only the timestamp of the file, but file timestamps only record hours, minutes and seconds. A lot can happen to a data source in one second. It's because of this that I decided to store the +Time.now.to_i+ value. +Time.now+ returns a time value with much greater detail than jus seconds, so it looked like a good idea.
|
26
|
+
|
27
|
+
|
28
|
+
== Example time
|
29
|
+
|
30
|
+
# It's time to register your application or data with ThreadSoSafe!
|
31
|
+
# Just pass in a unique string to the register_token method to generate
|
32
|
+
# a new thread-safety token.
|
33
|
+
ThreadSoSafe.register_token('My.App Users')
|
34
|
+
|
35
|
+
# Great! Now lets capture our data
|
8
36
|
users = User.find(:all)
|
9
|
-
|
37
|
+
|
38
|
+
# Lets make a change to our data set
|
39
|
+
users.last.destroy
|
40
|
+
|
41
|
+
# Our data has changed so let's send an update to ThreadSoSafe so that
|
42
|
+
# any other piece of code using ThreadSoSafe.register_token('My.App Users')
|
43
|
+
# will know the data has changed.
|
44
|
+
ThreadSoSafe.update!
|
45
|
+
|
10
46
|
# ...
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
47
|
+
|
48
|
+
# Well we've changed the data, but as anyone else in the meanwhile?
|
49
|
+
# We can use the in_sync? method to check.
|
50
|
+
if ThreadSoSafe.in_sync?
|
51
|
+
puts "The data is great! Keep going!"
|
52
|
+
# ...
|
15
53
|
end
|
16
|
-
|
54
|
+
|
17
55
|
###
|
18
|
-
|
56
|
+
|
19
57
|
# if you're working with multiple data sets that need to be in sync
|
20
58
|
# you can pass the name (in the first example's case, My.App Users)
|
21
|
-
# into the
|
22
|
-
|
23
|
-
ThreadSoSafe.
|
59
|
+
# into the in_sync? and update!
|
60
|
+
|
61
|
+
ThreadSoSafe.register_token('Users')
|
24
62
|
users = User.find(:all)
|
25
|
-
|
26
|
-
ThreadSoSafe.
|
63
|
+
|
64
|
+
ThreadSoSafe.register_token('Roles')
|
27
65
|
roles = Role.find(:all)
|
28
|
-
|
66
|
+
|
29
67
|
# ...
|
30
|
-
|
31
|
-
|
68
|
+
|
69
|
+
# Notice we're calling in_sync? but with a string now
|
70
|
+
unless ThreadSoSafe.in_sync?('Roles')
|
32
71
|
users = User.find(:all)
|
72
|
+
|
73
|
+
# Also notice we're calling update!, but with a string as well.
|
33
74
|
ThreadSoSafe.update!('Roles)
|
34
|
-
end
|
75
|
+
end
|
76
|
+
|
77
|
+
###
|
78
|
+
|
79
|
+
# We can notify other threads that data has changed with update!,
|
80
|
+
# but that updates our thread token as well. I've hit a case where
|
81
|
+
# I want to notify other threads that my data has changed AND force
|
82
|
+
# a reload of data in my current thread. This is why I made a reset!
|
83
|
+
# method. Calling reset! updates the token that all threads reference,
|
84
|
+
# but does not update the token stored in the gem. This results in
|
85
|
+
# in_sync? returning false and your code can pull the appropriate data
|
86
|
+
# and call update! afterwards to re-sync the threads.
|
87
|
+
|
88
|
+
class Settings < ActiveRecord::Base
|
89
|
+
class << self
|
90
|
+
def [](key)
|
91
|
+
load_collection
|
92
|
+
@settings_collection[key]
|
93
|
+
end
|
94
|
+
|
95
|
+
def []=(key, value)
|
96
|
+
item = find_or_create_by(:key => key)
|
97
|
+
item.value = value
|
98
|
+
item.save!
|
99
|
+
|
100
|
+
ThreadSoSafe.reset!
|
101
|
+
# We've reset the token. Next time Settings[:key] is called the
|
102
|
+
# load_collection method will rebuild the @settings_collection
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
def load_collection
|
107
|
+
if @settings_collection.nil?
|
108
|
+
# Our @settings_collection variable doesn't exist so register your token
|
109
|
+
ThreadSoSafe.register_token('MyAppSettings')
|
110
|
+
elsif ThreadSoSafe.in_sync?
|
111
|
+
# If our token is already registered and we're in-sync just return and
|
112
|
+
# don't bother running the rest of the code
|
113
|
+
return
|
114
|
+
end
|
115
|
+
|
116
|
+
# We've just registered our token or we're not in-sync anymore. I guess
|
117
|
+
# we had better reload our data
|
118
|
+
|
119
|
+
@settings_collection = {}
|
120
|
+
|
121
|
+
all.each { |s| @settings_collection[s.key] = s.value }
|
122
|
+
|
123
|
+
# We reloaded our data so lets inform the thread that we're re-synced
|
124
|
+
ThreadSoSafe.update!
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
== So... I have to use this in my app?
|
130
|
+
|
131
|
+
Yes. +thread_so_safe+ is just the mechanism used to keep track and inform whether or not data has changed. If have App1 using +thread_so_safe+, but App2 doesn't -- *and* manipulates the same data, your data wont be in sync. You'll need to use +thread_so_safe+ with both App1 and App2.
|
132
|
+
|
133
|
+
|
134
|
+
== Questions/Comments
|
135
|
+
|
136
|
+
Feel free to send me a message on Github or on Twitter (@daneharrigan). Thanks!
|
137
|
+
|
35
138
|
|
36
139
|
Copyright (c) 2010 Dane Harrigan. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
3
|
require 'spec/rake/spectask'
|
4
|
+
require 'sdoc'
|
4
5
|
|
5
6
|
begin
|
6
7
|
require 'jeweler'
|
7
8
|
Jeweler::Tasks.new do |gem|
|
8
9
|
gem.name = "thread_so_safe"
|
9
|
-
gem.version = "0.
|
10
|
+
gem.version = "0.2"
|
10
11
|
gem.summary = %Q{thread_so_safe is a very simple gem to help keep multi-threaded environments synced.}
|
11
12
|
gem.description = gem.summary
|
12
13
|
gem.email = "dane.harrigan@gmail.com"
|
13
14
|
gem.homepage = "http://github.com/daneharrigan/thread_so_safe"
|
14
15
|
gem.authors = ["Dane Harrigan"]
|
15
|
-
gem.add_development_dependency "rspec", ">= 1.
|
16
|
+
gem.add_development_dependency "rspec", ">= 1.3.0"
|
16
17
|
end
|
17
18
|
Jeweler::GemcutterTasks.new
|
18
19
|
rescue LoadError
|
@@ -43,4 +44,4 @@ Rake::RDocTask.new do |rdoc|
|
|
43
44
|
rdoc.title = "thread_so_safe #{version}"
|
44
45
|
rdoc.rdoc_files.include('README*')
|
45
46
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
-
end
|
47
|
+
end
|
data/lib/thread_so_safe.rb
CHANGED
@@ -3,32 +3,114 @@ require 'fileutils'
|
|
3
3
|
|
4
4
|
class ThreadSoSafe
|
5
5
|
@@threads = {}
|
6
|
-
@@
|
6
|
+
@@current_token = nil
|
7
7
|
|
8
8
|
class << self
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
# register_token - This method creates and/or resumes existing ThreadSoSafe
|
10
|
+
# sessions. register_token accepts one parameter as a string. Multiple tokens can
|
11
|
+
# be registered ThreadSoSafe but only one at a time. The last registered_token is
|
12
|
+
# stored in @@current_token. Storing the last token allows for later methods to
|
13
|
+
# process without needing the token passed.
|
14
|
+
#
|
15
|
+
# ==== Example
|
16
|
+
# ThreadSoSafe.register_token('My.Application')
|
17
|
+
# ThreadSoSafe.register_token('My.Application Users')
|
18
|
+
def register_token(name)
|
19
|
+
@@current_token = name
|
20
|
+
token = file_name(name)
|
21
|
+
@@threads[token] = set_timestamp(token)
|
16
22
|
return
|
17
23
|
end
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
25
|
+
# in_sync? - This method returns a boolean value indicating whether or not the
|
26
|
+
# current thread is in sync with any other thread/application using the same
|
27
|
+
# token. in_sync? accepts a token, but isn't necessary if there is only one
|
28
|
+
# token registered in your application. If you have multiple tokens it's
|
29
|
+
# necessary to pass the token when checking if your thread is in-sync.
|
30
|
+
#
|
31
|
+
# ==== Example
|
32
|
+
# if ThreadSoSafe.in_sync?
|
33
|
+
# puts "Good thing I'm the only token registered!"
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# # with multiple tokens
|
37
|
+
# if ThreadSoSafe.in_sync?('My.Application')
|
38
|
+
# puts "We're still synced"
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# if ThreadSoSafe.in_sync?('My.Application Users')
|
42
|
+
# puts "Users are synced as well"
|
43
|
+
# end
|
44
|
+
def in_sync?(name=@@current_token)
|
45
|
+
token = file_name(name)
|
46
|
+
|
47
|
+
@@threads[token] == File.read(full_path(token))
|
48
|
+
end
|
22
49
|
|
23
|
-
|
50
|
+
# update! - This method updates the ThreadSoSafe session. Aftering being called,
|
51
|
+
# the in_sync? method will return true on the current thread, but false on any
|
52
|
+
# other thread until update! is called on it.
|
53
|
+
#
|
54
|
+
# ==== Example
|
55
|
+
# # thread 1
|
56
|
+
# ThreadSoSafe.register_token('My.Application Users')
|
57
|
+
# users = User.find(:all)
|
58
|
+
# users.last.destroy
|
59
|
+
# ThreadSoSafe.update!
|
60
|
+
#
|
61
|
+
# if ThreadSoSafe.in_sync?
|
62
|
+
# puts "Data is in sync"
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# ####
|
66
|
+
#
|
67
|
+
# # thread 2
|
68
|
+
# unless ThreadSoSafe.in_sync?
|
69
|
+
# puts "Data is out of sync"
|
70
|
+
# users = User.find(:all)
|
71
|
+
# ThreadSoSafe.update!
|
72
|
+
# end
|
73
|
+
def update!(name=@@current_token)
|
74
|
+
token = file_name(name)
|
75
|
+
file_content = File.read full_path(token)
|
76
|
+
if @@threads[token] == file_content
|
77
|
+
@@threads[token] = set_timestamp(token)
|
78
|
+
else
|
79
|
+
@@threads[token] = file_content
|
80
|
+
end
|
81
|
+
return
|
24
82
|
end
|
25
83
|
|
26
|
-
|
27
|
-
name = file_name(name)
|
28
|
-
file = full_path(name)
|
84
|
+
# Update token only and notify other threads
|
29
85
|
|
30
|
-
|
31
|
-
|
86
|
+
# reset! - This method updates the ThreadSoSafe session. Every thread, including
|
87
|
+
# the current, is notified of the change. This notification causes the in_sync?
|
88
|
+
# method returns false until update! is called.
|
89
|
+
#
|
90
|
+
# ==== Example
|
91
|
+
# # thread 1
|
92
|
+
# ThreadSoSafe.register_token('My.Application Users')
|
93
|
+
# users = User.find(:all)
|
94
|
+
# users.last.destroy
|
95
|
+
# ThreadSoSafe.reset!
|
96
|
+
#
|
97
|
+
# unless ThreadSoSafe.in_sync?
|
98
|
+
# puts "Data needs to be recaptured"
|
99
|
+
# users = User.find(:all)
|
100
|
+
# ThreadSoSafe.update!
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# ####
|
104
|
+
#
|
105
|
+
# # thread 2
|
106
|
+
# unless ThreadSoSafe.in_sync?
|
107
|
+
# puts "Data is out of sync"
|
108
|
+
# users = User.find(:all)
|
109
|
+
# ThreadSoSafe.update!
|
110
|
+
# end
|
111
|
+
def reset!(name=@@current_token)
|
112
|
+
set_timestamp file_name(name)
|
113
|
+
return
|
32
114
|
end
|
33
115
|
|
34
116
|
private
|
@@ -45,17 +127,22 @@ class ThreadSoSafe
|
|
45
127
|
end
|
46
128
|
|
47
129
|
def use_default_directory?
|
130
|
+
FileUtils.mkdir(default_directory) unless File.exists?(default_directory)
|
48
131
|
File.writable?(default_directory)
|
49
132
|
end
|
50
133
|
|
51
|
-
#:nordoc:
|
52
134
|
def default_directory
|
53
135
|
'/tmp/thread_so_safe'
|
54
136
|
end
|
55
137
|
|
56
|
-
#:nordoc:
|
57
138
|
def gem_directory
|
58
139
|
File.expand_path(File.dirname(__FILE__)+'/../tmp')
|
59
140
|
end
|
141
|
+
|
142
|
+
def set_timestamp(token)
|
143
|
+
timestamp = Time.now.to_f.to_s
|
144
|
+
File.open(full_path(token), 'w+') { |f| f.write timestamp }
|
145
|
+
timestamp
|
146
|
+
end
|
60
147
|
end
|
61
148
|
end
|
data/spec/thread_so_safe_spec.rb
CHANGED
@@ -2,22 +2,33 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe ThreadSoSafe do
|
4
4
|
before(:all) do
|
5
|
-
@
|
5
|
+
@token = 'My.Application'
|
6
6
|
@default_directory = '/tmp/thread_so_safe'
|
7
|
+
|
8
|
+
ThreadSoSafe.register_token(@token)
|
9
|
+
end
|
10
|
+
|
11
|
+
# :all works differently in rspec 1.3.0 and 2.0.
|
12
|
+
# :suite runs after everything is done in 2.0 like :all did in 1.3.0
|
13
|
+
# The Spec namespace has also been renamed to RSpec so I'm looking
|
14
|
+
# for Spec and if it doesn't exist then it must be RSpec 2
|
15
|
+
after(defined?(RSpec) ? :suite : :all) do
|
16
|
+
gem_directory = ThreadSoSafe.send(:gem_directory)
|
17
|
+
|
18
|
+
FileUtils.rm_rf(@default_directory) if File.exists?(@default_directory)
|
19
|
+
FileUtils.rm_rf("#{gem_directory}/*")
|
7
20
|
end
|
8
21
|
|
9
|
-
it "should be
|
10
|
-
ThreadSoSafe.
|
11
|
-
ThreadSoSafe.safe?.should == true
|
22
|
+
it "should be in-sync without passing the token" do
|
23
|
+
ThreadSoSafe.in_sync?.should == true
|
12
24
|
end
|
13
25
|
|
14
|
-
it "should be
|
15
|
-
ThreadSoSafe.
|
16
|
-
ThreadSoSafe.safe?(@app_name).should == true
|
26
|
+
it "should be in-sync with the token passed" do
|
27
|
+
ThreadSoSafe.in_sync?(@token).should == true
|
17
28
|
end
|
18
29
|
|
19
|
-
it "should encode the
|
20
|
-
ThreadSoSafe.send(:file_name,@
|
30
|
+
it "should encode the token with md5" do
|
31
|
+
ThreadSoSafe.send(:file_name,@token).should == Digest::MD5.hexdigest(@token)
|
21
32
|
end
|
22
33
|
|
23
34
|
it "should return /tmp/thread_so_safe as the default directory" do
|
@@ -35,47 +46,82 @@ describe ThreadSoSafe do
|
|
35
46
|
end
|
36
47
|
|
37
48
|
it "should return the full path to the /tmp file" do
|
38
|
-
file_name = ThreadSoSafe.send(:file_name, @
|
49
|
+
file_name = ThreadSoSafe.send(:file_name, @token)
|
39
50
|
ThreadSoSafe.send(:full_path, file_name).should == "#{@default_directory}/#{file_name}"
|
40
51
|
end
|
41
52
|
|
42
|
-
context "when
|
53
|
+
context "when updating the thread-safe token" do
|
43
54
|
before(:each) do
|
44
|
-
ThreadSoSafe.
|
55
|
+
file_name = ThreadSoSafe.send(:file_name, @token)
|
56
|
+
@full_path = ThreadSoSafe.send(:full_path, file_name)
|
57
|
+
@file_content = File.read(@full_path)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should update the file content in on the /tmp file with the token passed" do
|
61
|
+
ThreadSoSafe.update!(@token)
|
62
|
+
File.read(@full_path).should_not == @file_content
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should update the file content on the /tmp file without the token passed" do
|
66
|
+
ThreadSoSafe.update!
|
67
|
+
File.read(@full_path).should_not == @file_content
|
68
|
+
end
|
45
69
|
|
46
|
-
|
70
|
+
context "when the remote token is udpated by another thread" do
|
71
|
+
before(:each) do
|
72
|
+
File.open(@full_path, 'w') { |f| f.write(Time.now.to_i) }
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should use the token stored in the /tmp file" do
|
76
|
+
ThreadSoSafe.update!(@token)
|
77
|
+
ThreadSoSafe.in_sync?.should == true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when another thread updates the thread-safe token" do
|
83
|
+
before(:all) do
|
84
|
+
file_name = ThreadSoSafe.send(:file_name, @token)
|
47
85
|
full_path = ThreadSoSafe.send(:full_path, file_name)
|
48
86
|
|
49
|
-
File.
|
87
|
+
File.open(full_path, 'w') { |f| f.write(Time.now.to_f) }
|
50
88
|
end
|
51
89
|
|
52
|
-
it "should not be safe without passing the
|
53
|
-
ThreadSoSafe.
|
90
|
+
it "should not be safe without passing the token" do
|
91
|
+
ThreadSoSafe.in_sync?.should == false
|
54
92
|
end
|
55
93
|
|
56
|
-
it "should be safe with the
|
57
|
-
ThreadSoSafe.
|
94
|
+
it "should be safe with the token passed" do
|
95
|
+
ThreadSoSafe.in_sync?(@token).should == false
|
58
96
|
end
|
59
97
|
end
|
60
98
|
|
61
|
-
context "when
|
62
|
-
before(:
|
63
|
-
ThreadSoSafe.
|
64
|
-
file_name = ThreadSoSafe.send(:file_name, @app_name)
|
99
|
+
context "when resetting the thread-safe token" do
|
100
|
+
before(:all) do
|
101
|
+
file_name = ThreadSoSafe.send(:file_name, @token)
|
65
102
|
@full_path = ThreadSoSafe.send(:full_path, file_name)
|
103
|
+
end
|
66
104
|
|
67
|
-
|
68
|
-
|
105
|
+
it "should not be in sync without the token passed" do
|
106
|
+
ThreadSoSafe.reset!
|
107
|
+
ThreadSoSafe.in_sync?.should == false
|
69
108
|
end
|
70
109
|
|
71
|
-
it "should
|
72
|
-
ThreadSoSafe.
|
73
|
-
|
110
|
+
it "should not be in sync with the token passed" do
|
111
|
+
ThreadSoSafe.reset!(@token)
|
112
|
+
ThreadSoSafe.in_sync?.should == false
|
74
113
|
end
|
75
114
|
|
76
|
-
it "should update the
|
77
|
-
|
78
|
-
|
115
|
+
it "should update the file content on the /tmp file without the token passed" do
|
116
|
+
file_content = File.read(@full_path)
|
117
|
+
ThreadSoSafe.reset!
|
118
|
+
File.read(@full_path).should_not == file_content
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should update the file content on the /tmp file with the token passed" do
|
122
|
+
file_content = File.read(@full_path)
|
123
|
+
ThreadSoSafe.reset!(@token)
|
124
|
+
File.read(@full_path).should_not == file_content
|
79
125
|
end
|
80
126
|
end
|
81
127
|
|
data/thread_so_safe.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{thread_so_safe}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Dane Harrigan"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-07-04}
|
13
13
|
s.description = %q{thread_so_safe is a very simple gem to help keep multi-threaded environments synced.}
|
14
14
|
s.email = %q{dane.harrigan@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -17,8 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
"README.rdoc"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
|
-
".
|
21
|
-
".gitignore",
|
20
|
+
".gitignore",
|
22
21
|
"LICENSE",
|
23
22
|
"README.rdoc",
|
24
23
|
"Rakefile",
|
@@ -30,7 +29,7 @@ Gem::Specification.new do |s|
|
|
30
29
|
s.homepage = %q{http://github.com/daneharrigan/thread_so_safe}
|
31
30
|
s.rdoc_options = ["--charset=UTF-8"]
|
32
31
|
s.require_paths = ["lib"]
|
33
|
-
s.rubygems_version = %q{1.3.
|
32
|
+
s.rubygems_version = %q{1.3.7}
|
34
33
|
s.summary = %q{thread_so_safe is a very simple gem to help keep multi-threaded environments synced.}
|
35
34
|
s.test_files = [
|
36
35
|
"spec/spec_helper.rb",
|
@@ -41,13 +40,13 @@ Gem::Specification.new do |s|
|
|
41
40
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
42
41
|
s.specification_version = 3
|
43
42
|
|
44
|
-
if Gem::Version.new(Gem::
|
45
|
-
s.add_development_dependency(%q<rspec>, [">= 1.
|
43
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
44
|
+
s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
|
46
45
|
else
|
47
|
-
s.add_dependency(%q<rspec>, [">= 1.
|
46
|
+
s.add_dependency(%q<rspec>, [">= 1.3.0"])
|
48
47
|
end
|
49
48
|
else
|
50
|
-
s.add_dependency(%q<rspec>, [">= 1.
|
49
|
+
s.add_dependency(%q<rspec>, [">= 1.3.0"])
|
51
50
|
end
|
52
51
|
end
|
53
52
|
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thread_so_safe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
-
|
9
|
-
version: 0.1.1
|
8
|
+
- 2
|
9
|
+
version: "0.2"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Dane Harrigan
|
@@ -14,21 +14,23 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-07-04 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rspec
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
24
25
|
requirements:
|
25
26
|
- - ">="
|
26
27
|
- !ruby/object:Gem::Version
|
28
|
+
hash: 27
|
27
29
|
segments:
|
28
30
|
- 1
|
29
|
-
-
|
30
|
-
-
|
31
|
-
version: 1.
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
version: 1.3.0
|
32
34
|
type: :development
|
33
35
|
version_requirements: *id001
|
34
36
|
description: thread_so_safe is a very simple gem to help keep multi-threaded environments synced.
|
@@ -41,7 +43,6 @@ extra_rdoc_files:
|
|
41
43
|
- LICENSE
|
42
44
|
- README.rdoc
|
43
45
|
files:
|
44
|
-
- .document
|
45
46
|
- .gitignore
|
46
47
|
- LICENSE
|
47
48
|
- README.rdoc
|
@@ -60,23 +61,27 @@ rdoc_options:
|
|
60
61
|
require_paths:
|
61
62
|
- lib
|
62
63
|
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
63
65
|
requirements:
|
64
66
|
- - ">="
|
65
67
|
- !ruby/object:Gem::Version
|
68
|
+
hash: 3
|
66
69
|
segments:
|
67
70
|
- 0
|
68
71
|
version: "0"
|
69
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
70
74
|
requirements:
|
71
75
|
- - ">="
|
72
76
|
- !ruby/object:Gem::Version
|
77
|
+
hash: 3
|
73
78
|
segments:
|
74
79
|
- 0
|
75
80
|
version: "0"
|
76
81
|
requirements: []
|
77
82
|
|
78
83
|
rubyforge_project:
|
79
|
-
rubygems_version: 1.3.
|
84
|
+
rubygems_version: 1.3.7
|
80
85
|
signing_key:
|
81
86
|
specification_version: 3
|
82
87
|
summary: thread_so_safe is a very simple gem to help keep multi-threaded environments synced.
|