<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<title>Julio Capote's Blog - Feed</title>
<id>tag:capotej.com,2005:/blog/feed</id>
<link href="https://capotej.com"/>
<link rel="self" type="application/atom+xml" href="https://capotej.com/atom.xml"/>
<updated>2026-06-01T00:31:34Z</updated>
<entry>
<title>The first thing I've been adding to AGENTS.md</title>
<link href="https://capotej.com/2026/06/01/the-first-thing-i-ve-been-adding-to-agents-md/"/>
<id>808abe8dc4e19f5e1038c501841db385340a9efd8840ceb6d543475c451af826</id>
<published>2026-06-01T00:27:17Z</published>
<updated>2026-06-01T00:31:34Z</updated>
<summary type="html">&lt;p&gt;Dive into agentic coding long enough and value of a separating the planning process from the implementation process becomes obvious. These planning artifacts have many names and varying levels of complexity (“.plan”, “SPEC.md”, “PRD”, etc.), occasionally even distributing skills to assist in creating and implementing them.&lt;/p&gt;&lt;p&gt;In this post I’ll share a skill-free, lightweight version of this that has been working well for me lately.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;The first thing I’ve been adding to AGENTS.md&lt;/h2&gt;&lt;p&gt;My projects these days will usually begin with an &lt;code&gt;AGENTS.md&lt;/code&gt; file where I’ll define the stack of linters and context required for an agent to successfully stumble onto a working solution (most of the time, anyway).&lt;/p&gt;&lt;p&gt;But lately, I’ve been starting with just the following snippet:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;## RFCs

Significant changes, architectural decisions, and new features should be proposed as RFCs in the `rfcs/` directory. RFCs use the format `rfcs/YYYY-MM-DD_short_title.md` with the following structure:

- `# Title` — short descriptive title
- `**Date:**` — proposal date (ISO format)
- `**Status:**` — `Proposed`, `Accepted`, `Implemented`, or `Rejected`
- `## Goal` — what the RFC aims to accomplish
- Remaining sections are free-form but typically include motivation, technical details, migration notes, and an implementation checklist
- When moving an RFC to `Implemented`, update `AGENTS.md` and `README.md` to reflect any new infrastructure, commands, or workflows introduced by the RFC&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then in a fresh session I’ll prompt:&lt;/p&gt;&lt;blockquote&gt;“Let’s create a new RFC to &lt;em&gt;insert goal here&lt;/em&gt;. Ask me any questions to disambiguate the plan”&lt;/blockquote&gt;&lt;p&gt;(I’d imagine &lt;a href="https://github.com/mattpocock/skills/blob/main/skills/productivity/grill-me/SKILL.md"&gt;grill-me&lt;/a&gt; would work well here too).&lt;/p&gt;&lt;p&gt;After some back and fourth we have a &lt;code&gt;Proposed&lt;/code&gt; RFC pushed up as a Pull Request.&lt;/p&gt;&lt;p&gt;What I like about pushing up just the RFC is that we can discuss/socialize the plan before costly time and tokens are spent pursuing a non-obvious architectural dead-end. This is exactly how the real &lt;a href="https://en.wikipedia.org/wiki/Request_for_Comments"&gt;RFC process works&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Another benefit is once the RFC is moved to &lt;code&gt;Implemented&lt;/code&gt;, the RFC stays within the repository forever, effectively turning them into a free &lt;a href="https://github.com/architecture-decision-record/architecture-decision-record"&gt;Architecture Decision Record&lt;/a&gt; (this is why the date in the filename matters).&lt;/p&gt;&lt;p&gt;I’ve already noticed my agents save research time (and tokens) while also avoiding repeat design mistakes by leveraging previously &lt;code&gt;Implemented&lt;/code&gt; RFCs.&lt;/p&gt;</content>
</entry>
<entry>
<title>Running an aarch64 Fedora VM on MacOS using QEMU 10</title>
<link href="https://capotej.com/2025/09/06/running-aarch64-fedora-vm-on-macos-using-qemu-10/"/>
<id>28c30e80eda3637c523149cfcd2027edc652b2e2b71a8d1952f70ea3c634227f</id>
<published>2025-09-06T18:47:34Z</published>
<updated>2025-09-07T01:23:49Z</updated>
<summary type="html">&lt;p&gt;This was surprisingly tricky to figure out since I had to consult a combination of QEMU docs, StackOverflow and GitHub issues, many of which had outdated arguments and options, or didn’t apply for &lt;code&gt;aarch64&lt;/code&gt;/&lt;code&gt;arm&lt;/code&gt; architectures. Writing out the steps that worked for me as a guide in case they help others.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Running an aarch64 Fedora VM on MacOS using QEMU 10&lt;/h2&gt;&lt;h4&gt;“Works on my machine”&lt;/h4&gt;&lt;p&gt;The following configuration works on my 2021 M1 Max laptop running MacOS Sequoia 15.6.1.&lt;/p&gt;&lt;h4&gt;ARM Architecture&lt;/h4&gt;&lt;p&gt;Most of the guides I found were focused on emulating &lt;code&gt;amd64&lt;/code&gt;/&lt;code&gt;x86-64&lt;/code&gt; architectures. This guide instead targets the &lt;code&gt;aarch64&lt;/code&gt;/&lt;code&gt;arm64&lt;/code&gt; architecture (same as the host system) to minimize emulation overhead.&lt;/p&gt;&lt;h4&gt;Steps&lt;/h4&gt;&lt;p&gt;These steps should get you a Fedora Server VM that you can SSH into from your host OS.&lt;/p&gt;&lt;h5&gt;Install QEMU&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ brew install qemu&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Download Fedora Server filesystem image&lt;/h5&gt;&lt;p&gt;Thankfully, Fedora provides ready-to-go filesystem images on their &lt;a href="https://fedoraproject.org/server/download"&gt;downloads&lt;/a&gt; page. Download the QEMU &lt;code&gt;qcow2&lt;/code&gt; filesystem image for the latest server version (42 as of writing).&lt;/p&gt;&lt;h5&gt;Resize the Fedora Server filesystem image&lt;/h5&gt;&lt;p&gt;The Fedora Server image downloaded in the previous step is only large enough to fit the base installation and will likely need to be resized for your needs. You can use &lt;code&gt;qemu-img&lt;/code&gt; to resize it like so:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ qemu-img resize Fedora-Server-Guest-Generic-42-1.1.aarch64.qcow2 15G&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Where &lt;code&gt;15G&lt;/code&gt; is the target size.&lt;/p&gt;&lt;h5&gt;Create a startup script &lt;code&gt;start.sh&lt;/code&gt;&lt;/h5&gt;&lt;p&gt;Create a file &lt;code&gt;start.sh&lt;/code&gt; that will contain the &lt;code&gt;qemu&lt;/code&gt; invocation for your new VM.&lt;/p&gt;&lt;p&gt;I found it helpful to check this script into git while iterating on different qemu arguments so I could rollback to working versions.&lt;/p&gt;&lt;p&gt;The script:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;qemu-system-aarch64 \
    -name "Fedora" \
    -machine type=virt,iommu=smmuv3 \
    -accel hvf \
    -cpu cortex-a57 \
    -smp 4 \
    -m 2048 \
    -rtc base=utc \
    -drive if=pflash,format=raw,file="/opt/homebrew/share/qemu/edk2-aarch64-code.fd",readonly=on \
    -drive if=pflash,format=raw,file="/opt/homebrew/share/qemu/edk2-arm-vars.fd" \
    -drive file="Fedora-Server-Guest-Generic-42-1.1.aarch64.qcow2",if=virtio,format=qcow2 \
    -boot menu=on \
    -display default \
    -device virtio-gpu \
    -nic user,id=NAT,model=virtio-net-pci,mac=02:00:00:00:00:01,hostfwd=tcp::2222-:22 \
    -device qemu-xhci \
    -device usb-kbd \
    -device usb-tablet&lt;/code&gt;&lt;/pre&gt;&lt;h6&gt;Argument Explanation&lt;/h6&gt;&lt;p&gt;Some of these arguments are worth paying attention to, I’ll list the important ones below.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;accel hvf&lt;/code&gt;: This sets the acceleration framework to the use native &lt;a href="https://developer.apple.com/documentation/hypervisor"&gt;MacOS Hypervisor Framework&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;m 2048&lt;/code&gt;: Give the VM 2GB of RAM.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;smp 4&lt;/code&gt;: Give the VM 4 CPUs.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;-nic user,id=NAT,model=virtio-net-pci,mac=02:00:00:00:00:01,hostfwd=tcp::2222-:22&lt;/code&gt;: The &lt;code&gt;hostfwd&lt;/code&gt; option forwards port 2222 on the host OS to port 22 (ssh) on the guest OS.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5&gt;Start the VM&lt;/h5&gt;&lt;p&gt;Boot up the VM using &lt;code&gt;start.sh&lt;/code&gt;&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ sh start.sh&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should see a window open up like so:&lt;/p&gt;&lt;p&gt;&lt;img src="/assets/2025-09-06-qemu-vm-1.png" alt="Screenshot 2025-09-06 at 3.12.26 PM.png"&gt;&lt;/p&gt;&lt;p&gt;Then you’ll see it say “Display output is not active.”, this is okay, just wait a bit, eventually you will see the machine boot.&lt;/p&gt;&lt;p&gt;&lt;img src="/assets/2025-09-06-qemu-vm-2.png" alt="Screenshot 2025-09-06 at 3.13.30 PM.png"&gt;&lt;/p&gt;&lt;h5&gt;Configure Fedora Server&lt;/h5&gt;&lt;p&gt;The first time you boot Fedora Server there is a clunky setup text UI thing for setting up a non-root user and giving it a password. Once this is done and you can hit &lt;code&gt;c&lt;/code&gt; to continue, your VM is good to go.&lt;/p&gt;&lt;h5&gt;SSH from Host OS&lt;/h5&gt;&lt;p&gt;You should now be able to SSH into the Guest OS using the username and password created in the previous step by using port 2222, like so:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ ssh capotej@localhost -P 2222&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Grow Guest filesystem to use new size&lt;/h5&gt;&lt;p&gt;In order for the Guest OS to take advantage of new size from the &lt;code&gt;qemu-resize&lt;/code&gt; above, we need to grow the root partition as well as extend our logical volume using the commands below.&lt;/p&gt;&lt;h6&gt;Extend the partition&lt;/h6&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ sudo growpart /dev/vda 3&lt;/code&gt;&lt;/pre&gt;&lt;h6&gt;Extend the physical volume&lt;/h6&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ sudo pvresize /dev/vda3&lt;/code&gt;&lt;/pre&gt;&lt;h6&gt;Extend the logical volume&lt;/h6&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ sudo lvextend -l +100%FREE /dev/systemVG/LVRoot&lt;/code&gt;&lt;/pre&gt;&lt;h6&gt;Resize the filesystem&lt;/h6&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ sudo xfs_growfs /&lt;/code&gt;&lt;/pre&gt;&lt;h6&gt;Verify extra space&lt;/h6&gt;&lt;p&gt;You can verify that &lt;code&gt;/&lt;/code&gt; has more space by running &lt;code&gt;df&lt;/code&gt; like so&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ df -h /&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Shutting down the VM&lt;/h5&gt;&lt;p&gt;You can use the standard &lt;code&gt;shutdown&lt;/code&gt; command to shutdown the VM while logged into ssh:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ sudo shutdown -h now&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Headless Mode&lt;/h5&gt;&lt;p&gt;Once you have your VM setup, you can edit &lt;code&gt;start.sh&lt;/code&gt; and change &lt;code&gt;display&lt;/code&gt; to :&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;-display none -serial null \&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then when you run &lt;code&gt;start.sh&lt;/code&gt; it will run the VM in the foreground without a display. You can use &lt;code&gt;Ctrl-C&lt;/code&gt; to stop the VM from the host OS.&lt;/p&gt;&lt;p&gt;Your VM should now be ready for experimentation, happy VMing!&lt;/p&gt;</content>
</entry>
<entry>
<title>Tmux Session Coloring</title>
<link href="https://capotej.com/2014/02/24/tmux-session-coloring/"/>
<id>283e45d5f245322028bcd80bdd36e893c0b4978ac25a9c7df21dc324fcec7f05</id>
<published>2014-02-24T00:00:00Z</published>
<updated>2025-01-27T14:23:55Z</updated>
<summary type="html">&lt;p&gt;Recently, I’ve really gotten into tmux for managing all my terminal sessions/windows. Not so much for the panes, but more for keeping a highly contextual environment per project or task.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Tmux Session Coloring&lt;/h2&gt;&lt;p&gt;Recently, I’ve really gotten into tmux for managing all my terminal sessions/windows. Not so much for the panes, but more for keeping a highly contextual environment per project or task.&lt;/p&gt;&lt;p&gt;As the number of sessions grew, they became difficult to tell apart. For a few days now, I’ve had the idea of hashing the name of the session into a unique color, so that every session had its own &lt;code&gt;status-bg&lt;/code&gt; color.&lt;/p&gt;&lt;p&gt;First, the &lt;code&gt;tmuxHashColor&lt;/code&gt; function:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;tmuxHashColor() {
  local hsh=$(echo $1 | cksum | cut -d ' ' -f 1)
  local num=$(expr $hsh % 255)
  echo "colour$num"
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In our &lt;code&gt;ns&lt;/code&gt; function (new session), we hash the supplied session name to a color, then use &lt;code&gt;tmux send-keys&lt;/code&gt; to set its &lt;code&gt;status-bg&lt;/code&gt; color to it:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;ns() {
  if [ -z $1 ]; then
    1=$(basename $(pwd))
  fi
  tmux new-session -d -s $1
  local color=$(tmuxHashColor $1)
  tmux send-keys -t $1 "tmux set-option status-bg $color" C-m
  tmux send-keys -t $1 "clear" C-m
  tmux attach -t $1
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now every session has it’s own distinct &lt;code&gt;status-bg&lt;/code&gt; color!&lt;/p&gt;</content>
</entry>
<entry>
<title>Golang HTTP Handlers as Middleware</title>
<link href="https://capotej.com/2013/10/07/golang-http-handlers-as-middleware/"/>
<id>6926b97cb61c63561079613895344d545017fbbc6e363ddf6e858036682cc8cc</id>
<published>2013-10-07T08:52:00Z</published>
<updated>2025-01-28T01:48:23Z</updated>
<summary type="html">&lt;p&gt;Most modern web stacks allow the “filtering” of requests via stackable/composable middleware, allowing you to cleanly separate cross-cutting concerns from your web application. This weekend I needed to hook into go’s &lt;code&gt;http.FileServer&lt;/code&gt; and was pleasantly surprised how easy it was to do.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Golang HTTP Handlers as Middleware&lt;/h2&gt;&lt;p&gt;Most modern web stacks allow the “filtering” of requests via stackable/composable middleware, allowing you to cleanly separate cross-cutting concerns from your web application. This weekend I needed to hook into go’s &lt;code&gt;http.FileServer&lt;/code&gt; and was pleasantly surprised how easy it was to do.&lt;/p&gt;&lt;p&gt;Let’s start with a basic file server for &lt;code&gt;/tmp&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;go main.go
func main() {
    http.ListenAndServe(":8080", http.FileServer(http.Dir("/tmp")))
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This starts up a local file server at :8080. How can we hook into this so we can run some code before file requests are served? Let’s look at the method signature for &lt;code&gt;http.ListenAndServe&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="go"&gt;&lt;span style="color: #cba6f7"&gt;func&lt;/span&gt; &lt;em&gt;&lt;span style="color: #89b4fa"&gt;ListenAndServe&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;addr&lt;/span&gt;&lt;/em&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;string&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;em&gt;&lt;span style="color: #eba0ac"&gt;handler&lt;/span&gt;&lt;/em&gt; &lt;span style="color: #cba6f7"&gt;Handler&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;error&lt;/span&gt;&lt;/em&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So it looks like &lt;code&gt;http.FileServer&lt;/code&gt; returns a &lt;code&gt;Handler&lt;/code&gt; that knows how to serve files given a root directory. Now let’s look at the &lt;code&gt;Handler&lt;/code&gt; interface:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="go"&gt;&lt;span style="color: #cba6f7"&gt;type&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;Handler&lt;/span&gt;&lt;/em&gt; &lt;span style="color: #cba6f7"&gt;interface&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;    &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;ServeHTTP&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;ResponseWriter&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;*&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;Request&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Because of go’s granular interfaces, any object can be a &lt;code&gt;Handler&lt;/code&gt; so long as it implements &lt;code&gt;ServeHTTP&lt;/code&gt;. It seems all we need to do is construct our own &lt;code&gt;Handler&lt;/code&gt; that wraps &lt;code&gt;http.FileServer&lt;/code&gt;’s handler. There’s a built in helper for turning ordinary functions into handlers called &lt;code&gt;http.HandlerFunc&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="go"&gt;&lt;span style="color: #cba6f7"&gt;type&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;HandlerFunc&lt;/span&gt;&lt;/em&gt; &lt;span style="color: #cba6f7"&gt;func&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;ResponseWriter&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;*&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;Request&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then we just wrap &lt;code&gt;http.FileServer&lt;/code&gt; like so:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="go"&gt;&lt;span style="color: #cba6f7"&gt;func&lt;/span&gt; &lt;em&gt;&lt;span style="color: #89b4fa"&gt;OurLoggingHandler&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;h&lt;/span&gt;&lt;/em&gt; http&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;Handler&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt; http&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;Handler&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
  &lt;span style="color: #cba6f7"&gt;return&lt;/span&gt; http&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;HandlerFunc&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;func&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;w&lt;/span&gt;&lt;/em&gt; http&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;ResponseWriter&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;em&gt;&lt;span style="color: #eba0ac"&gt;r&lt;/span&gt;&lt;/em&gt; &lt;span style="color: #94e2d5"&gt;*&lt;/span&gt;http&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;Request&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
    fmt&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;Println&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;*&lt;/span&gt;r&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;URL&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
    h&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;ServeHTTP&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;w&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; r&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
  &lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;

&lt;span style="color: #cba6f7"&gt;func&lt;/span&gt; &lt;em&gt;&lt;span style="color: #89b4fa"&gt;main&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
    fileHandler &lt;span style="color: #94e2d5"&gt;:=&lt;/span&gt; http&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;FileServer&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;http&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;Dir&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;/tmp&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
    wrappedHandler &lt;span style="color: #94e2d5"&gt;:=&lt;/span&gt; &lt;em&gt;&lt;span style="color: #89b4fa"&gt;OurLoggingHandler&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;fileHandler&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
    http&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;ListenAndServe&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;:8080&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; wrappedHandler&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Go has a bunch of other builtin &lt;a href="http://golang.org/pkg/net/http/#Handler"&gt;handlers&lt;/a&gt; like &lt;a href="http://golang.org/pkg/net/http/#TimeoutHandler"&gt;TimeoutHandler&lt;/a&gt; and &lt;a href="http://golang.org/pkg/net/http/#RedirectHandler"&gt;RedirectHandler&lt;/a&gt; that can be mixed and matched the same way.&lt;/p&gt;</content>
</entry>
<entry>
<title>Playing with groupcache</title>
<link href="https://capotej.com/2013/07/28/playing-with-groupcache/"/>
<id>7306ec2e9caa6847364506b6919882c51d9e71d10d7bbe25e0f6d0954d221bf9</id>
<published>2013-07-28T14:49:00Z</published>
<updated>2025-01-27T00:37:12Z</updated>
<summary type="html">&lt;p&gt;This week, &lt;a href="http://twitter.com/bradfitz"&gt;@bradfitz&lt;/a&gt; (of memcached fame) released &lt;a href="http://github.com/golang/groupcache"&gt;groupcache&lt;/a&gt; at OSCON 2013. I’m already a big fan of &lt;a href="http://memcached"&gt;memcached&lt;/a&gt; and &lt;a href="http://camlistore.org"&gt;camlistore&lt;/a&gt;, so I couldn’t wait to download it and kick the tires.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Playing with groupcache&lt;/h2&gt;&lt;p&gt;This week, &lt;a href="http://twitter.com/bradfitz"&gt;@bradfitz&lt;/a&gt; (of memcached fame) released &lt;a href="http://github.com/golang/groupcache"&gt;groupcache&lt;/a&gt; at OSCON 2013. I’m already a big fan of &lt;a href="http://memcached"&gt;memcached&lt;/a&gt; and &lt;a href="http://camlistore.org"&gt;camlistore&lt;/a&gt;, so I couldn’t wait to download it and kick the tires.&lt;/p&gt;&lt;p&gt;By the way, I &lt;strong&gt;strongly&lt;/strong&gt; recommend you go through the &lt;a href="http://talks.golang.org/2013/oscon-dl.slide#1"&gt;slides&lt;/a&gt; and &lt;a href="http://github.com/golang/groupcache"&gt;README&lt;/a&gt; before going further.&lt;/p&gt;&lt;h4&gt;What groupcache is not&lt;/h4&gt;&lt;p&gt;After downloading it (without reading the &lt;a href="http://talks.golang.org/2013/oscon-dl.slide#1"&gt;slides&lt;/a&gt;), I instinctively searched around for how to actually start the server(s), only to find nothing. Turns out, groupcache is more of a &lt;em&gt;library&lt;/em&gt; with a server built in, rather than a traditional standalone server. Another important consideration is that theres &lt;strong&gt;no support for set/update/evict operations&lt;/strong&gt;, all you get is GET. Really fast, consistent, distributed GET’s.&lt;/p&gt;&lt;h4&gt;What it is&lt;/h4&gt;&lt;p&gt;Once you realize that groupcache is more of a &lt;strong&gt;smart, distributed LRU cache&lt;/strong&gt;, rather than an outright memcached replacement, it all makes much more sense. Especially considering what it was built for, caching immutable file blobs for &lt;a href="http://dl.google.com"&gt;dl.google.com&lt;/a&gt;.&lt;/p&gt;&lt;h4&gt;How to use it&lt;/h4&gt;&lt;p&gt;For groupcache to work, you have to give it a closure in which: given a &lt;code&gt;key&lt;/code&gt;, fill up this &lt;code&gt;dest&lt;/code&gt; buffer with the bytes for the value of that key, from however you store them. This could be hitting a database, a network filesystem, anything. Then you create a groupcache &lt;code&gt;group&lt;/code&gt; object, which knows the addresses of all the other groupcache instances. This is pluggable, so you can imagine rigging that up to zookeeper or the like for automatic node discovery. Finally, you start groupcache up by using go’s built in &lt;code&gt;net/http&lt;/code&gt; and a &lt;code&gt;ServeHTP&lt;/code&gt; provided by the previously constructed &lt;code&gt;group&lt;/code&gt; object.&lt;/p&gt;&lt;h4&gt;Running the demo&lt;/h4&gt;&lt;p&gt;In order to really try out groupcache, I realized I needed to create a mini test infrastructure, consisting of a slow database, frontends, and a client. Visit the &lt;a href="http://github.com/capotej/groupcache-db-experiment"&gt;Github Repo&lt;/a&gt; for more details. This is what the topology looks like:&lt;/p&gt;&lt;p&gt;&lt;img src="/assets/2013-07-28-groupcache-topology.png" alt="groupcache topology"&gt;&lt;/p&gt;&lt;h6&gt;Setup&lt;/h6&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;git clone git@github.com:capotej/groupcache-db-experiment.git&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;cd groupcache-db-experiment&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;sh build.sh&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h6&gt;Start database server&lt;/h6&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;cd dbserver &amp;amp;&amp;amp; ./dbserver&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h6&gt;Start Multiple Frontends&lt;/h6&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;cd frontend&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;./frontend -port 8001&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;./frontend -port 8002&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;./frontend -port 8003&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h6&gt;Use the CLI to play around&lt;/h6&gt;&lt;p&gt;Let’s set a value into the database:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;./cli -set -key foo -value bar&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now get it out again to make sure it’s there:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;./cli -get -key foo&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should see &lt;code&gt;bar&lt;/code&gt; as the response, after about a noticeable, 300ms lag.&lt;/p&gt;&lt;p&gt;Let’s ask for the same value, via cache this time:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;./cli -cget -key foo&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should see on one of the frontend’s output, the key &lt;code&gt;foo&lt;/code&gt; was requested, and in turn requested from the database. Let’s get it again:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;./cli -cget -key foo&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should have gotten this value instantly, as it was served from groupcache.&lt;/p&gt;&lt;p&gt;Here’s where things get interesting; Request that same key from a different frontend:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;./cli -port 9002 -cget -key foo&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should still see &lt;code&gt;bar&lt;/code&gt; come back instantly, even though this particular groupcache node did not have this value. This is because groupcache knew that 9001 had this key, went to that node to fetch it, then cached it itself. &lt;strong&gt;This is groupcache’s killer feature&lt;/strong&gt;, as it avoids the common thundering herd issue associated with losing cache nodes.&lt;/p&gt;&lt;h6&gt;Node failure&lt;/h6&gt;&lt;p&gt;Let’s simulate single node failure, find the “owner” of key &lt;code&gt;foo&lt;/code&gt; (this is going to be the frontend that said “asking for foo from dbserver”), and kill it with Ctrl+C. Request the value again:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;./cli -cget -key foo&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It’ll most likely hit the dbserver again (unless that particular frontend happens to have it), and cache the result on one of the other remaining frontends. As more clients ask for this value, it’ll spread through the caches organically. When that server comes back up, it’ll start receiving other keys to share, and so on. The fan out is explained in more detail on this &lt;a href="http://talks.golang.org/2013/oscon-dl.slide#47"&gt;slide&lt;/a&gt;.&lt;/p&gt;&lt;h4&gt;Conclusion / Use cases&lt;/h4&gt;&lt;p&gt;Since there is no support (by design) for eviction or updates, groupcache is a really good fit with read heavy, immutable content. Some use cases:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Someone like &lt;a href="http://github.com"&gt;Github&lt;/a&gt; using it to cache blobrefs from their file servers&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Large websites using it as a CDN (provided their assets were unique &lt;code&gt;logo-0492830483.png&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Backend for &lt;a href="http://en.wikipedia.org/wiki/Content-addressable_storage"&gt;Content-addressable storage&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Definitely a clever tool to have in the distributed systems toolbox.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Shout out to professors &lt;a href="http://twitter.com/jmhodges"&gt;@jmhodges&lt;/a&gt; and &lt;a href="http://twitter.com/mrb_bk"&gt;@mrb_bk&lt;/a&gt; for proof reading this project and post&lt;/em&gt;&lt;/p&gt;</content>
</entry>
<entry>
<title>Automatic High Quality Releases</title>
<link href="https://capotej.com/2012/11/13/automatic-high-quality-releases/"/>
<id>af4aa6f0cdc4280934f926c05befe133e9f50392f08ce82e3aa75c440299bbd1</id>
<published>2012-11-13T21:31:00Z</published>
<updated>2025-01-28T01:48:52Z</updated>
<summary type="html">&lt;p&gt;Recently, I invested some time into automating some of the work that goes into a &lt;a href="http://github.com/capotej/finatra#readme"&gt;Finatra&lt;/a&gt; release.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Automatic High Quality Releases&lt;/h2&gt;&lt;p&gt;Recently, I invested some time into automating some of the work that goes into a &lt;a href="http://github.com/capotej/finatra#readme"&gt;Finatra&lt;/a&gt; release.&lt;/p&gt;&lt;p&gt;The work consists of updating:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;The version in the XML fragment of the main &lt;a href="https://github.com/finatra/https://github.com/capotej/finatra/blob/master/README.markdown"&gt;README.markdown&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The version in the &lt;a href="https://github.com/capotej/finatra-example/blob/master/pom.xml"&gt;pom.xml&lt;/a&gt; of the &lt;a href="http://github.com/capotej/finatra-example"&gt;example app&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Any API changes in the &lt;a href="http://github.com/capotej/finatra-example"&gt;example app&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The version in the template pom.xml of the app generator&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The generated unit test of the app generator that demonstrates testing any new API features&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Any API changes inside the app template of the app generator&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Using &lt;a href="https://github.com/37signals/sub#readme"&gt;sub&lt;/a&gt;, I was able to create a &lt;code&gt;finatra&lt;/code&gt; &lt;a href="https://github.com/capotej/finatra/tree/master/script/finatra"&gt;command&lt;/a&gt; that automated all of the above based on a single template, which also happens to be the &lt;a href="https://github.com/capotej/finatra/blob/master/src/test/scala/com/twitter/finatra/ExampleSpec.scala"&gt;main unit test&lt;/a&gt;. This ensures that the README, the example app, and the app generator never fall out of sync with the frameworks API.&lt;/p&gt;&lt;p&gt;Last week we released &lt;a href="https://github.com/capotej/finatra/commit/37c81957271dde77d4c3f6361bbae705a5142c89"&gt;1.1.0&lt;/a&gt;, and the README was &lt;a href="https://github.com/capotej/finatra/commit/913d0ed5bfa18c903feb5779d4d8b9d87703b6c5"&gt;completely generated&lt;/a&gt;, as was the &lt;a href="https://github.com/capotej/finatra-example/commit/dbc82908360f3cb4cfc4388c28f593f17258fab2"&gt;example app&lt;/a&gt;. Not to mention, all generated apps would also contain the latest templates and examples!&lt;/p&gt;&lt;p&gt;&lt;img src="/assets/2012-11-13-releases.jpg"&gt;&lt;/p&gt;&lt;p&gt;Let’s dive into how it all works:&lt;/p&gt;&lt;h4&gt;The source of truth&lt;/h4&gt;&lt;p&gt;I annotated our main unit test with special tokens, like so:&lt;/p&gt;&lt;p&gt;```scala ExampleAppSpec.scala class ExampleSpec extends SpecHelper {&lt;/p&gt;&lt;p&gt;/* ###BEGIN_APP### */&lt;/p&gt;&lt;p&gt;class ExampleApp extends Controller {&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/**
 * Basic Example
 *
 * curl http://localhost:7070/hello =&gt; "hello world"
 */
get("/") { request =&gt;
  render.plain("hello world").toFuture
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;}&lt;/p&gt;&lt;p&gt;val app = new ExampleApp&lt;/p&gt;&lt;p&gt;/* ###END_APP### */&lt;/p&gt;&lt;p&gt;/* ###BEGIN_SPEC### */&lt;/p&gt;&lt;p&gt;“GET /hello” should “respond with hello world” in { get(“/”) response.body should equal (“hello world”) }&lt;/p&gt;&lt;p&gt;/* ###END_SPEC### */ } ```&lt;/p&gt;&lt;p&gt;Using the special &lt;code&gt;/* ### */&lt;/code&gt; comments, the main app and its test can be extracted from the code of our test.&lt;/p&gt;&lt;h4&gt;The app generator&lt;/h4&gt;&lt;p&gt;Now that we have our “template”, we can build our app generator to use it. I customized &lt;a href="http://capotej.com/blog/2012/11/01/base-a-scala-project-generator/"&gt;base&lt;/a&gt; and ended up with: &lt;a href="https://github.com/capotej/finatra/blob/master/script/finatra/libexec/finatra-new"&gt;script/finatra/libexec/finatra-new&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You can then run:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ ./finatra new com.example.myapp&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and it will generate &lt;code&gt;myapp/&lt;/code&gt; based on the tested example code from the test suite above.&lt;/p&gt;&lt;h4&gt;The example app&lt;/h4&gt;&lt;p&gt;The &lt;a href="https://github.com/capotej/finatra-example#readme"&gt;example app&lt;/a&gt; is just a generated app using the latest app generator:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;#!/bin/bash
# Usage: finatra update-example
# Summary: generates the example app from the template

set -e

source $_FINATRA_ROOT/lib/base.sh

tmpdir=$(mktemp -d /tmp/finatra_example.XXX)

$_FINATRA_ROOT/bin/finatra new com.twitter.finatra_example $tmpdir

cp -Rv $tmpdir/finatra_example/ $EXAMPLE_REPO

rm -rf $tmpdir

cd $EXAMPLE_REPO &amp;amp;&amp;amp; mvn test&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This also tests the app generator and the generated app!&lt;/p&gt;&lt;h4&gt;Updating the README&lt;/h4&gt;&lt;p&gt;Lastly, there’s a &lt;a href="https://github.com/capotej/finatra/blob/master/script/finatra/libexec/finatra-update-readme"&gt;command&lt;/a&gt; for updating the README with the new example and version number.&lt;/p&gt;</content>
</entry>
<entry>
<title>Announcing Finatra 1.0.0</title>
<link href="https://capotej.com/2012/11/07/announcing-finatra-1-0-0/"/>
<id>43abcd082ee4bf2f645ebec1f3f76fb95dc3281e57f5fa797487220617d5b8ed</id>
<published>2012-11-07T21:20:00Z</published>
<updated>2025-01-28T01:49:13Z</updated>
<summary type="html">&lt;p&gt;After months of work &lt;a href="https://github.com/capotej/finatra#readme"&gt;Finatra&lt;/a&gt; 1.0.0 is finally available! Finatra is a scala web framework inspired by &lt;a href="https://github.com/sinatra/sinatra#readme"&gt;Sinatra&lt;/a&gt; built on top of &lt;a href="http://twitter.github.com/finagle"&gt;Finagle&lt;/a&gt;.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Announcing Finatra 1.0.0&lt;/h2&gt;&lt;p&gt;After months of work &lt;a href="https://github.com/capotej/finatra#readme"&gt;Finatra&lt;/a&gt; 1.0.0 is finally available! Finatra is a scala web framework inspired by &lt;a href="https://github.com/sinatra/sinatra#readme"&gt;Sinatra&lt;/a&gt; built on top of &lt;a href="http://twitter.github.com/finagle"&gt;Finagle&lt;/a&gt;.&lt;/p&gt;&lt;h5&gt;The API&lt;/h5&gt;&lt;p&gt;The API looks like what you’d expect, here’s a simple endpoint that uses route parameters:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="scala"&gt;get&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;/user/:username&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt; request &lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&gt;
  &lt;span style="color: #cba6f7"&gt;val&lt;/span&gt; &lt;span style="color: #fab387"&gt;username&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;=&lt;/span&gt; request&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;routeParams&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;getOrElse&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;username&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;default_user&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
  render&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;plain&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;hello &lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; + username&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;toFuture
&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;toFuture&lt;/code&gt; call means that the response is actually a &lt;a href="http://twitter.github.com/scala_school/finagle.html#Future"&gt;Future&lt;/a&gt;, a powerful concurrency abstraction worth checking out.&lt;/p&gt;&lt;p&gt;Testing it is just as easy:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="scala"&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;GET /user/foo&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; should &lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;responds with hello foo&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; in &lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
  get&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;/user/foo&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
  response&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;body should equal &lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;hello foo&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;A super quick demo&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ git clone https://github.com/capotej/finatra.git
$ cd finatra
$ ./finatra new com.example.myapp /tmp&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now you have an &lt;code&gt;/tmp/myapp&lt;/code&gt; you can use:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ cd /tmp/myapp
$ mvn scala:run&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A simple app should’ve started up locally on port 7070, verify with:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ curl http://localhost:7070
hello world&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can see the rest of the endpoints at &lt;code&gt;/tmp/myapp/src/main/scala/com/example/myapp/App.scala&lt;/code&gt;&lt;/p&gt;&lt;h5&gt;Heroku integration&lt;/h5&gt;&lt;p&gt;The generated apps work in heroku out of the box:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ heroku create
$ git init
$ git add .
$ git commit -am 'stuff'
$ git push heroku master&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Make sure to see the full details in the &lt;a href="https://github.com/capotej/finatra#readme"&gt;README&lt;/a&gt; and check out the &lt;a href="https://github.com/capotej/finatra-example"&gt;example app&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Props to &lt;a href="http://twitter.com/twoism"&gt;@twoism&lt;/a&gt; and &lt;a href="http://twitter.com/thisisfranklin"&gt;@thisisfranklin&lt;/a&gt; for their code, feedback and moral support.&lt;/p&gt;</content>
</entry>
<entry>
<title>Base: A Scala Project Generator</title>
<link href="https://capotej.com/2012/11/01/base-a-scala-project-generator/"/>
<id>c16031f11b5207e2833f72d37a4ea7be7e00bfe0010ddc511fc23af218e72c13</id>
<published>2012-11-01T14:39:00Z</published>
<updated>2025-01-28T01:49:46Z</updated>
<summary type="html">&lt;p&gt;Finally got tired of copy pasting other projects and gutting them to make new ones, so I created &lt;a href="http://github.com/capotej/base"&gt;base&lt;/a&gt;, a shell command that creates new scala projects.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Base: A Scala Project Generator&lt;/h2&gt;&lt;p&gt;Finally got tired of copy pasting other projects and gutting them to make new ones, so I created &lt;a href="http://github.com/capotej/base"&gt;base&lt;/a&gt;, a shell command that creates new scala projects.&lt;/p&gt;&lt;p&gt;Creating the project:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ base new com.capotej.newproj
creating project: newproj
  creating App.scala
  creating AppSpec.scala
  creating pom.xml
  creating .gitignore
  creating .travis.yml
  creating LICENSE
  creating README.markdown
Done! run mvn scala:run to run your project&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Based on the package name, it inferred that the project name is &lt;code&gt;newproj&lt;/code&gt; and created the project under that folder. Let’s build and run it:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ cd newproj
$ mvn compile scala:run
(... maven output ...)
hello world&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This uses the new incremental compiler for maven, &lt;a href="http://github.com/typesafehub/zinc"&gt;zinc&lt;/a&gt;, which dramatically speeds up compile times (except for the first time you run it). It also sets you up with the latest scalatest maven plugin, which gives you sweet looking test output, like so:&lt;/p&gt;&lt;p&gt;&lt;img src="/assets/2012-11-01-base.png"&gt;&lt;/p&gt;&lt;p&gt;See the base &lt;a href="http://github.com/capotej/base#readme"&gt;README&lt;/a&gt; for installation instructions.&lt;/p&gt;</content>
</entry>
<entry>
<title>Riak at Posterous</title>
<link href="https://capotej.com/2012/10/11/riak-at-posterous/"/>
<id>0a3e7e0f731326b8f24d13d2173ef91ba55da3a935f93b5ae6a292dc5646cc18</id>
<published>2012-10-11T13:47:00Z</published>
<updated>2025-01-28T01:50:10Z</updated>
<summary type="html">&lt;p&gt;A few months ago, I gave a presentation on how Posterous uses Riak for it’s post cache; At &lt;a href="http://basho.com/community/ricon2012/"&gt;#ricon2012&lt;/a&gt; I ended up retelling this story to numerous people, so I thought I’d post the slides and video here.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Riak at Posterous&lt;/h2&gt;&lt;p&gt;A few months ago, I gave a presentation on how Posterous uses Riak for it’s post cache; At &lt;a href="http://basho.com/community/ricon2012/"&gt;#ricon2012&lt;/a&gt; I ended up retelling this story to numerous people, so I thought I’d post the slides and video here.&lt;/p&gt;&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/35905739?title=0&amp;amp;byline=0&amp;amp;portrait=0&amp;amp;color=000000" width="512" height="421" allowfullscreen="true"&gt;&lt;/iframe&gt;&lt;/p&gt;&lt;p&gt;&lt;iframe src="https://www.slideshare.net/slideshow/embed_code/11160556?rel=0" width="512" height="421" allowfullscreen="true"&gt;&lt;/iframe&gt;&lt;/p&gt;</content>
</entry>
<entry>
<title>An Embedded Key/Value Store for Shell Scripts</title>
<link href="https://capotej.com/2012/10/07/an-embedded-key-value-store-for-shell-scripts/"/>
<id>1f26ee5f62a7739fa5c5801befa22f930b2f7359fda1f2dc08c577bfcaa5431d</id>
<published>2012-10-07T10:06:00Z</published>
<updated>2025-01-28T01:51:03Z</updated>
<summary type="html">&lt;p&gt;Cooked this up last night when I needed a simple key/value store for use in a shell script.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;An Embedded Key/Value Store for Shell Scripts&lt;/h2&gt;&lt;p&gt;&lt;em&gt;UPDATE: this is now available as a &lt;a href="http://github.com/37signals/sub"&gt;sub&lt;/a&gt; command, here: &lt;a href="http://github.com/capotej/kiev"&gt;kiev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Cooked this up last night when I needed a simple key/value store for use in a shell script:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;#!/bin/sh

DBFILE=example.db

put(){
  echo "export kv_$1=$2" &gt;&gt; $DBFILE
}

del(){
  echo "unset kv_$1" &gt;&gt; $DBFILE
}

get(){
  source $DBFILE
  eval r=\$$(echo "kv_$1")
  echo $r
}

list(){
  source $DBFILE
  for i in $(env | grep "kv_" | cut -d= -f1 ); do
    eval r=\$$i; echo $(echo $i | sed -e 's/kv_//') $r;
  done
}

## cmd dispatch

if [ ${1:-0} == "set" ]; then
  put $2 $3
elif [ ${1:-0} == "get" ] ; then
  get $2
elif [ ${1:-0} == "list" ] ; then
  list
elif [ ${1:-0} == "del" ] ; then
  del $2
else
  echo "unknown cmd"
fi&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Use it like so:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ ./db.sh set foo bar&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;$ ./db.sh get foo&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;$ ./db.sh set foo baz&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;$ ./db.sh get foo&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;$ ./db.sh del foo&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;$ ./db.sh list&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;How it works&lt;/h4&gt;&lt;p&gt;Every time you update/set/delete a value, it writes a shell expression to an append-only log, exporting a shell variable (key) with that value. By sourcing the file every time we read a value, we replay the log, bringing our environment to a consistent state. Then, reading the value is just looking up that dynamic variable (key) in our shell environment.&lt;/p&gt;</content>
</entry>
<entry>
<title>Finagle with scala-bootstrapper</title>
<link href="https://capotej.com/2012/01/25/finagle-with-scala-bootstrapper/"/>
<id>2d4e20ef4ea5b8aa80aed9c95853704e625aefa68a8524fccee4c29213444cdd</id>
<published>2012-01-25T09:45:00Z</published>
<updated>2025-01-26T13:48:33Z</updated>
<summary type="html">&lt;p&gt;I’ve been fascinated by the concepts in &lt;a href="http://twitter.github.com/finagle/"&gt;finagle&lt;/a&gt; for some time, but being a scala noob, I never knew how to bootstrap a finagle project.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Finagle with scala-bootstrapper&lt;/h2&gt;&lt;p&gt;I’ve been fascinated by the concepts in &lt;a href="http://twitter.github.com/finagle/"&gt;finagle&lt;/a&gt; for some time, but being a scala noob, I never knew how to bootstrap a finagle project. Turns out twitter has a gem, &lt;a href="https://github.com/twitter/scala-bootstrapper"&gt;scala-bootstrapper&lt;/a&gt;, that generates a simple thirft based key/value store for you. There’s even a&lt;a href="http://twitter.github.com/scala_school/searchbird.html"&gt;tutorial&lt;/a&gt; on how to extend the example project into a distributed search service.&lt;/p&gt;&lt;p&gt;This is a guide on setting it all up locally, it assumes you have Git, Homebrew, and OS X.&lt;/p&gt;&lt;h5&gt;Install scala 2.8.1&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ brew versions scala
$ cd/usr/local/(or wherever you have homebrew installed)
$ git checkout -b scala281 0e16b9d(make sure the SHA matches versions output)
$ brew install scala
$ git checkout master$git branch -D scala281&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Install sbt 0.7.4 (assumes you have a ~/bin in your $PATH)&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ curl -O http://simple-build-tool.googlecode.com/files/sbt-launch-0.7.4.jar &gt; ~/bin/sbt-launch.jar
$ echo 'java -Xmx1G -jar `dirname $0`/sbt-launch.jar "$@"'&gt; ~/bin/sbt&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Install scala-bootstrapper&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ gem install scala-bootstrapper&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Generate finagle project**&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ mkdir newbird
$ cd newbird
$ scala-bootstrapper newbird
$ sbt update
$ sbt test&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Add a Client class&lt;/h5&gt;&lt;p&gt;create newbird/src/main/scala/com/twitter/newbird/Client.scala with&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="scala"&gt;&lt;span style="color: #cba6f7"&gt;package&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;com.twitter.newbird&lt;/span&gt;

&lt;span style="color: #cba6f7"&gt;import&lt;/span&gt; com&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;twitter&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;finagle&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;builder&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;ClientBuilder
&lt;span style="color: #cba6f7"&gt;import&lt;/span&gt; com&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;twitter&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;finagle&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;thrift&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;ThriftClientFramedCodec
&lt;span style="color: #cba6f7"&gt;import&lt;/span&gt; com&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;twitter&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;newbird&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;thrift&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;_
&lt;span style="color: #cba6f7"&gt;import&lt;/span&gt; org&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;apache&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;thrift&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;protocol&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;TBinaryProtocol

&lt;span style="color: #cba6f7"&gt;import&lt;/span&gt; java&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;net&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;InetSocketAddress

&lt;span style="color: #cba6f7"&gt;class&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; Client&lt;/span&gt;&lt;/em&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt;

  &lt;span style="color: #cba6f7"&gt;val&lt;/span&gt; &lt;span style="color: #fab387"&gt;service&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;=&lt;/span&gt; ClientBuilder&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;hosts&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;Seq&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;newInetSocketAddress&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;localhost&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;span style="color: #fab387"&gt;9999&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;codec&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;ThriftClientFramedCodec&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;hostConnectionLimit&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #fab387"&gt;1&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;build&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
  &lt;span style="color: #cba6f7"&gt;val&lt;/span&gt; &lt;span style="color: #fab387"&gt;client&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;=&lt;/span&gt; &lt;span style="color: #cba6f7"&gt;new&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;NewbirdServiceClientAdapter&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;
    &lt;span style="color: #cba6f7"&gt;new&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;thrift&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;NewbirdService&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;ServiceToClient&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;service&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;newTBinaryProtocol&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;Factory&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;

  &lt;span style="color: #cba6f7"&gt;def&lt;/span&gt; &lt;em&gt;&lt;span style="color: #89b4fa"&gt;get&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;key&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;:&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;String&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;=&lt;/span&gt; client&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;get&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;key&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
  &lt;span style="color: #cba6f7"&gt;def&lt;/span&gt; &lt;em&gt;&lt;span style="color: #89b4fa"&gt;put&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;key&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;:&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;String&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;em&gt;&lt;span style="color: #eba0ac"&gt;value&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;:&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;String&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;=&lt;/span&gt; client&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;put&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;key&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;value&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;

&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Running the server&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ cd newbird
$ sbt&gt; run -f config/development.scala&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Playing with the client&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ cd newbird
$ sbt console
scala&gt; import com.twitter.newbird.Client
scala&gt; val client = new Client()
scala&gt; client.put("foo","bar")
scala&gt; client.get("foo")&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Bonus&lt;/h5&gt;&lt;p&gt;finagle exports a stats url you can curl:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;$ curl http://localhost:9900/stats.txt
counters:  Newbird/connects: 1
Newbird/requests: 4
Newbird/success: 4
gauges:
  Newbird/connections: 0
  Newbird/pending: 0
  jvm_heap_committed: 588251136
  jvm_heap_max: 2146828288
  jvm_heap_used: 64354560
  jvm_nonheap_committed: 83267584
  jvm_nonheap_max: 318767104
  jvm_nonheap_used: 68655360
  jvm_num_cpus: 4
  jvm_start_time: 1327511164928
  jvm_thread_count: 14
  jvm_thread_daemon_count: 9
  jvm_thread_peak_count: 14
  jvm_uptime: 2626505
labels:
  metrics:
    Newbird/connection_duration: (average=2590412, count=1, maximum=2590412, minimum=2590412, p25=2590412, p50=2590412, p75=2590412, p90=2590412, p99=2590412, p999=2590412, p9999=2590412)
    Newbird/connection_received_bytes: (average=192, count=1, maximum=192, minimum=192, p25=192, p50=192, p75=192, p90=192, p99=192, p999=192, p9999=192)
    Newbird/connection_requests: (average=4, count=1, maximum=4, minimum=4, p25=4, p50=4, p75=4, p90=4, p99=4, p999=4, p9999=4)
    Newbird/connection_sent_bytes: (average=120, count=1, maximum=120, minimum=120, p25=120, p50=120, p75=120, p90=120, p99=120, p999=120, p9999=120)
    Newbird/request_latency_ms: (average=14, count=4, maximum=39, minimum=2, p25=2, p50=8, p75=10, p90=39, p99=39, p999=39, p9999=39)&lt;/code&gt;&lt;/pre&gt;</content>
</entry>
<entry>
<title>Render image links directly inside Adium</title>
<link href="https://capotej.com/2011/09/13/render-image-links-directly-inside-adium/"/>
<id>80529c073a6cc5fb854b81638e528624a5cbea5ba6050956bc4d301c0fb1884e</id>
<published>2011-09-13T08:59:00Z</published>
<updated>2025-01-28T01:54:10Z</updated>
<summary type="html">&lt;p&gt;Last night I delightfully discovered that Adium Message Styles are just html, css, and javascript rendered inside a webview.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Render image links directly inside Adium&lt;/h2&gt;&lt;p&gt;Last night I delightfully discovered that Adium Message Styles are just html, css, and javascript rendered inside a webview. The next natural step was to write something in it, so I wrote a Message Style that tries to render any image link directly inline the conversation (campfire style).&lt;/p&gt;&lt;p&gt;The code was written at midnight after a long day, so its not best. Basically, it’s a setInterval that runs every 2.5 seconds that loops through all message elements, appending an img tag to the body of the message if an image link is detected. It also removes the processing class as to not reprocess the same messages.&lt;/p&gt;&lt;p&gt;Installation is simple, just download: (no longer available)&lt;/p&gt;&lt;p&gt;and extract into ~/Library/Adium 2.0/Message Styles (create if necessary). Then choose the TOP Stockholm theme (no idea why there are two entries), and close your chat window. It should be activated next time a chat window opens.&lt;/p&gt;</content>
</entry>
<entry>
<title>MigrationFor: Write migrations right from the command line!</title>
<link href="https://capotej.com/2011/01/03/migrationfor-write-migrations-right-from-the-command-line/"/>
<id>4d8e53b0e75c7a36f2f0a73eee9fa4637d550aa25dc018b1e037bddc0e131d27</id>
<published>2011-01-03T09:48:00Z</published>
<updated>2025-01-28T01:54:38Z</updated>
<summary type="html">&lt;p&gt;As someone who mostly stays in the rails console, I’ve always hated forgetting a field, creating a migration, finding it among your other 500 migration files, then adding the one line you need to add, then running it.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;MigrationFor: Write migrations right from the command line!&lt;/h2&gt;&lt;p&gt;As someone who mostly stays in the rails console, I’ve always hated forgetting a field, creating a migration, finding it among your other 500 migration files, then adding the one line you need to add, then running it. This is probably the most annoying part of the Rails experience. I’ve always wanted to write a better migration generator that could take a list of commands/fields and write the migration for you, since most of the time what you name a migration has all the info it needs (add_index_to_post_id). Thanks to the heavily refactored plugin/generator API in Rails 3, I was able to do just that.&lt;/p&gt;&lt;p&gt;Let’s take a look at how it works:&lt;/p&gt;&lt;p&gt;First, install it (only works for Rails 3)&lt;/p&gt;&lt;pre&gt;&lt;code&gt;rails plugin install git://github.com/capotej/migration_for.git&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, you can create migrations like so:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;rails g migration_for add_index:posts:posts_id&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It would generate db/migrate/20110103182654_add_index_posts_posts_id.rb):&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;&lt;span style="color: #cba6f7"&gt;class&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;AddIndexPostsPostsId&lt;/span&gt;&lt;/em&gt; &lt;span style="color: #9399b2"&gt;&amp;lt;&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;ActiveRecord&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;::&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;Migration&lt;/span&gt;&lt;/em&gt;

  &lt;span style="color: #cba6f7"&gt;def&lt;/span&gt; self&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;up&lt;/span&gt;&lt;/em&gt;
    add_index &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;posts&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;posts_id&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;
  &lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;

  &lt;span style="color: #cba6f7"&gt;def&lt;/span&gt; self&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;down&lt;/span&gt;&lt;/em&gt;
    &lt;em&gt;&lt;span style="color: #9399b2"&gt;#&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #9399b2"&gt;waiting for reversible migrations in rails 3.1!&lt;/span&gt;&lt;/em&gt;
  &lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;

&lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which you can then run normally with &lt;code&gt;rake db:migrate&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Let’s look at a more complex example:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ rails g migration_for create_table:posts add_column:posts:title:string add_column:posts:user_id:integer&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Would generate:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;&lt;span style="color: #cba6f7"&gt;class&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;CreateTablePostsaddColumnPostsTitleStringaddColumnPostsUserIdIntegeraddIndexPostsUserId&lt;/span&gt;&lt;/em&gt; &lt;span style="color: #9399b2"&gt;&amp;lt;&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;ActiveRecord&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;::&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;Migration&lt;/span&gt;&lt;/em&gt;

  &lt;span style="color: #cba6f7"&gt;def&lt;/span&gt; self&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;up&lt;/span&gt;&lt;/em&gt;
    create_table &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;posts&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;
    add_column &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;posts&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;title&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;string&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;
    add_column &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;posts&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;user_id&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;integer&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;
    add_index &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;posts&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;user_id&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;
  &lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;

  &lt;span style="color: #cba6f7"&gt;def&lt;/span&gt; self&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;down&lt;/span&gt;&lt;/em&gt;
    &lt;em&gt;&lt;span style="color: #9399b2"&gt;#&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #9399b2"&gt;waiting for reversible migrations in rails 3.1!&lt;/span&gt;&lt;/em&gt;
  &lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;

&lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It uses a lookup table with all the &lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/Migration.html"&gt;activerecord transformations&lt;/a&gt; and will only insert an expression into a migration if the method name is valid and it has the right number of arguments, so botched commands wont mess up the migration. Hope you enjoy it as much as I have!&lt;/p&gt;&lt;p&gt;Source available here: &lt;a href="https://github.com/capotej/migration_for"&gt;https://github.com/capotej/migration_for&lt;/a&gt;&lt;/p&gt;</content>
</entry>
<entry>
<title>What I released in 2010</title>
<link href="https://capotej.com/2010/12/31/what-i-released-in-2010/"/>
<id>1f4730515c37c0d35d091cce9809065b467ca5b6bd07aba8621d8a074d7034e4</id>
<published>2010-12-31T13:29:00Z</published>
<updated>2025-01-26T13:48:33Z</updated>
<summary type="html">&lt;p&gt;Here’s a recap of what I’ve worked on and released in 2010: ###&lt;a href="https://github.com/capotej/youtube_fraiche"&gt;Youtube Fraiche&lt;/a&gt; I couldn’t find a youtube downloader that worked on github,&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;What I released in 2010&lt;/h2&gt;&lt;p&gt;Here’s a recap of what I’ve worked on and released in 2010:&lt;/p&gt;&lt;h5&gt;&lt;a href="https://github.com/capotej/youtube_fraiche"&gt;Youtube Fraiche&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;I couldn’t find a youtube downloader that worked on github, so I wrote my own one evening&lt;/p&gt;&lt;h5&gt;&lt;a href="https://github.com/capotej/uploadd"&gt;Uploadd&lt;/a&gt; and &lt;a href="https://github.com/capotej/paperclip_uploadd"&gt;paperclip_uploadd&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;I wanted to upload and store images off-site (using paperclip/rails) on an server which has cheaper bandwidth rates than S3. Using rainbows, this tiny rack script has handled over a 1.5 million uploads at a peak of 10-15 uploads/sec. Also, it’s been running for about 6 months now without a single crash. Thank you Eric Wong! There is also a plugin for the popular paperclip gem to use uploadd as a storage backend transparently.&lt;/p&gt;&lt;h5&gt;&lt;a href="https://github.com/capotej/mrskinner/blob/master/mrskinner.js"&gt;mrskinner&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Tiny javascript for making the site gutters clickable based on a fixed width layout&lt;/p&gt;&lt;h5&gt;&lt;a href="https://github.com/capotej/existential"&gt;existential&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Completely inspired by Nick Kallen’s &lt;a href="http://pivotallabs.com/users/nick/blog/articles/272-access-control-permissions-in-rails"&gt;post&lt;/a&gt; on authorization, I wanted to extract that pattern into a rails plugin that I could use for all my projects. I use devise/existential for all my projects now.&lt;/p&gt;&lt;h5&gt;&lt;a href="https://github.com/capotej/has_opengraph"&gt;has_opengraph&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Easy way to participate in opengraph and draw facebook like buttons. Just annotate your models with meta data, and draw it in your view easily.&lt;/p&gt;&lt;h5&gt;&lt;a href="https://github.com/capotej/chewbacca"&gt;chewbacca&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;I kinda feel bad that I took a cool name for such a lame script. Anyway it’s a set of rake tasks that provide a hair of abstraction above scp. Useful when you have a set of files locally that map to a different set of files remotely.&lt;/p&gt;&lt;p&gt;I already have tons of stuff in the works for 2011!&lt;/p&gt;</content>
</entry>
<entry>
<title>Using Rack applications inside GWT Hosted mode</title>
<link href="https://capotej.com/2009/07/19/using-rack-applications-inside-gwt-hosted-mode/"/>
<id>13bbe761bda5ff5697e73299e26632079d81f00b74877c2fcfe27e4e995da556</id>
<published>2009-07-19T18:51:00Z</published>
<updated>2025-01-26T13:48:33Z</updated>
<summary type="html">&lt;p&gt;This guide will show you how you can use JRuby to run any Rack application inside Google Web Toolkit’s (GWT) hosted mode server so your&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Using Rack applications inside GWT Hosted mode&lt;/h2&gt;&lt;p&gt;This guide will show you how you can use JRuby to run any Rack application inside Google Web Toolkit’s (GWT) hosted mode server so your interface and your backend are of the Same Origin.&lt;/p&gt;&lt;p&gt;BackgroundGWT has two ways of interacting with a server: GWT Remote Procedure Call (RPC) and plain HTTP (XHR). GWT-RPC is a high level library designed for interacting with server-side Java code. GWT-RPC implements the GWT Remote Service interface allowing you to call those methods from the user interface. Essentially, GWT handles the dirty work for you. However, it only works on Java backends that can implement that interface. Since most of my backends are Sinatra/Rack applications, I’ll be using the plain HTTP library.&lt;/p&gt;&lt;p&gt;The problemDue to the restriction of the &lt;a href="http://en.wikipedia.org/wiki/Same_origin_policy"&gt;Same Origin&lt;/a&gt; policy, the interface served out of GWT’s development, or Hosted Mode server can only make requests back to itself. If you were using real servlets or GWT’s RemoteService this wouldn’t be an issue; but since Rack applications listen on their own port, you cannot make requests from GWT to our application without resorting to something like JSONP or server-side proxying. This leaves you having to compile our interface to HTML/JS/CSS, which is lengthy process, and serve it from the origin of the Rack application to see our changes.&lt;/p&gt;&lt;p&gt;The solutionSince I wanted to develop using GWT’s development environment with a Rack backend, I devised a way to use jruby-rack to load arbitrary Rack applications alongside our interface.&lt;/p&gt;&lt;p&gt;First let’s setup our environment:&lt;/p&gt;&lt;h5&gt;Download and unpack the latest GWT for your platform (mine’s being linux)&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;wget http://google-web-toolkit.googlecode.com/files/gwt-linux-1.7.0.tar.bz2
tar -xvjpf gwt-linux-1.7.0.tar.bz2cd gwt-linux-1.7.0&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Download the latest jruby-complete.jar&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;wget http://repository.codehaus.org/org/jruby/jruby-complete/1.3.1/jruby-complete-1.3.1.jar
mv jruby-complete-1.3.1.jar jruby-complete.jar&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Download the latest jruby-rack.jar&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;wget http://repository.codehaus.org/org/jruby/rack/jruby-rack/0.9.4/jruby-rack-0.9.4.jar
mv jruby-rack-0.9.4.jar jruby-rack.jar&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Create an app with webAppCreator&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;./webAppCreator -out MySinatra com.example.MySinatra
cd MySinatra&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Package gem dependencies&lt;/h5&gt;&lt;p&gt;In order for this to work you have to package any gem dependencies your backend needs (sinatra, in our case) as jars within your application. For Sinatra it looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="shell"&gt;java -jar jruby-complete.jar -S gem install -i ./sinatra sinatra --no-rdoc --no-ri
jar cf sinatra.jar -C sinatra .&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Add jruby-complete.jar, jruby-rack.jar, sinatra.jar (and any other jars you’ve created) to the libs target of your build.xml&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="xml"&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;target&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;name&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;libs&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;description&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;Copy libs to WEB-INF/lib&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;mkdir&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;dir&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;war/WEB-INF/lib&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;/&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;copy&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;todir&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;war/WEB-INF/lib&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;file&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;${gwt.sdk}/gwt-servlet.jar&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;/&gt;&lt;/span&gt;
  &lt;em&gt;&lt;span style="color: #9399b2"&gt;&amp;lt;!--&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #9399b2"&gt; Add any additional server libs that need to be copied &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #9399b2"&gt;--&gt;&lt;/span&gt;&lt;/em&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;copy&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;todir&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;war/WEB-INF/lib&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;file&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;${gwt.sdk}/jruby-complete.jar&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;/&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;copy&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;todir&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;war/WEB-INF/lib&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;file&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;${gwt.sdk}/jruby-rack.jar&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;/&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;copy&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;todir&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;war/WEB-INF/lib&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;file&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;${gwt.sdk}/sinatra.jar&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;/&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;target&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Add these lines right after in war/WEB-INF/web.xml&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="xml"&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;context-param&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;param-name&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;rackup&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;param-name&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;param-value&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    require 'rubygems'
    require './lib/sinatra_app'
    map '/api' do
      run MyApp
    end
  &lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;param-value&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;context-param&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter-name&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;RackFilter&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter-name&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter-class&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;org.jruby.rack.RackFilter&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter-class&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter-mapping&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter-name&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;RackFilter&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter-name&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;url-pattern&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;/api/*&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;url-pattern&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;filter-mapping&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;listener&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
  &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;listener-class&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;org.jruby.rack.RackServletContextListener&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;listener-class&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;listener&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note: All you’re doing here is passing the contents of a config.ru file into the &lt;code&gt;&amp;lt;param-value&gt;&lt;/code&gt; element for the &lt;code&gt;&amp;lt;context-param&gt;&lt;/code&gt; (make sure this is HTML encoded!). This states that any request to /api is to be handled by your Sinatra application and not GWT’s Hosted mode servlet.&lt;/p&gt;&lt;h5&gt;Create your Sinatra backend and place it in war/WEB-INF/lib/sinatra_app.rb&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;&lt;span style="color: #cba6f7"&gt;require&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;sinatra&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;
&lt;span style="color: #cba6f7"&gt;require&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;open-uri&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;
&lt;span style="color: #cba6f7"&gt;class&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;MyApp&lt;/span&gt;&lt;/em&gt; &lt;span style="color: #9399b2"&gt;&amp;lt;&lt;/span&gt; &lt;em&gt;&lt;span style="color: #f9e2af"&gt;Sinatra&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;::&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;Base&lt;/span&gt;&lt;/em&gt;
  get &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;/showpage&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt; &lt;span style="color: #cba6f7"&gt;do&lt;/span&gt;
    &lt;em&gt;&lt;span style="color: #f38ba8"&gt;open&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;http://www.yahoo.com&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;read
  &lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;

  get &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;/helloworld&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt; &lt;span style="color: #cba6f7"&gt;do&lt;/span&gt;
    &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;hello world&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;
  &lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;
&lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Run your new awesome setup&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;ant hosted&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now when navigate to &lt;a href="http://localhost:8888/api/helloworld"&gt;http://localhost:8888/api/helloworld&lt;/a&gt; or &lt;a href="http://localhost:8888/api/showpage"&gt;http://localhost:8888/api/showpage&lt;/a&gt; you should see the Sinatra application being served via GWT.&lt;/p&gt;</content>
</entry>
<entry>
<title>Useful Rails Routing tips</title>
<link href="https://capotej.com/2009/01/02/useful-rails-routing-tips/"/>
<id>934d607ae810cbd4a73e4c4b653110f1d0ec5316a2540e11734666ebc4bd2cc1</id>
<published>2009-01-02T15:50:00Z</published>
<updated>2025-01-26T13:48:33Z</updated>
<summary type="html">&lt;p&gt;Even though I have been using Rails for fun and profit for about 2 years now, I felt I never really used it’s routing engine&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Useful Rails Routing tips&lt;/h2&gt;&lt;p&gt;Even though I have been using Rails for fun and profit for about 2 years now, I felt I never really used it’s routing engine to its full potential. So I checked out new &lt;a href="http://guides.rubyonrails.org/routing_outside_in.html"&gt;Rails Routing from the outside in&lt;/a&gt; guide and discovered bunch of useful tricks that I (and maybe you) had no idea you could do. Here they are:&lt;/p&gt;&lt;h5&gt;Multiple resource definitions on a single line&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;map&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;resources &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;photos&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;books&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;videos&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Impose a certain format for resource identifiers&lt;/h5&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;map&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;resources &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;photos&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;requirements &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;id &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt; &lt;/span&gt;&lt;span style="color: #f5c2e7"&gt;/&lt;/span&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;A-Z&lt;/span&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;A-Z&lt;/span&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;0-9&lt;/span&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;+&lt;/span&gt;&lt;span style="color: #f5c2e7"&gt;/&lt;/span&gt; &lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This way, &lt;code&gt;/photos/3&lt;/code&gt; would not work, but &lt;code&gt;/photos/DA321&lt;/code&gt; would.&lt;/p&gt;&lt;h5&gt;Friendlier action names&lt;/h5&gt;&lt;p&gt;Say for your application ‘create’ and ‘change’ make more sense than the default ‘new’ and ‘edit’ you can do&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;map&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;resources &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;photos&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;path_names &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;new &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;make&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;edit &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;change&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt; &lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also do this site-wide also, in your environment.rb&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;config&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;action_controller&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;resources_path_names &lt;span style="color: #94e2d5"&gt;=&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;new &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;make&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;edit &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;change&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt; &lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Trim the fat off resources with :only and :except&lt;/h5&gt;&lt;p&gt;When you use map.resources, rails generates 7 restful routes for that resource; But what if that resource only needed to be seen and listed, never edited or created?&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;map&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;resources &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;photos&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;only &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;span style="color: #9399b2"&gt;:&lt;/span&gt;index&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;show&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If your application uses a lot of &lt;code&gt;map.resources&lt;/code&gt; calls but not necessarily all its generated routes, you can save memory this way.&lt;/p&gt;&lt;h5&gt;Adding extra routes to your resources&lt;/h5&gt;&lt;p&gt;Instead of fighting the &lt;code&gt;map.resources&lt;/code&gt; generator by placing a horror like this atop your routes.rb&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;map&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;connect &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;/photos/:id/preview&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;controller &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;photos&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;action &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;preview&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt; &lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can do this to your already mapped resource&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;map&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;resources &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;photos&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;member &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;preview &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;get &lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will map all GET’s to &lt;code&gt;/photos/3&lt;/code&gt; to the preview action of your photos controller&lt;/p&gt;&lt;p&gt;This can also be used in collections instead of singular members, just change &lt;code&gt;:member&lt;/code&gt; to &lt;code&gt;:collection&lt;/code&gt;&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;map&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;resources &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;photos&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;collection &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #9399b2"&gt;{&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;search &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;get &lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will give you &lt;code&gt;/photos/search&lt;/code&gt; and hit the search action within the photos controller&lt;/p&gt;</content>
</entry>
<entry>
<title>So you want to click that button?</title>
<link href="https://capotej.com/2008/10/28/so-you-want-to-click-that-button/"/>
<id>5ff6eb0600af686c5dde7eea557d4c15a18cdfde203b4c5b36b7384f438b4735</id>
<published>2008-10-28T21:05:00Z</published>
<updated>2025-01-26T13:48:33Z</updated>
<summary type="html">&lt;p&gt;I stumbled upon&lt;a href="http://clickthatbutton.com"&gt;http://clickthatbutton.com&lt;/a&gt; during my routine lurking of&lt;a href="http://news.ycombinator.com"&gt;hacker news&lt;/a&gt; . After being amused for about 10 seconds,  I decided to take it to the next&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;So you want to click that button?&lt;/h2&gt;&lt;p&gt;I stumbled upon&lt;a href="http://clickthatbutton.com"&gt;http://clickthatbutton.com&lt;/a&gt; during my routine lurking of&lt;a href="http://news.ycombinator.com"&gt;hacker news&lt;/a&gt; . After being amused for about 10 seconds, I decided to take it to the next level; I wanted to click on it really, really fast. After going through a few solutions (simple js while loop in firebug, then curl/wget) and failing, the idea of using selenium popped into my head. So I went off to their&lt;a href="http://selenium-ide.openqa.org/download.jsp"&gt;site&lt;/a&gt; and installed the extension. I figured a simple recording of the mouse event, then wrapping it around a loop in selenium would do the trick, but I quickly found that selenium doesn’t support loops. Not to be stopped, I searched google and ended up with&lt;a href="http://51elliot.blogspot.com/2008/02/selenium-ide-goto.html"&gt;this&lt;/a&gt; . After installing the plugin for selenium (a plugin for a plugin!?) and restarting firefox, I tried it again and to my surprise it worked! The click counter was going up steadily on its own (18k clicks and counting). Here is my selenium test case for those of you following along:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="html"&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;?&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;xml&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;version&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;1.0&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;encoding&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;UTF-8&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;?&gt;&lt;/span&gt;
&lt;span style="color: #cba6f7"&gt;&amp;lt;!&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;DOCTYPE&lt;/span&gt; &lt;span style="color: #f38ba8"&gt;html&lt;/span&gt; &lt;span style="color: #cba6f7"&gt;PUBLIC&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;-//W3C//DTD XHTML 1.0 Strict//EN&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;html&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;xmlns&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;http://www.w3.org/1999/xhtml&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;xml:lang&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;en&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;lang&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;en&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;head&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;profile&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;http://selenium-ide.openqa.org/profiles/test-case&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;meta&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;http-equiv&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;Content-Type&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;content&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;text/html; charset=UTF-8&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;/&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;link&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;rel&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;selenium.base&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;href&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;http://clickthatbutton.com/&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;/&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;title&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;haha&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;title&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;head&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;body&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;table&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;cellpadding&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;1&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;cellspacing&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;1&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;border&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;1&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;thead&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;rowspan&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;1&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #f9e2af"&gt;colspan&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;3&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;haha&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;thead&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tbody&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;open&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;/&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;store&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;x&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;1&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;while&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;storedVars['x'] == storedVars['x']&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;click&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;submit&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;endWhile&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
    &lt;span style="color: #94e2d5"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;td&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tr&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;

&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;tbody&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;table&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;body&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;
&lt;span style="color: #94e2d5"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #89b4fa"&gt;html&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just paste that into a file, open it with selenium ide, hit play and you should be good to go.&lt;/p&gt;</content>
</entry>
<entry>
<title>Arrow key navigation for text fields</title>
<link href="https://capotej.com/2008/10/12/arrow-key-navigation-for-text-fields/"/>
<id>dbd8065d387dc819f219fb3f004cc534c74507bcb8e6e3aa1291abcabca5bb45</id>
<published>2008-10-12T17:41:00Z</published>
<updated>2025-01-28T01:54:57Z</updated>
<summary type="html">&lt;p&gt;Here is a class for enabling the use of arrow keys to navigate through a grid of input fields: (using mootools)&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Arrow key navigation for text fields&lt;/h2&gt;&lt;p&gt;Here is a class for enabling the use of arrow keys to navigate through a grid of input fields: (using mootools)&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="javascript"&gt;&lt;span style="color: #cba6f7"&gt;var&lt;/span&gt; FocusMover &lt;span style="color: #94e2d5"&gt;=&lt;/span&gt; &lt;span style="color: #cba6f7"&gt;new&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;Class&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;

&lt;em&gt;&lt;span style="color: #f9e2af"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;initialize&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;:&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;sel&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;col_num&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;

&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;sel&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;sel&lt;/em&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;col_num&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;col_num&lt;/em&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;inputs&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;$$&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;sel&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;current_focus&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #fab387"&gt;0&lt;/span&gt;&lt;/em&gt;

&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;var&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;self&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;

&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;inputs&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;each&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;item&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;index&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;item&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;addEvent&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;em&gt;&lt;span style="color: #a6e3a1"&gt;keydown&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;key&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;$try&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                                        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;self&lt;/em&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;em&gt;key&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;key&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                                &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                        &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;item&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;addEvent&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;em&gt;&lt;span style="color: #a6e3a1"&gt;focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;e&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;self&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;refresh&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;e&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                        &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;

&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;item&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;set&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;em&gt;&lt;span style="color: #a6e3a1"&gt;myid&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;index&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;

&lt;em&gt;&lt;span style="color: #89b4fa"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;inputs&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;em&gt;&lt;span style="color: #fab387"&gt;0&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;

&lt;em&gt;&lt;span style="color: #89b4fa"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;


&lt;em&gt;&lt;span style="color: #f9e2af"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;refresh&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;:&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;e&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;current_focus&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;e&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;target&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;get&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;em&gt;&lt;span style="color: #a6e3a1"&gt;myid&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;

&lt;em&gt;&lt;span style="color: #f9e2af"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;down&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;:&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;i&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;parseInt&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;current_focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;+&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;parseInt&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;col_num&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;inputs&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;em&gt;i&lt;/em&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;

&lt;em&gt;&lt;span style="color: #f9e2af"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;up&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;:&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;i&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;parseInt&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;current_focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;-&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;parseInt&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;col_num&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;inputs&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;em&gt;i&lt;/em&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;

&lt;em&gt;&lt;span style="color: #f9e2af"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;left&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;:&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;i&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;parseInt&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;current_focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;-&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #fab387"&gt;1&lt;/span&gt;&lt;/em&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;inputs&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;em&gt;i&lt;/em&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;

&lt;em&gt;&lt;span style="color: #f9e2af"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;right&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;:&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;i&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;parseInt&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;current_focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;+&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #fab387"&gt;1&lt;/span&gt;&lt;/em&gt;
&lt;em&gt;&lt;span style="color: #f9e2af"&gt;                &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f38ba8"&gt;this&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;inputs&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;em&gt;i&lt;/em&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;focus&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;

&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, the constructor takes two arguments: a selector (which should return a list of all your input fields), and the number of input field columns. So for a 4x2 table, you would set it up like this:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="javascript"&gt;&lt;span style="color: #cba6f7"&gt;var&lt;/span&gt; FM &lt;span style="color: #94e2d5"&gt;=&lt;/span&gt; &lt;span style="color: #cba6f7"&gt;new&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;FocusMover&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;em&gt;&lt;span style="color: #a6e3a1"&gt;#mytable input&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #fab387"&gt;4&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
</entry>
<entry>
<title>Tabbing through fields vertically</title>
<link href="https://capotej.com/2008/10/11/tabbing-through-fields-vertically/"/>
<id>5c00addafdfb4ec674260369d01b8b8bb8bf96af5e1ccbdd1243c6b3f49b92b2</id>
<published>2008-10-11T01:54:00Z</published>
<updated>2025-01-28T01:55:19Z</updated>
<summary type="html">&lt;p&gt;Sometimes it’s useful to switch the browser’s default tabbing behavior (left to right) to the opposite (top to bottom) when your input fields are in a grid layout instead the of the usual single column layout. Having to do this manually is a real pain, especially for large grids; So here is a solution in javascript, using mootools:&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Tabbing through fields vertically&lt;/h2&gt;&lt;p&gt;Sometimes it’s useful to switch the browser’s default tabbing behavior (left to right) to the opposite (top to bottom) when your input fields are in a grid layout instead the of the usual single column layout. Having to do this manually is a real pain, especially for large grids; So here is a solution in javascript, using mootools:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="javascript"&gt;&lt;em&gt;&lt;span style="color: #f9e2af"&gt;window&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;addEvent&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;em&gt;&lt;span style="color: #a6e3a1"&gt;domready&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;    &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;var&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;trs&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;$$&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;em&gt;&lt;span style="color: #a6e3a1"&gt;#mytable tr&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;    &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #cba6f7"&gt;var&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;accum&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #fab387"&gt;0&lt;/span&gt;&lt;/em&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;    &lt;/span&gt;&lt;/em&gt;&lt;em&gt;trs&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;each&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;tr&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;trindex&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;accum&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;trindex&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;+&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #fab387"&gt;1&lt;/span&gt;&lt;/em&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;em&gt;tr&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;getChildren&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;each&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #cba6f7"&gt;function&lt;/span&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;td&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;tdindex&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;{&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;            &lt;/span&gt;&lt;/em&gt;&lt;em&gt;td&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt;getChildren&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;em&gt;&lt;span style="color: #a6e3a1"&gt;input&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;em&gt;&lt;span style="color: #fab387"&gt;0&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;]&lt;/span&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;tabIndex&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;accum&lt;/em&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;            &lt;/span&gt;&lt;/em&gt;&lt;em&gt;accum&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;=&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;accum&lt;/em&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;+&lt;/span&gt;&lt;em&gt;&lt;span style="color: #89b4fa"&gt; &lt;/span&gt;&lt;/em&gt;&lt;em&gt;trs&lt;/em&gt;&lt;span style="color: #94e2d5"&gt;.&lt;/span&gt;&lt;em&gt;&lt;span style="color: #94e2d5"&gt;length&lt;/span&gt;&lt;/em&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;        &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;em&gt;&lt;span style="color: #89b4fa"&gt;    &lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
&lt;span style="color: #9399b2"&gt;}&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
</entry>
<entry>
<title>Why MooTools (or Why not JQuery)</title>
<link href="https://capotej.com/2008/09/30/why-mootools-or-why-not-jquery/"/>
<id>75774022e0d69a453ccf656b34e479674bfa0b70cf9ec4748da21ddeaa6ffbcb</id>
<published>2008-09-30T10:26:00Z</published>
<updated>2025-01-28T01:56:23Z</updated>
<summary type="html">&lt;p&gt;I’ve been toying around with MooTools a bit lately, and I’ve found the experience quite enjoyable and refreshing.&lt;/p&gt;</summary>
<content type="html">&lt;h2&gt;Why MooTools (or Why not JQuery)&lt;/h2&gt;&lt;p&gt;I’ve been toying around with MooTools a bit lately, and I’ve found the experience quite enjoyable and refreshing. Naturally, I &lt;a href="http://twitter.com/capotej/statuses/939831956"&gt;twittered&lt;/a&gt; about it and went along my merry way. Moments later (and much to my surprise), I had a direct message from John Resig himself asking “Why, what’s wrong with jQuery?”. I was pretty taken aback that he would take time from his surely busy day to message a total stranger in an effort to improve his project or at least gain an insight in the everyday life of a js developer (it’s not like DHH would personally message people that are dumping rails to use merb). I figured he deserved a straight, honest answer; One that at least would be longer than &lt;a href="http://twitter.com/capotej/statuses/940082809"&gt;140 characters&lt;/a&gt; (even though I managed to use every single one). So it begs the question, Why MooTools?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Class support. JQuery’s SQL-like syntax is fine for quick and dirty javascripting, but eventually you’ll want real classes to structure your UI logic.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;It smells, feels and tastes like regular javascript. JQuery doesn’t even look like javascript, which isn’t necessarily a bad thing, since that’s kind of their goal. MooTools however, feels like just an extension of the language&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Faster. &lt;a href="http://mootools.net/slickspeed/"&gt;‘Nuff Said&lt;/a&gt; EDIT: This was pointed out to be false; It is only faster in certain cases (such as mine, WebKit nightly on OS X).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Robert Penner’s easing equations baked right in.This could just be me, but I find the animations that mootools creates are alot smoother than JQuery’s (especially the easing).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Creating new DOM elements is a snap.Need to create a dom element? &lt;code&gt;var el = new Element(‘a’, { ‘href’: ‘juliocapote.com’});&lt;/code&gt; Done.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Modular. I like that I can just build and pull down a moo.js that only contains the functionality I need.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Better Documented.Or at least, its faster to find what you need.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Easier to hack on and extend. While I haven’t personally delved into the internals of either system, the consensus seems to be that jquery is an unintelligible mess when it comes to modifying how it works.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Prototype Approach (versus a namespaced approach) This is really just matter of preference; MooTools achieves it’s magic by just extending the prototypes of common objects (Array, String, etc); While this is obstrusive, it makes for shorter, more natural code. JQuery does its thing via a main object (which you can name, hence the namespace), that you wrap around whatever you want to make magical; This is unobtrusive, but you pay for that by having to wrap anything you want to use (which ends up being everything). It basically boils down to arr.each(fn) vs $.each(arr, fn)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;It’s not a revolution. It feels as if JQuery is trying to take on the world (it seems like it too, since its now included with visual studio and the nokia sdk). However, I’m not; I’m just trying to write some javascript here.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It’s not like I’m never going to use JQuery again; It simply isn’t my default js framework any longer.&lt;/p&gt;</content>
</entry>
<entry>
<title>Highlight link based on current page in rails</title>
<link href="https://capotej.com/2008/09/27/highlight-link-based-on-current-page-in-rails/"/>
<id>e5f0a92757b49b6f4fd9d72b40d575557b56faf2d322ac8b4b0630bc6d65c882</id>
<published>2008-09-27T00:00:00Z</published>
<updated>2008-09-27T00:00:00Z</updated>
<summary type="html"></summary>
<content type="html">&lt;h2&gt;Highlight link based on current page in rails&lt;/h2&gt;&lt;p&gt;This is common pattern in website navigation, where it highlights the link (usually by setting &lt;code&gt;class=”active”&lt;/code&gt;) that took you to the current page while you are on that page.&lt;/p&gt;&lt;p&gt;First, define a helper:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;&lt;span style="color: #cba6f7"&gt;def&lt;/span&gt; &lt;em&gt;&lt;span style="color: #89b4fa"&gt;is_active?&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;em&gt;&lt;span style="color: #eba0ac"&gt;page_name&lt;/span&gt;&lt;/em&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
  &lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;active&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt; &lt;span style="color: #cba6f7"&gt;if&lt;/span&gt; params&lt;span style="color: #9399b2"&gt;[&lt;/span&gt;&lt;span style="color: #9399b2"&gt;:&lt;/span&gt;action&lt;span style="color: #9399b2"&gt;]&lt;/span&gt; &lt;span style="color: #94e2d5"&gt;==&lt;/span&gt; page_name
&lt;span style="color: #cba6f7"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then call it in your link_to’s in your layout as such:&lt;/p&gt;&lt;pre&gt;&lt;code data-lang="ruby"&gt;link_to &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;Home&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;/&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;class &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; is_active?&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;index&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
link_to &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;About&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;/about&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;class &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; is_active?&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;about&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;
link_to &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;contact&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;/contact&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;'&lt;/span&gt;&lt;span style="color: #9399b2"&gt;,&lt;/span&gt; &lt;span style="color: #9399b2"&gt;:&lt;/span&gt;class &lt;span style="color: #94e2d5"&gt;=&gt;&lt;/span&gt; is_active?&lt;span style="color: #9399b2"&gt;(&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;contact&lt;/span&gt;&lt;span style="color: #a6e3a1"&gt;"&lt;/span&gt;&lt;span style="color: #9399b2"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This effect is achieved due to how link_to handles being passed &lt;code&gt;nil&lt;/code&gt; for its &lt;code&gt;:class&lt;/code&gt;, so when `is_active?&lt;code&gt;returns&lt;/code&gt;nil&lt;code&gt;(because its not the current page),&lt;/code&gt;link_to&lt;code&gt;outputs nothing as its class (not&lt;/code&gt;class=””` as you might expect).&lt;/p&gt;</content>
</entry>

</feed>
