rails-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.
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: []