-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.json
1 lines (1 loc) · 34.8 KB
/
index.json
1
[{"content":"See all posts\n","href":"/","title":"Home"},{"content":" in reply to: @aaronpk Trying out this guide to sending webmentions ","href":"/handwritten/webmention-test/","title":"Webmention Test"},{"content":"","href":"/blog/","title":"Blogs"},{"content":"","href":"/tags/","title":"Tags"},{"content":" Today I learned that hugo has support for org-mode files.\n I knew that ox-hugo is a thing, but this is different. And I never thought to dig into the docs to figure out if I can author posts in org-mode and have it work.\n","href":"/blog/this-is-an-org-post/","title":"This is an Org Post"},{"content":"","href":"/tags/written-in-org-mode/","title":"written-in-org-mode"},{"content":"","href":"/tags/clojure/","title":"clojure"},{"content":"","href":"/tags/emacs/","title":"emacs"},{"content":"TLDR This post is just to document a scenario I put myself in. But I wanted to write it down in case I need to use it again in the future.\nProblem You have a CSV file from your bank with some expenses listed.\n You want to generate an PDF invoice file to send to someone and collect some money.\n Solution I think the solution is to\n import the CSV into an org table use your favorite programming language to format the data use maaslalani/invoice to quickly make a PDF Deep Dive When I\u0026rsquo;m dealing with CSV files, I often reach for org-mode in emacs just to make the file easier to read.\nFor example, this is a mess\nTransaction Date,Post Date,Description,Category,Type,Amount,Memo 12/01/2023,12/02/2023,Big Expense Number 1,Shopping,Sale,-2414.73, 12/02/2023,12/03/2023,Some Other Expense,Personal,Sale,-95.00, 12/03/2023,12/04/2023,This Expense You Don't Recognize,Groceries,Sale,-20.82, But if you copy this file \u0026ndash; honestly I just open the original \u0026ndash;, open in org-mode, C-x h to select the entire buffer, M-x org-mode, and M-x org-table-create-or-convert-from-region, then you get this:\n| Transaction Date | Post Date | Description | Category | Type | Amount | Memo | | 12/01/2023 | 12/02/2023 | Big Expense Number 1 | Shopping | Sale | -2414.73 | | | 12/02/2023 | 12/03/2023 | Some Other Expense | Personal | Sale | -95.00 | | | 12/03/2023 | 12/04/2023 | This Expense You Don't Recognize | Groceries | Sale | -20.82 | | I always separate the header row from the body, so just add |-| as a new line 2 and hit tab. Which turns this\n| Transaction Date | Post Date | Description | Category | Type | Amount | Memo | |-| | 12/01/2023 | 12/02/2023 | Big Expense Number 1 | Shopping | Sale | -2414.73 | | | 12/02/2023 | 12/03/2023 | Some Other Expense | Personal | Sale | -95.00 | | | 12/03/2023 | 12/04/2023 | This Expense You Don't Recognize | Groceries | Sale | -20.82 | | into this\n| Transaction Date | Post Date | Description | Category | Type | Amount | Memo | |------------------+------------+----------------------------------+-----------+------+----------+------| | 12/01/2023 | 12/02/2023 | Big Expense Number 1 | Shopping | Sale | -2414.73 | | | 12/02/2023 | 12/03/2023 | Some Other Expense | Personal | Sale | -95.00 | | | 12/03/2023 | 12/04/2023 | This Expense You Don't Recognize | Groceries | Sale | -20.82 | | If you look through the README for invoice, you\u0026rsquo;ll see that you can specify multiple --item .. --quantity ... lines in one invoice generate call. I took a guess that this applies to --rate as well. But this is perfect for the problem I have. I just need to turn my table into a big block of shell code.\norg-mode lets you name the tables you create and pass them to various code blocks as input. I won\u0026rsquo;t go through the following clojure at all, but the intent is \u0026ldquo;given an org table as input, select the relevant columns, and format them into code we can hand to invoice\u0026rdquo;.\n(let [[header \u0026amp; data] input rows (-\u0026gt;\u0026gt; data (reduce (fn [state row] (conj state (zipmap header row))) []))] (doseq [{:strs [Description Amount]} (butlast rows)] (println (format \u0026#34;--item \\\u0026#34;%s\\\u0026#34; --quantity 1 --rate %.2f \\\\\u0026#34; Description Amount))) (let [{:strs [Description Amount]} (last rows)] (print (format \u0026#34;--item \\\u0026#34;%s\\\u0026#34; --quantity 1 --rate %.2f \\\\\u0026#34; Description Amount)))) Here\u0026rsquo;s the org block I needed\n#+NAME: table-to-lines #+begin_src clojure :colnames no :var input=table-of-data :exports results :results output \u0026lt;\u0026lt;all of the clojure from above\u0026gt;\u0026gt; #+end_src #+NAME: table-to-lines is the variable name for this code block This will help me export the code to a particular spot later :colnames no tells org-babel to not strip the header row :var input=table-of-data binds input to some thing I named table-of-data I just slapped a #+NAME: table-of-data onto the org table from before Because this is a clojure source, I think it turned the table into seq of seqs ((header1 header2 header3) (r1c1 r1c2 r1c3) (r2c1 r2c2 r2c3) ...) :exports results tells org-babel later to export this block as only the results, not the code itself :results output says to collect everything printed as the result, not just the final return value The one thing I do want to say about the clojure is that I needed to separate the final row from the others because the final println and whatever org-babel is doing resulted in my output having a blank line at the end. This caused issues in my final script, and I couldn\u0026rsquo;t find the answer on how to clean that up through org-mode. So the next best thing was to just modify my code a bit. Thankfully clojure has butlast and last.\nYou can run this and should see the folowing output:\n#+RESULTS: table-to-lines : --item \u0026quot;Big Expense Number 1\u0026quot; --quantity 1 --rate -2414.73 \\ : --item \u0026quot;Some Other Expense\u0026quot; --quantity 1 --rate -95.00 \\ : --item \u0026quot;This Expense You Don't Recognize\u0026quot; --quantity 1 --rate -20.82 \\ This is the meat of the script I want to generate. The next part is to write the two source blocks that are needed to round out the command. You can imagine it looks something like this\n#+NAME: make-invoice-start #+begin_src shell invoice generate \\ --from \u0026quot;Andy, Inc.\u0026quot; \\ --to \u0026quot;Owes Me Money\u0026quot; \\ #+end_src #+NAME: make-invoice-end #+begin_src shell --tax 0 --discount 0.99 \\ --note \u0026quot;TEST Don't actually pay this\u0026quot; #+end_src So now that I have 3 named source code blocks, I can create a script that is generated from this org file I\u0026rsquo;ve been working in\n#+begin_src shell :dir /tmp/ :tangle make-invoice :noweb yes \u0026lt;\u0026lt;make-invoice-start\u0026gt;\u0026gt; \u0026lt;\u0026lt;table-to-lines()\u0026gt;\u0026gt; \u0026lt;\u0026lt;make-invoice-end\u0026gt;\u0026gt; #+end_src This says, on tangle, create a file called make-invoice and concatenates the contents of the following named blocks as the file. The parens in table-to-lines() say to evaluate that block before tangling. And remember that I set an option on that block to export the results instead of the code. So make-invoice has the command I want to call.\nTo actually generate that file, run M-x org-babel-tangle.\nYou can run it from inside org-mode too. Just add a block like this and C-c C-c\n#+begin_src shell :dir /tmp/ bash ./make-invoice #+end_src I\u0026rsquo;m going to leave it as a challenge for the reader to tweak this solution so that I only need to combine two source blocks instead of my three.\n","href":"/blog/org-mode-is-better-glue-code-than-bash/","title":"Org Mode Is Better Glue Code Than Bash"},{"content":"","href":"/tags/python/","title":"python"},{"content":"I didn\u0026rsquo;t know what to use between poetry and hatch.\nI just followed the guide from the Packaging Guide.\nIt ended with me having the following directory structure\n. ├── pyproject.toml ├── README.org ├── src │ └── andy_aoc_2023 │ ├── day1.py │ └── __init__.py └── tests Which isn\u0026rsquo;t too crazy I guess.\n","href":"/blog/python-packaging/","title":"Python Packaging"},{"content":"","href":"/tags/magit/","title":"magit"},{"content":"I wanted to do the equivalent of this, but in magit\ngit remote rename origin github git remote add codeberg https://codeberg.org/jkreeftmeijer/ox-md-title.el.git git remote add origin https://codeberg.org/jkreeftmeijer/ox-md-title.el.git git remote set-url --add --push origin https://codeberg.org/jkreeftmeijer/ox-md-title.el.git git remote set-url --add --push origin https://github.com/jeffkreeftmeijer/ox-md-title.el git remote show origin * remote origin Fetch URL: https://codeberg.org/jkreeftmeijer/ox-md-title.el.git Push URL: https://codeberg.org/jkreeftmeijer/ox-md-title.el.git Push URL: https://github.com/jeffkreeftmeijer/ox-md-title.el HEAD branch: main Remote branch: main new (next fetch will store in remotes/origin) Local ref configured for \u0026#39;git push\u0026#39;: main pushes to main (up to date) Source\nRenaming was easy:\n M r, origin, andylu Adding another remote was also simple:\n M a, github, git@github.com:blah.git Adding origin back in was just:\n M a, origin, ssh://git@gitea.andylu.dev:1702/blah.git Setting multiple push remotes took bit of work to figure out. Ultimately, you just give it a comma-space separated list of push URLs:\n M C, origin, s, ssh://git@gitea.andylu.dev:1702/blah.git, git@github.com:blah.git Last thing was setting the push remote default back to origin:\n P C, hit p until origin is highlighted ","href":"/blog/magit-multiple-push-remotes/","title":"Magit: Multiple Push Remotes"},{"content":"","href":"/tags/homelab/","title":"homelab"},{"content":"I\u0026rsquo;ve made a couple attempts at this so far.\nThe software I\u0026rsquo;ve been looking at is:\n https://stalw.art/ https://www.cyrusimap.org/ I didn\u0026rsquo;t really give Cyrus a good go because I got Stalwart in a spot where I could receive messages through IMAP.\nI didn\u0026rsquo;t get far at all with sending messages. Oracle is my currently cloud provider and port 25 is blocked on my account.\nThe feature that drew me to both of these projects is JMAP support. I heard about it while talking to a few people at a recent Philly JS meetup.\nI think right now, the novelty of a new protocol is what makes me want to set up an email server. The other reason I want to host it myself is to be able to host email for friends and family - though no one has approached me about wanting a service like this.\nThe only scary part for me right now is that I don\u0026rsquo;t know what it would take to provide a stable service. But maybe that\u0026rsquo;s not something I really need to think about. If my email goes down, I probably have other things I need to be worried about.\nAnyway, I\u0026rsquo;ll see how much more time I want to throw at this. I think the most reasonable next step is pay for Fastmail and watch Stalwart develop over the next year and maybe self host in the future.\n","href":"/blog/self-hosting-email/","title":"Self Hosting Email"},{"content":"","href":"/tags/bash/","title":"bash"},{"content":"Here\u0026rsquo;s a bash snippet I can see myself using one day\nwhile :; do ls -lh \u0026#34;$( ls -t1 /tmp/tmp* | head -n 1 )\u0026#34;; sleep 10; done \u0026quot;$( ls -t1 /tmp/tmp* | head -n 1 )\u0026quot;: Get a file name ls -t1 /tmp/tmp*: List the files in /tmp/tmp* ordered by most recently updated head -n 1: Get the first one ls -lh \u0026lt;one-file\u0026gt;: Get the long listing of just one file The rest of it is just to do it over and over so that I can see changes in the file size ","href":"/blog/bash-while-loops/","title":"Bash `while` loops"},{"content":"","href":"/tags/hugo/","title":"hugo"},{"content":" I think I\u0026rsquo;ve tried to keep a blog going for so long and on so many different stacks that I never sat down to learn about how hugo works.\n Like wow, I can write some templating, shove it in a \u0026ldquo;shortcode\u0026rdquo;, and reuse it everywhere?\n Testing it now: Docs\n Here\u0026rsquo;s a center block\n I\u0026rsquo;m not entirely sure when I would be using this center thing. It feels like one of those \u0026ldquo;pull a quote out of the larger article things\u0026rdquo;. I kind of wish it had a border of some kind or a background difference Just kidding, I easily updated the shortcode with what I wanted Here is some centered text\nIt\u0026rsquo;s a ton of words\nhi\n End of center block I would try the conversation one, but I think it would look cooler as a text message bubble with the left and right justification. ","href":"/blog/learning-about-hugo/","title":"Learning About Hugo"},{"content":"This is my digital garden.\n","href":"/about/","title":"About"},{"content":"","href":"/page/","title":"Pages"},{"content":"See my now page for my latest updates.\nThere\u0026rsquo;s nothing on this page yet.\n","href":"/old-now/archive/","title":"Archive"},{"content":"","href":"/old-now/","title":"Old-nows"},{"content":"This is a now page. For stuff that used to be here, check out the archive.\nSelf Hosting stuff I\u0026rsquo;m working on my tech independence and putting my tech skills to the test.\nThis also relates to things I\u0026rsquo;ve been reading about permacomputing. By keeping the services I depend on to things that I can host and maintain and on older hardware that I\u0026rsquo;m in charge of, maybe I\u0026rsquo;m taking a step towards making technology more sustainable for the planet.\nGo In my spare time, I\u0026rsquo;ve been learning how to play Go.\nBJJ I got my purple belt from Balance Studios on 2023-10-28.\n","href":"/now/","title":"Now"},{"content":"This post is the one I want to reference when I do something in babashka and forget syntax or something.\nCapture a process\u0026rsquo;s STDOUT and process the string after (require \u0026#39;[babashka.process :as bp]) (-\u0026gt; (bp/shell {:out :string} \u0026#34;do-the-thing\u0026#34;) :out (clojure.string/split #\u0026#34;\\n\u0026#34;)) Import a local library Dynamically You can add deps as one of the first steps in your scripts.\n(require \u0026#39;[babashka.deps :as deps]) (deps/add-deps \u0026#39;{:deps {io.github.lispyclouds/bblgum {:git/sha \u0026#34;1d4de3d49b84f64d1b71930fa1161f8d2622a4d9\u0026#34;} dev.andylu/clj-lipgloss {:local/root \u0026#34;/home/andy/git/clj-lipgloss\u0026#34;}}}) (require \u0026#39;[bblgum.core :as b]) (require \u0026#39;[clj-lipgloss.core :as lg :use [log-info log-warn log-debug]]) Statically Put this in a bb.edn\n{:deps {io.github.lispyclouds/bblgum {:git/sha \u0026#34;1d4de3d49b84f64d1b71930fa1161f8d2622a4d9\u0026#34;} dev.andylu/clj-lipgloss {:local/root \u0026#34;/home/andy/git/clj-lipgloss\u0026#34;}}} And then in your code\n(require \u0026#39;[bblgum.core :as b]) (require \u0026#39;[clj-lipgloss.core :as lg :use [log-info log-warn log-debug]]) ","href":"/blog/my-babashka-cookbook/","title":"My Babashka Cookbook"},{"content":"Here are my notes as I go through the Biff tutorial.\nI\u0026rsquo;ve made a github repo here.\nI spotted something that didn\u0026rsquo;t work quite right. So I opened an issue and PR on the main repo. Here are those links: issue and PR.\nSo I got to a point where the tutorial wanted me to hook up an email sending service to get login information sent out to users. I knew I already had a Resend account, so I opted to hook that up instead.\nAnd instead of figuring out the API myself, I\u0026rsquo;m just using charm\u0026rsquo;s pop app and shelling out to it.\n","href":"/blog/biff-tutorial/","title":"Biff Tutorial"},{"content":"","href":"/tags/babashka/","title":"babashka"},{"content":"If I need a datetime in Python, I reach for\nfrom datetime import datetime datetime.now() So I need something as quick for clojure.\nApparently, it\u0026rsquo;s just\n(-\u0026gt; (java.time.LocalDate/now) str) ;; =\u0026gt; \u0026#34;2023-10-15\u0026#34; ","href":"/blog/babashka-datetime/","title":"Babashka Datetime"},{"content":"Here\u0026rsquo;s just a running list of things I want to write down thoughts about.\n babashka CLI design traefik as a reverse proxy for tailscale services ","href":"/blog/future-blog-ideas/","title":"Future Blog Ideas"},{"content":"","href":"/tags/projects/","title":"projects"},{"content":"","href":"/tags/story/","title":"story"},{"content":"These days I\u0026rsquo;ve been writing a lot of babashka instead of reaching for bash or python to do minor scripting tasks at work.\nI am also a big fan of the work done at charm and the relevant products in this case are gum and lipgloss.\nUsing gum style I can log any text in basically any color I want. And while there\u0026rsquo;s not a ton of functional benefits to this, I think there\u0026rsquo;s something to be said about adding color into the logs we write.\nI just wanted to take the time to outline the steps I took to do all of the following\n Add a local dependency to a babashka script and import library code from a local directory How to write color to shell streams Colored Logging I picked the two color palettes from log to emulate first.\nLocal Dependencies ","href":"/blog/terminal-logging-with-color/","title":"Terminal Logging With Color"},{"content":"","href":"/tags/git/","title":"git"},{"content":"The first time I learned about git submodules was actually from working with hugo.\nThe documentation used to describe how you can have your main branch live in a directory that points to a diffent origin than your current branch.\nIt\u0026rsquo;s a cool feature that I never remember how to use, so here are notes I took after rediscovering it recently.\n To add a submodule\ngit submodule add https://github.com/some-other-repo To clone a submodule locally\ngit submodule update To remove a submodule\ngit rm path/to/the/submodule git commit -m \u0026#39;Removed a submodule\u0026#39; Related to the use case described by the hugo docs, git worktree can also make another branch live in a directory.\nTake the repo I use to manage this site, for example. I want to write on the dev branch, so my repo is checking out dev most of the time.\nBut hugo builds to a folder called public. And I want all of the contents of public to be the stuff that lives on my master branch. So all I have to do is add a worktree in my directory under the name public. That makes the folder exist as master, so then every time hugo builds and changes something in there, I just add everything and commit to deploy the site.\n","href":"/blog/git-submodules/","title":"Git Submodules"},{"content":"","href":"/tags/golang/","title":"golang"},{"content":"Was in a flower shop today and the cashier was trying to describe how a big carton of silica gel can be used to dry out flowers.\nI chuckled because I\u0026rsquo;m not entirely sure where to get a bunch of silica gel, but then thought it\u0026rsquo;s time to start hoarding \u0026ldquo;Do not eat\u0026rdquo; packets.\n","href":"/blog/til-silica-gel/","title":"TIL: Silica Gel can make Dried Bouquet"},{"content":" ","href":"/handwritten/introducing-handwritten-pages/","title":"Introducing handwritten pages"},{"content":"","href":"/tags/docker/","title":"docker"},{"content":"","href":"/handwritten/","title":"handwritten"},{"content":"Setup Installing docker I always like the DigitalOcean tutorials, so that\u0026rsquo;s what I followed here. Installing Docker on Ubuntu 20.04\nOne note: I had to restart my computer instead of just logging out in order to get the docker group update to take effect.\nClone the repos git clone git@github.com:KAllan357/answering-machine.git git clone git@github.com:KAllan357/telephone.git Nothing special here.\nInstall Go I just followed these instructions.\nDo the thing Start the answering machine outside of docker Running go run main.go seems to do a thing. And we get the 200 like the instructions say if you curl it. Great, so it seems like we can expect the app to hang and just wait for requests. And to log them when it handles them.\nStart the answering machine container I need to build the thing first.\nMy docker build . hit this error\ndocker build . Sending build context to Docker daemon 74.24kB Step 1/10 : FROM debian:wheezy wheezy: Pulling from library/debian 2b15b7abe8b3: Pull complete Digest: sha256:2259b099d947443e44bbd1c94967c785361af8fd22df48a08a3942e2d5630849 Status: Downloaded newer image for debian:wheezy ---\u0026gt; 10fcec6d95c4 Step 2/10 : RUN apt-get update -y \u0026amp;\u0026amp; apt-get install --no-install-recommends -y -q curl build-essential ca-certificates ---\u0026gt; Running in 900695c2fa97 Ign http://deb.debian.org wheezy Release.gpg Ign http://deb.debian.org wheezy-updates Release.gpg Ign http://deb.debian.org wheezy Release Ign http://security.debian.org wheezy/updates Release.gpg Ign http://deb.debian.org wheezy-updates Release Ign http://security.debian.org wheezy/updates Release Err http://security.debian.org wheezy/updates/main amd64 Packages Err http://security.debian.org wheezy/updates/main amd64 Packages Err http://security.debian.org wheezy/updates/main amd64 Packages Err http://security.debian.org wheezy/updates/main amd64 Packages Err http://security.debian.org wheezy/updates/main amd64 Packages 404 Not Found [IP: 151.101.66.132 80] Err http://deb.debian.org wheezy/main amd64 Packages 404 Not Found Err http://deb.debian.org wheezy-updates/main amd64 Packages 404 Not Found W: Failed to fetch http://deb.debian.org/debian/dists/wheezy/main/binary-amd64/Packages 404 Not Found W: Failed to fetch http://deb.debian.org/debian/dists/wheezy-updates/main/binary-amd64/Packages 404 Not Found W: Failed to fetch http://security.debian.org/debian-security/dists/wheezy/updates/main/binary-amd64/Packages 404 Not Found [IP: 151.101.66.132 80] E: Some index files failed to download. They have been ignored, or old ones used instead. The command \u0026#39;/bin/sh -c apt-get update -y \u0026amp;\u0026amp; apt-get install --no-install-recommends -y -q curl build-essential ca-certificates\u0026#39; returned a non-zero code: 100 My first thought is maybe it has to do with wheezy? So I dropped that tag and it seems to run fine. docker images showed me the successful build. Oh, I also bumped the go version because I noticed my install grabbed v1.6 instead of v1.3. I sure that won\u0026rsquo;t affect this exercise very much.\nmodified Dockerfile @@ -1,11 +1,12 @@ -FROM debian:wheezy +FROM debian RUN apt-get update -y \u0026amp;\u0026amp; apt-get install --no-install-recommends -y -q curl build-essential ca-certificates -RUN mkdir /usr/local/go \u0026amp;\u0026amp; curl https://storage.googleapis.com/golang/go1.3.linux-amd64.tar.gz | tar xvzf - -C /usr/local/go --strip-components=1 +RUN mkdir /usr/local/go \u0026amp;\u0026amp; curl https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz | tar xvzf - -C /usr/local/go --strip-components=1 Ah a simple docker run -it \u0026lt;image id\u0026gt; said it panicked because it\u0026rsquo;s missing a config. So I\u0026rsquo;ll add it like the other files.\nADD messages.html /app/ +ADD config.json /app/ A rebuild and a run looks just like the it did when I ran the main.go, so let\u0026rsquo;s try a curl.\nIt failed. I need to expose a port.\nAdding a -p 8080:8080 seemed to do the trick and I got \u0026ldquo;Jimbo\u0026rsquo;s answering machine\u0026rdquo;. A quick ag says that\u0026rsquo;s from the config. Seems like the instructions are out of date, it references a line 54 and an environment variable, but 54 is a blank line. And a config file controls the name now.\nI don\u0026rsquo;t want to think too hard about this, so I\u0026rsquo;m moving on to the telephone. But first, I was able to get the container running in the background and tailing the logs.\ndocker run -dit -p 8080:8080 --name andy_am 94f348ecb597 docker logs -f andy_am Start the telephone container Nice, telephone\u0026rsquo;s Dockerfile is super bare - literally empty. Since I pulled the debian image earlier, I thought I would use that as a base.\nSo I started with an interactive debian container to figure out how to install ruby and gems and whatnot.\ndocker run -it debian bash root@7c91165552bb:/# Installing ruby was easy, and I got rubygems for free. Then installing a gem was easy. So I threw all of that into the Dockerfile.\nmodified Dockerfile @@ -1,12 +1,15 @@ -# See the reference doc - http://docs.docker.com/reference/builder/ +FROM debian +MAINTAINER luandy64 -# FROM - what is the base image for this Dockerfile? http://docs.docker.com/reference/builder/#from -# MAINTAINER - who owns this Dockerfile? http://docs.docker.com/reference/builder/#maintainer +RUN apt update -# Install Ruby - perhaps from a package manager? http://docs.docker.com/reference/builder/#run +RUN apt install -y ruby +RUN gem install httparty Now I just need to figure out how to run the ruby file in the container. So first we need to get the ruby file into the container. Taking inspiration from the answering-machine/Dockerfile, I made these changes to telephone/Dockerfile\nmodified Dockerfile @@ -1,12 +1,15 @@ -# See the reference doc - http://docs.docker.com/reference/builder/ +FROM debian +MAINTAINER luandy64 -# FROM - what is the base image for this Dockerfile? http://docs.docker.com/reference/builder/#from -# MAINTAINER - who owns this Dockerfile? http://docs.docker.com/reference/builder/#maintainer +RUN apt update -# Install Ruby - perhaps from a package manager? http://docs.docker.com/reference/builder/#run +RUN apt install -y ruby +RUN gem install httparty -# Install the \u0026#39;httparty\u0026#39; gem - how do you install gems? http://docs.docker.com/reference/builder/#run +RUN mkdir /app +ADD telephone.rb /app/ -# ADD the telephone(.rb) application - http://docs.docker.com/reference/builder/#add -# Write a CMD - http://docs.docker.com/reference/builder/#cmd +WORKDIR /app +RUN ruby telephone.rb Attempting the build now results in a failure because the telephone.rb script is unhappy. So I comment it out and try to iterate on it. Here\u0026rsquo;s the error I see\nroot@c59f07ad8b04:/app# ruby telephone.rb Traceback (most recent call last): 12: from telephone.rb:11:in `\u0026lt;main\u0026gt;\u0026#39; 11: from /var/lib/gems/2.5.0/gems/httparty-0.18.1/lib/httparty.rb:631:in `post\u0026#39; 10: from /var/lib/gems/2.5.0/gems/httparty-0.18.1/lib/httparty.rb:524:in `post\u0026#39; 9: from /var/lib/gems/2.5.0/gems/httparty-0.18.1/lib/httparty.rb:594:in `perform_request\u0026#39; 8: from /var/lib/gems/2.5.0/gems/httparty-0.18.1/lib/httparty/request.rb:145:in `perform\u0026#39; 7: from /usr/lib/ruby/2.5.0/net/http.rb:1458:in `request\u0026#39; 6: from /usr/lib/ruby/2.5.0/net/http.rb:909:in `start\u0026#39; 5: from /usr/lib/ruby/2.5.0/net/http.rb:920:in `do_start\u0026#39; 4: from /usr/lib/ruby/2.5.0/net/http.rb:935:in `connect\u0026#39; 3: from /usr/lib/ruby/2.5.0/timeout.rb:103:in `timeout\u0026#39; 2: from /usr/lib/ruby/2.5.0/timeout.rb:93:in `block in timeout\u0026#39; 1: from /usr/lib/ruby/2.5.0/net/http.rb:936:in `block in connect\u0026#39; /usr/lib/ruby/2.5.0/net/http.rb:939:in `rescue in block in connect\u0026#39;: Failed to open TCP connection to localhost:8080 (Cannot assign requested address - connect(2) for \u0026#34;localhost\u0026#34; port 8080) (Errno::EADDRNOTAVAIL) My first thought is maybe it doesn\u0026rsquo;t want localhost but will take 127.0.0.1. But that results in a similar error. So I just tried the IP address of the answering-machine container. That worked, but I wanted to make it configurable annd based on an environment variable.\nAfter looking up how to grab env vars in ruby, I modified telephone.rb for real and rebuilt the image.\n-# FIXME -url = \u0026#34;http://localhost:8080/messages\u0026#34; +url = \u0026#34;http://#{ENV[ANSWERING_MACHINE_HOST]}:8080/messages\u0026#34; It kept erroring on me, but I realized that I was calling RUN ruby telephone.rb instead of CMD ruby telephone.rb. So let\u0026rsquo;s make that change now.\n-RUN ruby telephone.rb +CMD ruby telephone.rb It builds successfully now, but if I run the image I get an error about some constant ANSWERING_MACHINE_HOST.\nHa, you have to quote keys for maps. Duh.\n- url = \u0026#34;http://#{ENV[ANSWERING_MACHINE_HOST]}:8080/messages\u0026#34; + url = \u0026#34;http://#{ENV[\u0026#34;ANSWERING_MACHINE_HOST\u0026#34;]}:8080/messages\u0026#34; I learned that ruby has a \u0026ldquo;safe get\u0026rdquo; from a map too. So I used that to set values for the sender and message.\n-# FIXME -sender = \u0026#34;Telemarketer Bob\u0026#34; +sender = ENV.fetch(\u0026#34;SENDER\u0026#34;, \u0026#34;Telemarketer Bob\u0026#34;) -# FIXME -message = \u0026#34;I have a credit card offer you can\u0026#39;t refuse.\u0026#34; +message = ENV.fetch(\u0026#34;MESSAGE\u0026#34;, \u0026#34;I have a credit card offer you can\u0026#39;t refuse.\u0026#34;) Finally, the exercise mentions linking two containers. Since I already named the answering machine andy_am, I start the telephone with\ndocker run -it -e ANSWERING_MACHINE_HOST=\u0026#39;andy_am\u0026#39; --link andy_am 80ad0c4699d8 And we can check the logs of the answering machine with\ndocker logs -f andy_am And with that I\u0026rsquo;m calling it done.\nReflection I think this exercise showed me that I know more about Docker than I give myself credit for. Also, while I don\u0026rsquo;t have the specifics of \u0026ldquo;my docker workflow\u0026rdquo; nailed down, my goal to always shrink the iteration loop seems like a helpful approach. In this case, it led me to stage the container exactly how I want it, but then I can run various commands iteractively to solve the problems I\u0026rsquo;m having.\nI didn\u0026rsquo;t know the difference between RUN and CMD in a Dockerfile. But it has completely different outcomes on the build process. But just being me, I commented out the RUN to let the image build successfully. Then I ran the container and iterated on the bug until I solved it. It\u0026rsquo;s the same process I used to install ruby on the container in the first place.\nThis is also a good example of how learning one language makes it easier to learn other languages. I don\u0026rsquo;t know ruby. But by knowing python, I quickly troubleshot:\n installing a gem: since gem is to ruby as pip is to python accessing environment variables from the script: I knew there would be an object of some king, hopefully a map. But it was also a simple google search using a \u0026ldquo;safe get\u0026rdquo; on a map: this one felt like a bigger stretch in my mind, but it was also just a quick google search There was a moment when I debated whether I should even bother with all of this ruby. The goal of this exercise is to see what I know about docker, not ruby. And given that the telephone.rb file is so simple, I debated just translating it straight to python (or even curl). But actully, because it was so small and the idea of the program is so simple, I chose to stick it out.\n","href":"/blog/docker-practice/","title":"KAllan's Telephone and Answering Machine Notes"},{"content":"","href":"/tags/clojurescript/","title":"clojurescript"},{"content":"Outcomes By the end of this post, you will be able to setup a new clojurescript project, setup emacs to be your development environment, and deploy your app to Github Pages\nBackground I\u0026rsquo;ve written two clojurescript projects now, Sundial and DataBooze, and I want to document the set up process. The first time was a massive undertaking because it was my first time. But frustrating enough, so was the second time, even though I thought I nailed down the process and took notes on it. The process didn\u0026rsquo;t work and the notes don\u0026rsquo;t exist, so here we are today.\nPre-requisites There are only three things you need to start: an editor, a terminal, and leiningen. For me, this means Emacs, bash, and leiningen installed through my package manager.\nInstructions In your terminal, navigate to where you want the project to live and run\n$ lein new figwheel-main my-awesome-project -- --reagent You should see a new directory called my-awesome-project. There\u0026rsquo;s a basic skeleton that is created for you, so we want to check that it works\n$ cd my-awesome-project $ lein fig:build This will compile the clojurescript into javascript, serve the project on (by default) http://localhost:9500, and give you a clojurescript REPL.\nIf you don\u0026rsquo;t use emacs, I recommend you leave this REPL open and start editing the code in src/my_awesome_project/core.cljs. If you do use emacs, I recommend setting up CIDER and getting nREPL to work.\nFirst we open emacs and install CIDER\nM-x package-refresh-contents [RET] M-x package-install [RET] cider [RET] When I run M-x cider-version, I get CIDER 0.26.0-snapshot.\nIf you haven\u0026rsquo;t already, kill the REPL we opened from lein fig:build. We\u0026rsquo;re going to start a new one in emacs.\nOpen src/my_awesome_project/core.cljs in a buffer. Then run\nM-x cider-jack-in-cljs [RET] A series of prompts will appear, answer with:\n lein This one most likely will not appear, but I saw it once and the other option was shadow-cljs. I didn\u0026rsquo;t try to pick shadow-cljs because it just worked when I picked lein figwheel-main dev This will give you a buffer with output similar to what we got at the terminal. You can edit the code in core.cljs and see the UI reload when you save. More importantly, you can evaluate the clojurescript in your buffer.\nDeploying to Github Pages I\u0026rsquo;m assuming this would work for Gitlab Pages or Bitbucket Pages, but I use Github and didn\u0026rsquo;t test those options.\nCreate a branch called gh-pages. You\u0026rsquo;ll just need 3 files at the root of your project: index.html, style.css, and main.js. Obviously, the names are up to you, but here\u0026rsquo;s where I got the files.\nindex.html is just some boilerplate HTML. Here\u0026rsquo;s one you can use:\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;My title\u0026lt;/title\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; type=\u0026#34;text/css\u0026#34; href=\u0026#34;style.css\u0026#34;/\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;app\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;script src=\u0026#34;main.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; The important part here is that you have some element with id=\u0026quot;app\u0026quot; because that\u0026rsquo;s how we hook up the javascript to the project. But again, even that is configurable.\nTo get style.css and main.js, I run these commands at the root of the project\n$ lein fig:min 2020-07-06 09:13:50.378:INFO::main: Logging initialized @4805ms to org.eclipse.jetty.util.log.StdErrLog [Figwheel] Validating figwheel-main.edn [Figwheel] figwheel-main.edn is valid \\(ツ)/ [Figwheel] Compiling build dev to \u0026#34;resources/public/cljs-out/dev-main.js\u0026#34; [Figwheel] Successfully compiled build dev to \u0026#34;resources/public/cljs-out/dev-main.js\u0026#34; in 12.307 seconds. $ cp resources/public/cljs-out/dev-main.js main.js $ cp resources/public/css/style.css . So now you can commit the HTML, CSS, and JS to the gh-pages branch, push, turn on Github Pages deploys in the Settings tab of your repo, and see your live project.\n","href":"/blog/clojurescript-project-setup/","title":"Clojurescript Project Setup"},{"content":"","href":"/","title":""},{"content":"this is my archive\n","href":"/archive/","title":"Archive"}]