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

Return nil when comparing a Post with a non-Post. #3484

Closed

Conversation

justinweiss
Copy link
Contributor

In Ruby < 2.2, == will swallow any exceptions raised by <=> and
return nil.

In Ruby > 2.2, == will surface any exceptions raised by <=>.

In Ruby 2.2, == will show an annoying warning message if <=> raises
an exception, which drowns out most of Jekyll's output.

This patch double-checks that the date and slug methods exist on
whatever you're comparing a Post to, and returns nil if they
don't. This keeps compatibility with Ruby 2.2+.

The Ruby change was discussed here:
https://bugs.ruby-lang.org/issues/7688, and this patch was inspired by
the one @tenderlove made to rdoc here:
https://github.com/rdoc/rdoc/commit/23c276de

Why LSI has nil documents it's comparing posts to is beyond me, but this
patch will do the right thing when it does!

In Ruby < 2.2, `==` will swallow any exceptions raised by `<=>` and
return nil.

In Ruby > 2.2, `==` will surface any exceptions raised by `<=>`.

In Ruby 2.2, `==` will show an annoying warning message if `<=>` raises
an exception, which drowns out most of Jekyll's output.

This patch double-checks that the `date` and `slug` methods exist on
whatever you're comparing a `Post` to, and returns `nil` if they
don't. This keeps compatibility with Ruby 2.2+.
@parkr
Copy link
Member

parkr commented Feb 20, 2015

I'm cool with this. How does this affect sorting if a nil item is in the array? A test case would be nice for that.

@mattr- ?

@@ -155,6 +155,9 @@ def relative_path
#
# Returns -1, 0, 1
def <=>(other)
return unless other.respond_to?(:date) &&
Copy link
Member

Choose a reason for hiding this comment

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

Can we rescue the exception instead?

def <=>(other)
  # existing code
rescue
  nil
end

Copy link
Member

Choose a reason for hiding this comment

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

👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking into it further, it seems like there's a bug that was being masked in earlier versions of Ruby. Could you take a look at the pull request conversation? I have a fix, I'm just not sure how to put together a test for it.

@mattr-
Copy link
Member

mattr- commented Feb 20, 2015

If we want to make sure we don't affect sorting, then we'd want to compact any arrays that have nil in them.

[41] pry(main)> ['a', 'b', nil, 'c'].sort
ArgumentError: comparison of String with nil failed
from (pry):40:in `sort'
[42] pry(main)> ['a', 'b', nil, 'c'].compact.sort
=> ["a", "b", "c"]

Granted, the example above is really contrived, but ¯_(ツ)_/¯

@justinweiss
Copy link
Contributor Author

So, after sleeping on it, I think this is the wrong solution -- it's hiding something that might actually be a bug. So I changed the line that threw the warning (from the LSI gem):

proximity_array_for_content( doc, &block ).reject { |pair| pair[0] == doc }

to

proximity_array_for_content( doc, &block ).reject { |pair| (pair[0] <=> doc) == 0 }

to show the actual error. And it turns out it's not comparing to nil. It looks like it's trying to compare a Post object to a string (Post content).

I can try to take another stab at this and figure out why. But I'd appreciate any insight if you have it!

@justinweiss
Copy link
Contributor Author

Ah, this might be why. Jekyll is indexing posts:

        site.posts.each do |x|
          lsi.add_item(x)
        end

But finding related posts based on content:

    def lsi_related_posts
      self.class.lsi.find_related(post.content, 11) - [post]
    end

@mattr-
Copy link
Member

mattr- commented Feb 20, 2015

well, that definitely seems like a source of issues. 😃 What happens if you change it to find related posts based on the post object itself?

@justinweiss
Copy link
Contributor Author

Changing lsi_related_posts to

def lsi_related_posts
  self.class.lsi.find_related(post, 11)
end

doesn't seem to cause any problems. It's still excluding the current post from the related posts, and the related posts seem as reasonable as a magical black-box classifier can give you.

What kind of test would you want to see for a change like this? I could mock it, but that seems like it might be too implementation-focused.

@justinweiss
Copy link
Contributor Author

Two posts later, things still seem to be working well with that lsi_related_posts fix. Do you have any suggestions for tests I could write? I'd be happy to close this pull request and start another with the new code.

justinweiss added a commit to justinweiss/jekyll that referenced this pull request Mar 30, 2015
When looking for related posts, Jekyll was indexing `Jekyll::Post`
objects, but finding related posts based on `Jekyll::Post#content`. This
caused two problems:

1. Ruby 2.2 will warn on == if <=> throws an exception (and future Ruby
versions will surface that exception). Because `String`s can't be
compared with `Jekyll::Post`s, this warning was appearing all the time
while searching for related posts.

2. LSI won't return a post itself when searching for related posts. But
LSI could never tell that we were searching on a post, since Jekyll
passed post content, not a post object. With this fix, we can remove the
`- [post]` from `Jekyll::RelatedPosts#find_related`.

This is a more accurate fix for jekyll#3484.
@justinweiss
Copy link
Contributor Author

This PR is superseded by #3629.

@jekyll jekyll locked and limited conversation to collaborators Feb 27, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants