kjess 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.
- data/CONTRIBUTING.md +45 -0
- data/HISTORY.rdoc +5 -0
- data/LICENSE +16 -0
- data/Manifest.txt +53 -0
- data/README.rdoc +44 -0
- data/Rakefile +337 -0
- data/example/client_test.rb +73 -0
- data/lib/kjess.rb +11 -0
- data/lib/kjess/client.rb +289 -0
- data/lib/kjess/connection.rb +119 -0
- data/lib/kjess/error.rb +5 -0
- data/lib/kjess/protocol.rb +76 -0
- data/lib/kjess/request.rb +31 -0
- data/lib/kjess/request/delete.rb +11 -0
- data/lib/kjess/request/dump_stats.rb +7 -0
- data/lib/kjess/request/flush.rb +11 -0
- data/lib/kjess/request/flush_all.rb +7 -0
- data/lib/kjess/request/get.rb +21 -0
- data/lib/kjess/request/quit.rb +7 -0
- data/lib/kjess/request/reload.rb +7 -0
- data/lib/kjess/request/set.rb +19 -0
- data/lib/kjess/request/shutdown.rb +7 -0
- data/lib/kjess/request/stats.rb +7 -0
- data/lib/kjess/request/status.rb +8 -0
- data/lib/kjess/request/version.rb +8 -0
- data/lib/kjess/response.rb +76 -0
- data/lib/kjess/response/client_error.rb +19 -0
- data/lib/kjess/response/deleted.rb +5 -0
- data/lib/kjess/response/dumped_stats.rb +48 -0
- data/lib/kjess/response/end.rb +5 -0
- data/lib/kjess/response/eof.rb +5 -0
- data/lib/kjess/response/error.rb +13 -0
- data/lib/kjess/response/flushed_all_queues.rb +6 -0
- data/lib/kjess/response/not_found.rb +5 -0
- data/lib/kjess/response/not_stored.rb +5 -0
- data/lib/kjess/response/reloaded_config.rb +6 -0
- data/lib/kjess/response/server_error.rb +18 -0
- data/lib/kjess/response/stats.rb +60 -0
- data/lib/kjess/response/stored.rb +5 -0
- data/lib/kjess/response/unknown.rb +3 -0
- data/lib/kjess/response/value.rb +26 -0
- data/lib/kjess/response/version.rb +10 -0
- data/lib/kjess/stats_cache.rb +31 -0
- data/spec/client_spec.rb +265 -0
- data/spec/kestrel_server.rb +137 -0
- data/spec/request/set_spec.rb +13 -0
- data/spec/request/version_spec.rb +17 -0
- data/spec/request_spec.rb +30 -0
- data/spec/response/client_error_spec.rb +17 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/utils.rb +18 -0
- data/spec/version_spec.rb +9 -0
- data/tasks/kestrel.rake +70 -0
- metadata +193 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'spec/utils'
|
3
|
+
require 'net/http'
|
4
|
+
module KJess::Spec
|
5
|
+
class KestrelServer
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def version
|
9
|
+
"2.3.4"
|
10
|
+
end
|
11
|
+
|
12
|
+
def dir
|
13
|
+
File.join( KJess::Spec.project_root, "kestrel/kestrel-#{version}" )
|
14
|
+
end
|
15
|
+
|
16
|
+
def zip
|
17
|
+
"#{dir}.zip"
|
18
|
+
end
|
19
|
+
|
20
|
+
def jar
|
21
|
+
File.join( dir, "kestrel_2.9.1-#{version}.jar" )
|
22
|
+
end
|
23
|
+
|
24
|
+
def queue_path
|
25
|
+
File.join( dir, 'data' )
|
26
|
+
end
|
27
|
+
|
28
|
+
def log_path
|
29
|
+
File.join( dir, 'logs' )
|
30
|
+
end
|
31
|
+
|
32
|
+
def log_file
|
33
|
+
File.join( log_path, 'kestrel.log' )
|
34
|
+
end
|
35
|
+
|
36
|
+
def config_file
|
37
|
+
File.join( dir, 'config', 'kjess.scala' )
|
38
|
+
end
|
39
|
+
|
40
|
+
def config_contents
|
41
|
+
contents = <<_EOC
|
42
|
+
import com.twitter.conversions.storage._
|
43
|
+
import com.twitter.conversions.time._
|
44
|
+
import com.twitter.logging.config._
|
45
|
+
import com.twitter.ostrich.admin.config._
|
46
|
+
import net.lag.kestrel.config._
|
47
|
+
|
48
|
+
new KestrelConfig {
|
49
|
+
listenAddress = "0.0.0.0"
|
50
|
+
memcacheListenPort = 22133
|
51
|
+
|
52
|
+
queuePath = "#{KJess::Spec::KestrelServer.queue_path}"
|
53
|
+
|
54
|
+
clientTimeout = 30.seconds
|
55
|
+
|
56
|
+
expirationTimerFrequency = 1.second
|
57
|
+
|
58
|
+
maxOpenTransactions = 100
|
59
|
+
|
60
|
+
// default queue settings:
|
61
|
+
default.defaultJournalSize = 16.megabytes
|
62
|
+
default.maxMemorySize = 128.megabytes
|
63
|
+
default.maxJournalSize = 1.gigabyte
|
64
|
+
|
65
|
+
admin.httpPort = 2223
|
66
|
+
|
67
|
+
admin.statsNodes = new StatsConfig {
|
68
|
+
reporters = new TimeSeriesCollectorConfig
|
69
|
+
}
|
70
|
+
|
71
|
+
loggers = new LoggerConfig {
|
72
|
+
level = Level.DEBUG
|
73
|
+
handlers = new FileHandlerConfig {
|
74
|
+
filename = "#{KJess::Spec::KestrelServer.log_file}"
|
75
|
+
roll = Policy.Never
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
_EOC
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_response( path )
|
83
|
+
uri = URI.parse( "http://localhost:2223/#{path}" )
|
84
|
+
resp = Net::HTTP.get_response( uri )
|
85
|
+
JSON.parse( resp.body )
|
86
|
+
end
|
87
|
+
|
88
|
+
def start
|
89
|
+
Dir.chdir( KJess::Spec::KestrelServer.dir ) do
|
90
|
+
cmd = "java -server -Xmx1024m -Dstage=kjess -jar #{KJess::Spec::KestrelServer.jar} &"
|
91
|
+
puts cmd
|
92
|
+
system( cmd )
|
93
|
+
loop do
|
94
|
+
break if is_running?
|
95
|
+
end
|
96
|
+
puts "Started."
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def is_running?
|
101
|
+
return "pong" == ping
|
102
|
+
rescue Exception => e
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
def is_stopped?
|
107
|
+
return !is_running?
|
108
|
+
end
|
109
|
+
|
110
|
+
def status
|
111
|
+
puts "Running" if "pong" == ping
|
112
|
+
rescue Exception
|
113
|
+
puts "Stopped."
|
114
|
+
end
|
115
|
+
|
116
|
+
def stop
|
117
|
+
shutdown
|
118
|
+
loop do
|
119
|
+
break if is_stopped?
|
120
|
+
end
|
121
|
+
puts "Stopped."
|
122
|
+
end
|
123
|
+
|
124
|
+
def ping
|
125
|
+
get_response('ping')['response']
|
126
|
+
end
|
127
|
+
|
128
|
+
def shutdown
|
129
|
+
h = get_response( 'shutdown' )
|
130
|
+
return h['response'] == "ok"
|
131
|
+
rescue => e
|
132
|
+
false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KJess::Request::Set do
|
4
|
+
it "converts to the protocol" do
|
5
|
+
s = KJess::Request::Set.new( :queue_name => 'test', :data => 'a job' )
|
6
|
+
s.to_protocol.must_equal "SET test 0 0 5\r\na job\r\n"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "sets the expriation time" do
|
10
|
+
s = KJess::Request::Set.new( :queue_name => 'test', :expiration => 42, :data => 'a job' )
|
11
|
+
s.to_protocol.must_equal "SET test 0 42 5\r\na job\r\n"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KJess::Request::Version do
|
4
|
+
|
5
|
+
it "has a keyword" do
|
6
|
+
KJess::Request::Version.keyword.must_equal "VERSION"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "converts to the protocol" do
|
10
|
+
v = KJess::Request::Version.new
|
11
|
+
v.to_protocol.must_equal "VERSION\r\n"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has a valid response" do
|
15
|
+
KJess::Request::Version.valid_responses.size.must_equal 1
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class KJess::Spec::TestRequest < KJess::Request
|
4
|
+
keyword "TEST"
|
5
|
+
arity 1
|
6
|
+
|
7
|
+
def parse_options_to_args( opts )
|
8
|
+
opts.values
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe KJess::Response do
|
13
|
+
it "defines a keyword for child classes" do
|
14
|
+
KJess::Spec::TestRequest.keyword.must_equal 'TEST'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "uses a callback to parse the options to args" do
|
18
|
+
r = KJess::Spec::TestRequest.new( :foo => 'this' )
|
19
|
+
r.args.must_equal %w[ this ]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "converts the request into a protocol stream" do
|
23
|
+
r = KJess::Spec::TestRequest.new( :foo => 'that' )
|
24
|
+
r.to_protocol.must_equal "TEST that\r\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "registers child classes" do
|
28
|
+
KJess::Request.registry['TEST'].must_equal KJess::Spec::TestRequest
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KJess::ClientError do
|
4
|
+
before do
|
5
|
+
@client = KJess::Client.new
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
KJess::Spec.reset_server( @client )
|
10
|
+
end
|
11
|
+
|
12
|
+
it "raises a client error if we send an invalid command" do
|
13
|
+
lambda { @client.send_recv( KJess::Request::Status.new ) }.must_raise KJess::ClientError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
data/spec/spec_helper.rb
ADDED
data/spec/utils.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module KJess
|
2
|
+
module Spec
|
3
|
+
ROOT = File.expand_path( "..", __FILE__ )
|
4
|
+
def self.project_root
|
5
|
+
File.expand_path( "..", ROOT )
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.reset_server( client )
|
9
|
+
client.flush_all
|
10
|
+
qlist = client.stats['queues']
|
11
|
+
if qlist then
|
12
|
+
qlist.keys.each do |q|
|
13
|
+
client.delete( q )
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/tasks/kestrel.rake
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
load 'spec/kestrel_server.rb'
|
2
|
+
|
3
|
+
namespace :kestrel do
|
4
|
+
|
5
|
+
directory 'kestrel'
|
6
|
+
file KJess::Spec::KestrelServer.zip => 'kestrel' do
|
7
|
+
require 'uri'
|
8
|
+
require 'net/http'
|
9
|
+
|
10
|
+
url = ::URI.parse("http://robey.github.com/kestrel/download/kestrel-#{KJess::Spec::KestrelServer.erver.version}.zip")
|
11
|
+
|
12
|
+
puts "downloading #{url.to_s} to #{KJess::Spec::KestrelServer.zip} ..."
|
13
|
+
File.open( KJess::Spec::KestrelServer.erver.zip, "wb+") do |f|
|
14
|
+
res = Net::HTTP.get_response( url )
|
15
|
+
f.write( res.body )
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
file KJess::Spec::KestrelServer.jar => KJess::Spec::KestrelServer.zip do
|
21
|
+
require 'zip'
|
22
|
+
puts "extracting #{KJess::Spec::KestrelServer.zip}"
|
23
|
+
Zip::ZipFile.open( KJess::Spec::KestrelServer.zip ) do |zipfile|
|
24
|
+
zipfile.entries.each do |entry|
|
25
|
+
next unless entry.file?
|
26
|
+
dest_name = File.join('kestrel', entry.name.strip)
|
27
|
+
dirname = File.dirname( dest_name )
|
28
|
+
FileUtils.mkdir_p( dirname ) unless File.directory?( dirname )
|
29
|
+
entry.extract( dest_name ) { true }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Unpack kestrel to use for testing"
|
35
|
+
task :extract => KJess::Spec::KestrelServer.jar
|
36
|
+
|
37
|
+
directory KJess::Spec::KestrelServer.queue_path
|
38
|
+
directory KJess::Spec::KestrelServer.log_path
|
39
|
+
|
40
|
+
file KJess::Spec::KestrelServer.config_file => [ KJess::Spec::KestrelServer.jar,
|
41
|
+
KJess::Spec::KestrelServer.queue_path,
|
42
|
+
KJess::Spec::KestrelServer.log_path ] do
|
43
|
+
File.open( KJess::Spec::KestrelServer.config_file, "w+" ) do |f|
|
44
|
+
puts "Writing #{KJess::Spec::KestrelServer.config_file}"
|
45
|
+
f.write( KJess::Spec::KestrelServer.config_contents )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Start a kestrel server"
|
50
|
+
task :start => KJess::Spec::KestrelServer.config_file do
|
51
|
+
KJess::Spec::KestrelServer.start
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Stop a kestrel server"
|
55
|
+
task :stop => KJess::Spec::KestrelServer.config_file do
|
56
|
+
KJess::Spec::KestrelServer.stop
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "See the status of the kestrel server"
|
60
|
+
task :status => KJess::Spec::KestrelServer.config_file do
|
61
|
+
KJess::Spec::KestrelServer.status
|
62
|
+
end
|
63
|
+
|
64
|
+
task :clean do
|
65
|
+
FileUtils.rm_rf( KJess::Spec::KestrelServer.dir, :verbose => true )
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
task :clean => 'kestrel:clean'
|
70
|
+
task :test => KJess::Spec::KestrelServer.config_file
|
metadata
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kjess
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jeremy Hinegardner
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-10-31 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rake
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 11
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 9
|
32
|
+
- 2
|
33
|
+
- 2
|
34
|
+
version: 0.9.2.2
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: minitest
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 11
|
46
|
+
segments:
|
47
|
+
- 3
|
48
|
+
- 3
|
49
|
+
- 0
|
50
|
+
version: 3.3.0
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rdoc
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 31
|
62
|
+
segments:
|
63
|
+
- 3
|
64
|
+
- 12
|
65
|
+
version: "3.12"
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: zip
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 11
|
77
|
+
segments:
|
78
|
+
- 2
|
79
|
+
- 0
|
80
|
+
- 2
|
81
|
+
version: 2.0.2
|
82
|
+
type: :development
|
83
|
+
version_requirements: *id004
|
84
|
+
description: KJess is a pure ruby Kestrel client that supports Kestrel's Memcache style protocol.
|
85
|
+
email: jeremy@copiousfreetime.org
|
86
|
+
executables: []
|
87
|
+
|
88
|
+
extensions: []
|
89
|
+
|
90
|
+
extra_rdoc_files:
|
91
|
+
- HISTORY.rdoc
|
92
|
+
- Manifest.txt
|
93
|
+
- README.rdoc
|
94
|
+
files:
|
95
|
+
- CONTRIBUTING.md
|
96
|
+
- HISTORY.rdoc
|
97
|
+
- LICENSE
|
98
|
+
- Manifest.txt
|
99
|
+
- README.rdoc
|
100
|
+
- Rakefile
|
101
|
+
- example/client_test.rb
|
102
|
+
- lib/kjess.rb
|
103
|
+
- lib/kjess/client.rb
|
104
|
+
- lib/kjess/connection.rb
|
105
|
+
- lib/kjess/error.rb
|
106
|
+
- lib/kjess/protocol.rb
|
107
|
+
- lib/kjess/request.rb
|
108
|
+
- lib/kjess/request/delete.rb
|
109
|
+
- lib/kjess/request/dump_stats.rb
|
110
|
+
- lib/kjess/request/flush.rb
|
111
|
+
- lib/kjess/request/flush_all.rb
|
112
|
+
- lib/kjess/request/get.rb
|
113
|
+
- lib/kjess/request/quit.rb
|
114
|
+
- lib/kjess/request/reload.rb
|
115
|
+
- lib/kjess/request/set.rb
|
116
|
+
- lib/kjess/request/shutdown.rb
|
117
|
+
- lib/kjess/request/stats.rb
|
118
|
+
- lib/kjess/request/status.rb
|
119
|
+
- lib/kjess/request/version.rb
|
120
|
+
- lib/kjess/response.rb
|
121
|
+
- lib/kjess/response/client_error.rb
|
122
|
+
- lib/kjess/response/deleted.rb
|
123
|
+
- lib/kjess/response/dumped_stats.rb
|
124
|
+
- lib/kjess/response/end.rb
|
125
|
+
- lib/kjess/response/eof.rb
|
126
|
+
- lib/kjess/response/error.rb
|
127
|
+
- lib/kjess/response/flushed_all_queues.rb
|
128
|
+
- lib/kjess/response/not_found.rb
|
129
|
+
- lib/kjess/response/not_stored.rb
|
130
|
+
- lib/kjess/response/reloaded_config.rb
|
131
|
+
- lib/kjess/response/server_error.rb
|
132
|
+
- lib/kjess/response/stats.rb
|
133
|
+
- lib/kjess/response/stored.rb
|
134
|
+
- lib/kjess/response/unknown.rb
|
135
|
+
- lib/kjess/response/value.rb
|
136
|
+
- lib/kjess/response/version.rb
|
137
|
+
- lib/kjess/stats_cache.rb
|
138
|
+
- spec/client_spec.rb
|
139
|
+
- spec/kestrel_server.rb
|
140
|
+
- spec/request/set_spec.rb
|
141
|
+
- spec/request/version_spec.rb
|
142
|
+
- spec/request_spec.rb
|
143
|
+
- spec/response/client_error_spec.rb
|
144
|
+
- spec/spec_helper.rb
|
145
|
+
- spec/utils.rb
|
146
|
+
- spec/version_spec.rb
|
147
|
+
- tasks/kestrel.rake
|
148
|
+
homepage: http://github.com/copiousfreetime/kjess
|
149
|
+
licenses: []
|
150
|
+
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options:
|
153
|
+
- --main
|
154
|
+
- README.rdoc
|
155
|
+
- --markup
|
156
|
+
- tomdoc
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
none: false
|
161
|
+
requirements:
|
162
|
+
- - ">="
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
hash: 3
|
165
|
+
segments:
|
166
|
+
- 0
|
167
|
+
version: "0"
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
hash: 3
|
174
|
+
segments:
|
175
|
+
- 0
|
176
|
+
version: "0"
|
177
|
+
requirements: []
|
178
|
+
|
179
|
+
rubyforge_project:
|
180
|
+
rubygems_version: 1.8.24
|
181
|
+
signing_key:
|
182
|
+
specification_version: 3
|
183
|
+
summary: KJess is a pure ruby Kestrel client that supports Kestrel's Memcache style protocol.
|
184
|
+
test_files:
|
185
|
+
- spec/client_spec.rb
|
186
|
+
- spec/kestrel_server.rb
|
187
|
+
- spec/request/set_spec.rb
|
188
|
+
- spec/request/version_spec.rb
|
189
|
+
- spec/request_spec.rb
|
190
|
+
- spec/response/client_error_spec.rb
|
191
|
+
- spec/spec_helper.rb
|
192
|
+
- spec/utils.rb
|
193
|
+
- spec/version_spec.rb
|