raml_ruby 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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