source2swagger 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /*.gem
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - ree
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ script: rake test
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/LICENCE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2012 3scale networks S.L.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,189 @@
1
+
2
+ ## Description
3
+
4
+ Coming soon...
5
+
6
+ ## Usage
7
+
8
+ #### Dependencies
9
+
10
+ * Ruby (1.8x or 1.9x)
11
+ * Gem
12
+ * JSON gem (gem install json)
13
+
14
+ #### Parameters
15
+
16
+ $ bin/source2swagger
17
+
18
+ Usage: source2swagger [options]
19
+ -i, --input PATH Directory of the input source code
20
+ -e, --ext ("rb"|"c"|"js"|"py") File extension of the source code
21
+ -c, --comment ("##~"|"//~") Comment tag used to write docs
22
+ -o, --output PATH Directory where the json output will be saved (optional)
23
+
24
+ #### Example
25
+
26
+ $ bin/souce2swagger -i ~/project/lib -e "rb" -c "##~"_
27
+
28
+ This will output the Swagger compatible JSON specs on the terminal.
29
+
30
+ Add *-o /tmp* and it will write the JSON file(s) to */tmp*
31
+
32
+ #### Contributions
33
+
34
+ Feel free to extend the code and issue a pull request,
35
+
36
+ The test suite can be invoked as
37
+
38
+ $ rake test
39
+
40
+ Requires *rake* and the *gem test/unit*
41
+
42
+
43
+ ## How to
44
+
45
+ Check [test/data/sample3.rb](https://github.com/solso/source2swagger/blob/master/test/data/sample3.rb) for a comprehensive real example of the *source2swagger* inline docs for Ruby.
46
+
47
+ The names of the attributes can be seen on the section Grammar (partially) or better yet in the original [Swagger Specification](http://swagger.wordnik.com/spec).
48
+
49
+ #### API names declaration
50
+
51
+ First you need to declare the API
52
+
53
+ ##~ a = source2swagger.namespace("your_api_spec_name")
54
+
55
+ This will generate the file your_api_spec_name.json. The name can be declared in multiple files and several times in the same file. Each time *namespace* is invoked it returns the reference to the root element of the API named "your_api_spec_name".
56
+
57
+ #### Setting attributes elements
58
+
59
+ One by one,
60
+
61
+ ##~ a.basePath = "http://helloworld.3scale.net"
62
+ ##~ a.swagrVersion = "0.1a"
63
+ ##~ a.apiVersion = "1.0"
64
+
65
+ or all at the same time,
66
+
67
+ ##~ a.set "basePath" => "http://helloworld.3scale.net", "swagrVersion" => "0.1a", "apiVersion" => "1.0"
68
+
69
+
70
+ You can always combine
71
+
72
+ ##~ a.set "basePath" => "http://helloworld.3scale.net", "swagrVersion" => "0.1a"
73
+ ##~ a.apiVersion = "1.0"
74
+
75
+ #### Adding and element to a list attribute
76
+
77
+ ##~ op = a.operations.add
78
+ ##~ op.httpMethod = "GET"
79
+ ##~ op.tags = ["production"]
80
+ ##~ op.nickname = "get_word"
81
+ ##~ deprecated => false
82
+ ##~
83
+ ##~ op = a.operations.add
84
+ ##~ op.set :httpMethod => "POST", :tags => ["production"], :nickname => "set_word", :deprecated => false
85
+
86
+ Two elements (*operations*) were added to *a.operations*, you can also add directly if you do not need to have a reference to the variable *op*
87
+
88
+ ##~ a.operations.add :httpMethod => "GET", :tags => ["production"], :nickname => "get_word", :deprecated => false
89
+ ##~ a.operations.add :httpMethod => "POST", :tags => ["production"], :nickname => "set_word", :deprecated => false
90
+
91
+ #### Using variables for common structures
92
+
93
+ The source2swagger notation also allows you to define variables that can be defined anywhere on the source code files as *@name = value*, the value is typically a hash structure in ruby notation (*{"key_1" => "value_1", ... , "key_n" => "value_n"}*)
94
+
95
+ *Note:* all variable declarations are evaluated before the non-variable statements so vars will always available no matter where they are defined. For instance,
96
+
97
+ ...
98
+ ## in foo.rb
99
+ ##~ op.parameters.add @parameter_app_id
100
+ ...
101
+ ## in bar.rb
102
+ ##~ @parameter_app_id = {"name" => "word", "description" => "The word whose sentiment is to be set", "dataType" => "string", "required" => true, "paramType" => "path"}
103
+ ...
104
+
105
+
106
+ #### Adding comments
107
+
108
+ You can add comments on the inline docs specification, just use the normal comment tags of your language
109
+
110
+ ##~ op = a.operations.add
111
+ ##
112
+ ## HERE IS MY COMMENT (do not use the comment tag, e.g. ##~ but the comment tag specific of your language, in ruby #)
113
+ ##
114
+ ##~ op.httpMethod = "GET"
115
+
116
+
117
+
118
+ Check [test/data/sample3.rb](https://github.com/solso/source2swagger/blob/master/test/data/sample3.rb) for a comprehensive real example of the *source2swagger* inline docs for Ruby.
119
+
120
+
121
+ ### Grammar
122
+
123
+ (partial)
124
+
125
+ For a more comprehensive specification of the fields needes to declare your API on the Swagger format you can always go to the [source](http://swagger.wordnik.com/spec)
126
+
127
+ $ROOT
128
+
129
+ a = source2swagger.namespace(STRING)
130
+
131
+ a.basepath = STRING [required, ]
132
+ a.swagrVersion = STRING []
133
+ a.apiVersion = STRING []
134
+ a.apis = LIST[$ENDPOINTS] [required, ]
135
+
136
+ a.models = LIST[$MODELS] []
137
+
138
+ $ENDPOINTS
139
+
140
+ e = a.apis.add
141
+
142
+ e.path = STRING [required, ]
143
+ e.format = LIST[STRING] [required, ]
144
+ e.description = STRING [required, ]
145
+ e.errorResponses = LIST[$ERRORS][]
146
+ e.operations = LIST[$OPERATIONS][]
147
+
148
+ $OPERATIONS
149
+
150
+ o = e.operations.add
151
+
152
+ o.httpMethod = STRING [required, ]
153
+ o.tags = LIST[STRING] []
154
+ o.nickname = STRING []
155
+ o.deprecated = BOOLEAN []
156
+ o.summary = STRING []
157
+ o.parameters = LIST[$PARAMETERS][]
158
+
159
+ PARAMETERS
160
+
161
+ p = o.parameters.add
162
+
163
+ p.name = STRING [required]
164
+ p.description = STRING []
165
+ p.dataType = STRING [required]
166
+ p.allowMultiple = BOOLEAN []
167
+ p.required = BOOLEAN []
168
+ p.paramType = STRING
169
+
170
+ ERRORS
171
+
172
+ err = e.operations.add
173
+
174
+ err.reason = STRING []
175
+ err.code = INT []
176
+
177
+ ## Extra Resources
178
+
179
+ You can edit and view the generated Swagger JSON specs online here: [JSON Editor](http://jsoneditor.appspot.com/)
180
+
181
+ It's pretty basic but it works great for a quick manual inspection and edition
182
+ of the json generated by *source2swagger*. If you know of another online editor
183
+ please let us know.
184
+
185
+ ## License
186
+
187
+ MIT License
188
+
189
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rake/testtask'
2
+ require 'bundler/gem_tasks'
3
+
4
+ desc 'Run all unit tests'
5
+
6
+ Rake::TestTask.new(:test) do |task|
7
+ task.test_files = FileList['test/unit/**/*_test.rb']
8
+ task.verbose = true
9
+ end
10
+
@@ -0,0 +1,84 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require "json"
5
+
6
+ begin
7
+ require "swagger_hash"
8
+ require "swagger_reader"
9
+ rescue LoadError
10
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
11
+
12
+ if $:.include?(lib)
13
+ raise
14
+ else
15
+ $:.unshift(lib)
16
+ retry
17
+ end
18
+ end
19
+
20
+ opt_input = nil
21
+ opt_output = nil
22
+ opt_comment = nil
23
+ opt_extension = nil
24
+
25
+ parser = OptionParser.new do |parser|
26
+ parser.on('-i','--input PATH', 'Directory of the input source code') do |value|
27
+ opt_input = value
28
+ end
29
+
30
+ parser.on('-e','--ext ("rb"|"c"|"js"|"py")', 'File extension of the source code') do |value|
31
+ opt_extension = value
32
+ end
33
+
34
+ parser.on('-c','--comment ("##~"|"//~")','Comment tag used to write docs') do |value|
35
+ opt_comment = value
36
+ end
37
+
38
+ parser.on('-o','--output PATH','Directory where the json output will be saved (optional)') do |value|
39
+ opt_output = value
40
+ end
41
+
42
+ parser.parse!
43
+ end
44
+
45
+ unless opt_extension and opt_input and opt_comment
46
+ puts parser
47
+ abort
48
+ end
49
+
50
+ def save(results, output_path)
51
+ results.each do |k,v|
52
+ puts " Saving API #{k} to #{output_path}/#{k}.json"
53
+ File.new("#{output_path}/#{k}.json","w").puts v.to_json
54
+ end
55
+ end
56
+
57
+ def print(results)
58
+ cont = 1
59
+ results.each do |k,v|
60
+ puts "API: #{cont}, #{k}\n"
61
+ puts v.to_json
62
+ cont=cont+1
63
+ end
64
+ end
65
+
66
+ def run(input, extension, comment, output)
67
+ reader = SwaggerReader.new
68
+ $_swaggerhash = Hash.new
69
+
70
+ code = reader.analyze_all_files(input,extension,comment)
71
+ results = reader.process_code(code) unless code.nil? || code.empty?
72
+
73
+ puts "Swagger API in #{ARGV[0]}/**/*.#{ARGV[1]}: #{results.size} API\n"
74
+ if output.nil?
75
+ print(results)
76
+ else
77
+ save(results,output)
78
+ end
79
+ puts "Done!"
80
+
81
+ end
82
+
83
+
84
+ run(opt_input, opt_extension, opt_comment, opt_output)
@@ -0,0 +1,70 @@
1
+
2
+ class SwaggerHash < Hash
3
+
4
+ KEEP_METHODS = %w{default []= each merge! debugger puts __id__ __send__ instance_eval == equal? initialize delegate caller object_id raise class [] to_json inspect to_s new nil?}
5
+ ((private_instance_methods + instance_methods).map(&:to_sym) - KEEP_METHODS.map(&:to_sym)).each{|m| undef_method(m) }
6
+
7
+ def initialize
8
+ @namespaces = Hash.new
9
+ ##super
10
+ end
11
+
12
+ def namespace(name)
13
+ @namespaces[name] ||= SwaggerHash.new
14
+ end
15
+
16
+ def namespaces
17
+ @namespaces
18
+ end
19
+
20
+ def method_missing(method, *args, &block)
21
+
22
+ if method.to_s.match(/=$/)
23
+ self[method.to_s.gsub("=","").to_sym] = args.first
24
+ else
25
+ if self[method].nil?
26
+ if not method == :add
27
+ self[method] = SwaggerHash.new
28
+ else
29
+ if (args.nil? || args.empty?)
30
+ self[:_array] ||= Array.new
31
+ item = SwaggerHash.new
32
+ self[:_array] << item
33
+ return item
34
+ else
35
+ self[:_array] ||= Array.new
36
+ args.each do |item|
37
+ self[:_array] << item
38
+ end
39
+ end
40
+ end
41
+ end
42
+ return self[method]
43
+ end
44
+ end
45
+
46
+ def set(*args)
47
+ merge!(*args)
48
+ end
49
+
50
+ def to_hash
51
+ h2 = Hash.new
52
+ self.each do |k,v|
53
+ if v.class != SwaggerHash && v.class != Hash
54
+ h2[k] = v
55
+ else
56
+ if not (v[:_array].nil?)
57
+ v2 = []
58
+ v[:_array].each do |item|
59
+ v2 << item.to_hash
60
+ end
61
+ h2[k] = v2
62
+ else
63
+ h2[k] = v.to_hash
64
+ end
65
+ end
66
+ end
67
+ return h2
68
+ end
69
+
70
+ end
@@ -0,0 +1,113 @@
1
+
2
+ class SwaggerReader
3
+
4
+ def analyze_file(file, comment_str)
5
+
6
+ code = {:code => [], :line_number => [], :file =>[]}
7
+
8
+ File.open(file,"r") do |f|
9
+ line_number = 1
10
+ while (line = f.gets)
11
+ v = line.strip.split(" ")
12
+ if !v.nil? && v.size > 0 && (v[0]==comment_str)
13
+ code[:code] << v[1..v.size].join(" ")
14
+ code[:file] << file
15
+ code[:line_number] << line_number
16
+ end
17
+ line_number = line_number + 1
18
+ end
19
+ end
20
+
21
+ return code
22
+
23
+ end
24
+
25
+ def analyze_all_files(base_path, file_extension, comment_str)
26
+
27
+ code = {:code => [], :line_number => [], :file =>[]}
28
+
29
+ files = Dir["#{base_path}/**/*.#{file_extension}"].sort
30
+
31
+ files.each do |file|
32
+ fcode = analyze_file(file,comment_str)
33
+ [:code, :line_number, :file].each do |lab|
34
+ code[lab] = code[lab] + fcode[lab]
35
+ end
36
+ end
37
+
38
+ return code
39
+
40
+ end
41
+
42
+ def sort_for_vars_declaration(code)
43
+
44
+ tmp_vars = {:code => [], :line_number => [], :file =>[]}
45
+ tmp_not_vars = {:code => [], :line_number => [], :file =>[]}
46
+
47
+ cont = 0
48
+ code[:code].each do |code_line|
49
+ code_line.strip!
50
+ if code_line[0]=='@'
51
+ tmp_vars[:code] << code_line.gsub('@',' ')
52
+ tmp_vars[:line_number] << code[:line_number][cont]
53
+ tmp_vars[:file] << code[:file][cont]
54
+ else
55
+ tmp_not_vars[:code] << code_line.gsub('@',' ')
56
+ tmp_not_vars[:line_number] << code[:line_number][cont]
57
+ tmp_not_vars[:file] << code[:file][cont]
58
+ end
59
+ cont=cont+1
60
+ end
61
+
62
+ res = {:code => tmp_vars[:code] + tmp_not_vars[:code], :line_number => tmp_vars[:line_number] + tmp_not_vars[:line_number], :file => tmp_vars[:file] + tmp_not_vars[:file]}
63
+
64
+ return res
65
+
66
+ end
67
+
68
+
69
+ def process_code(code)
70
+
71
+ code = sort_for_vars_declaration(code)
72
+
73
+ code[:code].insert(0,"source2swagger = SwaggerHash.new")
74
+
75
+ code[:code].size.times do |cont|
76
+
77
+ begin
78
+ v = code[:code][0..cont]
79
+ v << "out = {:apis => []}"
80
+ v << "source2swagger.namespaces.each {|k,v| out[k] = v.to_hash}"
81
+ str=v.join(";")
82
+ proc do
83
+ $SAFE = 4
84
+ eval(str)
85
+ end.call
86
+ rescue Exception => e
87
+ raise SwaggerReaderException, "Error parsing source files at #{code[:file][cont-1]}:#{code[:line_number][cont-1]}\n#{e.inspect}"
88
+ end
89
+ end
90
+
91
+ code[:code] << "out = {:apis => []}"
92
+ code[:code] << "source2swagger.namespaces.each {|k,v| out[k] = v.to_hash}"
93
+ str = code[:code].join(";")
94
+ res = proc do
95
+ $SAFE = 4
96
+ eval(str)
97
+ end.call
98
+
99
+ raise SwaggerReaderException, "Error on the evaluation of the code in docs: #{res}" unless res.class==Hash
100
+
101
+ res.each do |k, v|
102
+ res[k] = v.to_hash
103
+ end
104
+
105
+ res
106
+ end
107
+
108
+ end
109
+
110
+ class SwaggerReaderException < Exception
111
+
112
+ end
113
+