rails-swagger 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0ca0079ec6e56d59984482c58ba8f88848f0c4cf
4
+ data.tar.gz: 74419e6fdc5516c8f86b52791c56c7acce3b0904
5
+ SHA512:
6
+ metadata.gz: 9513137bdc27e6caed663bf6a6efe819ae089be425d9b9d1eecac25a1fc00b26f37d3d84bc70aec51fa4fea51fc536cd8c763765703b82307c68f9c9ebfe6951
7
+ data.tar.gz: f047a592844a8cf8b44fee3ee267ac09916674295b92de1325ca30382184e0d303c74d9bfc4c15582d9cf3f903c9b1d8e9e012e8d8c1ac05bc684ee9bed6c1af
@@ -0,0 +1,30 @@
1
+ module Rails
2
+ module Swagger
3
+ module Controller
4
+
5
+ # Injects swagger-related code into the controller when included
6
+ def self.included base
7
+
8
+ # Add controller hooks
9
+ base.class_eval do
10
+ before_action :validate_swagger_params
11
+ end
12
+
13
+ # Returns the swagger spec definition for the endpoint serving
14
+ # the current request.
15
+ def swagger_endpoint
16
+ key = "#{params[:controller]}##{params[:action]}"
17
+ endpoint = rails_swagger_engine.endpoints[key]
18
+ end
19
+
20
+ # Validates request parameters against the Swagger API spec
21
+ # associated with this controller.
22
+ def validate_swagger_params
23
+ #puts swagger_endpoint.inspect.white
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,149 @@
1
+ module Rails
2
+ module Swagger
3
+
4
+ # Defines the Swagger spec file formats that are parsable by this gem.
5
+ # Currently only the JSON format is supported.
6
+ ALLOWED_FORMATS = [".json"].freeze
7
+
8
+ # Defines a base class from which Swagger API engines can be created.
9
+ # Uses namespace isolation to ensure routes don't conflict with any
10
+ # pre-existing routes from the main rails application.
11
+ class Engine < Rails::Engine
12
+ isolate_namespace Rails::Swagger
13
+ end
14
+
15
+ # Helper method to create a new engine based on a module namespace
16
+ # prefix and Swagger spec file. The engine ceated will be a subclass of
17
+ # Rails::Swagger::Engine, which itself inherits from Rails::Engine.
18
+ def self.Engine base_module, file
19
+
20
+ # Convert the module prefix into a constant if passed in as a string
21
+ base_module = Object.const_get base_module if String === base_module
22
+
23
+ # Ensure the Swagger spec file is in an acceptable format
24
+ ext = File.extname(file)
25
+ unless ALLOWED_FORMATS.include? ext
26
+ raise "Swagger files must end with #{ALLOWED_FORMATS.join(' or ')}. File given: #{file}"
27
+ end
28
+
29
+ # Attempt to read and parse the Swagger spec file
30
+ document = File.read file
31
+ case File.extname file
32
+ when ".json"
33
+ begin
34
+ require 'json'
35
+ document = JSON.parse document
36
+ rescue JSON::ParserError
37
+ raise $!, "Problem parsing swagger spec file \"#{file}\": #{$!.message.lines.first.strip}", $@
38
+ end
39
+ else
40
+ raise "Swagger files must end with #{ALLOWED_FORMATS.join(' or ')}. File given: #{file}"
41
+ end
42
+
43
+ # Verify that the swagger version is supported
44
+ unless document["swagger"] == "2.0"
45
+ raise "Unsupported swagger version: #{document["swagger"]}. #{self} supports only version 2.0"
46
+ end
47
+
48
+ # # Parse the swagger schema
49
+ # schema = nil
50
+ # begin
51
+ # schema = JSchema.build document
52
+ # rescue JSchema::UnknownError, JSchema::InvalidSchema => e
53
+ # raise $!, "Problem parsing swagger spec file \"#{file}\": #{e.message}", $@
54
+ # end
55
+
56
+ # Builds a routing tree based on the swagger spec file.
57
+ # We'll add each endpoint to the routing tree and additionally
58
+ # store it in an array to be used below.
59
+ router = Router.new
60
+ endpoints = []
61
+ document["paths"].each do |url, actions|
62
+ actions.each do |verb, definition|
63
+ route = Endpoint.new(verb.downcase.to_sym, url, definition)
64
+ router << route
65
+ endpoints << route
66
+ end
67
+ end
68
+
69
+ # Creates the engine that will be used to actually route the
70
+ # contents of the swagger spec file. The engine will eventually be
71
+ # attached to the base module (argument to this current method).
72
+ #
73
+ # Exposes `::router` and `::endpoints` methods to allow other parts
74
+ # of the code to tie requests back to their spec file definitions.
75
+ engine = Class.new Engine do
76
+
77
+ @router = router
78
+ @endpoints = Hash.new
79
+ @schema = document
80
+
81
+ class << self
82
+ def router
83
+ @router
84
+ end
85
+ def endpoints
86
+ @endpoints
87
+ end
88
+ def schema
89
+ @schema
90
+ end
91
+ end
92
+
93
+ # Adds routes to the engine by passing the Mapper to the top
94
+ # of the routing tree. `self` inside the block refers to an
95
+ # instance of `ActionDispatch::Routing::Mapper`.
96
+ self.routes.draw do
97
+ scope module: base_module.name.underscore, format: false do
98
+ router.draw self
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ # Assign the engine as a class on the base module
105
+ base_module.const_set :Engine, engine
106
+
107
+ # Creates a hash that maps routes back to their swagger spec file
108
+ # equivalents. This is accomplished by mocking a request for each
109
+ # swagger spec file endpoint and determining which controller and
110
+ # action the request is routed to. Swagger spec file definitions
111
+ # are then attached to that controller/action pair.
112
+ endpoints.each do |route|
113
+
114
+ # Mocks a request using the route's URL
115
+ url = ::ActionDispatch::Journey::Router::Utils.normalize_path route.path
116
+ env = ::Rack::MockRequest.env_for url, method: route[:method].upcase
117
+ req = ::ActionDispatch::Request.new env
118
+
119
+ # Maps the swagger spec endpoint to the destination controller
120
+ # action by routing the request.
121
+ mapped = engine.routes.router.recognize(req){}.first[1]
122
+ key = "#{mapped[:controller]}##{mapped[:action]}"
123
+ engine.endpoints[key] = route
124
+
125
+ end
126
+ engine.endpoints.freeze
127
+
128
+ # Defines a helper module on the base module that can be used to
129
+ # properly generate swagger-aware controllers. Any controllers
130
+ # referenced from a swagger spec file should include this module.
131
+ mod = Module.new do
132
+ @base = base_module
133
+ def self.included controller
134
+ base_module = @base
135
+ controller.include Controller
136
+ define_method :rails_swagger_engine do
137
+ base_module.const_get :Engine
138
+ end
139
+ end
140
+ end
141
+ base_module.const_set :SwaggerController, mod
142
+
143
+ # Returns the new engine
144
+ base_module.const_get :Engine
145
+
146
+ end
147
+
148
+ end
149
+ end
@@ -0,0 +1,178 @@
1
+ module Rails
2
+ module Swagger
3
+
4
+ Endpoint = Struct.new(:method, :url, :definition, :_path) do
5
+ def initialize *opts
6
+ super
7
+ self[:_path] = self.path
8
+ end
9
+ def path
10
+ self[:url].gsub /\{(.+)\}/, ':\\1'
11
+ end
12
+ end
13
+
14
+ RESOURCE_ROUTES = {
15
+ get: :index,
16
+ post: :create
17
+ }.freeze
18
+ PARAM_ROUTES = {
19
+ get: :show,
20
+ patch: :update,
21
+ put: :update,
22
+ delete: :destroy
23
+ }.freeze
24
+
25
+ class Router
26
+
27
+ attr_accessor :endpoints
28
+
29
+ def initialize prefix = [], parent = nil
30
+ @parent = parent
31
+ @prefix = prefix
32
+ @endpoints = []
33
+ @subroutes = Hash.new do |hash, k|
34
+ hash[k] = Router.new(@prefix + [k], self)
35
+ end
36
+ end
37
+
38
+ # Adds an individual endpoint to the routing tree
39
+ def << route
40
+ raise "Argument must be an Endpoint" unless Endpoint === route
41
+ base, *subroute = route[:_path].split '/' # Split out first element
42
+ if subroute.count == 0
43
+ route[:_path] = ""
44
+ @endpoints << route
45
+ else
46
+ route[:_path] = subroute.join '/'
47
+ self[subroute[0]] << route
48
+ end
49
+ end
50
+
51
+ # Returns a specific branch of the routing tree
52
+ def [] path
53
+ @subroutes[path]
54
+ end
55
+
56
+ # Returns the routing path
57
+ def path
58
+ "/" + @prefix.join("/")
59
+ end
60
+
61
+ # Returns the mode used for collecting routes
62
+ def route_mode
63
+ mode = :resource
64
+ mode = :namespace if @endpoints.count == 0
65
+ mode = :action if @subroutes.count == 0 && @parent && @parent.route_mode == :resource
66
+ mode
67
+ end
68
+
69
+ # Returns the mode used for actions in this router
70
+ def action_mode
71
+ if /^:/ === @prefix[-2]
72
+ :member
73
+ elsif /^:/ === @prefix[-1]
74
+ :param
75
+ else
76
+ :collection
77
+ end
78
+ end
79
+
80
+ # Determines the action for a specific route
81
+ def action_for route
82
+ raise "Argument must be an Endpoint" unless Endpoint === route
83
+ action = @prefix[-1]
84
+ action = PARAM_ROUTES[route[:method]] if self.action_mode == :param
85
+ action = RESOURCE_ROUTES[route[:method]] if self.route_mode == :resource && self.action_mode == :collection
86
+ action
87
+ end
88
+
89
+ # Draws the routes for this router
90
+ def draw map
91
+ case self.route_mode
92
+ when :resource
93
+
94
+ # Find collection-level resource actions
95
+ actions = @endpoints.map{ |route| self.action_for route }.select{ |action| Symbol === action }
96
+
97
+ # Find parameter-level resource actions
98
+ @subroutes.select{ |k, subroute| /^:/ === k}.values.each do |subroute|
99
+ actions += subroute.endpoints.map{ |route| subroute.action_for route }.select{ |action| Symbol === action }
100
+ end
101
+
102
+ map.resources @prefix.last.to_sym, only: actions do
103
+ draw_actions! map
104
+ draw_subroutes! map
105
+ end
106
+
107
+ when :namespace
108
+ if @prefix.join("/").blank?
109
+ draw_subroutes! map
110
+ else
111
+ map.namespace @prefix.last do
112
+ draw_subroutes! map
113
+ end
114
+ end
115
+ when :action
116
+ draw_actions! map
117
+ end
118
+
119
+ end
120
+
121
+ def routing_tree
122
+
123
+ puts self.path + " - #{self.route_mode}"
124
+ @endpoints.each do |route|
125
+ puts "\t#{route[:method].to_s.upcase} to ##{self.action_for route} (#{self.action_mode})"
126
+ end
127
+ @subroutes.each do |k, subroute| subroute.routing_tree end
128
+
129
+ end
130
+
131
+ # Outputs the routing tree in text format
132
+ def to_s
133
+
134
+ output = ""
135
+
136
+ path = "/" + @prefix.join('/')
137
+ @endpoints.each do |route|
138
+ output += "#{route[:method].to_s.upcase} #{path}\n"
139
+ end
140
+ @subroutes.each do |k, subroute|
141
+ output += subroute.to_s
142
+ end
143
+
144
+ output
145
+
146
+ end
147
+
148
+ protected
149
+
150
+ def draw_actions! map
151
+ indent = "\t" * @prefix.count
152
+ endpoint = @prefix.last
153
+ @endpoints.each do |route|
154
+
155
+ params = Hash.new
156
+ params[:via] = route[:method]
157
+ params[:on] = self.action_mode unless self.action_mode == :param
158
+ params[:action] = self.action_for route
159
+
160
+ # These are handled in the resource
161
+ next if Symbol === params[:action]
162
+
163
+ # Add this individual route
164
+ map.match endpoint, params
165
+
166
+ end
167
+ end
168
+
169
+ def draw_subroutes! map
170
+ @subroutes.values.each do |subroute|
171
+ subroute.draw map
172
+ end
173
+ end
174
+
175
+ end
176
+
177
+ end
178
+ end
@@ -0,0 +1,9 @@
1
+ require "jschema"
2
+ require "rails/swagger/controller"
3
+ require "rails/swagger/router"
4
+ require "rails/swagger/engine"
5
+
6
+ module Rails
7
+ module Swagger
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-swagger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kenaniah Cerny
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jschema
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - kenaniah@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/rails/swagger.rb
77
+ - lib/rails/swagger/controller.rb
78
+ - lib/rails/swagger/engine.rb
79
+ - lib/rails/swagger/router.rb
80
+ homepage: https://github.com/kenaniah/rails-swagger
81
+ licenses: []
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.6.8
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Turns Swagger API schemas into mountable Rails engines
103
+ test_files: []