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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b54d1b0e0db59e223cf8720ae8b1c886c8f687b6
4
- data.tar.gz: 77114b9066d6498f3497c46c684d1b86fc406f77
3
+ metadata.gz: 7b70147d89c1ed39a3d2622ace5dd0465cf0e859
4
+ data.tar.gz: 8ddd953cdb4b3d83dcc8f76181c021d527dbd4f9
5
5
  SHA512:
6
- metadata.gz: 6d226a63c03330548194f7f0783873ea1b4468084f8655a629b36dc786b2a6f447cef0b56d6c3121c61ee62271de049c8846bcc406ab5523f42d62d37288d272
7
- data.tar.gz: 92c3514964ad7b19fd31f0b5bef62dddb4f45a69face9446bb758c3ea0612cace7e466b3481736f1a4b635d06f63c894a1c6d0d1843050d88170f9114783ea84
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"] =~ /application\/json/
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"] =~ /application\/json/
56
+ next unless inflection && @content_type_check.call(headers["Content-Type"])
16
57
  response.each do |body|
17
58
  begin
18
- new_response = JSON.parse(body)
19
- rescue JSON::ParserError
59
+ new_response = MultiJson.load(body)
60
+ rescue MultiJson::ParseError
20
61
  next
21
62
  end
22
63
 
23
- if inflection == "camel"
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.to_json)
66
+ body.replace(MultiJson.dump(new_response))
38
67
  end
39
68
  end
40
69
  end
41
70
 
42
- def underscore_params(env)
43
- req = ActionDispatch::Request.new(env)
44
- req.request_parameters
45
- req.query_parameters
71
+ private
46
72
 
47
- env["action_dispatch.request.request_parameters"].deep_transform_keys!(&:underscore)
48
- env["action_dispatch.request.query_parameters"].deep_transform_keys!(&:underscore)
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
@@ -1,3 +1,3 @@
1
1
  module OliveBranch
2
- VERSION = '2.0.0'
2
+ VERSION = '2.1.0'
3
3
  end
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.0.0
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-07 00:00:00.000000000 Z
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: '3.2'
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: '3.2'
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