raml_ruby 0.1.1

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.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +71 -0
  7. data/Rakefile +1 -0
  8. data/fixtures/include_1.raml +7 -0
  9. data/fixtures/schemas/canonicalSchemas.raml +3 -0
  10. data/fixtures/schemas/filesystem/file.json +1 -0
  11. data/fixtures/schemas/filesystem/files.json +1 -0
  12. data/fixtures/schemas/filesystem/fileupdate.json +1 -0
  13. data/fixtures/schemas/filesystem/relative/test.json +1 -0
  14. data/lib/raml.rb +104 -0
  15. data/lib/raml/exceptions.rb +27 -0
  16. data/lib/raml/mixin/bodies.rb +32 -0
  17. data/lib/raml/mixin/documentable.rb +32 -0
  18. data/lib/raml/mixin/global.rb +20 -0
  19. data/lib/raml/mixin/headers.rb +22 -0
  20. data/lib/raml/mixin/merge.rb +24 -0
  21. data/lib/raml/mixin/parent.rb +54 -0
  22. data/lib/raml/mixin/validation.rb +49 -0
  23. data/lib/raml/node.rb +219 -0
  24. data/lib/raml/node/abstract_method.rb +61 -0
  25. data/lib/raml/node/abstract_resource.rb +165 -0
  26. data/lib/raml/node/abstract_resource_circular.rb +5 -0
  27. data/lib/raml/node/body.rb +94 -0
  28. data/lib/raml/node/documentation.rb +28 -0
  29. data/lib/raml/node/header.rb +4 -0
  30. data/lib/raml/node/method.rb +106 -0
  31. data/lib/raml/node/parameter/abstract_parameter.rb +251 -0
  32. data/lib/raml/node/parameter/base_uri_parameter.rb +6 -0
  33. data/lib/raml/node/parameter/form_parameter.rb +6 -0
  34. data/lib/raml/node/parameter/query_parameter.rb +6 -0
  35. data/lib/raml/node/parameter/uri_parameter.rb +7 -0
  36. data/lib/raml/node/parametized_reference.rb +15 -0
  37. data/lib/raml/node/reference.rb +4 -0
  38. data/lib/raml/node/resource.rb +26 -0
  39. data/lib/raml/node/resource_type.rb +20 -0
  40. data/lib/raml/node/resource_type_reference.rb +5 -0
  41. data/lib/raml/node/response.rb +32 -0
  42. data/lib/raml/node/root.rb +246 -0
  43. data/lib/raml/node/schema.rb +41 -0
  44. data/lib/raml/node/schema_reference.rb +5 -0
  45. data/lib/raml/node/template.rb +55 -0
  46. data/lib/raml/node/trait.rb +18 -0
  47. data/lib/raml/node/trait_reference.rb +5 -0
  48. data/lib/raml/parser.rb +57 -0
  49. data/lib/raml/parser/include.rb +25 -0
  50. data/lib/raml/patch/hash.rb +6 -0
  51. data/lib/raml/patch/module.rb +12 -0
  52. data/lib/raml/version.rb +3 -0
  53. data/raml_ruby.gemspec +35 -0
  54. data/raml_spec_reqs.md +276 -0
  55. data/templates/abstract_parameter.slim +68 -0
  56. data/templates/body.slim +15 -0
  57. data/templates/collapse.slim +10 -0
  58. data/templates/documentation.slim +2 -0
  59. data/templates/method.slim +38 -0
  60. data/templates/resource.slim +33 -0
  61. data/templates/response.slim +13 -0
  62. data/templates/root.slim +39 -0
  63. data/templates/style.sass +119 -0
  64. data/test/apis/box-api.raml +4224 -0
  65. data/test/apis/instagram-api.raml +3378 -0
  66. data/test/apis/stripe-api.raml +12227 -0
  67. data/test/apis/twilio-rest-api.raml +6618 -0
  68. data/test/apis/twitter-rest-api.raml +34284 -0
  69. data/test/raml/body_spec.rb +268 -0
  70. data/test/raml/documentation_spec.rb +49 -0
  71. data/test/raml/header_spec.rb +17 -0
  72. data/test/raml/include_spec.rb +40 -0
  73. data/test/raml/method_spec.rb +701 -0
  74. data/test/raml/parameter/abstract_parameter_spec.rb +564 -0
  75. data/test/raml/parameter/form_parameter_spec.rb +17 -0
  76. data/test/raml/parameter/query_parameter_spec.rb +33 -0
  77. data/test/raml/parameter/uri_parameter_spec.rb +44 -0
  78. data/test/raml/parser_spec.rb +53 -0
  79. data/test/raml/raml_spec.rb +32 -0
  80. data/test/raml/resource_spec.rb +440 -0
  81. data/test/raml/resource_type_spec.rb +51 -0
  82. data/test/raml/response_spec.rb +251 -0
  83. data/test/raml/root_spec.rb +655 -0
  84. data/test/raml/schema_spec.rb +110 -0
  85. data/test/raml/spec_helper.rb +11 -0
  86. data/test/raml/template_spec.rb +98 -0
  87. data/test/raml/trait_spec.rb +31 -0
  88. metadata +337 -0
@@ -0,0 +1,5 @@
1
+ class Raml::AbstractResource < Raml::PropertiesNode
2
+ # @!attribute [r] type
3
+ # @return [Raml::ResourceType, Raml::ResourceTypeReference>] the resource type or resource type references, if any.
4
+ child_of :type, [ Raml::ResourceType, Raml::ResourceTypeReference ]
5
+ end
@@ -0,0 +1,94 @@
1
+ module Raml
2
+ class Body < PropertiesNode
3
+ inherit_class_attributes
4
+
5
+ include Global
6
+ include Merge
7
+ include Parent
8
+ include Validation
9
+
10
+ # @private
11
+ MEDIA_TYPE_RE = %r{[a-z\d][-\w.+!#$&^]{0,63}/[a-z\d][-\w.+!#$&^]{0,63}(;.*)?}oi
12
+
13
+ # @!attribute [rw] example
14
+ # @return [String, nil] an example of a valid body.
15
+
16
+ # @!attribute [r] form_parameters
17
+ # @return [Hash<String, Raml::Parameter::FormParameter>] the form parameters, keyed
18
+ # by the parameter name. Only valid for "application/x-www-form-urlencoded" and
19
+ # "multipart/form-data" media types.
20
+
21
+ # @!attribute [r] schema
22
+ # @return [Raml::Schema, nil] the body's schema. Only valid if the media type
23
+ # is not one of "application/x-www-form-urlencoded" or "multipart/form-data".
24
+
25
+ # @!attribute [r] media\_type
26
+ # @return [String] media type of the of body. An alias for #name.
27
+
28
+ scalar_property :example
29
+ non_scalar_property :form_parameters, :schema
30
+
31
+ alias_method :media_type, :name
32
+
33
+ self.doc_template = relative_path 'body.slim'
34
+
35
+ children_by :form_parameters, :name, Parameter::FormParameter
36
+
37
+ child_of :schema, [ Schema, SchemaReference ]
38
+
39
+ # Returns whether the body is a web form. Returns true for "application/x-www-form-urlencoded" and
40
+ # "multipart/form-data" media types.
41
+ # @return [Boolean] true if the body is a web form, false otherwise.
42
+ def web_form?
43
+ [ 'application/x-www-form-urlencoded', 'multipart/form-data' ].include? media_type
44
+ end
45
+
46
+ # @private
47
+ def merge(other)
48
+ raise MergeError, "Media types don't match." if media_type != other.media_type
49
+
50
+ super
51
+
52
+ merge_properties other, :form_parameters
53
+
54
+ if other.schema
55
+ @children.delete_if { |c| [ Schema, SchemaReference ].include? c.class } if schema
56
+ @children << other.schema
57
+ end
58
+
59
+ self
60
+ end
61
+
62
+ private
63
+
64
+ def validate_name
65
+ raise InvalidMediaType, 'body media type is invalid' unless media_type =~ Body::MEDIA_TYPE_RE
66
+ end
67
+
68
+ def parse_form_parameters(value)
69
+ validate_hash 'formParameters', value, String, Hash
70
+
71
+ value.map do |name, form_parameter_data|
72
+ Parameter::FormParameter.new name, form_parameter_data, self
73
+ end
74
+ end
75
+
76
+ def parse_schema(value)
77
+ validate_string :schema, value
78
+
79
+ if schema_declarations.include? value
80
+ SchemaReference.new value, self
81
+ else
82
+ Schema.new '_', value, self
83
+ end
84
+ end
85
+
86
+ def validate
87
+ if web_form?
88
+ raise InvalidProperty, 'schema property can\'t be defined for web forms.' if schema
89
+ raise RequiredPropertyMissing, 'formParameters property must be specified for web forms.' if
90
+ form_parameters.empty?
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,28 @@
1
+ require 'kramdown'
2
+
3
+ module Raml
4
+ class Documentation < PropertiesNode
5
+
6
+ # @!attribute [rw] content
7
+ # @return [String] the documentation content.
8
+
9
+ # @!attribute [rw] title
10
+ # @return [String] the documentation title. An alias for #name.
11
+
12
+ scalar_property :content
13
+ alias_method :title, :name
14
+
15
+ self.doc_template = relative_path 'documentation.slim'
16
+
17
+ private
18
+
19
+ def validate
20
+ raise InvalidProperty, 'document title cannot be empty.' if title.nil? or title.empty?
21
+ raise InvalidProperty, 'document content cannot be empty.' if content.nil? or content.empty?
22
+ end
23
+
24
+ def html_content
25
+ Kramdown::Document.new(content, input: :GFM).to_html
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,4 @@
1
+ module Raml
2
+ class Header < Parameter::AbstractParameter
3
+ end
4
+ end
@@ -0,0 +1,106 @@
1
+ module Raml
2
+ class Method < AbstractMethod
3
+ inherit_class_attributes
4
+
5
+ NAMES = %w(options get head post put delete trace connect patch)
6
+
7
+ # @!attribute [r] traits
8
+ # @return [Array<Raml::Trait, Raml::TraitReference>] the traits and trait references.
9
+
10
+ non_scalar_property :is
11
+
12
+ children_of :traits, [ Trait, TraitReference ]
13
+
14
+ self.doc_template = relative_path 'method.slim'
15
+
16
+ # @private
17
+ def apply_traits
18
+ # We apply resource traits before method traits, and apply traits at each level in
19
+ # the other they are listed (first to last, left to righ). Later traits scalar
20
+ # properties overwrite earlier ones. We end by merging a copy of the method, so
21
+ # that scalar properties in the method hierarchy overwrite those in the traits.
22
+ # We must apply the traits against the method first, as they may contain optional
23
+ # properties that depend on the method hiearchy.
24
+ cloned_self = self.clone
25
+
26
+ (@parent.traits + traits).
27
+ map { |trait| instantiate_trait trait }.
28
+ each { |trait| merge trait }
29
+
30
+ merge cloned_self
31
+ end
32
+
33
+ # @private
34
+ def merge(other)
35
+ super
36
+
37
+ merge_properties other, :headers
38
+ merge_properties other, :query_parameters
39
+ merge_properties other, :bodies
40
+ merge_properties other, :responses
41
+
42
+ # We may be applying a resource type, which will result in the merging of a method that may have
43
+ # traits, instead of a trait that can't have no traits.
44
+ if other.is_a? Method
45
+ # merge traits. insert the non-matching ones in the front, so they have the least priority.
46
+ match, no_match = other.traits.partition do |other_trait|
47
+ if other_trait.is_a? Trait
48
+ false
49
+ else # TraitReference
50
+ self.traits.any? do |self_trait|
51
+ self_trait.is_a?(TraitReference) &&
52
+ self_trait.name == other_trait.name &&
53
+ self_trait.parameters == other_trait.parameters
54
+ end
55
+ end
56
+ end
57
+ @children.unshift(*no_match)
58
+ end
59
+
60
+ self
61
+ end
62
+
63
+ private
64
+
65
+ def validate_name
66
+ raise InvalidMethod, "#{@name} is an unsupported HTTP method" unless NAMES.include? @name
67
+ end
68
+
69
+ def validate_parent
70
+ raise InvalidParent, "Parent of method cannot be nil." if @parent.nil?
71
+ end
72
+
73
+ def parse_is(value)
74
+ validate_array :is, value, [String, Hash]
75
+
76
+ value.map do |trait|
77
+ if trait.is_a? Hash
78
+ if trait.keys.size == 1 and trait_declarations.include? trait.keys.first
79
+ raise InvalidProperty, 'is property with map of trait name but params are not a map' unless
80
+ trait.values[0].is_a? Hash
81
+ TraitReference.new( *trait.first, self )
82
+ else
83
+ Trait.new '_', trait, self
84
+ end
85
+ else
86
+ raise UnknownTraitReference, "#{trait} referenced in method but not found in traits declaration." unless
87
+ trait_declarations.include? trait
88
+ TraitReference.new trait, self
89
+ end
90
+ end
91
+ end
92
+
93
+ def instantiate_trait(trait)
94
+ reserved_params = {
95
+ 'resourcePath' => @parent.resource_path,
96
+ 'resourcePathName' => @parent.resource_path.split('/')[-1],
97
+ 'methodName' => self.name
98
+ }
99
+ if TraitReference === trait
100
+ trait_declarations[trait.name].instantiate trait.parameters.merge reserved_params
101
+ else
102
+ trait.instantiate reserved_params
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,251 @@
1
+ module Raml
2
+ module Parameter
3
+ class AbstractParameter < PropertiesNode
4
+ inherit_class_attributes
5
+
6
+ include Documentable
7
+ include Merge
8
+ include Parent
9
+
10
+ VALID_TYPES = %w(string number integer date boolean file)
11
+
12
+ # @!attribute [rw] type
13
+ # @return [String] the value type. One of: "string", "number", "integer", "date",
14
+ # "boolean", or "file".
15
+
16
+ # @!attribute [rw] enum
17
+ # @return [Array<String>,nil] the possible values. Only valid for parameters of type "string".
18
+
19
+ # @!attribute [rw] pattern
20
+ # @return [Regexp,nil] a regular expression the value must match. Only valid for
21
+ # parameters of type "string".
22
+
23
+ # @!attribute [rw] min_length
24
+ # @return [Integer,nil] the minimum value length. Only valid for parameters of type "string".
25
+
26
+ # @!attribute [rw] max_length
27
+ # @return [Integer,nil] the maximum value length. Only valid for parameters of type "string".
28
+
29
+ # @!attribute [rw] minimum
30
+ # @return [Numeric,nil] the minimum value length. Only valid for parameters of type "number" or "integer".
31
+
32
+ # @!attribute [rw] maximum
33
+ # @return [Numeric,nil] the maximum value length. Only valid for parameters of type "number" or "integer".
34
+
35
+ # @!attribute [rw] example
36
+ # @return [String,Numeric,Boolean,nil] an example of the value.
37
+
38
+ # @!attribute [rw] default
39
+ # @return [String,Numeric,Boolean,nil] the default value.
40
+
41
+ # @!attribute [rw] required
42
+ # @return [Boolean] whether the parameter is required.
43
+
44
+ # @!attribute [rw] repeat
45
+ # @return [Boolean] whether the parameter can be repeated.
46
+
47
+ # @!attribute [r] types
48
+ # @return [Hash<String, Raml::Parameter::AbstractParameter>] if the parameter supports multiple types,
49
+ # the type alternatives, keyed by the type.
50
+
51
+ scalar_property :type , :enum , :pattern , :min_length ,
52
+ :max_length , :minimum , :maximum , :example ,
53
+ :repeat , :required , :default
54
+
55
+ attr_reader_default :type , 'string'
56
+ attr_reader_default :repeat , false
57
+ attr_reader_default :required, false
58
+
59
+ children_by :types, :type, AbstractParameter
60
+
61
+ self.doc_template = relative_path 'abstract_parameter.slim'
62
+
63
+ # @param name [String] the parameter name.
64
+ # @param parameter_data [Hash, Array<Hash>] the parameter data. If the parameter supports multiple types,
65
+ # it should be an array of hashes, one hash each for each type.
66
+ # @param parent [Raml::Node] the parameter's parent node.
67
+ def initialize(name, parameter_data, parent)
68
+ if parameter_data.is_a? Array
69
+ @name = name
70
+ @children ||= []
71
+ parameter_data.each do |parameter|
72
+ @children << self.class.new(name, parameter, self)
73
+ end
74
+ elsif parameter_data.is_a? Hash
75
+ super
76
+ end
77
+ end
78
+
79
+ # @return [Boolean] true if the parameter supports multiple type alternatives, false otherwise.
80
+ def has_multiple_types?
81
+ not children.empty?
82
+ end
83
+
84
+ # @private
85
+ def merge(other)
86
+ raise MergeError, "#{self.class} names don't match." if name != other.name
87
+
88
+ case [ has_multiple_types?, other.has_multiple_types? ]
89
+ when [ true , true ]
90
+ match, no_match = other.types.values.partition { |param| types.include? param.type }
91
+
92
+ # Merge parameters with the same type.
93
+ match = Hash[ match.map { |param| [ param.type, param ] } ]
94
+ types.each { |type, param| param.merge match[type] if match[type] }
95
+
96
+ # Add parameters with no matching type.
97
+ @children.concat no_match
98
+
99
+ when [ true , false ]
100
+ if types[other.type]
101
+ types[other.type].merge other
102
+ else
103
+ @children << other
104
+ end
105
+
106
+ when [ false, true ]
107
+ if other.types[self.type]
108
+ self.merge other.types[self.type]
109
+ @children << self.clone
110
+ @children.concat other.types.values.reject { |type| self.type == type.type }
111
+ reset
112
+
113
+ else
114
+ @children << self.clone
115
+ @children.concat other.types.values
116
+ reset
117
+ end
118
+
119
+ when [ false, false ]
120
+ super
121
+ end
122
+
123
+ self
124
+ end
125
+
126
+ private
127
+
128
+ def validate_type
129
+ raise InvalidParameterType unless VALID_TYPES.include? type
130
+ end
131
+
132
+ def validate_enum
133
+ if enum
134
+ if type == 'string'
135
+ raise InvalidParameterAttribute, "enum attribute must be an array of strings: #{enum} (#{enum.class})" unless
136
+ enum.is_a?(Array) && enum.all? { |val| val.is_a? String }
137
+ else
138
+ raise InapplicableParameterAttribute, 'enum attribute is only applicable to string parameters.'
139
+ end
140
+ end
141
+ end
142
+
143
+ def validate_pattern
144
+ if pattern
145
+ if type == 'string'
146
+ raise InvalidParameterAttribute, 'pattern attribute must be a string' unless pattern.is_a? String
147
+ pattern.gsub!(/\\*\^/u) { |m| m.size.odd? ? "#{m.chop}\\A" : m }
148
+ pattern.gsub!(/\\*\$/u) { |m| m.size.odd? ? "#{m.chop}\\z" : m }
149
+ begin
150
+ @pattern = Regexp.new pattern
151
+ rescue RegexpError
152
+ raise InvalidParameterAttribute, 'pattern attribute must be a valid regexp'
153
+ end
154
+ else
155
+ raise InapplicableParameterAttribute, 'pattern attribute is only applicable to string parameters.'
156
+ end
157
+ end
158
+ end
159
+
160
+ def validate_min_length
161
+ if min_length
162
+ if type != 'string'
163
+ raise InapplicableParameterAttribute, 'minLength attributes are applicable only to string parameters.'
164
+ else
165
+ raise InvalidParameterAttribute, 'minLength attributes must be an integer' unless min_length.is_a? Integer
166
+ end
167
+ end
168
+ end
169
+
170
+ def validate_max_length
171
+ if max_length
172
+ if type != 'string'
173
+ raise InapplicableParameterAttribute, 'maxLength attributes are applicable only to string parameters.'
174
+ else
175
+ raise InvalidParameterAttribute, 'maxLength attributes must be an integer' unless max_length.is_a? Integer
176
+ end
177
+ end
178
+ end
179
+
180
+ def validate_minimum
181
+ if minimum
182
+ if %w(integer number).include? type
183
+ raise InvalidParameterAttribute, 'minimum attribute must be numeric' unless minimum.is_a? Numeric
184
+ else
185
+ raise InapplicableParameterAttribute,
186
+ 'minimum attribute applicable only to number or integer parameters.'
187
+ end
188
+ end
189
+ end
190
+
191
+ def validate_maximum
192
+ if maximum
193
+ if %w(integer number).include? type
194
+ raise InvalidParameterAttribute, 'maximum attribute must be numeric' unless maximum.is_a? Numeric
195
+ else
196
+ raise InapplicableParameterAttribute,
197
+ 'maximum attribute applicable only to number or integer parameters.'
198
+ end
199
+ end
200
+ end
201
+
202
+ def validate_example
203
+ validate_value :example
204
+ end
205
+
206
+ def validate_repeat
207
+ unless [true, false].include?(repeat)
208
+ raise InvalidParameterAttribute, 'repeat attribute must be true or false.'
209
+ end
210
+ end
211
+
212
+ def validate_required
213
+ unless [true, false].include?(required)
214
+ raise InvalidParameterAttribute, "required attribute must be true or false: #{required} (#{required.class})"
215
+ end
216
+ end
217
+
218
+ def validate_default
219
+ validate_value :default
220
+ end
221
+
222
+ def validate_value(which)
223
+ val = send which
224
+ if val
225
+ err_msg = "#{which} attribute for a %s parameter must be a %s: #{val} (#{val.class})"
226
+ case type
227
+ when 'string'
228
+ raise InvalidParameterAttribute,
229
+ ( err_msg % [ 'string' , 'string' ] ) unless val.is_a? String
230
+ when 'number'
231
+ raise InvalidParameterAttribute,
232
+ ( err_msg % [ 'number' , 'number' ] ) unless val.is_a? Numeric
233
+ when 'integer'
234
+ raise InvalidParameterAttribute,
235
+ ( err_msg % [ 'integer', 'integer' ] ) unless val.is_a? Integer
236
+ when 'date'
237
+ raise InvalidParameterAttribute,
238
+ ( err_msg % [ 'date' , 'string' ] ) unless val.is_a? String
239
+ when 'boolean'
240
+ raise InvalidParameterAttribute,
241
+ ( err_msg % [ 'boolean', 'boolean' ] ) unless [TrueClass, FalseClass].include? val.class
242
+ end
243
+ end
244
+ end
245
+
246
+ def reset
247
+ scalar_properties.each { |prop| instance_variable_set "@#{prop}", nil }
248
+ end
249
+ end
250
+ end
251
+ end