<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Joe McCall</title>
    <description>The latest articles on DEV Community by Joe McCall (@joemccall86).</description>
    <link>https://dev.to/joemccall86</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F390551%2F73e27185-4e7e-468a-8c34-dfaecdbecdfc.jpeg</url>
      <title>DEV Community: Joe McCall</title>
      <link>https://dev.to/joemccall86</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joemccall86"/>
    <language>en</language>
    <item>
      <title>Grails 5 no longer runs my tests!</title>
      <dc:creator>Joe McCall</dc:creator>
      <pubDate>Mon, 21 Mar 2022 19:56:05 +0000</pubDate>
      <link>https://dev.to/joemccall86/grails-5-no-longer-runs-my-tests-13ki</link>
      <guid>https://dev.to/joemccall86/grails-5-no-longer-runs-my-tests-13ki</guid>
      <description>&lt;h1&gt;
  
  
  Problem
&lt;/h1&gt;

&lt;p&gt;I just followed the guide to upgrade from Grails 4.0 to Grails 5.1 &lt;a href="http://docs.grails.org/snapshot/guide/single.html#upgrading40x"&gt;here&lt;/a&gt;. I now try to run my unit and integration tests and Gradle is reporting there are no tests to run!&lt;/p&gt;

&lt;h1&gt;
  
  
  Solution
&lt;/h1&gt;

&lt;p&gt;In our case, we need to add a new line in &lt;code&gt;build.gradle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;useJUnitPlatform&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Here&lt;/span&gt;
    &lt;span class="n"&gt;systemProperty&lt;/span&gt; &lt;span class="s2"&gt;"geb.env"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'geb.env'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;systemProperty&lt;/span&gt; &lt;span class="s2"&gt;"geb.build.reportsDir"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reporting&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"geb/integrationTest"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;systemProperty&lt;/span&gt; &lt;span class="s2"&gt;"webdriver.chrome.driver"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'webdriver.chrome.driver'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;systemProperty&lt;/span&gt; &lt;span class="s2"&gt;"webdriver.gecko.driver"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'webdriver.gecko.driver'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Gradle that we're using JUnit-style tests.&lt;/p&gt;

</description>
      <category>grails</category>
      <category>gradle</category>
      <category>groovy</category>
    </item>
    <item>
      <title>"Repository Not Found" when accessing a GitHub remote</title>
      <dc:creator>Joe McCall</dc:creator>
      <pubDate>Wed, 09 Dec 2020 14:55:57 +0000</pubDate>
      <link>https://dev.to/joemccall86/repository-not-found-when-accessing-a-github-remote-i88</link>
      <guid>https://dev.to/joemccall86/repository-not-found-when-accessing-a-github-remote-i88</guid>
      <description>&lt;p&gt;Boy was this frustrating! Basically I have several keys in &lt;code&gt;~/.ssh/&lt;/code&gt; that I use to access various projects. It turned out that one of them was added to GitHub on another account (likely a bot account I had created for another project).&lt;/p&gt;

&lt;p&gt;I would try to run a &lt;code&gt;git pull&lt;/code&gt; but would be greeted with an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git pull
ERROR: Repository not found.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would only happen on my org-owned projects, since my personal projects worked just fine. So what was the issue?&lt;/p&gt;

&lt;p&gt;We get a hint by following the troubleshooting tips here: &lt;a href="https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/error-repository-not-found"&gt;https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/error-repository-not-found&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh -T git@github.com
Hi &amp;lt;not my expected username&amp;gt;! You've successfully authenticated, but GitHub does not provide shell access.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically my SSH agent (gnome-keyring) was presenting a key that belonged to a valid GitHub user (so authentication passed), but not one with access to my project (so GitHub denied access).&lt;/p&gt;

&lt;p&gt;In other words, even though I had the correct key in my keychain, the SSH agent presented the wrong one, so I couldn't access the repository.&lt;/p&gt;

&lt;p&gt;The solution: add this to my &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host github.com
    IdentityFile /path/to/the/correct/key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when I perform a &lt;code&gt;git pull&lt;/code&gt; it authenticates correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git pull
Already up to date.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>On simplicity</title>
      <dc:creator>Joe McCall</dc:creator>
      <pubDate>Wed, 27 May 2020 12:15:24 +0000</pubDate>
      <link>https://dev.to/joemccall86/on-simplicity-1f42</link>
      <guid>https://dev.to/joemccall86/on-simplicity-1f42</guid>
      <description>&lt;p&gt;We're all familiar with the phrase, "Keep it simple, stupid!" However over the years I have heard this uttered in a rather dismissive tone. What is really being communicated is, "My criticism of your solution relates to its apparent complexity, therefore you are unintelligent." This is not only mean, but also incorrect. I argue that simplicity is in the eyes of the beholder, so to speak.&lt;/p&gt;

&lt;p&gt;Let us examine a hypothetical "pet-store" example. Note that some of the details moving forward are intentionally exaggerated for the sake of making a point. In this pet store application, the existing code base is standard HTML/CSS, with a small amount of javascript (to drive things like analytics, or a twitter feed). It allows the user to browse available pets for adoption, read descriptions, leave comments, etc. Authenticated users can log in to add new pets and remove adopted pets from the list.&lt;/p&gt;

&lt;p&gt;Now, as of writing this post, everyone is encouraged to stay home for public safety. So there are far fewer customers than normal at our pet store in-person, where adoptions typically take place. So we decide to add a "virtual" adoption experience with real-time video and chat.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let's keep it simple!&lt;/em&gt; Our stack is HTML/CSS/JS, so we should just write our chat application from scratch using javascript, rendered on our existing HTML page. No need to introduce new libraries or languages into our stack.&lt;/p&gt;

&lt;p&gt;Wait, that's re-inventing the wheel! We don't want to write all that new code! It will be a mess! What about cross-browser compatibility? I guess there's a thing called WebRTC for video chat. Is my team prepared to embark on solving unknown problems by writing all this code from scratch?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let's keep it simple!&lt;/em&gt; This problem has been solved before. In 2020 people use React/Vue to program real-time applications. There are several examples and ready-to-use libraries that do exactly what we want. We could easily throw together some isolated testable components, and render them on our page by including a bundle.js.&lt;/p&gt;

&lt;p&gt;But now our code base has a mix of HTML/CSS/JS for our main Pet Store, and JS/JSX for our chat application. If there's an issue with one of our pages in the pet store app, we now have two different places to look. We also need to perform an additional step to generate a bundle before we deploy, but only for the javascript we wrote for the chat app (i.e., operational complexity was introduced). It's inconsistent. Some developers of our team are much more comfortable editing the static portion of our site, while other developers prefer working with JS/JSX. This inconsistency has now split the team!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let's keep it simple!&lt;/em&gt; All of our code should be in one language/framework for ease of maintenance. We re-implement the HTML/CSS/JS sections of our site to be react components, and our page is a simple HTML file that serves the "uber-bundle.js." All CSS rules are expressed as inline react stylesheets. All developers can now focus on one standardized way of doing things.&lt;/p&gt;

&lt;p&gt;Hold on though, now we've completely bypassed standard HTML. Our users with screen readers have all complained that our site is completely broken. Furthermore, our search engine score has dropped significantly (someone from our team mentioned something about needing server-side rendering for this?). We also have to worry about maintaining browser history and populating the URL bar for bookmarks- something the browser used to do for us! We have introduced problems here that we didn't need to solve before.&lt;/p&gt;

&lt;p&gt;In reality, the simplest answer is going to be a compromise. Any new code will introduce at least the complexity of learning/maintaining a "new" way of doing things. We &lt;em&gt;all&lt;/em&gt; want to arrive at the simplest solution in the end, and it benefits nobody good to call each other "stupid." Often times complexity is just pushed around from one component to the next. When evaluating a seemingly complex solution, let us all keep in mind that simplicity is not all that simple to discover.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>random</category>
      <category>opinion</category>
    </item>
    <item>
      <title>Terraform - InvalidID on aws_eip</title>
      <dc:creator>Joe McCall</dc:creator>
      <pubDate>Tue, 28 Apr 2020 17:39:23 +0000</pubDate>
      <link>https://dev.to/joemccall86/terraform-invalidid-on-awseip-1okk</link>
      <guid>https://dev.to/joemccall86/terraform-invalidid-on-awseip-1okk</guid>
      <description>&lt;p&gt;I was trying to apply a terraform template today and ran into this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;module.network.aws_eip.eip_nat_az2: Creating...

Error: error adding tags: error tagging resource &lt;span class="o"&gt;(&lt;/span&gt;18.214.131.208&lt;span class="o"&gt;)&lt;/span&gt;: InvalidID: The ID &lt;span class="s1"&gt;'18.214.131.208'&lt;/span&gt; is not valid
        status code: 400, request &lt;span class="nb"&gt;id&lt;/span&gt;: 49aab8a6-8c26-4327-90a5-389a2e5962bd

  on ../modules/network/main.tf line 104, &lt;span class="k"&gt;in &lt;/span&gt;resource &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"eip_nat_az1"&lt;/span&gt;:
 104: resource &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"eip_nat_az1"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

Error: error adding tags: error tagging resource &lt;span class="o"&gt;(&lt;/span&gt;100.24.169.116&lt;span class="o"&gt;)&lt;/span&gt;: InvalidID: The ID &lt;span class="s1"&gt;'100.24.169.116'&lt;/span&gt; is not valid
        status code: 400, request &lt;span class="nb"&gt;id&lt;/span&gt;: 212e2891-6500-4150-94e1-76f28603a2b2

  on ../modules/network/main.tf line 111, &lt;span class="k"&gt;in &lt;/span&gt;resource &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"eip_nat_az2"&lt;/span&gt;:
 111: resource &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"eip_nat_az2"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

Error: error adding tags: error tagging resource &lt;span class="o"&gt;(&lt;/span&gt;52.206.41.2&lt;span class="o"&gt;)&lt;/span&gt;: InvalidID: The ID &lt;span class="s1"&gt;'52.206.41.2'&lt;/span&gt; is not valid
        status code: 400, request &lt;span class="nb"&gt;id&lt;/span&gt;: 38dbad9b-8985-428d-ae67-064fdd4f0f93

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NOTE: These IPs are just random from the AWS pool and don’t really point to anything useful.&lt;/p&gt;

&lt;p&gt;Searching for this issue yielded no results, so I figured I should post the solution.&lt;/p&gt;

&lt;p&gt;It turns out my module had defined eips like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"eip_nat_az1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"foo"&lt;/span&gt;
        &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I examined the created IP addresses in the AWS console I saw that they were “Classic” EIPs and not VPC.&lt;/p&gt;

&lt;p&gt;I had forgotten to specify &lt;code&gt;vpc = true&lt;/code&gt;. The module now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"eip_nat_az1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;vpc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"foo"&lt;/span&gt;
        &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;terraform apply&lt;/code&gt; runs cleanly!&lt;/p&gt;

</description>
      <category>terraform</category>
    </item>
    <item>
      <title>Groovy’s compareTo operator and Equality</title>
      <dc:creator>Joe McCall</dc:creator>
      <pubDate>Sat, 11 May 2019 16:54:23 +0000</pubDate>
      <link>https://dev.to/joemccall86/groovy-s-compareto-operator-and-equality-312n</link>
      <guid>https://dev.to/joemccall86/groovy-s-compareto-operator-and-equality-312n</guid>
      <description>&lt;p&gt;Let’s assume we have a Book class that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="nd"&gt;@CompileStatic&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
  &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;
  &lt;span class="n"&gt;Float&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within our app we have several places where a widget is presented to the end-user, such as an API that drives a mobile app, an API that drives a single-page-application (like react/vue/etc), or even something rendered server-side like a GSP.&lt;/p&gt;

&lt;p&gt;The data is represented well, and the customer is happy, but now there’s a business rule that states that all books should be presented sorted alphabetically. We can do this several ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sort it in a controller&lt;/li&gt;
&lt;li&gt;Sort it in the view itself&lt;/li&gt;
&lt;li&gt;(Edit) Specify the sort order in the domain class&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Edit: If you are using a data source that supports it, you can specify sort columns and order directly in the &lt;code&gt;mapping&lt;/code&gt; closure. The point of this post still stands outside the context of GORM though. If you have a list of any POGO you could be affected.&lt;/p&gt;

&lt;p&gt;If we sort it in the controller it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ReadOnly&lt;/span&gt;
&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// old code&lt;/span&gt;
   &lt;span class="c1"&gt;//respond Book.list()&lt;/span&gt;

   &lt;span class="c1"&gt;// new code to sort by name&lt;/span&gt;
   &lt;span class="n"&gt;respond&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will work, but some developers may want to record the fact that a book’s “natural order” is that of being sorted by name. In other words, the fact is stored on the domain itself of how it &lt;em&gt;should&lt;/em&gt; be sorted. Let’s impliment this using java’s &lt;code&gt;compareTo&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="nd"&gt;@CompileStatic&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;Comparable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;
    &lt;span class="n"&gt;Float&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Sort by name ascending&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our controller method looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ReadOnly&lt;/span&gt;
&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// old code&lt;/span&gt;
    &lt;span class="c1"&gt;//respond Book.list()&lt;/span&gt;

    &lt;span class="c1"&gt;// new code to sort, this time by using the pre-defined "natural order"&lt;/span&gt;
    &lt;span class="n"&gt;respond&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  THIS HAS A VERY SERIOUS SIDE-EFFECT
&lt;/h2&gt;

&lt;p&gt;The above is &lt;em&gt;not&lt;/em&gt; an ideal solution, and it’s best illustrated with a concrete example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;book1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'The Gadfly'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;author:&lt;/span&gt; &lt;span class="s2"&gt;"Ethel Voynich"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;price:&lt;/span&gt; &lt;span class="mf"&gt;19.95&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'The Gadfly'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;author:&lt;/span&gt; &lt;span class="s2"&gt;"Johnny Copycat"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;price:&lt;/span&gt; &lt;span class="mf"&gt;9.95&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;book1&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="c1"&gt;// THIS FAILS!!! GROOVY THINKS THEY ARE EQUAL!&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a bit worrisome that the two books are equal. This is clearly not the case.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s happening?
&lt;/h3&gt;

&lt;p&gt;I’ll get to the technical reasons in a minute, but based on observation, it appears that it’s only checking the &lt;code&gt;name&lt;/code&gt; field of our class for equality, and stopping there.&lt;/p&gt;

&lt;p&gt;At this point all we know is the language is incorrectly interpreting the idea of equality between two books. It thinks that just because the book names match, that the book objects must be equal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let’s try to fix this
&lt;/h3&gt;

&lt;p&gt;We introduced natrual ordering, but it has clearly affected the definition of equality for books. Therefore let’s try to rectify this situation be defining an &lt;code&gt;equals&lt;/code&gt; method so there’s no confusion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="nd"&gt;@CompileStatic&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;Comparable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;
    &lt;span class="n"&gt;Float&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Sort by name ascending&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;author&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;author&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’re expressing the idea that a book can only equal another book if the name, author, and price match.&lt;/p&gt;

&lt;p&gt;Let’s test this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;book1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'The Gadfly'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;author:&lt;/span&gt; &lt;span class="s2"&gt;"Ethel Voynich"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;price:&lt;/span&gt; &lt;span class="mf"&gt;19.95&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'The Gadfly'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;author:&lt;/span&gt; &lt;span class="s2"&gt;"Johnny Copycat"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;price:&lt;/span&gt; &lt;span class="mf"&gt;9.95&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;book1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// This passes, we should be ok&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;book1&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="c1"&gt;// THIS STILL FAILS!!! WHY IS THIS?!&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why didn’t defining &lt;code&gt;equals&lt;/code&gt; fix &lt;code&gt;==&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;After all, doesn’t the &lt;code&gt;==&lt;/code&gt; operator just delegate to the &lt;code&gt;equals&lt;/code&gt; method in groovy?&lt;/p&gt;

&lt;p&gt;Let’s look closer at the documentation: &lt;a href="http://docs.groovy-lang.org/latest/html/documentation/index.html#_behaviour_of_code_code"&gt;http://docs.groovy-lang.org/latest/html/documentation/index.html#_behaviour_of_code_code&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In Java == means equality of primitive types or identity for objects. In Groovy == translates to a.compareTo(b)==0, if they are Comparable, and a.equals(b) otherwise. To check for identity, there is is. E.g. a.is(b).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oof. This really hinders our ability to define a natural order. Even if we define &lt;code&gt;equals&lt;/code&gt;, it will be ignored if our class implements &lt;code&gt;Comparable&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the solution?
&lt;/h3&gt;

&lt;p&gt;Let’s look at the Java recommendations for the &lt;code&gt;Comparable&lt;/code&gt; interface: &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html"&gt;https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is strongly recommended (though not required) that natural orderings be consistent with equals. This is so because sorted sets (and sorted maps) without explicit comparators behave “strangely” when they are used with elements (or keys) whose natural ordering is inconsistent with equals. In particular, such a sorted set (or sorted map) violates the general contract for set (or map), which is defined in terms of the equals method.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In groovy, if we follow this recommendataion everything works fine. So let’s make &lt;code&gt;compareTo&lt;/code&gt; consistent with &lt;code&gt;equals&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="nd"&gt;@CompileStatic&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;Comparable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;
    &lt;span class="n"&gt;Float&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Sort by name ascending&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;

        &lt;span class="c1"&gt;// Then sort by author, then price&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;author&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;author&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;author&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;author&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we call &lt;code&gt;compareTo&lt;/code&gt; on another instance it will only return &lt;code&gt;0&lt;/code&gt; when the instances are equal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;book1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'The Gadfly'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;author:&lt;/span&gt; &lt;span class="s2"&gt;"Ethel Voynich"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;price:&lt;/span&gt; &lt;span class="mf"&gt;19.95&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'The Gadfly'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;author:&lt;/span&gt; &lt;span class="s2"&gt;"Johnny Copycat"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;price:&lt;/span&gt; &lt;span class="mf"&gt;9.95&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;book1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;// passes&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;book1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// passes&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;book1&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="c1"&gt;// passes&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  That’s too much code. Is there a better way?
&lt;/h3&gt;

&lt;p&gt;We can use Groovy’s built-in transforms to achieve the same result with far less code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="nd"&gt;@CompileStatic&lt;/span&gt;
&lt;span class="nd"&gt;@Sortable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;includes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'author'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'price'&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@EqualsAndHashCode&lt;/span&gt; &lt;span class="c1"&gt;// Not required, but here for completeness&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;
    &lt;span class="n"&gt;Float&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@Sortable&lt;/code&gt; annotation implements the &lt;code&gt;Comparable&lt;/code&gt; interface for us. Furthermore, if the &lt;code&gt;includes&lt;/code&gt; argument is used it will check the fields in the order they are listed.&lt;/p&gt;

&lt;p&gt;I’ve listed the &lt;code&gt;@EqualsAndHashCode&lt;/code&gt; transformation here as well to generate those methods, simply because we had them in the above snippets. They aren’t strictly necessary to make our test pass since Groovy will only look at &lt;code&gt;compareTo&lt;/code&gt;, but I think that documenting that &lt;em&gt;we&lt;/em&gt; are defining equality &lt;em&gt;somewhere&lt;/em&gt; in this class is important, and this is a way to achieve that.&lt;/p&gt;

&lt;p&gt;Just a word of caution: be sure to include all fields in the &lt;code&gt;@Sortable&lt;/code&gt; includes list. Otherwise the natural order of the class becomes inconsistent with the equality.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if I want to use a custom &lt;code&gt;equals&lt;/code&gt; method?
&lt;/h3&gt;

&lt;p&gt;I can’t think of a reason you would need this, but it &lt;em&gt;should&lt;/em&gt; be able to do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// custom definition of equality&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;equalsObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Normal sort logic from above:&lt;/span&gt;

            &lt;span class="c1"&gt;// Sort by name ascending&lt;/span&gt;
            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;

            &lt;span class="c1"&gt;// Then sort by author, then price&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;author&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;author&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, the key is to ensure that the natural order is consistent with equality.&lt;/p&gt;

&lt;p&gt;I hope this helps someone out!&lt;/p&gt;

</description>
      <category>groovy</category>
    </item>
    <item>
      <title>DataBinding and GORM objects</title>
      <dc:creator>Joe McCall</dc:creator>
      <pubDate>Wed, 20 Feb 2019 20:54:23 +0000</pubDate>
      <link>https://dev.to/joemccall86/databinding-and-gorm-objects-1hka</link>
      <guid>https://dev.to/joemccall86/databinding-and-gorm-objects-1hka</guid>
      <description>&lt;p&gt;In Groovy, you are able to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Widget&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When this widget is a GORM object (in a Grails application), the default configuration doesn’t allow for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside grails-app/domain&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PendingDeletion&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
  &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Elsewhere in code&lt;/span&gt;
&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;pendingDeletion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PendingDeletion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'foo'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;pendingDeletion&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="c1"&gt;// Fails; pendingDeletion.path is null&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because the default configuration instructs the Grails data-binder to convert empty strings to null &lt;sup id="fnref:1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;I’m not exactly sure why this is the default. In order to make the above snippet work in your Grails application, specify the following in application.yml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;grails&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;databinding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;convertEmptyStringsToNull&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the GORM map constructor to behave the same as a normal POGO map constructor.&lt;/p&gt;

&lt;p&gt;Alternatively, just do not use the GORM map constructor when the field values can be null.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="http://docs.grails.org/3.3.9/ref/Constraints/nullable.html"&gt;http://docs.grails.org/3.3.9/ref/Constraints/nullable.html&lt;/a&gt; ↩
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>grails</category>
      <category>groovy</category>
      <category>gorm</category>
    </item>
    <item>
      <title>Grails Views - Rendering Empty Lists</title>
      <dc:creator>Joe McCall</dc:creator>
      <pubDate>Fri, 25 Jan 2019 20:54:23 +0000</pubDate>
      <link>https://dev.to/joemccall86/grails-views-rendering-empty-lists-4bhn</link>
      <guid>https://dev.to/joemccall86/grails-views-rendering-empty-lists-4bhn</guid>
      <description>&lt;p&gt;Do you have a controller action that looks like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;respond&lt;/span&gt; &lt;span class="n"&gt;myService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listItems&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Does that controller show a view like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;itemList&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may appear to work fine when itemList is populated, but you may notice that the API is broken when the index method is run when there are no items in the list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/api/items
&lt;span class="o"&gt;[&lt;/span&gt;
   null
&lt;span class="o"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What? This is supposed to render &lt;code&gt;[]&lt;/code&gt; because we’re responding with an empty list. What happened?&lt;/p&gt;

&lt;p&gt;Let’s look at this from the documentation found here: &lt;a href="http://views.grails.org/latest/"&gt;http://views.grails.org/latest/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the collection is empty, emptyCollection will be used as the default model name. This is due to not being able to inspect the first object’s type.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the itemList field on our model is null because the auto model-binding computes to emptyCollection. There are technical reasons for this. The fastest way to fix this is to change how we call respond:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nl"&gt;itemList:&lt;/span&gt; &lt;span class="n"&gt;myService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listItems&lt;/span&gt;&lt;span class="o"&gt;()])&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This forces the itemList in the model to be bound manually, whether it is empty or not.&lt;/p&gt;

</description>
      <category>grails</category>
      <category>groovy</category>
    </item>
  </channel>
</rss>
