kjson-roda 0.1.0

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: aecd0fd2eb4fa562138cf9932ea18dcd54c8a5f9
4
+ data.tar.gz: 2a1baf15dabf93d3ccbee63d2cf37eb48b6e29a4
5
+ SHA512:
6
+ metadata.gz: 30a2e3d70de27a1820d343f9bddc2905e1ce306a430a0483d3d193e7d9ccc9e6de7c0aa542ebf08dff00dda24b3f90c4c621af4dc030c658893cb30df9537395
7
+ data.tar.gz: 4c9feb1d648ad101cdc61bf0cbb3bf445c0ce613364804cb90507729f03616d30781ea909beabeab4b19186f265f471df4b6e563a52422c4b0771c3a6dfef2e3
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,59 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'spec/**/*'
4
+
5
+ Metrics/AbcSize:
6
+ # Make it large for now
7
+ Max: 40
8
+ Metrics/LineLength:
9
+ Enabled: false
10
+ Metrics/MethodLength:
11
+ # Make it large for now
12
+ Max: 20
13
+
14
+ Style/Alias:
15
+ EnforcedStyle: prefer_alias_method
16
+ Style/AlignParameters:
17
+ EnforcedStyle: with_fixed_indentation
18
+ Style/BlockDelimiters:
19
+ EnforcedStyle: semantic
20
+ IgnoredMethods:
21
+ - lambda
22
+ - proc
23
+ - it
24
+ - Form
25
+ - Schema
26
+ Style/Documentation:
27
+ Enabled: false
28
+ Style/DoubleNegation:
29
+ Enabled: false
30
+ Style/FrozenStringLiteralComment:
31
+ Enabled: false
32
+ Style/IfUnlessModifier:
33
+ Enabled: false
34
+ Style/IndentationWidth:
35
+ Enabled: false
36
+ Style/Lambda:
37
+ Enabled: false
38
+ Style/LambdaCall:
39
+ Enabled: false
40
+ Style/MethodName:
41
+ Enabled: false
42
+ Style/PercentLiteralDelimiters:
43
+ Enabled: false
44
+ Style/RegexpLiteral:
45
+ EnforcedStyle: mixed
46
+ Style/SpaceInsideBlockBraces:
47
+ EnforcedStyle: space
48
+ Style/SpaceInsideHashLiteralBraces:
49
+ EnforcedStyle: no_space
50
+ Style/SpecialGlobalVars:
51
+ Enabled: false
52
+ Style/StabbyLambdaParentheses:
53
+ EnforcedStyle: require_no_parentheses
54
+ Style/StringLiterals:
55
+ EnforcedStyle: double_quotes
56
+ Style/TrailingCommaInArguments:
57
+ Enabled: false
58
+ Style/TrailingCommaInLiteral:
59
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kjson-roda.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem "pry"
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Maksim V
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # Kjson
2
+
3
+ # Usage
4
+
5
+ ```ruby
6
+ require 'roda'
7
+
8
+ class App < Roda
9
+ route do |r|
10
+ r.plugin :kjson
11
+
12
+ r.api do
13
+ r.endpoint "user" do
14
+ r.endpoint "new" do
15
+ r.success {"name": "Emmy"}
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ run App.freeze.new
23
+ ```
24
+
25
+ # Create a successful response
26
+
27
+ ```ruby
28
+ r.api do
29
+ r.success
30
+ end
31
+ ```
32
+
33
+ An alternative way is,
34
+
35
+ ```ruby
36
+ class AlwaysSuccessService
37
+ def call
38
+ throw :success
39
+ end
40
+ end
41
+
42
+ r.api do
43
+ r.endpoint "always_success" do
44
+ service = AlwaysSuccessService.new
45
+ service.()
46
+ end
47
+ end
48
+ ```
49
+
50
+ It will generate,
51
+
52
+ ```json
53
+ {
54
+ "data": null
55
+ "error": null
56
+ }
57
+ ```
58
+
59
+ # Fill data in response
60
+
61
+ ```ruby
62
+ r.api do
63
+ r.success data
64
+ end
65
+ ```
66
+
67
+ Or,
68
+
69
+ ```ruby
70
+ r.api do
71
+ throw :success, data
72
+ end
73
+ ```
74
+
75
+ # A complex endpoint
76
+
77
+ For request {"endpoint":"part.value","data":null},
78
+
79
+ ```ruby
80
+ r.api do
81
+ r.endpoint "part" do
82
+ puts r.endpoint # "value"
83
+ end
84
+ end
85
+ ```
86
+
87
+ # An arbitrary exception
88
+
89
+ ```ruby
90
+ r.api do
91
+ raise "Something went wrong."
92
+ end
93
+ ```
94
+
95
+ Response will look as:
96
+
97
+ ```json
98
+ {
99
+ "data": null,
100
+ "error": {
101
+ "type": "SERVICE_ERROR",
102
+ "message": "INTERNAL",
103
+ "cause": {
104
+ "message": "Something went wrong."
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ # A service exception
111
+
112
+ ```ruby
113
+ r.api do
114
+ throw :error, "RESOURCE_EXHAUSTED"
115
+ end
116
+ ```
117
+
118
+ Response will look as:
119
+
120
+ ```json
121
+ {
122
+ "data": null,
123
+ "error": {
124
+ "type": "SERVICE_ERROR",
125
+ "message": "RESOURCE_EXHAUSTED"
126
+ }
127
+ }
128
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "kjson-roda"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require "pry"
10
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "kjson/roda/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kjson-roda"
8
+ spec.version = Kjson::Roda::VERSION
9
+ spec.authors = ["Maksim V."]
10
+ spec.email = ["inre.storm@gmail.com"]
11
+
12
+ spec.summary = "KJson Roda plugin"
13
+ # spec.description = %q{TODO: Write a longer description or delete this line.}
14
+ spec.homepage = "https://github.com/inre"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "oj", "~> 2"
24
+ spec.add_dependency "roda", "~> 2"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.14"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ end
data/lib/kjson-roda.rb ADDED
@@ -0,0 +1 @@
1
+ require "kjson/roda"
data/lib/kjson/roda.rb ADDED
@@ -0,0 +1,186 @@
1
+ require "oj"
2
+ require "roda"
3
+
4
+ module Kjson
5
+ module Roda
6
+ OPTS = {}.freeze
7
+ JSON_PARAMS_KEY = "roda.json_params".freeze
8
+ INPUT_KEY = "rack.input".freeze
9
+ CONTENT_TYPE_MATCH = /json/
10
+ ENDPOINT_SEPARATOR = "."
11
+ JSON_ENCODER = Oj.method(:dump)
12
+ JSON_DECODER = Oj.method(:load)
13
+ JSON_OPTIONS = {
14
+ mode: :strict,
15
+ quirks_mode: false
16
+ }.freeze
17
+ KJSON_REQUEST_KEY = "kjson.request".freeze
18
+ KJSON_RESPONSE_KEY = "kjson.response".freeze
19
+
20
+ REQUEST_ERROR = "REQUEST_ERROR".freeze
21
+ SERVICE_ERROR = "SERVICE_ERROR".freeze
22
+ RESPONSE_ERROR = "RESPONSE_ERROR".freeze
23
+
24
+ TIMEOUT = "TIMEOUT".freeze
25
+ CLOSED = "CLOSED".freeze
26
+ TRANSPORT = "TRANSPORT".freeze
27
+ UNAVAILABLE = "UNAVAILABLE".freeze
28
+ BAD_REQUEST = "BAD_REQUEST".freeze
29
+ INVALID_FORMAT = "INVALID_FORMAT".freeze
30
+ UNIMPLEMENTED = "UNIMPLEMENTED".freeze
31
+
32
+ UNAUTHENTICATED = "UNAUTHENTICATED".freeze
33
+ NO_DATA = "NO_DATA".freeze
34
+
35
+ INTERNAL = "INTERNAL".freeze
36
+ PARSE_ERROR_HANDLER = proc { |r| r.halt [400, {}, [Oj.dump({ data: nil, error: { type: REQUEST_ERROR, message: UNIMPLEMENTED } }, JSON_OPTIONS).freeze]] }
37
+
38
+ # Handle options for the kjson plugin:
39
+ def self.configure(app, opts = OPTS)
40
+ app.opts[:kjson_error_handler] = opts[:error_handler] || app.opts[:kjson_error_handler] || PARSE_ERROR_HANDLER
41
+ app.opts[:kjson_content_type] = opts[:content_type] || app.opts[:kjson_content_type] || CONTENT_TYPE_MATCH
42
+ app.opts[:kjson_encoder] = opts[:json_encoder] || app.opts[:kjson_json_encoder] || JSON_ENCODER
43
+ app.opts[:kjson_decoder] = opts[:json_decoder] || app.opts[:kjson_json_decoder] || JSON_DECODER
44
+ app.opts[:kjson_options] = opts[:json_options] || app.opts[:kjson_json_options] || JSON_OPTIONS
45
+ end
46
+
47
+ module RequestMethods
48
+
49
+ def api(&_b)
50
+ return unless kjson?
51
+ catch_error do
52
+ catch_success do
53
+ kjson_parse
54
+ kjson_api
55
+ yield
56
+ throw :error, [UNIMPLEMENTED, REQUEST_ERROR]
57
+ end
58
+ end
59
+ end
60
+
61
+ def endpoint(path=nil, &_b)
62
+ return @remaining_endpoints.join(ENDPOINT_SEPARATOR) unless path
63
+ path = path.to_s.split(ENDPOINT_SEPARATOR)
64
+ index = @remaining_endpoints.zip(path).index{|z| z.first == z.last}
65
+ if index && index == path.size - 1
66
+ stored_endpoints = @remaining_endpoints
67
+ @remaining_endpoints = @remaining_endpoints.drop(path.size)
68
+ if @remaining_endpoints.empty?
69
+ block_result(yield)
70
+ throw :error, [NO_DATA, RESPONSE_ERROR] if @remaining_endpoints.empty?
71
+ else
72
+ yield
73
+ end
74
+ @remaining_endpoints = stored_endpoints
75
+ end
76
+ end
77
+
78
+ def success(data=nil)
79
+ throw :success, data
80
+ end
81
+
82
+ def error(msg=INTERNAL)
83
+ throw :error, msg
84
+ end
85
+
86
+ def auth
87
+ env[KJSON_REQUEST_KEY]["auth"]
88
+ end
89
+
90
+ def data
91
+ env[KJSON_REQUEST_KEY]["data"]
92
+ end
93
+
94
+ def kjson?
95
+ post? && content_type =~ roda_class.opts[:kjson_content_type]
96
+ end
97
+
98
+ private
99
+
100
+ def kjson_parse
101
+ input = env[INPUT_KEY]
102
+ str = input.read
103
+ input.rewind
104
+ return super if str.empty?
105
+ env[KJSON_REQUEST_KEY] = kjson_decode(str)
106
+ rescue
107
+ roda_class.opts[:kjson_error_handler].call(self)
108
+ end
109
+
110
+ def kjson_api
111
+ kjson_request = env[KJSON_REQUEST_KEY]
112
+ throw :error, [BAD_REQUEST, REQUEST_ERROR] unless kjson_request["endpoint"].is_a?(String)
113
+ @remaining_endpoints = kjson_request["endpoint"].split(ENDPOINT_SEPARATOR)
114
+ end
115
+
116
+ def catch_success(&_b)
117
+ data = catch(:success) {
118
+ yield
119
+ }
120
+ response.data(data)
121
+ throw :halt, response.finish
122
+ end
123
+
124
+ def catch_error(&_b)
125
+ error = catch(:error) {
126
+ begin
127
+ yield
128
+ rescue
129
+ [INTERNAL, SERVICE_ERROR, $!]
130
+ end
131
+ }
132
+ error = [error] unless error.is_a?(Array)
133
+ response.error(*error)
134
+ throw :halt, response.finish
135
+ end
136
+
137
+ def kjson_decode(str)
138
+ roda_class.opts[:kjson_decoder].call(str, roda_class.opts[:kjson_options])
139
+ end
140
+ end
141
+
142
+ module ResponseMethods
143
+ def data(dt)
144
+ @kjson_response = true
145
+ @data = dt
146
+ nil
147
+ end
148
+
149
+ def error(message, type=SERVICE_ERROR, cause=nil)
150
+ @kjson_response = true
151
+ @error = {
152
+ "type" => type,
153
+ "message" => message
154
+ }
155
+ case cause
156
+ when Exception
157
+ @error["cause"] = {
158
+ "message" => cause.message,
159
+ "backtrace" => cause.backtrace
160
+ }
161
+ when Hash
162
+ @error["cause"] = cause
163
+ end
164
+ nil
165
+ end
166
+
167
+ def finish
168
+ if @kjson_response
169
+ @body = [kjson_encode(
170
+ "data" => @data,
171
+ "error" => @error
172
+ )]
173
+ end
174
+ super
175
+ end
176
+
177
+ private
178
+
179
+ def kjson_encode(obj)
180
+ roda_class.opts[:kjson_encoder].call(obj, roda_class.opts[:kjson_options])
181
+ end
182
+ end
183
+ end
184
+ end
185
+
186
+ Roda::RodaPlugins.register_plugin(:kjson, Kjson::Roda)
@@ -0,0 +1,5 @@
1
+ module Kjson
2
+ module Roda
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kjson-roda
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Maksim V.
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-03-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: oj
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: roda
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.14'
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: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description:
84
+ email:
85
+ - inre.storm@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".rubocop.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - kjson-roda.gemspec
100
+ - lib/kjson-roda.rb
101
+ - lib/kjson/roda.rb
102
+ - lib/kjson/roda/version.rb
103
+ homepage: https://github.com/inre
104
+ licenses: []
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.6.8
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: KJson Roda plugin
126
+ test_files: []