raml_ruby 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +1 -0
- data/fixtures/include_1.raml +7 -0
- data/fixtures/schemas/canonicalSchemas.raml +3 -0
- data/fixtures/schemas/filesystem/file.json +1 -0
- data/fixtures/schemas/filesystem/files.json +1 -0
- data/fixtures/schemas/filesystem/fileupdate.json +1 -0
- data/fixtures/schemas/filesystem/relative/test.json +1 -0
- data/lib/raml.rb +104 -0
- data/lib/raml/exceptions.rb +27 -0
- data/lib/raml/mixin/bodies.rb +32 -0
- data/lib/raml/mixin/documentable.rb +32 -0
- data/lib/raml/mixin/global.rb +20 -0
- data/lib/raml/mixin/headers.rb +22 -0
- data/lib/raml/mixin/merge.rb +24 -0
- data/lib/raml/mixin/parent.rb +54 -0
- data/lib/raml/mixin/validation.rb +49 -0
- data/lib/raml/node.rb +219 -0
- data/lib/raml/node/abstract_method.rb +61 -0
- data/lib/raml/node/abstract_resource.rb +165 -0
- data/lib/raml/node/abstract_resource_circular.rb +5 -0
- data/lib/raml/node/body.rb +94 -0
- data/lib/raml/node/documentation.rb +28 -0
- data/lib/raml/node/header.rb +4 -0
- data/lib/raml/node/method.rb +106 -0
- data/lib/raml/node/parameter/abstract_parameter.rb +251 -0
- data/lib/raml/node/parameter/base_uri_parameter.rb +6 -0
- data/lib/raml/node/parameter/form_parameter.rb +6 -0
- data/lib/raml/node/parameter/query_parameter.rb +6 -0
- data/lib/raml/node/parameter/uri_parameter.rb +7 -0
- data/lib/raml/node/parametized_reference.rb +15 -0
- data/lib/raml/node/reference.rb +4 -0
- data/lib/raml/node/resource.rb +26 -0
- data/lib/raml/node/resource_type.rb +20 -0
- data/lib/raml/node/resource_type_reference.rb +5 -0
- data/lib/raml/node/response.rb +32 -0
- data/lib/raml/node/root.rb +246 -0
- data/lib/raml/node/schema.rb +41 -0
- data/lib/raml/node/schema_reference.rb +5 -0
- data/lib/raml/node/template.rb +55 -0
- data/lib/raml/node/trait.rb +18 -0
- data/lib/raml/node/trait_reference.rb +5 -0
- data/lib/raml/parser.rb +57 -0
- data/lib/raml/parser/include.rb +25 -0
- data/lib/raml/patch/hash.rb +6 -0
- data/lib/raml/patch/module.rb +12 -0
- data/lib/raml/version.rb +3 -0
- data/raml_ruby.gemspec +35 -0
- data/raml_spec_reqs.md +276 -0
- data/templates/abstract_parameter.slim +68 -0
- data/templates/body.slim +15 -0
- data/templates/collapse.slim +10 -0
- data/templates/documentation.slim +2 -0
- data/templates/method.slim +38 -0
- data/templates/resource.slim +33 -0
- data/templates/response.slim +13 -0
- data/templates/root.slim +39 -0
- data/templates/style.sass +119 -0
- data/test/apis/box-api.raml +4224 -0
- data/test/apis/instagram-api.raml +3378 -0
- data/test/apis/stripe-api.raml +12227 -0
- data/test/apis/twilio-rest-api.raml +6618 -0
- data/test/apis/twitter-rest-api.raml +34284 -0
- data/test/raml/body_spec.rb +268 -0
- data/test/raml/documentation_spec.rb +49 -0
- data/test/raml/header_spec.rb +17 -0
- data/test/raml/include_spec.rb +40 -0
- data/test/raml/method_spec.rb +701 -0
- data/test/raml/parameter/abstract_parameter_spec.rb +564 -0
- data/test/raml/parameter/form_parameter_spec.rb +17 -0
- data/test/raml/parameter/query_parameter_spec.rb +33 -0
- data/test/raml/parameter/uri_parameter_spec.rb +44 -0
- data/test/raml/parser_spec.rb +53 -0
- data/test/raml/raml_spec.rb +32 -0
- data/test/raml/resource_spec.rb +440 -0
- data/test/raml/resource_type_spec.rb +51 -0
- data/test/raml/response_spec.rb +251 -0
- data/test/raml/root_spec.rb +655 -0
- data/test/raml/schema_spec.rb +110 -0
- data/test/raml/spec_helper.rb +11 -0
- data/test/raml/template_spec.rb +98 -0
- data/test/raml/trait_spec.rb +31 -0
- metadata +337 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
module Raml
|
2
|
+
class ParametizedReference < Reference
|
3
|
+
# @!attribute [rw] parameters
|
4
|
+
# @return [Hash<String,String>] parameters to interpolate when instantiating the resouce type or trait.
|
5
|
+
attr_accessor :parameters
|
6
|
+
|
7
|
+
# @param name [String] the resource type or trait name.
|
8
|
+
# @param parameters [Hash<String,String>] parameters to interpolate when instantiating the resouce type or trait.
|
9
|
+
# @param parent [Raml::Node] the parent node.
|
10
|
+
def initialize(name, parameters={}, parent)
|
11
|
+
super name, parent
|
12
|
+
@parameters = parameters
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Raml
|
2
|
+
class Resource < AbstractResource
|
3
|
+
inherit_class_attributes
|
4
|
+
|
5
|
+
regexp_property( /\A\//, ->(key,value) { Resource.new key, value, self } )
|
6
|
+
|
7
|
+
# @!attribute [r] resources
|
8
|
+
# @return [Hash<String, Raml::Resource>] the nested resources, keyed by the resource relative path.
|
9
|
+
|
10
|
+
children_by :resources, :name, Resource
|
11
|
+
|
12
|
+
self.doc_template = relative_path 'resource.slim'
|
13
|
+
|
14
|
+
# @private
|
15
|
+
def apply_resource_type
|
16
|
+
super
|
17
|
+
resources.values.each(&:apply_resource_type)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @private
|
21
|
+
def apply_traits
|
22
|
+
methods.values.each(&:apply_traits)
|
23
|
+
resources.values.each(&:apply_traits)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Raml
|
2
|
+
class ResourceType < Template
|
3
|
+
class Instance < AbstractResource
|
4
|
+
inherit_class_attributes
|
5
|
+
|
6
|
+
# @!attribute [rw] usage
|
7
|
+
# @return [String,nil] how the resource type should be used.
|
8
|
+
scalar_property :usage
|
9
|
+
end
|
10
|
+
|
11
|
+
# Instantiate a new resource type with the given parameters.
|
12
|
+
# @param params [Hash] the parameters to interpolate in the resource type.
|
13
|
+
# @return [Raml::ResourceType::Instance] the instantiated resouce type.
|
14
|
+
def instantiate(params)
|
15
|
+
instance = Instance.new( *interpolate(params), @parent )
|
16
|
+
instance.apply_resource_type
|
17
|
+
instance
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Raml
|
2
|
+
class Response < PropertiesNode
|
3
|
+
inherit_class_attributes
|
4
|
+
|
5
|
+
include Documentable
|
6
|
+
include Global
|
7
|
+
include Merge
|
8
|
+
include Parent
|
9
|
+
include Validation
|
10
|
+
include Bodies
|
11
|
+
include Headers
|
12
|
+
|
13
|
+
self.doc_template = relative_path 'response.slim'
|
14
|
+
|
15
|
+
def initialize(name, properties, parent)
|
16
|
+
super
|
17
|
+
@name = name.to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
# @private
|
21
|
+
def merge(other)
|
22
|
+
raise MergeError, "Response status codes don't match." if name != other.name
|
23
|
+
|
24
|
+
super
|
25
|
+
|
26
|
+
merge_properties other, :headers
|
27
|
+
merge_properties other, :bodies
|
28
|
+
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'sass'
|
2
|
+
require 'uri'
|
3
|
+
require 'uri_template'
|
4
|
+
|
5
|
+
module Raml
|
6
|
+
# RAML root node. Its parent is itself.
|
7
|
+
class Root < PropertiesNode
|
8
|
+
inherit_class_attributes
|
9
|
+
|
10
|
+
include Parent
|
11
|
+
include Validation
|
12
|
+
|
13
|
+
# @!attribute [rw] title
|
14
|
+
# @return [String] API title.
|
15
|
+
|
16
|
+
# @!attribute [rw] version
|
17
|
+
# @return [String,nil] API version.
|
18
|
+
|
19
|
+
# @!attribute [rw] base_uri
|
20
|
+
# @return [String] the API base URI.
|
21
|
+
|
22
|
+
# @!attribute [rw] protocols
|
23
|
+
# @return [Array<String>, nil] the supported protocols. Nil or an array of up to two string
|
24
|
+
# elements from the set "HTTP" and "HTTPS".
|
25
|
+
|
26
|
+
# @!attribute [rw] media_type
|
27
|
+
# @return [String] the default request and response body media type.
|
28
|
+
|
29
|
+
# @!attribute [r] documents
|
30
|
+
# @return [Array<Raml::Documentation>] the top level documentation.
|
31
|
+
|
32
|
+
# @!attribute [r] base_uri_parameters
|
33
|
+
# @return [Hash<String, Raml::Parameter::BaseUriParameter>] the base URI parameters, keyed
|
34
|
+
# by the parameter name.
|
35
|
+
|
36
|
+
# @!attribute [r] schemas
|
37
|
+
# @return [Hash<String, Raml::Schema>] the schema definitions, keyed by the schema name.
|
38
|
+
|
39
|
+
# @!attribute [r] resources
|
40
|
+
# @return [Hash<String, Raml::Resource>] the nested resources, keyed by the resource relative path.
|
41
|
+
|
42
|
+
# @!attribute [r] traits
|
43
|
+
# @return [Hash<String, Raml::Trait>] the trait definitions, keyed by the trait name.
|
44
|
+
|
45
|
+
# @!attribute [r] resource_types
|
46
|
+
# @return [Hash<String, Raml::ResourceType>] the resource type definitions, keyed by the resource type name.
|
47
|
+
|
48
|
+
scalar_property :title , :version , :base_uri ,
|
49
|
+
:protocols , :media_type
|
50
|
+
|
51
|
+
non_scalar_property :base_uri_parameters, :documentation , :schemas, :secured_by,
|
52
|
+
:security_schemes , :resource_types, :traits
|
53
|
+
|
54
|
+
regexp_property( /\A\//, ->(key,value) { Resource.new key, value, self } )
|
55
|
+
|
56
|
+
children_of :documents, Documentation
|
57
|
+
|
58
|
+
children_by :base_uri_parameters, :name, Parameter::BaseUriParameter
|
59
|
+
children_by :resources , :name, Resource
|
60
|
+
children_by :schemas , :name, Schema
|
61
|
+
children_by :traits , :name, Trait
|
62
|
+
children_by :resource_types , :name, ResourceType
|
63
|
+
|
64
|
+
alias :default_media_type :media_type
|
65
|
+
alias :trait_declarations :traits
|
66
|
+
alias :resource_type_declarations :resource_types
|
67
|
+
alias :schema_declarations :schemas
|
68
|
+
|
69
|
+
self.doc_template = relative_path 'root.slim'
|
70
|
+
|
71
|
+
def initialize(root_data)
|
72
|
+
super nil, root_data, self
|
73
|
+
end
|
74
|
+
|
75
|
+
# Applies resource types and traits, and inlines schemas. It should be called
|
76
|
+
# before documentation is generated.
|
77
|
+
def expand
|
78
|
+
unless @expanded
|
79
|
+
resources.values.each(&:apply_resource_type)
|
80
|
+
resources.values.each(&:apply_traits)
|
81
|
+
inline_reference SchemaReference, schemas, @children
|
82
|
+
@expanded = true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# @private
|
87
|
+
def resource_path
|
88
|
+
''
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def validate
|
94
|
+
raise RequiredPropertyMissing, 'Missing root title property.' if title.nil?
|
95
|
+
raise RequiredPropertyMissing, 'Missing root baseUri property' if base_uri.nil?
|
96
|
+
_validate_base_uri
|
97
|
+
end
|
98
|
+
|
99
|
+
def validate_title
|
100
|
+
validate_string :title, title
|
101
|
+
end
|
102
|
+
|
103
|
+
def _validate_base_uri
|
104
|
+
validate_string :base_uri, base_uri
|
105
|
+
|
106
|
+
# Check whether its a URL.
|
107
|
+
uri = parse_uri base_uri
|
108
|
+
|
109
|
+
# If the parser doesn't think its a URL or the URL is not for HTTP or HTTPS,
|
110
|
+
# try to parse it as a URL template.
|
111
|
+
if uri.nil? and not uri.kind_of? URI::HTTP
|
112
|
+
template = parse_template
|
113
|
+
|
114
|
+
# The template parser did not complain, but does it generate valid URLs?
|
115
|
+
uri = template.expand Hash[ template.variables.map {|var| [ var, 'a'] } ]
|
116
|
+
uri = parse_uri uri
|
117
|
+
raise InvalidProperty, 'baseUri property is not a URL or a URL template.' unless
|
118
|
+
uri and uri.kind_of? URI::HTTP
|
119
|
+
|
120
|
+
raise RequiredPropertyMissing, 'version property is required when baseUri template has version parameter' if
|
121
|
+
template.variables.include? 'version' and version.nil?
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def validate_protocols
|
126
|
+
if protocols
|
127
|
+
validate_array :protocols, protocols, String
|
128
|
+
|
129
|
+
@protocols.map!(&:upcase)
|
130
|
+
|
131
|
+
raise InvalidProperty, 'protocols property elements must be HTTP or HTTPS' unless
|
132
|
+
protocols.all? { |p| [ 'HTTP', 'HTTPS'].include? p }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def validate_media_type
|
137
|
+
if media_type
|
138
|
+
validate_string :media_type, media_type
|
139
|
+
raise InvalidProperty, 'mediaType property is malformed' unless media_type =~ Body::MEDIA_TYPE_RE
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def parse_schemas(schemas)
|
144
|
+
validate_array :schemas, schemas, Hash
|
145
|
+
|
146
|
+
raise InvalidProperty, 'schemas property must be an array of maps with string keys' unless
|
147
|
+
schemas.all? {|s| s.keys.all? {|k| k.is_a? String }}
|
148
|
+
|
149
|
+
raise InvalidProperty, 'schemas property must be an array of maps with string values' unless
|
150
|
+
schemas.all? {|s| s.values.all? {|v| v.is_a? String }}
|
151
|
+
|
152
|
+
raise InvalidProperty, 'schemas property contains duplicate schema names' unless
|
153
|
+
schemas.map(&:keys).flatten.uniq!.nil?
|
154
|
+
|
155
|
+
schemas.reduce({}) { |memo, map | memo.merge! map }.
|
156
|
+
map { |name, data| Schema.new name, data, self }
|
157
|
+
end
|
158
|
+
|
159
|
+
def parse_base_uri_parameters(base_uri_parameters)
|
160
|
+
validate_hash :base_uri_parameters, base_uri_parameters, String, Hash
|
161
|
+
|
162
|
+
raise InvalidProperty, 'baseUriParameters property can\'t contain reserved "version" parameter' if
|
163
|
+
base_uri_parameters.include? 'version'
|
164
|
+
|
165
|
+
base_uri_parameters.map { |name, data| Parameter::BaseUriParameter.new name, data, self }
|
166
|
+
end
|
167
|
+
|
168
|
+
def parse_documentation(documentation)
|
169
|
+
validate_array :documentation, documentation
|
170
|
+
|
171
|
+
raise InvalidProperty, 'documentation property must include at least one document or not be included' if
|
172
|
+
documentation.empty?
|
173
|
+
|
174
|
+
documentation.map { |doc| doc = doc.dup; Documentation.new doc.delete("title"), doc, self }
|
175
|
+
end
|
176
|
+
|
177
|
+
def parse_secured_by(data)
|
178
|
+
# XXX ignored for now
|
179
|
+
end
|
180
|
+
|
181
|
+
def parse_security_schemes(data)
|
182
|
+
# XXX ignored for now
|
183
|
+
end
|
184
|
+
|
185
|
+
def parse_resource_types(types)
|
186
|
+
validate_array :resource_types, types, Hash
|
187
|
+
|
188
|
+
raise InvalidProperty, 'resourceTypes property must be an array of maps with string keys' unless
|
189
|
+
types.all? {|t| t.keys.all? {|k| k.is_a? String }}
|
190
|
+
|
191
|
+
raise InvalidProperty, 'resourceTypes property must be an array of maps with map values' unless
|
192
|
+
types.all? {|t| t.values.all? {|v| v.is_a? Hash }}
|
193
|
+
|
194
|
+
raise InvalidProperty, 'resourceTypes property contains duplicate type names' unless
|
195
|
+
types.map(&:keys).flatten.uniq!.nil?
|
196
|
+
|
197
|
+
types.reduce({}) { |memo, map | memo.merge! map }.
|
198
|
+
map { |name, data| ResourceType.new name, data, self }
|
199
|
+
end
|
200
|
+
|
201
|
+
def parse_traits(traits)
|
202
|
+
validate_array :traits, traits, Hash
|
203
|
+
|
204
|
+
raise InvalidProperty, 'traits property must be an array of maps with string keys' unless
|
205
|
+
traits.all? {|t| t.keys.all? {|k| k.is_a? String }}
|
206
|
+
|
207
|
+
raise InvalidProperty, 'traits property must be an array of maps with map values' unless
|
208
|
+
traits.all? {|t| t.values.all? {|v| v.is_a? Hash }}
|
209
|
+
|
210
|
+
raise InvalidProperty, 'traits property contains duplicate trait names' unless
|
211
|
+
traits.map(&:keys).flatten.uniq!.nil?
|
212
|
+
|
213
|
+
traits.reduce({}) { |memo, map | memo.merge! map }.
|
214
|
+
map { |name, data| Trait.new name, data, self }
|
215
|
+
end
|
216
|
+
|
217
|
+
def parse_uri(uri)
|
218
|
+
URI.parse uri
|
219
|
+
rescue URI::InvalidURIError
|
220
|
+
nil
|
221
|
+
end
|
222
|
+
|
223
|
+
def parse_template
|
224
|
+
URITemplate::RFC6570.new base_uri
|
225
|
+
rescue URITemplate::RFC6570::Invalid
|
226
|
+
raise InvalidProperty, 'baseUri property is not a URL or a URL template.'
|
227
|
+
end
|
228
|
+
|
229
|
+
def inline_reference(reference_type, map, nodes)
|
230
|
+
nodes.map! do |node|
|
231
|
+
if node.is_a? reference_type
|
232
|
+
map[node.name]
|
233
|
+
else
|
234
|
+
inline_reference reference_type, map, node.children if node.respond_to? :children
|
235
|
+
node
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def style_sheet
|
241
|
+
File.open(self.class.relative_path('style.sass'), 'r') do |file|
|
242
|
+
Sass::Engine.new(file.read, syntax: :scss).render
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'json-schema'
|
2
|
+
|
3
|
+
module Raml
|
4
|
+
class Schema < ValueNode
|
5
|
+
# @return [Boolean] true if the schema appears to be an JSON Schema, false otherwise.
|
6
|
+
def json_schema?
|
7
|
+
/"\$schema":\s*"http:\/\/json-schema.org\/[^"]*"/ === @value
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [Boolean] true if the schema appears to be an XML Schema, false otherwise.
|
11
|
+
def xml_schema?
|
12
|
+
/<xs:schema [^>]*xmlns:xs="http:\/\/www\.w3\.org\/2001\/XMLSchema"[^>]*>/ === @value
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns HTML documenting the node and child nodes.
|
16
|
+
# @return [String] HTML documentation.
|
17
|
+
def document
|
18
|
+
highlight @value, parent.media_type
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def validate_value
|
24
|
+
validate_json if json_schema?
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_json
|
28
|
+
parsed_schema = JSON.parse @value
|
29
|
+
version = parsed_schema['$schema']
|
30
|
+
# json-schema gem doesn't handle this lastest version string
|
31
|
+
version = nil if version == 'http://json-schema.org/schema#'
|
32
|
+
# fix up schema versions URLs that don't end in "#""
|
33
|
+
version = "#{version}#" if version =~ /\Ahttps?:\/\/json-schema\.org\/draft-\d\d\/schema\z/
|
34
|
+
|
35
|
+
meta_schema = JSON::Validator.metaschema_for JSON::Validator.version_string_for version
|
36
|
+
JSON::Validator.validate! meta_schema, parsed_schema
|
37
|
+
rescue JSON::ParserError, JSON::Schema::SchemaError, JSON::Schema::ValidationError => e
|
38
|
+
raise InvalidSchema, "Could not parse JSON Schema: #{e}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
module Raml
|
4
|
+
class Template < ValueNode
|
5
|
+
|
6
|
+
# @private
|
7
|
+
def interpolate(params)
|
8
|
+
name = @name.clone
|
9
|
+
data = clone_data
|
10
|
+
interpolate_params name, params
|
11
|
+
interpolate_params data, params
|
12
|
+
[ name, data ]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def clone_data
|
18
|
+
# ugly but effective
|
19
|
+
Marshal.load Marshal.dump @value
|
20
|
+
end
|
21
|
+
|
22
|
+
def interpolate_params(value, params)
|
23
|
+
case value
|
24
|
+
when String
|
25
|
+
interpolate_params_string value, params
|
26
|
+
when Hash
|
27
|
+
value.map! { |key,val| [ interpolate_params(key, params), interpolate_params(val, params) ] }
|
28
|
+
when Array
|
29
|
+
value.map! { |val| interpolate_params val, params }
|
30
|
+
else
|
31
|
+
value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def interpolate_params_string(value, params)
|
36
|
+
value = value.dup if value.frozen?
|
37
|
+
|
38
|
+
value.gsub!(/(<<([^!\s>]+)(?:\s*\|\s*!(\w+))?>>)/) do |match|
|
39
|
+
param_name = $2
|
40
|
+
function = $3
|
41
|
+
|
42
|
+
param = params[param_name]
|
43
|
+
raise UnknownTypeOrTraitParameter, "#{param_name} is not a known parameter." if param.nil?
|
44
|
+
|
45
|
+
if function
|
46
|
+
raise UnknownTypeOrTraitParamFunction, function unless [ 'singularize', 'pluralize'].include? function
|
47
|
+
param = param.send function
|
48
|
+
end
|
49
|
+
|
50
|
+
param
|
51
|
+
end
|
52
|
+
value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|