json_client 0.0.2 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -5
- data/json_client.gemspec +1 -0
- data/lib/json_client.rb +1 -0
- data/lib/json_client/base.rb +52 -26
- data/lib/json_client/base_requests/create.rb +2 -8
- data/lib/json_client/base_requests/destroy.rb +1 -1
- data/lib/json_client/base_requests/index.rb +1 -1
- data/lib/json_client/base_requests/show.rb +1 -1
- data/lib/json_client/base_requests/update.rb +2 -8
- data/lib/json_client/base_responses/create.rb +0 -3
- data/lib/json_client/base_responses/destroy.rb +0 -3
- data/lib/json_client/base_responses/index.rb +0 -3
- data/lib/json_client/base_responses/response.rb +7 -8
- data/lib/json_client/base_responses/show.rb +0 -3
- data/lib/json_client/base_responses/update.rb +0 -3
- data/lib/json_client/base_serializer.rb +12 -0
- data/lib/json_client/dsl.rb +49 -0
- data/lib/json_client/dsl/collector.rb +13 -0
- data/lib/json_client/dsl/on_use_collector.rb +32 -0
- data/lib/json_client/dsl/requests_collector.rb +15 -0
- data/lib/json_client/dsl/serializers_collector.rb +15 -0
- data/lib/json_client/empty_serializer.rb +8 -0
- data/lib/json_client/resource_handler.rb +9 -0
- data/lib/json_client/version.rb +1 -1
- data/spec/json_client/base_responses/response_spec.rb +8 -2
- data/spec/json_client/dsl/collector_spec.rb +25 -0
- data/spec/json_client/dsl/serializers_collector_spec.rb +26 -0
- data/spec/json_client/dsl_spec.rb +73 -0
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 702dbe914a769256fa420b376e62097b91f8cc2b
|
4
|
+
data.tar.gz: 0fa43e21e5dbfec83ca99537f3d9d16089fd6edf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32833898addf3fd748f79681cfbf77542768233f24c77f2dacdee05c0c5e0c7d5a9b06b74c8e6016c252e4f91a439be6a046ee7fe048780bca887d6df7daa991
|
7
|
+
data.tar.gz: bdfabe4c865a9461d59a83adceb325f527e214e3dc5450397278d67222624b4ed754d69022141e633db59bc7c5f3a43721f33bc5c7b0632b499e0b8863bd9f49
|
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# JsonClient
|
2
|
+
[![Gem
|
3
|
+
Version](https://badge.fury.io/rb/json_client.svg)](http://badge.fury.io/rb/json_client)
|
2
4
|
[![Build
|
3
5
|
Status](https://travis-ci.org/johnmcconnell/json_client.svg?branch=master)](https://travis-ci.org/johnmcconnell/json_client)
|
4
6
|
[![Coverage
|
@@ -52,23 +54,20 @@ config = {
|
|
52
54
|
api_password: 'api_password'
|
53
55
|
}
|
54
56
|
|
55
|
-
|
56
57
|
client = Client.new(config)
|
57
58
|
|
58
59
|
# GET https://example.host.com/api/v1/objects?api_key=api_key&api_password=api_password
|
59
60
|
client.index.json
|
60
61
|
=> { 'server_json' : 'is_parsed_here' }
|
61
62
|
|
62
|
-
|
63
|
-
|
64
63
|
# GET https://my.host.com/api/v1/objects/2?api_key=api_key&api_password=api_password
|
65
|
-
client.show(2).json
|
64
|
+
client.show(2).json
|
66
65
|
=> { 'object_json' : 'is_parsed_here' }
|
67
66
|
```
|
68
67
|
|
69
68
|
## Best Guide
|
70
69
|
|
71
|
-
|
70
|
+
The best guide is [this
|
72
71
|
test](https://github.com/johnmcconnell/json_client/blob/master/spec/json_client/base_spec.rb).
|
73
72
|
Sorry non rspec people
|
74
73
|
|
data/json_client.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_dependency 'rest-client', '~> 1.7.2'
|
22
|
+
spec.add_dependency 'attr_init', '~> 0.0.4'
|
22
23
|
|
23
24
|
spec.add_development_dependency "simplecov", "~> 0.8.0"
|
24
25
|
spec.add_development_dependency "coveralls", "~> 0.7.0"
|
data/lib/json_client.rb
CHANGED
data/lib/json_client/base.rb
CHANGED
@@ -1,11 +1,47 @@
|
|
1
1
|
require 'rest_client'
|
2
|
+
|
3
|
+
require 'json_client/dsl'
|
2
4
|
require 'json_client/responses'
|
3
5
|
require 'json_client/requests'
|
6
|
+
require 'json_client/base_serializer'
|
7
|
+
require 'json_client/empty_serializer'
|
8
|
+
require 'json_client/base_requests/index'
|
9
|
+
require 'json_client/base_requests/show'
|
10
|
+
require 'json_client/base_requests/create'
|
11
|
+
require 'json_client/base_requests/update'
|
12
|
+
require 'json_client/base_requests/destroy'
|
13
|
+
require 'json_client/base_responses/index'
|
14
|
+
require 'json_client/base_responses/show'
|
15
|
+
require 'json_client/base_responses/create'
|
16
|
+
require 'json_client/base_responses/update'
|
17
|
+
require 'json_client/base_responses/destroy'
|
4
18
|
|
5
19
|
module JsonClient
|
6
20
|
class Base
|
21
|
+
extend DSL
|
7
22
|
attr_reader :api_key, :api_password, :pather
|
8
23
|
|
24
|
+
requests do |r|
|
25
|
+
r.on :index, use: BaseRequests::Index.new
|
26
|
+
r.on :show, use: BaseRequests::Show.new
|
27
|
+
r.on :create, use: BaseRequests::Create.new
|
28
|
+
r.on :update, use: BaseRequests::Update.new
|
29
|
+
r.on :destroy, use: BaseRequests::Destroy.new
|
30
|
+
end
|
31
|
+
|
32
|
+
responses do |r|
|
33
|
+
r.on :index, use: BaseResponses::Index
|
34
|
+
r.on :show, use: BaseResponses::Show
|
35
|
+
r.on :create, use: BaseResponses::Create
|
36
|
+
r.on :update, use: BaseResponses::Update
|
37
|
+
r.on :destroy, use: BaseResponses::Destroy
|
38
|
+
end
|
39
|
+
|
40
|
+
serializers do |s|
|
41
|
+
s.on :create, :update, :destroy, use: BaseSerializer.new
|
42
|
+
s.on :index, :show, use: EmptySerializer.new
|
43
|
+
end
|
44
|
+
|
9
45
|
def initialize(pather, config)
|
10
46
|
@api_key = config[:api_key]
|
11
47
|
@api_password = config[:api_password]
|
@@ -14,52 +50,42 @@ module JsonClient
|
|
14
50
|
end
|
15
51
|
|
16
52
|
def index
|
17
|
-
|
18
|
-
responders.index.new(response.body, response.code)
|
53
|
+
result(:index, nil)
|
19
54
|
end
|
20
55
|
|
21
56
|
def show(id)
|
22
|
-
|
23
|
-
responders.show.new(response.body, response.code)
|
57
|
+
result(:show, nil, id)
|
24
58
|
end
|
25
59
|
|
26
60
|
def create(model)
|
27
|
-
|
28
|
-
request_path, auth_params, model
|
29
|
-
)
|
30
|
-
responders.create.new(response.body, response.code)
|
61
|
+
result(:create, model)
|
31
62
|
end
|
32
63
|
|
33
64
|
def update(id, model)
|
34
|
-
|
35
|
-
request_path(id), auth_params, model
|
36
|
-
)
|
37
|
-
responders.update.new(response.body, response.code)
|
65
|
+
result(:update, model, id)
|
38
66
|
end
|
39
67
|
|
40
68
|
def destroy(id)
|
41
|
-
|
42
|
-
request_path(id), auth_params
|
43
|
-
)
|
44
|
-
responders.destroy.new(response.body, response.code)
|
69
|
+
result(:destroy, nil, id)
|
45
70
|
end
|
46
71
|
|
47
72
|
protected
|
48
73
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
def responders
|
54
|
-
@responders ||= Responses.new
|
74
|
+
def result(name, model, id = nil)
|
75
|
+
response_class = responses.public_send(name)
|
76
|
+
response_class.new(fetch_response(name, model, id))
|
55
77
|
end
|
56
78
|
|
57
|
-
def
|
58
|
-
|
79
|
+
def fetch_response(name, model, id = nil)
|
80
|
+
path = pather.path(id)
|
81
|
+
request = requests.public_send(name)
|
82
|
+
serializer = serializers.public_send(name)
|
83
|
+
params = serializer.serialize(model)
|
84
|
+
fetch(path, request, params)
|
59
85
|
end
|
60
86
|
|
61
|
-
def
|
62
|
-
|
87
|
+
def fetch(path, request, params)
|
88
|
+
request.fetch(path, auth_params, params)
|
63
89
|
end
|
64
90
|
|
65
91
|
def auth_params
|
@@ -3,20 +3,14 @@ require_relative 'request'
|
|
3
3
|
module JsonClient
|
4
4
|
module BaseRequests
|
5
5
|
class Create < Request
|
6
|
-
def fetch(url, auth_params,
|
6
|
+
def fetch(url, auth_params, params)
|
7
7
|
client.post(
|
8
8
|
url,
|
9
|
-
auth_params.merge(
|
9
|
+
auth_params.merge(params).to_json,
|
10
10
|
content_type: :json,
|
11
11
|
accept: :json
|
12
12
|
)
|
13
13
|
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def create_params(model)
|
18
|
-
model.to_h
|
19
|
-
end
|
20
14
|
end
|
21
15
|
end
|
22
16
|
end
|
@@ -3,20 +3,14 @@ require_relative 'request'
|
|
3
3
|
module JsonClient
|
4
4
|
module BaseRequests
|
5
5
|
class Update < Request
|
6
|
-
def fetch(url, auth_params,
|
6
|
+
def fetch(url, auth_params, params)
|
7
7
|
client.put(
|
8
8
|
url,
|
9
|
-
auth_params.merge(
|
9
|
+
auth_params.merge(params).to_json,
|
10
10
|
content_type: :json,
|
11
11
|
accept: :json
|
12
12
|
)
|
13
13
|
end
|
14
|
-
|
15
|
-
protected
|
16
|
-
|
17
|
-
def update_params(model)
|
18
|
-
model.to_h
|
19
|
-
end
|
20
14
|
end
|
21
15
|
end
|
22
16
|
end
|
@@ -1,19 +1,18 @@
|
|
1
1
|
module JsonClient
|
2
2
|
module BaseResponses
|
3
3
|
class Response
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :raw_response
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
@
|
8
|
-
@code = code
|
6
|
+
def initialize(raw_response)
|
7
|
+
@raw_response = raw_response
|
9
8
|
end
|
10
9
|
|
11
|
-
def
|
12
|
-
|
10
|
+
def method_missing(name, *args)
|
11
|
+
raw_response.public_send(name, *args)
|
13
12
|
end
|
14
13
|
|
15
|
-
def
|
16
|
-
|
14
|
+
def json
|
15
|
+
parse_json
|
17
16
|
end
|
18
17
|
|
19
18
|
protected
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'json_client/dsl/serializers_collector'
|
2
|
+
require 'json_client/dsl/requests_collector'
|
3
|
+
require 'json_client/dsl/on_use_collector'
|
4
|
+
|
5
|
+
module JsonClient
|
6
|
+
module DSL
|
7
|
+
def serializers_collector
|
8
|
+
@serializers_collector ||= SerializersCollector.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def serializers
|
12
|
+
collector = serializers_collector
|
13
|
+
|
14
|
+
yield collector
|
15
|
+
|
16
|
+
define_method :serializers do
|
17
|
+
collector
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def requests_collector
|
22
|
+
@requests_collector ||= RequestsCollector.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def requests
|
26
|
+
collector = requests_collector
|
27
|
+
|
28
|
+
yield collector
|
29
|
+
|
30
|
+
define_method :requests do
|
31
|
+
collector
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def responses_collector
|
36
|
+
@responses_collector ||= OnUseCollector.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def responses
|
40
|
+
collector = responses_collector
|
41
|
+
|
42
|
+
yield collector
|
43
|
+
|
44
|
+
define_method :responses do
|
45
|
+
collector
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module JsonClient
|
2
|
+
module DSL
|
3
|
+
class OnUseCollector
|
4
|
+
def on(*names, opts)
|
5
|
+
validate_opts!(opts)
|
6
|
+
|
7
|
+
thing = opts[:use]
|
8
|
+
|
9
|
+
Array(names).each do |name|
|
10
|
+
define_singleton_method(name) do
|
11
|
+
thing
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def collector_name
|
20
|
+
'thing'
|
21
|
+
end
|
22
|
+
|
23
|
+
def use_not_defined_message
|
24
|
+
"use: `#{collector_name}` must be defined"
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_opts!(opts)
|
28
|
+
fail use_not_defined_message if opts[:use].nil?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/json_client/version.rb
CHANGED
@@ -18,14 +18,20 @@ describe JsonClient::BaseResponses::Response do
|
|
18
18
|
%q<{ "hello" : "world" }>
|
19
19
|
end
|
20
20
|
|
21
|
+
let(:response) do
|
22
|
+
Object.new.tap do |r|
|
23
|
+
def r.code; 200; end
|
24
|
+
def r.body; %q<{ "hello" : "world" }>; end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
21
28
|
let(:json) do
|
22
29
|
JSON.parse(body)
|
23
30
|
end
|
24
31
|
|
25
32
|
subject do
|
26
33
|
described_class.new(
|
27
|
-
|
28
|
-
code
|
34
|
+
response
|
29
35
|
)
|
30
36
|
end
|
31
37
|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rspec/collection_matchers'
|
3
|
+
require 'json_client'
|
4
|
+
require 'vcr'
|
5
|
+
|
6
|
+
VCR.configure do |c|
|
7
|
+
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
8
|
+
c.hook_into :webmock
|
9
|
+
end
|
10
|
+
|
11
|
+
describe JsonClient::DSL::Collector do
|
12
|
+
let(:collected) do
|
13
|
+
'hello world'
|
14
|
+
end
|
15
|
+
|
16
|
+
subject { described_class.new }
|
17
|
+
|
18
|
+
describe '::method_missing' do
|
19
|
+
it 'defines the method and returns the first argument' do
|
20
|
+
subject.index collected
|
21
|
+
|
22
|
+
expect(subject.index).to eq 'hello world'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rspec/collection_matchers'
|
3
|
+
require 'json_client'
|
4
|
+
require 'vcr'
|
5
|
+
|
6
|
+
VCR.configure do |c|
|
7
|
+
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
8
|
+
c.hook_into :webmock
|
9
|
+
end
|
10
|
+
|
11
|
+
describe JsonClient::DSL::SerializersCollector do
|
12
|
+
let(:collected) do
|
13
|
+
'hello world'
|
14
|
+
end
|
15
|
+
|
16
|
+
subject { described_class.new }
|
17
|
+
|
18
|
+
describe '::method_missing' do
|
19
|
+
it 'defines the method and returns the first argument' do
|
20
|
+
subject.on :a, :b, use: 'hello'
|
21
|
+
|
22
|
+
expect(subject.a).to eq 'hello'
|
23
|
+
expect(subject.b).to eq 'hello'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rspec/collection_matchers'
|
3
|
+
require 'json_client'
|
4
|
+
require 'vcr'
|
5
|
+
|
6
|
+
VCR.configure do |c|
|
7
|
+
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
8
|
+
c.hook_into :webmock
|
9
|
+
end
|
10
|
+
|
11
|
+
describe JsonClient::DSL do
|
12
|
+
let(:example_class) do
|
13
|
+
class A
|
14
|
+
extend JsonClient::DSL
|
15
|
+
|
16
|
+
requests do |r|
|
17
|
+
r.on :index, use: 'abc'
|
18
|
+
r.on :delete, use: 'xyz'
|
19
|
+
end
|
20
|
+
|
21
|
+
serializers do |s|
|
22
|
+
s.on :index, :show, :read, use: 'hello'
|
23
|
+
s.on :delete, use: 'world'
|
24
|
+
end
|
25
|
+
|
26
|
+
responses do |r|
|
27
|
+
r.on :index, use: '0'
|
28
|
+
r.on :show, use: '1'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# return the class
|
33
|
+
A
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:serializers) do
|
37
|
+
example_class.new.serializers
|
38
|
+
end
|
39
|
+
|
40
|
+
let(:requests) do
|
41
|
+
example_class.new.requests
|
42
|
+
end
|
43
|
+
|
44
|
+
let(:responses) do
|
45
|
+
example_class.new.responses
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
describe '#serializers' do
|
50
|
+
it 'adds a way of dynamically adding serializers' do
|
51
|
+
|
52
|
+
expect(serializers.index).to eq 'hello'
|
53
|
+
expect(serializers.show).to eq 'hello'
|
54
|
+
expect(serializers.read).to eq 'hello'
|
55
|
+
|
56
|
+
expect(serializers.delete).to eq 'world'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#responses' do
|
61
|
+
it 'adds a way of dynamically adding responses' do
|
62
|
+
expect(responses.index).to eq '0'
|
63
|
+
expect(responses.show).to eq '1'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#requests' do
|
68
|
+
it 'adds a way of dynamically adding requests' do
|
69
|
+
expect(requests.index).to eq 'abc'
|
70
|
+
expect(requests.delete).to eq 'xyz'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- johnmcconnell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.7.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: attr_init
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.4
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.0.4
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: simplecov
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,8 +164,16 @@ files:
|
|
150
164
|
- lib/json_client/base_responses/response.rb
|
151
165
|
- lib/json_client/base_responses/show.rb
|
152
166
|
- lib/json_client/base_responses/update.rb
|
167
|
+
- lib/json_client/base_serializer.rb
|
168
|
+
- lib/json_client/dsl.rb
|
169
|
+
- lib/json_client/dsl/collector.rb
|
170
|
+
- lib/json_client/dsl/on_use_collector.rb
|
171
|
+
- lib/json_client/dsl/requests_collector.rb
|
172
|
+
- lib/json_client/dsl/serializers_collector.rb
|
173
|
+
- lib/json_client/empty_serializer.rb
|
153
174
|
- lib/json_client/pather.rb
|
154
175
|
- lib/json_client/requests.rb
|
176
|
+
- lib/json_client/resource_handler.rb
|
155
177
|
- lib/json_client/responses.rb
|
156
178
|
- lib/json_client/version.rb
|
157
179
|
- spec/fixtures/vcr_cassettes/all_success.yml
|
@@ -163,6 +185,9 @@ files:
|
|
163
185
|
- spec/json_client/base_requests/request_spec.rb
|
164
186
|
- spec/json_client/base_responses/response_spec.rb
|
165
187
|
- spec/json_client/base_spec.rb
|
188
|
+
- spec/json_client/dsl/collector_spec.rb
|
189
|
+
- spec/json_client/dsl/serializers_collector_spec.rb
|
190
|
+
- spec/json_client/dsl_spec.rb
|
166
191
|
- spec/json_client/pather_spec.rb
|
167
192
|
- spec/json_client/requests_spec.rb
|
168
193
|
- spec/json_client/responses_spec.rb
|
@@ -202,6 +227,9 @@ test_files:
|
|
202
227
|
- spec/json_client/base_requests/request_spec.rb
|
203
228
|
- spec/json_client/base_responses/response_spec.rb
|
204
229
|
- spec/json_client/base_spec.rb
|
230
|
+
- spec/json_client/dsl/collector_spec.rb
|
231
|
+
- spec/json_client/dsl/serializers_collector_spec.rb
|
232
|
+
- spec/json_client/dsl_spec.rb
|
205
233
|
- spec/json_client/pather_spec.rb
|
206
234
|
- spec/json_client/requests_spec.rb
|
207
235
|
- spec/json_client/responses_spec.rb
|