ruby-swagger 0.0.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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/lib/ruby-swagger.rb +7 -0
  3. data/lib/ruby-swagger/data/contact.rb +49 -0
  4. data/lib/ruby-swagger/data/definitions.rb +49 -0
  5. data/lib/ruby-swagger/data/document.rb +181 -0
  6. data/lib/ruby-swagger/data/example.rb +29 -0
  7. data/lib/ruby-swagger/data/external_documentation.rb +24 -0
  8. data/lib/ruby-swagger/data/header.rb +34 -0
  9. data/lib/ruby-swagger/data/headers.rb +48 -0
  10. data/lib/ruby-swagger/data/info.rb +56 -0
  11. data/lib/ruby-swagger/data/items.rb +45 -0
  12. data/lib/ruby-swagger/data/license.rb +51 -0
  13. data/lib/ruby-swagger/data/mime.rb +31 -0
  14. data/lib/ruby-swagger/data/operation.rb +82 -0
  15. data/lib/ruby-swagger/data/parameter.rb +88 -0
  16. data/lib/ruby-swagger/data/parameters.rb +53 -0
  17. data/lib/ruby-swagger/data/path.rb +115 -0
  18. data/lib/ruby-swagger/data/paths.rb +50 -0
  19. data/lib/ruby-swagger/data/reference.rb +30 -0
  20. data/lib/ruby-swagger/data/response.rb +25 -0
  21. data/lib/ruby-swagger/data/responses.rb +50 -0
  22. data/lib/ruby-swagger/data/schema.rb +68 -0
  23. data/lib/ruby-swagger/data/scopes.rb +44 -0
  24. data/lib/ruby-swagger/data/security_definitions.rb +49 -0
  25. data/lib/ruby-swagger/data/security_requirement.rb +35 -0
  26. data/lib/ruby-swagger/data/security_scheme.rb +67 -0
  27. data/lib/ruby-swagger/data/tag.rb +24 -0
  28. data/lib/ruby-swagger/data/url.rb +26 -0
  29. data/lib/ruby-swagger/data/xml_object.rb +15 -0
  30. data/lib/ruby-swagger/grape/grape.rb +2 -0
  31. data/lib/ruby-swagger/grape/grape_config.rb +160 -0
  32. data/lib/ruby-swagger/grape/grape_presenter.rb +48 -0
  33. data/lib/ruby-swagger/grape/grape_template.rb +67 -0
  34. data/lib/ruby-swagger/grape/method.rb +295 -0
  35. data/lib/ruby-swagger/grape/param.rb +33 -0
  36. data/lib/ruby-swagger/grape/route_path.rb +37 -0
  37. data/lib/ruby-swagger/grape/routes.rb +52 -0
  38. data/lib/ruby-swagger/grape/type.rb +141 -0
  39. data/lib/ruby-swagger/io/comparable.rb +30 -0
  40. data/lib/ruby-swagger/io/definitions.rb +48 -0
  41. data/lib/ruby-swagger/io/file_system.rb +97 -0
  42. data/lib/ruby-swagger/io/paths.rb +55 -0
  43. data/lib/ruby-swagger/io/security.rb +45 -0
  44. data/lib/ruby-swagger/object.rb +67 -0
  45. data/lib/ruby-swagger/railtie.rb +7 -0
  46. data/lib/ruby-swagger/template.rb +29 -0
  47. data/lib/tasks/swagger.rake +125 -0
  48. metadata +176 -0
@@ -0,0 +1,33 @@
1
+ require 'ruby-swagger/grape/type'
2
+
3
+ module Swagger::Grape
4
+ class Param
5
+
6
+ def initialize(param)
7
+ @param = param
8
+ end
9
+
10
+ def to_swagger
11
+ swagger_param = {}
12
+ swagger_param['description'] = @param[:desc] if @param[:desc].present?
13
+ swagger_param['default'] = @param[:default] if @param[:default].present?
14
+
15
+ swagger_param.merge! Swagger::Grape::Type.new(@param[:type]).to_swagger
16
+
17
+ swagger_param
18
+ end
19
+
20
+ def has_type_definition?
21
+ type.downcase == 'object'
22
+ end
23
+
24
+ def type_definition
25
+ (Object.const_get(type)).to_s
26
+ end
27
+
28
+ def type
29
+ @param[:type].to_s || 'string'
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ require 'ruby-swagger/data/path'
2
+ require 'ruby-swagger/data/operation'
3
+ require 'ruby-swagger/grape/method'
4
+
5
+ module Swagger::Grape
6
+ class RoutePath
7
+
8
+ attr_reader :types, :scopes
9
+
10
+ def initialize(route_name)
11
+ @name = route_name
12
+ @operations = {}
13
+ @types = []
14
+ @scopes = []
15
+ end
16
+
17
+ def add_operation(route)
18
+ method = Swagger::Grape::Method.new(@name, route)
19
+ grape_operation = method.operation
20
+
21
+ @types = (@types | method.types).uniq
22
+ @scopes = (@scopes | method.scopes).uniq
23
+ @operations[route.route_method.downcase] = grape_operation
24
+ end
25
+
26
+ def to_swagger
27
+ path = Swagger::Data::Path.new
28
+
29
+ @operations.each do |operation_verb, operation|
30
+ path.send("#{operation_verb}=", operation)
31
+ end
32
+
33
+ path
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,52 @@
1
+ require 'ruby-swagger/data/paths'
2
+ require 'ruby-swagger/data/path'
3
+ require 'ruby-swagger/grape/route_path'
4
+
5
+ module Swagger::Grape
6
+ class Routes
7
+
8
+ attr_reader :types, :scopes
9
+
10
+ def initialize(routes)
11
+ @routes = routes
12
+ @types = []
13
+ @scopes = []
14
+ end
15
+
16
+ def to_swagger
17
+ swagger = Swagger::Data::Paths.new
18
+ paths = {}
19
+
20
+ @routes.each do |route|
21
+
22
+ next if route.route_hidden == true #implement custom "hidden" extension
23
+
24
+ swagger_path_name = swagger_path_name(route)
25
+ paths[swagger_path_name] ||= Swagger::Grape::RoutePath.new(swagger_path_name)
26
+ paths[swagger_path_name].add_operation(route)
27
+ end
28
+
29
+ paths.each do |path_name, path|
30
+ swagger.add_path(path_name, path.to_swagger)
31
+ @types = (@types | path.types).uniq
32
+ @scopes = (@scopes | path.scopes).uniq
33
+ end
34
+
35
+ swagger
36
+ end
37
+
38
+ private
39
+
40
+ def swagger_path_name(grape_route)
41
+ grape_path_name = grape_route.route_path
42
+ grape_prefix = grape_route.route_prefix
43
+ grape_path_name.gsub!(/^\/#{grape_prefix}/, '') if grape_prefix
44
+ grape_path_name.gsub!(/^\/:version/, '') #remove api version - if any
45
+ grape_path_name.gsub!(/\(\.:format\)$/, '') #remove api format - if any
46
+ grape_path_name.gsub!(/\(\..+\)$/, '') #remove api format - if any
47
+ grape_path_name.gsub!(/\/:([a-zA-Z0-9_]+)/, '/{\1}') #convert parameters from :format into {format}
48
+ grape_path_name
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,141 @@
1
+ module Swagger::Grape
2
+ class Type
3
+
4
+ attr_reader :discovered_types
5
+
6
+ def initialize(type)
7
+ @type = type.to_s || 'String'
8
+ end
9
+
10
+ def to_swagger(with_definition = true)
11
+ type_convert(@type.to_s, with_definition)
12
+ end
13
+
14
+ def sub_types
15
+ type = Object.const_get(@type)
16
+ return [] unless type.respond_to?(:exposures)
17
+
18
+ types = []
19
+
20
+ type.exposures.each do |property, definition|
21
+ types << definition[:using] if definition[:using].present?
22
+ end
23
+
24
+ types.uniq
25
+ end
26
+
27
+ private
28
+
29
+ def type_convert(type, with_definition = true)
30
+ swagger_type = {}
31
+
32
+ case type.downcase
33
+ when 'string'
34
+ swagger_type['type'] = 'string'
35
+ when 'integer'
36
+ swagger_type['type'] = 'integer'
37
+ when 'array'
38
+ swagger_type['type'] = 'array'
39
+ swagger_type['items'] = {'type' => 'string'}
40
+ when 'hash'
41
+ swagger_type['type'] = 'object'
42
+ swagger_type['properties'] = {}
43
+ when 'boolean'
44
+ swagger_type['type'] = 'boolean'
45
+ when 'virtus::attribute::boolean'
46
+ swagger_type['type'] = 'boolean'
47
+ when 'symbol'
48
+ swagger_type['type'] = 'string'
49
+ when 'float'
50
+ swagger_type['type'] = 'number'
51
+ swagger_type['format'] = 'float'
52
+ when 'rack::multipart::uploadedfile'
53
+ swagger_type['type'] = 'string'
54
+ STDERR.puts "Warning - I have no idea how to handle the type file. Right now I will consider this a string, but we should probably handle it..."
55
+ when 'date'
56
+ swagger_type['type'] = 'date'
57
+ when 'datetime'
58
+ swagger_type['format'] = 'date-time'
59
+ swagger_type['format'] = 'string'
60
+ else
61
+ swagger_type['type'] = "object"
62
+
63
+ if with_definition
64
+ # I can just reference the name of the object here
65
+ swagger_type['$ref'] = "#/definitions/#{type}"
66
+ else
67
+ type = Object.const_get(@type)
68
+ # I need to define the full object
69
+ raise ArgumentError.new("Don't know how to translate the object #{@type}") unless type.respond_to?(:exposures)
70
+
71
+ swagger_type['properties'] = {}
72
+
73
+ type.exposures.each do |property, definition|
74
+
75
+ cursor = swagger_type
76
+ target = property.to_s
77
+
78
+ if definition[:nested]
79
+ # it's a nested parameter
80
+ path = target.split('__')
81
+ cursor = find_elem_in_schema(cursor, path.dup)
82
+ target = path.last
83
+ end
84
+
85
+ target = definition[:as].to_s if definition[:as].present?
86
+
87
+ cursor['properties'][target] = {}
88
+
89
+ if definition[:documentation].present? && definition[:documentation][:type].present?
90
+ cursor['properties'][target] = type_convert(definition[:documentation][:type].to_s, true)
91
+ end
92
+
93
+ if definition[:using].present?
94
+ #it's either an object or an array of object
95
+ using = type_convert(definition[:using].to_s, true)
96
+
97
+ if cursor['type'].present? && cursor['type'] == 'array'
98
+ cursor['items'] = using
99
+ else
100
+ cursor['properties'][target] = using
101
+ end
102
+
103
+ end
104
+
105
+ cursor['properties'][target]['description'] = definition[:documentation][:desc] if definition[:documentation].present?
106
+ cursor['properties'][target]['type'] ||= 'string' #no type defined, assuming it's a string
107
+ end
108
+ end
109
+ end
110
+
111
+ swagger_type
112
+ end
113
+
114
+ def find_elem_in_schema(root, schema_path)
115
+ return root if schema_path.nil? || schema_path.empty?
116
+
117
+ next_elem = schema_path.shift
118
+
119
+ return root if root['properties'][next_elem].nil?
120
+
121
+ case root['properties'][next_elem]['type']
122
+ when 'array'
123
+ #to descend an array this must be an array of objects
124
+ root['properties'][next_elem]['items']['type'] = 'object'
125
+ root['properties'][next_elem]['items']['properties'] ||= {}
126
+
127
+ find_elem_in_schema(root['properties'][next_elem]['items'], schema_path)
128
+ when 'object'
129
+ find_elem_in_schema(root['properties'][next_elem], schema_path)
130
+ else
131
+ # I'm discending an object that before I assumed was something else
132
+ root['properties'][next_elem]['type'] = 'object'
133
+ root['properties'][next_elem]['properties'] ||= {}
134
+
135
+ find_elem_in_schema(root['properties'][next_elem], schema_path)
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+ end
@@ -0,0 +1,30 @@
1
+ module Swagger::IO
2
+ class Comparable
3
+
4
+ def self.copy_description_old_definition(definition, old_definition)
5
+ return if definition.nil? || old_definition.nil? || definition.class != old_definition.class
6
+
7
+ case definition
8
+ when Hash
9
+
10
+ definition.keys.each do |key|
11
+ old_v = definition[key]
12
+
13
+ if (key == 'description' || key == 'summary') && old_definition[key]
14
+ definition[key] = old_definition[key]
15
+ end
16
+
17
+ if old_v.is_a?(Hash) || old_v.is_a?(Array)
18
+ copy_description_old_definition(definition[key], old_definition[key])
19
+ end
20
+ end
21
+ when Array
22
+ definition.each_with_index do |item, index|
23
+ copy_description_old_definition(definition[index], old_definition[index])
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,48 @@
1
+ require 'ruby-swagger/io/file_system'
2
+ require 'ruby-swagger/io/comparable'
3
+
4
+ module Swagger::IO
5
+ class Definitions
6
+
7
+ def self.read_definitions
8
+ definitions = {}
9
+
10
+ Swagger::IO::FileSystem.all_files("definitions/**/*.yml").each do |file|
11
+ definitions[File.basename(file, ".yml")] = YAML::load_file(file)
12
+ end
13
+
14
+ definitions
15
+ end
16
+
17
+ def self.write_definitions(definitions)
18
+ return if definitions.nil?
19
+
20
+ #Remove dead definitions
21
+ Swagger::IO::FileSystem.all_files("definitions/**/*.yml").each do |file|
22
+ def_name = File.basename(file, ".yml")
23
+
24
+ unless definitions[def_name]
25
+ STDERR.puts "#{def_name} is not present anymore, removing #{file}"
26
+ Swagger::IO::FileSystem.delete_file(file)
27
+ end
28
+
29
+ end
30
+
31
+ # Write new definitions
32
+ definitions.each do |definition_name, definition|
33
+
34
+ # If an old definition exists, we copy over the documentation to the generated definition
35
+ if Swagger::IO::FileSystem.file_exists?("definitions/#{definition_name}.yml")
36
+ old_definition = Swagger::IO::FileSystem.read_file("definitions/#{definition_name}.yml")
37
+
38
+ Swagger::IO::Comparable.copy_description_old_definition(definition, old_definition)
39
+ end
40
+
41
+ Swagger::IO::FileSystem.write_file(definition.to_yaml, "definitions/#{definition_name}.yml", true)
42
+ end
43
+ end
44
+
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,97 @@
1
+ require 'ruby-swagger/object'
2
+ require 'ruby-swagger/data/document'
3
+ require 'ruby-swagger/io/security'
4
+ require 'ruby-swagger/io/definitions'
5
+ require 'ruby-swagger/io/paths'
6
+
7
+ module Swagger::IO
8
+ class FileSystem
9
+
10
+ DOC_SUBPARTS = %w(responses security tags)
11
+
12
+ @@default_path = './doc/swagger'
13
+
14
+ def self.default_path=(new_path)
15
+ @@default_path = new_path
16
+ end
17
+
18
+ def self.default_path
19
+ @@default_path
20
+ end
21
+
22
+ def self.init_fs_structure
23
+ FileUtils.mkdir_p(@@default_path) unless Dir.exists?(@@default_path)
24
+ end
25
+
26
+ def self.read_file(name)
27
+ YAML::load_file(@@default_path + '/' + name)
28
+ end
29
+
30
+ def self.write_file(content, location, overwrite = false)
31
+ file_path = @@default_path + '/' + location
32
+
33
+ return if !overwrite && File.exists?(file_path)
34
+
35
+ dir_path = File.dirname(file_path)
36
+
37
+ FileUtils.mkdir_p(dir_path) unless Dir.exists?(dir_path)
38
+ File.open(file_path, 'w') {|f| f.write(content) }
39
+ end
40
+
41
+ def self.file_exists?(name)
42
+ File.exists?(@@default_path + '/' + name)
43
+ end
44
+
45
+ def self.all_files(pattern)
46
+ Dir["#{@@default_path}/#{pattern}"]
47
+ end
48
+
49
+ def self.delete_file(file)
50
+ FileUtils.rm_f(file)
51
+ end
52
+
53
+ def initialize(swagger_doc)
54
+ @doc = swagger_doc
55
+ end
56
+
57
+ def write!
58
+ Swagger::IO::FileSystem.init_fs_structure
59
+
60
+ swagger = @doc.to_swagger
61
+
62
+ Swagger::IO::Paths.write_paths(swagger.delete('paths'))
63
+
64
+ DOC_SUBPARTS.each {|doc_part| write_subpart(doc_part, swagger.delete(doc_part))}
65
+ Swagger::IO::Definitions.write_definitions(swagger.delete('definitions'))
66
+ Swagger::IO::Security.write_security_definitions(swagger.delete('securityDefinitions'))
67
+ Swagger::IO::FileSystem.write_file(swagger.to_yaml, 'base_doc.yml')
68
+ end
69
+
70
+ def self.read
71
+ doc = YAML::load_file("#{default_path}/base_doc.yml")
72
+
73
+ DOC_SUBPARTS.each do |doc_part|
74
+ file_path = "#{default_path}/#{doc_part}.yml"
75
+ doc[doc_part] = YAML::load_file(file_path) if File.exists?(file_path)
76
+ end
77
+
78
+ doc['paths'] = Swagger::IO::Paths.read_paths
79
+ doc['definitions'] = Swagger::IO::Definitions.read_definitions
80
+ doc['securityDefinitions'] = Swagger::IO::Security.read_security_definitions
81
+
82
+ Swagger::Data::Document.parse(doc)
83
+ end
84
+
85
+ def compile!
86
+ Swagger::IO::FileSystem.write_file(JSON.pretty_generate(@doc.to_swagger), 'swagger.json', true)
87
+ end
88
+
89
+ private
90
+
91
+ def write_subpart(subpart, content)
92
+ return unless content
93
+ write_file(content.to_yaml, "#{subpart}.yml")
94
+ end
95
+
96
+ end
97
+ end