svelte 0.1.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.
@@ -0,0 +1,41 @@
1
+ module Svelte
2
+ # Describes a Swagger API Path
3
+ class Path
4
+ attr_reader :non_parameter_elements, :parameter_elements
5
+
6
+ # Creates a new Path.
7
+ # @param path [String] path i.e. `'/store/inventory'`
8
+ # @param operations [Hash] path operations
9
+ def initialize(path:, operations:)
10
+ @path = path
11
+ separate_path_elements
12
+ @raw_operations = operations
13
+ end
14
+
15
+ # Path operations
16
+ # @return [Array<Operation>] list of operations for the path
17
+ def operations
18
+ validate_operations
19
+ @operations ||= @raw_operations.map do |operation, properties|
20
+ Operation.new(verb: operation, properties: properties, path: self)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def separate_path_elements
27
+ path_elements = @path.split('/').reject(&:empty?)
28
+ @non_parameter_elements,
29
+ @parameter_elements = path_elements.partition do |element|
30
+ !element.match(/\{\w+\}/)
31
+ end
32
+ @parameter_elements.map! { |p| p.scan(/{(\w*)}/) }.flatten!
33
+ end
34
+
35
+ def validate_operations
36
+ unless @raw_operations.is_a?(Hash)
37
+ raise JSONError, "Expected the path to contain a list of operations"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ module Svelte
2
+ # Dynamically builds a module hierarchy on top of a given module
3
+ # based on the given Path
4
+ class PathBuilder
5
+ class << self
6
+ # Builds a new Module hierarchy on top of `module_constant`
7
+ # If the path contains more than one part, modules will be built
8
+ # on top of each other.
9
+ #
10
+ # Example:
11
+ # If the `path` is `/store/inventory` and the `module_constant` is
12
+ # `Test`, the resulting module hierarchy will be `Test::Store::Inventory`
13
+ # @param path [Path] path to build
14
+ # @param module_constant [Module] operation to build
15
+ def build(path:, module_constant:)
16
+ create_module_hierarchy(base_module: module_constant,
17
+ additional_modules: path.non_parameter_elements)
18
+ end
19
+
20
+ private
21
+
22
+ def create_module_hierarchy(base_module:, additional_modules:)
23
+ additional_modules.reduce(base_module) do |current_module, element|
24
+ constant_name = StringManipulator.constant_name_for(element)
25
+
26
+ unless current_module.const_defined?(constant_name, false)
27
+ current_module.const_set(constant_name, Module.new)
28
+ end
29
+
30
+ current_module.const_get(constant_name, false)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,44 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'typhoeus'
4
+ require 'typhoeus/adapters/faraday'
5
+
6
+ module Svelte
7
+ # Rest client to make requests to the service endpoints
8
+ class RestClient
9
+ class << self
10
+
11
+ # Makes an http call to a given REST endpoint
12
+ # @param verb [String] http verb to use for the request
13
+ # (`get`, `post`, `put`, etc.)
14
+ # @param url [String] request url
15
+ # @param params [Hash] parameters to send to the request
16
+ # @param options [Hash] options
17
+ # @raise [HTTPError] if an HTTP layer error occurs,
18
+ # an exception will be raised
19
+ #
20
+ # @return [Faraday::Response] http response from the service
21
+ def call(verb:, url:, params: {}, options: {})
22
+ connection.send verb, url, params do |request|
23
+ request.options.timeout = options[:timeout] if options[:timeout]
24
+ end
25
+ rescue Faraday::TimeoutError => e
26
+ raise HTTPError.new(parent: e)
27
+ rescue Faraday::ResourceNotFound => e
28
+ raise HTTPError.new(parent: e)
29
+ rescue Faraday::ClientError => e
30
+ raise HTTPError.new(parent: e)
31
+ end
32
+
33
+ private
34
+
35
+ def connection
36
+ @@connection ||= Faraday.new(ssl: { verify: true }) do |faraday|
37
+ faraday.request :json
38
+ faraday.response :json, content_type: /\bjson$/
39
+ faraday.adapter :typhoeus
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ module Svelte
2
+ # Dynamically generates a client to consume a Swagger API
3
+ class Service
4
+ class << self
5
+ # Generate a Service via URL or JSON.
6
+ # @param url [String] full URL of the Swagger API spec
7
+ # @param json [String] full Swagger API spec as a String
8
+ # @param module_name [String] constant name where Svelte will
9
+ # build the functionality on top of
10
+ # @return [Module] A newly created `Module` with the
11
+ # module hierarchy and methods to consume the Swagger API
12
+ # The new module will be built on top of `Svelte::Service` and will
13
+ # have `module_name` as its constant name, therefore it can also be
14
+ # accessed via `Svelte::Service::<module_name>`. For example, if
15
+ # `module_name` is `Comments`, the new module will be built in
16
+ # `Svelte::Service::Comments`
17
+ # @note Either `url` or `json` need to be provided. `url` will take
18
+ # precedence over `json`
19
+ def create(url:, json:, module_name:, options:)
20
+ json = get_json(url: url) if url
21
+ SwaggerBuilder.new(raw_hash: JSON.parse(json.to_s),
22
+ module_name: module_name,
23
+ options: options).make_resource
24
+ end
25
+
26
+ private
27
+
28
+ def get_json(url:)
29
+ Faraday.get(url).body
30
+ rescue Faraday::ClientError => e
31
+ raise HTTPError.new(
32
+ message: "Could not get API json from #{url}",
33
+ parent: e
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,66 @@
1
+ module Svelte
2
+ # Provides helper methods to manipulate strings in order to generate
3
+ # valid constant and method names from them
4
+ module StringManipulator
5
+ class << self
6
+ # Generates a valid Ruby constant name as similar as possible to `string`
7
+ # @param string [String] input string
8
+ # @return [String] a valid Ruby constant name based on `string`
9
+ def constant_name_for(string)
10
+ s = fixify(string)
11
+ pascalize(s)
12
+ end
13
+
14
+ # Generates a valid Ruby method name as similar as possible to `string`
15
+ # @param string [String] input string
16
+ # @return [String] a valid Ruby method name based on `string`
17
+ def method_name_for(string)
18
+ s = fixify(string)
19
+ snakify(s)
20
+ end
21
+
22
+ private
23
+
24
+ # Converts a single digit to a number.
25
+ def fixify(string)
26
+ dictionary = {
27
+ '1' => 'One',
28
+ '2' => 'Two',
29
+ '3' => 'Three',
30
+ '4' => 'Four',
31
+ '5' => 'Five',
32
+ '6' => 'Six',
33
+ '7' => 'Seven',
34
+ '8' => 'Eight',
35
+ '9' => 'Nine',
36
+ '0' => 'Zero'
37
+ }
38
+
39
+ string.sub(/^(\d+)/) { dictionary[Regexp.last_match[1]] }
40
+ end
41
+
42
+ def pascalize(string)
43
+ string.split('-').map(&method(:capitalize_first_char)).join
44
+ end
45
+
46
+ def capitalize_first_char(string)
47
+ string.sub(/^(.)/) { Regexp.last_match[1].capitalize }
48
+ end
49
+
50
+ def snakify(string)
51
+ string.gsub('-', '_').
52
+ # This first regex handles the case of a string ending in an acroynm
53
+ gsub(/([a-z])([A-Z]+)\z/, '\1_\2').
54
+ # This regex then handles acronyms in other places, including at
55
+ # the start of the string
56
+ # This is aided by the fact that a acronym cannot be preceded by
57
+ # an unrelated capital in camel case.
58
+ gsub(/([A-Z]+)([A-Z])([^A-Z_])/, '\1_\2\3').
59
+ # This then ensures all lower case letters that
60
+ # are not yet followed by an underscore
61
+ # or another lower case letter get an underscored appended.
62
+ gsub(/([a-z])([^a-z_])/, '\1_\2').downcase
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,94 @@
1
+ module Svelte
2
+ # Dynamically builds Swagger API paths and operations on top of a given module
3
+ class SwaggerBuilder
4
+ attr_reader :raw_hash, :module_name, :configuration
5
+
6
+ # Creates a new SwaggerBuilder
7
+ # @param raw_hash [Hash] Swagger API definition
8
+ # @param module_name [String] name of the constant you want built
9
+ # @param options [Hash] Swagger API options. It will be used to build the
10
+ # [Configuration]. Supports the `:host` value for now.
11
+ def initialize(raw_hash:, module_name:, options:)
12
+ @raw_hash = raw_hash
13
+ @module_name = module_name
14
+ @configuration = build_configuration(options)
15
+ validate
16
+ end
17
+
18
+ # Dynamically creates a new resource on top of `Svelte::Service` with the
19
+ # name `module_name`, based on the Swagger API description provided
20
+ # in `raw_hash`
21
+ # @return [Module] the module built
22
+ def make_resource
23
+ resource = Module.new
24
+ paths.each do |path|
25
+ new_module = PathBuilder.build(path: path, module_constant: resource)
26
+ path.operations.each do |operation|
27
+ OperationBuilder.build(operation: operation,
28
+ module_constant: new_module,
29
+ configuration: configuration)
30
+ end
31
+ end
32
+ Service.const_set(module_name, resource)
33
+ end
34
+
35
+ # @return [Array<Path>] Paths of the Swagger spec
36
+ def paths
37
+ raw_hash['paths'].map do |path, operations|
38
+ Path.new(path: path, operations: operations)
39
+ end
40
+ end
41
+
42
+ # @return [String] base path of the Swagger spec
43
+ def base_path
44
+ raw_hash['basePath']
45
+ end
46
+
47
+ # @return [String] host of the Swagger spec
48
+ def host
49
+ raw_hash['host']
50
+ end
51
+
52
+ private
53
+
54
+ def build_configuration(_options)
55
+ options = {
56
+ host: host,
57
+ base_path: base_path,
58
+ protocol: _options[:protocol]
59
+ }
60
+ Configuration.new(options: options)
61
+ end
62
+
63
+ def validate
64
+ validate_version
65
+ validate_paths
66
+ validate_host
67
+ validate_base_path
68
+ end
69
+
70
+ def validate_version
71
+ if raw_hash['swagger'] != '2.0'
72
+ raise VersionError
73
+ end
74
+ end
75
+
76
+ def validate_paths
77
+ unless raw_hash['paths'].is_a?(Hash)
78
+ raise JSONError, 'Expected JSON to contain an object of valid paths'
79
+ end
80
+ end
81
+
82
+ def validate_host
83
+ unless raw_hash['host'].is_a?(String)
84
+ raise JSONError, '`host` field in JSON is invalid'
85
+ end
86
+ end
87
+
88
+ def validate_base_path
89
+ unless raw_hash['basePath'].is_a?(String)
90
+ raise JSONError, '`basePath` field in JSON is invalid'
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,4 @@
1
+ module Svelte
2
+ # Version
3
+ VERSION = '0.1.1'.freeze
4
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'svelte/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'svelte'
8
+ spec.version = Svelte::VERSION
9
+ spec.authors = ['notonthehighstreet.com']
10
+ spec.email = ['tech.contact@notonthehighstreet.com']
11
+
12
+ spec.summary = 'Dynamic Ruby API Client from Swagger JSON Spec'
13
+ spec.description = 'This gem consumes a Swagger API json file and maps the API into easy-to-use Ruby objects'
14
+ spec.homepage = 'https://github.com/notonthehighstreet/svelte'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+ spec.required_ruby_version = '~> 2.1'
21
+
22
+ spec.add_dependency 'faraday', '~> 0.9'
23
+ spec.add_dependency 'faraday_middleware', '~> 0.10'
24
+ spec.add_dependency 'typhoeus', '~> 1.0'
25
+
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'redcarpet', '~> 3.3'
28
+ spec.add_development_dependency 'rspec', '~> 3.4'
29
+ spec.add_development_dependency 'simplecov', '~> 0.11'
30
+ spec.add_development_dependency 'webmock', '~> 1.22'
31
+ spec.add_development_dependency 'yard', '~> 0.8'
32
+ spec.add_development_dependency 'rubocop', '~> 0.36'
33
+ end
metadata ADDED
@@ -0,0 +1,219 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: svelte
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - notonthehighstreet.com
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0.10'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: typhoeus
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.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: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: redcarpet
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '3.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '3.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '3.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '3.4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '0.11'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '0.11'
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '1.22'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: '1.22'
125
+ - !ruby/object:Gem::Dependency
126
+ name: yard
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ~>
130
+ - !ruby/object:Gem::Version
131
+ version: '0.8'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: '0.8'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ~>
144
+ - !ruby/object:Gem::Version
145
+ version: '0.36'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ~>
151
+ - !ruby/object:Gem::Version
152
+ version: '0.36'
153
+ description: This gem consumes a Swagger API json file and maps the API into easy-to-use
154
+ Ruby objects
155
+ email:
156
+ - tech.contact@notonthehighstreet.com
157
+ executables: []
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - .gitignore
162
+ - .rspec
163
+ - .rubocop.yml
164
+ - .ruby-version
165
+ - .travis.yml
166
+ - .yardopts
167
+ - CODE_OF_CONDUCT.md
168
+ - Gemfile
169
+ - LICENSE.txt
170
+ - README.md
171
+ - Rakefile
172
+ - bin/console
173
+ - bin/setup
174
+ - lib/svelte.rb
175
+ - lib/svelte/configuration.rb
176
+ - lib/svelte/errors.rb
177
+ - lib/svelte/errors/http_error.rb
178
+ - lib/svelte/errors/json_error.rb
179
+ - lib/svelte/errors/parameter_error.rb
180
+ - lib/svelte/errors/version_error.rb
181
+ - lib/svelte/generic_operation.rb
182
+ - lib/svelte/model_factory.rb
183
+ - lib/svelte/model_factory/parameter.rb
184
+ - lib/svelte/operation.rb
185
+ - lib/svelte/operation_builder.rb
186
+ - lib/svelte/path.rb
187
+ - lib/svelte/path_builder.rb
188
+ - lib/svelte/rest_client.rb
189
+ - lib/svelte/service.rb
190
+ - lib/svelte/string_manipulator.rb
191
+ - lib/svelte/swagger_builder.rb
192
+ - lib/svelte/version.rb
193
+ - svelte.gemspec
194
+ homepage: https://github.com/notonthehighstreet/svelte
195
+ licenses:
196
+ - MIT
197
+ metadata: {}
198
+ post_install_message:
199
+ rdoc_options: []
200
+ require_paths:
201
+ - lib
202
+ required_ruby_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ~>
205
+ - !ruby/object:Gem::Version
206
+ version: '2.1'
207
+ required_rubygems_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - '>='
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ requirements: []
213
+ rubyforge_project:
214
+ rubygems_version: 2.4.6
215
+ signing_key:
216
+ specification_version: 4
217
+ summary: Dynamic Ruby API Client from Swagger JSON Spec
218
+ test_files: []
219
+ has_rdoc: