fastly 1.6.1 → 1.7.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/CHANGELOG.md +2 -3
- data/lib/fastly.rb +13 -5
- data/lib/fastly/acl.rb +62 -0
- data/lib/fastly/acl_entry.rb +61 -0
- data/lib/fastly/client.rb +5 -0
- data/lib/fastly/gem_version.rb +1 -1
- data/test/fastly/acl_entry_test.rb +110 -0
- data/test/fastly/acl_test.rb +163 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c4e9505c38a1b0faecb9f4736028f0f65e97aeb
|
4
|
+
data.tar.gz: 0551e76c05ef7cf3ef0a6273ad9b3c5386741c44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a097ce55c406517e7d35e70beb08381bf7f49f5e0aac21f42be35d1fe3c2a2b9c78ce17342675590484d9e71501e2d84e73098f6310795d3fc146c748de8f7b8
|
7
|
+
data.tar.gz: fef8fa3bb0a808fc7644107592c9195e77d85708cacad79c42a11cfbcf17f878597177045ec7b072a8f791974c401f91fba52bad31a0b07c0797840301417ff9
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [
|
4
|
-
|
5
|
-
[Full Changelog](https://github.com/fastly/fastly-ruby/compare/v1.6.0...HEAD)
|
3
|
+
## [v1.6.1](https://github.com/fastly/fastly-ruby/tree/v1.6.1) (2016-09-06)
|
4
|
+
[Full Changelog](https://github.com/fastly/fastly-ruby/compare/v1.6.0...v1.6.1)
|
6
5
|
|
7
6
|
**Merged pull requests:**
|
8
7
|
|
data/lib/fastly.rb
CHANGED
@@ -9,6 +9,8 @@ require 'fastly/fetcher'
|
|
9
9
|
require 'fastly/client'
|
10
10
|
require 'fastly/base'
|
11
11
|
require 'fastly/belongs_to_service_and_version'
|
12
|
+
require 'fastly/acl'
|
13
|
+
require 'fastly/acl_entry'
|
12
14
|
require 'fastly/backend'
|
13
15
|
require 'fastly/cache_setting'
|
14
16
|
require 'fastly/condition'
|
@@ -142,7 +144,7 @@ class Fastly
|
|
142
144
|
client.get_stats('/stats/regions')
|
143
145
|
end
|
144
146
|
|
145
|
-
[User, Customer, Backend, CacheSetting, Condition, Dictionary, DictionaryItem, Director, Domain, Header, Healthcheck, Gzip, Match, RequestSetting, ResponseObject, Service, S3Logging, Syslog, VCL, Version].each do |klass|
|
147
|
+
[ACL, ACLEntry, User, Customer, Backend, CacheSetting, Condition, Dictionary, DictionaryItem, Director, Domain, Header, Healthcheck, Gzip, Match, RequestSetting, ResponseObject, Service, S3Logging, Syslog, VCL, Version].each do |klass|
|
146
148
|
type = Util.class_to_path(klass)
|
147
149
|
|
148
150
|
if klass.respond_to?(:pluralize)
|
@@ -151,6 +153,12 @@ class Fastly
|
|
151
153
|
plural = "#{type}s"
|
152
154
|
end
|
153
155
|
|
156
|
+
if klass.respond_to?(:singularize)
|
157
|
+
singular = klass.singularize
|
158
|
+
else
|
159
|
+
singular = type
|
160
|
+
end
|
161
|
+
|
154
162
|
# unless the class doesn't have a list path or it already exists
|
155
163
|
unless klass.list_path.nil? || klass.respond_to?("list_#{plural}".to_sym)
|
156
164
|
send :define_method, "list_#{plural}".to_sym do |*args|
|
@@ -158,19 +166,19 @@ class Fastly
|
|
158
166
|
end
|
159
167
|
end
|
160
168
|
|
161
|
-
send :define_method, "get_#{
|
169
|
+
send :define_method, "get_#{singular}".to_sym do |*args|
|
162
170
|
get(klass, *args)
|
163
171
|
end
|
164
172
|
|
165
|
-
send :define_method, "create_#{
|
173
|
+
send :define_method, "create_#{singular}".to_sym do |obj|
|
166
174
|
create(klass, obj)
|
167
175
|
end
|
168
176
|
|
169
|
-
send :define_method, "update_#{
|
177
|
+
send :define_method, "update_#{singular}".to_sym do |obj|
|
170
178
|
update(klass, obj)
|
171
179
|
end
|
172
180
|
|
173
|
-
send :define_method, "delete_#{
|
181
|
+
send :define_method, "delete_#{singular}".to_sym do |obj|
|
174
182
|
delete(klass, obj)
|
175
183
|
end
|
176
184
|
end
|
data/lib/fastly/acl.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
class Fastly
|
2
|
+
# Acces Control List configuration
|
3
|
+
class ACL < BelongsToServiceAndVersion
|
4
|
+
attr_accessor :id, :service_id, :name
|
5
|
+
|
6
|
+
##
|
7
|
+
# :attr: service_id
|
8
|
+
#
|
9
|
+
# The id of the service this belongs to.
|
10
|
+
|
11
|
+
##
|
12
|
+
# :attr: version
|
13
|
+
#
|
14
|
+
# The number of the version this belongs to.
|
15
|
+
|
16
|
+
##
|
17
|
+
# :attr: name
|
18
|
+
#
|
19
|
+
# The name for the ACL.
|
20
|
+
|
21
|
+
##
|
22
|
+
# List ACL entries that belong to the ACL
|
23
|
+
def list_entries
|
24
|
+
fetcher.list_acl_entries(:service_id => service_id, :acl_id => id)
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Create an ACL entry and add it to the ACL
|
29
|
+
#
|
30
|
+
def create_entry(opts = {})
|
31
|
+
fetcher.create_acl_entry(
|
32
|
+
service_id: service_id,
|
33
|
+
acl_id: id,
|
34
|
+
ip: opts[:ip],
|
35
|
+
negated: opts[:negated],
|
36
|
+
subnet: opts[:subnet],
|
37
|
+
comment: opts[:comment]
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Retrieve an ACL entry
|
43
|
+
#
|
44
|
+
def get_entry(entry_id)
|
45
|
+
fetcher.get_acl_entry(service_id, id, entry_id)
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Update an ACL entry
|
50
|
+
#
|
51
|
+
def update_entry(entry)
|
52
|
+
fetcher.update_acl_entry(entry)
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Delete an ACL entry
|
57
|
+
#
|
58
|
+
def delete_entry(entry)
|
59
|
+
fetcher.delete_acl_entry(entry)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
class Fastly
|
4
|
+
# Acces Control List Entry configuration
|
5
|
+
class ACLEntry < Base
|
6
|
+
attr_accessor :id, :service_id, :ip, :subnet, :acl_id, :negated, :comment
|
7
|
+
|
8
|
+
##
|
9
|
+
# :attr: ip
|
10
|
+
#
|
11
|
+
# The IP address.
|
12
|
+
|
13
|
+
##
|
14
|
+
# :attr: subnet
|
15
|
+
#
|
16
|
+
# Optional subnet for the IP address.
|
17
|
+
|
18
|
+
##
|
19
|
+
# :attr: acl_id
|
20
|
+
#
|
21
|
+
# The ACL this entry belongs to.
|
22
|
+
|
23
|
+
##
|
24
|
+
# :attr: negated
|
25
|
+
#
|
26
|
+
# A boolean that will negate the match if true.
|
27
|
+
|
28
|
+
##
|
29
|
+
# :attr: comment
|
30
|
+
#
|
31
|
+
# A descriptive note.
|
32
|
+
|
33
|
+
def self.get_path(service_id, acl_id, id)
|
34
|
+
"/service/#{service_id}/acl/#{acl_id}/entry/#{CGI.escape(id)}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.post_path(opts)
|
38
|
+
"/service/#{opts[:service_id]}/acl/#{opts[:acl_id]}/entry"
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.put_path(object)
|
42
|
+
get_path(object.service_id, object.acl_id, object.id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.delete_path(object)
|
46
|
+
put_path(object)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.list_path(opts = {})
|
50
|
+
"/service/#{opts[:service_id]}/acl/#{opts[:acl_id]}/entries"
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.singularize
|
54
|
+
'acl_entry'
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.pluralize
|
58
|
+
'acl_entries'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/fastly/client.rb
CHANGED
@@ -102,6 +102,11 @@ class Fastly
|
|
102
102
|
extras = params.delete(:headers) || {}
|
103
103
|
uri = URI.parse(url)
|
104
104
|
http = Net::HTTP.new(uri.host, uri.port)
|
105
|
+
|
106
|
+
if uri.is_a? URI::HTTPS
|
107
|
+
http.use_ssl = true
|
108
|
+
end
|
109
|
+
|
105
110
|
resp = http.request Net::HTTP::Purge.new(uri.request_uri, headers(extras))
|
106
111
|
|
107
112
|
fail Error, resp.body unless resp.kind_of?(Net::HTTPSuccess)
|
data/lib/fastly/gem_version.rb
CHANGED
@@ -0,0 +1,110 @@
|
|
1
|
+
require'test_helper'
|
2
|
+
|
3
|
+
describe Fastly::ACLEntry do
|
4
|
+
let(:fastly) { Fastly.new(api_key: 'secret') }
|
5
|
+
let(:service) { fastly.create_service(name: 'acl_service') }
|
6
|
+
let(:acl) { fastly.create_acl(service_id: service.id, version: 1, name: 'acl_group') }
|
7
|
+
let(:acl_entry) { fastly.create_acl_entry(service_id: 'serviceid', acl_id: 'aclid', ip: '1.2.3.5') }
|
8
|
+
let(:response) { nil }
|
9
|
+
|
10
|
+
before do
|
11
|
+
# Must be first
|
12
|
+
stub_request(:any, /api.fastly.com/)
|
13
|
+
.to_return(:status => 200, :body => response)
|
14
|
+
|
15
|
+
# shared Service
|
16
|
+
create_service_response_body = JSON.dump(
|
17
|
+
'id' => 'aclserviceid',
|
18
|
+
'name' => 'acl_service'
|
19
|
+
)
|
20
|
+
stub_request(:post, 'https://api.fastly.com/service')
|
21
|
+
.to_return(:status => 200, :body => create_service_response_body)
|
22
|
+
|
23
|
+
# shared ACL
|
24
|
+
create_acl_response_body = JSON.dump(
|
25
|
+
'id' => 'aclid',
|
26
|
+
'name' => 'acl_group'
|
27
|
+
)
|
28
|
+
stub_request(:post, 'https://api.fastly.com/service/aclserviceid/version/1/acl')
|
29
|
+
.to_return(:status => 200, :body => create_acl_response_body)
|
30
|
+
|
31
|
+
# shared ACLEntry
|
32
|
+
create_acl_entry_response_body = JSON.dump(
|
33
|
+
'id' => 'aclentryid',
|
34
|
+
'acl_id' => 'aclid',
|
35
|
+
'ip' => '1.2.3.5',
|
36
|
+
'service_id' => 'serviceid'
|
37
|
+
)
|
38
|
+
|
39
|
+
stub_request(:post, 'https://api.fastly.com/service/serviceid/acl/aclid/entry')
|
40
|
+
.to_return(:status => 200, :body => create_acl_entry_response_body)
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#create_entry' do
|
44
|
+
let(:response) do
|
45
|
+
JSON.dump(
|
46
|
+
'id' => 'aclentryid',
|
47
|
+
'ip' => '1.2.3.5'
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'creates an ACL entry' do
|
52
|
+
assert_equal acl_entry.ip, '1.2.3.5'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#get_entry' do
|
57
|
+
let(:response) do
|
58
|
+
JSON.dump(
|
59
|
+
'id' => 'aclentryid',
|
60
|
+
'ip' => '1.2.3.5'
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'gets an ACL entry' do
|
65
|
+
assert_equal fastly.get_acl_entry('serviceid', 1, 'aclentryid').ip, acl_entry.ip
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#update_entry' do
|
70
|
+
let(:response) do
|
71
|
+
JSON.dump(
|
72
|
+
'id' => 'aclentryid',
|
73
|
+
'ip' => '1.2.3.5',
|
74
|
+
'subnet' => 8
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'updates an ACL entry' do
|
79
|
+
acl_entry.subnet = 8
|
80
|
+
fastly.update_acl_entry(acl_entry)
|
81
|
+
assert_equal acl_entry.ip, '1.2.3.5'
|
82
|
+
assert_equal acl_entry.subnet, 8
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#delete_entry' do
|
87
|
+
let(:response) { '{"status": "ok"}' }
|
88
|
+
|
89
|
+
it 'deletes an ACL entry' do
|
90
|
+
assert_equal fastly.delete_acl_entry(acl_entry), true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#list_entries' do
|
95
|
+
let(:response) do
|
96
|
+
JSON.dump(
|
97
|
+
[
|
98
|
+
{
|
99
|
+
'id' => 'aclentryid',
|
100
|
+
'ip' => '1.2.3.5'
|
101
|
+
}
|
102
|
+
]
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'lists ACL entries' do
|
107
|
+
assert_equal fastly.list_acl_entries.first.id, 'aclentryid'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require'test_helper'
|
2
|
+
|
3
|
+
describe Fastly::ACL do
|
4
|
+
let(:fastly) { Fastly.new(api_key: 'secret') }
|
5
|
+
let(:service) { fastly.create_service(name: 'acl_service') }
|
6
|
+
let(:acl) { fastly.create_acl(service_id: service.id, version: 1, name: 'acl_group') }
|
7
|
+
let(:acl_entry) { acl.create_entry(ip: '1.2.3.5') }
|
8
|
+
let(:response) { nil }
|
9
|
+
|
10
|
+
before do
|
11
|
+
# Must be first
|
12
|
+
stub_request(:any, /api.fastly.com/)
|
13
|
+
.to_return(:status => 200, :body => response)
|
14
|
+
|
15
|
+
# shared Service
|
16
|
+
create_service_response_body = JSON.dump(
|
17
|
+
'id' => 'aclserviceid',
|
18
|
+
'name' => 'acl_service'
|
19
|
+
)
|
20
|
+
stub_request(:post, 'https://api.fastly.com/service')
|
21
|
+
.to_return(:status => 200, :body => create_service_response_body)
|
22
|
+
|
23
|
+
# shared ACL
|
24
|
+
create_acl_response_body = JSON.dump(
|
25
|
+
'id' => 'aclid',
|
26
|
+
'name' => 'acl_group'
|
27
|
+
)
|
28
|
+
stub_request(:post, 'https://api.fastly.com/service/aclserviceid/version/1/acl')
|
29
|
+
.to_return(:status => 200, :body => create_acl_response_body)
|
30
|
+
|
31
|
+
# shared ACLEntry
|
32
|
+
create_acl_entry_response_body = JSON.dump(
|
33
|
+
'id' => 'aclentryid',
|
34
|
+
'ip' => '1.2.3.5'
|
35
|
+
)
|
36
|
+
stub_request(:post, 'https://api.fastly.com/service//acl/aclid/entry')
|
37
|
+
.to_return(:status => 200, :body => create_acl_entry_response_body)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'creates a new ACL' do
|
41
|
+
assert_equal acl.name, 'acl_group'
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#get_acl' do
|
45
|
+
let(:response) do
|
46
|
+
JSON.dump(
|
47
|
+
'id' => 'aclid',
|
48
|
+
'name' => 'acl_group'
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'gets an ACL' do
|
53
|
+
assert_equal fastly.get_acl(service.id, 1, acl.id).id, 'aclid'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#update_acl' do
|
58
|
+
let(:response) do
|
59
|
+
JSON.dump(
|
60
|
+
'id' => 'aclid',
|
61
|
+
'name' => 'acl_group_2'
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'updates an ACL' do
|
66
|
+
acl.name = 'acl_group_2'
|
67
|
+
assert_equal fastly.update_acl(acl).name, 'acl_group_2'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#delete_acl' do
|
72
|
+
let(:response) { '{"status": "ok"}' }
|
73
|
+
|
74
|
+
it 'deletes an ACL' do
|
75
|
+
assert_equal fastly.delete_acl(acl), true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#list_acls' do
|
80
|
+
let(:response) do
|
81
|
+
JSON.dump(
|
82
|
+
[
|
83
|
+
{
|
84
|
+
'id' => 'aclid',
|
85
|
+
'name' => 'acl_group'
|
86
|
+
}
|
87
|
+
]
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'lists ACLs' do
|
92
|
+
assert_equal fastly.list_acls.first.id, 'aclid'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#create_entry' do
|
97
|
+
let(:response) do
|
98
|
+
JSON.dump(
|
99
|
+
'id' => 'aclentryid',
|
100
|
+
'ip' => '1.2.3.5'
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'creates an ACL entry' do
|
105
|
+
assert_equal acl_entry.ip, '1.2.3.5'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#get_entry' do
|
110
|
+
let(:response) do
|
111
|
+
JSON.dump(
|
112
|
+
'id' => 'aclentryid',
|
113
|
+
'ip' => '1.2.3.5'
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'gets an ACL entry' do
|
118
|
+
assert_equal acl.get_entry('aclentryid').ip, acl_entry.ip
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#update_entry' do
|
123
|
+
let(:response) do
|
124
|
+
JSON.dump(
|
125
|
+
'id' => 'aclentryid',
|
126
|
+
'ip' => '1.2.3.5',
|
127
|
+
'subnet' => 8
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'updates an ACL entry' do
|
132
|
+
acl_entry.subnet = 8
|
133
|
+
acl.update_entry(acl_entry)
|
134
|
+
assert_equal acl_entry.ip, '1.2.3.5'
|
135
|
+
assert_equal acl_entry.subnet, 8
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '#delete_entry' do
|
140
|
+
let(:response) { '{"status": "ok"}' }
|
141
|
+
|
142
|
+
it 'deletes an ACL entry' do
|
143
|
+
assert_equal acl.delete_entry(acl_entry), true
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#list_entries' do
|
148
|
+
let(:response) do
|
149
|
+
JSON.dump(
|
150
|
+
[
|
151
|
+
{
|
152
|
+
'id' => 'aclentryid',
|
153
|
+
'ip' => '1.2.3.5'
|
154
|
+
}
|
155
|
+
]
|
156
|
+
)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'lists ACL entries' do
|
160
|
+
assert_equal acl.list_entries.first.id, 'aclentryid'
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fastly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Client library for the Fastly acceleration system
|
14
14
|
email:
|
@@ -37,6 +37,8 @@ files:
|
|
37
37
|
- gemfiles/HEAD.gemfile
|
38
38
|
- gemfiles/ruby_1.9.gemfile
|
39
39
|
- lib/fastly.rb
|
40
|
+
- lib/fastly/acl.rb
|
41
|
+
- lib/fastly/acl_entry.rb
|
40
42
|
- lib/fastly/backend.rb
|
41
43
|
- lib/fastly/base.rb
|
42
44
|
- lib/fastly/belongs_to_service_and_version.rb
|
@@ -68,6 +70,8 @@ files:
|
|
68
70
|
- test/admin_test.rb
|
69
71
|
- test/api_key_test.rb
|
70
72
|
- test/common.rb
|
73
|
+
- test/fastly/acl_entry_test.rb
|
74
|
+
- test/fastly/acl_test.rb
|
71
75
|
- test/fastly/client_test.rb
|
72
76
|
- test/fastly/dictionary_test.rb
|
73
77
|
- test/fastly/util_test.rb
|
@@ -104,6 +108,8 @@ test_files:
|
|
104
108
|
- test/admin_test.rb
|
105
109
|
- test/api_key_test.rb
|
106
110
|
- test/common.rb
|
111
|
+
- test/fastly/acl_entry_test.rb
|
112
|
+
- test/fastly/acl_test.rb
|
107
113
|
- test/fastly/client_test.rb
|
108
114
|
- test/fastly/dictionary_test.rb
|
109
115
|
- test/fastly/util_test.rb
|