oven 0.1.0.rc1

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,27 @@
1
+ # frozen-string-literal: true
2
+ module Oven
3
+ module Patches
4
+ module StringExt
5
+ refine String do
6
+ def underscore
7
+ return self unless self =~ /[A-Z-]|::/
8
+ word = gsub('::', '/')
9
+ word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{/(?=a)b/})(?=\b|[^a-z])/) { "#{$1 && '_'}#{$2.downcase}" }
10
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
11
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
12
+ word.tr!("-", "_")
13
+ word.downcase!
14
+ word
15
+ end
16
+
17
+ def deconstantize
18
+ self[0, rindex('::') || 0]
19
+ end
20
+
21
+ def demodulize
22
+ (i = rindex('::')) ? self[(i+2)..-1] : self
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,82 @@
1
+ # frozen-string-literal: true
2
+ require 'net/http'
3
+ <%- requires.each do |_require| -%>
4
+ require '<%= File.join(name_declaration.select(&:namespace?).map(&:underscore) << _require) %>'
5
+ <%- end -%>
6
+
7
+ <%- name_declaration.each do |name| -%>
8
+ <%= name.type %> <%= name %>
9
+ <%- end -%>
10
+ attr_reader :domain, :proxy_addr, :proxy_port, :proxy_user, :proxy_password
11
+
12
+ def initialize(domain, proxy_addr: nil, proxy_port: nil, proxy_user: nil, proxy_password: nil, **options)
13
+ @domain, @proxy_addr, @proxy_port, @proxy_user, @proxy_password, @interceptors, @observers, @options =
14
+ domain, proxy_addr, proxy_port, proxy_user, proxy_password, [], [], DEFAULT_OPTIONS.merge(options)
15
+
16
+ <%- interceptors.each do |interceptor| -%>
17
+ register_interceptor(<%= interceptor %>.new)
18
+ <%- end -%>
19
+ <%- observers.each do |observer| -%>
20
+ register_observer(<%= observer %>.new)
21
+ <%- end -%>
22
+ end
23
+
24
+ def register_interceptor(interceptor)
25
+ @interceptors << interceptor
26
+ self
27
+ end
28
+
29
+ def register_observer(observer)
30
+ @observers << observer
31
+ self
32
+ end
33
+ <%- method_definitions.each do |definition| %>
34
+ def <%= definition.method_name %>(<%= definition.parameter_signature %><%= ', ' if !definition.parameters.empty? %>query: {}, headers: {}, **options)
35
+ request(Net::HTTP::<%= definition.verb.to_s.capitalize %>, uri("<%= definition.path %>", query), <%= definition.variable_name_for_body %>, headers, method: :<%= definition.method_name %>, **options)
36
+ end
37
+ <% definition.aliases.each {|name| %>alias <%= name %> <%= definition.method_name %><% } %>
38
+ <%- end %>
39
+ private
40
+
41
+ DEFAULT_OPTIONS = {
42
+ ca_file: nil,
43
+ ca_path: nil,
44
+ cert: nil,
45
+ cert_store: nil,
46
+ ciphers: nil,
47
+ close_on_empty_response: nil,
48
+ key: nil,
49
+ open_timeout: nil,
50
+ read_timeout: nil,
51
+ ssl_timeout: nil,
52
+ ssl_version: nil,
53
+ use_ssl: nil,
54
+ verify_callback: nil,
55
+ verify_depth: nil,
56
+ verify_mode: nil
57
+ }.freeze
58
+
59
+ HTTPS = 'https'.freeze
60
+
61
+ def request(request_class, uri, body, headers, options = {})
62
+ uri, body, headers, options = @interceptors.reduce([uri, body, headers, @options.merge(options)]) {|r, i| i.before_request(*r) }
63
+
64
+ begin
65
+ response = Net::HTTP.start(uri.host, uri.port, proxy_addr, proxy_port, proxy_user, proxy_password, options, use_ssl: (uri.scheme == HTTPS)) do |http|
66
+ http.request request_class.new(uri, headers), body
67
+ end
68
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
69
+ raise NetworkError, "A network error occurred: #{e.class} (#{e.message})"
70
+ end
71
+
72
+ @observers.reduce(response) {|r, o| o.received_response(r, options) }
73
+ end
74
+
75
+ def uri(path, query = {})
76
+ uri = URI.join(domain, path)
77
+ uri.query = URI.encode_www_form(query) if !query.empty?
78
+ uri
79
+ end
80
+ <%- name_declaration.each do -%>
81
+ end
82
+ <%- end -%>
@@ -0,0 +1,45 @@
1
+ <%- name_declaration.namespace_or_client_name.each do |name| -%>
2
+ <%= name.type %> <%= name %>
3
+ <%- end -%>
4
+ class APIError < StandardError; end
5
+ class NetworkError < APIError; end
6
+
7
+ class HttpError < APIError
8
+ attr_reader :response
9
+
10
+ def initialize(message, response)
11
+ super(message)
12
+ @response = response
13
+ end
14
+ end
15
+
16
+ class ClientError < HttpError; end
17
+
18
+ <%- CLIENT_ERROR_STATUSES.each do |status, error_name| -%>
19
+ class <%= error_name.tr(" ", "").ljust(27) %> < ClientError; end # status: <%= status %>
20
+ <%- end -%>
21
+
22
+ class ServerError < HttpError; end
23
+
24
+ <%- SERVER_ERROR_STATUSES.each do |status, error_name| -%>
25
+ class <%= error_name.tr(" ", "").ljust(29) %> < ServerError; end # status: <%= status %>
26
+ <%- end -%>
27
+
28
+ STATUS_TO_EXCEPTION_MAPPING = {
29
+ <%- CLIENT_ERROR_STATUSES.merge(SERVER_ERROR_STATUSES).each do |status, error| -%>
30
+ '<%= status %>' => <%= error.tr(" ", "") %>,
31
+ <%- end -%>
32
+ }.freeze
33
+
34
+ class ResponseHandler
35
+ def received_response(response, options)
36
+ error = STATUS_TO_EXCEPTION_MAPPING[response.code]
37
+ raise error.new("Receieved an error response #{response.code} #{error.to_s.split("::").last}: #{response.body}", response) if error
38
+ response
39
+ end
40
+ end
41
+
42
+ private_constant :ResponseHandler
43
+ <%- name_declaration.namespace_or_client_name.each do -%>
44
+ end
45
+ <%- end -%>
@@ -0,0 +1,47 @@
1
+ require 'json'
2
+ require 'delegate'
3
+
4
+ <%- name_declaration.namespace_or_client_name.each do |name| -%>
5
+ <%= name.type %> <%= name %>
6
+ <%- end -%>
7
+ class JsonResponse < DelegateClass(Net::HTTPResponse)
8
+ alias response __getobj__
9
+ alias headers to_hash
10
+ HAS_SYMBOL_GC = RUBY_VERSION > '2.2.0'
11
+
12
+ def json
13
+ parsable? ? JSON.parse(body, symbolize_names: HAS_SYMBOL_GC) : nil
14
+ end
15
+
16
+ def inspect
17
+ "#<JsonResponse response: #{response.inspect}, json: #{json}>"
18
+ end
19
+ alias to_s inspect
20
+
21
+ def parsable?
22
+ !!body && !body.empty?
23
+ end
24
+ end
25
+
26
+ class JsonSerializer
27
+ APPLICATION_JSON = 'application/json'.freeze
28
+ JSON_REQUEST_HEADERS = {
29
+ 'Content-Type' => APPLICATION_JSON,
30
+ 'Accept' => APPLICATION_JSON
31
+ }.freeze
32
+
33
+ def before_request(uri, body, headers, options)
34
+ [uri, (body.nil? ? body : JSON.dump(body)), headers.merge(JSON_REQUEST_HEADERS), options]
35
+ end
36
+ end
37
+
38
+ class JsonDeserializer
39
+ def received_response(response, options)
40
+ JsonResponse.new(response)
41
+ end
42
+ end
43
+
44
+ private_constant :JsonSerializer, :JsonDeserializer
45
+ <%- name_declaration.namespace_or_client_name.each do -%>
46
+ end
47
+ <%- end -%>
@@ -0,0 +1,21 @@
1
+ <%- extension.class_names.each do |class_name| -%>
2
+ require '<%= File.join(name_declaration.select(&:namespace?).map(&:underscore) << 'models' << class_name.underscore) %>'
3
+ <%- end -%>
4
+
5
+ <%- name_declaration.namespace_or_client_name.each do |name| -%>
6
+ <%= name.type %> <%= name %>
7
+ <%- end -%>
8
+ OBJECT_MAP = {
9
+ <%- extension.object_mapping.each do |class_name, attrs| -%>
10
+ <%= class_name %> => {
11
+ <%- attrs.each do |attr_name, attr_class| -%>
12
+ <%- if !extension.primitive_classes.include?(attr_class) -%>
13
+ <%= attr_name %>: <%= attr_class %>,
14
+ <%- end -%>
15
+ <%- end -%>
16
+ },
17
+ <%- end -%>
18
+ }
19
+ <%- name_declaration.namespace_or_client_name.each do -%>
20
+ end
21
+ <%- end -%>
@@ -0,0 +1,39 @@
1
+ require 'object_mapper'
2
+
3
+ <%- name_declaration.namespace_or_client_name.each do |name| -%>
4
+ <%= name.type %> <%= name %>
5
+ <%- end -%>
6
+ class ObjectConverter
7
+ METHOD_TO_CLASS_MAP = {
8
+ <%- extension.method_definitions.select {|d| d.options.key?(:class) }.each do |definition| -%>
9
+ <%= definition.method_name %>: <%= definition.options.fetch(:class, Hash) %>,
10
+ <%- end -%>
11
+ }
12
+
13
+ def received_response(response, method: nil, **)
14
+ if method && response.respond_to?(:parsable?) && response.parsable? && METHOD_TO_CLASS_MAP[method]
15
+ ObjectResponse.new(response, METHOD_TO_CLASS_MAP[method])
16
+ else
17
+ response
18
+ end
19
+ end
20
+ end
21
+
22
+ class ObjectResponse < DelegateClass(JsonResponse)
23
+ OBJECT_MAPPER = ::ObjectMapper.new(OBJECT_MAP)
24
+
25
+ def initialize(response, target_class)
26
+ super(response)
27
+ @target_class = target_class
28
+ end
29
+
30
+ def object
31
+ OBJECT_MAPPER.convert(json, to: @target_class)
32
+ end
33
+ alias body_as_object object
34
+ end
35
+
36
+ private_constant :ObjectConverter, :ObjectResponse
37
+ <%- name_declaration.namespace_or_client_name.each do -%>
38
+ end
39
+ <%- end -%>
@@ -0,0 +1,13 @@
1
+ <%- name_declaration.namespace_or_client_name.each do |name| -%>
2
+ <%= name.type %> <%= name %>
3
+ <%- end -%>
4
+ class <%= class_name %>
5
+ attr <%= attributes.map{|attr| ":#{attr}" }.join(', ') %>
6
+
7
+ def initialize(<%= attributes.map{|attr| "#{attr}: nil" }.join(', ') %>)
8
+ <%= attributes.map{|attr| "@#{attr}" }.join(", ") %> = <%= attributes.map{|attr| reserved_keyword_map.fetch(attr.to_s, attr) }.join(", ") %>
9
+ end
10
+ end
11
+ <%- name_declaration.namespace_or_client_name.each do -%>
12
+ end
13
+ <%- end -%>
@@ -0,0 +1,3 @@
1
+ module Oven
2
+ VERSION = "0.1.0.rc1"
3
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'oven/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "oven"
8
+ spec.version = Oven::VERSION
9
+ spec.authors = ["Yuki Nishijima"]
10
+ spec.email = ["mail@yukinishijima.net"]
11
+ spec.summary = %q{Static API client generator}
12
+ spec.description = %q{Oven generates a dependency-free HTTP client.}
13
+ spec.homepage = "https://github.com/yuki24/oven"
14
+ spec.license = "MIT"
15
+ spec.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test)/}) }
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_development_dependency "bundler"
19
+ spec.add_development_dependency "rake"
20
+ spec.add_development_dependency "minitest"
21
+ spec.add_development_dependency "webmock"
22
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oven
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.rc1
5
+ platform: ruby
6
+ authors:
7
+ - Yuki Nishijima
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
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: rake
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: minitest
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: webmock
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: Oven generates a dependency-free HTTP client.
70
+ email:
71
+ - mail@yukinishijima.net
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - CODE_OF_CONDUCT.md
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - lib/oven.rb
86
+ - lib/oven/builders.rb
87
+ - lib/oven/dsl_context.rb
88
+ - lib/oven/extension_configurers.rb
89
+ - lib/oven/http_statuses.rb
90
+ - lib/oven/path_parser.rb
91
+ - lib/oven/path_parser.y
92
+ - lib/oven/path_parser/nodes.rb
93
+ - lib/oven/path_parser/parser_extension.rb
94
+ - lib/oven/path_parser/path_ast.rb
95
+ - lib/oven/path_parser/scanner.rb
96
+ - lib/oven/refinements.rb
97
+ - lib/oven/templates/client.rb.erb
98
+ - lib/oven/templates/exceptions.rb.erb
99
+ - lib/oven/templates/json.rb.erb
100
+ - lib/oven/templates/models.rb.erb
101
+ - lib/oven/templates/object_mapping.rb.erb
102
+ - lib/oven/templates/poro.rb.erb
103
+ - lib/oven/version.rb
104
+ - oven.gemspec
105
+ - tmp/.gitkeep
106
+ homepage: https://github.com/yuki24/oven
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">"
122
+ - !ruby/object:Gem::Version
123
+ version: 1.3.1
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.6.8
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Static API client generator
130
+ test_files: []