opensrs 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/rubocop-analysis.yml +43 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.rubocop.yml +131 -0
- data/Gemfile +2 -11
- data/Gemfile.lock +89 -43
- data/{LICENSE → LICENSE.txt} +4 -2
- data/README.md +126 -0
- data/Rakefile +1 -40
- data/SECURITY.md +12 -0
- data/bin/console +9 -0
- data/bin/setup +7 -0
- data/lib/opensrs.rb +7 -6
- data/lib/opensrs/response.rb +18 -12
- data/lib/opensrs/sanitizable_string.rb +21 -0
- data/lib/opensrs/server.rb +55 -27
- data/lib/opensrs/version.rb +1 -3
- data/lib/opensrs/xml_processor.rb +36 -37
- data/lib/opensrs/xml_processor/libxml.rb +66 -45
- data/lib/opensrs/xml_processor/nokogiri.rb +57 -38
- data/opensrs.gemspec +28 -72
- metadata +111 -54
- data/README.rdoc +0 -105
- data/spec/opensrs/server_spec.rb +0 -109
- data/spec/opensrs/version_spec.rb +0 -9
- data/spec/opensrs/xml_processor/libxml_spec.rb +0 -254
- data/spec/opensrs/xml_processor/nokogiri_spec.rb +0 -228
- data/spec/spec_helper.rb +0 -6
data/README.rdoc
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
= OpenSRS
|
2
|
-
|
3
|
-
{<img src="https://travis-ci.org/voxxit/opensrs.png" />}[https://travis-ci.org/voxxit/opensrs]
|
4
|
-
{<img src="https://codeclimate.com/github/voxxit/opensrs.png" />}[https://codeclimate.com/github/voxxit/opensrs]
|
5
|
-
|
6
|
-
This (unofficial) OpenSRS gem provides basic support to connect to, and utilize the OpenSRS API. This library has been well-tested in high-performance production
|
7
|
-
environments. More information on the API can be located here:
|
8
|
-
|
9
|
-
http://www.opensrs.com/site/resources/documentation/api
|
10
|
-
|
11
|
-
== Installation
|
12
|
-
|
13
|
-
You can install this gem by doing the following:
|
14
|
-
|
15
|
-
$ gem install opensrs
|
16
|
-
|
17
|
-
You can then include it in a Ruby project, like so:
|
18
|
-
|
19
|
-
require 'opensrs'
|
20
|
-
|
21
|
-
Alternatively, you can include it in a Rails 2.x project in the <tt>environment.rb</tt> file, like so:
|
22
|
-
|
23
|
-
config.gem "opensrs"
|
24
|
-
|
25
|
-
For Rails 3.x, use the <tt>Gemfile</tt>:
|
26
|
-
|
27
|
-
gem "opensrs"
|
28
|
-
|
29
|
-
== Usage
|
30
|
-
|
31
|
-
This library provides basic functionality for interacting with the OpenSRS XML API.
|
32
|
-
|
33
|
-
* Connection handling
|
34
|
-
* Error reporting
|
35
|
-
* XML encoding
|
36
|
-
* XML decoding
|
37
|
-
|
38
|
-
Currently, the library supports LibXML and Nokogiri as XML parsers. By default, it uses LibXML to parse and generate XML. If you'd like to use Nokogiri (1.4.7 and below) for parsing XML, then in one of your app initializers add the following line:
|
39
|
-
|
40
|
-
OpenSRS::Server.xml_processor = :nokogiri
|
41
|
-
|
42
|
-
To connect, instantiate a new <tt>OpenSRS::Server</tt> object:
|
43
|
-
|
44
|
-
server = OpenSRS::Server.new(
|
45
|
-
:server => "https://rr-n1-tor.opensrs.net:55443/",
|
46
|
-
:username => "testing",
|
47
|
-
:password => "53cr3t",
|
48
|
-
:key => "c633be3170c7fb3fb29e2f99b84be2410..."
|
49
|
-
)
|
50
|
-
|
51
|
-
NOTE: Connecting to OpenSRS requires that you add the IP(s) you're connecting from to their whitelist. Login to the testing or production servers, and add your IP(s) under Profile Management > Add IPs for Script/API Access. IP changes take about one hour to take effect.
|
52
|
-
|
53
|
-
Once you have a server connection class, you can build from this to create the methods that you need. For instance, let's say we want to grab our account balance. The OpenSRS XML API takes a couple of attributes for all commands. You can include those here:
|
54
|
-
|
55
|
-
def get_balance
|
56
|
-
server.call(
|
57
|
-
:action => "GET_BALANCE",
|
58
|
-
:object => "BALANCE"
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
Sometimes you might need to include attributes for the command, such as a cookie, or the data attributes themselves. You can do this, too:
|
63
|
-
|
64
|
-
def create_nameserver(nameserver)
|
65
|
-
server.call(
|
66
|
-
:action => "CREATE",
|
67
|
-
:object => "NAMESERVER",
|
68
|
-
:cookie => "366828736:3210384",
|
69
|
-
:attributes => {
|
70
|
-
:name => nameserver.hostname,
|
71
|
-
:ipaddress => "212.112.123.11"
|
72
|
-
}
|
73
|
-
)
|
74
|
-
end
|
75
|
-
|
76
|
-
Responses from OpenSRS are returned in an OpenSRS::Response object, which gives you access to a multitude of things.
|
77
|
-
|
78
|
-
* <tt>response.response</tt> - This gives you the response in a Hash form, which is highly accessible to most other actions in your application.
|
79
|
-
* <tt>response.errors</tt> - If there are errors which come back from OpenSRS, they are returned here. If not, it returns nil.
|
80
|
-
* <tt>response.success?</tt> - Returns true if the response was labeled as successful. If not, it returns false.
|
81
|
-
* <tt>response.request_xml</tt> - Returns raw request XML.
|
82
|
-
* <tt>response.response_xml</tt> - Returns raw response XML.
|
83
|
-
|
84
|
-
== Bugs/Feature Requests
|
85
|
-
|
86
|
-
If you have any bugs or feature requests for this gem, feel free to add them in the Issues portion of the GitHub repository here:
|
87
|
-
|
88
|
-
http://github.com/voxxit/opensrs/issues
|
89
|
-
|
90
|
-
== Note on Patches/Pull Requests
|
91
|
-
|
92
|
-
* Fork the project.
|
93
|
-
* Make your feature addition or bug fix.
|
94
|
-
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
95
|
-
* Commit, do not mess with rakefile, version, or history. If you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull.
|
96
|
-
* Send me a pull request. Bonus points for topic branches.
|
97
|
-
|
98
|
-
== Contributors (in order of appearance)
|
99
|
-
|
100
|
-
* Josh Delsman
|
101
|
-
* Glenn Roberts
|
102
|
-
|
103
|
-
== Copyright
|
104
|
-
|
105
|
-
Copyright (c) 2010-2013 Josh Delsman. Distributed under the MIT license. See LICENSE for details.
|
data/spec/opensrs/server_spec.rb
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe OpenSRS::Server do
|
4
|
-
let(:server) { OpenSRS::Server.new }
|
5
|
-
|
6
|
-
describe '#new' do
|
7
|
-
it 'allows timeouts to be set' do
|
8
|
-
server = OpenSRS::Server.new({ :timeout => 90 })
|
9
|
-
server.timeout.should == 90
|
10
|
-
server.open_timeout.should be_nil
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'allows open timeouts to be set' do
|
14
|
-
server = OpenSRS::Server.new({ :timeout => 90, :open_timeout => 10 })
|
15
|
-
server.timeout.should eq(90)
|
16
|
-
server.open_timeout.should eq(10)
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'leaves it up to Net::HTTP if no timeouts given' do
|
20
|
-
server.timeout.should be_nil
|
21
|
-
server.open_timeout.should be_nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe ".call" do
|
26
|
-
let(:response) { double(:body => 'some response') }
|
27
|
-
let(:header) { {"some" => "header" } }
|
28
|
-
let(:xml) { '<some xml></some xml>' }
|
29
|
-
let(:response_xml) { xml }
|
30
|
-
let(:xml_processor) { double OpenSRS::XmlProcessor }
|
31
|
-
let(:http) { double(Net::HTTP, :use_ssl= => true, :verify_mode= => true) }
|
32
|
-
|
33
|
-
before :each do
|
34
|
-
server.stub(:headers).and_return header
|
35
|
-
xml_processor.stub(:build).and_return xml
|
36
|
-
xml_processor.stub(:parse).and_return response_xml
|
37
|
-
server.stub(:xml_processor).and_return xml_processor
|
38
|
-
http.stub(:post).and_return response
|
39
|
-
Net::HTTP.stub(:new).and_return http
|
40
|
-
end
|
41
|
-
|
42
|
-
it "builds XML request" do
|
43
|
-
xml_processor.should_receive(:build).with(:protocol => "XCP", :some => 'option')
|
44
|
-
server.call(:some => 'option')
|
45
|
-
end
|
46
|
-
|
47
|
-
it "posts to given path" do
|
48
|
-
server.server = URI.parse 'http://with-path.com/endpoint'
|
49
|
-
http.should_receive(:post).with('/endpoint', xml, header).and_return double.as_null_object
|
50
|
-
server.call
|
51
|
-
end
|
52
|
-
|
53
|
-
it "parses the response" do
|
54
|
-
xml_processor.should_receive(:parse).with(response.body)
|
55
|
-
server.call(:some => 'option')
|
56
|
-
end
|
57
|
-
|
58
|
-
it "posts to root path" do
|
59
|
-
server.server = URI.parse 'http://root-path.com/'
|
60
|
-
http.should_receive(:post).with('/', xml, header).and_return double.as_null_object
|
61
|
-
server.call
|
62
|
-
end
|
63
|
-
|
64
|
-
it "defaults path to '/'" do
|
65
|
-
server.server = URI.parse 'http://no-path.com'
|
66
|
-
http.should_receive(:post).with('/', xml, header).and_return double.as_null_object
|
67
|
-
server.call
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'allows overriding of default (Net:HTTP) timeouts' do
|
71
|
-
server.timeout = 90
|
72
|
-
|
73
|
-
http.should_receive(:open_timeout=).with(90)
|
74
|
-
http.should_receive(:read_timeout=).with(90)
|
75
|
-
|
76
|
-
server.call( { :some => 'data' } )
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'allows overriding of default (Net:HTTP) timeouts' do
|
80
|
-
server.timeout = 180
|
81
|
-
server.open_timeout = 30
|
82
|
-
|
83
|
-
http.should_receive(:read_timeout=).with(180)
|
84
|
-
http.should_receive(:open_timeout=).with(180)
|
85
|
-
http.should_receive(:open_timeout=).with(30)
|
86
|
-
|
87
|
-
server.call( { :some => 'data' } )
|
88
|
-
end
|
89
|
-
|
90
|
-
it 're-raises Net:HTTP timeouts' do
|
91
|
-
http.should_receive(:post).and_raise err = Timeout::Error.new('test')
|
92
|
-
expect { server.call }.to raise_exception OpenSRS::TimeoutError
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
describe "#test xml processor" do
|
97
|
-
context "on class initialization" do
|
98
|
-
it { server.xml_processor.should eql(OpenSRS::XmlProcessor::Libxml) }
|
99
|
-
end
|
100
|
-
|
101
|
-
context "on changing xml processor" do
|
102
|
-
before(:each) do
|
103
|
-
OpenSRS::Server.xml_processor = :nokogiri
|
104
|
-
end
|
105
|
-
|
106
|
-
it { server.xml_processor.should eql(OpenSRS::XmlProcessor::Nokogiri) }
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
@@ -1,254 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'date'
|
3
|
-
|
4
|
-
describe OpenSRS::XmlProcessor::Libxml do
|
5
|
-
describe ".build" do
|
6
|
-
it "should create XML for a nested hash" do
|
7
|
-
attributes = {:foo => {:bar => 'baz'}}
|
8
|
-
xml = OpenSRS::XmlProcessor::Libxml.build(attributes)
|
9
|
-
xml.should eq %{<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<OPS_envelope>\n <header>\n <version>0.9</version>\n </header>\n <body>\n <data_block>\n <dt_assoc>\n <item key=\"foo\">\n <dt_assoc>\n <item key=\"bar\">baz</item>\n </dt_assoc>\n </item>\n </dt_assoc>\n </data_block>\n </body>\n</OPS_envelope>\n}
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
describe '.encode_data' do
|
14
|
-
context "on a 3 element array" do
|
15
|
-
before(:each) do
|
16
|
-
@e = OpenSRS::XmlProcessor::Libxml.encode_data([1,2,3])
|
17
|
-
end
|
18
|
-
|
19
|
-
it "is a REXML::Element" do
|
20
|
-
@e.should be_an_instance_of(LibXML::XML::Node)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "is a dt_array" do
|
24
|
-
@e.name.should == 'dt_array'
|
25
|
-
end
|
26
|
-
|
27
|
-
it "has 3 children all called <item>" do
|
28
|
-
@e.should have(3).children
|
29
|
-
@e.children[0].name.should == "item"
|
30
|
-
@e.children[1].name.should == "item"
|
31
|
-
@e.children[2].name.should == "item"
|
32
|
-
end
|
33
|
-
|
34
|
-
it "has children with keys 0, 1 and 2" do
|
35
|
-
@e.children[0].attributes["key"].should == "0"
|
36
|
-
@e.children[1].attributes["key"].should == "1"
|
37
|
-
@e.children[2].attributes["key"].should == "2"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context "on a hash" do
|
42
|
-
before(:each) do
|
43
|
-
@e = OpenSRS::XmlProcessor::Libxml.encode_data({:name => "kitteh"})
|
44
|
-
end
|
45
|
-
|
46
|
-
it "is a REXML::Element" do
|
47
|
-
@e.should be_an_instance_of(LibXML::XML::Node)
|
48
|
-
end
|
49
|
-
|
50
|
-
it "is a dt_assoc" do
|
51
|
-
@e.name.should == 'dt_assoc'
|
52
|
-
end
|
53
|
-
|
54
|
-
it "has an <item> child with the right key" do
|
55
|
-
@e.should have(1).children
|
56
|
-
@e.children[0].name.should == 'item'
|
57
|
-
@e.children[0].attributes["key"].should == 'name'
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
context "on a nested hash" do
|
62
|
-
before(:each) do
|
63
|
-
@e = OpenSRS::XmlProcessor::Libxml.encode_data({:suggestion => {:maximum => "10"}})
|
64
|
-
end
|
65
|
-
|
66
|
-
it "is a REXML::Element" do
|
67
|
-
@e.should be_an_instance_of(LibXML::XML::Node)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "is a dt_assoc" do
|
71
|
-
@e.name.should == 'dt_assoc'
|
72
|
-
end
|
73
|
-
|
74
|
-
it "has an <item> child with the correct children" do
|
75
|
-
@e.should have(1).children
|
76
|
-
suggestion = @e.children[0]
|
77
|
-
suggestion.name.should == 'item'
|
78
|
-
suggestion.attributes["key"].should == 'suggestion'
|
79
|
-
|
80
|
-
suggestion.should have(1).children
|
81
|
-
dt_assoc = suggestion.children[0]
|
82
|
-
dt_assoc.name.should == 'dt_assoc'
|
83
|
-
|
84
|
-
dt_assoc.should have(1).children
|
85
|
-
maximum = dt_assoc.children[0]
|
86
|
-
maximum.name.should == 'item'
|
87
|
-
maximum.attributes["key"].should == 'maximum'
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
context "produces a scalar" do
|
92
|
-
it "from a string" do
|
93
|
-
OpenSRS::XmlProcessor::Libxml.encode_data("cheezburger").to_s.should == "cheezburger"
|
94
|
-
end
|
95
|
-
|
96
|
-
it "from a string with XML characters" do
|
97
|
-
OpenSRS::XmlProcessor::Libxml.encode_data("<smile>").to_s.should == "<smile>"
|
98
|
-
end
|
99
|
-
|
100
|
-
it "from an integer" do
|
101
|
-
OpenSRS::XmlProcessor::Libxml.encode_data(12345).to_s.should == "12345"
|
102
|
-
end
|
103
|
-
|
104
|
-
it "from a date" do
|
105
|
-
date = Date.parse("2010/02/12")
|
106
|
-
OpenSRS::XmlProcessor::Libxml.encode_data(date).to_s.should == "2010-02-12"
|
107
|
-
end
|
108
|
-
|
109
|
-
it "from a symbol" do
|
110
|
-
OpenSRS::XmlProcessor::Libxml.encode_data(:name).to_s.should == "name"
|
111
|
-
end
|
112
|
-
|
113
|
-
it "from true or false" do
|
114
|
-
OpenSRS::XmlProcessor::Libxml.encode_data(true).to_s.should == "true"
|
115
|
-
OpenSRS::XmlProcessor::Libxml.encode_data(false).to_s.should == "false"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
describe '.parse' do
|
121
|
-
it "should handle scalar values" do
|
122
|
-
xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
|
123
|
-
<!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
|
124
|
-
<OPS_envelope>
|
125
|
-
<header>
|
126
|
-
<version>1.0</version>
|
127
|
-
</header>
|
128
|
-
<body>
|
129
|
-
<data_block>
|
130
|
-
<dt_scalar>Tom Jones</dt_scalar>
|
131
|
-
</data_block>
|
132
|
-
</body>
|
133
|
-
</OPS_envelope>}
|
134
|
-
|
135
|
-
resp = OpenSRS::XmlProcessor::Libxml.parse(xml)
|
136
|
-
resp.should == "Tom Jones"
|
137
|
-
end
|
138
|
-
|
139
|
-
it "should handle associate arrays with arrays of values" do
|
140
|
-
xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
|
141
|
-
<!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
|
142
|
-
<OPS_envelope>
|
143
|
-
<header>
|
144
|
-
<version>1.0</version>
|
145
|
-
</header>
|
146
|
-
<body>
|
147
|
-
<data_block>
|
148
|
-
<dt_assoc>
|
149
|
-
<item key='domain_list'>
|
150
|
-
<dt_array>
|
151
|
-
<item key='0'>ns1.example.com</item>
|
152
|
-
<item key='1'>ns2.example.com</item>
|
153
|
-
<item key='2'>ns3.example.com</item>
|
154
|
-
</dt_array>
|
155
|
-
</item>
|
156
|
-
</dt_assoc>
|
157
|
-
</data_block>
|
158
|
-
</body>
|
159
|
-
</OPS_envelope>}
|
160
|
-
|
161
|
-
resp = OpenSRS::XmlProcessor::Libxml.parse(xml)
|
162
|
-
resp["domain_list"].class.should == Array
|
163
|
-
resp["domain_list"][0].should == "ns1.example.com"
|
164
|
-
resp["domain_list"][1].should == "ns2.example.com"
|
165
|
-
resp["domain_list"][2].should == "ns3.example.com"
|
166
|
-
end
|
167
|
-
|
168
|
-
it "should handle associative arrays containing other associative arrays" do
|
169
|
-
xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
|
170
|
-
<!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
|
171
|
-
<OPS_envelope>
|
172
|
-
<header>
|
173
|
-
<version>1.0</version>
|
174
|
-
</header>
|
175
|
-
<body>
|
176
|
-
<data_block>
|
177
|
-
<dt_assoc>
|
178
|
-
<item key="contact_set">
|
179
|
-
<dt_assoc>
|
180
|
-
<item key='owner'>
|
181
|
-
<dt_assoc>
|
182
|
-
<item key='first_name'>Tom</item>
|
183
|
-
<item key='last_name'>Jones</item>
|
184
|
-
</dt_assoc>
|
185
|
-
</item>
|
186
|
-
<item key='tech'>
|
187
|
-
<dt_assoc>
|
188
|
-
<item key='first_name'>Anne</item>
|
189
|
-
<item key='last_name'>Smith</item>
|
190
|
-
</dt_assoc>
|
191
|
-
</item>
|
192
|
-
</dt_assoc>
|
193
|
-
</item>
|
194
|
-
</dt_assoc>
|
195
|
-
</data_block>
|
196
|
-
</body>
|
197
|
-
</OPS_envelope>}
|
198
|
-
|
199
|
-
resp = OpenSRS::XmlProcessor::Libxml.parse(xml)
|
200
|
-
|
201
|
-
resp["contact_set"]["owner"]["first_name"].should == "Tom"
|
202
|
-
resp["contact_set"]["owner"]["last_name"].should == "Jones"
|
203
|
-
resp["contact_set"]["tech"]["first_name"].should == "Anne"
|
204
|
-
resp["contact_set"]["tech"]["last_name"].should == "Smith"
|
205
|
-
end
|
206
|
-
|
207
|
-
context "with a balance enquiry example response" do
|
208
|
-
before(:each) do
|
209
|
-
xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
|
210
|
-
<!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
|
211
|
-
<OPS_envelope>
|
212
|
-
<header>
|
213
|
-
<version>0.9</version>
|
214
|
-
</header>
|
215
|
-
<body>
|
216
|
-
<data_block>
|
217
|
-
<dt_assoc>
|
218
|
-
<item key="protocol">XCP</item>
|
219
|
-
<item key="action">REPLY</item>
|
220
|
-
<item key="object">BALANCE</item>
|
221
|
-
<item key="is_success">1</item>
|
222
|
-
<item key="response_code">200</item>
|
223
|
-
<item key="response_text">Command successful</item>
|
224
|
-
<item key="attributes">
|
225
|
-
<dt_assoc>
|
226
|
-
<item key="balance">8549.18</item>
|
227
|
-
<item key="hold_balance">1676.05</item>
|
228
|
-
</dt_assoc>
|
229
|
-
</item>
|
230
|
-
</dt_assoc>
|
231
|
-
</data_block>
|
232
|
-
</body>
|
233
|
-
</OPS_envelope>}
|
234
|
-
|
235
|
-
@resp = OpenSRS::XmlProcessor::Libxml.parse(xml)
|
236
|
-
end
|
237
|
-
|
238
|
-
it "produces a hash" do
|
239
|
-
@resp.should be_an_instance_of(Hash)
|
240
|
-
end
|
241
|
-
|
242
|
-
it "has top level keys" do
|
243
|
-
@resp["protocol"].should == "XCP"
|
244
|
-
@resp["action"].should == "REPLY"
|
245
|
-
@resp["object"].should == "BALANCE"
|
246
|
-
end
|
247
|
-
|
248
|
-
it "has second level keys" do
|
249
|
-
@resp["attributes"]["balance"].should == "8549.18"
|
250
|
-
@resp["attributes"]["hold_balance"].should == "1676.05"
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|