pingpongpear 1.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 398675b7d66d9103e11e13a4c5e16d6dc4084dac
4
+ data.tar.gz: ebe8152549dae3433f437f563af4f6abe22b49b6
5
+ SHA512:
6
+ metadata.gz: 2794b44623036b5b525c332fe52e676e9b751585c0406ba63c77fb5fcc9b712b7956131114c00a12794a4de425be655a144b8202dc7a2d39e95a20a496a4a9bb
7
+ data.tar.gz: b61c396a9d1fd66ca26d9604e3f508f5969e7377c671378b690c0ea1abe72140d09c01fc5755511fd2e1e0a5baaca80f655a4ebf1d660d4d1fb66d9e5323de53
data/.autotest ADDED
@@ -0,0 +1,8 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ Autotest.add_hook :initialize do |at|
6
+ at.testlib = 'minitest/autorun'
7
+ at.find_directories = ARGV unless ARGV.empty?
8
+ end
data/.gemtest ADDED
File without changes
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2015-01-03
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ .autotest
2
+ CHANGELOG.rdoc
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/pingpongpear
7
+ lib/ping_pong_pear.rb
8
+ test/test_ping_pong_pear.rb
data/README.rdoc ADDED
@@ -0,0 +1,52 @@
1
+ = ping_pong_pear
2
+
3
+ * http://github.com/tenderlove/pingpongpear
4
+
5
+ == DESCRIPTION:
6
+
7
+ It is for ping pong pearing
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * many
12
+
13
+ == SYNOPSIS:
14
+
15
+ $ pingpongpear start team_fun
16
+
17
+ Other machine:
18
+
19
+ $ pingpongpear clone team_fun
20
+ $ cd team_fun
21
+ $ pingpongpear start
22
+
23
+ Then start pairing
24
+
25
+ == INSTALL:
26
+
27
+ * gem install pingpongpear
28
+
29
+ == LICENSE:
30
+
31
+ (The MIT License)
32
+
33
+ Copyright (c) 2015 Aaron Patterson
34
+
35
+ Permission is hereby granted, free of charge, to any person obtaining
36
+ a copy of this software and associated documentation files (the
37
+ 'Software'), to deal in the Software without restriction, including
38
+ without limitation the rights to use, copy, modify, merge, publish,
39
+ distribute, sublicense, and/or sell copies of the Software, and to
40
+ permit persons to whom the Software is furnished to do so, subject to
41
+ the following conditions:
42
+
43
+ The above copyright notice and this permission notice shall be
44
+ included in all copies or substantial portions of the Software.
45
+
46
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
47
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
48
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
49
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
50
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
51
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
52
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.plugins.delete :rubyforge
7
+ Hoe.plugin :minitest
8
+ Hoe.plugin :gemspec # `gem install hoe-gemspec`
9
+ Hoe.plugin :git # `gem install hoe-git`
10
+
11
+ Hoe.spec 'pingpongpear' do
12
+ developer('Aaron Patterson', 'aaron@tenderlovemaking.com')
13
+ self.readme_file = 'README.rdoc'
14
+ self.history_file = 'CHANGELOG.rdoc'
15
+ self.extra_rdoc_files = FileList['*.rdoc']
16
+ end
17
+
18
+ # vim: syntax=ruby
data/bin/pingpongpear ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ping_pong_pear'
4
+ PingPongPear::Commands.run ARGV
@@ -0,0 +1,143 @@
1
+ require 'socket'
2
+ require 'ipaddr'
3
+ require 'json'
4
+
5
+ module PingPongPear
6
+ VERSION = '1.0.0'
7
+ UDP_PORT = 4545
8
+ MULTICAST_ADDR = "224.0.0.1"
9
+
10
+ class Broadcaster
11
+ def self.my_public_address
12
+ Socket.ip_address_list.reject { |addr|
13
+ addr.ipv4_loopback? || addr.ipv6_loopback? || addr.ipv6_linklocal?
14
+ }.first
15
+ end
16
+
17
+ def self.send_update args
18
+ new.send ['commit'] + args
19
+ end
20
+
21
+ def self.send_location name, address, port
22
+ new.send ['source', name, address, port]
23
+ end
24
+
25
+ def self.where_is? name
26
+ listener_t = Thread.new {
27
+ listener = Listener.new
28
+ listener.start { |cmd, n, *rest|
29
+ break(rest) if cmd == 'source' && n == name
30
+ }
31
+ }
32
+ broadcast = new
33
+ while listener_t.alive?
34
+ broadcast.send ['locate', name]
35
+ end
36
+ addr, port = listener_t.value
37
+ "http://#{addr}:#{port}/"
38
+ end
39
+
40
+ def initialize
41
+ @multicast_addr = MULTICAST_ADDR
42
+ @port = UDP_PORT
43
+ @socket = UDPSocket.open
44
+ @socket.setsockopt :IPPROTO_IP, :IP_MULTICAST_TTL, 1
45
+ end
46
+
47
+ def send message
48
+ @socket.send(JSON.dump(message), 0, @multicast_addr, @port)
49
+ end
50
+ end
51
+
52
+ class Listener
53
+ def initialize
54
+ @multicast_addr = MULTICAST_ADDR
55
+ @bind_addr = "0.0.0.0"
56
+ @port = UDP_PORT
57
+
58
+ @socket = UDPSocket.new
59
+ membership = IPAddr.new(@multicast_addr).hton + IPAddr.new(@bind_addr).hton
60
+
61
+ @socket.setsockopt :IPPROTO_IP, :IP_ADD_MEMBERSHIP, membership
62
+ @socket.setsockopt :SOL_SOCKET, :SO_REUSEPORT, 1
63
+
64
+ @socket.bind @bind_addr, @port
65
+ end
66
+
67
+ def start
68
+ loop do
69
+ message, _ = @socket.recvfrom 1024
70
+ yield JSON.parse(message)
71
+ end
72
+ end
73
+ end
74
+
75
+ module Commands
76
+ def self.start args
77
+ require 'webrick'
78
+ require 'securerandom'
79
+ require 'shellwords'
80
+
81
+ project_name = args.first || File.basename(Dir.pwd)
82
+ post_commit_hook = '.git/hooks/post-commit'
83
+ uuid = SecureRandom.uuid
84
+
85
+ system 'git update-server-info'
86
+
87
+ server = WEBrick::HTTPServer.new Port: 0, DocumentRoot: '.git'
88
+ http_port = server.listeners.map { |x| x.addr[1] }.first
89
+ my_address = Broadcaster.my_public_address.ip_address
90
+
91
+ File.open(post_commit_hook, 'w') { |f|
92
+ f.puts "#!/usr/bin/env ruby"
93
+ f.puts "ARGV[0] = 'update'"
94
+ f.puts "ARGV[1] = '#{project_name}'"
95
+ f.puts "ARGV[2] = '#{uuid}'"
96
+ f.puts "ARGV[3] = '#{my_address}'"
97
+ f.puts "ARGV[4] = '#{http_port}'"
98
+ f.write File.read __FILE__
99
+ }
100
+ File.chmod 0755, post_commit_hook
101
+
102
+ Thread.new {
103
+ listener = PingPongPear::Listener.new
104
+ listener.start { |cmd, name, *rest|
105
+ next unless name == project_name
106
+
107
+ case cmd
108
+ when 'locate' then Broadcaster.send_location project_name, my_address, http_port
109
+ when 'commit'
110
+ unless rest.first == uuid
111
+ url = "http://#{rest.drop(1).join(':')}"
112
+ system "git pull #{Shellwords.escape(url)}"
113
+ end
114
+ end
115
+ }
116
+ }
117
+
118
+ trap('INT') {
119
+ File.unlink post_commit_hook
120
+ server.shutdown
121
+ }
122
+ server.start
123
+ end
124
+
125
+ def self.clone args
126
+ require 'shellwords'
127
+ name = args.first
128
+ url = PingPongPear::Broadcaster.where_is? name
129
+ system "git clone #{Shellwords.escape(url)} #{Shellwords.escape(name)}"
130
+ end
131
+
132
+ def self.update args
133
+ system 'git update-server-info'
134
+ PingPongPear::Broadcaster.send_update args
135
+ end
136
+
137
+ def self.run args
138
+ send args.first, args.drop(1)
139
+ end
140
+ end
141
+ end
142
+
143
+ PingPongPear::Commands.run(ARGV) if $0 == __FILE__
@@ -0,0 +1,247 @@
1
+ require 'minitest/autorun'
2
+ require 'ping_pong_pear'
3
+ require 'tmpdir'
4
+
5
+ class TestPingPongPear < MiniTest::Test
6
+ def setup
7
+ super
8
+ @current_dir = Dir.pwd
9
+ Dir.chdir Dir.mktmpdir
10
+ end
11
+
12
+ def teardown
13
+ Dir.chdir @current_dir
14
+ super
15
+ end
16
+
17
+ def test_boot
18
+ system 'git init'
19
+ stderr_rd, stderr_wr = IO.pipe
20
+ pid = fork {
21
+ $stderr.reopen stderr_wr
22
+ PingPongPear::Commands.run ['start']
23
+ }
24
+ stderr_wr.close
25
+ loop do
26
+ line = stderr_rd.readline
27
+ break if line =~ /WEBrick::HTTPServer#start/
28
+ end
29
+ assert File.exist? '.git/hooks/post-commit'
30
+ ensure
31
+ Process.kill 'INT', pid
32
+ Process.waitpid pid
33
+ end
34
+
35
+ def test_shutdown
36
+ system 'git init'
37
+ stderr_rd, stderr_wr = IO.pipe
38
+ pid = fork {
39
+ $stderr.reopen stderr_wr
40
+ PingPongPear::Commands.run ['start']
41
+ }
42
+ stderr_wr.close
43
+ loop do
44
+ line = stderr_rd.readline
45
+ break if line =~ /WEBrick::HTTPServer#start/
46
+ end
47
+ assert File.exist? '.git/hooks/post-commit'
48
+ Process.kill 'INT', pid
49
+ Process.waitpid pid
50
+ refute File.exist? '.git/hooks/post-commit'
51
+ end
52
+
53
+ def test_clone
54
+ system 'git init'
55
+ File.write 'out.txt', 'aset'
56
+ system 'git add out.txt'
57
+ system 'git commit -m"xx"'
58
+
59
+ project_name = File.basename Dir.pwd
60
+ stderr_rd, stderr_wr = IO.pipe
61
+ pid = fork {
62
+ $stderr.reopen stderr_wr
63
+ PingPongPear::Commands.run ['start']
64
+ }
65
+ stderr_wr.close
66
+ loop do
67
+ line = stderr_rd.readline
68
+ break if line =~ /WEBrick::HTTPServer#start/
69
+ end
70
+
71
+ Dir.chdir Dir.mktmpdir
72
+ PingPongPear::Commands.run ['clone', project_name]
73
+
74
+ assert File.exist?(project_name), 'should be cloned'
75
+ assert File.exist? File.join(project_name, 'out.txt')
76
+ ensure
77
+ Process.kill 'INT', pid
78
+ Process.waitpid pid
79
+ end
80
+
81
+ def boot
82
+ stderr_rd, stderr_wr = IO.pipe
83
+ stdout_rd, stdout_wr = IO.pipe
84
+ pid = fork {
85
+ $stderr.reopen stderr_wr
86
+ $stdout.reopen stdout_wr
87
+ PingPongPear::Commands.run ['start']
88
+ }
89
+ stderr_wr.close
90
+ stdout_wr.close
91
+ loop do
92
+ line = stderr_rd.readline
93
+ break if line =~ /WEBrick::HTTPServer#start/
94
+ end
95
+ [pid, stderr_rd, stdout_rd]
96
+ end
97
+
98
+ def test_commit
99
+ system 'git init'
100
+ File.write 'out.txt', 'aset'
101
+ system 'git add out.txt'
102
+ system 'git commit -m"xx"'
103
+
104
+ project_name = File.basename Dir.pwd
105
+ pid, err, out = boot
106
+
107
+ original_dir = File.expand_path Dir.pwd
108
+ Dir.chdir Dir.mktmpdir
109
+ PingPongPear::Commands.run ['clone', project_name]
110
+ pid2 = nil
111
+ Dir.chdir(project_name) do
112
+ pid2, = boot
113
+
114
+ File.write 'out2.txt', 'ddddd'
115
+ system 'git add out2.txt'
116
+ system 'git commit -m"xx"'
117
+ end
118
+ require 'timeout'
119
+ Timeout.timeout(2) {
120
+ loop do
121
+ break if File.exist? File.join(original_dir, 'out2.txt')
122
+ end
123
+ }
124
+ ensure
125
+ Process.kill 'INT', pid
126
+ Process.waitpid pid
127
+ Process.kill 'INT', pid2
128
+ Process.waitpid pid2
129
+ end
130
+
131
+ def test_commit_works_on_branches
132
+ system 'git init'
133
+ File.write 'out.txt', 'aset'
134
+ system 'git checkout -b xxx'
135
+ system 'git add out.txt'
136
+ system 'git commit -m"xx"'
137
+
138
+ project_name = File.basename Dir.pwd
139
+ stderr_rd, stderr_wr = IO.pipe
140
+ pid = fork {
141
+ $stderr.reopen stderr_wr
142
+ PingPongPear::Commands.run ['start']
143
+ }
144
+ stderr_wr.close
145
+ loop do
146
+ line = stderr_rd.readline
147
+ break if line =~ /WEBrick::HTTPServer#start/
148
+ end
149
+
150
+ original_dir = File.expand_path Dir.pwd
151
+ newdir = Dir.mktmpdir
152
+ Dir.chdir newdir
153
+ PingPongPear::Commands.run ['clone', project_name]
154
+ Dir.chdir project_name
155
+
156
+ pid2 = fork { PingPongPear::Commands.run ['start'] }
157
+ loop do
158
+ break if File.exist? '.git/hooks/post-commit'
159
+ end
160
+ File.write 'out2.txt', 'ddddd'
161
+ system 'git checkout xxx'
162
+ system 'git add out2.txt'
163
+ system 'git commit -m"xx"'
164
+ require 'timeout'
165
+ Timeout.timeout(2) {
166
+ loop do
167
+ break if File.exist? File.join(original_dir, 'out2.txt')
168
+ end
169
+ }
170
+ ensure
171
+ Process.kill 'INT', pid
172
+ Process.waitpid pid
173
+ Process.kill 'INT', pid2
174
+ Process.waitpid pid2
175
+ end
176
+
177
+ def test_clone_works_with_name
178
+ system 'git init'
179
+ File.write 'out.txt', 'aset'
180
+ system 'git add out.txt'
181
+ system 'git commit -m"xx"'
182
+
183
+ project_name = 'foo'
184
+ stderr_rd, stderr_wr = IO.pipe
185
+ pid = fork {
186
+ $stderr.reopen stderr_wr
187
+ PingPongPear::Commands.run ['start', project_name]
188
+ }
189
+ stderr_wr.close
190
+ loop do
191
+ line = stderr_rd.readline
192
+ break if line =~ /WEBrick::HTTPServer#start/
193
+ end
194
+
195
+ Dir.chdir Dir.mktmpdir
196
+ PingPongPear::Commands.run ['clone', project_name]
197
+
198
+ assert File.exist?(project_name), 'should be cloned'
199
+ assert File.exist? File.join(project_name, 'out.txt')
200
+ ensure
201
+ Process.kill 'INT', pid
202
+ Process.waitpid pid
203
+ end
204
+
205
+ def test_commit_works_with_name
206
+ system 'git init'
207
+ File.write 'out.txt', 'aset'
208
+ system 'git add out.txt'
209
+ system 'git commit -m"xx"'
210
+
211
+ project_name = 'foo'
212
+ stderr_rd, stderr_wr = IO.pipe
213
+ pid = fork {
214
+ $stderr.reopen stderr_wr
215
+ PingPongPear::Commands.run ['start', project_name]
216
+ }
217
+ stderr_wr.close
218
+ loop do
219
+ line = stderr_rd.readline
220
+ break if line =~ /WEBrick::HTTPServer#start/
221
+ end
222
+
223
+ original_dir = File.expand_path Dir.pwd
224
+ Dir.chdir Dir.mktmpdir
225
+ PingPongPear::Commands.run ['clone', project_name]
226
+ Dir.chdir project_name
227
+
228
+ pid2 = fork { PingPongPear::Commands.run ['start', project_name] }
229
+ loop do
230
+ break if File.exist? '.git/hooks/post-commit'
231
+ end
232
+ File.write 'out2.txt', 'ddddd'
233
+ system 'git add out2.txt'
234
+ system 'git commit -m"xx"'
235
+ require 'timeout'
236
+ Timeout.timeout(2) {
237
+ loop do
238
+ break if File.exist? File.join(original_dir, 'out2.txt')
239
+ end
240
+ }
241
+ ensure
242
+ Process.kill 'INT', pid
243
+ Process.waitpid pid
244
+ Process.kill 'INT', pid2
245
+ Process.waitpid pid2
246
+ end
247
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pingpongpear
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Patterson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rdoc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: hoe
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.13'
55
+ description: It is for ping pong pearing
56
+ email:
57
+ - aaron@tenderlovemaking.com
58
+ executables:
59
+ - pingpongpear
60
+ extensions: []
61
+ extra_rdoc_files:
62
+ - CHANGELOG.rdoc
63
+ - Manifest.txt
64
+ - README.rdoc
65
+ files:
66
+ - ".autotest"
67
+ - ".gemtest"
68
+ - CHANGELOG.rdoc
69
+ - Manifest.txt
70
+ - README.rdoc
71
+ - Rakefile
72
+ - bin/pingpongpear
73
+ - lib/ping_pong_pear.rb
74
+ - test/test_ping_pong_pear.rb
75
+ homepage: http://github.com/tenderlove/pingpongpear
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options:
81
+ - "--main"
82
+ - README.rdoc
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.4.5
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: It is for ping pong pearing
101
+ test_files:
102
+ - test/test_ping_pong_pear.rb