olive_branch 2.1.2 → 4.0.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
- SHA1:
3
- metadata.gz: af62cb65fbea81bf6ad9a11e59412a2b8a834c94
4
- data.tar.gz: 716f051af8d506fb0ebd1d9091c532654209baf6
2
+ SHA256:
3
+ metadata.gz: 3bec73b70112a823836210ff185bcf853dfa2d7417be60ddab5e9d2e37d59f37
4
+ data.tar.gz: 8c494c373619e9543d0ccb28bbafe3664edc32d3a975a220c052a9422fce916e
5
5
  SHA512:
6
- metadata.gz: 833e3fb79c0f420ab19feb5b58b5b696671e755ced9d1c52556be334e4f2f0a1345118cede1fb18f1cd77a3c75e9b2a1a097b246362b996d6335649e4ee105a3
7
- data.tar.gz: 775099adb481be8ab774b68e2a8eb1abeb01e7084304d879df953d6616d637701e35f1b15afa3b96ceb3085531f24630041ddf84f4ed11de963367f6ddbb7535
6
+ metadata.gz: 2ca6f8224a09fa9113a6e6c0caa45cb928f2eba5ed2538a06e2726d931892af3a31e31c1ca7fb90cec95e7392a5a3cc9a21b8be9427f0af4b50ee3f73672bc86
7
+ data.tar.gz: b949f436070efe153eb34c11b9677a0f86b8999406630753cc726de5387d5d266b856afd30c028e24df733c93669a673d4919ca922c91517cc9f9bca8f31c628
data/README.md CHANGED
@@ -9,22 +9,38 @@ This gem lets your API users pass in and receive camelCased or dash-cased keys,
9
9
 
10
10
  1. Add this to your Gemfile and then `bundle install`:
11
11
 
12
- gem "olive_branch"
12
+ ```ruby
13
+ gem "olive_branch"
14
+ ```
13
15
 
14
- 2. Add this to `config/applcation.rb`:
16
+ 2. Add this to `config/applcation.rb` if you want the clients to control the transformation behaviour through the `Key-Inflection` HTTP header sent by the client:
15
17
 
16
- config.middleware.use OliveBranch::Middleware
18
+ ```ruby
19
+ config.middleware.use OliveBranch::Middleware
20
+ ```
17
21
 
18
- ## Use
22
+ Alternative, if you want to always convert between snake_case and camelCase for your API and only your API, to keep Rubyist and JavaScript developer's happy, use the following configuration:
19
23
 
20
- Include a `X-Key-Inflection` header with values of `camel`, `dash`, or `snake` in your JSON API requests.
24
+ ```ruby
25
+ excluded_routes = ->(env) { !env["PATH_INFO"].match(%r{^/api}) }
26
+ config.middleware.use OliveBranch::Middleware,
27
+ inflection: "camel",
28
+ exclude_params: excluded_routes,
29
+ exclude_response: excluded_routes
30
+ ```
21
31
 
32
+ in your `config/application.rb`.
33
+
34
+ ## Use
35
+
36
+ Include a `Key-Inflection` header with values of `camel`, `dash`, `snake` or `pascal` in your JSON API requests.
22
37
 
23
38
  For more examples, see [our blog post](https://www.viget.com/articles/introducing-olivebranch).
24
39
 
25
40
  ## Optimizations and configuration
26
41
 
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.
42
+ `OliveBranch` uses `multi_json`, which will automatically choose the fastest available JSON parsing library present in your application.
43
+ Most Ruby applications default to using the JSON library that ships with Ruby. However, by including a coder that `multi_json` considers faster, like [Oj](https://github.com/ohler55/oj) in your gemfile, you can potentially save up to ~20% response time.
28
44
 
29
45
  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
46
 
@@ -42,14 +58,19 @@ end
42
58
 
43
59
  ...
44
60
 
45
- config.middleware.use OliveBranch::Middleware, camelize: FastCamel.method(:camelize)
61
+ config.middleware.use OliveBranch::Middleware, camelize: FastCamel.method(:camelize)
62
+ ```
63
+
64
+ Default inflection header key can be changed like
46
65
 
66
+ ```ruby
67
+ config.middleware.use OliveBranch::Middleware, inflection_header: 'Inflect-With'
47
68
  ```
48
69
 
49
- A default inflection can be specified so you don't have to include the `X-Key-Inflection` header on every request.
70
+ A default inflection can be specified so you don't have to include the `Key-Inflection` header on every request. If you opt for default inflection, you may want to exclude the routes that Rails uses (see Filtering).
50
71
 
51
72
  ```ruby
52
- config.middleware.use OliveBranch::Middleware, inflection: 'camel'
73
+ config.middleware.use OliveBranch::Middleware, inflection: 'camel'
53
74
  ```
54
75
 
55
76
  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.
@@ -61,7 +82,9 @@ A benchmark of this compared to the standard implementation shows a saving of ~7
61
82
  It is also possible to include a custom content type check in the same manner
62
83
 
63
84
  ```ruby
64
- config.middleware.use OliveBranch::Middleware, content_type_check: -> (content_type) { content_type == "my/content-type" }
85
+ config.middleware.use OliveBranch::Middleware, content_type_check: -> (content_type) {
86
+ content_type == "my/content-type"
87
+ }
65
88
  ```
66
89
 
67
90
  #### Excluding URLs
@@ -71,13 +94,41 @@ Additionally you can define a custom check by passing a proc
71
94
  For params transforming
72
95
 
73
96
  ```ruby
74
- config.middleware.use OliveBranch::Middleware, exclude_params: -> (env) { env['PATH_INFO'].match(/^\/do_not_transform/) }
97
+ config.middleware.use OliveBranch::Middleware, exclude_params: -> (env) {
98
+ env['PATH_INFO'].match(/^\/do_not_transform/)
99
+ }
75
100
  ```
76
101
 
77
102
  Or response transforming
78
103
 
79
104
  ```ruby
80
- config.middleware.use OliveBranch::Middleware, exclude_response: -> (env) { env['PATH_INFO'].match(/^\/do_not_transform/) }
105
+ config.middleware.use OliveBranch::Middleware, exclude_response: -> (env) {
106
+ env['PATH_INFO'].match(/^\/do_not_transform/)
107
+ }
108
+ ```
109
+
110
+ #### Rails routes & Action Text
111
+
112
+ If you're using default inflection, exclude the routes that Rails uses
113
+ ```ruby
114
+ rails_routes = -> (env) { env['PATH_INFO'].match(/^\/rails/) }
115
+ config.middleware.use OliveBranch::Middleware, inflection: "camel", exclude_params: rails_routes, exclude_response: rails_routes
116
+ ```
117
+
118
+ ## Upgrading to version 3
119
+
120
+ Default inflection header changed from `X-Key-Inflection` to `Key-Inflection`.
121
+
122
+ ## Troubleshooting
123
+
124
+ We've seen folks raise issues that inbound transformations are not taking place. This is often due to the fact that OliveBranch, by default, is only transforming keys when a request's Content-Type is `application/json`.
125
+
126
+ Note that your HTTP client library may suppress even a manually specified `Content-Type` header if the request body is empty (e.g. [Axios does this](https://github.com/axios/axios/issues/86)). This is a common gotcha for GET requests, the body of which are [often expected to be empty](https://stackoverflow.com/questions/978061/http-get-with-request-body) for reasons of caching. If you're seeing the middleware perform on POST or PATCH requests, but not GET requests, this may be your issue.
127
+
128
+ You may choose to force inbound transformation on every request by overriding the `content_type_check` functionality:
129
+
130
+ ```ruby
131
+ config.middleware.use OliveBranch::Middleware, content_type_check: -> (content_type) { true }
81
132
  ```
82
133
 
83
134
  * * *
@@ -3,7 +3,7 @@ require "multi_json"
3
3
  module OliveBranch
4
4
  class Checks
5
5
  def self.content_type_check(content_type)
6
- content_type =~ /application\/json/
6
+ content_type =~ /application\/json/ || content_type =~ /application\/vnd\.api\+json/
7
7
  end
8
8
 
9
9
  def self.default_exclude(env)
@@ -22,6 +22,10 @@ module OliveBranch
22
22
  end
23
23
  end
24
24
 
25
+ def pascalize(string)
26
+ string.underscore.camelize(:upper)
27
+ end
28
+
25
29
  def camelize(string)
26
30
  string.underscore.camelize(:lower)
27
31
  end
@@ -46,30 +50,37 @@ module OliveBranch
46
50
  @app = app
47
51
  @camelize = args[:camelize] || Transformations.method(:camelize)
48
52
  @dasherize = args[:dasherize] || Transformations.method(:dasherize)
53
+ @pascalize = args[:pascalize] || Transformations.method(:pascalize)
49
54
  @content_type_check = args[:content_type_check] || Checks.method(:content_type_check)
50
55
  @exclude_response = args[:exclude_response] || Checks.method(:default_exclude)
51
56
  @exclude_params = args[:exclude_params] || Checks.method(:default_exclude)
52
57
  @default_inflection = args[:inflection]
58
+ @inflection_header = args.fetch(:inflection_header, 'Key-Inflection').gsub(/[^a-z0-9]/i, '_').upcase
59
+ @inflection_header = "HTTP_#{@inflection_header}" unless @inflection_header.start_with?('HTTP_')
53
60
  end
54
61
 
55
62
  def call(env)
56
63
  Transformations.underscore_params(env) unless exclude_params?(env)
64
+ status, headers, response = @app.call(env)
57
65
 
58
- @app.call(env).tap do |_status, headers, response|
59
- next if exclude_response?(env, headers)
60
-
61
- response.each do |body|
62
- begin
63
- new_response = MultiJson.load(body)
64
- rescue MultiJson::ParseError
65
- next
66
- end
66
+ return [status, headers, response] if exclude_response?(env, headers)
67
67
 
68
- Transformations.transform(new_response, inflection_method(env))
68
+ new_responses = []
69
69
 
70
- body.replace(MultiJson.dump(new_response))
70
+ response.each do |body|
71
+ begin
72
+ new_response = MultiJson.load(body)
73
+ rescue MultiJson::ParseError
74
+ new_responses << body
75
+ next
71
76
  end
77
+
78
+ Transformations.transform(new_response, inflection_method(env))
79
+
80
+ new_responses << MultiJson.dump(new_response)
72
81
  end
82
+
83
+ [status, headers, new_responses]
73
84
  end
74
85
 
75
86
  private
@@ -91,7 +102,7 @@ module OliveBranch
91
102
  end
92
103
 
93
104
  def inflection_type(env)
94
- env["HTTP_X_KEY_INFLECTION"] || @default_inflection
105
+ env[@inflection_header] || @default_inflection
95
106
  end
96
107
 
97
108
  def inflection_method(env)
@@ -101,6 +112,8 @@ module OliveBranch
101
112
  @camelize
102
113
  elsif inflection == "dash"
103
114
  @dasherize
115
+ elsif inflection == 'pascal'
116
+ @pascalize
104
117
  else
105
118
  # probably misconfigured, do nothing
106
119
  -> (string) { string }
@@ -1,3 +1,3 @@
1
1
  module OliveBranch
2
- VERSION = '2.1.2'
2
+ VERSION = '4.0.0'
3
3
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: olive_branch
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eli Fatsi
8
8
  - David Eisinger
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-01-29 00:00:00.000000000 Z
12
+ date: 2021-04-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -39,20 +39,6 @@ dependencies:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
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'
56
42
  - !ruby/object:Gem::Dependency
57
43
  name: rspec
58
44
  requirement: !ruby/object:Gem::Requirement
@@ -95,20 +81,6 @@ dependencies:
95
81
  - - ">="
96
82
  - !ruby/object:Gem::Version
97
83
  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'
112
84
  description: Handle camel/snake/dash case conversion
113
85
  email:
114
86
  - eli.fatsi@viget.com
@@ -126,7 +98,7 @@ homepage: https://github.com/vigetlabs/olive_branch
126
98
  licenses:
127
99
  - MIT
128
100
  metadata: {}
129
- post_install_message:
101
+ post_install_message:
130
102
  rdoc_options: []
131
103
  require_paths:
132
104
  - lib
@@ -141,9 +113,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
113
  - !ruby/object:Gem::Version
142
114
  version: '0'
143
115
  requirements: []
144
- rubyforge_project:
145
- rubygems_version: 2.5.1
146
- signing_key:
116
+ rubygems_version: 3.0.1
117
+ signing_key:
147
118
  specification_version: 4
148
119
  summary: Handle camel/snake/dash case conversion
149
120
  test_files: []