<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Untitled RSS Feed]]></title><description><![CDATA[Untitled RSS Feed]]></description><link>http://github.com/dylang/node-rss</link><generator>GatsbyJS test</generator><lastBuildDate>Mon, 07 Jun 2021 10:11:42 GMT</lastBuildDate><item><title><![CDATA[Getting started with a Serverless Github App]]></title><description><![CDATA[<p>If you've considered enhancing your teams workflow with a Github app, or even developing something for a wider audience, the Github API is very good. Nevertheless, it can be a little tricky if you want to use serverless node, as the official tutorial is in Ruby and the Official Octokit node packages seem more catered to more server-full options (i.e. they have middleware for handling auth, which is not super applicable to serverless functions).<br><br>We're going to walk through how to set up a very basic Github app that's able to comment on Pull requests so that we have an end-to-end example from code to comment and you can take it from there.</p>]]></description><link>https://kurthutten.com/blog/getting-start-with-a-serverless-github-app</link><guid isPermaLink="false">-3c119cc1-0a17-5850-9a5f-f3f044f09383</guid><pubDate>Wed, 05 May 2021 14:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;If you&apos;ve considered enhancing your teams workflow with a Github app, or even developing something for a wider audience, the Github API is very good. Nevertheless, it can be a little tricky if you want to use serverless-node as the &lt;a href=&quot;https://docs.github.com/en/developers/apps/setting-up-your-development-environment-to-create-a-github-app&quot;&gt;official tutorial is in Ruby&lt;/a&gt; and the Official &lt;a href=&quot;https://www.npmjs.com/package/octokit&quot;&gt;Octokit node packages&lt;/a&gt; is catered to a more server-full solution (i.e. they have middleware for handling auth, which is not super applicable to serverless functions).&lt;br&gt;&lt;br&gt;We&apos;re going to walk through how to set up a very basic Github app that&apos;s able to comment on Pull requests in response to a web hook, so that we have an end-to-end example from code to comment and you can take it from there. Finished repo is &lt;a href=&quot;https://github.com/Irev-Dev/serverless-github-app-starter&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;Code Setup&lt;/h3&gt;&lt;p&gt;We&apos;re going to use Redwood as an example, mostly because it makes getting setup and coding an absolute breeze, but you should have no trouble following along with this tutorial if you have a prefered node base serverless setup (netlify, serverless etc).&lt;/p&gt;&lt;p&gt;&apos;cd&apos; into a new folder where you want your app to go and run &apos;yarn create redwood-app ./&apos;. This will generate a lot of code as it&apos;s setting up a fullstack app, but we&apos;re only concerned about &apos;api/src/functions/&apos;, creating a new file called &apos;githubhook.js&apos;.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;export const handler = () =&amp;gt; {
  console.log(&apos;hi&apos;)
  return {
    statusCode: 200,
    body: &apos;hello&apos;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Spin it up with &apos;yarn rw dev&apos;. It&apos;s going to serve the redwood frontend on port 8910, but the functions will be on 8911 so go to &lt;a href=&quot;http://localhost:8911/githubhook&quot;&gt;localhost:8911/githubhook&lt;/a&gt; and you should see &apos;hello&apos; in the browser and &apos;hi&apos; in the terminal.&lt;/p&gt;&lt;p&gt;Great, our function is setup. The reason we&apos;ve set up a function is Github apps work by listening to web hooks that Github sends to our application (or function in this case). In response to these webhooks we can do most other actions on Github (leave comments, close issues etc). Before we turn out attention to Github, let&apos;s make a quick change to our handler so that we can see info about incoming web hooks. Change it to:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;export const handler = (req) =&amp;gt; {
  const theirSignature = req.headers[&apos;x-hub-signature-256&apos;]
  const eventType = req.headers[&apos;x-github-event&apos;]
  const event = JSON.parse(req.body)
  console.log(`
theirSignature is ${theirSignature}
eventType is ${eventType}
installation id is ${event.installation.id}
  `)
  return {
    statusCode: 200,
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The event type should show us if it&apos;s an &apos;installation&apos; event or &apos;pull_request&apos; event. Now let&apos;s set up Github.&lt;/p&gt;&lt;h3&gt;Creating a Github app&lt;/h3&gt;&lt;p&gt;For longevity, I&apos;ll leave a link to the &lt;a href=&quot;https://docs.github.com/en/developers/apps/setting-up-your-development-environment-to-create-a-github-app&quot;&gt;official tutorial&lt;/a&gt;. Let&apos;s get the app settings ready in 11  quick steps&lt;br&gt;&lt;/p&gt;&lt;p&gt;1. Go to &lt;a href=&quot;https://smee.io&quot;&gt;smee.io&lt;/a&gt; and click &quot;start new channel&quot; to get yourself a unique url, we&apos;ll use this to forward webhooks to your local app, then install and run the smee client in a new terminal.&lt;br&gt;&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;text&quot;&gt;npm install --global smee-client
smee --url https://smee.io/&amp;lt;your-unique-hash&amp;gt; --path /githubhook --port 8911&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2. Go to &lt;a href=&quot;https://github.com/settings/apps&quot;&gt;app settings&lt;/a&gt; and click &quot;New Github App&quot;.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/72fe54d109c3ece5a4a47bee0818ea8e47a463af-2010x316.jpg&quot;&gt;&lt;p&gt;3. Give your app a name.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/e84bf104cd8340f664f64b9626f800e855cad829-934x196.jpg&quot;&gt;&lt;p&gt;4. Put your smee url into the home page URL (you can change this later, but for the time being you need to put something in this field).&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/58f454198e998554c91f97af7dd369f94bb88c65-924x188.jpg&quot;&gt;&lt;p&gt;5. Put your smee url into the webhook URL and add a secret (the secret is optional but we&apos;re going to cover it). I normally use a password generater to create a secret.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/7269b01bf3d1f3ebb1116f8afe535ab6de154c12-1076x382.jpg&quot;&gt;&lt;p&gt;6. Create a &apos;.env&apos; file in the root of your project. You should see &apos;.env.example&apos; already generated for you. Once you create &apos;.env&apos; you should notice that it&apos;s already gitignored. This is good as we don&apos;t want to commit our secrets. Add a new variable for our secret like so.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;sh&quot;&gt;GITHUB_APP_SECRET=d7VtfAC2Uk3G49K4hygTUktzNkvyKx&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;7. Under repositry settings, give your app &quot;read&quot; permission for metadata, and &quot;read and write&quot; permission for pull requests.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/36a825e8f0cc85c94a11ca691e7a3bb9e2c2ae14-1532x882.jpg&quot;&gt;&lt;p&gt;8. Subscribe to pull-request events.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/e81d7183d7b5d270bc58e4c4f4ee18c7404c7e12-1710x852.jpg&quot;&gt;&lt;p&gt;9. Click the big green &quot;Create Github App&quot; button.&lt;/p&gt;&lt;p&gt;10. Add your app id to the &apos;.env&apos; file:&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/ee508f56f3d19dcb514be678d63151f39c339495-550x302.jpg&quot;&gt;&lt;pre&gt;&lt;code lang=&quot;sh&quot;&gt;GITHUB_APP_SECRET=d7VtfAC2Uk3G49K4hygTUktzNkvyKx

GITHUB_APP_ID=113860&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;11. Generate a private key.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/c91177cc6985c00837f8dfbb66bce57a68eb80b6-1528x772.jpg&quot;&gt;&lt;p&gt;You will then download a &apos;.pem&apos; file. Open it in a text editor as we need to paste the contents into our &apos;.env&apos; file. Unfortunatly with Node we can&apos;t use multiline environment variables, so you will have to replace all of the new lines with escaped new lines instead (i.e. &apos;\n&apos;). Your .env file should look something like this now:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;sh&quot;&gt;GITHUB_APP_SECRET=d7VtfAC2Uk3G49K4hygTUktzNkvyKx

GITHUB_APP_ID=113860

GITHUB_APP_PRIVATE_KEY=&quot;-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQAA4+M/9x8bCQDI8h4CfRyWqXCyyrBIlNENhTDci51cq3JzRgoz\nNoj1A1ytvzO7pDnd3kkeQ2Vy9Yh/gB6dpsBc6r3w+40/apafzQh6RrpbSR6WCeOa\nRz8EmdbbnWwCmfDxB0YyLXvv9LIaYanNhlifbGnS5LMB7hPVsbiefKbF/UApbKnT\nME70qEhUpgtMsgFcDutH8Ru7h4sgMxUyK6trYj3IffNhOI6NCfKMzNh1zKENuKUr\nuMJvl0hdA4UpOwVzFe9N7xXsTHwGRZLKELeBS/UOoQH9fatTevSoZ7oNlsVbTd0q\nQ6G/5rkrFp3ThYMXrqi2KExn4TKKPaslqyoFIQIDAQABAoIBACjSoLbIH7OxLE4T\nCicXY/XedmjQw2/FM0LUye1Itz0PN48obJwsVJfRll5WChsVMqWLO5yfI8TQOubc\nlSk19G8or26gkuepK98y0ZSx9YBXtlD3ML/qjgxw7M56dszU2JiQ/pQfS5DuBsPQ\nAz05yvVEz76gQow/iVxY0itKRNVkvn2Vrh2cGRMwHtidLi6PVDe1P9PP1pkpzSGB\nO+k9qlAOTuOOP9h8GhDtT4IUN8uvoNkuXaX9YHYVoQfTKE51y5miAoMaDlAYJcJy\nJw6Loi9QP240UqAmNUHXZ+6Z0WC0M8D67gGwuHAfQRszWkEG7YlQFVTFx2syXvMb\n63MabAECgYEA8h9N8k5mn4el7nJn3umy+G6Te6GCZDH+PaEf3gUMybQz7ay4U5Q6\nnKG1dJvxxx5SF4HlwPSBW/FUg19RqqwmWWNL3yil6LIwmwAxpcqCDdAZSEc398oU\nrEHuMq2cuWLnYJOKzF/4q/es4NoRgRTFnxMmN/h1Ih+lehCTfELFuPECgYEA8PMU\nRlamrqJPJNsE4iTRKdGBPT3mZNT5g9xlNIWolbFW5VSmsyHBheKgWFA4XpyzsWCL\ndSNvmbjVml93FubVdf4NJN91Tchhy4JDXabhcHQ3j5u8ORDP10gZspClORvCUdaG\nBmQUqjNwC+fYLKobcRncXMMBe/zS4dQP7ikzjzECgYBeD3NEktijWRtJSwC3RKrW\ngH6jJNd2/UT7xECRC/0vzuXti5AASDGM7/WCW6LN7CWQJFKRZ2tpwJNIhhs/5qjv\nSPgMtcneYHspfCXNdqKXoyRvQ9umU8c8NFDJN1EPZDDm/+qIAzCj/hAXOiBauSsc\n5V+PluJKY2jxxsbFG1ucwQKBgQCQ/AOoK33SuVHcQHoYxcSiYDFfM38OD2Uwpg6z\n4vVFVdeO2TgRs+8p6+tGGMdCjxJFWm2wB6mgmyrU4DrdqfqqLDumg1uneTr3ZSO6\nF6+xpgzEuhYxVF9sEDN+UjFJQt3Ttr0g3Vnd7GOwlkpq3dTzYndJzgF3pPMT9jG7\nwkkHEQKBgD1+EqeubKxzmBvVinBssWKk7m26pg2Uf+tqe1Ox+CZhUi0aOfFL3NlT\nKpgTSA0iNtp9srSZrUtEWNZ/2ITLIZypBC1cMqbUG4r+VcWfLPKPrzLUuxWgJKr3\nggQlB6rrePyMxDL80uAcf6omuh1/s049XfdApQRs96SVw2kWLghw\n-----END RSA PRIVATE KEY-----&quot;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Triggering a web hook&lt;/h3&gt;&lt;p&gt;Okay we&apos;re ready to start building out the app. But first as a sanity check let&apos;s make sure we can recieve web hooks. It makes sense to start with an installation event since we need to install the app. Go to your &lt;a href=&quot;https://github.com/settings/apps&quot;&gt;app settings&lt;/a&gt; and click edit on your app. From there click &quot;install app&quot; on the left hand side and then the green &quot;install&quot; on the right hand side.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/e14d74ae774c651ada3e536509755e412bf42b55-2036x574.jpg&quot;&gt;&lt;p&gt;You will need to choose permissions for the app. Since it&apos;s your own app &quot;all repositories&quot; will be fine.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/3dcb1c45b22a47f1549d0d457e0da2de5fff7fa9-1088x850.jpg&quot;&gt;&lt;p&gt;And with that we would have triggered a webhook. Check back in your terminal and you should see something like this:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;text&quot;&gt;api | eventType is installation
api | signature is sha256=f59dba407b01bbc18b625b8a27fe6d217c1602bf99bb0fc72c0838c20895f1ff
api | installation id is 16726859&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you plan on running the Github app in tandem with your own app, then you can store this installation id in your database, but for now we&apos;ll leave it. Let&apos;s now trigger a pull-request event. Make a small change to one of your non-critical repos (or create a test repo for this purpose). Editing a read me is an easy way to whip up a pull request.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/9a8650bf4106ed1abf5a56359f910684ce0804ef-1542x780.jpg&quot;&gt;&lt;p&gt;And just like that we should have triggered another event and web hook. The terminal should contain something along the lines of this:&lt;br&gt;&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;text&quot;&gt;api | eventType is pull_request
api | signature is sha256=32be921ea95bd573802ceea398e0d868e6b0c04e6f848ff562f0c7addcfac481
api | installation id is 16726859&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The key bit here is that the event is now &apos;pull_request&apos;.&lt;/p&gt;&lt;h3&gt;Verifying incoming web hooks&lt;/h3&gt;&lt;p&gt;Let&apos;s handle security now. Because a web hook is nothing more than an endpoint exposed to the web waiting for another service to use it, we want to make sure requests come from Github before we proceed with taking any actions. We can do that using the signature. The signature is a SHA256 that&apos;s made with the secret and the body of the request, which means the correct signature can only be made if you know the secret. We can replicate the signature and verify it&apos;s Github sending the request. Let&apos;s put that in a function:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;import { createHmac } from &apos;crypto&apos;

const signRequestBody = (secret, body) =&amp;gt;
  &apos;sha256=&apos; + createHmac(&apos;sha256&apos;, secret)
  .update(body, &apos;utf-8&apos;)
  .digest(&apos;hex&apos;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &apos;crypto&apos; module doesn&apos;t need to be installed as it&apos;s a core Node module. Now all we need to do is compare the signatures. Let&apos;s update our handler:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;export const handler = async (req) =&amp;gt; {
  const theirSignature = req.headers[&apos;x-hub-signature-256&apos;]
  const ourSignature = signRequestBody(process.env.GITHUB_APP_SECRET, req.body)
  if (theirSignature !== ourSignature) {
    return {
      statusCode: 401,
      body: &apos;Bad signature&apos;
    }
  }
  const eventType = req.headers[&apos;x-github-event&apos;]
  const event = JSON.parse(req &amp;amp;&amp;amp; req.body  || &apos;{&quot;installation&quot;: {}}&apos;)
  return {
    statusCode: 200,
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now if you visit &lt;a href=&quot;http://127.0.0.1:8911/githubhook&quot;&gt;localhost:8911/githubhook&lt;/a&gt; in a browser, you should see &quot;bad signature&quot; as the browser is not signing its requests. Great!&lt;/p&gt;&lt;p&gt;Note, that if you&apos;re using Redwood, you can use their &lt;a href=&quot;https://github.com/redwoodjs/redwoodjs.com/blob/f1c1823f5ebd4dd89eb1cb5d7c9e0878f4835c97/docs/webhooks.md#sha256-verifier-used-by-github-discourse&quot;&gt;webhook helpers&lt;/a&gt; to verify the signature instead like so&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;import { verifyEvent } from &apos;@redwoodjs/api/webhooks&apos;

export const handler = async (req) =&amp;gt; {
  verifyEvent(&apos;sha256Verifier&apos;, {
    event: req,
    secret: process.env.GITHUB_APP_SECRET,
    options: { signatureHeader: &apos;x-hub-signature-256&apos; },
  })
  // rest of the handler ...
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&apos;s also check that the event type is a pull request.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;import { createHmac } from &apos;crypto&apos;

const signRequestBody = (secret, body) =&amp;gt;
  &apos;sha256=&apos; + createHmac(&apos;sha256&apos;, secret)
  .update(body, &apos;utf-8&apos;)
  .digest(&apos;hex&apos;)

export const handler = async (req) =&amp;gt; {
  const theirSignature = req.headers[&apos;x-hub-signature-256&apos;]
  const ourSignature = signRequestBody(process.env.GITHUB_APP_SECRET, req.body)
  if (theirSignature !== ourSignature) {
    return {
      statusCode: 401,
      body: &apos;Bad signature&apos;
    }
  }
  const eventType = req.headers[&apos;x-github-event&apos;]
  if (eventType !== &apos;pull_request&apos;) {
    return { statusCode: 200 }
  }
  const event = JSON.parse(req &amp;amp;&amp;amp; req.body  || &apos;{&quot;installation&quot;: {}}&apos;)
  return {
    statusCode: 200,
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If it&apos;s not a pull request we&apos;re returning a health status code of 200 as there&apos;s nothing wrong with the inbound request, it&apos;s just we&apos;re only interested in pull_requests.&lt;/p&gt;&lt;p&gt;Now that we&apos;ve handled security and checked for pull request events, let&apos;s try to comment on newly opened pull requests. We&apos;re going to use Github&apos;s official npm packages for this -- &apos;octokit&apos;. Go ahead and install it with &apos;yarn workspace api add @octokit/app&apos;. If you&apos;re not familiar with Redwood or yarn workspaces, &apos;workspace api&apos; in the previous command is just telling yarn to install this package for the backend API, and not for the web app. &lt;/p&gt;&lt;p&gt;Now we can initialise octokit with all the values in our &apos;.env&apos; file. Add the following close to the top of the file:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;import { App } from &apos;@octokit/app&apos;

const app = new App({
  privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
  appId: process.env.GITHUB_APP_ID,
  webhooks: {
    secret: process.env.GITHUB_APP_SECRET,
  },
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we&apos;ve set up octokit there are a multitude of things we can do.&lt;/p&gt;&lt;h3&gt;Finding docs&lt;/h3&gt;&lt;p&gt;But we&apos;re aiming specifically to leave a comment on a pull request. So how do we find the docs we&apos;re after? We can start &lt;a href=&quot;https://docs.github.com/en/rest/reference&quot;&gt;here&lt;/a&gt; and use the search input on the top right hand side of the page. &quot;create comment pull&quot; sounds like a reasonable search phrase.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/b711069bd236d74245999879c4fe6d5f9978821d-2576x1102.jpg&quot;&gt;&lt;p&gt;The third result relates to their API so let&apos;s go there. Straight away it informs us that we&apos;re actually after the issues API with an explanation as to why and &lt;a href=&quot;https://docs.github.com/en/rest/reference/issues&quot;&gt;a link&lt;/a&gt; to the API.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/ec054697003acda8fd4c13687e8906816f94c69b-1456x260.jpg&quot;&gt;&lt;p&gt;More specifically, we&apos;re interested in the &apos;&lt;a href=&quot;https://docs.github.com/en/rest/reference/issues#create-an-issue-comment&quot;&gt;Create an issue comment&lt;/a&gt;&apos; end point. Along with a great deal of information about the endpoint, it gives us the path of this endpoint &quot;POST /repos/{owner}/{repo}/issues/{issue_number}/comments&quot;. We can use this text directly with one of the octokit helpers; it will look something like:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;octokit.request(
  &apos;POST /repos/{owner}/{repo}/issues/{issue_number}/comments&apos;,
  /* other params */
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But before we get ahead of oursevles, let&apos;s make a function &apos;writePullRequestComment&apos; that takes the web hook event, and our comment text -- i.e. it will be called like so &quot;writePullRequestComment(event, &apos;Salutations, what a fine PR you have here.&apos;)&quot;.&lt;/p&gt;&lt;p&gt;Even though we have already initialised octokit with our &apos;.env&apos; secrets, there&apos;s one more step to the initialisation for setting it up for a specific installation. This is because Github needs to know the specific instance of the app that&apos;s leaving a comment. When we installed the app on our profile, this created a new instance of the app. Because this will change each time the web hook is called we do this initialisation in our new function and we can get the installation id from the event.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;const writePullRequestComment = async ({ event, message }) =&amp;gt; {
  const octokit = await app.getInstallationOctokit(event.installation.id)
  /* more code to come */
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can finish off the function by adding the request helper:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;const writePullRequestComment = async ({ event, message }) =&amp;gt; {
  const octokit = await app.getInstallationOctokit(event.installation.id)
  return octokit.request(
    &apos;POST /repos/{owner}/{repo}/issues/{issue_number}/comments&apos;,
    {
      owner: event.repository.owner.login,
      repo: event.repository.name,
      issue_number: event.number,
      body: message,
    }
  )
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&apos;s now ready to be used in our handler function. Here&apos;s the handler in full:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;export const handler = async (req) =&amp;gt; {
  const theirSignature = req.headers[&apos;x-hub-signature-256&apos;]
  const ourSignature = signRequestBody(process.env.GITHUB_APP_SECRET, req.body)
  if (theirSignature !== ourSignature) {
    return {
      statusCode: 401,
      body: &apos;Bad signature&apos;
    }
  }
  const eventType = req.headers[&apos;x-github-event&apos;]
  if (eventType !== &apos;pull_request&apos;) {
    return { statusCode: 200 }
  }
  const event = JSON.parse(req.body)
  if ([&apos;reopened&apos;, &apos;opened&apos;].includes(event.action)) {
    await writePullRequestComment({event, message: &apos;Salutations, what a fine PR you have here.&apos;})
  }
  return {
    statusCode: 200,
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We also added an if statement so that the bot only leaves the comment when a pull request is opened or re-opened, and not every single pull request action. Here&apos;s the result:&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/44c089680c3ba94bc53379739855c0bd0266c71d-1880x926.jpg&quot;&gt;&lt;p&gt;We&apos;ve now got a mimimum example working of communication both ways between Github and our own serverless function. We&apos;re recieving a web hook from Github, and using that to take an action on Github by leaving a comment (and we verified the web hook too).&lt;br&gt;&lt;br&gt;I hope you build something awesome.&lt;br&gt;&lt;br&gt;Before I go I&apos;ll mention that if you&apos;re a typescript fan, then the package &apos;@octokit/webhooks-types&apos; is very handy. The complete &lt;a href=&quot;https://github.com/Irev-Dev/serverless-github-app-starter&quot;&gt;example repo&lt;/a&gt; is typed and here&apos;s a typed version of the code:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;import { createHmac } from &apos;crypto&apos;
import { App } from &apos;@octokit/app&apos;
import type { Endpoints } from &apos;@octokit/types&apos;
import type { PullRequestEvent } from &apos;@octokit/webhooks-types&apos;

const app = new App({
  privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
  appId: process.env.GITHUB_APP_ID,
  webhooks: {
    secret: process.env.GITHUB_APP_SECRET,
  },
})

const signRequestBody = (secret: string, body: string): string =&amp;gt;
  &apos;sha256=&apos; + createHmac(&apos;sha256&apos;, secret).update(body, &apos;utf-8&apos;).digest(&apos;hex&apos;)

const writePullRequestComment = async ({
  event,
  message,
}: {
  event: PullRequestEvent
  message: string
}): Promise&amp;lt;
  Endpoints[&apos;POST /repos/{owner}/{repo}/issues/{issue_number}/comments&apos;][&apos;response&apos;]
&amp;gt; =&amp;gt; {
  const octokit = await app.getInstallationOctokit(event.installation.id)
  return octokit.request(
    &apos;POST /repos/{owner}/{repo}/issues/{issue_number}/comments&apos;,
    {
      owner: event.repository.owner.login,
      repo: event.repository.name,
      issue_number: event.number,
      body: message,
    }
  )
}

export const handler = async (req: {
  body: string
  headers: {
    &apos;x-hub-signature-256&apos;: string
    &apos;x-github-event&apos;: string
  }
}) =&amp;gt; {
  const theirSignature = req.headers[&apos;x-hub-signature-256&apos;]
  const ourSignature = signRequestBody(process.env.GITHUB_APP_SECRET, req.body)
  if (theirSignature !== ourSignature) {
    return {
      statusCode: 401,
      body: &apos;Bad signature&apos;,
    }
  }
  const eventType = req.headers[&apos;x-github-event&apos;]
  if (eventType !== &apos;pull_request&apos;) {
    return { statusCode: 200 }
  }
  const event: PullRequestEvent = JSON.parse(req.body)
  if ([&apos;reopened&apos;, &apos;opened&apos;].includes(event.action)) {
    await writePullRequestComment({
      event,
      message: &apos;Salutations, what a fine PR you have here.&apos;,
    })
  }
  return {
    statusCode: 200,
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Gotchas when using AWS docker-lambdas with the Serverless framework]]></title><description><![CDATA[<p>I've recently spun up some lambda-docker containers for a <span>CadHub</span> feature. Infinitely scaling lamdas where I can control the environment sounds perfect right! The devil is always in the details and here are some sticking points that I hit.</p>]]></description><link>https://kurthutten.com/blog/gotchas-when-using-aws-docker-lamdas-with-the-serverless-framework</link><guid isPermaLink="false">-547fd4c2-2e21-586c-baed-35c04e7ee5b7</guid><pubDate>Fri, 30 Apr 2021 14:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;I&apos;ve recently spun up some lambda-docker containers for a &lt;a href=&quot;https://cadhub.xyz/&quot;&gt;CadHub&lt;/a&gt; feature. Infinitely scaling lamdas where I can control the environment sounds perfect right! The devil is always in the details and here are some sticking points that I hit.&lt;/p&gt;&lt;h2&gt;Building on top of amazon&apos;s images is hard.&lt;/h2&gt;&lt;p&gt;AWS needs some of their own magic on the docker images in order for them to spin them up quickly. If you build on top of one of their provided images this should be handled for you. Since I&apos;m a frontend developer without tonnes of linux experience I found it difficult to figure out how to install the OS level packages I needed on these specialised images. I instead ended up using something more standard and easy to work with (Ubuntu) and then install Amazon&apos;s utilities on top. I found this to be easier, if you&apos;re looking to do this yourself &lt;a href=&quot;https://github.com/Irev-Dev/cadhub/pull/228/files#diff-eb3fba0fb3219ebc2acb6ff0daead1b66499d03b319165f268d4e8a6bb1dc662R19-R34&quot;&gt;these lines&lt;/a&gt; in my docker file and this `&lt;a href=&quot;https://github.com/Irev-Dev/cadhub/pull/228/files#diff-3580d8e4c7c5af0bb39d37f13f1ad40098c0a765ab5e86a0498f6975d408f94f&quot;&gt;entrypoint.sh&lt;/a&gt;` might be of use.&lt;/p&gt;&lt;h2&gt;Serverless CORS config doesn&apos;t agree with docker-lamdas&lt;/h2&gt;&lt;p&gt;Using the Serverless framework, adding CORS to an endpoint should be simple and &lt;a href=&quot;https://www.serverless.com/blog/cors-api-gateway-survival-guide?rd=true%2F&quot;&gt;one line of config&lt;/a&gt;. Unfortunately, this seems more catered to normal lamdas, and found it didn&apos;t work for my docker-lamdas and ended up making options endpoints to roll my own CORS for browser-preflight.&lt;/p&gt;&lt;h2&gt;Binary types have quirks&lt;/h2&gt;&lt;p&gt;If you want to return binary types from your lambda you need to add API &lt;a href=&quot;https://www.serverless.com/plugins/serverless-apigw-binary&quot;&gt;Gateway settings &lt;/a&gt;to allow this, but what you actually return from the lamdba is base64 encoded, but another quirk is that the input into the lambda is also base64 encoded and as far as I know this can&apos;t be turned off. The biggest drawback I found to this workflow is how much of a pain it is to test the lambda locally since you can&apos;t send it a dumby payload without first encoding it. Another thing to be aware of is that the API Gateway has a response limit of 10mb so it might be worth skipping the binary types altogether and instead dump the asset in S3 and return a signed S3 url.&lt;/p&gt;&lt;h2&gt;Wrap up&lt;/h2&gt;&lt;p&gt;These are all manageable problems, the worst part about it is how much they can stifle momentum when you hit unintuitive issues. Because I was trying to use node on these containers, I found most node resources to be focused on node-lamdas, not docker, so community blog posts were lacking. Would love to hear issues other front-end developers hit trying to use AWS and serverless, you can &lt;a href=&quot;https://twitter.com/IrevDev&quot;&gt;@ me&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Curated Code CAD]]></title><description><![CDATA[<div><p>You like 3d modelling, but don't like using a GUI. Good for you! But which modeller do you use?? You'll have to figure that out for yourself, but here are some options.</p><p>special mention</p><p>Openscad</p><p>The rest of the packages are in alphabetical order, but OpenScad gets a special mention because it's the OG. Many of the projects below were inspired by OpenScad and is the most well-known choice. If you're new to code-cad this is the safest choice, even if only from tutorials and content perspective.</p><p>opencascade</p><p>It's a c++ library that a number the projects below wrap.</p></div>]]></description><link>https://kurthutten.com/blog/curated-code-cad</link><guid isPermaLink="false">-8a36c772-8834-5578-9c3d-39e8ca6e0f3a</guid><pubDate>Fri, 30 Oct 2020 13:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2&gt;What is Code-CAD?&lt;/h2&gt;&lt;p&gt;It&apos;s software that allows you to define 3D CAD models with code. It&apos;s a niche popular amongst software devs for obvious reasons — it gives you parametric models almost by default and it&apos;s easy to maintain and extend models within a team over time when paired with git. The coding nature of it allows teams to build their own abstraction for re-use and quick prototyping. The &lt;a href=&quot;https://cadhub.xyz/&quot;&gt;Cadhub&lt;/a&gt; homepage has a good breakdown of the potential of the Code-CAD paradigm. Code-CAD is not to be confused with 3d geometry libraries, Code-CAD instead has opinionated abstractions for quickly developing mechanical parts.&lt;/p&gt;&lt;h2&gt;&lt;/h2&gt;&lt;h2&gt;Which one should you use?&lt;/h2&gt;&lt;p&gt;I recommend reading through the entire list below to see if one chimes with you and your needs, beyond that I can make the following recommendation and points:&lt;/p&gt;&lt;p&gt;My main recommendation is to use one of the packages that wrap OpenCascade (a mature C++ CAD library). Packages that do so are CadQuery, CascadeStudio, DeclaraCAD and pythonOCC. My reasons for recommending these are as follows:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Most of Code-CAD tools are plagued with a CSG mindset (that is unions, subtractions and intersections of primitive shapes; cubes spheres etc). This is an inherently limited paradigm (one simple example of this is how internal fillets, which are important for reducing stress concentrations in parts, become very difficult). While CadQuery, CascadeStudio, DeclaraCAD and pythonOCC still offer CSG functionality, you&apos;re also able to move beyond it with concepts like lofts and sweeps.&lt;/li&gt;&lt;li&gt;OpenCascade uses a B-rep (boundary representation) kernel, In my opinion, this means you&apos;ll be learning a future-proof tool that won&apos;t limit the types of applications you can model for, which is likely the case for mesh kernels, which will cause trouble in for some applications like optics and injection moulding.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;OpenSCAD is tried and true, with lots of examples and tutorials floating around the internet. It also has a very intuitive syntax that many people without prior programming experience have been able to quickly pick up. However, some reasons you might want to look elsewhere are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;It can be hard to build powerful abstractions since they&apos;ve rolled their own language. Consequences of this include that it doesn&apos;t have a package manager like many modern languages, and the presence of quirks with the language, such as function definitions that aren&apos;t ergonomic.&lt;/li&gt;&lt;li&gt;Performance can start to suffer with complex parts.&lt;/li&gt;&lt;li&gt;Its mesh-based kernel has limitations if you want to move beyond 3d-printed parts.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Check out the birdhouse example, while anecdotal, seeing the same part made with three different tools might help you decide which syntax you like the most.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;You might want to simply pick a tool based on your language of choice. Clojure, Enaml, Go, Haskell, Lisp, Javascript and Python are all represented below.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;If you want to make 3D art, Curv is specifically trying to hit that niche.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;No matter which one is your tool of choice, if you&apos;re here and you love Code-CAD and you&apos;ll want to checkout &lt;a href=&quot;https://cadhub.xyz/&quot;&gt;Cadhub&lt;/a&gt;. Think of it as Codepen crossed with a thing repository, and it&apos;s our love letter to the Maker community. Currently, CascadeStudio is the only Code-CAD integration, but we&apos;re working on more. &lt;a href=&quot;https://cadhub.xyz/&quot;&gt;Site&lt;/a&gt;, &lt;a href=&quot;https://github.com/Irev-Dev/cadhub&quot;&gt;repo&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Special mention&lt;/h2&gt;&lt;h3&gt;&lt;a href=&quot;http://www.openscad.org/&quot;&gt;OpenScad&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/openscad/openscad&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.openscad.org/community.html&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.openscad.org/documentation.html&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: GPL-2&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The rest of the packages are in alphabetical order, but OpenScad gets a special mention because it&apos;s the OG. Many of the projects below were inspired by OpenScad and is the most well-known choice. If you&apos;re new to code-cad this is the safest choice. The syntax is easy to pick up and there lots of guides around the internet.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://www.opencascade.com/&quot;&gt;OpenCascade&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/tpaviot/oce&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://dev.opencascade.org/&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://old.opencascade.com/doc/occt-6.9.1/refman/html/index.html&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: LGPL-2.1&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It&apos;s a c++ library that a number the projects below wrap. OpenCascade uses a Boundary representation (B-rep) kernel, which is a powerful way representing solid geometry, this is a point of difference for some many of the other projects that use a polygon mesh.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Contributing&lt;/h2&gt;&lt;p&gt;There are a couple of ways you can help:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Know of a package that we missed? tell us with an issue or open up a PR.&lt;/li&gt;&lt;li&gt;Contribute a birdhouse design in one of the tools that are missing.&lt;/li&gt;&lt;li&gt;Do you think we missed an important point for one of the projects, suggest more details.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Here they are:&lt;/h2&gt;&lt;h3&gt;&lt;a href=&quot;https://arnholm.github.io/angelcad-docs/&quot;&gt;AngelCAD&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/arnholm/angelcad&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://forum.abmesh.com/index.php&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://arnholm.github.io/angelcad-docs/&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: GPL-2 or GPL-3&lt;/li&gt;&lt;li&gt;Online editor&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;AngelCAD aim to do two things:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Offer an embedded, but general-purpose scripting language for Constructive Solid Geometry, via &lt;a href=&quot;https://www.angelcode.com/angelscript/&quot;&gt;AngelScript&lt;/a&gt;. This allows for a natural programming style with true variables, user-defined functions and even classes. Programmers should feel at home. See &lt;a href=&quot;https://github.com/arnholm/angelcad-samples&quot;&gt;AngelCAD sample scripts&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Offer a fast boolean engine, which is powered by &lt;a href=&quot;https://github.com/arnholm/carve&quot;&gt;Carve&lt;/a&gt; is used for this purpose. This means that AngelCAD is generally many times faster than other mesh-based systems.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;AngelCAD is capable of running OpenSCAD script for interoperability and has features like text support and DXF import coming soon.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://bitbybit.dev/home&quot;&gt;bitbybit&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/bitbybit-dev/bitbybit&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://discord.com/invite/GSe3VMe&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://docs.bitbybit.dev/&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: MIT&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://bitbybit.dev/app&quot;&gt;Online editor&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;bitbybit is both a node editor and Code-CAD as they have exposed a &lt;a href=&quot;https://medium.com/@bitbybit/v0-3-0-release-typescript-in-monaco-editor-for-bit-by-bit-developers-46bcb1a3b91&quot;&gt;typescript&lt;/a&gt; interface that can be used in their app.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://cadhub.xyz/&quot;&gt;CadHub&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Irev-Dev/cadhub&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://discord.com/invite/SD7zFRNjGH&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: GPL-3&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://cadhub.xyz/&quot;&gt;Online editor&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A community hub for sharing code-cad projects. Currently integrates with the excellent &lt;a href=&quot;https://zalo.github.io/CascadeStudio/&quot;&gt;CascadeStudio&lt;/a&gt;. Built and maintained by yours truly.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://cadquery.readthedocs.io/en/latest/intro.html&quot;&gt;CadQuery&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/CadQuery/cadquery&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://discord.gg/qz3uAdF&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://cadquery.readthedocs.io/en/latest/intro.html&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: Apache, 2.0&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;CadQuery is a Python library that wraps and extends &lt;a href=&quot;https://github.com/tpaviot/oce&quot;&gt;OpenCascade&lt;/a&gt;. It has a philosophy of capturing design intent. The API has been designed to be as close as possible to how you’d describe the object to a human. An example of this is its ability to &quot;select&quot; parts of the model&apos;s geometry to run operations on, such as the following code that selects only the edges running along the Z-axis and fillets them.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;python&quot;&gt;result = cq.Workplane(&quot;XY&quot; ).box(3, 3, 0.5).edges(&quot;|Z&quot;).fillet(0.125)&lt;/code&gt;&lt;/pre&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/0ea1a220cd2ffc4b34879837a9313e7f8c9a88a9-727x500.png&quot;&gt;&lt;h3&gt;&lt;a href=&quot;https://zalo.github.io/CascadeStudio/&quot;&gt;CascadeStudio&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/zalo/CascadeStudio&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/zalo/CascadeStudio/discussions&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;No official docs, but the online editor does have IntelliSense support&lt;/li&gt;&lt;li&gt;License: MIT&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://zalo.github.io/CascadeStudio/&quot;&gt;Online editor&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A javascript wrapper for &lt;a href=&quot;https://github.com/tpaviot/oce&quot;&gt;OpenCascade&lt;/a&gt; that runs in the browser. (OpenCascade can run in the browser when compiled to web-assembly). &lt;a href=&quot;https://cadhub.xyz/&quot;&gt;CadHub&lt;/a&gt; integrates with CascadeStudio.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;http://www.curv3d.org/&quot;&gt;Curv&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/curv3d/curv&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://groups.google.com/d/forum/curv&quot;&gt;Community&lt;/a&gt; (mailing list)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/curv3d/curv/tree/master/docs&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: Apache, 2.0&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Curv is a programming language for creating art using mathematics. It’s a 2D and 3D geometric modelling tool that supports full colour, animation and 3D printing. It was inspired by OpenScad and &lt;a href=&quot;https://www.shadertoy.com/&quot;&gt;shadertoy&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://www.freecadweb.org/&quot;&gt;FreeCAD&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/FreeCAD/FreeCAD&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://forum.freecadweb.org/&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://wiki.freecadweb.org/Getting_started&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: LGPLv2&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;FreeCad is a more traditional CAD package that supports python scripting, Both for modelling as well as controlling the FreeCAD GUI itself. Not only that it has a built in &lt;a href=&quot;https://wiki.freecadweb.org/OpenSCAD_Module&quot;&gt;OpenScad workbench&lt;/a&gt; as well as an external &lt;a href=&quot;https://wiki.freecadweb.org/CadQuery_Workbench&quot;&gt;CadQuery workbench&lt;/a&gt;, making it the best in this list at interoperability. FreeCAD uses OpenCascade under-the-hood.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;http://www.implicitcad.org/&quot;&gt;ImplicitCAD&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/colah/ImplicitCAD&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.implicitcad.org/docs/tutorial&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: AGPL-3&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.implicitcad.org/editor&quot;&gt;Online editor&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Inspired by OpenScad with a very similar language, implemented in Haskell and includes the ability to write definitions in Haskell, instead of just OpenSCAD, and is part of an &apos;almost stack&apos; of tools including ExplicitCAD (for a GUI), and HSlice (for an STL slicer).&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://declaracad.com/&quot;&gt;DeclaraCAD&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/codelv/declaracad&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Community&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://declaracad.com/docs/introduction/&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: GPL-3&lt;/li&gt;&lt;li&gt;Online editor&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A declarative parametric 3D modelling program built using &lt;a href=&quot;https://github.com/LaughlinResearch/pyOCCT&quot;&gt;OpenCASCADE&lt;/a&gt; and &lt;a href=&quot;https://github.com/nucleic/enaml/&quot;&gt;enaml&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://openjscad.org/&quot;&gt;JSCAD&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/jscad/OpenJSCAD.org&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://openjscad.nodebb.com/&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://openjscad.org/dokuwiki/doku.php?id=jscad_quick_reference&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: MIT&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://openjscad.org/&quot;&gt;Online editor&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Formerly OpenJsCad, also inspired by OpenScad. The project is close to a v2.0 release. Implemented in javascript allowing it to be modelled directly within a browser.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://libfive.com/&quot;&gt;libfive&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/libfive/libfive&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/libfive/libfive/issues&quot;&gt;Community&lt;/a&gt; (Github Issues)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://libfive.com/examples/&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: Mozilla Public License 2.0 and GPL-2 or later&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Libfive is a software library and set of tools for solid modelling, especially suited for parametric and procedural design. Lisp based language, (so (you (((((can expect ) lots of parentheses))))).&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;http://www.pythonocc.org/&quot;&gt;pythonOCC&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/tpaviot/pythonocc-core&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.pythonocc.org/category/documentation/&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: LGPL-3&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://mybinder.org/v2/gh/tpaviot/pythonocc-binderhub/7.4.0&quot;&gt;Online editor&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Python-based, Also uses &lt;a href=&quot;https://github.com/tpaviot/oce&quot;&gt;OpenCascade&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://gilesbathgate.com/category/rapcad/&quot;&gt;RapCAD&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/GilesBathgate/RapCAD&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: GPL-3&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Another project inspired by OpenScad. The author considers key differences to be procedural vs functional programming language style, (i.e variables can be modified) and the use of arbitrary precision arithmetic throughout (meaning there are no unexpected double/float rounding errors). There is a handy &lt;a href=&quot;https://github.com/GilesBathgate/RapCAD/blob/master/doc/feature_matrix.asciidoc&quot;&gt;feature matrix&lt;/a&gt; between RapCAD, OpenScad and ImplicitCad.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://github.com/farrellm/scad-clj&quot;&gt;scad-clj&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/farrellm/scad-clj&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;No docs but mirrors openscad functions&lt;/li&gt;&lt;li&gt;License: EPL-1.0&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;OpenSCAD DSL in Clojure. Functions generally mirror OpenSCAD, with a couple of notable exceptions.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://github.com/farrellm/scad-hs&quot;&gt;scad-hs&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/farrellm/scad-hs&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;No docs but mirrors openscad functions&lt;/li&gt;&lt;li&gt;License: BSD-3-Clause License&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Same author as scad-cji, he likes functional programming languages clearly.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://github.com/deadsy/sdfx&quot;&gt;sdfx&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/deadsy/sdfx&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/deadsy/sdfx/issues&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://godoc.org/github.com/deadsy/sdfx/sdf&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: MIT&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Go-based Code-CAD package that uses a signed distance functions (SDFs) kernel. Is capable of doing fillets and chamfering. The repo includes a &lt;a href=&quot;https://github.com/deadsy/sdfx/tree/master/obj&quot;&gt;standard-library&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://solidpython.readthedocs.io/en/latest/&quot;&gt;SolidPython&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/SolidCode/SolidPython&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://solidpython.readthedocs.io/en/latest/&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: GPL-2 or later&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Python-based library that wraps OpenScad, i.e. it outputs OpenScad code.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://www.gitlab.com/kavalogic-inc/tovero&quot;&gt;Tovero&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.gitlab.com/kavalogic-inc/tovero&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://gitlab.com/kavalogic-inc/tovero/-/issues&quot;&gt;Community&lt;/a&gt; (Gitlab Issues)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://gitlab.com/kavalogic-inc/tovero/-/blob/master/README&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: LGPL-2.1 or later and GPL-2 or later&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Tovero is a binding of Libfive to Common Lisp, including a standalone REPL-based viewer. Tovero can be integrated with &lt;a href=&quot;https://www.gitlab.com/kavalogic-inc/clive&quot;&gt;Clive&lt;/a&gt;, a Common Lisp scene graph and 3D GUI, to build more complex modelling applications.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Node editors / other&lt;/h2&gt;&lt;p&gt;Not quiet Code-Cad, but they do embody much of the same thought process.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://www.blockscad3d.com&quot;&gt;BlocksCAD&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Look unmaintained.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/einsteinsworkshop/blockscad&quot;&gt;Repo&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.blockscad3d.com&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.blockscad3d.com/training-resources&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: GPL-3&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.blockscad3d.com&quot;&gt;Online edito&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;a href=&quot;https://github.com/DynamoDS/Dynamo&quot;&gt;Dynamo&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/DynamoDS/Dynamo&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://forum.dynamobim.com/&quot;&gt;Community&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://primer.dynamobim.org/&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: Apache 2.0&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Dynamo is, quite literally, what you make it. Working with Dynamo may include using the application, either in connection with other Autodesk software or not, engaging a Visual Programming process, or participating in a broad community of users and contributors. Works with &lt;a href=&quot;https://github.com/infeeeee/DynFreeCAD&quot;&gt;FreeCad&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://makecode.buildbee.com/&quot;&gt;MakeCode&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/buildbee/makecode&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://makecode.buildbee.com/&quot;&gt;Docs&lt;/a&gt; (tutorials)&lt;/li&gt;&lt;li&gt;License: MIT&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://makecode.buildbee.com/&quot;&gt;Online editor&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;MakeCode&apos;s block editor supplies many helpers that make it perfect for making functional 3d prints, for example, there are functions that help stack and layout parts, as well as fillet utils (called styled edges). It also has a fast hull function (called wrap shapes). MakeCode is sponsored by &lt;a href=&quot;https://buildbee.com&quot;&gt;BuildBee&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://github.com/nortikin/sverchok&quot;&gt;Sverchok&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/nortikin/sverchok&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://nikitron.cc.ua/sverch/html/main.html&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;License: GPL-3&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Add-on for blender. Sverchok is a powerful parametric tool for architects, allowing geometry to be programmed visually with nodes.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Something I missed? Open an issue on &lt;a href=&quot;https://github.com/Irev-Dev/curated-code-cad&quot;&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Testing netlify identity functions within RedWoodJS]]></title><description><![CDATA[<p>So your working on a RedWood app and have opted to use netlify identity. That's great, allowing netlify to do the heavy lifting of your Auth, while still letting you hook into particular events by triggering "identity events". This is covered a little in the <span>RBAC cookbook</span>, but here I want to talk specifically about using these events to write to your db, such as creating a new user. Here's an example.</p>]]></description><link>https://kurthutten.com/blog/redwoodjs-netilfy-identity-functions</link><guid isPermaLink="false">-83de3b42-5bf4-5cd6-b96d-82d414cb51f1</guid><pubDate>Tue, 20 Oct 2020 13:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;So your working on a RedWood app and have opted to use netlify identity. That&apos;s great, allowing netlify to do the heavy lifting of your Auth, while still letting you hook into particular events by triggering &quot;identity events&quot;. This is covered a little in the &lt;a href=&quot;https://redwoodjs.com/cookbook/role-based-access-control-rbac#how-to-default-roles-on-signup-using-netlify-identity-triggers&quot;&gt;RBAC cookbook&lt;/a&gt;, but here I want to talk specifically about using these events to write to your db, such as creating a new user. Here&apos;s an example.&lt;br&gt;&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;// src/functions/identity-signup.js
import { createUser } from &apos;src/services/users/users.js&apos;
export const handler = async (req, _context) =&amp;gt; {
  const body = JSON.parse(req.body)
  const user = body.user
  const email = user.email
  if (body.event === &apos;signup&apos;) {
    await createUser({input: {
      email,
      bio: &apos;default bio&apos;
    }})
  }
  return { statusCode: 200 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&apos;re importing one of our services, but of course db can be imported directly. Now that we&apos;re ready to test our code we want to avoid deploying for the sole purpose of testing it, because even if you have a staging environment setup where test data can be created, it&apos;s still a slow development cycle and burns up build minutes.&lt;br&gt;&lt;br&gt;First a point of clarification, by testing, I mean manual verification checks done while in development, not automated tests, though some of this information could be leveraged for that too.&lt;br&gt;&lt;br&gt;We can use netlify&apos;s cli to invoke these functions. Detailed information is on the &lt;a href=&quot;https://community.netlify.com/t/new-netlify-functions-invoke-command-in-cli/2270&quot;&gt;netlify community forum&lt;/a&gt;, but in short the steps are:&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;sh&quot;&gt;# Install the cli
yarn add netlify-cli -g

# Rebuild api after your last changes to /functions
yarn rw build api

# Invoke your function with the CLI, pointing it to the rw dev port
netlify functions:invoke identity-signup --port 8910&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Netlify will invoke the function and include relevant test data as if it were being called in production. For example here&apos;s the body it passed through.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;{
  event: &apos;signup&apos;,
  user: {
    id: &apos;1111a1a1-a11a-1111-aa11-aaa11111a11a&apos;,
    aud: &apos;&apos;,
    role: &apos;&apos;,
    email: &apos;foo@trust-this-company.com&apos;,
    app_metadata: { provider: &apos;email&apos; },
    user_metadata: { full_name: &apos;Test Person&apos; },
    created_at: &apos;2020-10-21T09:43:23.992Z&apos;,
    update_at: &apos;2020-10-21T09:43:23.992Z&apos;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The reason you need to rebuild the api after each change is because the netlify-cli uses your netlify.toml to determine where the functions are, and this should point to your build directory `functions = &quot;api/dist/functions&quot;`&lt;/p&gt;&lt;p&gt;You should now see changes you expect to data in your local db.&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[bending time to our will]]></title><description><![CDATA[<div><p>What if I told you that the clock was alive, and has to not only the power but also the motive to:</p><ul><li>shrink the oceans</li><li>pull down the heavens</li><li>make lots of money</li><li>and perhaps, stab you in the back</li></ul><p></p><p>We're going to use the clock today to learn something about emerging technologies. The clock is useful because it's origin is unknown to most, which allows us to reflect upon it with fresh eyes, without bias. They say that retrospect is 20/20, but that's not always accurate depending on the context, if history is a road with many forks, where a single path is taken at each split. Then reflecting back give us the illusion of a single path since we can no longer see all the forks. Take a well known piece of transformative technology such as </p></div>]]></description><link>https://kurthutten.com/blog/bending-time-to-our-will</link><guid isPermaLink="false">-efb69297-dac5-5c51-9abc-c0cebfcd4a88</guid><pubDate>Tue, 06 Oct 2020 13:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;What if I told you that humble clock was alive, and has not only the power but also the motive to:&lt;/p&gt;&lt;p&gt;- shrink the oceans&lt;/p&gt;&lt;p&gt;- pull down the heavens&lt;/p&gt;&lt;p&gt;- make lots of money&lt;/p&gt;&lt;p&gt;- and perhaps, stab you in the back&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;I&apos;m referring to the clock generally, but the device that I&apos;m holding is very much a product of the maker movement. It&apos;s 3D printed, the creator put this out for free, and one thing I appreciate is he&apos;s used his page to draw attention to horology - the craft of clock making.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The reality is though that in the context of today this is nothing more than a toy. It&apos;s good to have some reminders of the past around us. But the device alone can&apos;t tell you its tale.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;We&apos;re going to use the clock today to learn something about emerging technologies. The clock is useful because it&apos;s origin is unknown to most, which allows us to reflect upon it with fresh eyes, without bias. They say that retrospect is 20/20, but that&apos;s not always accurate depending on the context, if history is a road with many forks, where a single path is taken at each split. Then, reflecting back gives us the illusion of a single path since we can no longer see all the forks. Take a well-known piece of transformative technology, such as Gutenberg&apos;s press. You&apos;re probably not an expert on the matter, but likely know that by making text and books easily and cheaply reproducible, the printing press led to an explosion of ideas impacting many parts of European society and then the world. Because it&apos;s better known, it easily provokes a well-trodden narrative about the technology, like the one I quickly mentioned. Such a narrative makes it hard for us to imagine other possibilities for the technology and makes it feel like both the invention and its subsequent impact was inevitable and I would argue that&apos;s not the case. For one thing printing presses already existed before Gutenberg&apos;s improvements and there were many cultural forces at play that contributed to the impact that the press had. Plus the impact it would have was not predicted at the time.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Back to clocks, how did this tech shape society? The full history of all time-related devices such as sundials and hours glasses goes way back. Though a watershed moment for clocks was the invention of the escapement, which is the mechanism in the clock that gives it it&apos;s precise ticking.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;What&apos;s important, though, is who invented it and what their motivation was. By who I mean which group of people as we don&apos;t know the individual, instead we know it was twelfth-century Benedict monks trying to improve their timekeeping as they had time allotments for many things that made up life in the monastery. Being monks, times of devotion was significant to them, and in fact, they had seven periods of devotion a day. It&apos;s understandable that better precision in timekeeping would help with their devotions, rather than relying upon one of the monks ringing a bell multiple times a day to prompt their rituals.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Once the escapement, hence the clock hand, had been invented it soon &quot;escapemented&quot; the monastery walls and by the mid-fourteenth century there were many &quot;town&quot; or &quot;public&quot; clocks. The roll-out of the clock did not only increase the precision of timekeeping but changed the notion of time itself. Conical hours was the time kept by the church and it was pretty rough because it never needed to be exact. For example, vigil was from about 2 am to dawn, matin was just a later portion of vigil, lauds was dawn and so obviously would vary with the seasons and so on and so forth. Now with an exact measurement of time possible, suddenly time was no longer a sequence of events that followed on from one another but instead, a resource that should be spent carefully. A sign of this is what was engraved on many of the early time-pieces like &quot;time is short&quot;, &quot;wasting time&quot; etc. Phrases we&apos;re familiar with, but we don&apos;t feel the need to write onto our clocks. Such a change in society&apos;s understanding of time, obviously had social consequences.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;But first who pushed for these &quot;town&quot; clocks? It was the merchant class, as timekeeping fit in well with business, they also enjoyed that the new time standards gave separation from the church. It&apos;s not surprising that Italian cities like Venice and Genoa saw the largest boom in clocks since they were famous for trade, and the lives of businesspeople became more regimented than ever.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The church wasn&apos;t happy about this. One simple reason is that there were now conflicts in the ringing of bells between the church&apos;s time and the town clock&apos;s time. As the merchant class gained influence and secular life grew, more often than not the church was forced to change its bell timing, not the reverse. There were more theological reasons why the church didn&apos;t like the advent of the clock. They were concerned with the public&apos;s new conception of time as a challenge to their faith, as time previously was thought to be part of nature and so belonged to God. The new, more abstract view of time changed that, so now it belonged to the individual, as merchants started to sell their time directly in hour increments instead of days, it was seen as selling something that belonged to God. The church fought the clock, but ceded ground and the clock is here to stay.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The clock became a force for controlling and synchronizing the actions of workers. By introducing regular work hours, it also helped pave the way for the industrial revolution and the rise of capitalism centuries later.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;To bring us back to the silly thought that the clock is alive and has its own motivations. I&apos;m not serious about the alive part obviously, but it is accurate that technologies have particular bias embedded in them that can exploit characteristics of the culture they find themselves in, often against the best wishes of the inventor. Devote monks set out to produce a device that would help their spiritual pursuits and ended up, instead being most efficient at aiding in monetary activities.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;At the same time, the clock shrunk oceans by aiding in navigation, pulled down the heavens with its uses in astronomy, made money for the rising merchant class, and it betrayed its original inventors by reducing spiritual life.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;During the Reformation, a period that saw the catholic church lose its monopoly status over spiritual life in Europe, Martin Luther, a prominent figure in these events, understood what having a bible in every home would mean for the catholic church&apos;s power, as, if everyone could read God&apos;s word for themselves they wouldn&apos;t need the churches hierarchies to be the arbiters of spiritual truth. That is to say, Luther understood and exploited an inherent bias in the technology of the printing press.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Which brings us back to Gutenberg, a devote catholic, who would have turned in his grave if he learned the way Martin Luther was using his device. Another example of technology betraying its creator.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;It seems that currently there are only two, polarised views on tech. There&apos;s a camp that is extremely critical and spends all their time imagining dystopias, and the other that equates technical progress with human progress.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;We need a more complex and nuanced view of tech, as we are currently facing a big problem with misinformation and there&apos;s a comparison to be made between the printing press and the internet. After the printing press was invented and dramatically reduced the cost of information, there was a lot of tension between different religious factions (Catholic and protestant). It started with violent language and eventually, outright violence, in fact about 130 years of it. With the advent of the internet just a handful of decades ago, we&apos;ve seen a dramatic reduction in the cost of information and also rising political tensions with lots of violent language. I sure hope that we don&apos;t have another 130 years of violence, but I&apos;m not terribly hopeful given the kinds of solutions that are being proposed. Take Twitter&apos;s &quot;bird watch&quot; program, which is a bandaid solution if I&apos;ve ever seen one. It seems very little people are actually proposing to ban some of the root causes, which seem to be recommendation algorithms that optimise for engagement but have the side effect of promoting false information.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;That&apos;s a massive topic so I&apos;ll stop there, but I&apos;d recommend a talk by Niall Ferguson https://www.youtube.com/watch?v=07KKYostAJ0&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;As a closing statement, I&apos;d suggest you try and get less screen time. Remember that there are whole teams of engineers trying to get you to spend more time staring at your device, and it&apos;s not in your best interest.&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[OpenScad Review - Is it worth learning?]]></title><description><![CDATA[<p>OpenScad is a CAD or computer-aided-design package. In a nutshell, it's software for making 3d models that are dimensional and mechanical in nature, as opposed 3d graphics software like blender, which is better suited for artistic endeavours like assets for a game, as an example. Since OpenScad is cad software, it's amongst packages like fusion 360, Inventor, Freecad, Onshape and many more. The point of difference though for OpenScad, is that it's all programmed. Every part of the model comes from text you write, and the GUI part of the software is only there to inspect your model.</p>]]></description><link>https://kurthutten.com/blog/openscad-review</link><guid isPermaLink="false">-9158794b-05c4-5b24-a16d-95debe88bc0b</guid><pubDate>Sat, 05 Sep 2020 14:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;This is an:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Overview of &lt;a href=&quot;https://www.openscad.org/downloads.html&quot;&gt;OpenScad&lt;/a&gt; and where it fits in with other cad packages.&lt;/li&gt;&lt;li&gt;What&apos;s it good for and not so good for,&lt;/li&gt;&lt;li&gt;And whether I think it&apos;s worth learning.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br&gt;Checkout out the video version here:&lt;/p&gt;&lt;a href=&quot;https://www.youtube.com/embed/_uVCD7_j-L8&quot;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;OpenScad is a CAD or computer-aided-design package. In a nutshell, it&apos;s software for making 3d models that are dimensional and mechanical in nature, as opposed 3d graphics software like blender, which is better suited for artistic endeavours like assets for a game, as an example.&lt;/p&gt;&lt;p&gt;Since OpenScad is cad software, it&apos;s amongst packages like fusion 360, Inventor, Freecad, Onshape and many more. The point of difference though for OpenScad, is that it&apos;s all programmed. Every part of the model comes from text you write, and the GUI part of the software is only there to inspect your model.&lt;/p&gt;&lt;p&gt;This is worlds apart from the click and drag nature of the other packages. The programmed nature of OpenScad gives it some unique advantages that have resulted in somewhat of a cult following for the software, but also some severe drawbacks.&lt;/p&gt;&lt;h2&gt;Let&apos;s start with its pros.&lt;/h2&gt;&lt;h4&gt;&lt;br&gt;1) It&apos;s Opensource&lt;/h4&gt;&lt;p&gt;OpenSCAD is free, open-source software that you can download and run on your machine and do whatever you want with. You don&apos;t need an account; there&apos;s no vendor lock-in or licensing issues. Also OpenScad files are likely very robust and will continue for work for many years.&lt;/p&gt;&lt;h4&gt;2) Community libraries&lt;/h4&gt;&lt;p&gt;Being opensource means there&apos;s a great open-source community with many libraries available that can help make you more productive or open up new possibilities.&lt;/p&gt;&lt;h4&gt;3) Fostering community &lt;/h4&gt;&lt;p&gt;Parts being 100% code is a significant benefit to the community, as hosting code on Github is a very robust way of making community contributions, as it goes through the same process software would, without awkwardly trying to pass around save files when changes are made.&lt;/p&gt;&lt;h4&gt;4) It Hits a niche&lt;/h4&gt;&lt;p&gt;OpenSCAD is very intuitive for those that come from a programming background.&lt;/p&gt;&lt;h4&gt;5) Power of a programming language &lt;/h4&gt;&lt;p&gt;There are some creative and straight-up wacky things you can do when you have a programming language at your disposal that otherwise wouldn&apos;t be possible. Also because it&apos;s all code, there are creative ways that it can be fed into a pipeline. A real-world example is &lt;a href=&quot;https://makerware.thingiverse.com/apps/customizer&quot;&gt;Thingiverse&apos;s customizer&lt;/a&gt; that allows you to edit variables for Openscad models uploaded to the site and download the resulting STLs.&lt;/p&gt;&lt;h4&gt;6) Parametric to the core&lt;/h4&gt;&lt;p&gt;Most cad packages these days are parametric. Loosely that means that as they&apos;re modelled, they&apos;re given dimensions along the way that can be changed later and have the model adjust itself. An example might be a bearing housing that&apos;s made for a particular sized bearing, if you want to bump up the size by a few millimetres it shouldn&apos;t be a problem, but as you can imagine there would be limits to this. If the bearing is made twice the size of the rest of the model, then the resulting shape is probably not going to work if it&apos;s not out-right broken. This is true for OpenScad too, though, with the power of a programming language, you&apos;re able to put whatever logic into the model to help it deal with extreme values if that&apos;s needed. Going so far to completely re-arrange the model to make things work if need be.&lt;/p&gt;&lt;h4&gt;7) Beginner-friendly language&lt;/h4&gt;&lt;p&gt;The language is fairly simple and therefore easy to pick up. This makes it a friendly first programming language for those looking to learn. Learning a skill that can outlive your use of the software is a great side-benefit. And the visual nature of the code results in rapid and satisfying feedback. Couple that with the fact you&apos;re making solid models that can be 3d printed and you get an experience that&apos;s tangible, I mean physically tangible in ways that other languages can&apos;t compete with. That makes it particularly compelling for getting kids into programming, which I think is fantastic.&lt;/p&gt;&lt;h2&gt;Now for the cons:&lt;/h2&gt;&lt;h4&gt;1) Unintuitive CAD interface&lt;/h4&gt;&lt;p&gt;The fact is that for most people, coding up a part is not intuitive, because 3d modelling is inherently very visual, a graphical interface is better, at least for getting started.&lt;/p&gt;&lt;h4&gt;2) Complex shapes can be challenging&lt;/h4&gt;&lt;p&gt;This largely stems from the fact that the core process of making parts in OpenScad is with boolean operations of primitive shapes, that is union, difference and intersection. This paradigm of modelling doesn&apos;t lend itself particularly well to smooth transitions, flowing curves or even something trivial in other packages like adding a fillet is difficult. This can be somewhat alleviated with the help of other libraries, but it&apos;s still a firm con of the software.&lt;/p&gt;&lt;h4&gt;3) Time-consuming&lt;/h4&gt;&lt;p&gt;This follows on from the last point as they are connected, the difficulty of modelling complex parts means more tinkering is needed to get what you what.&lt;/p&gt;&lt;h4&gt;4) The language is limiting&lt;/h4&gt;&lt;p&gt;It might seem ironic that I&apos;m now criticising the language since I put it in the pro list for being easy to pick up, but that&apos;s the flip side of its simple nature. OpenScad is it&apos;s own language and generally, I think rolling your own language for a project is treacherous. On the one hand, you might be able to design the language to meet the specific needs of the project very well. But on the other hand, you have to re-invent the wheel for so many things that are common to all programming languages. Effectively, the project is building two things at once that it has to get right, their software and a language. Further, if it had instead been built on top of another language then it would also be possible for users to take advantage of libraries that already exist, say math libraries. In fairness, if the project had been built on another language, it would likely make it less beginner-friendly. Because even if beginners only needed to know a small subset of the language to get started, They could still easily get lost in all of the existing documentation for that language. I think my biggest problem with OpenScad rolling their own language is without a lot of resources it&apos;s inevitable that the language would be sub-par with what developers are used to. Sure OpenScad is bound to have fewer features than other languages, that&apos;s fine but even what has been implemented feels a little janky. Functions aren&apos;t intuitive and defining scoped variables within them feels janky. For-loops are weird too, and re-assigning variables doesn&apos;t cause errors but easily lead to bugs as it&apos;s behaviour is counter-intuitive.&lt;/p&gt;&lt;h4&gt;5) Lacking mature features&lt;/h4&gt;&lt;p&gt;There are features that OpenScad lacks like FEM or CAM. but I&apos;d label these as specialised features and not mature features, as it&apos;s not like every CAD package includes these, and it&apos;s reasonable for OpenScad to focus just the modelling stage of a part&apos;s lifecycle. My point is simply it&apos;s not a big deal these feature&apos;s aren&apos;t there, and I don&apos;t think OpenScad suffers because of it. What&apos;s more of a problem though is limited file format compatibility. OpenScad&apos;s lack of support for STEP files in particular, as they are an industry standard for manufacturing. If you want to move beyond 3d printing and start getting parts made with injection moulding, then you might need to redo parts in another package. From what I understand, STEP files are very difficult for OpenScad for &lt;a href=&quot;https://github.com/openscad/openscad/issues/893&quot;&gt;historic reasons&lt;/a&gt;. Something worth considering though under the theme of &quot;mature features&quot; is &quot;are there companies using OpenScad?&quot; Hard to say but I certainly don&apos;t know of many. Prusa did use OpenScad for their printers, in fact all the scad files for their mk2 printer are up on github. Though if you look at their latest printer&apos;s github (prusa-mini), the files are all .ipt or inventor files. That doesn&apos;t mean they never use OpenScad now, but it is a sign that they&apos;ve found it prudent to move away from it somewhat.&lt;/p&gt;&lt;h2&gt;Closing thoughts&lt;/h2&gt;&lt;p&gt;With all of that out of the way, my answer to the question of should you learn OpenSCAD my short answer is yes, and my long answer is it depends, and the best way for me to break down the long answer is to propose some scenarios.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;If you are a programmer then go ahead, you&apos;ll probably enjoy it.&lt;/li&gt;&lt;li&gt;If you are interested in learning to code, then OpenScad is a pretty gentle introduction. There are plenty of programming concepts that you will never learn in OpenScad, but they can come later with the next language.&lt;/li&gt;&lt;li&gt;If you want to make parts that are very robust to changing parameters and want to be able to host it on Thingiverse, where users can put in their own parameters, then OpenSCAD is the only option available.&lt;/li&gt;&lt;li&gt;If you like the concept of OpenSCAD, whether that&apos;s the code, fostering community contributions, or lack of vendor lock-in, and you are making parts to be 3d printed, then OpenSCAD is a solid choice.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;However, if you need to be productive today, or need to support STEP files for manufacturing, or need to make very complex shapes with smooth flowing meshes, then you&apos;ll be battling against OpenScad to get it to do what you need and would be a poor choice.&lt;/p&gt;&lt;p&gt;Some other things to consider before you make your choice are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;If the lack of vendor lock in, and opensource nature is appealing to you, then you could always consider other open-source CAD packages like FreeCad.&lt;/li&gt;&lt;li&gt;If you love the idea of programming-CAD, while OpenScad is the most popular, there are alternatives. &lt;a href=&quot;https://solidpython.readthedocs.io/en/latest/&quot;&gt;SolidPython&lt;/a&gt; is a python wrapper for OpenSCAD, or one of my favourites is &lt;a href=&quot;https://openjscad.org/&quot;&gt;jsCAD&lt;/a&gt;. It&apos;s implemented in javascript, and models can be made right in the browser.&lt;/li&gt;&lt;li&gt;If you&apos;re worried about what I said about it being difficult to make complex shapes, have a look at libraries that are available first, as it should give you a good idea of the kinds of parts you can produce. The libraries officially &lt;a href=&quot;https://www.openscad.org/libraries.html&quot;&gt;recommended by OpenScad&lt;/a&gt; are an excellent place to start, but if I may, I&apos;ll plug my library, &lt;a href=&quot;https://kurthutten.com/blog/round-anything-a-pragmatic-approach-to-openscad-design&quot;&gt;Round-Anything&lt;/a&gt;. The library has a variety of features, but what I think is of most significance is offering a way to add rounding to polygons. The result is a move away from the &quot;boolean with primitives&quot; paradigm, towards the sketch and extrude paradigm, which is, in my opinion, the workhorse of more traditional cad packages.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;One interesting take-away from everything I&apos;ve said thus far is that even with some pretty heavy criticism of the software, I still recommend it for a variety of situations. This speaks to how well OpenScad fills a niche, and I think that means that the software will continue to be used and loved for many years to come.&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Round-Anything API]]></title><description><![CDATA[<div><p>Below is a detailed look at the full round-anything API with examples. To read more about the motivation behind the library click here.</p><p>minkowskiRound</p><p>This module will round its children. External and internal radii can be defined separately. The syntax is:</p></div>]]></description><link>https://kurthutten.com/blog/round-anything-api</link><guid isPermaLink="false">-7c5a8e06-9fbf-5613-b471-57f8b95951ce</guid><pubDate>Sat, 01 Aug 2020 14:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;This is a deep dive into the &lt;a href=&quot;https://github.com/Irev-Dev/Round-Anything&quot;&gt;Round-Anything library&lt;/a&gt; API for OpenScad. &lt;br&gt;For a general overview of features and how to get started and the motivation behind the library, see the &lt;a href=&quot;https://kurthutten.com/blog/round-anything-a-pragmatic-approach-to-openscad-design&quot;&gt;written overview&lt;/a&gt; or the video overview below.&lt;/p&gt;&lt;a href=&quot;https://www.youtube.com/embed/laxv2wFKq8Q&quot;&gt;&lt;/a&gt;&lt;h3&gt;Quick side-note&lt;/h3&gt;&lt;p&gt;I&apos;m currently working on a community website for &quot;Code-CAD&quot; (like OpenSCAD). A good way to think of it is codepen crossed with a thing repository. You can check it out at &lt;a href=&quot;https://cadhub.xyz/&quot;&gt;cadhub.xyz&lt;/a&gt; or it&apos;s &lt;a href=&quot;https://github.com/Irev-Dev/cadhub&quot;&gt;repo&lt;/a&gt;.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h2&gt;MinkowskiRound&lt;/h2&gt;&lt;p&gt;This module will round its children. External and internal radii can be defined separately. The syntax is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;minkowskiRound(OR, IR, enable, boundingEnvelope) {
  // children
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;OR&lt;/strong&gt;: Set the external radius&lt;br&gt;&lt;strong&gt;IR&lt;/strong&gt;: Set the internal radius&lt;br&gt;&lt;strong&gt;enable&lt;/strong&gt;: Toggle whether the rounding enabled, as the module is computationally expensive, so it&apos;s convenient to have an easy way to disable it.&lt;br&gt;&lt;strong&gt;boundingEnvelope&lt;/strong&gt;: An array with three values which should be large enough to capture the children&lt;/p&gt;&lt;p&gt;Because this module will round anything after-the-fact, it&apos;s useful adding radii to complex shapes that would be very difficult to do otherwise. Such as the curving internal edge of this cube-cylinder union.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/a6da59a4fd1c925f68e19e7865c24f1fda140640-1215x1454.png&quot;&gt;&lt;pre&gt;&lt;code&gt;$fn=20;
minkowskiRound(0.7,1.5,1,[50,50,50])
  union(){
    cube([6,6,22]);
      rotate([30,45,10])
        cylinder(h=22,d=10);
  }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;h2&gt;polyRound&lt;/h2&gt;&lt;p&gt;Function for adding radii to any point of a polygon. Syntax is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;polygonArray = polyRound(radiipoints,fn,mode)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;radiiPoints&lt;/strong&gt;: nest array of [x, y, r] points. That is x-y coordinates and the radius for that point, .ie. [[x1, y1, r1],[x2, y2, r2] ...]&lt;br&gt;&lt;strong&gt;fn&lt;/strong&gt;: The amount of point each radius is subdivided.&lt;br&gt;&lt;strong&gt;mode&lt;/strong&gt;: Three different modes for handling conflicting radii:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Default, automatically reduces radii to stop conflicts.&lt;/li&gt;&lt;li&gt;Debugging mode, print reduced radii to the console.&lt;/li&gt;&lt;li&gt;Radii conflict resolution disabled.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The function only returns the polygon points, therefore it&apos;s typical to pair it with polygon and linear_extrude. Here&apos;s a simple example&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/e35b08cb58bbbe944370d12661041bf01192e837-1371x1567.png&quot;&gt;&lt;pre&gt;&lt;code&gt;radiiPoints=[[-4,0,1],[5,3,1.5],[0,7,0.1],[8,7,10],[20,20,0.8],[10,0,10]];
polygon(polyRound(radiiPoints,30));&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To further understand how the radii conflict resolution works here&apos;s an example of that.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/d72c25a7b622fa34a66ea1ddb631d5fddcb2c5ca-1888x1949.png&quot;&gt;&lt;pre&gt;&lt;code&gt;//example of radii conflict handling and debuging feature
function makeRadiiPoints(r1, r2)=[[0,0,0],[0,20,r1],[20,20,r1],[20,0,0]];

// the squre shape being 20 wide, two radii of 10 both fit into the shape (just)
translate([-25,0,0])polygon(polyRound(makeRadiiPoints(10,10),50));

//radii are too large and are reduced to fit and will be reduce to 10 and 10
translate([0,0,0])polygon(polyRound(makeRadiiPoints(30,30),50));

//radii are too large again and are reduced to fit, but keep their ratios r1 will go from 10 to 4 and r2 will go from 40 to 16
translate([25,0,0])polygon(polyRound(makeRadiiPoints(10,40),50));

//mode 2 = no radii limiting
translate([50,0,0])polygon(polyRound(makeRadiiPoints(15,20),50,mode=2));&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;See bellow for an even deeper dive into the logic behind it.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/1c8e7ba4f28b1d7d5d21ff26c2906d1e01d8c896-1013x1441.png&quot;&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;h2&gt;translateRadiiPoints&lt;/h2&gt;&lt;p&gt;Function for moving radii points to make their reuse easier. Syntax is:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;translatedRadiiPoints = translateRadiiPoints(radiiPoints, tran, rot);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;radiiPoints&lt;/strong&gt;: list of [x, y, r] points to be translated.&lt;br&gt;&lt;strong&gt;tran&lt;/strong&gt;: [x, y] translation points.&lt;br&gt;&lt;strong&gt;rot&lt;/strong&gt;: how to rotate the points on the z-axis.&lt;/p&gt;&lt;p&gt;Because the function returns radiiPoints, they still need to be passed to the polyRound function before used as a polygon. Though realistically points are used multiple times in one part hence the need to translate them while still in point form, and so would be combined with concat first. Here&apos;s a simple example.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/2b16e42399c6dddb7622423a0f894f174c372d94-1947x1885.png&quot;&gt;&lt;pre&gt;&lt;code&gt;nutW=5.5;   nutH=3; boltR=1.6;
minT=2;     minR=0.8;
function nutCapture(startAndEndRadius=0)=[
  [-boltR,        0,         startAndEndRadius],
  [-boltR,        minT,      0],
  [-nutW/2,       minT,      minR],
  [-nutW/2,       minT+nutH, minR],
  [nutW/2,        minT+nutH, minR],
  [nutW/2,        minT,      minR],
  [boltR,         minT,      0],
  [boltR,         0,         startAndEndRadius],
];
translate([-5,0,0])polygon(polyRound(nutCapture(),20));

negativeNutCapture=translateRadiiPoints(nutCapture(),tran=[5,0]);
rotatedNegativeNutCapture=translateRadiiPoints(nutCapture(1),tran=[20,5],rot=90);
aSquare=concat(
  [[0,0,0]],
  negativeNutCapture,
  [[20,0,0]],
  rotatedNegativeNutCapture,
  [[20,10,0]],
  [[0,10,0]]
);
polygon(polyRound(aSquare,20));&lt;/code&gt;&lt;/pre&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/6076451e66be1b183e12176f5dd1464be4e570e4-992x1481.png&quot;&gt;&lt;h2&gt;shell2d&lt;/h2&gt;&lt;p&gt;Module that will create a shell out of any 2d object. If given more than one child, the inside will be filled with the following children. Syntax is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;shell2d(offset1,offset2=0,minOR=0,minIR=0){
  // shell child
  // fill children
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;offset1, offset2&lt;/strong&gt;: Two offsets that together define the thickness of the shell and are measured relative to the perimeter of the original 2d shape. Negative value go towards the centre of the shape, positive value go away.&lt;br&gt;&lt;strong&gt;minOR, minIR&lt;/strong&gt;: minimum radii can be defined, if you&apos;re using this in conjunction with ployRound they can be ignored for the most part.&lt;/p&gt;&lt;p&gt;Here&apos;s a simple example.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/ec4943e1084296b7121c3aa1ec2ac1e12dfdc62b-1324x1254.png&quot;&gt;&lt;pre&gt;&lt;code&gt;radiiPoints=[[-4,0,1],[5,3,1.5],[0,7,0.1],[8,7,10],[20,20,0.8],[10,0,10]];
shell2d(-0.5)polygon(polyRound(radiiPoints,30));
translate([0,-10,0])shell2d(-0.5){
  polygon(polyRound(radiiPoints,30));
  translate([8,8])gridpattern(memberW = 0.3, sqW = 1, iter = 17, r = 0.2);
}&lt;/code&gt;&lt;/pre&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/51707e3ffc6d22f56b01dce6ee784322f6d93e29-992x1878.png&quot;&gt;&lt;h2&gt;beamChain&lt;/h2&gt;&lt;p&gt;This function takes a series of radii points and creats a beam of constant thickness with each pair of points. Syntax is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;radiiPoints = beamChain(radiiPoints, offset1, offset2, mode, minR, startAngle, endAngle)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;radiiPoints: radiiPoints&lt;/strong&gt;: list of [x, y, r] points. Note, negative radiuses only allowed for the first and last radii points.&lt;br&gt;&lt;strong&gt;offset1, offset2&lt;/strong&gt;: The two offsets that give the beam it&apos;s thickness. When using with mode=2 only offset1 is needed as there is no return path for the polygon.&lt;br&gt;&lt;strong&gt;minR&lt;/strong&gt;: Min radius, if all of your radii are set properly within the radii points this value can be ignored&lt;br&gt;&lt;strong&gt;startAngle, endAngle&lt;/strong&gt;: Angle at each end of the beam, different mode determine if this angle is relative to the ending legs of the beam or absolute.&lt;br&gt;&lt;strong&gt;mode&lt;/strong&gt;: Different modes for how the end angles are handled and if the return path of the beam polygon in included.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;StartAngle and endAngle are relative to the angle of the last two points and equal 90deg if not defined.&lt;/li&gt;&lt;li&gt;Only the forward path is defined, useful for combining the beam with other radii points, see examples for a use-case.&lt;/li&gt;&lt;li&gt;StartAngle and endAngle are absolute from the x axis and are 0 if not defined.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This function is very flexible in how it&apos;s used so below are a series of examples increasing complexity.&lt;/p&gt;&lt;p&gt;The first shows how a series of points can form the bean chain, how radii can be added and adding thickness to the beams&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/c4c4496ef7bcda6c2cc44ff86ba583e71f37ebcd-1221x1935.png&quot;&gt;&lt;p&gt;The second example shows adding a angle and filleting radius to the end of the beams&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/3b05d926517bff74657d6d85f95ae83b28feac2d-1221x1889.png&quot;&gt;&lt;p&gt;Lastly this example show how seperating the beams polygong path into forward and return paths can be used to add extra polgon points at the beam. The advantage of this over regular union is adding a transitioning radius between the beam and the extra points.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/a4cf1d4b8809c348a017cba331b4a71c867774db-1360x2282.png&quot;&gt;&lt;pre&gt;&lt;code&gt;function beamPoints(r1,r2,rStart=0,rEnd=0)=[[0,0,rStart],[2,8,0],[5,4,r1],[15,10,r2],[17,2,rEnd]];

// chained lines by themselves
translate(){
  radiiPoints=beamPoints(0,0);
  for(i=[0: len(radiiPoints)]){color(&quot;red&quot;)translate([radiiPoints[i].x,radiiPoints[i].y,0])cylinder(d=0.2, h=1);}
  polygon(polyRound(beamChain(radiiPoints,offset1=0.02, offset2=-0.02),20));
}


// Add some radii to the line transitions
translate([0,-7,0]){
  radiiPoints=beamPoints(2,1);
  for(i=[0: len(ex3)]){color(&quot;red&quot;)translate([radiiPoints[i].x,radiiPoints[i].y,0])cylinder(d=0.2, h=1);}
  polygon(polyRound(beamChain(radiiPoints,offset1=0.02, offset2=-0.02),20));
}

// Give make the lines beams with some thickness
translate([0,-7*2,0]){
  radiiPoints=beamPoints(2,1);
  polygon(polyRound(beamChain(radiiPoints,offset1=0.5, offset2=-0.5),20));
}

// Add an angle to the start of the beam
translate([0,-7*3,0]){
  radiiPoints=beamPoints(2,1);
  polygon(polyRound(beamChain(radiiPoints,offset1=0.5, offset2=-0.5, startAngle=45),20));
}

// Put a negative radius at the start for transationing to a flat surface
translate([0,-7*4,0]){
  radiiPoints=beamPoints(2,1,rStart=-0.7);
  polygon(polyRound(beamChain(radiiPoints,offset1=0.5, offset2=-0.5, startAngle=45),20));
}

// Define more points for a polygon to be atteched to the end of the beam chain
clipP=[[16,1.2,0],[16,0,0],[16.5,0,0],[16.5,1,0.2],[17.5,1,0.2],[17.5,0,0],[18,0,0],[18,1.2,0]];
translate([-15,-7*5+3,0]){
  for(i=[0:len(clipP)-1]){color(&quot;red&quot;)translate([clipP[i].x,clipP[i].y,0])cylinder(d=0.2, h=1);}
  polygon(polyRound(clipP,20));
}

// Attached to the end of the beam chain by dividing the beam paths in forward and return and
// concat other polygon inbetween
translate([0,-7*6,0]){
  radiiPoints=beamPoints(2,1);
  forwardPath=beamChain(radiiPoints,offset1=0.5,startAngle=-15,mode=2);
  returnPath=revList(beamChain(radiiPoints,offset1=-0.5,startAngle=-15,mode=2));
  entirePath=concat(forwardPath,clipP,returnPath);
  polygon(polyRound(entirePath,20));
}

// Add transitioning radii into the end polygong
translate([0,-7*7-2,0]){
  radiiPoints=beamPoints(2,1,rEnd=3);
  forwardPath=beamChain(radiiPoints,offset1=0.5,startAngle=-15,mode=2);
  returnPath=revList(beamChain(radiiPoints,offset1=-0.5,startAngle=-15,mode=2));
  entirePath=concat(forwardPath,clipP,returnPath);
  polygon(polyRound(entirePath,20));
}

// Define multiple shells from the the one set of points
translate([0,-7*9,0]){
  for(i=[0:2]){polygon(polyRound(beamChain(ex3,offset1=-1+i*0.4, offset2=-1+i*0.4+0.25),20));}
}&lt;/code&gt;&lt;/pre&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/893d5e7630711fab6453162c6b794f34a22844f0-1238x2148.png&quot;&gt;&lt;h2&gt;mirrorPoints&lt;/h2&gt;&lt;p&gt;Function for mirroring radiiPoints. The advantage of this over other mirror techniques is when using radii points it allows for adding a radius to the transition of the two halves. syntax is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;mirroredRadiiPoinst = mirrorPoints(radiiPoints, rot, endAttenuation)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;radiiPoints&lt;/strong&gt;: radiiPoints: list of [x, y, r] points.&lt;br&gt;&lt;strong&gt;rot&lt;/strong&gt;: angle of rotation.&lt;br&gt;&lt;strong&gt;endAttenuation&lt;/strong&gt;: [start, end]. Amount of points to be removed from either end of the radiiPoints. Its purpose is to remove single points from the ends if they lie right on the mirror axis and would cause two points on top of each other.&lt;/p&gt;&lt;p&gt;Below is a simple example.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/9db0693353027201e514129e52a5c29e4fd5816b-1121x1723.png&quot;&gt;&lt;pre&gt;&lt;code&gt;centerRadius=7;
points=[[0,0,0],[2,8,0],[5,4,3],[15,10,0.5],[10,2,centerRadius]];
mirroredPoints2=mirrorPoints(points,0,[0,0]);
translate([0,-20,0])polygon(polyRound(mirroredPoints2,20));&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;polyRoundExtrude&lt;/h2&gt;&lt;p&gt;Module for extruding a radiiPoints, where the top and the bottom of the extrusion can be rounded. syntax is:&lt;br&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;polyRoundExtrude(radiiPoints,length,r1,r2,fn,convexity)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;radiiPoints&lt;/strong&gt;: radiiPoints: list of [x, y, r] points.&lt;br&gt;&lt;strong&gt;length&lt;/strong&gt;: length of the extrusion.&lt;br&gt;&lt;strong&gt;r1, r2&lt;/strong&gt;: Start and end radii.&lt;br&gt;&lt;strong&gt;fn&lt;/strong&gt;: amount of subdivisions for forming the polyhedron.&lt;br&gt;&lt;strong&gt;convexity&lt;/strong&gt;: convexity of the underlying polyhedron.&lt;br&gt;&lt;br&gt;This module is very similar to &lt;strong&gt;extrudeWithRadius&lt;/strong&gt; in purpose, though by using radiiPoints directly instead of a generic 2d child it&apos;s abet to offer smoother curves in a more preformant manner, and so is recommended over &lt;strong&gt;extrudeWithRadius&lt;/strong&gt; where possible. Below is a simple example.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/cb5a2a5e4483e03f1a5d0e6216e3260b4ce6e256-1021x1652.png&quot;&gt;&lt;pre&gt;&lt;code&gt;radiiPoints=[[10,0,10],[20,20,1.1],[8,7,10],[0,7,0.3],[5,3,0.1],[-4,0,1]];
polyRoundExtrude(radiiPoints,2,0.5,-0.8,fn=50);&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;extrudeWithRadius&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Module for extruding a 2d child, with the ability to put a radius on each end of the extrusion. This module is similar to &lt;strong&gt;polyRoundExtrude&lt;/strong&gt;, though this module is more flexible as it&apos;s able to work on any 2d child, however it&apos;s less preformant. &lt;strong&gt;polyRoundExtrude&lt;/strong&gt; is recommended over &lt;strong&gt;extrudeWithRadius&lt;/strong&gt; if your use-case allows it.&lt;br&gt;Syntax is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;extrudeWithRadius(length,r1=0,r2=0,fn=30){
  // 2d child
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;length&lt;/strong&gt;: length of the extrusion.&lt;br&gt;&lt;strong&gt;r1, r2&lt;/strong&gt;: Start and end radii.&lt;br&gt;&lt;strong&gt;fn&lt;/strong&gt;: How much the radii are subdivided.&lt;/p&gt;&lt;p&gt;Below is a simple example.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/bd5abc0522181a4309a7e604b7e6a9ca88295937-1289x1675.png&quot;&gt;&lt;pre&gt;&lt;code&gt;radiiPoints=[[-4,0,1],[5,3,1.5],[0,7,0.1],[8,7,10],[20,20,0.8],[10,0,10]];
extrudeWithRadius(3,0.5,0.5,50)polygon(polyRound(radiiPoints,30));
#translate([7,4,3])extrudeWithRadius(3,-0.5,0.95,50)circle(1,$fn=30);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Round-Anything, a pragmatic approach to OpenSCAD design]]></title><description><![CDATA[<p>Round-Anything is primarily a set of OpenSCAD utilities that help with rounding parts, but it also embodies a robust approach to developing OpenSCAD parts. The heart of the library a list of polygon points, with a 3rd radius parameter at each point. That is a series of [x, y, radius] points.</p>]]></description><link>https://kurthutten.com/blog/round-anything-a-pragmatic-approach-to-openscad-design</link><guid isPermaLink="false">-b27eceb4-6467-5a81-b96e-9b072c96b81b</guid><pubDate>Wed, 22 Jul 2020 14:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/Irev-Dev/Round-Anything&quot;&gt;Round-Anything&lt;/a&gt; is primarily a set of OpenSCAD utilities that help with rounding parts, but it also embodies a robust approach to developing OpenSCAD parts. I built this library to solve some of my own struggles with rounding and OpenSCAD, and I&apos;ve been happy to see other&apos;s are finding it useful too.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/389662801d8158768c5028b6217071e164aeabaf-1724x1111.png&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;The Why&lt;/h2&gt;&lt;p&gt;The truth is radii, internal radii, in particular, can be a real pain to add in openscad. And the more you move away from shapes with 90 degree angles, the more difficult it becomes, effectively putting a complexity ceiling on parts you can produce in OpenScad. Because of how important radii in both making an appealing and strong part, reducing stress concentration etc. A library that focuses on radii as a core principle makes for a solid foundation for your parts. Furthermore, the heart of the library revolves around the polygon, this is because we&apos;re leveraging the battle-tested paradigm of extruding from 2d sketches of most CAD packages. I can&apos;t imagine making an OpenScad part without Round-Anything.&lt;/p&gt;&lt;p&gt;Below is an overview of the library&apos;s features, For documentation of its &lt;a href=&quot;https://kurthutten.com/blog/round-anything-api&quot;&gt;API click here&lt;/a&gt;.&lt;br&gt;There&apos;s also a &lt;a href=&quot;https://twitter.com/IrevDev/status/1292945044647731201&quot;&gt;timelapse of a part&lt;/a&gt; made using the library.&lt;/p&gt;&lt;p&gt;A video version of this article can be found below:&lt;br&gt;&lt;/p&gt;&lt;a href=&quot;https://www.youtube.com/embed/laxv2wFKq8Q&quot;&gt;&lt;/a&gt;&lt;h3&gt;Quick side-note&lt;/h3&gt;&lt;p&gt;I&apos;m currently working on a community website for &quot;Code-CAD&quot; (like OpenSCAD). A good way to think of it is codepen crossed with a thing repository. You can check it out at &lt;a href=&quot;https://cadhub.xyz/&quot;&gt;cadhub.xyz&lt;/a&gt; or it&apos;s &lt;a href=&quot;https://github.com/Irev-Dev/cadhub&quot;&gt;repo&lt;/a&gt;.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Features&lt;/h2&gt;&lt;h4&gt;Minkowski Round&lt;/h4&gt;&lt;p&gt;A power tool for rounding all edges of a design after the fact. Adjustable for external and internal radii.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/27a7709beb249fb2bf0fdee91490e9a0132ecef8-1275x1439.png&quot;&gt;&lt;h4&gt;PolyRound&lt;/h4&gt;&lt;p&gt;Gives the ability to round each point of a polygon.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/85138e25db3b32c5279dc9a89ae938afc52c2ea9-1371x1567.png&quot;&gt;&lt;h4&gt;Radius Conflict resolution&lt;/h4&gt;&lt;p&gt;Can automatically Shrink radii for any pair that are too close together and would cause a conflict, can be opted out of.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/ed08517192f2e7923ac40c861ac55870a3940992-1434x1775.png&quot;&gt;&lt;h4&gt;Point translation helpers&lt;/h4&gt;&lt;p&gt;Aids in the reuse of polygon of points. To keep a single source of truth and for editing them together.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/01c359347f2ae8c6f0ad09c96c6f057b6b4874bd-1093x1376.png&quot;&gt;&lt;h4&gt;2D-shell&lt;/h4&gt;&lt;p&gt;Self-explanatory, when combined with poly round, it gives a nice smooth transitioning shell.&lt;br&gt;&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/ea3a69019bfa96f338408a08430931adb1b00230-907x1080.png&quot;&gt;&lt;h4&gt;Beam chain&lt;/h4&gt;&lt;p&gt;Unlike a regular polygon, where the last point is joint to the first to complete the shape. Instead, we can treat each set of points as a beam without it wrapping at the end. We can round the transitions between the beams and combine with other points, to form more complex shapes.&lt;/p&gt;&lt;p&gt;An offset from the normal gives beams their thickness.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/e2c41bff8546a15c03b2aa07d49aeb49cb7ab5ee-1174x1333.png&quot;&gt;&lt;h4&gt;Mirror points&lt;/h4&gt;&lt;p&gt;For mirroring a set of points. The benefit of this over using the native OpenSCAD mirror transformation is the ability to put smooth radii along the mirrored plane.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/58666db7e41741222598337f6f65a43c80a01b3c-1081x1311.png&quot;&gt;&lt;h4&gt;polyRoundExtrude&lt;/h4&gt;&lt;p&gt;Allows for putting a radius on the end of extrusions when using polygons. A negative vaule will flare the end out for a transition to flat.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/66b6ab670a1b6042064c665a324181a361c601f5-841x1333.png&quot;&gt;&lt;h4&gt;&lt;br&gt;Radius extrude&lt;/h4&gt;&lt;p&gt;Similar to the above but will work with any 2d shape, though uses a more hacky and less proformant method.&lt;/p&gt;&lt;img src=&quot;https://cdn.sanity.io/images/2hqqc7om/production/7cad5623aabe989e39181d6d86f63931bbc6b123-841x1448.png&quot;&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Deep AST matching guide]]></title><description><![CDATA[<div><p>So you want to take advantage of the javascript AST, and move beyond elemental use cases, (e.g. finding string concatenation in order to convert to template literals).</p><p>The technique well go over is match more than a single node, while keeping track of where we are within the AST as we traverse it. As a simple example, let's say you want to match function-returns where the return is an object with a key called `coolKey`, but only if the function name also starts with `cool`. We're going to need a rough understanding of the structure of the AST, and how AST traversing libraries move through the tree structure.</p><p>Firstly the structure of the AST. For any given thing you want to match on, you can simply paste your code into <a href="https://astexplorer.net/">astexplorer.net</a> and inspect it, though something to keep in mind is that it's generally safe to assume that the AST will follow the same rough structure as your code indentation. We use indentation as a way to visually represent hierarchy of the code, and the AST has a similar hierarchy (though with a great deal many more levels). They are similar enough to know that the return statement of a function is going to be nested further in the structure than the function definition.</p><p>Now AST libraries. These generally take callbacks for entering and existing nodes, though in what order? How do they pick the next node to enter? Since most folks will be familiar XML style tree structure, let's use that as an example.</p></div>]]></description><link>https://kurthutten.com/blog/deep-ast-matching</link><guid isPermaLink="false">-e999c472-13dc-54f4-b57f-87dd5bcf489e</guid><pubDate>Wed, 08 Jan 2020 13:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;So you want to take advantage of the javascript AST, and move beyond elemental use cases, (e.g. finding string concatenation in order to convert to template literals).&lt;/p&gt;&lt;p&gt;The technique well go over is match more than a single node, while keeping track of where we are within the AST as we traverse it. As a simple example, let&apos;s say you want to match function-returns where the return is an object with a key called `coolKey`, but only if the function name also starts with `cool`. We&apos;re going to need a rough understanding of the structure of the AST, and how AST traversing libraries move through the tree structure.&lt;/p&gt;&lt;p&gt;Firstly the structure of the AST. For any given thing you want to match on, you can simply paste your code into &lt;a href=&quot;https://astexplorer.net/&quot;&gt;astexplorer.net&lt;/a&gt; and inspect it, though something to keep in mind is that it&apos;s generally safe to assume that the AST will follow the same rough structure as your code indentation. We use indentation as a way to visually represent hierarchy of the code, and the AST has a similar hierarchy (though with a great deal many more levels). They are similar enough to know that the return statement of a function is going to be nested further in the structure than the function definition.&lt;/p&gt;&lt;p&gt;Now AST libraries. These generally take callbacks for entering and existing nodes, though in what order? How do they pick the next node to enter? Since most folks will be familiar XML style tree structure, let&apos;s use that as an example.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;html&quot;&gt;```
&amp;lt;body&amp;gt;
    &amp;lt;node1&amp;gt;
        &amp;lt;node2&amp;gt;
            &amp;lt;node3&amp;gt;
                &amp;lt;node4&amp;gt;
                &amp;lt;/node4&amp;gt;
            &amp;lt;/node3&amp;gt;
        &amp;lt;/node2&amp;gt;
        &amp;lt;node5&amp;gt;
        &amp;lt;/node5&amp;gt;
    &amp;lt;/node1&amp;gt;
    &amp;lt;node6&amp;gt;
    &amp;lt;/node6&amp;gt;
    &amp;lt;node7&amp;gt;
    &amp;lt;/node7&amp;gt;
&amp;lt;/body&amp;gt;
```&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It will call enter and leave nodes in the same order as if you were reading the above line-by-line. Or how I like to think of it is, once it enters a node, it will fully explore everything nested in it before it leaves that node. To be 100% explicit the order would be.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;text&quot;&gt;enter-node1, enter-node2, enter-node3,
enter-node4, leave-node4, leave-node3,
leave-node2, enter-node5, leave-node5,
leave-node1, enter-node6, leave-node6,
enter-node7, leave-node7&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Back to our first example, putting the simplest version of what we&apos;re trying to match into AST explorer.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;function coolFunction() {
    return {
      coolKey: &apos;should match&apos;,
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The AST for this (with properties remove for brevity) would look like:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;json&quot;&gt;{
    &quot;type&quot;: &quot;FunctionDeclaration&quot;,
    &quot;id&quot;: {
        &quot;type&quot;: &quot;Identifier&quot;,
        &quot;name&quot;: &quot;coolFunction&quot;
    },
    &quot;body&quot;: {
        &quot;type&quot;: &quot;BlockStatement&quot;,
        &quot;body&quot;: [{
            &quot;type&quot;: &quot;ReturnStatement&quot;,
            &quot;argument&quot;: {
                &quot;type&quot;: &quot;ObjectExpression&quot;,
                &quot;properties&quot;: [{
                    &quot;type&quot;: &quot;Property&quot;,
                    &quot;key&quot;: {
                        &quot;type&quot;: &quot;Identifier&quot;,
                        &quot;name&quot;: &quot;coolKey&quot;
                    },
                    &quot;value&quot;: {
                        &quot;type&quot;: &quot;Literal&quot;,
                        &quot;value&quot;: &quot;should match&quot;,
                    },
                }]
            }
        }]
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This example is simple enough that we could match the entire thing from the function declaration node. However, that wouldn&apos;t be very robust as the return statement could be nested in an `if` statement, possibly multiple times, changing the structure of the AST significantly. It&apos;s better to match the function declaration node and the return node separately. Doing so will also be useful later if we want to expand code to cover more cases, say class and arrow function declarations.&lt;/p&gt;&lt;p&gt;Code for matching the function declaration and the return statement:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;const isCoolFunction = node =&amp;gt; 
    node.type === &apos;FunctionDeclaration&apos; &amp;amp;&amp;amp;
    node.id.type === &apos;Identifier&apos; &amp;amp;&amp;amp;
    node.id.name.startsWith(&apos;cool&apos;)

const isCoolReturn = node =&amp;gt; 
    node.type === &apos;ReturnStatement&apos; &amp;amp;&amp;amp;
    node.argument.type === &apos;ObjectExpression&apos; &amp;amp;&amp;amp;
    node.argument.properties.some(property =&amp;gt; 
        property.key &amp;amp;&amp;amp;
        property.key.type === &apos;Identifier&apos; &amp;amp;&amp;amp;
        property.key.name === &apos;coolKey&apos;
    )&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Putting it all together, we can now match both the function name and the return statement. We know the return node is nested within the function declaration node. Therefore we&apos;ll only match the return statement if we&apos;re already within a `coolFunction` node by setting a flag `isMatchCoolFunction`.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;let isMatchCoolFunction = false;
const onEnter = node =&amp;gt; {
    if (isCoolFunction(node)) {
        isMatchCoolFunction = true;
    }
    if (isMatchCoolFunction &amp;amp;&amp;amp; isCoolReturn(node)) {
        console.log(&apos;found it 🤘, so cool!&apos;);
    }
};

const onLeave = node =&amp;gt; {
    if(isCoolFunction(node)) {
        isMatchCoolFunction = false;
    }
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Please note that we&apos;re matching the `coolFunction` node again when leaving the function node so that we can unset set flag. Without this step, we&apos;d start matching cool return statements with un-coolFunctions ... Not cool at all 💅.&lt;/p&gt;&lt;p&gt;Some more sincere examples I know of are from using a framework like Angular where I aimed to get the component name and template path together (`coolDirective` and `template/path.html` respectively).&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;angularModule.directive(&apos;coolDirective&apos;, () =&amp;gt; {
    // component logic
    return {
        template: require(&apos;template/path.html&apos;),
    };
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The process would be very similar to what we just went through, use a flag to for when you&apos;ve entered a component declaration, then match the template string. What if there are multiple components chained off one another.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;angularModule
    .directive(&apos;coolDirective1&apos;, () =&amp;gt; {/* ... */})
    .directive(&apos;coolDirective2&apos;, () =&amp;gt; {/* ... */})
    .directive(&apos;coolDirective3&apos;, () =&amp;gt; {/* ... */})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The chaining complicates things as the AST structure is a little tricky, lets look at another simplified-XML style version (with emojis for visually clarity of the hierarchy):&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;html&quot;&gt;&amp;lt;body&amp;gt;
    &amp;lt;🏂CallExpression-directive3&amp;gt;
        &amp;lt;🏂MemberExpression-directive3&amp;gt;
            &amp;lt;🚒CallExpression-directive2&amp;gt;
                &amp;lt;🚒MemberExpression-directive2&amp;gt;
                    &amp;lt;🧚CallExpression-directive1&amp;gt;
                        &amp;lt;🧚MemberExpression-directive1&amp;gt;
                        &amp;lt;🧚arguments-for-directive1 /&amp;gt;
                    &amp;lt;🧚/CallExpression-directive1&amp;gt;
                &amp;lt;🚒/MemberExpression-directive2&amp;gt;
                &amp;lt;🚒arguments-for-directive2 /&amp;gt;
            &amp;lt;🚒/CallExpression-directive2&amp;gt;
        &amp;lt;🏂/MemberExpression-directive3&amp;gt;
        &amp;lt;🏂arguments-for-directive3/&amp;gt;
    &amp;lt;🏂/CallExpression-directive3&amp;gt;
&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;First thing we might notice is the reversed order as it starts with number 3; however, that&apos;s of little concern. Note that the `CallExpression` is where we&apos;d match the component declaration, but it&apos;s within the `arguments` that we&apos;d match the return statement and where the template path is defined. The challenge is that the `MemberExpression` is before it&apos;s sibling node &quot;arguments&quot;, and the `MemberExpression` is what contains the next `CallExpression`. The result is that we&apos;ll hit all of the `CallExpression`s first before we hit any of the `arguments`, and what&apos;s more when we do get the `arguments`, we&apos;ll get them in the opposite order. Again 100% explicit the order we&apos;ll enter these nodes is (skipping the `MemberExpression`s:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;text&quot;&gt;CallExpression-directive3, CallExpression-directive2,
CallExpression-directive1, arguments-for-directive1,
arguments-for-directive2, arguments-for-directive3, &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once we know this, solving this problem becomes simple, we can keep track of what CallExpressions we&apos;ve entered with a `stack` approach, and pop and push appropriately.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;const isCoolChainedDirectiveDeclaration = node =&amp;gt; {
  /*Here well match the CallExpressions that are &quot;directives&quot;*/
};
const getDirectiveName = node =&amp;gt; {
  /* returns the component/directive name i.e. &quot;coolDirective1&quot; */
};
const isDirectiveReturnStatement = node =&amp;gt; {
  /*Here well match the the return statements containing the template path*/
};
const getTemplatePath = node =&amp;gt; {
  /* extracts the template path from the return node */
};

const coolDirectiveStack = [];

const onEnter = node =&amp;gt; {
  if (isCoolChainedDirectiveDeclaration(node)) {
    const directiveName = getDirectiveName(node);
    coolDirectiveStack.unshift(directiveName);
  }
  if (
    coolDirectiveStack.length 
    &amp;amp;&amp;amp; isDirectiveReturnStatement(node)
  ) {
    const templatePath = getTemplatePath(node);
    const directiveName = coolDirectiveStack[0];
    console.log(
      `Yew!🤘The directive ${directiveName}&apos;s 
      template is at: ${templatePath}`
    );
  }
};

const onLeave = node =&amp;gt; {
  if (isCoolChainedDirectiveDeclaration(node)) {
    coolDirectiveStack.shift();
  }
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The approach is very similar to how we matched our coolFuntion/Return, except we&apos;re using a stack instead of a flag to keep track of how nested we are in the tree. Note as a personal preference I use .unshift and .shift, Rather than .push and .pop as I find `stack[0]` to be an elegant method for accessing the last thing on the stack, rather than `stack[stack.length - 1]`.&lt;/p&gt;&lt;p&gt;None of these examples will likely be of direct use to you, though the techniques of matching to multiple nodes within the broader pattern you want to match, while keeping track of where you are with the AST should be applicable for you matching advanced and deeply nested patterns within your code.&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Thinking of your code as data with ASTs]]></title><description><![CDATA[<div><p>ASTs (Abstract Syntax Trees) are an appealing tool for developers. If you don't know what they are, see my references below -- it's worth getting a base understanding so that you'll know when they could help you. One much spoken of possibility of ASTs is automatically refactoring code, so-called code-mods.</p><p>Another compelling application for them is to gain insights into your code or to help speed up tasks that your team needs to do every day. For example, a tool currently being worked on at Accelo is a component-to-URL tracker. What's the use case? Let's say a dev is fixing a bug, and in the process, they make a modification to the component `CoolComponent`. Given the name of this component, the tool gives a series of URLs that use the component. The URLs can be used for testing, either by the dev or passed on to our QA team.</p><p>It's worth mentioning that the tool is highly specific to our codebase as it deals with:</p></div>]]></description><link>https://kurthutten.com/blog/code-as-data-with-asts</link><guid isPermaLink="false">-1256cae2-c04b-52be-ab1a-3f6c0e337d28</guid><pubDate>Sat, 04 Jan 2020 13:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;ASTs (Abstract Syntax Trees) are an appealing tool for developers. If you don&apos;t know what they are, see my references below -- it&apos;s worth getting a base understanding so that you&apos;ll know when they could help you. One much spoken of possibility of ASTs is automatically refactoring code, so-called code-mods.&lt;/p&gt;&lt;p&gt;Another compelling application for them is to gain insights into your code or to help speed up tasks that your team needs to do every day. For example, a tool currently being worked on at Accelo is a component-to-URL tracker. What&apos;s the use case? Let&apos;s say a dev is fixing a bug, and in the process, they make a modification to the component `CoolComponent`. Given the name of this component, the tool gives a series of URLs that use the component. The URLs can be used for testing, either by the dev or passed on to our QA team.&lt;/p&gt;&lt;p&gt;It&apos;s worth mentioning that the tool is highly specific to our codebase as it deals with:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Perl and template-toolkit renders some HTML.&lt;/li&gt;&lt;li&gt;The rest is mostly angular 1.x code.&lt;/li&gt;&lt;li&gt;The is a broad spread of style within our angular code.&lt;/li&gt;&lt;li&gt;There is some jquery in dark corners.&lt;/li&gt;&lt;li&gt;We&apos;ve recently started adopting react.&lt;/li&gt;&lt;li&gt;It understands our file structure.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Which mean there would be little benefit to sharing the tool.&lt;/p&gt;&lt;p&gt;Without the tool being able to regression test components directly in our app is either guess-work that is likely incomplete or a gruelling task of chasing all the links through files to find each use. The latter option is more robust and is what the tool does for us now. What we&apos;ve done is taken developer knowledge and embedded it in the tool so that it no longer exists only in our heads. Here lies a subtle befit of such a tool, that it becomes a type of living documentation. If otherwise I was tasked with documenting the steps a dev needs to take to track a component to the URLs, I would be hesitant. This kind of documentation is likely to get forgotten about, out of date and become a source of misinformation. But embedding the knowledge into a tool ensures that it stays up-to-date so long as the tool is used. As I mentioned above, about what our tool deals with, having multiple frameworks, and so on. It means that there is a lot of dev knowledge now bundled into it.&lt;/p&gt;&lt;p&gt;So that&apos;s our use-case. You could do the same for your codebase too, or here are some other ideas that might be useful to your team:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A tool that counts how uses of each component, to keep track of the most used ones.&lt;/li&gt;&lt;li&gt;A tool that warns if there is a component that is used more than 3 or X times without sufficient tests.&lt;/li&gt;&lt;li&gt;A tool alerts you when a component is no longer used (depending on how advanced you wanted it, it could even delete the component and submit a pull request).&lt;/li&gt;&lt;li&gt;A tool that warns you if a name is used twice (you might want to avoid this for components, for example).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Please note the above is probably bias to web development (as that&apos;s what I do). The point is to think of code as a source of data and to pull out useful information and in the process, document knowledge.&lt;/p&gt;&lt;p&gt;ASTs exist for most modern programming languages (Javascript, Python, Go and Rust to name a few), so go forth parse your codebase. Fun fact; Perl does not have an AST (I know this because I work with Perl). If you&apos;ve ever heard the joke that Perl is a write-only language, it seems that extends to parsing tools too; they can&apos;t read it. That means there are only three things in the world that understand Perl code, you for a five-minute window immediately after you wrote it, the Perl engine and probably Larry Wall.&lt;br&gt;&lt;br&gt;References&lt;br&gt;&lt;a href=&quot;https://medium.com/airbnb-engineering/turbocharged-javascript-refactoring-with-codemods-b0cae8b326b9&quot;&gt;A good place to start for ASTs&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://www.accelo.com/&quot;&gt;Accelo&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;http://www.modernperlbooks.com/mt/2012/09/why-perl-5-needs-an-ast.html&quot;&gt;Perl&apos;s lack of an AST&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Ego and the computer.]]></title><description><![CDATA[<div><p>In 1992 Neil postman wrote "Will the computer raise egocentrism to the status of a virtue?", nearly three decades on was he right?</p><p>In the same text Technopoly, he notes that the computer as a medium emphasizes private learning and individual problem solving and hence the link to ego. So if we look to the tech-world/silicone-valley, both the emphasis of the medium and the prediction hold up. There are plenty of engineers work on challenging problems, and plenty of us don't bat an eyelid when members in this community say things like "We're here to put a dent in the universe" -Steve Jobs.</p><p>However, the medium has changed dramatically since then. Depending on one's usage habits, mindlessly consuming content served up to you by the algorithms of big tech companies can hardly be interpreted as "private learning and individual problem solving". Even here, thanks to the computer, it's now socially "healthy" to broadcast one's life on social media. Looks like he was right also in areas outside the initial scope.</p><p>Facebook and social media has to one of the most impactful pieces of technology of the past decades, especially if we measure by its involvement in the daily lives of ordinary people, and I always feel the need to ask was this inevitable? What other technological paths didn't we, or are we yet to explore as a society because social media developed when it did? Is the ego-centric nature of these platforms an extension of the computer as a medium? Or is it that it's a product of the disciples of the computer, who had already subtly embraced the intrinsic ego-centric values of their craft? Probably a bit of both.</p><p>Neil Postman died one year before Facebook launched.</p></div>]]></description><link>https://kurthutten.com/blog/ego-and-the-computer</link><guid isPermaLink="false">-ecbf1341-545d-52b4-95a2-5526c653bba3</guid><pubDate>Tue, 31 Dec 2019 13:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;In 1992 Neil Postman asked in his book Technolpoly &quot;Will the computer raise egocentrism to the status of a virtue?&quot;. Nearly three decades on from when it was published, has time proven him right?&lt;/p&gt;&lt;p&gt;In Technolpoly, Postman notes that the computer as a medium emphasises private learning and individual problem solving, hence the link to ego. If we look to the tech-world and Silicone Valley, both the emphasis of the medium and the prediction hold up. There are plenty of engineers in the tech sphere at work on challenging problems, and plenty of us don&apos;t bat an eye when members of this community say things like &lt;a href=&quot;https://www.macworld.com/article/1162827/steve-jobs-making-a-dent-in-the-universe.html&quot;&gt;&quot;We&apos;re here to put a dent in the universe&quot;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;However, computers and the tech world in general have changed dramatically since Postman&apos;s comments in 1992. Depending on one&apos;s usage habits, mindlessly consuming content served up to you by the algorithms of big tech companies can hardly be interpreted as &quot;private learning and individual problem solving&quot;. Even here, thanks to the computer, it&apos;s now socially &quot;healthy&quot; to broadcast one&apos;s life on social media. It seems as if Postman was right in areas outside the initial scope of his book.&lt;/p&gt;&lt;p&gt;Facebook and social media has to be one of the most impactful pieces of technology of the past decades, especially if we measure it by its involvement in the daily lives of ordinary people. I always feel the need to ask &apos;Was this inevitable?&apos; What other technological paths did we not explore, and which are we yet to explore because social media developed when it did? Is the ego-centric nature of these platforms an extension of the computer as a medium? Or is it that it&apos;s a product of the disciples of the computer, who had already subtly embraced the intrinsic ego-centric values of their craft? Probably a bit of both.&lt;/p&gt;&lt;p&gt;Neil Postman died one year before Facebook launched.&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[MRI Volume Slice - Project Spotlight]]></title><description><![CDATA[<p>My engineering honours thesis back in 2014 was in visual neuroscience. I worked a lot with MRI brain images as a technique called retinotopic mapping is a big tool in this field. Specifically for my thesis, I was looking for novel ways to automatically segment different brain tissue from a specific type of brain data (function-MRI data). That aside, it means I have quite an affinity towards MRI data. A common display across many different tools that deal with brain data is a 3-window view, where each window slices the brain data across a different axis - X, Y and Z.</p>]]></description><link>https://kurthutten.com/blog/mri-volume-slice</link><guid isPermaLink="false">-cd2ff0fb-f70d-5cc3-bad9-2021d366fe39</guid><pubDate>Mon, 14 Oct 2019 13:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;My engineering honours thesis back in 2014 was in visual neuroscience. I worked a lot with MRI brain images as a technique called retinotopic mapping is a big tool in this field. Specifically for my thesis, I was looking for novel ways to automatically segment different brain tissue from a specific type of brain data (functional MRI data). That aside, it means I have quite an affinity towards MRI data. A typical display across many different tools that deal with brain data is a 3-window view, where each window slices the brain data across a different axis - X, Y and Z. Clicking inside one of the windows affects which slice the other two windows are showing. It&apos;s hard to describe and is easier to understand by playing around with it.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;a href=&quot;https://mri-slice.kurthutten.com&quot;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;I am really proud of this little project. Partly because I&apos;m still surprised at how responsive it is even though it&apos;s manipulating quite a large amount of data on the fly (about 20mb), but also because I built it while on holidays just a few months after I had started my first developer job. At the time, I was still most comfortable with vanilla-js and as such, there&apos;s no framework for this simple app. I think this is still mostly appropriate, especially because of its small scope and because it makes such heavy use of canvas though I am often tempted to write a React wrapper for it.&lt;/p&gt;&lt;p&gt;One of the most interesting challenges with this project was dealing with the 3D data and being able to grab a &quot;slice&quot; of the image from multiple axes. One of the things that makes it interesting is the data comes in one big flat array and I specifically chose to keep it in this format and write my own abstractions to get the data I needed for any given slice.&lt;/p&gt;&lt;p&gt;I also took full advantage of Hacktober-Fest (2018) and got a number of people to work on the project. While I wrote the core of the app that dealt with the data, many of the UI elements such as the loading bar, the upload field and the fetching of initial data was contributed to the project by others. For someone so new to dev work it was a great learning experience, writing up issues to be tackled by a random developer, as well as the review process for when dealing with people I didn&apos;t know but still wanting to keep a high standard of the repo. Honestly though, it wasn&apos;t hard as I had a number a great people contributing and a few that came back for more because they &lt;a href=&quot;https://github.com/Irev-Dev/MRI-Volume-Slice/pull/20#issuecomment-428648808&quot;&gt;enjoyed it so much&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;As a closing note, the data you see in the app is from &lt;a href=&quot;https://openneuro.org&quot;&gt;OpenNeuro&lt;/a&gt;, a database of publically available neuroimaging data provided by researchers. This is not the only website that fosters open-science around brain data, but I think it&apos;s pretty neat that this data is available for someone like me to mess around with. (Note the face has been removed from the data set for privacy reasons).&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;References&lt;/h4&gt;&lt;p&gt;&lt;br&gt;&lt;br&gt;&lt;a href=&quot;https://openneuro.org/&quot;&gt;OpenNeuro&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/Irev-Dev/MRI-Volume-Slice&quot;&gt;The Repo&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://mri-slice.kurthutten.com/&quot;&gt;The App&lt;/a&gt;&lt;/p&gt;&lt;p&gt;.&lt;/p&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[React hook - lazy loading pattern]]></title><description><![CDATA[<p>Lazy loading data, that you also want to save into your app data store is a common enough task to warrent abstracting a little. I have a pattern to suggest here that will mean that interfacing with data from endpoints will have a consistent feel as well as </p>]]></description><link>https://kurthutten.com/blog/react-hook-lazy-loading-pattern</link><guid isPermaLink="false">-f4f85059-be1b-5458-abf1-d2872b45eb9b</guid><pubDate>Mon, 07 Oct 2019 13:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;p&gt;Lazy loading some data, that you also want to save into your app data store is a common enough task to warrent abstracting to a common pattern.&lt;/p&gt;&lt;h3&gt;&lt;/h3&gt;&lt;h4&gt;TLDR&lt;/h4&gt;&lt;h3&gt;&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;Use a consitent convention for naming hooks following this pattern, for example `use&amp;lt;dataName&amp;gt;Data` so useFilterData, useActivityData etc.&lt;/li&gt;&lt;li&gt;Always return an object from the hook with data, loading and error properties. This will give them a predictable API.&lt;/li&gt;&lt;li&gt;The hook should take any arguments that are relvant to the specific data, So we might pass in the current tab to the useTabData hook.&lt;/li&gt;&lt;li&gt;The hook should return data from the store by default, and only make async calls if that data doesn&apos;t exist (and then update the store after fetching it).&lt;/li&gt;&lt;li&gt;Optional: Along side data, loading and error; a forth property onUpdate, which is a callback, can be returned for when the data also needs to be saved in the db&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The goal of this pattern is to make interfacing with data from endpoints have a consistent feel as well as taking advantage of lazy loading content, and not re-fetching if the data alreay exists in your data store. Here an minimal example app I made to demo the pattern.&lt;/p&gt;&lt;a href=&quot;https://lazy-hook-pattern.kurthutten.com&quot;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;So how would we write a hook using this pattern, lets say the end goal is ta make a custom hook called `useTabData`&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;const {
  data,
  loading,
  error
} = useTabData(tab)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It will fetch data depending on what tab the user has open, following this pattern we should always return an object with data, loading and error so our component can deal with these situations. And now the logic internal to this hook is basically going to check if the data is in the store and only make a async request if it&apos;s not there. Here&apos;s an example.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;import { useState, useEffect } from &quot;react&quot;;
import { useStore } from &quot;./store&quot;;

export const useTabData = tab =&amp;gt; {
  const [state, dispatch] = useStore();
  const coolData = state &amp;amp;&amp;amp; state.coolData &amp;amp;&amp;amp; state.coolData[tab];
  const shouldFetchData = tab &amp;amp;&amp;amp; !(coolData &amp;amp;&amp;amp; coolData.length);

  useEffect(() =&amp;gt; {
    if (shouldFetchData) fetchData(tab);
  }, [tab, shouldFetchData]);
  return {
    data: coolData || [],
    loading: isLoading,
    error: &quot;&quot;
  };
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Basically we are getting our state from our store, and if there is no data for the current tab, we&apos;ll skip running `fetchData` inside the useEffect. Of course returning data, loading and error. Filling this in a little more by defining fetchData.&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;export const useTabData = tab =&amp;gt; {
  const [isLoading, setIsLoading] = useState(true);
  const [state, dispatch] = useStore();
  const coolData = state &amp;amp;&amp;amp; state.coolData &amp;amp;&amp;amp; state.coolData[tab];
  const shouldFetchData = tab &amp;amp;&amp;amp; !(coolData &amp;amp;&amp;amp; coolData.length);

  const fetchData = async curretTab =&amp;gt; {
    setIsLoading(true);
    const promise = new Promise(resolve =&amp;gt; {
      setTimeout(
        () =&amp;gt; resolve(tab === &quot;sweet&quot;
          ? [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;]
          : [&quot;x&quot;, &quot;y&quot;, &quot;z&quot;]),
        3000
      );
    });
    const data = await promise;
    dispatch({
      type: &quot;UPDATE_COOL_DATA&quot;,
      payload: {
        type: currentTab,
        data
      }
    });
    setIsLoading(false);
  };

  useEffect(() =&amp;gt; {
    if (shouldFetchData) fetchData(tab);
  }, [tab, shouldFetchData]);
  return {/* data, loading, error etc*/};
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So were using another bit of state at the top of our hook for isLoading, setting that to true at the stare of a call to fetchData, then we make our async call (mocked here with a timeout), once the data comes back we&apos;ll update the store and set isLoading to false. All is well with our frondend 🤗.&lt;/p&gt;&lt;p&gt;The above example can be &lt;a href=&quot;https://lazy-hook-pattern.kurthutten.com&quot;&gt;seen here&lt;/a&gt;, Or &lt;a href=&quot;https://codesandbox.io/s/github/Irev-Dev/React-hook-lazy-loading-pattern-demo/tree/master&quot;&gt;codesandbox&lt;/a&gt; (styles arn&apos;t working in the sandbox 🤷‍♀️).&lt;br&gt;The Repo can be &lt;a href=&quot;https://github.com/Irev-Dev/React-hook-lazy-loading-pattern-demo/tree/master&quot;&gt;found here&lt;/a&gt;, (note, that it&apos;s writen in typescript).&lt;br&gt;Here&apos;s a &lt;a href=&quot;https://itnext.io/replace-redux-state-with-react-hooks-and-context-7906e0fd5521&quot;&gt;blog post&lt;/a&gt; detailing a react hooks data-store similar to the one that I&apos;m using in the example. (though the type of store doesn&apos;t matter)&lt;br&gt;&lt;/p&gt;&lt;h4&gt;&lt;/h4&gt;&lt;h4&gt;Some other points&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Note that in the example the data being returned is coming directly from the store (assigned to a varible inbetween). It might be tempting to put the data from your async call into a local useState, but I would highly discourage it as duplicating state like this could lead to some nasty bugs.&lt;/li&gt;&lt;li&gt;Also adding a onUpdate callback, for data that can be save to the backend from the fronend. This callback would obviously be making async calls and so It also should return a similar format of data, isLoading, error (maybe status instead of data, if you&apos;re not interested in the data comming back).&lt;/li&gt;&lt;li&gt;We would implement the returned error in a real example.&lt;/li&gt;&lt;li&gt;You might get stale data if you only ever return from the store. For most data this isn&apos;t likely to be a huge problem, but some extra logic to invalidate/timeout old data could be implemented.&lt;/li&gt;&lt;li&gt;If you find you&apos;re writing a lot of hook like this for you app, you migh want to abstract some of this into a hook factory in order to reduce duplication (i.e. the code that checks if the data is already in the store). I might come back and write more details about this. 🤞&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Use Apollo with Gatsby (or SSR)]]></title><description><![CDATA[<p>How to fix build errors for apollo client and gatsby/ssr</p>]]></description><link>https://kurthutten.com/blog/use-apollo-with-gatsby</link><guid isPermaLink="false">-dd7a0634-a422-59be-a9f8-1cb348d1d066</guid><pubDate>Sat, 05 Oct 2019 14:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h4&gt;So the short of it is, you need to use `isomorphic-fetch`.&lt;/h4&gt;&lt;p&gt;There&apos;s no point me repeating what&apos;s already been said well in the &lt;a href=&quot;https://www.apollographql.com/docs/react/get-started/&quot;&gt;apollo-docs&lt;/a&gt;, so start  there for setting up apollo-client. However you might find everything is working in dev mode, but your project won&apos;t build 😔. &lt;/p&gt;&lt;p&gt;Here&apos;s the fix, first:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;sh&quot;&gt;npm install isomorphic-fetch
# OR
yarn add isomorphic-fetch&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, where you have initialised your apollo client you need to:&lt;/p&gt;&lt;pre&gt;&lt;code lang=&quot;javascript&quot;&gt;import ApolloClient from &apos;apollo-boost&apos;;
import fetch from &apos;isomorphic-fetch&apos;

const client = new ApolloClient({
  fetch,
  uri: &apos;https://48p1r2roz4.sse.codesandbox.io&apos;,
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Why? It&apos;s because the ApolloClient by default uses the browser fetch api, and because Gatsby statically builds your website in node (and fetch doesn&apos;t exist in node). Luckily the &lt;a href=&quot;https://www.apollographql.com/docs/react/get-started/#configuration-options&quot;&gt;ApolloClient configeration options&lt;/a&gt; allow us to pass in a fetch compatiable api, of which isomorphic-fetch fits the bill nicely 👌.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;&lt;/h4&gt;&lt;h4&gt;Reference:&lt;/h4&gt;&lt;p&gt;&lt;a href=&quot;https://www.apollographql.com/docs/react&quot;&gt;Apollo react docs again&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/matthew-andrews/isomorphic-fetch&quot;&gt;isomorphic-fetch&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues/3650&quot;&gt;Github issue that help me solve this initially&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</content:encoded></item></channel></rss>