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,6 @@
1
+ module Raml
2
+ module Parameter
3
+ class BaseUriParameter < AbstractParameter
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Raml
2
+ module Parameter
3
+ class FormParameter < AbstractParameter
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Raml
2
+ module Parameter
3
+ class QueryParameter < AbstractParameter
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ module Raml
2
+ module Parameter
3
+ class UriParameter < AbstractParameter
4
+ attr_reader_default :required, true
5
+ end
6
+ end
7
+ end
@@ -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,4 @@
1
+ module Raml
2
+ class Reference < Node
3
+ end
4
+ 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,5 @@
1
+ module Raml
2
+ # A reference to a resource type defined in the root node.
3
+ class ResourceTypeReference < ParametizedReference
4
+ end
5
+ 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,5 @@
1
+ module Raml
2
+ # A reference to a schema defined in the root node.
3
+ class SchemaReference < Reference
4
+ end
5
+ 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