olive_branch 2.0.0 → 2.1.0
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 +4 -4
- data/README.md +32 -0
- data/lib/olive_branch/middleware.rb +59 -26
- data/lib/olive_branch/version.rb +1 -1
- metadata +74 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b70147d89c1ed39a3d2622ace5dd0465cf0e859
|
4
|
+
data.tar.gz: 8ddd953cdb4b3d83dcc8f76181c021d527dbd4f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5221720bceac61c7ab85c82723daf410edde7074c75727796caab89fb4df6af501bbaad7db6e78cb15a6afda97f2be33d8d93edc19fb6951dc104f0db4d036a8
|
7
|
+
data.tar.gz: ebac93fa3b2b241525b270e6512b3b9acfaa78e6551718dd6f9cf756a34d7bdce982457a1d34684c3ecc94eced0a0cade3e33c85b8ff79532dfdf1200f30e877
|
data/README.md
CHANGED
@@ -19,8 +19,40 @@ This gem lets your API users pass in and receive camelCased or dash-cased keys,
|
|
19
19
|
|
20
20
|
Include a `X-Key-Inflection` header with values of `camel`, `dash`, or `snake` in your JSON API requests.
|
21
21
|
|
22
|
+
|
22
23
|
For more examples, see [our blog post](https://www.viget.com/articles/introducing-olivebranch).
|
23
24
|
|
25
|
+
## Optimizations and configuration
|
26
|
+
|
27
|
+
`OliveBranch` uses `multi_json`, which will choose the fastest available JSON parsing library and use that. Combined with `Oj` can speed things up and save ~20% rails response time.
|
28
|
+
|
29
|
+
The middleware can be initialized with custom camelize/dasherize implementations, so if you know you have a fixed size set of keys, you can save a considerable amount of time by providing a custom camelize that caches like so:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class FastCamel
|
33
|
+
def self.camel_cache
|
34
|
+
@camel_cache ||= {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.camelize(string)
|
38
|
+
camel_cache[string] ||= string.underscore.camelize(:lower)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
...
|
44
|
+
|
45
|
+
config.middleware.use OliveBranch::Middleware, camelize: FastCamel.method(:camelize)
|
46
|
+
|
47
|
+
```
|
48
|
+
|
49
|
+
A benchmark of this compared to the standard implementation shows a saving of ~75% rails response times for a complex response payload, or a ~400% improvement, but there is a risk of memory usage ballooning if you have dynamic keys. You can make this method as complex as required, but keep in mind that it will end up being called a _lot_ in a busy app, so it's worth thinking about how to do what you need in the fastest manner possible.
|
50
|
+
|
51
|
+
It is also possible to include a custom content type check in the same manner
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
config.middleware.use OliveBranch::Middleware, content_type_check: -> (content_type) { content_type == "my/content-type" }
|
55
|
+
```
|
24
56
|
|
25
57
|
* * *
|
26
58
|
|
@@ -1,51 +1,84 @@
|
|
1
|
+
require "multi_json"
|
2
|
+
|
1
3
|
module OliveBranch
|
4
|
+
class Checks
|
5
|
+
def self.content_type_check(content_type)
|
6
|
+
content_type =~ /application\/json/
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Transformations
|
11
|
+
class << self
|
12
|
+
def transform(value, transform_method)
|
13
|
+
case value
|
14
|
+
when Array then value.map { |item| transform(item, transform_method) }
|
15
|
+
when Hash then value.deep_transform_keys! { |key| transform(key, transform_method) }
|
16
|
+
when String then transform_method.call(value)
|
17
|
+
else value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def camelize(string)
|
22
|
+
string.underscore.camelize(:lower)
|
23
|
+
end
|
24
|
+
|
25
|
+
def dasherize(string)
|
26
|
+
string.dasherize
|
27
|
+
end
|
28
|
+
|
29
|
+
def underscore_params(env)
|
30
|
+
req = ActionDispatch::Request.new(env)
|
31
|
+
req.request_parameters
|
32
|
+
req.query_parameters
|
33
|
+
|
34
|
+
env["action_dispatch.request.request_parameters"].deep_transform_keys!(&:underscore)
|
35
|
+
env["action_dispatch.request.query_parameters"].deep_transform_keys!(&:underscore)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
2
40
|
class Middleware
|
3
|
-
def initialize(app)
|
41
|
+
def initialize(app, args = {})
|
4
42
|
@app = app
|
43
|
+
@camelize = args[:camelize] || Transformations.method(:camelize)
|
44
|
+
@dasherize = args[:dasherize] || Transformations.method(:dasherize)
|
45
|
+
@content_type_check = args[:content_type_check] || Checks.method(:content_type_check)
|
5
46
|
end
|
6
47
|
|
7
48
|
def call(env)
|
8
49
|
inflection = env["HTTP_X_KEY_INFLECTION"]
|
9
50
|
|
10
|
-
if inflection && env["CONTENT_TYPE"]
|
11
|
-
underscore_params(env)
|
51
|
+
if inflection && @content_type_check.call(env["CONTENT_TYPE"])
|
52
|
+
Transformations.underscore_params(env)
|
12
53
|
end
|
13
54
|
|
14
55
|
@app.call(env).tap do |_status, headers, response|
|
15
|
-
next unless inflection && headers["Content-Type"]
|
56
|
+
next unless inflection && @content_type_check.call(headers["Content-Type"])
|
16
57
|
response.each do |body|
|
17
58
|
begin
|
18
|
-
new_response =
|
19
|
-
rescue
|
59
|
+
new_response = MultiJson.load(body)
|
60
|
+
rescue MultiJson::ParseError
|
20
61
|
next
|
21
62
|
end
|
22
63
|
|
23
|
-
|
24
|
-
if new_response.is_a? Array
|
25
|
-
new_response.each { |o| o.deep_transform_keys! { |k| k.underscore.camelize(:lower)} }
|
26
|
-
else
|
27
|
-
new_response.deep_transform_keys! { |k| k.underscore.camelize(:lower) }
|
28
|
-
end
|
29
|
-
elsif inflection == "dash"
|
30
|
-
if new_response.is_a? Array
|
31
|
-
new_response.each { |o| o.deep_transform_keys!(&:dasherize) }
|
32
|
-
else
|
33
|
-
new_response.deep_transform_keys!(&:dasherize)
|
34
|
-
end
|
35
|
-
end
|
64
|
+
Transformations.transform(new_response, inflection_method(inflection))
|
36
65
|
|
37
|
-
body.replace(new_response
|
66
|
+
body.replace(MultiJson.dump(new_response))
|
38
67
|
end
|
39
68
|
end
|
40
69
|
end
|
41
70
|
|
42
|
-
|
43
|
-
req = ActionDispatch::Request.new(env)
|
44
|
-
req.request_parameters
|
45
|
-
req.query_parameters
|
71
|
+
private
|
46
72
|
|
47
|
-
|
48
|
-
|
73
|
+
def inflection_method(inflection)
|
74
|
+
if inflection == "camel"
|
75
|
+
@camelize
|
76
|
+
elsif inflection == "dash"
|
77
|
+
@dasherize
|
78
|
+
else
|
79
|
+
# probably misconfigured, do nothing
|
80
|
+
-> (string) { string }
|
81
|
+
end
|
49
82
|
end
|
50
83
|
end
|
51
84
|
end
|
data/lib/olive_branch/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: olive_branch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eli Fatsi
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-09-
|
12
|
+
date: 2017-09-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -25,20 +25,90 @@ dependencies:
|
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '4.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: multi_json
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: oj
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
28
56
|
- !ruby/object:Gem::Dependency
|
29
57
|
name: rspec
|
30
58
|
requirement: !ruby/object:Gem::Requirement
|
31
59
|
requirements:
|
32
60
|
- - "~>"
|
33
61
|
- !ruby/object:Gem::Version
|
34
|
-
version:
|
62
|
+
version: 3.5.0
|
35
63
|
type: :development
|
36
64
|
prerelease: false
|
37
65
|
version_requirements: !ruby/object:Gem::Requirement
|
38
66
|
requirements:
|
39
67
|
- - "~>"
|
40
68
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
69
|
+
version: 3.5.0
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: appraisal
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec-rails
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: sqlite3
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
42
112
|
description: Handle camel/snake/dash case conversion
|
43
113
|
email:
|
44
114
|
- eli.fatsi@viget.com
|