{"id":3227,"date":"2016-06-16T16:49:54","date_gmt":"2016-06-16T20:49:54","guid":{"rendered":"http:\/\/fgiasson.com\/blog\/?p=3227"},"modified":"2016-11-17T13:46:43","modified_gmt":"2016-11-17T18:46:43","slug":"improving-org-babel-clojure","status":"publish","type":"post","link":"https:\/\/fgiasson.com\/blog\/index.php\/2016\/06\/16\/improving-org-babel-clojure\/","title":{"rendered":"Improving org-babel-clojure"},"content":{"rendered":"<p>In a previous blog post, I started to play with <a href=\"http:\/\/orgmode.org\/worg\/org-contrib\/babel\/languages\/ob-doc-clojure.html\">org-babel-clojure<\/a> to improve its capabilities such that Clojure gets better integrated into Org-mode for creating notebooks and Literate programs. The first thing I wanted to do is to remove the 20 seconds timeout that was defaulted with the <code>nrepl<\/code>. That meant that it was not possible to run procedures for longer than 20 seconds before it died with a timeout. Once this was implemented, the next step was to add a new feature to see the underlying process of a code block. Because the nature of my work (extensive work with big datasets), my procedures take time to run (minutes\u00e2\u20ac\u00a6 hours\u00e2\u20ac\u00a6) and much information [about the process] is output to the terminal. However, in the <code>org-babel-clojure<\/code> implementation, you had to wait until the code was executed before being able to see the processing. What I did at the time is to add a new <code>:async<\/code> code block parameter which told <code>org-babel-clojure<\/code> to output all the output of the <code>nrepl<\/code>, when it was being processed, in a new window.<\/p>\n<p>That worked like a charm. However, after much interaction with Nicolas Goaziou, one of the core maintainers of Org-mode, it was clear that my implementation was not an asynchronous implementation but really just a live processing output.<\/p>\n<p>At the same time, I did find another major irritant: if an exception was raised in my Clojure code, then nothing was output to Org-mode, it was simply silently dying. The only way to see the exception was to switch to the Clojure major mode (using <code>C-c '<\/code>) and to rerun the code block.<\/p>\n<p><!--more--><\/p>\n<p>Here are the two new improvements to my <code>org-babel-clojure<\/code> implementation:<\/p>\n<ol class=\"org-ol\">\n<li>Rename the <code>:async<\/code> block parameter to <code>:show-process<\/code><\/li>\n<li>Output the exceptions and errors messages with the <code>output<\/code> and the <code>value<\/code> results parameter<\/li>\n<\/ol>\n<p>By renaming to <code>:show-process<\/code> I remove the ambiguity of the feature. Eventually we should get to a real asynchronous process, but the issue is that it is much more complex than I initially thought and this is a problem being addressed in Org-mode for all backends and not just <code>org-babel-clojure<\/code>.<\/p>\n<p>Then every exception or error messages returned by <code>nrepl<\/code> are appended the <code>value<\/code> or the <code>output<\/code> returned by the code block. That way, we immediately see that something is going wrong directly within Org-mode.<\/p>\n<p>Here is the latest version of my org-mode-babel implementation:<\/p>\n<div class=\"org-src-container\">\n<pre class=\"src src-elisp\"><span style=\"color: #ae81ff;\">(<\/span><span style=\"color: #f92672;\">defvar<\/span> <span style=\"color: #fd971f;\">nrepl-sync-request-timeout<\/span><span style=\"color: #ae81ff;\">)<\/span>\n\n<span style=\"color: #ae81ff;\">(<\/span><span style=\"color: #f92672;\">defun<\/span> <span style=\"color: #a6e22e;\">org-babel-execute:clojure<\/span> <span style=\"color: #66d9ef;\">(<\/span>body params<span style=\"color: #66d9ef;\">)<\/span>\n  <span style=\"color: #75715e;\">\"Execute a block of Clojure code with Babel. The block can be executed<\/span>\n<span style=\"color: #75715e;\">   synchenously by default or asynchronously with the :show-process parameter\"<\/span>\n  <span style=\"color: #66d9ef;\">(<\/span><span style=\"color: #f92672;\">let<\/span> <span style=\"color: #a6e22e;\">(<\/span><span style=\"color: #e6db74;\">(<\/span>expanded <span style=\"color: #fd971f;\">(<\/span>org-babel-expand-body:clojure body params<span style=\"color: #fd971f;\">)<\/span><span style=\"color: #e6db74;\">)<\/span>\n        <span style=\"color: #e6db74;\">(<\/span>sbuffer <span style=\"color: #e6db74;\">\"*Clojure Show Process Sub Buffer*\"<\/span><span style=\"color: #e6db74;\">)<\/span>\n        <span style=\"color: #e6db74;\">(<\/span>show <span style=\"color: #fd971f;\">(<\/span><span style=\"color: #f92672;\">if<\/span> <span style=\"color: #f92672;\">(<\/span>assoc <span style=\"color: #f92672;\">:show-process<\/span> params<span style=\"color: #f92672;\">)<\/span> t nil<span style=\"color: #fd971f;\">)<\/span><span style=\"color: #e6db74;\">)<\/span>\n        <span style=\"color: #e6db74;\">(<\/span>response <span style=\"color: #fd971f;\">(<\/span>cons 'dict nil<span style=\"color: #fd971f;\">)<\/span><span style=\"color: #e6db74;\">)<\/span>\n        status\n        result<span style=\"color: #a6e22e;\">)<\/span>\n    <span style=\"color: #a6e22e;\">(<\/span>case org-babel-clojure-backend\n      <span style=\"color: #e6db74;\">(<\/span>cider\n       <span style=\"color: #fd971f;\">(<\/span><span style=\"color: #f92672;\">require<\/span> '<span style=\"color: #ae81ff;\">cider<\/span><span style=\"color: #fd971f;\">)<\/span>\n       <span style=\"color: #fd971f;\">(<\/span><span style=\"color: #f92672;\">let<\/span> <span style=\"color: #f92672;\">(<\/span><span style=\"color: #ae81ff;\">(<\/span>result-params <span style=\"color: #66d9ef;\">(<\/span>cdr <span style=\"color: #a6e22e;\">(<\/span>assoc <span style=\"color: #f92672;\">:result-params<\/span> params<span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #f92672;\">)<\/span>\n         <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">Check if the user want to run code asynchronously<\/span>\n         <span style=\"color: #f92672;\">(<\/span><span style=\"color: #f92672;\">when<\/span> show\n           <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">Create a new window with the show output buffer<\/span>\n           <span style=\"color: #ae81ff;\">(<\/span>switch-to-buffer-other-window sbuffer<span style=\"color: #ae81ff;\">)<\/span>\n\n           <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">Run the Clojure code asynchronously in nREPL<\/span>\n           <span style=\"color: #ae81ff;\">(<\/span>nrepl-request:eval\n            expanded \n            <span style=\"color: #66d9ef;\">(<\/span><span style=\"color: #f92672;\">lambda<\/span> <span style=\"color: #a6e22e;\">(<\/span>resp<span style=\"color: #a6e22e;\">)<\/span> \n              <span style=\"color: #a6e22e;\">(<\/span><span style=\"color: #f92672;\">when<\/span> <span style=\"color: #ae81ff;\">(<\/span>member <span style=\"color: #e6db74;\">\"out\"<\/span> resp<span style=\"color: #ae81ff;\">)<\/span>\n                <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">Print the output of the nREPL in the asyn output buffer<\/span>\n                <span style=\"color: #ae81ff;\">(<\/span>princ <span style=\"color: #66d9ef;\">(<\/span>nrepl-dict-get resp <span style=\"color: #e6db74;\">\"out\"<\/span><span style=\"color: #66d9ef;\">)<\/span> <span style=\"color: #66d9ef;\">(<\/span>get-buffer sbuffer<span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span>\n              <span style=\"color: #a6e22e;\">(<\/span><span style=\"color: #f92672;\">when<\/span> <span style=\"color: #ae81ff;\">(<\/span>member <span style=\"color: #e6db74;\">\"ex\"<\/span> resp<span style=\"color: #ae81ff;\">)<\/span>\n                <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">In case there is an exception, then add it to the output \n                ; buffer as well<\/span>\n                <span style=\"color: #ae81ff;\">(<\/span>princ <span style=\"color: #66d9ef;\">(<\/span>nrepl-dict-get resp <span style=\"color: #e6db74;\">\"ex\"<\/span><span style=\"color: #66d9ef;\">)<\/span> <span style=\"color: #66d9ef;\">(<\/span>get-buffer sbuffer<span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span>\n                <span style=\"color: #ae81ff;\">(<\/span>princ <span style=\"color: #66d9ef;\">(<\/span>nrepl-dict-get resp <span style=\"color: #e6db74;\">\"root-ex\"<\/span><span style=\"color: #66d9ef;\">)<\/span> <span style=\"color: #66d9ef;\">(<\/span>get-buffer sbuffer<span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span>\n              <span style=\"color: #a6e22e;\">(<\/span><span style=\"color: #f92672;\">when<\/span> <span style=\"color: #ae81ff;\">(<\/span>member <span style=\"color: #e6db74;\">\"err\"<\/span> resp<span style=\"color: #ae81ff;\">)<\/span>\n                <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">In case there is an error, then add it to the output \n                ; buffer as well<\/span>\n                <span style=\"color: #ae81ff;\">(<\/span>princ <span style=\"color: #66d9ef;\">(<\/span>nrepl-dict-get resp <span style=\"color: #e6db74;\">\"err\"<\/span><span style=\"color: #66d9ef;\">)<\/span> <span style=\"color: #66d9ef;\">(<\/span>get-buffer sbuffer<span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span>\n              <span style=\"color: #a6e22e;\">(<\/span>nrepl--merge response resp<span style=\"color: #a6e22e;\">)<\/span>\n              <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">Update the status of the nREPL output session<\/span>\n              <span style=\"color: #a6e22e;\">(<\/span>setq status <span style=\"color: #ae81ff;\">(<\/span>nrepl-dict-get response <span style=\"color: #e6db74;\">\"status\"<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span>\n            <span style=\"color: #66d9ef;\">(<\/span>cider-current-connection<span style=\"color: #66d9ef;\">)<\/span> \n            <span style=\"color: #66d9ef;\">(<\/span>cider-current-session<span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span>\n\n           <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">Wait until the nREPL code finished to be processed<\/span>\n           <span style=\"color: #ae81ff;\">(<\/span><span style=\"color: #f92672;\">while<\/span> <span style=\"color: #66d9ef;\">(<\/span>not <span style=\"color: #a6e22e;\">(<\/span>member <span style=\"color: #e6db74;\">\"done\"<\/span> status<span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span>\n             <span style=\"color: #66d9ef;\">(<\/span>nrepl-dict-put response <span style=\"color: #e6db74;\">\"status\"<\/span> <span style=\"color: #a6e22e;\">(<\/span>remove <span style=\"color: #e6db74;\">\"need-input\"<\/span> status<span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span>\n             <span style=\"color: #66d9ef;\">(<\/span>accept-process-output nil 0.01<span style=\"color: #66d9ef;\">)<\/span>\n             <span style=\"color: #66d9ef;\">(<\/span>redisplay<span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span>\n\n           <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">Delete the show buffer &amp; window when the processing is finalized<\/span>\n           <span style=\"color: #ae81ff;\">(<\/span><span style=\"color: #f92672;\">let<\/span> <span style=\"color: #66d9ef;\">(<\/span><span style=\"color: #a6e22e;\">(<\/span>wins <span style=\"color: #ae81ff;\">(<\/span>get-buffer-window-list sbuffer nil t<span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span>\n             <span style=\"color: #66d9ef;\">(<\/span><span style=\"color: #f92672;\">dolist<\/span> <span style=\"color: #a6e22e;\">(<\/span>win wins<span style=\"color: #a6e22e;\">)<\/span>\n               <span style=\"color: #a6e22e;\">(<\/span>delete-window win<span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span>\n             <span style=\"color: #66d9ef;\">(<\/span>kill-buffer sbuffer<span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span>\n\n           <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">Put the output or the value in the result section of the code block<\/span>\n           <span style=\"color: #ae81ff;\">(<\/span>setq result <span style=\"color: #66d9ef;\">(<\/span>concat <span style=\"color: #a6e22e;\">(<\/span>nrepl-dict-get response \n                                                <span style=\"color: #ae81ff;\">(<\/span><span style=\"color: #f92672;\">if<\/span> <span style=\"color: #66d9ef;\">(<\/span>or \n                                                      <span style=\"color: #a6e22e;\">(<\/span>member <span style=\"color: #e6db74;\">\"output\"<\/span> result-params<span style=\"color: #a6e22e;\">)<\/span>\n                                                      <span style=\"color: #a6e22e;\">(<\/span>member <span style=\"color: #e6db74;\">\"pp\"<\/span> result-params<span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span>\n                                                    <span style=\"color: #e6db74;\">\"out\"<\/span>\n                                                  <span style=\"color: #e6db74;\">\"value\"<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span>\n                                <span style=\"color: #a6e22e;\">(<\/span>nrepl-dict-get response <span style=\"color: #e6db74;\">\"ex\"<\/span><span style=\"color: #a6e22e;\">)<\/span>\n                                <span style=\"color: #a6e22e;\">(<\/span>nrepl-dict-get response <span style=\"color: #e6db74;\">\"root-ex\"<\/span><span style=\"color: #a6e22e;\">)<\/span>\n                                <span style=\"color: #a6e22e;\">(<\/span>nrepl-dict-get response <span style=\"color: #e6db74;\">\"err\"<\/span><span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #f92672;\">)<\/span>\n         <span style=\"color: #75715e; font-style: italic;\">; <\/span><span style=\"color: #75715e; font-style: italic;\">Check if user want to run code synchronously<\/span>\n         <span style=\"color: #f92672;\">(<\/span><span style=\"color: #f92672;\">when<\/span> <span style=\"color: #ae81ff;\">(<\/span>not show<span style=\"color: #ae81ff;\">)<\/span>\n           <span style=\"color: #ae81ff;\">(<\/span>setq response <span style=\"color: #66d9ef;\">(<\/span><span style=\"color: #f92672;\">let<\/span> <span style=\"color: #a6e22e;\">(<\/span><span style=\"color: #ae81ff;\">(<\/span>nrepl-sync-request-timeout \n                                 org-babel-clojure-sync-nrepl-timeout<span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span>\n                            <span style=\"color: #a6e22e;\">(<\/span>nrepl-sync-request:eval\n                             expanded <span style=\"color: #ae81ff;\">(<\/span>cider-current-connection<span style=\"color: #ae81ff;\">)<\/span> \n                                      <span style=\"color: #ae81ff;\">(<\/span>cider-current-session<span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span>\n           <span style=\"color: #ae81ff;\">(<\/span>setq result\n                 <span style=\"color: #66d9ef;\">(<\/span>concat \n                  <span style=\"color: #a6e22e;\">(<\/span>nrepl-dict-get response <span style=\"color: #ae81ff;\">(<\/span><span style=\"color: #f92672;\">if<\/span> <span style=\"color: #66d9ef;\">(<\/span>or <span style=\"color: #a6e22e;\">(<\/span>member <span style=\"color: #e6db74;\">\"output\"<\/span> result-params<span style=\"color: #a6e22e;\">)<\/span>\n                                                   <span style=\"color: #a6e22e;\">(<\/span>member <span style=\"color: #e6db74;\">\"pp\"<\/span> result-params<span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span>\n                                               <span style=\"color: #e6db74;\">\"out\"<\/span>\n                                             <span style=\"color: #e6db74;\">\"value\"<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span>\n                  <span style=\"color: #a6e22e;\">(<\/span>nrepl-dict-get response <span style=\"color: #e6db74;\">\"ex\"<\/span><span style=\"color: #a6e22e;\">)<\/span>\n                  <span style=\"color: #a6e22e;\">(<\/span>nrepl-dict-get response <span style=\"color: #e6db74;\">\"root-ex\"<\/span><span style=\"color: #a6e22e;\">)<\/span>\n                  <span style=\"color: #a6e22e;\">(<\/span>nrepl-dict-get response <span style=\"color: #e6db74;\">\"err\"<\/span><span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #f92672;\">)<\/span><span style=\"color: #fd971f;\">)<\/span><span style=\"color: #e6db74;\">)<\/span>\n       <span style=\"color: #e6db74;\">(<\/span>slime\n        <span style=\"color: #fd971f;\">(<\/span><span style=\"color: #f92672;\">require<\/span> '<span style=\"color: #ae81ff;\">slime<\/span><span style=\"color: #fd971f;\">)<\/span>\n        <span style=\"color: #fd971f;\">(<\/span><span style=\"color: #f92672;\">with-temp-buffer<\/span>\n          <span style=\"color: #f92672;\">(<\/span>insert expanded<span style=\"color: #f92672;\">)<\/span>\n          <span style=\"color: #f92672;\">(<\/span>setq result\n                <span style=\"color: #ae81ff;\">(<\/span>slime-eval\n                 `<span style=\"color: #66d9ef;\">(<\/span>swank:eval-and-grab-output\n                   ,<span style=\"color: #a6e22e;\">(<\/span>buffer-substring-no-properties <span style=\"color: #ae81ff;\">(<\/span>point-min<span style=\"color: #ae81ff;\">)<\/span> <span style=\"color: #ae81ff;\">(<\/span>point-max<span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span>\n                 <span style=\"color: #66d9ef;\">(<\/span>cdr <span style=\"color: #a6e22e;\">(<\/span>assoc <span style=\"color: #f92672;\">:package<\/span> params<span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span><span style=\"color: #f92672;\">)<\/span><span style=\"color: #fd971f;\">)<\/span><span style=\"color: #e6db74;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span>\n      <span style=\"color: #a6e22e;\">(<\/span>org-babel-result-cond <span style=\"color: #e6db74;\">(<\/span>cdr <span style=\"color: #fd971f;\">(<\/span>assoc <span style=\"color: #f92672;\">:result-params<\/span> params<span style=\"color: #fd971f;\">)<\/span><span style=\"color: #e6db74;\">)<\/span>\n        result\n        <span style=\"color: #e6db74;\">(<\/span><span style=\"color: #f92672;\">condition-case<\/span> nil <span style=\"color: #fd971f;\">(<\/span>org-babel-script-escape result<span style=\"color: #fd971f;\">)<\/span>\n          <span style=\"color: #fd971f;\">(<\/span><span style=\"color: #fd971f; font-weight: bold; font-style: italic; text-decoration: underline;\">error<\/span> result<span style=\"color: #fd971f;\">)<\/span><span style=\"color: #e6db74;\">)<\/span><span style=\"color: #a6e22e;\">)<\/span><span style=\"color: #66d9ef;\">)<\/span><span style=\"color: #ae81ff;\">)<\/span>\n<\/pre>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In a previous blog post, I started to play with org-babel-clojure to improve its capabilities such that Clojure gets better integrated into Org-mode for creating notebooks and Literate programs. The first thing I wanted to do is to remove the 20 seconds timeout that was defaulted with the nrepl. That meant that it was not [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[251,279,277,66],"tags":[278,252,254,276],"class_list":["post-3227","post","type-post","status-publish","format-standard","hentry","category-clojure","category-emacs","category-literate-programming","category-programming","tag-cider","tag-clojure-2","tag-emacs","tag-orgmode"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3227","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=3227"}],"version-history":[{"count":6,"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3227\/revisions"}],"predecessor-version":[{"id":3506,"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3227\/revisions\/3506"}],"wp:attachment":[{"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=3227"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=3227"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fgiasson.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=3227"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}