Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for passing options to JSON.parse #156

Merged
merged 4 commits into from
Jul 27, 2017

Conversation

stefansedich
Copy link
Contributor

This should fix #124 and allow all options to be passed to JSON.parse, it felt best to pass the options to the parse proc instead of overriding the parse method which was my second option and looks to be the way that the multijson middleware does it (which I am also open to doing).

Passing the options to the proc should be a non breaking change as any existing parsing middleware will continue to work as before and if required they will have access to the options argument.

@stefansedich
Copy link
Contributor Author

I notice the build is failing for Ruby 1.8.x, I saw in another issue that it was mentioned in 2016 that Faraday had dropped support for < 1.9 and that the middleware would follow suit, is this still the case?

Copy link
Member

@iMacTia iMacTia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @stefansedich and thanks for your PR.
I think we can achieve the same result in a cleaner and backward compatible way so please address the comments in my review before we proceed 👍

:create_additions => options[:create_additions],
:object_class => options[:object_class],
:array_class => options[:array_class]
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look DRY, it would be better to simply pass options as it is to JSON.parse.

@@ -47,7 +47,7 @@ def process_response(env)
def parse(body)
if self.class.parser
begin
self.class.parser.call(body)
self.class.parser.call(body, @options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than passing the entire @options hash, I would opt for deciding on a specific @options key (we can call it parser_options) to be passed to the parser.
This will make the call like:

self.class.parser.call(body, @options[:parser_options])

IMPORTANT: We should pass this parameter ONLY if it's provided. We might have people defining custom parser that doesn't accept the second parameter. In order to maintain backward compatibility we need to make the second parameter optional

response = process(body)
expect(response.body).to be(result)
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After applying the other 2 changes, this test can be refactored using the new parser_options option.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Um, is 1.8 complaining that the lines aren't:

allow(::JSON).to receive(:parse).with(body, options[:parser_options]) { result }

@stefansedich
Copy link
Contributor Author

Sounds good! Will make the changes today.

@stefansedich
Copy link
Contributor Author

@iMacTia how does that look now?

@iMacTia
Copy link
Member

iMacTia commented Jun 12, 2017

It looks much better now!
My only concern is about that next in the parser. I'd rather rollback to the unless from before as next is usually used in loops and enumerators.

Unless there's any specific reason for using it, of course!

Finally, don't worry about ruby 1.8.7, we're not supporting it anymore on Faraday so we can just do the same here. I need to update travis tests for that though.

@stefansedich
Copy link
Contributor Author

No problems @iMacTia I have rolled that back to what it was too!

@iMacTia
Copy link
Member

iMacTia commented Jun 12, 2017

Great, thanks @stefansedich 👍
Now it's up to me fixing those ruby 1.8.7 test.
I'll try to find some time in the next days

@stefansedich
Copy link
Contributor Author

No problems. if there is anything I can help out with let me know!

@iMacTia iMacTia added this to the 0.12.0 milestone Jun 13, 2017
@stefansedich
Copy link
Contributor Author

stefansedich commented Jul 2, 2017 via email

@iMacTia
Copy link
Member

iMacTia commented Jul 27, 2017

@stefansedich can you please pull/rebase from master?
I've dropped support for ruby < 1.9.3 and that should fix your tests :)
Preparing release 0.12

@stefansedich
Copy link
Contributor Author

@iMacTia done, funny thing is just the other day I was sitting there thinking to myself "I wish the middleware would let me pass in symbolize_names", I completely forgot I even had this PR sitting there 😄

@iMacTia
Copy link
Member

iMacTia commented Jul 27, 2017

Totally my fault there, it took me too much getting on this!
LGTM

@iMacTia iMacTia merged commit 038632a into lostisland:master Jul 27, 2017
@stefansedich
Copy link
Contributor Author

stefansedich commented Jul 27, 2017 via email

@bartoszkopinski
Copy link

bartoszkopinski commented Jul 31, 2017

@iMacTia @stefansedich This change is backwards incompatible.
When parser_options is nil JSON raises an exception - ArgumentError: options must be a hash.
I suggest updating to something like:

::JSON.parse(body, parser_options || {})

@iMacTia
Copy link
Member

iMacTia commented Jul 31, 2017

Mmmmh strange, I'm quite sure we have a good test coverage there.
First things first, I'd like to replicate the issue. @bartoszkopinski do you have an example that shows the backward incompatibility?

@iMacTia
Copy link
Member

iMacTia commented Jul 31, 2017

FYI, I've just tried the following:

conn = Faraday.new('https://jsonplaceholder.typicode.com') do |f|
  f.response :json
  f.adapter :net_http
end

res = conn.get('/posts/1')

and it works perfectly. res.body is correctly parsed as a ruby Hash

@bartoszkopinski
Copy link

bartoszkopinski commented Jul 31, 2017

@iMacTia Sorry, I thought this was something easier to recreate.

Looking at the code, it's possible to invoke the parser block without any options:

Which will then raise the error I mentioned:

::JSON.parse(body, parser_options) unless body.strip.empty?

I noticed it using a class that inherits from Faraday::Middleware:
https://github.com/shutl/shutl_resource/blob/master/lib/shutl/resource/default_logger.rb#L21

Now I'm not exactly sure how to write a standalone failing example yet, but let me know if you'd instead want me to open a separate issue once I have one.

@iMacTia
Copy link
Member

iMacTia commented Jul 31, 2017

Ok, let me complicate this one:

JSON::VERSION
#=> "2.1.0"
raw_body = "{\n  \"userId\": 1,\n  \"id\": 1,\n  \"title\": \"blahblahblah\",\n  \"body\": \"blahblahblah\"\n}"
JSON.parse(raw_body, nil)
#=> {"userId"=>1, "id"=>1, "title"=>"blahblahblah", "body"=>"blahblahblah"}

So it appears that the restriction on the options parameter you're referring to has been removed at some point.
Which version of JSON are you using?

@bartoszkopinski
Copy link

bartoszkopinski commented Jul 31, 2017

@iMacTia Interesting, I'm using Oj:

2.4.0 :002 > require 'json'
 => true
2.4.0 :003 > JSON::VERSION
 => "2.1.0"
2.4.0 :004 > JSON.parse("{}", nil)
 => {}
2.4.0 :005 > require 'oj'
 => true
2.4.0 :006 > Oj::VERSION
 => "3.3.2"
2.4.0 :007 > Oj.mimic_JSON()
 => JSON
2.4.0 :008 > JSON.parse("{}", nil)
ArgumentError: options must be a hash.
  from (irb):15:in `parse'

Didn't expect it to be incompatible with JSON, thanks for pointing it out.

@stefansedich
Copy link
Contributor Author

stefansedich commented Jul 31, 2017 via email

@iMacTia
Copy link
Member

iMacTia commented Jul 31, 2017

Mistery solved!
However I do understand people prefer to use Oj as it offers better performances.
Feel free to open an issue and I'll try to get support back in the next hotfix release 😉

@stefansedich
Copy link
Contributor Author

PR opened @iMacTia but GitHub seems to be having some fun at the moment!

@booleanbetrayal
Copy link

booleanbetrayal commented Jul 31, 2017

Also experiencing the issue @bartoszkopinski mentioned with slack-ruby-client upon upgrading faraday_middleware to latest. Also using OJ gem.

@iMacTia
Copy link
Member

iMacTia commented Jul 31, 2017

@booleanbetrayal I'll try to review @stefansedich PR tomorrow and plan an hotfix release for this week.
However, may I suggest someone opening an issue also on the Oj gem and point out that they're not 100% compatible with the latest JSON version?

@stefansedich
Copy link
Contributor Author

I will open the issue up over on OJ right now @iMacTia

@iMacTia
Copy link
Member

iMacTia commented Aug 1, 2017

Thank you!
For tracking purposes (and future visitors): ohler55/oj#411

@booleanbetrayal
Copy link

LGTM!

@iMacTia
Copy link
Member

iMacTia commented Aug 4, 2017

For future visitors.
The issue with Oj was addressed in Oj v3.3.3.
Moreover, @stefansedich provided a fix in #163 to support Oj < 3.3.3 which was shipped with v 0.12.2

mtyeh411 added a commit to mtyeh411/KahunaClient that referenced this pull request Aug 9, 2017
lostisland/faraday_middleware#156
since there `parser_options` is not defaulted to a Hash, `JSON.parse` bombs
mtyeh411 added a commit to mtyeh411/KahunaClient that referenced this pull request Aug 9, 2017
lostisland/faraday_middleware#156
since there `parser_options` is not defaulted to a Hash, `JSON.parse` bombs
@@ -7,8 +7,8 @@ class ParseJson < ResponseMiddleware
require 'json' unless defined?(::JSON)
end

define_parser do |body|
::JSON.parse body unless body.strip.empty?
define_parser do |body, parser_options|
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since you're introducing an argument and there are other things that rely on this, i think it'd be prudent to default the block argument to an empty hash. otherwise JSON.parse will bomb with an error that options is not a hash.

@stefansedich
Copy link
Contributor Author

@mtyeh411 this has already been dealt with in #163, JSON.parse can handle a nil options but Oj could not, both have been patched now and everything should be happy again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FaradayMiddleware::ParseJson: Pass Options to JSON.parse?
6 participants