git_multicast 0.1.0 → 0.2.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 +4 -4
- data/Gemfile.lock +17 -1
- data/README.md +1 -1
- data/git_multicast.gemspec +1 -0
- data/lib/git_multicast.rb +3 -8
- data/lib/git_multicast/adapter.rb +2 -0
- data/lib/git_multicast/{adapters → adapter}/bitbucket.rb +1 -1
- data/lib/git_multicast/{adapters → adapter}/github.rb +1 -1
- data/lib/git_multicast/cli.rb +38 -6
- data/lib/git_multicast/formatter.rb +6 -0
- data/lib/git_multicast/formatter/full.rb +18 -0
- data/lib/git_multicast/formatter/quiet.rb +13 -0
- data/lib/git_multicast/formatter/standard.rb +29 -0
- data/lib/git_multicast/formatter/status.rb +17 -0
- data/lib/git_multicast/multicaster.rb +31 -0
- data/lib/git_multicast/multicaster/clone.rb +35 -0
- data/lib/git_multicast/multicaster/pull.rb +30 -0
- data/lib/git_multicast/multicaster/status.rb +30 -0
- data/lib/git_multicast/repository_fetcher.rb +5 -2
- data/lib/git_multicast/task.rb +5 -3
- data/lib/git_multicast/task/result.rb +13 -0
- data/lib/git_multicast/task/runner.rb +38 -0
- data/lib/git_multicast/version.rb +1 -1
- data/spec/git_multicast/{adapters → adapter}/bitbucket_spec.rb +7 -3
- data/spec/git_multicast/cli_spec.rb +3 -3
- data/spec/git_multicast/formatter/full_spec.rb +32 -0
- data/spec/git_multicast/formatter/quiet_spec.rb +30 -0
- data/spec/git_multicast/formatter/standard_spec.rb +31 -0
- data/spec/git_multicast/formatter/status_spec.rb +32 -0
- data/spec/git_multicast/multicaster/clone_spec.rb +97 -0
- data/spec/git_multicast/multicaster/pull_spec.rb +41 -0
- data/spec/git_multicast/multicaster/status_spec.rb +41 -0
- data/spec/git_multicast/repository_fetcher_spec.rb +3 -3
- data/spec/git_multicast/task/runner_spec.rb +44 -0
- data/spec/git_multicast/task_spec.rb +8 -5
- metadata +39 -18
- data/lib/git_multicast/.rubocop.yml +0 -11
- data/lib/git_multicast/adapters.rb +0 -7
- data/lib/git_multicast/cloner.rb +0 -36
- data/lib/git_multicast/output_formatter.rb +0 -30
- data/lib/git_multicast/puller.rb +0 -28
- data/lib/git_multicast/statuser.rb +0 -28
- data/lib/git_multicast/task_result.rb +0 -7
- data/lib/git_multicast/task_runner.rb +0 -40
- data/spec/git_multicast/cloner_spec.rb +0 -94
- data/spec/git_multicast/output_formatter_spec.rb +0 -30
- data/spec/git_multicast/puller_spec.rb +0 -39
- data/spec/git_multicast/statuser_spec.rb +0 -39
- data/spec/git_multicast/task_runner_spec.rb +0 -53
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module GitMulticast
|
|
2
|
+
class Task
|
|
3
|
+
describe Runner do
|
|
4
|
+
subject(:runner) { described_class.new(tasks) }
|
|
5
|
+
|
|
6
|
+
let(:tasks) { [hey_task, ho_task] }
|
|
7
|
+
|
|
8
|
+
# Since tasks quack like lambdas, I will be using them here.
|
|
9
|
+
let(:hey_task) { ->() { first_result } }
|
|
10
|
+
let(:ho_task) { ->() { second_result } }
|
|
11
|
+
|
|
12
|
+
let!(:first_result) { Task::Result.new(:one, 'Hey!', 1) }
|
|
13
|
+
let!(:second_result) { Task::Result.new(:two, 'Ho!', 1) }
|
|
14
|
+
|
|
15
|
+
let(:formatter) { Formatter::Standard.new }
|
|
16
|
+
|
|
17
|
+
before do
|
|
18
|
+
allow(Formatter::Standard).to receive(:new)
|
|
19
|
+
.and_return(formatter)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe '#run!' do
|
|
23
|
+
subject(:run!) { runner.run! }
|
|
24
|
+
|
|
25
|
+
it 'creates a thread for each task'do
|
|
26
|
+
expect(Thread).to receive(:new)
|
|
27
|
+
.exactly(tasks.size).times
|
|
28
|
+
.and_call_original
|
|
29
|
+
|
|
30
|
+
run!
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'calls each task' do
|
|
34
|
+
expect(hey_task).to receive(:call)
|
|
35
|
+
.and_call_original
|
|
36
|
+
expect(ho_task).to receive(:call)
|
|
37
|
+
.and_call_original
|
|
38
|
+
|
|
39
|
+
run!
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
describe GitMulticast::Task do
|
|
2
2
|
subject(:task) { described_class.new(description, command) }
|
|
3
3
|
|
|
4
|
-
let(:description) { '
|
|
5
|
-
let(:command) { '
|
|
4
|
+
let(:description) { 'some echo' }
|
|
5
|
+
let(:command) { 'echo hi' }
|
|
6
6
|
|
|
7
7
|
let(:pid) { 42 }
|
|
8
|
+
let(:status) { instance_double(Process::Status, exitstatus: 0) }
|
|
8
9
|
|
|
9
10
|
let(:pipe) { IO.pipe }
|
|
10
11
|
let(:r) { pipe.first }
|
|
@@ -13,6 +14,8 @@ describe GitMulticast::Task do
|
|
|
13
14
|
before do
|
|
14
15
|
allow(task).to receive(:spawn)
|
|
15
16
|
.and_return(pid)
|
|
17
|
+
allow(task).to receive(:wait2)
|
|
18
|
+
.and_return([0, status])
|
|
16
19
|
allow(task).to receive(:wait)
|
|
17
20
|
.and_return(['', 0])
|
|
18
21
|
|
|
@@ -40,14 +43,14 @@ describe GitMulticast::Task do
|
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
it 'waits for its process to finish' do
|
|
43
|
-
expect(task).to receive(:
|
|
46
|
+
expect(task).to receive(:wait2)
|
|
44
47
|
.with(pid)
|
|
45
48
|
|
|
46
49
|
run!
|
|
47
50
|
end
|
|
48
51
|
|
|
49
|
-
it 'wraps its result in a
|
|
50
|
-
expect(GitMulticast::
|
|
52
|
+
it 'wraps its result in a Task::Result object' do
|
|
53
|
+
expect(GitMulticast::Task::Result).to receive(:new)
|
|
51
54
|
.with(description, 'I be output!', 0)
|
|
52
55
|
|
|
53
56
|
run!
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git_multicast
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Renan Ranelli
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-12-
|
|
11
|
+
date: 2014-12-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: recursive-open-struct
|
|
@@ -108,6 +108,20 @@ dependencies:
|
|
|
108
108
|
- - ">="
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rubocop
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
111
125
|
- !ruby/object:Gem::Dependency
|
|
112
126
|
name: webmock
|
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -185,21 +199,25 @@ files:
|
|
|
185
199
|
- bin/git_multicast
|
|
186
200
|
- git_multicast.gemspec
|
|
187
201
|
- lib/git_multicast.rb
|
|
188
|
-
- lib/git_multicast
|
|
189
|
-
- lib/git_multicast/
|
|
190
|
-
- lib/git_multicast/
|
|
191
|
-
- lib/git_multicast/adapters/github.rb
|
|
202
|
+
- lib/git_multicast/adapter.rb
|
|
203
|
+
- lib/git_multicast/adapter/bitbucket.rb
|
|
204
|
+
- lib/git_multicast/adapter/github.rb
|
|
192
205
|
- lib/git_multicast/cli.rb
|
|
193
|
-
- lib/git_multicast/
|
|
194
|
-
- lib/git_multicast/
|
|
195
|
-
- lib/git_multicast/
|
|
206
|
+
- lib/git_multicast/formatter.rb
|
|
207
|
+
- lib/git_multicast/formatter/full.rb
|
|
208
|
+
- lib/git_multicast/formatter/quiet.rb
|
|
209
|
+
- lib/git_multicast/formatter/standard.rb
|
|
210
|
+
- lib/git_multicast/formatter/status.rb
|
|
211
|
+
- lib/git_multicast/multicaster.rb
|
|
212
|
+
- lib/git_multicast/multicaster/clone.rb
|
|
213
|
+
- lib/git_multicast/multicaster/pull.rb
|
|
214
|
+
- lib/git_multicast/multicaster/status.rb
|
|
196
215
|
- lib/git_multicast/repository_fetcher.rb
|
|
197
216
|
- lib/git_multicast/repository_fetcher/bitbucket.rb
|
|
198
217
|
- lib/git_multicast/repository_fetcher/github.rb
|
|
199
|
-
- lib/git_multicast/statuser.rb
|
|
200
218
|
- lib/git_multicast/task.rb
|
|
201
|
-
- lib/git_multicast/
|
|
202
|
-
- lib/git_multicast/
|
|
219
|
+
- lib/git_multicast/task/result.rb
|
|
220
|
+
- lib/git_multicast/task/runner.rb
|
|
203
221
|
- lib/git_multicast/version.rb
|
|
204
222
|
- spec/fixtures/vcr_cassettes/bitbucket_all_user_repos.yml
|
|
205
223
|
- spec/fixtures/vcr_cassettes/bitbucket_repo.yml
|
|
@@ -208,16 +226,19 @@ files:
|
|
|
208
226
|
- spec/fixtures/vcr_cassettes/github_repo.yml
|
|
209
227
|
- spec/fixtures/vcr_cassettes/github_repo_parent.yml
|
|
210
228
|
- spec/fixtures/vcr_cassettes/repos_from_all_services.yml
|
|
211
|
-
- spec/git_multicast/
|
|
229
|
+
- spec/git_multicast/adapter/bitbucket_spec.rb
|
|
212
230
|
- spec/git_multicast/cli_spec.rb
|
|
213
|
-
- spec/git_multicast/
|
|
214
|
-
- spec/git_multicast/
|
|
215
|
-
- spec/git_multicast/
|
|
231
|
+
- spec/git_multicast/formatter/full_spec.rb
|
|
232
|
+
- spec/git_multicast/formatter/quiet_spec.rb
|
|
233
|
+
- spec/git_multicast/formatter/standard_spec.rb
|
|
234
|
+
- spec/git_multicast/formatter/status_spec.rb
|
|
235
|
+
- spec/git_multicast/multicaster/clone_spec.rb
|
|
236
|
+
- spec/git_multicast/multicaster/pull_spec.rb
|
|
237
|
+
- spec/git_multicast/multicaster/status_spec.rb
|
|
216
238
|
- spec/git_multicast/repository_fetcher/bitbucket_spec.rb
|
|
217
239
|
- spec/git_multicast/repository_fetcher/github_spec.rb
|
|
218
240
|
- spec/git_multicast/repository_fetcher_spec.rb
|
|
219
|
-
- spec/git_multicast/
|
|
220
|
-
- spec/git_multicast/task_runner_spec.rb
|
|
241
|
+
- spec/git_multicast/task/runner_spec.rb
|
|
221
242
|
- spec/git_multicast/task_spec.rb
|
|
222
243
|
- spec/spec_helper.rb
|
|
223
244
|
homepage: http://github.com/rranelli/git_multicast
|
data/lib/git_multicast/cloner.rb
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
require 'net/http'
|
|
2
|
-
require 'json'
|
|
3
|
-
|
|
4
|
-
module GitMulticast
|
|
5
|
-
class Cloner
|
|
6
|
-
def initialize(username, dir)
|
|
7
|
-
@username = username
|
|
8
|
-
@dir = dir
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def clone!
|
|
12
|
-
repos = RepositoryFetcher.get_all_repos_from_user(username)
|
|
13
|
-
|
|
14
|
-
tasks = repos.map do |repo|
|
|
15
|
-
Task.new(repo.name, command(repo))
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
TaskRunner.new(tasks).run!
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
protected
|
|
22
|
-
|
|
23
|
-
attr_reader :username, :dir
|
|
24
|
-
|
|
25
|
-
def command(repo)
|
|
26
|
-
if repo.fork
|
|
27
|
-
parent_repo = RepositoryFetcher.get_repo_parent(repo.url)
|
|
28
|
-
"git clone #{repo.ssh_url} #{File.join(dir, repo.name)} && \
|
|
29
|
-
git -C \"#{File.join(dir, repo.name)}\" remote add upstream \
|
|
30
|
-
#{parent_repo.ssh_url} --fetch"
|
|
31
|
-
else
|
|
32
|
-
"git clone #{repo.ssh_url} #{File.join(dir, repo.name)}"
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
require 'colorize'
|
|
2
|
-
|
|
3
|
-
module GitMulticast
|
|
4
|
-
class OutputFormatter
|
|
5
|
-
def initialize(start_time = nil)
|
|
6
|
-
@start_time = start_time
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def single_format(task_result)
|
|
10
|
-
case task_result.exit_status
|
|
11
|
-
when 0
|
|
12
|
-
'[Success]'.green + " #{task_result.name} #{time_report}"
|
|
13
|
-
else
|
|
14
|
-
<<EOF
|
|
15
|
-
#{'[Error]'.red} #{task_result.name}
|
|
16
|
-
#{task_result.result}
|
|
17
|
-
#{'-' * 25}
|
|
18
|
-
EOF
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
protected
|
|
23
|
-
|
|
24
|
-
attr_reader :start_time
|
|
25
|
-
|
|
26
|
-
def time_report
|
|
27
|
-
"in #{Time.now - start_time} seconds" unless start_time.nil?
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
data/lib/git_multicast/puller.rb
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
module GitMulticast
|
|
2
|
-
class Puller
|
|
3
|
-
def initialize(dir)
|
|
4
|
-
@dir = dir
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
def pull!
|
|
8
|
-
tasks = Dir.entries(dir)
|
|
9
|
-
.select { |f| File.directory? f }
|
|
10
|
-
.reject { |f| f =~ /^\./ } # ., .. and .git and the like
|
|
11
|
-
.map { |dir| Task.new(description(dir), command(dir)) }
|
|
12
|
-
|
|
13
|
-
TaskRunner.new(tasks).run!
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
protected
|
|
17
|
-
|
|
18
|
-
attr_reader :dir
|
|
19
|
-
|
|
20
|
-
def command(dir)
|
|
21
|
-
"git -C #{dir} pull -r origin"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def description(dir)
|
|
25
|
-
File.basename(dir)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
module GitMulticast
|
|
2
|
-
class Statuser
|
|
3
|
-
def initialize(dir)
|
|
4
|
-
@dir = dir
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
def statuses!
|
|
8
|
-
tasks = Dir.entries(dir)
|
|
9
|
-
.select { |f| File.directory? f }
|
|
10
|
-
.reject { |f| f =~ /^\./ } # ., .. and .git and the like
|
|
11
|
-
.map { |dir| Task.new(description(dir), command(dir)) }
|
|
12
|
-
|
|
13
|
-
TaskRunner.new(tasks).run!
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
protected
|
|
17
|
-
|
|
18
|
-
attr_reader :dir
|
|
19
|
-
|
|
20
|
-
def command(dir)
|
|
21
|
-
"cd #{dir} && git status"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def description(dir)
|
|
25
|
-
File.basename(dir)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
module GitMulticast
|
|
2
|
-
class TaskRunner
|
|
3
|
-
include Process
|
|
4
|
-
|
|
5
|
-
def initialize(tasks, formatter = OutputFormatter.new(Time.now))
|
|
6
|
-
@tasks = tasks
|
|
7
|
-
@formatter = formatter
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def run!
|
|
11
|
-
tasks
|
|
12
|
-
.map(&method(:future))
|
|
13
|
-
.map(&:get)
|
|
14
|
-
.inject('', &:+)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
protected
|
|
18
|
-
|
|
19
|
-
attr_reader :tasks, :formatter
|
|
20
|
-
|
|
21
|
-
def future(task)
|
|
22
|
-
PoorMansFuture.new { formatter.single_format(task.call) }
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
class PoorMansFuture
|
|
26
|
-
def initialize(&block)
|
|
27
|
-
@thread = Thread.new do
|
|
28
|
-
Thread.current[:output] = block.call
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def get
|
|
33
|
-
thread.join
|
|
34
|
-
thread[:output]
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
attr_reader :thread
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
module GitMulticast
|
|
2
|
-
describe Cloner do
|
|
3
|
-
subject(:cloner) { described_class.new(username, dir) }
|
|
4
|
-
|
|
5
|
-
let(:username) { 'rranelli' }
|
|
6
|
-
let(:dir) { '/kifita/' }
|
|
7
|
-
|
|
8
|
-
let(:repo_name) { 'git_multicast' }
|
|
9
|
-
|
|
10
|
-
before do
|
|
11
|
-
allow(task).to receive(:spawn)
|
|
12
|
-
.and_return(pid)
|
|
13
|
-
allow(task).to receive(:wait)
|
|
14
|
-
.and_return(['', 0])
|
|
15
|
-
|
|
16
|
-
allow(Task).to receive(:new)
|
|
17
|
-
.and_return(task)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
let(:task) { instance_double(Task, call: result) }
|
|
21
|
-
let(:result) { TaskResult.new(repo_name, 'success', 0) }
|
|
22
|
-
|
|
23
|
-
let(:pid) { 42 }
|
|
24
|
-
|
|
25
|
-
describe '#clone!' do
|
|
26
|
-
subject(:clone!) { cloner.clone! }
|
|
27
|
-
|
|
28
|
-
it do
|
|
29
|
-
VCR.use_cassette('clone_repos') do
|
|
30
|
-
expect(RepositoryFetcher).to receive(:get_all_repos_from_user)
|
|
31
|
-
.with(username)
|
|
32
|
-
.and_call_original
|
|
33
|
-
|
|
34
|
-
clone!
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
it 'creates a task for each fetched repository' do
|
|
39
|
-
VCR.use_cassette('clone_repos') do
|
|
40
|
-
expect(Task).to receive(:new).exactly(43).times
|
|
41
|
-
|
|
42
|
-
clone!
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it 'creates a task runner and asks it to run all tasks' do
|
|
47
|
-
VCR.use_cassette('clone_repos') do
|
|
48
|
-
expect(TaskRunner).to receive_message_chain(:new, :run!)
|
|
49
|
-
|
|
50
|
-
clone!
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it 'spawns a clone job for each repo' do
|
|
55
|
-
VCR.use_cassette('clone_repos') do
|
|
56
|
-
expect(task).to receive(:call).exactly(43).times
|
|
57
|
-
|
|
58
|
-
clone!
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'spawns a clone with the right parameters' do
|
|
63
|
-
VCR.use_cassette('clone_repos') do
|
|
64
|
-
clone_command = "git clone git@github.com:rranelli/#{repo_name}.git" \
|
|
65
|
-
' /kifita/git_multicast'
|
|
66
|
-
|
|
67
|
-
expect(Task).to receive(:new)
|
|
68
|
-
.with(repo_name, clone_command)
|
|
69
|
-
|
|
70
|
-
clone!
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
context 'when repo is a fork'do
|
|
75
|
-
let(:repo_name) { 'emacs.d' }
|
|
76
|
-
|
|
77
|
-
it 'adds upstream remote' do
|
|
78
|
-
VCR.use_cassette('clone_repos') do
|
|
79
|
-
expect(Task).to receive(:new)
|
|
80
|
-
.with(
|
|
81
|
-
repo_name,
|
|
82
|
-
"git clone git@github.com:rranelli/#{repo_name}.git " \
|
|
83
|
-
"/kifita/#{repo_name} && git -C \"/kifita/#{repo_name}\"" \
|
|
84
|
-
' remote add upstream ' \
|
|
85
|
-
"git@github.com:purcell/#{repo_name}.git --fetch"
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
clone!
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|