blender-zk 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +14 -0
- data/README.md +86 -0
- data/Rakefile +6 -0
- data/blender-zk.gemspec +26 -0
- data/build.sh +5 -0
- data/lib/blender/lock/zk.rb +45 -0
- data/lib/blender/zk.rb +19 -0
- data/lib/blender/zk/version.rb +22 -0
- data/spec/blender/lock_zk_spec.rb +102 -0
- data/spec/spec_helper.rb +17 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2e2737454d88462d18113150662f39f2c6a51105
|
4
|
+
data.tar.gz: 0a5239083b3c5ff50dc6a0c238e531d25ec83f87
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ca5b2ed888f5fcd1677e7a54a489a2d0352129e27eaaf20e31be41d9c4b1831b137a5db6f1ed752f7f7d17d6749206a72ea98e4266285479f216361b94da076c
|
7
|
+
data.tar.gz: 00880d15b3e1f2cc3b4053bf8d1544c8bb8710965c53f374a52bf85a28eb75f4c56ab2d2b85535baadd107501a7e273b0f479c7eadf8cbefe587f76cf65b8cd8
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
zookeeper*
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
2
|
+
License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
you may not use this file except in compliance with the License.
|
6
|
+
You may obtain a copy of the License at
|
7
|
+
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
See the License for the specific language governing permissions and
|
14
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# Blender::Zk
|
2
|
+
|
3
|
+
Zookeeper integration for blender. It allows [Blender](https://github.com/PagerDuty/blender)
|
4
|
+
to use zookeeper for locking. Blender jobs running in different nodes can synchornize/coordinate using
|
5
|
+
zookeeper.
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'blender-zk'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install blender-zk
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
- Blender by default used file based locking (flock/fcntl) for locking. You have
|
25
|
+
to explicitly tell blender to use zookeeper locking interface. This can be done
|
26
|
+
using the `lock_options` DSL method. It take the locking driver name ('zk'
|
27
|
+
in our case).
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
Blender.blend('test-1') do |sched|
|
31
|
+
sched.members(['localhost'])
|
32
|
+
sched.lock_options('zk')
|
33
|
+
sched.ruby_task('date') do
|
34
|
+
execute do
|
35
|
+
sleep 10
|
36
|
+
puts 'This will succeed'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
## in another script
|
42
|
+
Blender.blend('test-1') do |sched|
|
43
|
+
sched.members(['localhost'])
|
44
|
+
sched.lock_options('zk')
|
45
|
+
sched.ruby_task('date') do
|
46
|
+
execute do
|
47
|
+
puts 'This will fail, if both jobs are triggered at the same time '
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
By default blender-zk will use the job name 'test-1' as key name. This, and
|
53
|
+
other options (lock wait timeout, chroot etc) can be passed as option
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
Blender.blend('test-1') do |sched|
|
57
|
+
sched.members(['localhost'])
|
58
|
+
sched.lock_options('zk', path: '/path/to/lock')
|
59
|
+
sched.ruby_task('date') do
|
60
|
+
execute do
|
61
|
+
sleep 3
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
### in another script
|
66
|
+
Blender.blend('test-2') do |sched|
|
67
|
+
sched.members(['localhost'])
|
68
|
+
sched.lock_options('zk', path: '/path/to/lock', timeout: 5)
|
69
|
+
sched.ruby_task('date') do
|
70
|
+
execute do
|
71
|
+
puts 'This wont fail, since it will wait 5 second, by then the first job will finish'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
## License
|
78
|
+
[Apache 2](http://www.apache.org/licenses/LICENSE-2.0)
|
79
|
+
|
80
|
+
## Contributing
|
81
|
+
|
82
|
+
1. Fork it ( https://github.com/[my-github-username]/blender-zk/fork )
|
83
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
84
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
85
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
86
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/blender-zk.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'blender/zk/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'blender-zk'
|
8
|
+
spec.version = Blender::Zk::VERSION
|
9
|
+
spec.authors = ['Ranjib Dey']
|
10
|
+
spec.email = ['ranjib@pagerduty.com']
|
11
|
+
spec.summary = %q{Blender-Zookeeper integration}
|
12
|
+
spec.description = %q{Allows zookeeper based locks in blender}
|
13
|
+
spec.homepage = 'https://github.com/PagerDuty/blender-zk'
|
14
|
+
spec.license = 'Apache 2'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
spec.add_dependency 'pd-blender'
|
21
|
+
spec.add_dependency 'zk'
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
end
|
data/build.sh
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
ls -l zookeeper-3.4.6.tar.gz || wget -c http://mirrors.gigenet.com/apache/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz
|
3
|
+
ls -l zookeeper-3.4.6/build.xml || tar -zxvf zookeeper-3.4.6.tar.gz
|
4
|
+
ls -l zookeeper-3.4.6/conf/zoo.cfg || cp zookeeper-3.4.6/conf/zoo_sample.cfg zookeeper-3.4.6/conf/zoo.cfg
|
5
|
+
./zookeeper-3.4.6/bin/zkServer.sh start
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'zk'
|
19
|
+
require 'zk/locker'
|
20
|
+
require 'blender/exceptions'
|
21
|
+
|
22
|
+
module Blender
|
23
|
+
module Lock
|
24
|
+
class Zk
|
25
|
+
|
26
|
+
def initialize(name, options = {})
|
27
|
+
@path = options[:path] || name
|
28
|
+
@hosts = options[:hosts] || 'localhost:2181'
|
29
|
+
@chroot = options[:chroot] || :do_nothing
|
30
|
+
@thread = options[:thread] || :single
|
31
|
+
@timeout = options[:timeout] || 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def with_lock
|
35
|
+
ZK.open(@hosts) do |zk|
|
36
|
+
zk.with_lock(@path, wait: @timeout) do
|
37
|
+
yield if block_given?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue ZK::Exceptions::LockWaitTimeoutError => e
|
41
|
+
raise LockAcquisitionError, e.message
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/blender/zk.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'blender/zk/version'
|
19
|
+
require 'blender/lock/zk'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
module Blender
|
19
|
+
module Zk
|
20
|
+
VERSION = "0.0.1"
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
Thread.abort_on_exception = true
|
5
|
+
|
6
|
+
describe Blender::Lock::Zk do
|
7
|
+
|
8
|
+
context 'raw api' do
|
9
|
+
it 'without timeout' do
|
10
|
+
t = Thread.new do
|
11
|
+
l1 = Blender::Lock::Zk.new( '/a/b/c')
|
12
|
+
l1.with_lock do
|
13
|
+
sleep 2
|
14
|
+
end
|
15
|
+
end
|
16
|
+
sleep 1
|
17
|
+
|
18
|
+
l2 = Blender::Lock::Zk.new( '/a/b/c')
|
19
|
+
expect do
|
20
|
+
l2.with_lock do
|
21
|
+
puts 'Unreachable code'
|
22
|
+
end
|
23
|
+
end.to raise_error(Blender::LockAcquisitionError)
|
24
|
+
t.join
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'with timeout' do
|
28
|
+
t = Thread.new do
|
29
|
+
l1 = Blender::Lock::Zk.new( '/a/b/c')
|
30
|
+
l1.with_lock do
|
31
|
+
sleep 2
|
32
|
+
end
|
33
|
+
end
|
34
|
+
sleep 1
|
35
|
+
|
36
|
+
l2 = Blender::Lock::Zk.new( '/a/b/c', timeout: 3)
|
37
|
+
expect do
|
38
|
+
l2.with_lock do
|
39
|
+
x = 1 # Solution to NP hard problems
|
40
|
+
end
|
41
|
+
end.not_to raise_error
|
42
|
+
t.join
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with blender dsl' do
|
47
|
+
|
48
|
+
it 'lock without timeout inside dsl' do
|
49
|
+
t = Thread.new do
|
50
|
+
Blender.blend('test-1') do |sched|
|
51
|
+
sched.members(['localhost'])
|
52
|
+
sched.lock_options('zk')
|
53
|
+
sched.ruby_task('date') do
|
54
|
+
execute do
|
55
|
+
sleep 2
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
sleep 1
|
61
|
+
expect do
|
62
|
+
Blender.blend('test-1') do |sched|
|
63
|
+
sched.members(['localhost'])
|
64
|
+
sched.lock_options('zk')
|
65
|
+
sched.ruby_task('date') do
|
66
|
+
execute do
|
67
|
+
puts 'Should fail'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end.to raise_error(Blender::LockAcquisitionError)
|
72
|
+
t.join
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'lock with timeout' do
|
76
|
+
t = Thread.new do
|
77
|
+
Blender.blend('test-1') do |sched|
|
78
|
+
sched.members(['localhost'])
|
79
|
+
sched.lock_options('zk')
|
80
|
+
sched.ruby_task('date') do
|
81
|
+
execute do
|
82
|
+
sleep 2
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
sleep 1
|
88
|
+
expect do
|
89
|
+
Blender.blend('test-1') do |sched|
|
90
|
+
sched.members(['localhost'])
|
91
|
+
sched.lock_options('zk', timeout: 5)
|
92
|
+
sched.ruby_task('date') do
|
93
|
+
execute do
|
94
|
+
x = 2 # Another solution to NP hard problem
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end.not_to raise_error
|
99
|
+
t.join
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require 'rspec'
|
3
|
+
require 'rspec/mocks'
|
4
|
+
require 'rspec/expectations'
|
5
|
+
require 'blender'
|
6
|
+
require 'blender/zk'
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.mock_with :rspec do |mocks|
|
10
|
+
mocks.verify_doubled_constant_names = true
|
11
|
+
end
|
12
|
+
config.before(:each) do
|
13
|
+
doc = double(Blender::Handlers::Doc).as_null_object
|
14
|
+
allow(Blender::Handlers::Doc).to receive(:new).and_return(doc)
|
15
|
+
end
|
16
|
+
config.backtrace_exclusion_patterns = []
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: blender-zk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ranjib Dey
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pd-blender
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: zk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.6'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Allows zookeeper based locks in blender
|
84
|
+
email:
|
85
|
+
- ranjib@pagerduty.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
92
|
+
- Gemfile
|
93
|
+
- LICENSE.txt
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- blender-zk.gemspec
|
97
|
+
- build.sh
|
98
|
+
- lib/blender/lock/zk.rb
|
99
|
+
- lib/blender/zk.rb
|
100
|
+
- lib/blender/zk/version.rb
|
101
|
+
- spec/blender/lock_zk_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
homepage: https://github.com/PagerDuty/blender-zk
|
104
|
+
licenses:
|
105
|
+
- Apache 2
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.2.2
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Blender-Zookeeper integration
|
127
|
+
test_files:
|
128
|
+
- spec/blender/lock_zk_spec.rb
|
129
|
+
- spec/spec_helper.rb
|