swagger-core 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 504f9a4a59a9375122f41ce9f860d4ba987794cf
4
+ data.tar.gz: 188dd67ba02fac3c733a576a446daba3b8328aa4
5
+ SHA512:
6
+ metadata.gz: a9296b2964903801be8427af3f6a59e2a67c6fe8612c8be3c0bf634166584208d7732a0c8aeefcb10f3a6434114f4e34d8015a4eaa01bf08875ba3202bed678f
7
+ data.tar.gz: 7aea8b5d5d3f074fa856545d69b0c72b2d5d9d4df84c7b1c0c26e040e8cc81761411cf61e6835a7ff5c30cda0f92c6a5324149dc7e0634365f130c315fa707a3
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -7,6 +7,8 @@
7
7
 
8
8
  # Not yet...
9
9
  Style/Documentation:
10
+ # See https://github.com/bbatsov/rubocop/issues/947
11
+ # Use inch instead
10
12
  Enabled: false
11
13
  Style/Encoding:
12
14
  EnforcedStyle: when_needed
@@ -0,0 +1,4 @@
1
+ --no-private
2
+ lib/swagger/definition_section.rb
3
+ lib/swagger.rb
4
+ lib/**/*.rb
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in swagger.gemspec
4
4
  gemspec
5
+ gem 'pry'
data/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # Swagger
2
2
 
3
- TODO: Write a gem description
3
+ Swagger is a Ruby library for parsing, building, and traversing (Swagger)[http://swagger.io/] API definitions.
4
4
 
5
5
  ## Installation
6
6
 
7
+ *NOTE*: The gem is named `swagger-core`, because `swagger` was taken by an unrelated project.
8
+
7
9
  Add this line to your application's Gemfile:
8
10
 
9
11
  ```ruby
10
- gem 'swagger'
12
+ gem 'swagger-core'
11
13
  ```
12
14
 
13
15
  And then execute:
@@ -16,11 +18,92 @@ And then execute:
16
18
 
17
19
  Or install it yourself as:
18
20
 
19
- $ gem install swagger
21
+ $ gem install swagger-core
22
+
23
+ ## Features
24
+
25
+ - Structural and semantic validation of Swagger objects
26
+ - Convenient traversal APIs: use hierarchical or flat traversals
27
+ - Handles derived or combined properties, like joining root, path, and operation level property definitions
28
+ - A Swagger::Builder to help create valid Swagger documents from other data
20
29
 
21
30
  ## Usage
22
31
 
23
- TODO: Write usage instructions here
32
+ ### Parsing
33
+
34
+ If you're loading a Swagger document from a file, you can use `#load`. The Swagger version will be detected from the file content.
35
+
36
+ ```ruby
37
+ api = Swagger.load('swagger.yaml')
38
+ ```
39
+
40
+ If you already have the Swagger content loaded as a Hash you can call build, or you can call
41
+ build and tell Swagger the content is a JSON or YAML string it needs to parse.
42
+
43
+ ```ruby
44
+ api = Swagger.build(hash)
45
+ # or
46
+ api = Swagger.build(json, format: :json)
47
+ # or
48
+ api = Swagger.build(yaml, format: :yaml)
49
+ ```
50
+
51
+ ### Traversing
52
+
53
+ The parsing methods above all return an API object. The object has a hierarchical object that mirrors the Swagger specification. You can traverse it hierarchically, for example:
54
+
55
+ ```ruby
56
+ api.paths['/pets'].get
57
+ # {"tags"=>["Pet Operations"],
58
+ # "summary"=>"finds pets in the system",
59
+ # "responses"=>
60
+ {"200"=>{"description"=>"pet response", "schema"=>{"type"=>"array", "items"=>{"$ref"=>"#/definitions/Pet"}}, "headers"=>[{"x-expires"=>{"type"=>"string"}}]},
61
+ "default"=>{"description"=>"unexpected error", "schema"=>{"$ref"=>"#/definitions/Error"}}}}
62
+ ```
63
+
64
+ There are also methods available to provide flatter APIs or convenient derived properties. For example:
65
+
66
+ ```ruby
67
+ api.operations.each do | operation |
68
+ puts operation.signature
69
+ end
70
+ # GET petstore.swagger.wordnik.com/api/pets
71
+ # POST petstore.swagger.wordnik.com/api/pets
72
+ # GET petstore.swagger.wordnik.com/api/pets/{id}
73
+ # DELETE petstore.swagger.wordnik.com/api/pets/{id}
74
+ ```
75
+
76
+ See the RDoc documentation for more details.
77
+
78
+ ### Building
79
+
80
+ If you want to build a Swagger document from another structure, you can use the builder. It will validate the structure and data types as you build the Swagger document, but it won't enforce constraints about required Swagger fields until you call `Swagger::Builder#build`.
81
+
82
+ ```ruby
83
+ builder = Swagger.builder
84
+ # or builder = Swagger.builder(version: '2.0')
85
+
86
+ builder.swagger = 2.0
87
+ builder.info do |info|
88
+ info.title = 'Sample Swagger API'
89
+ info.version = '1.0'
90
+ end
91
+ builder.paths = {
92
+ '/foo' => {}
93
+ }
94
+ builder.paths['/foo'].get do |get|
95
+ get.description = 'Testing...'
96
+ get.tags = %w(foo bar)
97
+ end
98
+
99
+ api = builder.build
100
+ ```
101
+
102
+ ## TODO
103
+
104
+ * Support Swagger 1.2 - right now only Swagger 2.0 is supported
105
+ * Better handling of $ref
106
+ * Handle combined parameters, respones, etc
24
107
 
25
108
  ## Contributing
26
109
 
data/Rakefile CHANGED
@@ -1,8 +1,11 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
3
  require 'rubocop/rake_task'
4
+ require 'rake/notes/rake_task'
5
+ require 'inch/rake'
4
6
 
5
7
  RSpec::Core::RakeTask.new(:spec)
6
8
  RuboCop::RakeTask.new
9
+ Inch::Rake::Suggest.new
7
10
 
8
- task default: [:spec, :rubocop]
11
+ task default: [:spec, :rubocop, :notes]
@@ -3,6 +3,8 @@ require 'addressable/uri'
3
3
  require 'addressable/template'
4
4
  require 'hashie'
5
5
 
6
+ # Provides loading and building capabilities for Swagger.
7
+ # @see http://swagger.io Swagger
6
8
  module Swagger
7
9
  RESOURCES_DIR = File.expand_path '../../resources/', __FILE__
8
10
  class InvalidDefinition < StandardError
@@ -11,20 +13,42 @@ module Swagger
11
13
  super("The Swagger definition is invalid. The following errors were detected: #{errors.inspect}")
12
14
  end
13
15
  end
16
+ # Instantiates a Swagger::API from the content.
17
+ # @param [Hash] opts the build options
18
+ # @option opts [String] :version the target Swagger specification version
19
+ # @returns [API]
20
+ def self.build(content, opts = {})
21
+ parser ||= Swagger::Parsers.parser_for(opts[:format])
22
+ content = parser.parse(content) unless parser.nil?
23
+ Swagger::API.build(content)
24
+ end
14
25
 
15
- def self.load(file, loader = nil)
26
+ # Load a Swagger document from a file.
27
+ # @param [Hash] opts the load options
28
+ # @option opts [String] :format the format (yaml or json). Detected by file extension if omitted.
29
+ # @returns [API] a Swagger API object
30
+ def self.load(file, opts = {})
16
31
  ext = File.extname file
17
- loader ||= Swagger::Loaders.loader_for ext
18
- loader.load File.read(file)
32
+ opts[:format] = ext
33
+ content = File.read(file)
34
+ build(content, opts)
35
+ end
36
+
37
+ # Creates a Swagger::Builder that can be used to create a Swagger document.
38
+ # @param [Hash] opts the build options
39
+ # @option opts [String] :version the target Swagger specification version
40
+ # @returns Swagger::Builder
41
+ def self.builder(opts = {})
42
+ Swagger::Builder.builder(opts)
19
43
  end
20
44
  end
21
45
 
22
46
  require 'swagger/attachable'
23
- require 'swagger/definition_section'
47
+ require 'swagger/swagger_object'
24
48
  require 'swagger/schema'
25
49
  require 'swagger/uri'
26
50
  require 'swagger/uri_template'
27
- require 'swagger/loaders'
51
+ require 'swagger/parsers'
28
52
  require 'swagger/mime_type'
29
- require 'swagger/api_declaration'
53
+ require 'swagger/api'
30
54
  require 'swagger/builder'
@@ -1,11 +1,12 @@
1
1
  module Swagger
2
- class APIDeclaration < DefinitionSection
2
+ # A common interface for building or loading Swagger documents of any version. See subclasses.
3
+ class API < SwaggerObject
3
4
  def self.build(hash)
4
5
  version = hash['swaggerVersion'] || hash['swagger']
5
6
  major, _minor = version.to_s.split('.')
6
7
  case major
7
8
  when '2'
8
- Swagger::V2::APIDeclaration.new hash
9
+ Swagger::V2::API.new hash
9
10
  else
10
11
  fail ArgumentError, "Swagger version #{version} is not currently supported"
11
12
  end
@@ -22,4 +23,4 @@ module Swagger
22
23
  end
23
24
  end
24
25
 
25
- require 'swagger/v2/api_declaration'
26
+ require 'swagger/v2/api'
@@ -1,5 +1,13 @@
1
1
  module Swagger
2
+ # A module that attaches parent objects to their children so you can navigate back
3
+ # up the hierarchy.
2
4
  module Attachable
5
+ # The top-level object in the hierarchy.
6
+ def root
7
+ return self if parent.nil?
8
+ parent.root
9
+ end
10
+
3
11
  # @api private
4
12
  def attach_parent(parent)
5
13
  @parent = parent
@@ -22,10 +30,5 @@ module Swagger
22
30
  end
23
31
  end
24
32
  end
25
-
26
- def root
27
- return self if parent.nil?
28
- parent.root
29
- end
30
33
  end
31
34
  end
@@ -5,6 +5,7 @@ module Swagger
5
5
  # the same rules as a Dash except for 'required' properties,
6
6
  # which are not enforced until converting to a Dash via `build`.
7
7
  module Bash
8
+ # @api private
8
9
  module ClassMethods
9
10
  def self.extend_object(dash)
10
11
  fail TypeError, 'Bash only works on Dash' unless dash <= Hashie::Dash
@@ -17,6 +18,7 @@ module Swagger
17
18
  end
18
19
  end
19
20
 
21
+ # @api private
20
22
  def self.included(dash) # rubocop:disable Metrics/MethodLength
21
23
  fail TypeError, 'Bash only works on Dash' unless dash <= Hashie::Dash
22
24
  dash.extend ClassMethods
@@ -81,7 +83,27 @@ module Swagger
81
83
  end
82
84
  end
83
85
 
84
- class Builder < Swagger::V2::APIDeclaration
85
- include Swagger::Bash
86
+ # An object for building a Swagger document. Coerces and validates data types
87
+ # as create the document, but does not enforce required fields until you call
88
+ # #{Bash#build}.
89
+ module Builder
90
+ def self.builder(opts = {})
91
+ version = opts[:version] || '2.0'
92
+ target_class = target_api_class(version)
93
+ klass = Swagger::Bash.infect(target_class)
94
+ klass.new({})
95
+ end
96
+
97
+ private
98
+
99
+ def self.target_api_class(version)
100
+ major, _minor = version.to_s.split('.')
101
+ case major
102
+ when '2'
103
+ Swagger::V2::API
104
+ else
105
+ fail ArgumentError, "Swagger version #{version} is not currently supported"
106
+ end
107
+ end
86
108
  end
87
109
  end
@@ -1,12 +1,22 @@
1
1
  require 'mime/types'
2
2
 
3
3
  module Swagger
4
+ # Class representing Media Types (commonly known as MIME Types).
5
+ # @see http://en.wikipedia.org/wiki/Internet_media_type
4
6
  class MimeType < String
5
7
  extend Forwardable
6
8
  def_delegators :@mime_type, :media_type, :sub_type
7
9
 
8
10
  MIME_TYPE_FORMAT = /(\w+)\/(\w+\.)?([\w\.]+)(\+\w+)?\s*(;.*)?/
9
11
 
12
+ COMMON_ALIASES = {
13
+ txt: 'text/plain',
14
+ text: 'text/plain',
15
+ json: 'application/json',
16
+ xml: 'application/xml',
17
+ binary: 'application/octet-stream'
18
+ }
19
+
10
20
  def initialize(mime_type_name)
11
21
  @mime_type_name = mime_type_name.to_s
12
22
  @mime_type = MIME::Types[@mime_type_name].first || base_type(@mime_type_name)
@@ -14,6 +24,16 @@ module Swagger
14
24
  super @mime_type_name
15
25
  end
16
26
 
27
+ def self.parser_for(mime_type)
28
+ mime_type = COMMON_ALIASES[mime_type] if COMMON_ALIASES.key? mime_type
29
+ case mime_type
30
+ when 'application/json'
31
+ return JSON
32
+ else
33
+ fail NotImplementedError, "Parser support for #{mime_type} is not implemented"
34
+ end
35
+ end
36
+
17
37
  private
18
38
 
19
39
  def base_type(mime_type_name)
@@ -0,0 +1,38 @@
1
+ autoload :YAML, 'yaml'
2
+ autoload :JSON, 'json'
3
+
4
+ module Swagger
5
+ # Provides classes for loading Swagger from YAML or JSON.
6
+ module Parsers
7
+ def self.parser_for(format)
8
+ case format
9
+ when '.yaml', '.yml', :yaml
10
+ YAMLParser
11
+ when '.json', '.js', :json
12
+ JSONParser
13
+ else
14
+ nil
15
+ end
16
+ end
17
+
18
+ # Parses YAML content
19
+ class YAMLParser
20
+ # Parses a YAML document
21
+ # @param content [String] The YAML content to parse
22
+ # @return [Hash] the parsed content
23
+ def self.parse(content)
24
+ YAML.load(content)
25
+ end
26
+ end
27
+
28
+ # Parses JSON content
29
+ class JSONParser
30
+ # Parses a JSON document
31
+ # @param content [String] The JSON content to parse
32
+ # @return [Hash] the parsed content
33
+ def self.parse(content)
34
+ JSON.parse(content)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,4 +1,7 @@
1
1
  module Swagger
2
+ # Represents a Swagger Schema Object, a more deterministic subset of JSON Schema.
3
+ # @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#schema-object- Schema Object
4
+ # @see http://json-schema.org/ JSON Schema
2
5
  class Schema < Hashie::Mash
3
6
  include Attachable
4
7
  include Hashie::Extensions::MergeInitializer
@@ -0,0 +1,42 @@
1
+ module Swagger
2
+ # A class that represents an Object defined in the Swagger specification.
3
+ # Provides methods for defining fields in the object.
4
+ class SwaggerObject < Hashie::Dash
5
+ include Hashie::Extensions::Coercion
6
+ include Hashie::Extensions::IndifferentAccess
7
+ include Swagger::Attachable
8
+
9
+ attr_accessor :parent
10
+
11
+ # @api private
12
+ # Initializes a Swagger object, using Hashie::Dash,
13
+ # and attaches to children objects so navigation via +parent+
14
+ # and +root+ is possible.
15
+ def initialize(hash)
16
+ super
17
+ attach_to_children
18
+ end
19
+
20
+ # @api private
21
+ # @!macro [attach] field
22
+ # @!attribute [rw] $1
23
+ # Swagger field $1. $3
24
+ # @return [$2]
25
+ # Defines a Swagger field on a class.
26
+ def self.field(name, type, opts = {})
27
+ property name, opts
28
+ coerce_key name, type
29
+ end
30
+
31
+ # @api private
32
+ # @!macro [attach] required_field
33
+ # @!attribute [rw] $1
34
+ # **Required** Swagger field $1. $3
35
+ # @return [$2]
36
+ # Defines a required Swagger field on a class.
37
+ def self.required_field(name, type, opts = {})
38
+ opts[:required] = true
39
+ field(name, type, opts)
40
+ end
41
+ end
42
+ end
@@ -1,8 +1,9 @@
1
1
  module Swagger
2
+ # Class representing a URI. Backed by Addressable::URI.
3
+ # @see http://en.wikipedia.org/wiki/Uniform_resource_identifier
2
4
  class URI < String
3
5
  attr_reader :uri
4
6
  def initialize(string)
5
- # FIXME: Is it possible to initialize with heuristic parse once?
6
7
  @uri = Addressable::URI.heuristic_parse string
7
8
  super
8
9
  end