<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/styles.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>RyanParsleyDotCom</title><description>A software developer that is curious about quality.</description><link>https://ryanparsley.com</link><item><title>Hello Cerebro</title><link>https://ryanparsley.com/blog/2026/2026-05-25-hello-cerebro</link><guid isPermaLink="true">https://ryanparsley.com/blog/2026/2026-05-25-hello-cerebro</guid><description>Introducing a tool to reduce cognitive load of when context switching between many side projects. For when minimalism isn&apos;t your style.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I want to introduce a tool that I&amp;#39;ve been working on for a few weeks.
&lt;a href=&quot;https://codeberg.org/RyanParsley/cerebro&quot;&gt;Cerebro&lt;/a&gt; collects metadata about your
projects (git history, file system info, TODO comments) and writes markdown in a
designated folder. It&amp;#39;s not a tool &lt;em&gt;for&lt;/em&gt; agentic workflows. It does relieve some
of the cognitive inflammation that results from increased use of LLMs. It&amp;#39;s not
&lt;em&gt;for&lt;/em&gt; LLMs, it&amp;#39;s for people that may or may not be using LLMs. Cerebro is a tool
to reduce cognitive load of &amp;quot;too many&amp;quot; side projects. For when minimalism isn&amp;#39;t
your style.&lt;/p&gt;
&lt;h2&gt;My workbench has always been messy&lt;/h2&gt;
&lt;p&gt;When I write code in my &amp;quot;freetime&amp;quot;, I don&amp;#39;t have deadlines or stakeholders. I
more or less give myself permission to follow my muse. The result tends to leave
me with little piles of half baked code here and there. This is not a problem in
and of itself. The point of this activity isn&amp;#39;t shipping code, it&amp;#39;s about
learning and tinkering. Sometimes, I would like to take inventory of my little
experiments and get a little more &lt;a href=&quot;https://medium.com/@bre/the-cult-of-done-manifesto-724ca1c2ff13&quot;&gt;cult of
done&lt;/a&gt; about it.
It does feel good to ship things.&lt;/p&gt;
&lt;h2&gt;The funny thing about LLMs&lt;/h2&gt;
&lt;p&gt;I don&amp;#39;t know if LLMs make me &lt;a href=&quot;https://www.theregister.com/software/2025/07/11/ai-coding-tools-make-developers-slower-study-finds/1143832&quot;&gt;more
productive&lt;/a&gt;,
but I do find myself tinkering a lot more lately. Much of my tinkering involves
LLMs in one fashion or another. More than anything, I&amp;#39;m feeling a burden on my
attention because of that. While I am using LLMs, I&amp;#39;m not looking to just vibe
out slop. Many of my more recent projects have centered around learning how to
use LLMs to make high quality code. That means, I remain more at an
architectural meta level and am a bit removed from the implementation details.
The more casual relationship with this code means it&amp;#39;s harder to connect with
the architecture.&lt;/p&gt;
&lt;p&gt;Mario Zechner, creator of the Pi coding agent, referred to agents as &amp;quot;merchants
of complexity&amp;quot;. I feel that in my bones. Now before you go and suggest some sort
of detox or intervention... the problem isn&amp;#39;t the agents. Not really. And it
isn&amp;#39;t my complete lack of discipline (is the thing I keep telling myself). The
problem is that I can&amp;#39;t hold all that context in &lt;em&gt;my&lt;/em&gt; head. So, seeing I had a
problem of too many side projects I did the only &lt;a href=&quot;https://xkcd.com/927/&quot;&gt;reasonable
thing&lt;/a&gt; for an engineer, I started one more project.&lt;/p&gt;
&lt;h2&gt;Enter cerebro&lt;/h2&gt;
&lt;p&gt;Cerebro is my attempt to reduce the re-entry tax. It watches my development
activity and builds a dashboard of sorts. The &lt;code&gt;cerebro build&lt;/code&gt; command scrapes my
git history, AI session databases, and code for TODOs. It writes markdown files
to a designate location that I refer to as cortex. Cortex is just a folder of
markdown and all the data in it is otherwise accessible... but the ergonomics of
this dashboard are making a real difference.&lt;/p&gt;
&lt;h2&gt;The experiment continues&lt;/h2&gt;
&lt;p&gt;At this point, it&amp;#39;s been a little over a month and it seems like there&amp;#39;s
something of value building in cerebro. I&amp;#39;m toying around with a naive MCP
server to expose cortex to harnesses. I don&amp;#39;t know if I&amp;#39;ll keep it around, but
when I ask an agent about one of my projects, it reads the same context I&amp;#39;d read
myself and that seems nice for grounding and sharing between projects. I also
have a skill that is aware of cortex and can make use of the cli that it seems
just as good. Time will tell if the MCP is worth maintaining. For now, I have it
and it plays nicely with opencode.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m not using Cerebro to write more code faster. I&amp;#39;m using it to connect with
what I already built. The pile gets maintained, not expanded. It&amp;#39;s an effort to
reduce cognitive load when the need for context switching seems to be growing
at an increasing rate. It&amp;#39;s a tool for humans that happens to play nice with
LLMs.&lt;/p&gt;
</content:encoded></item><item><title>Hello Faircamp</title><link>https://ryanparsley.com/blog/2026/2026-02-28-hello-faircamp</link><guid isPermaLink="true">https://ryanparsley.com/blog/2026/2026-02-28-hello-faircamp</guid><description>I want to share the story of wiring up a selfhosted music portfolio using Faircamp.</description><pubDate>Sun, 01 Mar 2026 04:53:00 GMT</pubDate><content:encoded>&lt;p&gt;This is the story of self-hosting a &lt;a href=&quot;https://music.ryanparsley.com/&quot;&gt;music portfolio&lt;/a&gt; in my homelab using &lt;a href=&quot;https://simonrepp.com/faircamp/&quot;&gt;Faircamp&lt;/a&gt;. I doubt you are looking to set up exactly this project with exactly this tech stack, but I suspect sharing my journey will be useful in one way or another. All in all, the process is straightforward. Perhaps, seeing that will encourage you to try for yourself.&lt;/p&gt;
&lt;h2&gt;What is Faircamp&lt;/h2&gt;
&lt;p&gt;I can&amp;#39;t state it any better than the official documentation does:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Point Faircamp to a folder hierarchy containing your audio files. Within
minutes, Faircamp builds a complete website presenting your work. The
resulting site requires no database, no programming, no maintenance, and is
compatible with virtually every webhost on this planet.
-- &lt;a href=&quot;https://simonrepp.com/faircamp/&quot;&gt;Faircamp Landing Page&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While this post is about how I&amp;#39;m hosting Faircamp, it&amp;#39;s transferable
to any static site (folder full of html) you my want to share with the world.&lt;/p&gt;
&lt;h2&gt;What is even going on with your network?&lt;/h2&gt;
&lt;p&gt;I have a Raspberry Pi in my homelab. Nothing about this is uniquely suited to
that hardware though, anything you have will work just fine. It has a few
services running. All of them configured via Coolify, but I&amp;#39;m not going to use that today. To use that would get in the way of the slick minimal deploy workflow that Faircamp supports out of the box.&lt;/p&gt;
&lt;h3&gt;How do I safely expose myself?&lt;/h3&gt;
&lt;p&gt;I don&amp;#39;t want to make my local network any more public than I need to. Instead
of opening a port on my router, I&amp;#39;m going to make use of Cloudflare&amp;#39;s Zero Trust Tunnel. At the time of writing, you can make use of this for free and it&amp;#39;s a
user-friendly way to enable a local port to be accessible to the outside
world without having a static IP address. If all of that is Greek to you, no
problem, the point is you don&amp;#39;t need to deal with any of it.&lt;/p&gt;
&lt;h3&gt;DNS friction&lt;/h3&gt;
&lt;p&gt;Since this blog is hosted on CloudCannon, I have been using their DNS
configuration tool. It&amp;#39;s the most convenient way to get a domain name pointing
at a site they host. The problem today is, it doesn&amp;#39;t allow for me to do more
advanced things like wire up this Cloudflare tunnel. To enable that, I had to
move control of my domain name to Cloudflare. That went pretty smoothly because it&amp;#39;s a well documented popular choice.&lt;/p&gt;
&lt;p&gt;One gotcha that is easy to overlook is that the default CNAME for CloudCannon is &lt;code&gt;site.cloudcannon.com&lt;/code&gt; but it does not work with Cloudflare and that needs to be changed to &lt;code&gt;orange-cloud.cloudcannon.com&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Here&amp;#39;s a snapshot of what I did in the abstract to get things working.&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;*&lt;/td&gt;
&lt;td&gt;orange-cloud.cloudcannon.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;music&lt;/td&gt;
&lt;td&gt;(tunnel hostname)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Caddy as my web server&lt;/h2&gt;
&lt;p&gt;Thus far, all of the services being hosted on this computer are by way of docker containers. This means I didn&amp;#39;t have any sort of web server installed directly on the Pi yet. I&amp;#39;m not super familiar with Caddy, but I went with it on this project over apache or nginx. It seems modern and light-weight and it did &lt;em&gt;just work&lt;/em&gt; for me. Here is the entirety of my config (&lt;code&gt;/etc/caddy/Caddyfile&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;:1337 {
    root * /opt/music-site
    file_server
    handle_errors {
        respond “{err.status_code}“
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In that code snippet, &lt;code&gt;opt&lt;/code&gt; is the location I decided to place my static site.
Historically, &lt;code&gt;var/www&lt;/code&gt; is pretty idiomatic place for static html, but it also
kind of suggests to me that you&amp;#39;re using Apache. Services seem more appropriate in &lt;code&gt;opt&lt;/code&gt; and I am planning to set some up in Caddy soon. Considering I&amp;#39;m planning to serve up some rust services soon, I&amp;#39;m going to standardize on &lt;code&gt;opt&lt;/code&gt; for all the things that Caddy cares about on this computer. The choice is debatable. At the same time, the stakes couldn&amp;#39;t be lower.&lt;/p&gt;
&lt;p&gt;Another thing to note, if you&amp;#39;re just getting started with Caddy, is you want to
use the service for this. Don&amp;#39;t start Caddy on your server with &lt;code&gt;caddy anything&lt;/code&gt;. You want to run &lt;code&gt;sudo systemctl enable caddy&lt;/code&gt;. That way, the web server is started every time your computer is.&lt;/p&gt;
&lt;h2&gt;What does deployment look like?&lt;/h2&gt;
&lt;p&gt;Faircamp builds the site locally and even handles deployment with the following arguments.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;faircamp -d --deploy-destination user@raspberrypi:/opt/music-site/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works by way of rsync so you&amp;#39;ll need all the typical permission stuff set
up for this to work smoothly. I already had an ssh key established to avoid
needing a password each time.&lt;/p&gt;
&lt;p&gt;The only thing I had to do was give my user access to the opt folder. You&amp;#39;ll
likely see the same issue as this the &lt;code&gt;opt&lt;/code&gt; folder is locked down by default.
Here are my settings, yours will be different unless many fantastic coincidences line up.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo chown -R ryan:ryan /opt/music-site
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Tada&lt;/h2&gt;
&lt;p&gt;And that&amp;#39;s how I served up a collection of music I made via a Raspberry Pi. The
state of the web is in a funny place where we just keep heaping complexity on
top of abstractions. The idea of having a single binary run over a folder of
static files and rsyncing the result to a computer I own felt refreshing.&lt;/p&gt;
</content:encoded></item><item><title>I Finally Got POSSE Automated</title><link>https://ryanparsley.com/blog/2025/2025-08-31-posse-astro-integration</link><guid isPermaLink="true">https://ryanparsley.com/blog/2025/2025-08-31-posse-astro-integration</guid><description>My journey from manual syndication to fully automated POSSE.</description><pubDate>Sun, 31 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Almost a year ago, I wrote about my &lt;a href=&quot;/blog/posse&quot;&gt;game plan for POSSE&lt;/a&gt;. Like
many automation projects, it sat on my back burner making me feel guilty every
time I manually posted to social media. Today I finally built an automated
workflow I&amp;#39;m proud to share.&lt;/p&gt;
&lt;h2&gt;Static sites are the bees knees, but...&lt;/h2&gt;
&lt;p&gt;My blog runs on Astro, which is great, but I wanted to syndicate my ephemera
posts (notes in IndieWeb parlance) automatically to &lt;a href=&quot;https://mastodon.social/@RyanParsley&quot;&gt;social
media&lt;/a&gt;. The rub is you want bidirectional
linking and SSG has a bit of a chicken and egg problem.&lt;/p&gt;
&lt;p&gt;The gist is this, publishing content on your own site spawns the creation of a
post elsewhere. The syndicated post links to the canonical post and the
canonical post links to the syndication. The rub is, when you write markdown,
there is no syndicated post to mention in the frontmatter. This is a little
beyond the sweet spot of static sites, but not unachievable.&lt;/p&gt;
&lt;h3&gt;The old way was &lt;em&gt;fine&lt;/em&gt; but not great&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;#39;d write a post, publish it&lt;/li&gt;
&lt;li&gt;Remember to copy the content to Mastodon&lt;/li&gt;
&lt;li&gt;Do the same for Bluesky&lt;/li&gt;
&lt;li&gt;Hope I didn&amp;#39;t mess up the formatting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It worked, but I aspired for a more seamless process. Often, I&amp;#39;d regress to
simply using the mastodon app and only occasionally link up an ephemeral post
after the fact.&lt;/p&gt;
&lt;h3&gt;The new hotness&lt;/h3&gt;
&lt;p&gt;I spent a weekend refactoring everything into a proper Astro integration. Here&amp;#39;s
what the new system does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scans my ephemera folder automatically&lt;/li&gt;
&lt;li&gt;Finds posts from the last 24 hours&lt;/li&gt;
&lt;li&gt;Skips ones I&amp;#39;ve already shared&lt;/li&gt;
&lt;li&gt;Posts to Mastodon and Bluesky&lt;/li&gt;
&lt;li&gt;Handles images and links properly&lt;/li&gt;
&lt;li&gt;Logs everything so I can see what happened&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The best part? It runs every time I build the site. No extra steps, no
remembering to do it.&lt;/p&gt;
&lt;h2&gt;My first Astro integration&lt;/h2&gt;
&lt;p&gt;Since my site is served via CloudCannon, my first instinct was to use their
build hooks. I got a version working and it was fine but testing was manual and
I accidentally spammed my mastodon account via a bug. This had me thinking about
alternative implementation details.&lt;/p&gt;
&lt;p&gt;Getting this working required diving deeper into Astro&amp;#39;s API. Not gonna lie, the
idea of using an Astro Integration came from me using OpenCode as a rubber duck.
I&amp;#39;ve wired up a few 3rd party integrations but making a custom one hadn&amp;#39;t
occurred to me. I&amp;#39;m glad I went through the effort though because testing is way
easier and using Astro hooks feels more right than the previous approach did.&lt;/p&gt;
&lt;h3&gt;What that looks like&lt;/h3&gt;
&lt;p&gt;Here&amp;#39;s the core integration structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// src/integrations/posse.ts
export default function posseIntegration(options: PosseOptions = {}) {
  const { mastodon, bluesky, dryRun = false, maxPosts = 3 } = options;

  return {
    name: &amp;quot;posse-syndication&amp;quot;,
    hooks: {
      &amp;quot;astro:build:done&amp;quot;: async ({ logger }) =&amp;gt; {
        try {
          await runSyndication({ mastodon, bluesky, dryRun, maxPosts, logger });
          logger.info(&amp;quot;POSSE: Syndication process completed successfully&amp;quot;);
        } catch (error) {
          const errorMessage =
            error instanceof Error ? error.message : String(error);
          logger.error(`POSSE: Syndication process failed: ${errorMessage}`);
        }
      },
    },
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it just works. Every time I deploy, it syndicates new posts. No extra steps.&lt;/p&gt;
&lt;h3&gt;Code to post to the fediverse&lt;/h3&gt;
&lt;p&gt;This function handles content formatting for different platforms:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;function generatePostContent(
  data: EphemeraData,
  canonicalUrl: string,
  body: string,
  platform: &amp;quot;mastodon&amp;quot; | &amp;quot;bluesky&amp;quot;,
): string {
  const initialContent = body?.trim() ? cleanContentForSocial(body.trim()) : &amp;quot;&amp;quot;;
  const content =
    !initialContent || initialContent.length &amp;lt; 10
      ? data.title || &amp;quot;New ephemera post&amp;quot;
      : initialContent;

  const maxLength = platform === &amp;quot;bluesky&amp;quot; ? 300 : 400;
  // Reserve space for &amp;quot;\n\n&amp;quot; + canonicalUrl
  const urlSuffix = `\n\n${canonicalUrl}`;
  // Add safety buffer for grapheme counting differences and JSON overhead
  const safetyBuffer = platform === &amp;quot;bluesky&amp;quot; ? 20 : 10;
  const availableContentLength = maxLength - urlSuffix.length - safetyBuffer;

  const finalContent =
    content.length &amp;gt; availableContentLength
      ? content.substring(0, availableContentLength - 3) + &amp;quot;...&amp;quot;
      : content;

  return `${finalContent}${urlSuffix}`;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;More like a good start than a happy ending&lt;/h2&gt;
&lt;p&gt;This automation works and is useful today, but it&amp;#39;s a solid foundation for
bigger goals. For now though, I&amp;#39;m thrilled that my posts finally syndicate
themselves—no more manual copying and pasting, no more forgotten social media
updates. I can write a silly thing in markdown once, and trust copies find their
way to appropriate silos.&lt;/p&gt;
</content:encoded><category>code</category></item><item><title>LSDJ Hardware Notes</title><link>https://ryanparsley.com/blog/2025/2025-08-14-lsdj-notes</link><guid isPermaLink="true">https://ryanparsley.com/blog/2025/2025-08-14-lsdj-notes</guid><description>I&apos;ve been curious about LSDJ again and compiled notes about hardware considerations</description><pubDate>Thu, 14 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&amp;#39;ve been curious about &lt;a href=&quot;https://www.littlesounddj.com/lsd/index.php&quot;&gt;LSDJ&lt;/a&gt;
again and thought I&amp;#39;d write a different kind of post about it. If you&amp;#39;re not
familiar with LSDJ, it&amp;#39;s a chiptune tracker (music sequencer) written to be
played on a Gameboy. The project has been around for over 25 years and I think
it&amp;#39;s super cool. There&amp;#39;s a lot of information out there about how to use it but
I thought I&amp;#39;d write about hardware.&lt;/p&gt;
&lt;p&gt;First and foremost, the cheapest way to tinker with lsdj is to simply use
hardware you already have. You can fire up an emulator on your smart phone or
whatever computer you have at hand and get started for absolutely zero dollars.
The biggest downside to this approach is the complete lack of tactile feedback.
I think doing gameboy shaped things on a touch screen is a bummer of a
experience. It is super cool that you can and I accept that I&amp;#39;m super privileged
to look down my nose at using my pixel pro as a gameboy. Whatever, I love me
some buttons to mash. Let&amp;#39;s go shopping!&lt;/p&gt;
&lt;h2&gt;Shopping is a slippery slope&lt;/h2&gt;
&lt;p&gt;Once you decide to spend money on your LSDJ experience, it gets complicated
quickly. If you want nice buttons for you phone, you can get something like a
&lt;a href=&quot;https://amzn.to/3HyflGM&quot;&gt;GameSir G8&lt;/a&gt;, but that&amp;#39;s like $80 unless you can catch
a sale. For around $50 you can pick from half a dozen
&lt;a href=&quot;https://anbernic.com/&quot;&gt;Anbernic&lt;/a&gt; devices that are a whole fake gameboy (retro
emulation devices) to play on. Counterintuitively, that&amp;#39;s the second most
affordable way to go (first cheapest being to not spend any money at all). I
have my eye on the &lt;a href=&quot;https://amzn.to/47ghhyr&quot;&gt;RG35XX Pro&lt;/a&gt; because it has video
out which seems fun for this. However, my older
&lt;a href=&quot;https://amzn.to/4oA1SPu&quot;&gt;RG351V&lt;/a&gt; is still more than adequate so I&amp;#39;ll stick to
that. I bought mine for $112 three years ago but you can get them for half that
price now.&lt;/p&gt;
&lt;h2&gt;But I want a &lt;em&gt;real&lt;/em&gt; (game)boy&lt;/h2&gt;
&lt;p&gt;If you want a more authentic nostalgia hit and you don&amp;#39;t still have your old
gameboy around it&amp;#39;s probably the single most costly way to go. I&amp;#39;ve seen gameboy
advances go for about $60 - $70 at the flea market or half price books. Looking
on craigslist in my area, people have lost their minds and are asking twice
that. While $70 doesn&amp;#39;t sound that steep, you can&amp;#39;t play LSDJ on original
hardware without a &lt;a href=&quot;https://amzn.to/3HvuEjE&quot;&gt;cartridge&lt;/a&gt;. Those can run another
$50 or more depending on the quality/vendor/ gameboy model. So if you were to
buy a new to you used gameboy and an Everdrive, you&amp;#39;re looking at $120 - $200 to
get up and running. I have a GBA SP and an OG NDS but if I was starting over and
mostly interested in LSDJ, I&amp;#39;d look at the &lt;a href=&quot;https://funnyplaying.com/products/fpgbc-console?pr_prod_strat=e5_desc&amp;pr_rec_id=fb311b7de&amp;pr_rec_pid=7445149450301&amp;pr_ref_pid=7147650809917&amp;pr_seq=uniform&quot;&gt;Funny
Playing&lt;/a&gt;
gameboy color clone. It looks like a modded GBC but all the electronics are new
and you can pick up a complete for $100 or a kit for around $80. To my
sensibilities, $80 for a gbc with that upgraded screen is the way to go if you
want something that feels like a GBC and aren&amp;#39;t super hung up on it actually
being vintage hardware.&lt;/p&gt;
&lt;h2&gt;What about DIY!?&lt;/h2&gt;
&lt;p&gt;In 2025, that doesn&amp;#39;t make much sense unless you&amp;#39;re just either &lt;strong&gt;really&lt;/strong&gt;
interested in the experience of making one (which is great and you should do
that) or want to rig up something custom. A Raspberry Pi 3 (older model that&amp;#39;s
less power hungry, runs cooler and is a touch cheaper) is $35. Already, you can
buy a whole complete ready to rock &lt;a href=&quot;https://s.click.aliexpress.com/e/_ooBMyc1&quot;&gt;BATLEXP G350
(aliExpress)&lt;/a&gt;. You don&amp;#39;t have a
screen ($40), battery, buttons, case ... etc. If you really want to go DIY, a
piGrrl kit is reasonable to look at to stay grounded in all the bits you&amp;#39;ll need
to source. The &lt;a href=&quot;https://www.adafruit.com/product/3160&quot;&gt;PiGrrl Zero kit&lt;/a&gt; is
really cool and $45 (not including a pi that&amp;#39;s another $15) but I think the
screen is a little dinky at 2.2&amp;quot; (320x240). But for $60 you can build a complete
retro portable game console which is super cool to do.&lt;/p&gt;
&lt;h2&gt;Price Breakdown of options discussed&lt;/h2&gt;
&lt;p&gt;When considering the following, do not forget that if you happen to have a
Raspbery Pi sitting around or &lt;a href=&quot;https://amzn.to/419usxb&quot;&gt;8BitDo&lt;/a&gt; controller the
math shifts. The most affordable option is make use of what you have. Without
that context, here&amp;#39;s an overview of where we&amp;#39;re at with options as I see it.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hardware Option&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Just use your phone or computer&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User your phone and a nice controller&lt;/td&gt;
&lt;td&gt;$30-$80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buy an emulation device&lt;/td&gt;
&lt;td&gt;$35 and up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DIY an emulation device&lt;/td&gt;
&lt;td&gt;$60 and up ( ಠ_ಠ )&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buy a new to you old gameboy&lt;/td&gt;
&lt;td&gt;$120 and beyond!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;TLDR&lt;/h2&gt;
&lt;p&gt;GAS (Gear Acquisition Syndrome) is a real thing and if you want to make chiptune
music you don&amp;#39;t need to spend one red cent. Start by looking around at what you
have. Hopefully some of the above rambling reminded you of something you have
laying around and you can get some use out of it. Otherwise, the most affordable
way to go is with a retro emulation console like one by anbernic. If you&amp;#39;re
going vintage don&amp;#39;t forget to factor accessories into your budget.&lt;/p&gt;
&lt;h2&gt;It&amp;#39;s dangerous to go alone, take this&lt;/h2&gt;
&lt;p&gt;I captured some of the products with affiliate links for your convenience, if
you&amp;#39;re into that sort of thing.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/3HvuEjE&quot;&gt;Everdrive GB (Amazon)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://s.click.aliexpress.com/e/_ooBMyc1&quot;&gt;BATLEXP G350 (AliExpress)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/4mnsDFw&quot;&gt;TrimUI Brick (Amazon)&lt;/a&gt;(&lt;a href=&quot;https://s.click.aliexpress.com/e/_okxvPjX&quot;&gt;AliExpress&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/3HyflGM&quot;&gt;GameSir G8 (Amazon)&lt;/a&gt;(&lt;a href=&quot;https://s.click.aliexpress.com/e/_ooJgUfT&quot;&gt;AliExpress&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/419usxb&quot;&gt;8BitDo Pro (Amazon)&lt;/a&gt;(&lt;a href=&quot;https://s.click.aliexpress.com/e/_oD7CP2N&quot;&gt;AliExpress&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>music</category></item><item><title>How To Start Vim</title><link>https://ryanparsley.com/blog/2025/2025-04-24-how-to-start-vim</link><guid isPermaLink="true">https://ryanparsley.com/blog/2025/2025-04-24-how-to-start-vim</guid><description>People joke about how Vim is hard to quit, but I want to make it easier to start. Here is a collection of resources to help demystify Vim.</description><pubDate>Thu, 24 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;People joke about how Vim is hard to quit, but I want to make it easier to
&lt;em&gt;start&lt;/em&gt;. Here is a collection of resources that I believe demystify Vim to great
effect.&lt;/p&gt;
&lt;h2&gt;Why to Vim?&lt;/h2&gt;
&lt;p&gt;Personally, I prefer Vim to IDEs because I&amp;#39;m gradually building up
complexity that I mostly understand. Even if I largely copy/paste default
configurations, I&amp;#39;m selecting each plugin intentionally. As such, I don&amp;#39;t tend
to use the richer distro-like config options like AstroNvim or LazyVim. I
think they&amp;#39;re great and will poke through their docs for inspiration. However, I
want my config to be based on &lt;em&gt;my&lt;/em&gt; opinions and enjoyed largely building from
the ground up.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s why I&amp;#39;d recommend &lt;a href=&quot;https://github.com/nvim-lua/kickstart.nvim&quot;&gt;kickstart&lt;/a&gt;
as a config jumping off point. It&amp;#39;s less opinionated about what plugins you use
and more about teaching you what these hard to search concepts are. I say hard
to search because if you don&amp;#39;t know that an
&lt;a href=&quot;https://microsoft.github.io/language-server-protocol/&quot;&gt;LSP&lt;/a&gt; is what drives your
autocomplete... it&amp;#39;s hard to search LSP. Beyond the wonderfully documented code
found in the kickstart repo, TJ has a very accessible &lt;a href=&quot;https://www.youtube.com/watch?v=m8C0Cq9Uv9o&quot;&gt;youtube
video&lt;/a&gt; to introduce you to the
philosophy and tools.&lt;/p&gt;
&lt;h2&gt;What is the deal with Neovim!?&lt;/h2&gt;
&lt;p&gt;I switched to Neovim back in the Vim 7 era when there were more good reasons to
do so than there are now. After Vim 8 incorporated many downstream improvements,
my primary reason for sticking with Neovim became Lua support. Or more
precisely, my reason was to avoid Vimscript.&lt;/p&gt;
&lt;p&gt;I didn&amp;#39;t care to get good at a language that was only applicable in one narrow
context. There are other (arguably better) reasons to use Neovim but Lua is
enough for me (and I&amp;#39;m not even good at it).&lt;/p&gt;
&lt;h2&gt;This is my config. There are many like it, but this one is mine.&lt;/h2&gt;
&lt;p&gt;I don&amp;#39;t advocate that you fork my repo and use it &amp;quot;as-is&amp;quot;, but I offer &lt;a href=&quot;https://codeberg.org/RyanParsley/dotfiles/src/branch/main/nvim/.config/nvim/lua/plugins&quot;&gt;my
dotfiles&lt;/a&gt;
in the event any part of them may spark curiosity. I do advocate that you share
your dotfiles if you don&amp;#39;t already. If that&amp;#39;s a new to you journey, you may want
to check out my &lt;a href=&quot;https://ryanparsley.com/blog/stow&quot;&gt;post about Stow&lt;/a&gt; to make
that more ergonomic.&lt;/p&gt;
&lt;h2&gt;You got this!&lt;/h2&gt;
&lt;p&gt;Vim can be intimidating because it&amp;#39;s clearly a deep rabbit hole full of
wonderfully wacky yaks to shave. Relax, it&amp;#39;s &lt;em&gt;just&lt;/em&gt; a text editor. Kickstart
knocks down a few rough edges to make for a more ergonomic starting point. You
don&amp;#39;t &lt;em&gt;need&lt;/em&gt; to understand all the concepts of Vim as a prerequisite to
starting. I suspect there&amp;#39;s a ton about your current editor that you don&amp;#39;t know
and you&amp;#39;re fine with that. Get a little better every day and when you don&amp;#39;t like
how your workflow feels, change it.&lt;/p&gt;
</content:encoded><category>writing</category><category>code</category></item><item><title>Arduinoboy Build Notes</title><link>https://ryanparsley.com/blog/arduinoboy</link><guid isPermaLink="true">https://ryanparsley.com/blog/arduinoboy</guid><description>I made a thing so I can control MIDI via a Game Boy, and so can you!</description><pubDate>Fri, 27 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&amp;#39;ve been enjoying LSDJ recently and building an
&lt;a href=&quot;https://github.com/trash80/Arduinoboy&quot;&gt;Arduinoboy&lt;/a&gt; was the next &amp;quot;logical&amp;quot; step.
I&amp;#39;ve also been itching for an excuse to mill a PCB on my CNC. This felt like a
project that was just complicated enough that I&amp;#39;d benefit from the new-to-me
technique.&lt;/p&gt;
&lt;h2&gt;On the shoulders of giants&lt;/h2&gt;
&lt;p&gt;I stumbled upon a schematic in this &lt;a href=&quot;https://chipmusic.org/forums/post/202844/#p202844&quot;&gt;forum
post&lt;/a&gt; by way of &lt;a href=&quot;https://gr33nonline.wordpress.com/2019/04/06/making-your-own-arduinoboy/&quot;&gt;this blog
post&lt;/a&gt;.
While a PNG is not the most convenient way to express a circuit, I&amp;#39;m glad to have
found it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./arduinoBoyBW.png&quot; alt=&quot;Arduinoboy Black and White&quot;&gt;&lt;/p&gt;
&lt;p&gt;I opened the PNG in inkscape and traced it as a vector. From there, I
exported an SVG to import into Fusion 360. As you convert images from one format
to another be sure you don&amp;#39;t drift away from the desired board dimensions.
You&amp;#39;re targeting a board with the dimensions of 1.875&amp;quot; x 1.55&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./PCB_Simulation.png&quot; alt=&quot;PCB Simulation&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Millin&amp;#39; time&lt;/h2&gt;
&lt;p&gt;Once I had that sorted out, I sent it over to my &lt;a href=&quot;/note/mpcnc&quot;&gt;MPCNC&lt;/a&gt; for
milling.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./milled_PCB.jpg&quot; alt=&quot;Milled PCB&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Time to solder&lt;/h2&gt;
&lt;p&gt;Following this image, I soldered it up. Some people cut open the link cable and
solder that directly. I didn&amp;#39;t want to sacrifice the cable as a consumable so I
ordered up a &lt;a href=&quot;https://github.com/Palmr/gb-link-cable&quot;&gt;breakout PCB&lt;/a&gt; and soldered
that to my milled PCB.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./arduino_schematic.png&quot; alt=&quot;PCB
Schematic&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Link cable schematics&lt;/h3&gt;
&lt;p&gt;Since the link cable isn&amp;#39;t a standard like USB, the pinout was one more mystery
to solve. Fortunately, once again, kind strangers on the internet documented
their discoveries. This picture was worth more than 1000 words to me.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./linkCablePins.png&quot; alt=&quot;Link Cable Pins&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/afska/gba-remote-play&quot;&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Connections&lt;/h4&gt;
&lt;p&gt;As illustrated in the image above, the orientation of the plug assumes you&amp;#39;re
looking at it with the nubbin pointing up. The idea of top and bottom gets a
little muddy as you go from one blog post to another so be careful about that.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Really you only need 3 wires: Gound, Clock, and Data Input.&lt;/p&gt;
&lt;p&gt;--&lt;a href=&quot;https://forum.arduino.cc/t/arduino-gameboy-midi-communication/7151/4&quot;&gt;Found in this post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This table translates the data in the Link Cable image to the labels in the red
schematic from the &amp;quot;Time to solder&amp;quot; section.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plug Position&lt;/th&gt;
&lt;th&gt;Board&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Bottom Left&lt;/td&gt;
&lt;td&gt;TL&lt;/td&gt;
&lt;td&gt;VCC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bottom Center&lt;/td&gt;
&lt;td&gt;TC&lt;/td&gt;
&lt;td&gt;Serial In (SIN)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bottom Right&lt;/td&gt;
&lt;td&gt;TR&lt;/td&gt;
&lt;td&gt;Clock (SCLK)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top Left&lt;/td&gt;
&lt;td&gt;BL&lt;/td&gt;
&lt;td&gt;Serial Out (SOUT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top Center&lt;/td&gt;
&lt;td&gt;BC&lt;/td&gt;
&lt;td&gt;Not Connected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top Right&lt;/td&gt;
&lt;td&gt;BR&lt;/td&gt;
&lt;td&gt;Ground&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;It&amp;#39;s alive&lt;/h2&gt;
&lt;p&gt;Here is a shot of my custom PCB wired up to my Game Boy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./arduinoboy_soldered_up.jpg&quot; alt=&quot;Soldered up&quot;&gt;&lt;/p&gt;
&lt;h2&gt;In a slick enclosure&lt;/h2&gt;
&lt;p&gt;I couldn&amp;#39;t just leave it exposed like that. I took a little time to model a
&lt;a href=&quot;https://www.printables.com/model/783225-arduinoboy-enclosure&quot;&gt;custom
enclosure&lt;/a&gt;. Here
it is all finished up!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Arduinoboy_Enclosed.jpg&quot; alt=&quot;Arduinoboy Finished&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;p&gt;This was a fairly involved project that was far from paint by numbers. All in,
it took right at 2 weeks of &amp;quot;free time&amp;quot; to pull it off. Here are some dates of
milestones I captured.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2024-02-12 Milled&lt;/li&gt;
&lt;li&gt;2024-02-15 Soldered&lt;/li&gt;
&lt;li&gt;2024-02-21 Functioning&lt;/li&gt;
&lt;li&gt;2024-02-26 In an Enclosure&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Parts to consider&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/47IsoOK&quot;&gt;Everdrive GB x5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/47NSP5I&quot;&gt;Everdrive GBA x5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/3XLSHPg&quot;&gt;Link cable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/3ZJGDRg&quot;&gt;GBA Link Cable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/4ehOzOb&quot;&gt;GBA headphone dongle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://amzn.to/3N9CluR&quot;&gt;Copper blank for PCB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Links to Amazon have an affiliate tag on them. That happens to be where I do a
lot of shopping and all of those components are the one&amp;#39;s I used and can
confirm worked for me.&lt;/p&gt;
&lt;h4&gt;Selecting an Everdrive&lt;/h4&gt;
&lt;p&gt;Note about your Everdrive choice, LSDJ is a Game Boy game, not GBA. If you go
with the GBA, there is an emulator and you can use LSDJ through emulation.
However, there&amp;#39;s a big but there. Saving songs doesn&amp;#39;t work in LSDJ through
emulation. I own both and use them in different contexts. For the smoothest
LSDJ experience, buy the Game Boy version. If you&amp;#39;re looking to play with
Stepper, that&amp;#39;s a Game Boy Advanced game so you&amp;#39;ll need the GBA Everdrive.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Building this Arduinoboy was fun and challenging. It was a good excuse to
combine many interests (chiptune music, electronics, and 3D printing) in one
rich project. I found it to be a great MacGuffin to learn about PCB design and
fabrication. If you&amp;#39;re considering building your own Arduinoboy, I hope you find
these notes helpful!&lt;/p&gt;
</content:encoded><category>diy</category><category>music</category></item><item><title>Refactoring my Astro Gallery Component</title><link>https://ryanparsley.com/blog/astro-gallery-component</link><guid isPermaLink="true">https://ryanparsley.com/blog/astro-gallery-component</guid><description>I refactored my Gallery Component to make better use of the Astro build process.</description><pubDate>Thu, 26 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the first components I wrote for this blog migration was a naive image
gallery. It&amp;#39;s pretty straight forward and got mostly got the job done, but there
was a few implementation details I&amp;#39;ve been itching to improve and I recently
took the time to make those enhancements. Let&amp;#39;s dig into the details of my
refactor and talk about some of the benefits of this new iteration.&lt;/p&gt;
&lt;h2&gt;How it started&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s start by looking at the first iteration that I shipped. This component
was functional, and a completely respectable thing to ship to production, but
there&amp;#39;s always room for improvement. Does anything jump out to you as
problematic before you read on?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;---
type Image = {
 url: string;
 alt: string;
};
type Props = {
 images: Image[];
};
const { images } = Astro.props;
---
&amp;lt;div class=&amp;quot;gallery&amp;quot;&amp;gt;
 {
  images.map((image: Image, index: number) =&amp;gt; (
   &amp;lt;&amp;gt;
    &amp;lt;figure&amp;gt;
     &amp;lt;a href={`#image-${index}`} title={image.alt}&amp;gt;
      &amp;lt;img src={image.url} alt={image.alt} /&amp;gt;
     &amp;lt;/a&amp;gt;
    &amp;lt;/figure&amp;gt;
    &amp;lt;a
     href=&amp;quot;#_&amp;quot;
     class=&amp;quot;lightbox&amp;quot;
     id={`image-${index}`}
     title=&amp;quot;Click to return to post&amp;quot;
    &amp;gt;
     &amp;lt;div style={`background-image:url(${image.url})`} /&amp;gt;
    &amp;lt;/a&amp;gt;
   &amp;lt;/&amp;gt;
  ))
 }
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;How it&amp;#39;s going&lt;/h2&gt;
&lt;p&gt;Now, let&amp;#39;s take a look at my recently refactored version. It&amp;#39;s got more going on
to be sure.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;---
import { Image } from &amp;quot;astro:assets&amp;quot;;
import type { ImageMetadata } from &amp;quot;astro&amp;quot;;
type ImageData = {
 url: string;
 alt: string;
};
type Props = {
 images: ImageData[];
 path: string;
};
const { images, path } = Astro.props;
const getFullPath = (url: string) =&amp;gt;
 url.startsWith(&amp;quot;./&amp;quot;)
  ? `/src/content${path}/${url.replace(&amp;quot;./&amp;quot;, &amp;quot;&amp;quot;)}`
  : `/src/assets/${url.replace(&amp;quot;./&amp;quot;, &amp;quot;&amp;quot;)}`;
const imagePaths = images.map((img) =&amp;gt; ({
 ...img,
 url: getFullPath(img.url),
}));
const imageAssets = import.meta.glob&amp;lt;{ default: ImageMetadata }&amp;gt;(
 `/src/**/*.{jpeg,jpg,png,gif}`,
);
const deriveSrc = async (image: ImageData) =&amp;gt; {
 const source = imageAssets?.[image?.url]?.();
 if (source === undefined) {
  throw new Error(
   `&amp;quot;${image.url}&amp;quot; does not exist in glob: &amp;quot;src/assets/*.{jpeg,jpg,png,gif}&amp;quot;`,
  );
 }
 return source;
};
---
&amp;lt;div class=&amp;quot;gallery&amp;quot;&amp;gt;
 {
  imagePaths.map((image: ImageData, index: number) =&amp;gt; (
   &amp;lt;&amp;gt;
    &amp;lt;figure&amp;gt;
     &amp;lt;a href={`#image-${index}`} title={image.alt}&amp;gt;
      &amp;lt;Image src={deriveSrc(image)} alt={image.alt} /&amp;gt;
     &amp;lt;/a&amp;gt;
    &amp;lt;/figure&amp;gt;
    &amp;lt;a
     href=&amp;quot;#_&amp;quot;
     class=&amp;quot;lightbox&amp;quot;
     id={`image-${index}`}
     title=&amp;quot;Click to return to post&amp;quot;
    &amp;gt;
     &amp;lt;div&amp;gt;
      &amp;lt;Image src={deriveSrc(image)} alt={image.alt} /&amp;gt;
     &amp;lt;/div&amp;gt;
    &amp;lt;/a&amp;gt;
   &amp;lt;/&amp;gt;
  ))
 }
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Key improvements&lt;/h2&gt;
&lt;p&gt;My initial design failed to take advantage of Astro&amp;#39;s build-time image
optimization. The reason I couldn&amp;#39;t take advantage of that was this component
looked in the &lt;code&gt;public&lt;/code&gt; directory for assets. Only assets in the &lt;code&gt;src&lt;/code&gt; directory
are optimized at build time. As I wanted my component to receive props from
markdown frontmatter, relative pathing was a little tricker than I wanted to
sort out upfront. Supporting this is the primary reason for all the extra
complexity in the refactor.&lt;/p&gt;
&lt;h3&gt;Astro &lt;code&gt;Image&lt;/code&gt; integration&lt;/h3&gt;
&lt;p&gt;The refactor starts with the introduction of the &lt;code&gt;Image&lt;/code&gt; component from
&lt;code&gt;astro:assets&lt;/code&gt;. This is better than the native &lt;code&gt;img&lt;/code&gt; tag for the following
reasons.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic image optimization&lt;/li&gt;
&lt;li&gt;Lazy loading out of the box&lt;/li&gt;
&lt;li&gt;Proper sizing and srcset generation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;With great power... yada yada&lt;/h3&gt;
&lt;p&gt;Switching to this richer image solution comes with some complexity to get it up
and running. First, I can&amp;#39;t simply use a &lt;code&gt;src&lt;/code&gt; string prop and call it a day. I
I need to import images so Astro is enabled to do it&amp;#39;s thing at build time. The
docs make this look simple enough because they&amp;#39;re just importing a single static
asset. In my case, this gets more complicated because I want to handle this
dynamically. Fortunately, a more advanced recipe introduced me to a vite method
&lt;code&gt;import.meta.glob&lt;/code&gt; to help. The following code will gather all images found in
the &lt;code&gt;src&lt;/code&gt; directory and make them available for the &lt;code&gt;Image&lt;/code&gt; component.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const imageAssets = import.meta.glob&amp;lt;{ default: ImageMetadata }&amp;gt;(
  `/src/**/*.{jpeg,jpg,png,gif}`,
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Flexible path handling&lt;/h3&gt;
&lt;p&gt;Access to images is only half of the equation though. I need to sort out a
sensible strategy for mapping my frontmatter to this collection to retrieve the
images I want. The new &lt;code&gt;getFullPath&lt;/code&gt; function allows for this. I may build this
out more later, but for now, I images relative to the markdown, or absolute by
way of a designated asset directory.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const getFullPath = (url: string) =&amp;gt;
  url.startsWith(&amp;quot;./&amp;quot;)
    ? `/src/content${path}/${url.replace(&amp;quot;./&amp;quot;, &amp;quot;&amp;quot;)}`
    : `/src/assets/${url.replace(&amp;quot;./&amp;quot;, &amp;quot;&amp;quot;)}`;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When I first read about the Image component, I naively thought I&amp;#39;d get
performance &amp;quot;for free&amp;quot;. While supporting this took some additional work, both my
developer experience and the performance of my site are noticeably better for
having made this refactor. I&amp;#39;m trying to make it a little better every day.&lt;/p&gt;
</content:encoded><category>code</category></item><item><title>My POSSE on Broadband</title><link>https://ryanparsley.com/blog/posse</link><guid isPermaLink="true">https://ryanparsley.com/blog/posse</guid><description>Thoughts on how to own your data and play well with others</description><pubDate>Sun, 08 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I signed up for Twitter back in 2007. It was a simpler time. It was a simpler
Twitter. I really enjoyed using the service and didn&amp;#39;t mind gradually
contributing more and more content there. I became aware of the fact that this
came at the cost of making less regular updates to my own blog. I made peace
with that as I had more interesting conversations on Twitter than in the
comments of my blog. I didn&amp;#39;t mind essentially trading RSS feeds for Twitter
lists. I traded ownership for convenience. Everything is a compromise and that
felt reasonable at the time.&lt;/p&gt;
&lt;p&gt;Today, I&amp;#39;m evaluating that compromise and considering an alternative. I&amp;#39;m trying
to think of my personal site as less of a &amp;quot;blog&amp;quot; and more of a &amp;quot;&lt;a href=&quot;https://maggieappleton.com/garden-history&quot;&gt;digital garden&lt;/a&gt;&amp;quot;.
That&amp;#39;s a whole other post, but the significance here is: this has reduced
friction to writing for me and got me more excited to post than I&amp;#39;ve been in a
while. As I create more, I think more about workflow and tooling. I want &lt;em&gt;my
garden&lt;/em&gt; to be canonical and it looks like some smart people sorted out a
reasonable strategy for this by the name &lt;a href=&quot;https://indieweb.org/posse&quot;&gt;POSSE&lt;/a&gt; .&lt;/p&gt;
&lt;h2&gt;One does not simply comment on a static site&lt;/h2&gt;
&lt;p&gt;Most dynamic content management systems ship with some sort of comment feature,
but that&amp;#39;s less trivial to implement with a static site. There&amp;#39;s a lot of ways
to handle this, but I want to lean into the
&lt;a href=&quot;https://www.w3.org/TR/webmention/&quot;&gt;Webmention&lt;/a&gt; recommendation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you publish a post with links to other sites, your site can send a
webmention notification to sites that accept them to inform them that they
were linked to.&lt;/p&gt;
&lt;p&gt;A webmention receiver provides discovery of their webmention endpoint so it can
be found by others who link to the page. When a receiver’s endpoint receives a
webmention notification, it looks up the sender’s post and may display it as a
response.&lt;/p&gt;
&lt;p&gt;Mentions mainly require a link to the recipient on the page, but they can be
enhanced into more meaningful responses (such as comments, reposts, or likes)
by adding microformats to the sender’s post.&lt;/p&gt;
&lt;p&gt;--&lt;a href=&quot;https://indieweb.org&quot;&gt;indieWeb.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You could say there&amp;#39;s still a compromise in that I don&amp;#39;t &amp;quot;own the engagement&amp;quot;.
But, as I stated, everything is a compromise and I think I like this compromise
better. I want to own my content, but I don&amp;#39;t feel a need to own the
interactions with my content. Enabling readers to comment where they like and
aggregate that on my site feels like a reasonable thing to do.&lt;/p&gt;
&lt;h2&gt;Ok, but what is POSSE!?&lt;/h2&gt;
&lt;p&gt;POSSE stands for &amp;quot;Publish (on your) Own Site, Syndicate Everywhere.&amp;quot; The concept
is straightforward, but implementation can be a little tricky. In a nutshell
though, by opting into a few microformats, you can enrich the site you control
with engagement that occurs on platforms you don&amp;#39;t.&lt;/p&gt;
&lt;h3&gt;The POSSE Workflow as I understand it&lt;/h3&gt;
&lt;p&gt;This is getting a bit abstract, so I&amp;#39;ll reign it in with some concrete examples.
At the moment, I&amp;#39;m enjoying publishing my content via
&lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;. Nothing about my workflow is unique to Astro, just
the implementation details. Once you have content available on the web, sharing
via a platform like Mastodon satisfies the &amp;quot;syndication&amp;quot; bit. Assuming you have
your site setup to consume Webmentions, you can use Bridgy to convert Mastodon
replies to webmentions that you can display on your own blog.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart
    A[/Write Markdown/] --&amp;gt; B(Static Site)
    B --&amp;gt; C[/Share or Syndicate/]
    C --&amp;gt; D(Mastodon)
    C --&amp;gt; E(Pixelfed)
    C --&amp;gt; F(Bluesky)
    C --&amp;gt; G(Other Platforms)
    D --&amp;gt; H[/Mastodon Comments/]
    E --&amp;gt; I[/Pixelfed Comments/]
    F --&amp;gt; J[/Bluesky Comments/]
    G --&amp;gt; K[/Other Platform Comments/]
    H --&amp;gt; L(Bridgy)
    I --&amp;gt; L(Bridgy)
    J --&amp;gt; L(Bridgy)
    K --&amp;gt; L(Bridgy)
    L --&amp;gt; M(Webmentions)
    M --&amp;gt; B(Static Site)

    subgraph POSSE Workflow
        C[/Share or Syndicate/]
        D(Mastodon Post)
        E(Pixelfed Post)
        F(Bluesky Post)
        G(Other Platform Posts: Twitter, Facebook, LinkedIn)
        H[/Mastodon Comments/]
        I[/Pixelfed Comments/]
        J[/Bluesky Comments/]
        K[/Other Platform Comments/]
        L(Bridgy)
        M(Webmentions: webmention.io)
    end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Where do we go from here?&lt;/h2&gt;
&lt;p&gt;I&amp;#39;m currently folding ideas from this post into my blog. At the moment, I don&amp;#39;t
have syndication automated. I&amp;#39;m cautious about appearing overly promotional as I
navigate this new approach. I&amp;#39;ve shared a couple posts to
&lt;a href=&quot;https://mastodon.social/@RyanParsley/113082555966252676&quot;&gt;Mastodon&lt;/a&gt; and
&lt;a href=&quot;https://mastodon.social/@RyanParsley/113082577656954796&quot;&gt;replies&lt;/a&gt;
&lt;a href=&quot;/note/webmentions&quot;&gt;prove&lt;/a&gt; my workflow is functioning as intended.&lt;/p&gt;
&lt;p&gt;If you find this useful, hit me up on
&lt;a href=&quot;https://mastodon.social/@RyanParsley&quot;&gt;Mastodon&lt;/a&gt; or
&lt;a href=&quot;https://bsky.app/profile/ryanparsley.bsky.social&quot;&gt;Bluesky&lt;/a&gt;. If I have something
wrong or you find a bug in my blog, I&amp;#39;d love to see it as an
&lt;a href=&quot;https://github.com/RyanParsley/rpdc/issues/new&quot;&gt;issue&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>writing</category><category>code</category></item><item><title>Zod makes TypeScript better</title><link>https://ryanparsley.com/blog/zod</link><guid isPermaLink="true">https://ryanparsley.com/blog/zod</guid><description>Explore how Zod can complement TypeScript to provide runtime validation and enhanced type safety in your web development projects.</description><pubDate>Sat, 31 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;TypeScript can be leveraged reduce certain classes of bugs, but there are still
plenty of edges left exposed. Zod can elevate your type checking to make your
code more bug resistant. This post will explore what Zod is, why you might want
to use it alongside TypeScript, and how to integrate both into your app
effectively.&lt;/p&gt;
&lt;h2&gt;What is Zod?&lt;/h2&gt;
&lt;p&gt;Zod is a TypeScript-first schema declaration and validation library. It enables
you to create schemas for your data structures, which can be used for
both compile-time type inference and runtime data validation.&lt;/p&gt;
&lt;h3&gt;Key features&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;TypeScript-first approach&lt;/li&gt;
&lt;li&gt;Zero dependencies&lt;/li&gt;
&lt;li&gt;Compatibility with both Node.js and browser environments&lt;/li&gt;
&lt;li&gt;Support for complex object shapes, including nested objects and arrays&lt;/li&gt;
&lt;li&gt;Detailed error messages for invalid data&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;I&amp;#39;m already using TypeScript, why do I need another tool!?&lt;/h2&gt;
&lt;p&gt;The two tools sound like they&amp;#39;re addressing the same thing, but they have subtly
different strengths that complement each other. If you&amp;#39;re skeptical about
typescript helping readability, I&amp;#39;d recommend &lt;a href=&quot;/blog/2024-08-24-fancy-types-enable-simple-code&quot;&gt;this
post&lt;/a&gt; that I recently wrote on the
subject.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Runtime vs. Compile-time Checking&lt;/p&gt;
&lt;p&gt;TypeScript performs type checking at compile-time, catching type-related
errors before your code runs. However, it doesn&amp;#39;t provide runtime type
checking. Zod, on the other hand, allows you to perform validation at
runtime, ensuring that your data conforms to the expected shape even when it
comes from external sources like API responses or user inputs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More Expressive Schemas&lt;/p&gt;
&lt;p&gt;While TypeScript&amp;#39;s type system is powerful, Zod allows you to define even
more expressive schemas. You can easily add constraints like minimum and
maximum values, regex patterns, or custom validation logic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better Error Handling&lt;/p&gt;
&lt;p&gt;Zod provides detailed error messages when validation fails, making it easier
to identify and fix issues in your data.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Will it blend?&lt;/h2&gt;
&lt;p&gt;When introducing Zod alongside TypeScript, I was concerned about the potential
brittleness of managing two type systems. Turns out, that needn&amp;#39;t be a problem.
Zod is designed to work seamlessly with TypeScript, and when used correctly, it
takes over as the single source of truth. So, yes, it&amp;#39;s a new thing to learn.
However, it&amp;#39;s not &lt;em&gt;an additional&lt;/em&gt; thing to maintain over time.&lt;/p&gt;
&lt;h3&gt;Efficient Workflow&lt;/h3&gt;
&lt;p&gt;Here&amp;#39;s a workflow that leverages the strengths of both TypeScript and Zod while
minimizing duplication and potential inconsistencies:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Define Zod Schemas First&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Start by defining your data structures using Zod schemas. These will serve as
the single source of truth for your types.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { z } from &amp;quot;zod&amp;quot;;

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  age: z.number().min(18).optional(),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Infer TypeScript Types from Zod Schemas&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Next, use Zod&amp;#39;s built-in type inference to generate TypeScript types from
your schemas. This ensures alignment between your runtime validation and
compile-time types.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;type User = z.infer&amp;lt;typeof UserSchema&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Inferred Types in Your TypeScript Code&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now you can use the inferred types throughout your TypeScript code,
benefiting from compile-time type checking.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;function greetUser(user: User) {
  console.log(`Hello, ${user.name}!`);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validate Data at Runtime&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Use your Zod schemas to validate data at runtime, especially for external
inputs.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;function processUserData(data: unknown) {
  const user = UserSchema.parse(data);
  greetUser(user);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extend Schemas as Needed&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you need to modify your data structures, update the Zod schema. The
TypeScript types will automatically update, ensuring consistency.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const ExtendedUserSchema = UserSchema.extend({
  preferences: z.object({
    theme: z.enum([&amp;quot;light&amp;quot;, &amp;quot;dark&amp;quot;]),
    notifications: z.boolean(),
  }),
});

type ExtendedUser = z.infer&amp;lt;typeof ExtendedUserSchema&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Benefits of This Workflow&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single Source of Truth&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Your Zod schemas define both runtime validation
and TypeScript types, eliminating duplication.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type Safety&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You get the compile-time benefits of TypeScript and the
runtime safety of Zod.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Zod&amp;#39;s expressive schema definition allows for complex
validations that TypeScript alone can&amp;#39;t handle.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy Maintenance&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you need to change your data structures, you only
need to update the Zod schema. TypeScript types automatically stay in sync.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gradual Adoption&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can introduce Zod schemas gradually into an
existing TypeScript project, starting with the most critical data structures.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By following this workflow, you can efficiently use TypeScript and Zod together,
enhancing your app&amp;#39;s type safety without introducing unnecessary complexity or
brittleness. The key is to let Zod drive your type definitions and use
TypeScript&amp;#39;s inference capabilities to bridge the gap between runtime and
compile-time type checking.&lt;/p&gt;
&lt;h2&gt;Best Practices&lt;/h2&gt;
&lt;p&gt;To make the most of Zod and TypeScript in your projects, consider these best
practices and common pitfalls to avoid:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define schemas as constants&lt;/li&gt;
&lt;li&gt;Use .parse() for unknown data&lt;/li&gt;
&lt;li&gt;Leverage Zod&amp;#39;s built-in methods&lt;/li&gt;
&lt;li&gt;Compose schemas&lt;/li&gt;
&lt;li&gt;Use .extend() for inheritance&lt;/li&gt;
&lt;li&gt;Utilize Zod&amp;#39;s inference for TypeScript types&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Common pitfalls to watch out for include forgetting to handle validation errors,
overusing .any() or .unknown(), neglecting runtime validation, and not taking
advantage of Zod&amp;#39;s error customization features.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Integrating Zod with TypeScript offers a powerful combination for enhancing type
safety in your web apps. As you implement this approach, you&amp;#39;ll likely find that
the initial investment in setting up Zod schemas pays dividends in terms of code
reliability and maintainability.&lt;/p&gt;
</content:encoded><category>code</category></item><item><title>Fancy types enable simple code</title><link>https://ryanparsley.com/blog/2024-08-24-fancy-types-enable-simple-code</link><guid isPermaLink="true">https://ryanparsley.com/blog/2024-08-24-fancy-types-enable-simple-code</guid><description>Thoughts on type safety and generic types in astro</description><pubDate>Sat, 24 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Developers often claim that leveraging TypeScript&amp;#39;s advanced type system can
lead to more readable and maintainable code. While that can be the case, it&amp;#39;s
not guaranteed. If you&amp;#39;re not careful in implementation, following the orders
of a pedantic linters can feel like unwanted friction to shipping code. And
worse, you could be adding brittleness more than robustness to your code. This
article explores a case study in how generics and union types can improve
readability by enabling inference.&lt;/p&gt;
&lt;p&gt;Suppose you want to create a list of tags for notes. The following approach
seems reasonable at first glance. It works fine because it presumes you only
want to consider tags for one type of content.&lt;/p&gt;
&lt;h2&gt;Naive first draft&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const tags = [
  ...new Set(
    (await getCollection(&amp;quot;note&amp;quot;))
      .filter((note) =&amp;gt; !!note.data.tags)
      .map((note) =&amp;gt; note.data.tags)
      .flat()
      .filter((tag) =&amp;gt; tag !== &amp;quot;note&amp;quot;),
  ),
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I currently have two very similar content types: &lt;code&gt;blog&lt;/code&gt; and &lt;code&gt;note&lt;/code&gt;. They both
have &lt;em&gt;tags&lt;/em&gt;, but they&amp;#39;re not guaranteed to be the same or stay similar beyond
that.&lt;/p&gt;
&lt;h3&gt;How do I adjust this component to be usable by both?&lt;/h3&gt;
&lt;p&gt;My initial approach was to move the logic such that this component only dealt
with an array of strings. There&amp;#39;s a problem with moving the responsibility of
deriving tags to the parent component, this approach is more likely to let
duplication of logic to creep in. As this component is responsible for &lt;code&gt;tags&lt;/code&gt;
and not simply making a list from strings, the &lt;code&gt;Tags&lt;/code&gt; component feels like where
this logic should live.&lt;/p&gt;
&lt;h2&gt;Problematic second draft&lt;/h2&gt;
&lt;p&gt;Don&amp;#39;t copy and paste this next bit, it&amp;#39;s just here so I can critique it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const { collection, baseRoute } = Astro.props;
type Collection = { data: { tags: string[] } };
const tags = [
  ...new Set(
    collection
      .filter((note: Collection) =&amp;gt; !!note.data.tags)
      .map((note: Collection) =&amp;gt; note.data.tags)
      .flat()
      .filter((tag: string) =&amp;gt; tag !== &amp;quot;note&amp;quot;),
  ),
] as string[];
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The good&lt;/h3&gt;
&lt;p&gt;Passing in a collection and baseRoute as Props feels like what I want. It seems
reasonable to assume the parent component is concerned with a particular content
type when the component is implemented, so let them declare that at that level.&lt;/p&gt;
&lt;h3&gt;The bad&lt;/h3&gt;
&lt;p&gt;The problematic bits are around that Collection type. I created it because, once
I passed a collection in, all the clever type inference broke. At that point, I
needed to explicitly type all the things to keep my pedantic linter happy.
That&amp;#39;s not what you want. This works as intended, but we can do better.&lt;/p&gt;
&lt;p&gt;Note, if you find yourself using &lt;code&gt;as&lt;/code&gt;, consider that you might be doing
something wrong. Maybe you&amp;#39;re not, but consider it. It exists for a reason, but
I find I usually don&amp;#39;t need it when I&amp;#39;m &amp;quot;done&amp;quot; refactoring.&lt;/p&gt;
&lt;h2&gt;Chef&amp;#39;s Kiss third draft&lt;/h2&gt;
&lt;p&gt;The trick to getting inferred types back in action hinges around leveraging a
Props interface. I&amp;#39;m new to Astro, but I suspect you&amp;#39;ll want to make use of this
interface more often than not. In this interface, I opted to use a type union
that expresses all the collection types that I&amp;#39;m accounting for.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import type { CollectionEntry } from &amp;quot;astro:content&amp;quot;;
const { collection, baseRoute } = Astro.props;
interface Props {
  collection: CollectionEntry&amp;lt;&amp;quot;blog&amp;quot;&amp;gt;[] | CollectionEntry&amp;lt;&amp;quot;note&amp;quot;&amp;gt;[];
  baseRoute: string;
}
const tags = [
  ...new Set(
    collection
      .filter((entry) =&amp;gt; !!entry.data.tags)
      .map((entry) =&amp;gt; entry.data.tags)
      .flat()
      .filter((tag) =&amp;gt; tag !== &amp;quot;note&amp;quot;),
  ),
];
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Alternative idea&lt;/h2&gt;
&lt;p&gt;I could have defined that union in the content config by creating a generic
type, but that approach feels presumptuous and the wrong level of abstraction
for my purposes. I&amp;#39;ll have to write about why later.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With this final implementation, the compiler is back to inferring properly
without the need to explicitly type every callback. There is no need to create
that &lt;em&gt;lying&lt;/em&gt; Collection type that is doomed to drift away from reality. Through
explicitly typing a little up front, we can enjoy type safety implicitly
downstream.&lt;/p&gt;
</content:encoded><category>code</category></item><item><title>What the heck is Stow!?</title><link>https://ryanparsley.com/blog/stow</link><guid isPermaLink="true">https://ryanparsley.com/blog/stow</guid><description>Stow probably solves a problem that you have.</description><pubDate>Thu, 09 May 2024 02:31:00 GMT</pubDate><content:encoded>&lt;p&gt;Sometimes 1000 words doesn&amp;#39;t paint a very good picture.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GNU Stow is a symlink farm manager which takes distinct sets of software
and/or data located in separate directories on the filesystem, and makes them
all appear to be installed in a single directory tree.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I did not read this captivating description and fall in love with stow.
I stumbled into it by way of trying to solve a problem. A few months back, I
overhauled my &lt;a href=&quot;https://github.com/RyanParsley/dotfiles&quot;&gt;dotfiles&lt;/a&gt; and
accidentally learned about this new to me tool. I watched a video on
&lt;a href=&quot;https://frontendmasters.com/courses/developer-productivity/&quot;&gt;developer productivity&lt;/a&gt;
by &lt;a href=&quot;https://www.youtube.com/@ThePrimeagen&quot;&gt;ThePrimeagen&lt;/a&gt; and he showed me the
way of stow. He makes a lot of great points in that video, but the most
profound impact on my workflow was learning about stow.&lt;/p&gt;
&lt;h2&gt;This steak deserves more sizzle&lt;/h2&gt;
&lt;p&gt;Here is the thing about that description, the importance of symlinks have to
resonate with you for it to &lt;em&gt;feel&lt;/em&gt; exciting. For me, symlinks were not a big
part of my workflow. So a symlink manager didn&amp;#39;t seem to solve a problem that I
had. In retrospect, the &amp;quot;turns out&amp;quot; is that symlinks weren&amp;#39;t part of my
workflow because they&amp;#39;re kind of a pain to manage. Have you ever &lt;em&gt;needed&lt;/em&gt; a file
to live one place, but thought it would be more ergonomic if it lived someplace
else? You wanted a symlink, you just didn&amp;#39;t realize it.&lt;/p&gt;
&lt;h2&gt;What does this have to do with dotfiles?&lt;/h2&gt;
&lt;p&gt;I don&amp;#39;t want my home directory in source control, not all of it. But some things
that live in, or near, my home directory would be great to have in source control.
Stow allows me to designate a folder to be the home to my source controlled
files. I happen to have that directory in my repo, but it could be anywhere. I
can then, effectively, hoist them up to where my OS expects them to be via
&lt;code&gt;stow .&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I could &lt;em&gt;theoretically&lt;/em&gt; set my home directory up as a git repository and
maintain git ignore rules to get a similar effect. But that sounds like a pain
to maintain. It is way more more ergonomic and less error prone for me to keep
all of the dotfiles in a folder and set that folder up as a repo. This is the
problem that symlinks solve and is a great opportunity for stow to shine.&lt;/p&gt;
&lt;h2&gt;So, how does this work?&lt;/h2&gt;
&lt;p&gt;A simplified slice of my home directory could look like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;~/
  ├── .zshrc # symlink to .dotfiles/.zshrc
  └── .dotfiles/
      ├── .git/
      └── .zshrc # actual file
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From inside my &lt;code&gt;~/.dotfiles/&lt;/code&gt; directory, I can run &lt;code&gt;stow .&lt;/code&gt; to create and
maintain symlinks to the files within this directory. Any modifications done to
these files are immediately reflected in the appropriate place. I &lt;em&gt;just&lt;/em&gt; have to
remember to create new files in the sub directory and stow them instead of
creating them in the default directory.&lt;/p&gt;
&lt;h2&gt;What other problems might it solve?&lt;/h2&gt;
&lt;p&gt;I&amp;#39;m thinking about reworking my notes directory. It&amp;#39;s a folder full of markdown
that I tend to view and edit via Obsidian. To wire this up to a static generator,
I&amp;#39;d need to introduce all sorts of dependencies (node_modules, book.toml...
stuff that is not markdown) that would muck up the editing experience of my notes.
With stow, it seems reasonable to tend to a static site repo in one place while
I care for and feed my notes in another directory. But that, is another post for
another day.&lt;/p&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/stow/manual/stow.html&quot;&gt;Stow&amp;#39;s manual&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>code</category></item><item><title>Daily notes with zk</title><link>https://ryanparsley.com/blog/2024-03-17-zk-daily</link><guid isPermaLink="true">https://ryanparsley.com/blog/2024-03-17-zk-daily</guid><description>I explore the configuration of zk to approximate the daily note workflow I currently have set up with Obsidian.</description><pubDate>Mon, 18 Mar 2024 01:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While Obsidian is great, lately I&amp;#39;ve been considering how to get similar
affordances with alternative tools. &lt;a href=&quot;https://zk-org.github.io/zk/&quot;&gt;zk&lt;/a&gt; looks
like a nice tool to consider for a more cli driven workflow. In this post, I
explore the configuration of zk to approximate the daily note workflow I
currently have set up with Obsidian.&lt;/p&gt;
&lt;h2&gt;What does the template look like?&lt;/h2&gt;
&lt;p&gt;For personal stuff, I use a daily note to track habits and capture fleeting
notes during and about a given day. I don&amp;#39;t want to give the file a name or come
up with a clever title each day. I also want to have some frontmatter attributes
ready for consistent tracking of daily habits like meditation and playing
clarinet.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;---
title: { { format-date now &amp;#39;%Y-%m-%d&amp;#39; } }
created: { { format-date now &amp;#39;%Y-%m-%d&amp;#39; } }
clarinet: false
meditate: false
tags:
  - daily
---

# {{format-date now &amp;#39;%Y-%m-%d&amp;#39;}}

## Navigation

[Yesterday](/journal/daily/{{format-date (date &amp;quot;yesterday&amp;quot;) &amp;#39;%Y/%Y-%m-%d&amp;#39;}}) &amp;lt;-&amp;gt; [Tomorrow](/journal/daily/{{format-date (date &amp;quot;tomorrow&amp;quot;) &amp;#39;%Y/%Y-%m-%d&amp;#39;}})

## Tasks

## Notes
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Now, for some configuration&lt;/h2&gt;
&lt;p&gt;I like to store my daily notes in a folder structure like
&lt;code&gt;journal/daily/2024/03-March/2024-03-17.md&lt;/code&gt;. It wasn&amp;#39;t clear to me, as I read
the documentation, how to make use of a dynamic folder structure. Fortunately,
if you format the &lt;code&gt;filename&lt;/code&gt; with slashes zk does exactly what I want.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;# ~/.config/zk/config.toml
[group.daily.note]
filename = &amp;quot;{{format-date now &amp;#39;journal/daily/%Y/%m-%B/%Y-%m-%d&amp;#39;}}&amp;quot;
template = &amp;quot;daily.md&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Wire up an alias to make this easy&lt;/h3&gt;
&lt;p&gt;Now, you can create a daily not with the following command &lt;code&gt;zk new --group daily&lt;/code&gt;. That&amp;#39;s cool, but kind of a lot to type. Back to &lt;code&gt;config.toml&lt;/code&gt; and create
an alias like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;# ~/.config/zk/config.toml
[alias]
daily = &amp;quot;zk new --group daily&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Results... for now&lt;/h2&gt;
&lt;p&gt;Using &lt;a href=&quot;https://github.com/iamcco/markdown-preview.nvim&quot;&gt;Markdown Preview&lt;/a&gt; in Neovim shows a respectable start to a daily note.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/zk-daily/zk-markdownPreview.png&quot; alt=&quot;daily note markdown preview&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, from any directory, I can type &lt;code&gt;zk daily&lt;/code&gt; and a well-formed,
properly-named,file is generated as I like, where I like.&lt;/p&gt;
&lt;p&gt;My next step is to set up Neovim to respect this configuration when I navigate
to a daily note that doesn&amp;#39;t yet exist. However, this feels like enough for one
post.&lt;/p&gt;
</content:encoded><category>code</category></item><item><title>The story of my whittlin&apos; robot</title><link>https://ryanparsley.com/blog/2018-04-10-cnc</link><guid isPermaLink="true">https://ryanparsley.com/blog/2018-04-10-cnc</guid><description>In an effort to better understand how CNC Routers work, I decided to build one from scratch.</description><pubDate>Tue, 10 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In an effort to better understand how CNC Routers work, I decided to build one
from scratch. Having little confidence that I could pull it off, I came up with
a plan to break this ambitious project up into a series of smaller wins.&lt;/p&gt;
&lt;p&gt;The first step was to build a CNC mostly out of reclaimed electronics. I learned
that the motors in many cd rom drives are stepper motors, so I took apart some
old computers I had access to. For this build, I only needed to purchase 3
stepper motor drivers and an Arduino. For less than $50 I proved that I am able
to build such a device.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./cnc_dvd.jpg&quot; alt=&quot;Minimum Viable CNC from found parts&quot;&gt;&lt;/p&gt;
&lt;p&gt;With this success, I had the confidence to buy some NEMA 17 motors (at about $15
a piece) and scale the build up. This build worked mostly fine, but the motors
would occasionally stick. I got the feeling my naive design was to blame. About
that time, I had purchased a 3D Printer and decided to put it to work for me.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./cnc_version2.jpg&quot; alt=&quot;Less minimal, more viable&quot;&gt;&lt;/p&gt;
&lt;p&gt;A little research revealed the
&lt;a href=&quot;https://www.thingiverse.com/thing:989593&quot;&gt;MaduixaCNC&lt;/a&gt;. It&amp;#39;s an open source
design based on a handful of 3d printed parts coupled with fairly accessible
hardware. While not perfect, this design strikes me as a fairly nice jumping off
point if you wanted to make your own CNC.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./cnc_helloworld.jpg&quot; alt=&quot;It&amp;#39;s alive!&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the end, I built a CNC over the course of a year for a little over $200. If
you simply want to have a CNC, this may not be the most practical way to get
there. It was, however a very rewarding journey. One that I&amp;#39;d recommend to
anyone who wants to learn about how CNCs work.&lt;/p&gt;
&lt;iframe
  src=&quot;https://www.youtube.com/embed/TWuP_DbFSsA&quot;
  frameborder=&quot;0&quot;
  allow=&quot;autoplay; encrypted-media&quot;
  allowfullscreen=&quot;&quot;
  id=&quot;fitvid2&quot;&gt;&lt;/iframe&gt;</content:encoded><category>diy</category></item><item><title>CloudCannon</title><link>https://ryanparsley.com/blog/2018-01-22-cloudcannon</link><guid isPermaLink="true">https://ryanparsley.com/blog/2018-01-22-cloudcannon</guid><description>CloudCannon offers a delightful patina to an otherwise unchanged Jekyll site.</description><pubDate>Mon, 22 Jan 2018 20:47:00 GMT</pubDate><content:encoded>&lt;p&gt;A client recently requested a site be set up on &lt;a href=&quot;https://cloudcannon.com&quot;&gt;CloudCannon&lt;/a&gt;. I hadn&amp;#39;t heard of it, so I gritted my teeth and began to read the documentation. The tension quickly washed away as I realized &amp;quot;this is my kind of product&amp;quot;.&lt;/p&gt;
&lt;p&gt;In a nutshell, CloudCannon offers a delightful patina to an otherwise unchanged Jekyll site. You don&amp;#39;t have to code anything &amp;quot;CloudCannon Aware&amp;quot; or any such funny business. You simply set it to watch a branch on a repo and builds fire off and deploy automatically. Continuous Integration that&amp;#39;s triggered automatically by content changes really blurs the line between dynamic and static sites.&lt;/p&gt;
&lt;p&gt;Furthermore, they provide a user friendly markdown GUI. Often, I feel like a static site generator is more appropriate than a dynamic CMS, but don&amp;#39;t have the budget to sort out a way to make editing content ergonomic for non-developers. Those would be great times for something like CloudCannon. This post is not affiliated with or sponsored by &lt;a href=&quot;https://cloudcannon.com&quot;&gt;CloudCannon&lt;/a&gt;, I just think it&amp;#39;s pretty cool.&lt;/p&gt;
</content:encoded><category>code</category></item><item><title>Alexa Watson integration</title><link>https://ryanparsley.com/blog/2017-06-16-alexa-watson-integration</link><guid isPermaLink="true">https://ryanparsley.com/blog/2017-06-16-alexa-watson-integration</guid><description>Here are notes from building a proof of concept around integrating Watson Retrieve and Rank service into an Alexa skill.</description><pubDate>Fri, 16 Jun 2017 20:47:00 GMT</pubDate><content:encoded>&lt;p&gt;Here are notes from building a proof of concept around integrating Watson Retrieve and Rank service into an Alexa skill. I&amp;#39;m still learning, so this may not be the best way to do it. This is simply an approach that worked for me. I&amp;#39;m hoping this overview will illustrate how simple it can be to leverage these two complicated sounding platforms together. If I can sort it out, surely someone as clever as yourself can too.&lt;/p&gt;
&lt;h2&gt;Retrieve and Rank&lt;/h2&gt;
&lt;p&gt;You can sign up for a free bluemix account. I&amp;#39;m not sure the terms or pricing as I used credentials provided by work. You can definitely kick the tires for free, but I&amp;#39;m not sure how far you can get with that. The underlying technology is really impressive, but the web interface and general user experience need some love. IBM has great &lt;a href=&quot;https://www.ibm.com/watson/developercloud/doc/retrieve-rank/ranker_tooling.html&quot;&gt;documentation&lt;/a&gt; that walks you through the training process, but I&amp;#39;ll provide a high level overview. Check out the official &lt;a href=&quot;https://www.ibm.com/watson/developercloud/doc/retrieve-rank/ranker_tooling.html&quot;&gt;documentation&lt;/a&gt; when you&amp;#39;re setting up the service for yourself.&lt;/p&gt;
&lt;h3&gt;Create a Cluster&lt;/h3&gt;
&lt;p&gt;This dubiously named step fires up a solr instance to which you&amp;#39;ll upload documents. You can upload a variety of formats to the &lt;code&gt;Documents&lt;/code&gt; tab that will be used as source material to answer questions. By default, html documents are broken up into something called &amp;quot;Answer Units&amp;quot; based on the placement of Header tags. So, if you have non-semantic html, your milage will vary. Also, the more content you have between header tags, the more verbose your answer will be. You probably want your answer to be fairly terse since they will ultimately be consumed via a robotic voice.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;Questions&lt;/code&gt; tab you can upload a plain text list of training questions. After some questions have been uploaded, this tab will also be used to pair questions with relavant &amp;quot;Answer Units&amp;quot; in the uploaded documents. As I understand it, before training, you get solr responses. Then, you rate the answers on a 4 star rating which adds some meta data to enhance the search results.&lt;/p&gt;
&lt;h2&gt;Simple node.js CLI&lt;/h2&gt;
&lt;p&gt;Now that my data was trained, I wanted to test it in the simplest way possible. So I built a tiny node.js app to hit the service from my command line. IBM released a &lt;a href=&quot;https://www.npmjs.com/package/watson-developer-cloud&quot;&gt;node module&lt;/a&gt; that enabled me to do this in about 20 lines of code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var RetrieveAndRankV1 = require(&amp;#39;watson-developer-cloud/retrieve-and-rank/v1&amp;#39;);

var retrieve = new RetrieveAndRankV1({
  username: &amp;#39;&amp;#39;,
  password: &amp;#39;&amp;#39;,
});

var solrClient = retrieve.createSolrClient({
  cluster\\\_id: &amp;#39;&amp;#39;,
  collection\\\_name: &amp;#39;&amp;#39;
});

// search all documents
var query = solrClient.createQuery();
query.q(process.argv[2]);

solrClient.search(query, function(err, searchResponse) {
  if(err) {
    console.log(&amp;#39;Error searching for documents: &amp;#39; + err);
  } else {
    console.log(&amp;quot;The best answer I have to: &amp;quot;, process.argv[2]);
    console.log(JSON.stringify(searchResponse.response.docs[0].body, null, 2));
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that I could fire off requests via &lt;code&gt;node index.js &amp;quot;How many cups of sugar does it take to get to the moon?&amp;quot;&lt;/code&gt; and Watson&amp;#39;s answers would print to the command line.&lt;/p&gt;
&lt;h2&gt;Alexa Skill&lt;/h2&gt;
&lt;p&gt;Armed with the ego boost of a successful HelloWorld, I converted that CLI into a module like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var RetrieveAndRankV1 = require(&amp;#39;watson-developer-cloud/retrieve-and-rank/v1&amp;#39;);

var retrieve = new RetrieveAndRankV1({
  username: &amp;#39;&amp;#39;,
  password: &amp;#39;&amp;#39;,
});

var solrClient = retrieve.createSolrClient({
  cluster\\\_id: &amp;#39;&amp;#39;,
  collection\\\_name: &amp;#39;&amp;#39;
});

function watsonHelper() {
}

watsonHelper.prototype.query = function(question) {
  // search all documents
  var query = solrClient.createQuery();
  query.q(question);

  solrClient.search(query, function(err, searchResponse) {
    if(err) {
      console.log(&amp;#39;Error searching for documents: &amp;#39; + err);
      return &amp;#39;&amp;#39;;
    } else {
      console.log(JSON.stringify(searchResponse.response.docs[0].body, null, 2));
    }
  }).then(function(){
    return JSON.stringify(searchResponse.response.docs[0].body, null, 2);
  });
};
module.exports = watsonHelper;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I then required that helper in an Alexa app heavily based on based on a template app &lt;a href=&quot;https://github.com/alexa/&quot;&gt;provided by the Alexa team&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;#39;use strict&amp;#39;;
var \\\_ = require(&amp;#39;lodash&amp;#39;);
var Alexa = require(&amp;#39;alexa-app&amp;#39;);
var app = new Alexa.app(&amp;#39;NameOfYourSkill&amp;#39;);
var WatsonHelper = require(&amp;#39;./watson\\\_helper&amp;#39;);

app.launch(function(req, res) {
  var prompt = &amp;#39;Ask me questions, I&amp;#39;ll answer them from the Cloud.&amp;#39;;
  res.say(prompt).reprompt(prompt).shouldEndSession(false);
});

app.intent(&amp;#39;askQuestion&amp;#39;, {
  &amp;#39;slots&amp;#39;: {
    &amp;#39;QUESTION&amp;#39;: &amp;#39;QUESTION\\\_LIST&amp;#39;
  },
  &amp;#39;utterances&amp;#39;: [&amp;#39;{QUESTION}&amp;#39;]
},
function(req, res) {
  //get the slot
  var question = req.slot(&amp;#39;QUESTION&amp;#39;);
  var reprompt = &amp;#39;Sorry, not an instant win! Please try again.&amp;#39;;

  if (\\\_.isEmpty(question)) {
    var prompt = &amp;#39;I didn\\\\&amp;#39;t hear a question that I know. Please ask differently.&amp;#39;;
    res.say(prompt).reprompt(reprompt).shouldEndSession(false);
    return true;
  } else {
    var watsonHelper = new WatsonHelper();
    var answer = watsonHelper.query(question);
    res.say(answer).send();
  }
});

//hack to support custom utterances in utterance expansion string
console.log(app.utterances().replace(/\\\\{\\\\-\\\\|/g, &amp;#39;{&amp;#39;));
module.exports = app;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I uploaded that to &lt;a href=&quot;http://docs.aws.amazon.com/lambda/latest/dg/welcome.html&quot;&gt;lambda&lt;/a&gt; and went through the alexa configuration wizards and all was well.&lt;/p&gt;
&lt;h3&gt;Custom Slots and Utterances&lt;/h3&gt;
&lt;p&gt;Since Watson would be doing all the heavy lifting, I only have one utterance. The utterance is about as simple as they come, I pass the whole user string as &lt;code&gt;QUESTION&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;askQuestion {QUESTION}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since I had a list of plain text answers hanging out that were used to train watson, I pasted this list into a Custom Slot of Type &lt;code&gt;QUESTION\\\_LIST&lt;/code&gt;. A clever coworker commented that this is an optional step, but considering the list is already compiled, I used it. Custom slots can help Alexa sort out speech to text ambiguity like homonyms and poor enunciation.&lt;/p&gt;
&lt;h2&gt;That wasn&amp;#39;t so bad&lt;/h2&gt;
&lt;p&gt;That&amp;#39;s really all there is to it. There&amp;#39;s not a lot of custom code needed for an app like this because most of the really impressive bits are handled via Amazon and IBM. All you have to do is wire it up with a little javascript that should look mostly familiar.&lt;/p&gt;
&lt;h2&gt;Caveat&lt;/h2&gt;
&lt;p&gt;This proof of concept started out with the hope that we could dump existing documentation into the Watson&amp;#39;s service and we&amp;#39;d get instant robot tech support. This did not happen for 2 main reasons, both related to the source material.&lt;/p&gt;
&lt;h3&gt;Semantic markup&lt;/h3&gt;
&lt;p&gt;The documentation html that I received didn&amp;#39;t make use of header tags as you&amp;#39;d want. It was a funky custom accordion with divs and custom classes. Since these custom classes didn&amp;#39;t reveal any hierarchy, I opted to update the markup with appropriate header tags.&lt;/p&gt;
&lt;h3&gt;Verbose answers drag on in robot voice&lt;/h3&gt;
&lt;p&gt;Bear in mind that since &amp;quot;Answer units&amp;quot; are delineated by header tags, an answer is as long as all the text between header tags. In my sample data, some answers were acceptable, and other felt to drag on for an eternity. These answers were correct, but this is not a great user experience.&lt;/p&gt;
</content:encoded><category>node</category><category>cli</category><category>watson</category><category>nlp</category><category>alexa</category></item><item><title>CLI Driven Drupal</title><link>https://ryanparsley.com/blog/2017-06-13-cli-driven-drupal</link><guid isPermaLink="true">https://ryanparsley.com/blog/2017-06-13-cli-driven-drupal</guid><description>It&apos;s easy to get Drupal up and running by clicking around drupal.org, downloading and extracting packages, but that&apos;s not how I do it.</description><pubDate>Tue, 13 Jun 2017 20:47:00 GMT</pubDate><content:encoded>&lt;p&gt;It&amp;#39;s easy to get Drupal up and running by clicking around
&lt;a href=&quot;http://drupal.org&quot;&gt;http://drupal.org&lt;/a&gt;, downloading and extracting packages, but that&amp;#39;s not how I do
it. Like all respectable developer tools, you can get Drupal up and
running nearly entirely from the command line. This used to be done
entirely with a tool called Drush, but now more of Drush&amp;#39;s
responsibilities are being offloaded to the more general php tool
composer. Here&amp;#39;s a run through of how to get a Drupal instance running
via the terminal.&lt;/p&gt;
&lt;h2&gt;Set up your database&lt;/h2&gt;
&lt;p&gt;Before attempting to run the install script, you&amp;#39;ll want to create a mySQL database and user with access to it.&lt;/p&gt;
&lt;h2&gt;Set up a vhost&lt;/h2&gt;
&lt;p&gt;I&amp;#39;m on a Mac and tend to use the Apache that ships with the OS. As such,
I edit my vhosts like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo vim /etc/apache2/extra/httpd-vhosts.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
  ServerAdmin [email goes here]
  ServerName [local url goes here (eg drupal.dev)]
  DocumentRoot [path to drupal instance]
  ErrorLog &amp;quot;/private/var/log/apache2/drupal.dev-error_log&amp;quot;
  CustomLog &amp;quot;/private/var/log/apache2/drupal.dev-access_log&amp;quot; common
  &amp;lt;Directory [path to drupal instance]&amp;gt;
    AllowOverride All
  &amp;lt;/Directory&amp;gt;
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure to change everything in square brackets to something more
appropriate for your environment, and if you use MAMP or homebrew
installed Apache, you&amp;#39;ll need to edit a conf file in a different
location.&lt;/p&gt;
&lt;h2&gt;Update your hosts file&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sudo vim /etc/hosts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add a new entry like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;127.0.0.1 drupal.dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Download Drupal core via composer&lt;/h2&gt;
&lt;p&gt;If you don&amp;#39;t have a strong opinion about Drupal distributions, I&amp;#39;d
recommend using lightning as a starting point. This is a sensible
batteries included build that includes additional modules that have been
vetted by Acquia. You can install this via composer with the following
shell command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;composer create-project acquia/lightning-project MY_PROJECT
cd MY_PROJECT/docroot
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Drush to install the site&lt;/h2&gt;
&lt;p&gt;At this point you can visit the &lt;a href=&quot;http://drupal.dev&quot;&gt;http://drupal.dev&lt;/a&gt; (or whatever local domain name you configured above) and step through the installation wizard. Or, better yet, use the following shell script to install via drush in the command line. Note that I pass flags with db, user and password below. You&amp;#39;ll need to customize the site-install line.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;drush site-install standard --db-url=&amp;#39;mysql://drupalTest:drupalTest@localhost/drupal-test&amp;#39; --site-name=Example --account-name=ryan --account-pass=drupalTest
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Tweak config (hopefully optional, but I had to)&lt;/h2&gt;
&lt;p&gt;Something about my mySQL set up forced me to update the host value in &lt;code&gt;sites/default/settings.php&lt;/code&gt;. I had to change &lt;code&gt;localhost&lt;/code&gt; to &lt;code&gt;127.0.0.1&lt;/code&gt;. Then, all was well.&lt;/p&gt;
&lt;h2&gt;Update file permissions&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;chmod -R 777 sites/default/files
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;May want to clear cache for good measure&lt;/h2&gt;
&lt;p&gt;Since we have to manually adjust write permission to the files directory, and that&amp;#39;s where all sorts of theming goodness is built, you probably have broken website until the next cron job runs.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;drush cache-rebuild
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Profit&lt;/h2&gt;
&lt;p&gt;Congratulations, you installed Drupal!&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Here are a few tasks you&amp;#39;ll need to do before setting up Drupal if you haven&amp;#39;t yet.&lt;/p&gt;
&lt;h3&gt;Install Drush&lt;/h3&gt;
&lt;p&gt;Drush is one of the killer features of drupal. Be sure to install that right away if you haven&amp;#39;t already.&lt;/p&gt;
&lt;h3&gt;Raise memory limit for composer&lt;/h3&gt;
&lt;p&gt;The default is 128M which was not enough. Check your limit with &lt;code&gt;php -r &amp;quot;echo ini_get(&amp;#39;memory_limit&amp;#39;).PHP_EOL;&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;If you haven&amp;#39;t adjusted yours in the past, update &lt;code&gt;memory_limit&lt;/code&gt; in your php.ini.&lt;/h4&gt;
&lt;p&gt;My file was found at &lt;code&gt;/usr/local/etc/php/5.6/php.ini&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you don&amp;#39;t know where your php.ini file is, run &lt;code&gt;php --ini&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Set your timezone in php.ini&lt;/h3&gt;
&lt;p&gt;See above for how to locate your file, there is a &lt;code&gt;timezone&lt;/code&gt; field commented out by default.&lt;/p&gt;
</content:encoded><category>drupal</category><category>cli</category><category>composer</category><category>drush</category></item><item><title>Styleguide Thoughts</title><link>https://ryanparsley.com/blog/2017-01-17-styleguide-thoughts</link><guid isPermaLink="true">https://ryanparsley.com/blog/2017-01-17-styleguide-thoughts</guid><description>A properly leveraged styleguide can be a great communication tool between designer and developers.</description><pubDate>Tue, 17 Jan 2017 20:47:00 GMT</pubDate><content:encoded>&lt;p&gt;A properly leveraged styleguide can be a great communication tool between designer and developers. Furthermore, they can keep scope down by promoting reuse and ultimately keep a project&amp;#39;s style more consistent. If not leveraged properly, they can be little more than a vestigial artfact. Here are a few things to think about to ensure you&amp;#39;re not just going through the motions.&lt;/p&gt;
&lt;h2&gt;Strategy&lt;/h2&gt;
&lt;h3&gt;We&amp;#39;re making gears, not snowflakes.&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Focus on the fact that you&amp;#39;re building a system and not a poster.&lt;/li&gt;
&lt;li&gt;Components should be stress tested early, not simply displayed in an idealized context&lt;/li&gt;
&lt;li&gt;Consider the best case scenario or ideal context&lt;/li&gt;
&lt;li&gt;Don&amp;#39;t ignore the worst case scenario (it will happen)&lt;/li&gt;
&lt;li&gt;Example: How does the search result page look if there are no results or only 1? Don&amp;#39;t simply think about when it has the same number of results as columns that you plan to use.&lt;/li&gt;
&lt;li&gt;Displaying high fidelity layouts is &lt;em&gt;necessary&lt;/em&gt; to a point. Be realisitic about that point.&lt;/li&gt;
&lt;li&gt;Layouts really only exist in a styleguide to test your components.&lt;/li&gt;
&lt;li&gt;Beyond that point, usefullness drops off drastically and producing them becomes wasteful busy work.&lt;/li&gt;
&lt;li&gt;The point of the excercise is to establish patterns. Deviate when needed, not at whim&lt;/li&gt;
&lt;li&gt;Have a solid reason to deviate from established patterns&lt;/li&gt;
&lt;li&gt;Communicate that reason&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Tactics&lt;/h2&gt;
&lt;h3&gt;Develop early, deploy often&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Focus on making it easier to reuse elements than to deviate from established patterns.&lt;/li&gt;
&lt;li&gt;Photoshop (styles and libraries?)&lt;/li&gt;
&lt;li&gt;Illustrator/ Sketch (symbols and styles)&lt;/li&gt;
&lt;li&gt;Code (partials or whatever your framework of choice calls such modular components)&lt;/li&gt;
&lt;li&gt;Create channels to communicate deviations from established guidelines.&lt;/li&gt;
&lt;li&gt;Is this deviation an oversite?&lt;/li&gt;
&lt;li&gt;Is it a proposition to update the guidelines globally?&lt;/li&gt;
&lt;li&gt;Is this instance unique for some reason warranting an exception&lt;/li&gt;
&lt;li&gt;Only populate as many layouts as you need to evaluate contexts for components ot exist within.&lt;/li&gt;
&lt;li&gt;Layout a blog post... don&amp;#39;t layout all the blog posts&lt;/li&gt;
&lt;li&gt;The number of unique layout conditions should be pretty small&lt;/li&gt;
&lt;li&gt;Do this in code, not photoshop (Content placement in Photoshop tends to be a waste of time and sets bogus expectations).&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Jekyll Simply</title><link>https://ryanparsley.com/blog/2017-01-10-jekyll-simply</link><guid isPermaLink="true">https://ryanparsley.com/blog/2017-01-10-jekyll-simply</guid><description>Hopefully, this slimmed down example project will help you see the benefits of Jekyll without getting too overwhelmed with the embarassment of features that you likely don&apos;t need on day 1.</description><pubDate>Tue, 10 Jan 2017 20:47:00 GMT</pubDate><content:encoded>&lt;p&gt;I was talking with a coworker about my recent jekyll reboot. He had a handful of questions that lead me to believe that the official documentation may be a bit overwhelming to someone new to Jekyll. There are many ways you can configure Jekyll and many ways to cultivate a rich content strategy on top of it. What isn&amp;#39;t imediately obvious is: what&amp;#39;s the least I need to do to benefit from Jekyll.&lt;/p&gt;
&lt;p&gt;To help illustrate that point, I put together a quick repo (&lt;a href=&quot;https://github.com/RyanParsley/MVJekyll&quot;&gt;source&lt;/a&gt;, &lt;a href=&quot;https://ryanparsley.github.io/MVJekyll&quot;&gt;demo&lt;/a&gt;) minimally configured to get a site built for github pages. This project is complete with default configuration overrides, nested layouts and Sass compliation. And, not much else. Hopefully, this slimmed down example project will help you see the benefits of Jekyll without getting too overwhelmed with the embarassment of features that you likely don&amp;#39;t need on day 1.&lt;/p&gt;
</content:encoded></item><item><title>Don&apos;t call it a comeback</title><link>https://ryanparsley.com/blog/2017-01-04-comeback</link><guid isPermaLink="true">https://ryanparsley.com/blog/2017-01-04-comeback</guid><description>I got tired of upgrading drupal, so I migrated to jekyll.</description><pubDate>Wed, 04 Jan 2017 20:47:00 GMT</pubDate><content:encoded>&lt;p&gt;In an effort to avoid distraction and get right to writing, I chose to move this blog to &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;. It&amp;#39;s a simple enough tool that lets me draft posts in &lt;a href=&quot;https://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt;. I&amp;#39;m drafting this post on my phone as I pace the floor trying to get my youngest to take a nap.&lt;/p&gt;
&lt;p&gt;The major, potential, downside with choosing Jekyll is: I&amp;#39;m not familiar with it.
It is, however, made with familiar stuff. I don&amp;#39;t know if it&amp;#39;s the perfect tool,
but that fits the tone of this blog and may give me more content to write about.&lt;/p&gt;
&lt;h2&gt;Why Jekyll?&lt;/h2&gt;
&lt;p&gt;As I mentioned before, Jekyll sites are built from a collection of plain text
markdown files. This means I can write posts in any text editor of my choosing.
I don&amp;#39;t need a special app to get work done (although, I am enjoying &lt;a href=&quot;https://play.google.com/store/apps/details?id=org.faudroids.mrhyde&quot;&gt;Mr. Hyde&lt;/a&gt; Android app).
I don&amp;#39;t need to set up a CMS or blog platform on a server. All the software
needed to make it work runs in my phone.&lt;/p&gt;
&lt;h2&gt;Why not Drupal?&lt;/h2&gt;
&lt;p&gt;Drupal has been my CMS of choice for years, and I still like it better than any
other CMS that I&amp;#39;ve used. For this largely neglected blog, I found myself
spending more time running security updates than writing blog posts. At one
point, failing to run updates fast enough left me vulnerable for an attack.
Proper care and feeding of a CMS is not a hobby that I care to take on. I&amp;#39;m
drawn to a simpler approach.&lt;/p&gt;
&lt;h2&gt;How is it built?&lt;/h2&gt;
&lt;p&gt;While hosting via Github Pages is the simplest way to host a jekyll site, I opted to host my blog on my Rackspace server. This left me needing to sort out a deploy/ build strategy if I am to publish from my phone. While I can run the build script in &lt;a href=&quot;https://termux.com/&quot;&gt;Termux&lt;/a&gt; to generate html, or remote into my server and use my phone as a terminal, I opted to set up &lt;a href=&quot;https://jenkins.io/&quot;&gt;Jenkins&lt;/a&gt; to build and deploy on my behalf each time the master branch is updated on Github. I&amp;#39;ll likely go into more detail on those tools in future posts.&lt;/p&gt;
</content:encoded></item><item><title>AEA Debrief: The Developer&apos;s Ampersandwich</title><link>https://ryanparsley.com/blog/2014-03-14-developer-ampersandwich</link><guid isPermaLink="true">https://ryanparsley.com/blog/2014-03-14-developer-ampersandwich</guid><pubDate>Thu, 13 Mar 2014 19:08:10 GMT</pubDate><content:encoded>&lt;p&gt;At &lt;a href=&quot;http://aneventapart.com/event/atlanta-2014&quot;&gt;An Event Apart&lt;/a&gt; in Atlanta, &lt;a href=&quot;http://jennlukas.com&quot;&gt;Jenn Lukas&lt;/a&gt;, presented a developer’s perspective on design for the web. Here’s a collection of take aways that resonated with&amp;nbsp;me.&lt;/p&gt;
&lt;p&gt;Lukas makes a point that often gets overlooked. Good design is part of good craftsmanship. Good design isn’t necessarily all that tightly coupled with subjectivity, fashion, taste or any other magical factors that tend to be associated with design. The fact that there are design fundamentals, means you can achieve good design&amp;nbsp;logically.&lt;/p&gt;
&lt;h2&gt;Bootstrap your typographical&amp;nbsp;knowledge&lt;/h2&gt;
&lt;p&gt;Like any deep subject, typography can be rather intimidating to get started with. Just because you didn’t enjoy the Helvetica documentary doesn’t mean you’re doomed to create tacky web sites if unaided by a “proper&amp;nbsp;designer”.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;“Blah blah blah… Verdana for headlines, Georgia for body copy… type type type blah blah blah.”
  — Jason Santa Maria (as paraphrased by Jenn&amp;nbsp;Lucas)&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;You can do a lot worse than simply stick to a limited pallet of trusted typefaces. Not sure what a “trusted” typeface is? Lukas has a hack for that. In her presentation, she presented 5 lists of the top 10 fonts on various font services. These are what the pros are using. These fonts are used for a reason. You don’t &lt;em&gt;need&lt;/em&gt; to understand that reason. You can always learn it later if you care. Don’t be afraid to follow a trend if you don’t know any better. Emulation is often an early phase of&amp;nbsp;learning.&lt;/p&gt;&lt;h3&gt;Google Fonts most popular&amp;nbsp;fonts&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Open&amp;nbsp;Sans &lt;/li&gt;
&lt;li&gt;Oswald&lt;/li&gt;
&lt;li&gt;Droid&amp;nbsp;Sans &lt;/li&gt;
&lt;li&gt;Roboto&lt;/li&gt;
&lt;li&gt;Lato&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;caps&quot;&gt;PT&lt;/span&gt;&amp;nbsp;Sans&lt;/li&gt;
&lt;li&gt;Open Sans&amp;nbsp;Condensed &lt;/li&gt;
&lt;li&gt;Droid&amp;nbsp;Serif&lt;/li&gt;
&lt;li&gt;Ubuntu&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;caps&quot;&gt;PT&lt;/span&gt; Sans&amp;nbsp;Narrow&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;Typekit favorite&amp;nbsp;fonts&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Museo&amp;nbsp;Sans&lt;/li&gt;
&lt;li&gt;Adelle&lt;/li&gt;
&lt;li&gt;Proxima&amp;nbsp;Nova&lt;/li&gt;
&lt;li&gt;Museo&amp;nbsp;Slab&lt;/li&gt;
&lt;li&gt;Futura &lt;span class=&quot;caps&quot;&gt;PT&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Raleway&lt;/li&gt;
&lt;li&gt;Myriad&amp;nbsp;Pro&lt;/li&gt;
&lt;li&gt;Museo&lt;/li&gt;
&lt;li&gt;Freight Sans&amp;nbsp;Pro &lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;caps&quot;&gt;FF&lt;/span&gt; Tisa&amp;nbsp;Pro&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;Fonts.com top&amp;nbsp;fonts&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Trade&amp;nbsp;Gothic&lt;/li&gt;
&lt;li&gt;Neue&amp;nbsp;Helvetica &lt;/li&gt;
&lt;li&gt;Avenir&amp;nbsp;Next&lt;/li&gt;
&lt;li&gt;Univers&lt;/li&gt;
&lt;li&gt;Avenir&lt;/li&gt;
&lt;li&gt;Proxima&amp;nbsp;Nova &lt;/li&gt;
&lt;li&gt;Gill&amp;nbsp;Sans&lt;/li&gt;
&lt;li&gt;Frutiger&lt;/li&gt;
&lt;li&gt;Futura&lt;/li&gt;
&lt;li&gt;Helvetica&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;Typewolf top&amp;nbsp;fonts&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Open&amp;nbsp;Sans&lt;/li&gt;
&lt;li&gt;Apercu&lt;/li&gt;
&lt;li&gt;Brandon&amp;nbsp;Grotesque &lt;/li&gt;
&lt;li&gt;Montserrat&lt;/li&gt;
&lt;li&gt;Futura&lt;/li&gt;
&lt;li&gt;Avenir&lt;/li&gt;
&lt;li&gt;Inconsolata&lt;/li&gt;
&lt;li&gt;Roboto&lt;/li&gt;
&lt;li&gt;Gotham&lt;/li&gt;
&lt;li&gt;Proxima&amp;nbsp;Nova&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;Net Magazine showcase top&amp;nbsp;fonts&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Futura&lt;/li&gt;
&lt;li&gt;Proxima&amp;nbsp;Nova&lt;/li&gt;
&lt;li&gt;Open&amp;nbsp;Sans&lt;/li&gt;
&lt;li&gt;Brandon&amp;nbsp;Grotesque &lt;/li&gt;
&lt;li&gt;Atrament&amp;nbsp;Web&lt;/li&gt;
&lt;li&gt;Roboto&lt;/li&gt;
&lt;li&gt;Museo&lt;/li&gt;
&lt;li&gt;Archer&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;caps&quot;&gt;FF&lt;/span&gt; Tisa&amp;nbsp;Web&lt;/li&gt;
&lt;li&gt;Arvo&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Looking at her list, I found it interesting that there was a bit of overlap between the different services. I made this list of fonts that show up on more than one of the above&amp;nbsp;list:&lt;/p&gt;
&lt;h3&gt;Cross service top 5 (plus one)&amp;nbsp;fonts&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Futura -&amp;nbsp;4&lt;/li&gt;
&lt;li&gt;Proxima Nova -&amp;nbsp;4&lt;/li&gt;
&lt;li&gt;Roboto -&amp;nbsp;3&lt;/li&gt;
&lt;li&gt;Open Sans -&amp;nbsp;3&lt;/li&gt;
&lt;li&gt;Brandon Grotesque&amp;nbsp;-2&lt;/li&gt;
&lt;li&gt;Avenir -&amp;nbsp;2&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Familiarize yourself with web font&amp;nbsp;limitations&lt;/h2&gt;
&lt;p&gt;This is important for designers and developers alike. Test and share font-families via the browser early in the process to make sure selected fonts will work for your audience before design approval. Don’t for get that “Fixing” a problem in one browser can make things worse in another. Leave plenty of room for &lt;span class=&quot;caps&quot;&gt;QA&lt;/span&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“… sometimes they look just ridiculous on windows machines. It’s a bit better with &lt;span class=&quot;caps&quot;&gt;IE10&lt;/span&gt; but geez oh man. It’s enough to make a well designed website look like crap.”
— Jason&amp;nbsp;Head&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;&lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; Preprocessors/Variables Are Your&amp;nbsp;Friend&lt;/h2&gt;
&lt;p&gt;Variables in Sass (or whatever preprocessor you prefer) have a very friendly syntax for setting&amp;nbsp;variables:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$red: #ff0000;
$headline-font-family: &amp;quot;HelveticaNeue-Light&amp;quot;, &amp;quot;Helvetica Neue Light&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, &amp;quot;Lucida Grande&amp;quot;, sans-serif;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use a text file containing these variables to communicate colors and typographical information between designers and developers. Attributes shared via a list of like this are far less likely to get lost than by using the type tool and eyedropper in a Photoshop document. Also, if this list gets cumbersome, it’s a good smell that maybe some simplification is in&amp;nbsp;order.&lt;/p&gt;
&lt;p&gt;There’s a lot of talk about the importance of designers learning to development. There is an equally strong case to be made for the&amp;nbsp;inverse.&lt;/p&gt;
</content:encoded><category>design</category><category>code</category></item><item><title>AEA Debrief: Faster Design Decisions with Style Tiles</title><link>https://ryanparsley.com/blog/2014-03-13-faster-design-style-tiles</link><guid isPermaLink="true">https://ryanparsley.com/blog/2014-03-13-faster-design-style-tiles</guid><pubDate>Wed, 12 Mar 2014 16:50:04 GMT</pubDate><content:encoded>&lt;p&gt;At &lt;a href=&quot;http://aneventapart.com/event/atlanta-2014&quot;&gt;An Event Apart&lt;/a&gt; in Atlanta, &lt;a href=&quot;http://samanthatoy.com&quot;&gt;Samantha Warren&lt;/a&gt; made a case for why style tiles should be added to your back of tricks. Here’s a collection of take aways that resonated with me.&lt;/p&gt;
&lt;p&gt;A major point of Warren&amp;#39;s talk is that style tiles are a communication tool. It&amp;#39;s as simple as that, and as fundamentally crucial as that. We need to evaluate how we discuss web design and how we define success. Adopting style tiles doesn&amp;#39;t mandate any particular changes to your creative process. The big shift is what you present to clients and how conversations can evolve to be more productive and distraction free as a result.&lt;/p&gt;
&lt;h2&gt;Stop presenting multiple fixed width comps to clients&lt;/h2&gt;
&lt;p&gt;It is a good idea to separate style from layout when getting approval from clients (even internal ones). This is increasingly true as layouts get more complex with responsive layouts. The abstraction from layout brings clarity to conversations. Style tiles will help you to generate a definition of success that is separate from taste. There needs to be a better design artifact than what we, as an industry, currently produce and accept.&lt;/p&gt;
&lt;h2&gt;Design systems, not pages.&lt;/h2&gt;
&lt;p&gt;Design is about problem solving, not decorating. Style tiles help us to generate a definition of success that is separate from taste. They also tend to be device width agnostic so they tend to yield better conversation around responsive projects. Avoid the siren&amp;#39;s call for a big reveal. You are not Don Draper and shouldn&amp;#39;t aspire to be. Communicate early and often to get better results.&lt;/p&gt;
&lt;p&gt;&amp;quot;A beautiful design system is about finding the same balance of consistency &amp;amp; variety.&amp;quot; —Yesenia Prez-Cruz&lt;/p&gt;
&lt;h2&gt;Why is that better than moodboards or mockups?&lt;/h2&gt;
&lt;p&gt;There are a few tools that offer similar benefits to style tiles (or, attempt to). These options tend to get a bit conflated. Warren brought up key differences between style tiles, moodboards and mockups.&lt;/p&gt;
&lt;h3&gt;Moodboards&lt;/h3&gt;
&lt;p&gt;Warren explains a moodboard as a tool for setting the &amp;quot;mood&amp;quot;. Best for when you are discovering and establishing a new identity for a client while designing a website. Concepts &amp;amp; emotions. Works great for web startups. Warren&amp;#39;s &lt;em&gt;Goldilocks&lt;/em&gt; issue with moodboards is that they are too vague.&lt;/p&gt;
&lt;h2&gt;Mockups&lt;/h2&gt;
&lt;p&gt;If moodboards are less than ideal because they&amp;#39;re high level and vague, let&amp;#39;s evaluate the alternative. We can skip all that touchy feely throat clearing and jump right to mocking up the project. This works for really low budgets or sites that are being refined rather than redesigned. In other words, mockups work if expectations can somehow be set. Warren&amp;#39;s &lt;em&gt;Goldilocks&lt;/em&gt; issue with mockups is that they are too precise.&lt;/p&gt;
&lt;h2&gt;Tribal knowledge mileage may vary&lt;/h2&gt;
&lt;p&gt;Why do you follow the process that you do? Is it because it yields great results quickly? Is it simply the way you&amp;#39;ve always done it, because someone once told you it is &amp;quot;how it&amp;#39;s done&amp;quot;? Style tiles or not, that&amp;#39;s the path to madness. We need to evaluate our process from time to time. Do things for a reason and be watchful for truthiness that may shroud your process. We work in a young industry, it&amp;#39;s naive to think we know &amp;quot;the right way&amp;quot; of doing things.&lt;/p&gt;
</content:encoded><category>design</category><category>code</category></item><item><title>AEA Debrief: Designing Using Data</title><link>https://ryanparsley.com/blog/2014-03-11-aea-debrief-designing-using-data</link><guid isPermaLink="true">https://ryanparsley.com/blog/2014-03-11-aea-debrief-designing-using-data</guid><description> </description><pubDate>Tue, 11 Mar 2014 20:47:00 GMT</pubDate><content:encoded>&lt;p&gt;At An Event Apart in Atlanta, Sarah Parmenter encouraged designers to use data to inform the decision making process. Here&amp;#39;s a collection of take aways that resonated with me.&lt;/p&gt;
&lt;p&gt;Parmenter opened up by referencing a &lt;a href=&quot;http://jeffcroft.com/blog/2014/jan/03/web-standards-killed-the-html-star&quot;&gt;polarizing post&lt;/a&gt; recently made by Jeff Croft. In his post, Croft states that web designers need to diversify their offering. In fact, he concludes with: “Diversify or die” . It&amp;#39;s a bold statement that ruffled feathers, but Croft doesn&amp;#39;t give much guidance on how or in what direction designers should diversify.&lt;/p&gt;
&lt;h2&gt;“Instincts are experiments. Data is Proof.”&lt;/h2&gt;
&lt;p&gt;Parmenter picked up where Croft leaves off by proposing that data is a valuable asset for designers. One that should no longer be dismissed as someone else&amp;#39;s responsibility. When the decisions you make are based on real world observations of users and there habits, you&amp;#39;re more likely to see success.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;“…designers go from decorators to problem solvers.” —&amp;nbsp;Aarron&amp;nbsp;Walter&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;Armed with metrics from a successful redesign, you can qualify the value of your efforts with hard numbers. People want to believe that design is valuable, collecting data enables you to prove it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;“Nobody has an excuse to make an uninformed decision anymore.” —&amp;nbsp;Federico&amp;nbsp;Holgado&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;She argued that collecting data has never been simpler. Then, backed that up with a laundry list of services that focus on collecting and presenting information about your site&amp;#39;s traffic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kissmetrics.com/&quot;&gt;KISSmetrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gosquared.com/&quot;&gt;GoSquared&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://get.gaug.es/&quot;&gt;gauges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mixpanel.com/&quot;&gt;mixpanel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chartbeat.com/&quot;&gt;Chartbeat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.crazyegg.com/&quot;&gt;crazyegg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.optimizely.com/&quot;&gt;Optimizely&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Separate the wheat from the chaff&lt;/h2&gt;
&lt;p&gt;There&amp;#39;s a lot of data out there to be observed. Not all of it is helpful. Parmenter lumped all data that can not, directly, be used to make a decision into the category of vanity metrics.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;“Making your numbers go up (any numbers—your bmi, your blood sugar, your customer service ratings) is pointless if the numbers aren&amp;#39;t related to why you went to work this morning.” —&amp;nbsp;Seth&amp;nbsp;Godin&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;In addition to that rule of thumb, she presented a list of vanity metrics that tend to be particularly seductive to naive data enthusiasts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;hits&lt;/li&gt;
&lt;li&gt;total signups&lt;/li&gt;
&lt;li&gt;page views&lt;/li&gt;
&lt;li&gt;number of visits&lt;/li&gt;
&lt;li&gt;unique visitors&lt;/li&gt;
&lt;li&gt;likes&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;h2&gt;“Many designers who are skilled technicians, craftsmen, or researchers have struggled to survive in the messy environment required to solve today’s complex problems. They may play a valuable role, but they are destined to live in the downstream world of design execution.” —&amp;nbsp;Tim&amp;nbsp;Brown&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;Design is not simply making things pretty. Start grabbing data to validate your efforts.&lt;/p&gt;
</content:encoded><category>conference</category><category>design</category><category>data</category></item><item><title>The best Android todo app - Simpletask</title><link>https://ryanparsley.com/blog/2013-07-24-the-best-android-todo-app-simpletask</link><guid isPermaLink="true">https://ryanparsley.com/blog/2013-07-24-the-best-android-todo-app-simpletask</guid><pubDate>Wed, 24 Jul 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&amp;#39;ve been using Todo.txt for a little while now. I love it for it&amp;#39;s minimalism. &lt;a href=&quot;https://play.google.com/store/apps/details?id=nl.mpcjanssen.todotxtholo&quot;&gt;Simpletask&lt;/a&gt; is a more polished approach to Todo.txt. It has some nice additions like grouping tasks by project and more configurable widget.
&lt;a href=&quot;https://play.google.com/store/apps/details?id=nl.mpcjanssen.todotxtholo&quot;&gt;Simpletask on Play&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Runners-up&lt;/h2&gt;
&lt;h3&gt;Todo.txt&lt;/h3&gt;
&lt;p&gt;There is nothing wrong with this app, it just doesn&amp;#39;t have some of the niceties of Simpletask. You should gladly give Gina the $2 for coming up with Todo.txt.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.todotxt.todotxttouch&quot;&gt;Todo.txt on Play&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Trello&lt;/h3&gt;
&lt;p&gt;While not what I&amp;#39;d pick for independent task management, Trello is great for collaboration. I use both Simpletask and Trello daily.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.trello&quot;&gt;Trello on Play&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Simplicity?</title><link>https://ryanparsley.com/blog/2013-07-23-simplicity</link><guid isPermaLink="true">https://ryanparsley.com/blog/2013-07-23-simplicity</guid><pubDate>Tue, 23 Jul 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When talking about the pursuit of simplicity, there tends to be at least one &lt;em&gt;naysayer&lt;/em&gt;. &amp;quot;That&amp;#39;s fine for you, but my problem is, at it&amp;#39;s essence, complicated.&amp;quot; The conversation then shifts to unearth how this person is &lt;em&gt;failing&lt;/em&gt; to simplify his product. I think the &amp;quot;...but my problem...&amp;quot; part is a smell that the goal is a touch &lt;em&gt;oversimplified&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Shooting for simple is an admirable goal. It&amp;#39;s not unlike recommending people stick to Helvetica, when in doubt. It&amp;#39;s a fine default strategy if you don&amp;#39;t have a compelling reason to do something else. It&amp;#39;s not, a one size fits all solution.&lt;/p&gt;
&lt;h2&gt;Case in point: puzzles&lt;/h2&gt;
&lt;p&gt;I like solving puzzles. What is a puzzle? Whether jigsaw or crossword, all puzzles share at least one thing in common. Someone worked rather hard to make a relatively mundane task far &lt;em&gt;more&lt;/em&gt; difficult to achieve. That&amp;#39;s what makes them fun.&lt;/p&gt;
&lt;h2&gt;Clarity&lt;/h2&gt;
&lt;p&gt;I think what people are really talking about, really trying to achieve, is clarity. Here&amp;#39;s the rub: simple things are easier to explain clearly. If you can make something simpler, clarity tends to follow. This can make the distinction tricky. It&amp;#39;s important to realize the difference when you&amp;#39;re asked to tackle a problem that is inherently complex. It&amp;#39;s not a design failure if you can&amp;#39;t make a complex thing simple. It is, however if you can&amp;#39;t explain it clearly.&lt;/p&gt;
&lt;h2&gt;Design is hard&lt;/h2&gt;
&lt;p&gt;Design is more than regurgitating Jakob Nielsen soundbites. Appropriate solutions are found when context has been taken into consideration. Sometimes, it&amp;#39;s complicated... and that&amp;#39;s what makes it fun.&lt;/p&gt;
</content:encoded></item><item><title>The best Android text editor - Draft</title><link>https://ryanparsley.com/blog/2013-07-18-the-best-android-text-editor-draft</link><guid isPermaLink="true">https://ryanparsley.com/blog/2013-07-18-the-best-android-text-editor-draft</guid><pubDate>Thu, 18 Jul 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Contrary to popular belief, Android users like nice things too. There are a lot of text editors on Android and I spent a bit of time and money trying them all out so you don&amp;#39;t have to. I believe &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.mvilla.draft&quot;&gt;Draft&lt;/a&gt; is the best. It has a beautiful interface with support for Markdown and DropBox. What more could you ask for?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.mvilla.draft&quot;&gt;Draft on Play&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Runners-up&lt;/h2&gt;
&lt;h3&gt;Lightpaper&lt;/h3&gt;
&lt;p&gt;Lightpaper is great for all the reasons listed above, but the interface is a little more complicated than I need. I suspect I&amp;#39;d feel different if I were using it on a tablet.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.clockworkengine.android.LightPaper&quot;&gt;Lightpaper on Play&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Fetchnotes&lt;/h3&gt;
&lt;p&gt;Interface is gorgous and simple, but there&amp;#39;s no way to get your notes out of the app. My text wants to be free. Also, the letterboxed splash screen screams that this app was ported from iOS.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.fetchnotes.notes&quot;&gt;Fetchnotes on Play&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Setting up Middleman and Anvil with RVM</title><link>https://ryanparsley.com/blog/2013-07-17-setting-up-middleman-and-anvil-with-rvm</link><guid isPermaLink="true">https://ryanparsley.com/blog/2013-07-17-setting-up-middleman-and-anvil-with-rvm</guid><pubDate>Wed, 17 Jul 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&amp;#8217;m a big fan of designing with code, design in the browser or&amp;#8230; whatever you want to call it. &lt;a href=&quot;http://middlemanapp.com/&quot;&gt;Middleman&lt;/a&gt; and &lt;a href=&quot;http://anvilformac.com/&quot;&gt;Anvil&lt;/a&gt; work out as a nice pairing to create an efficient workflow. The rub is they don&amp;#8217;t play well together without a touch of configuration up front. &lt;/p&gt;
&lt;h2&gt;In the documentation&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;middleman init my_new_project --rack&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;The missing bits to make Anvil (pow) work&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;rvm --create --ruby-version use 1.9.3&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rvm env . -- --env &gt; .powenv&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Bob&amp;#8217;s your uncle&lt;/h2&gt;
&lt;p&gt;In Anvil add your project by clicking the &amp;#8220;Plus&amp;#8221; icon and navigating to your new project&amp;#8217;s directory&lt;/p&gt;
&lt;p&gt;This configuration is out there, but not all in one place. I&amp;#8217;m wrote this so I can stop Googleing for the same thing everytime I start a new project.&lt;/p&gt;</content:encoded></item><item><title>Getting a Rails app set up for Heroku</title><link>https://ryanparsley.com/blog/2013-06-07-getting-a-rails-app-set-up-for-heroku</link><guid isPermaLink="true">https://ryanparsley.com/blog/2013-06-07-getting-a-rails-app-set-up-for-heroku</guid><pubDate>Fri, 07 Jun 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I find myself daisy chaining a few blog posts together to get up and running with Rails. Here are the steps that I took most recently to deploy to Heroku.&lt;/p&gt;
&lt;h2&gt;Initial setup&lt;/h2&gt;
&lt;p&gt;Create your app.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rails new [appName] -d postgresql
git init
git add .
git commit -m &amp;quot;initial commit&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make help Pow and RVM play nice together.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rvm env -- `rvm current` &amp;gt; .powenvmk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create your databases locally.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;psql
CREATE DATABASE [appName_environment]
\q
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the Sass version of Bootstrap and follow the instructions at &lt;a href=&quot;https://github.com/thomas-mcdonald/bootstrap-sass&quot;&gt;https://github.com/thomas-mcdonald/bootstrap-sass&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gem &amp;#39;bootstrap-sass&amp;#39;, &amp;#39;~&amp;gt; 2.3.1.3&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Create your first resource&lt;/h2&gt;
&lt;p&gt;Generate the scaffold and set up your database.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rails g scaffold [resourceName] title:string body:text
rake db:create
rake db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Set up your default route (/config/routes.rb).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root to: &amp;#39;newsletters#index&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remove &amp;quot;/public/index.html&amp;quot;.&lt;/p&gt;
&lt;h2&gt;Make Heroku happy.&lt;/h2&gt;
&lt;p&gt;Change this in &amp;quot;/config/application.rb&amp;quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config.assets.initialize_on_precompile=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change this in &amp;quot;/config/environments/production.rb&amp;quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config.assets.compile = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create the heroku project.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;heroku apps:create [app-name]
git add .
git commit -m &amp;quot;Ready for heroku&amp;quot;
git push heroku master
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Piece of cake!&lt;/h2&gt;
&lt;p&gt;That should yield a good jumping off place for most apps. Don&amp;#39;t forget to duplicate the Heroku create steps for a staging environment. You&amp;#39;ll thank me later.&lt;/p&gt;
</content:encoded></item><item><title>The story of my first Rails app</title><link>https://ryanparsley.com/blog/2013-06-02-the-story-of-my-first-rails-app</link><guid isPermaLink="true">https://ryanparsley.com/blog/2013-06-02-the-story-of-my-first-rails-app</guid><pubDate>Sun, 02 Jun 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been using Ruby for a little while now. Mostly, making little tools to automate bits of my workflow. Each time client work came up, I’d “chicken out” and build it with Drupal. Here’s the story about how I accidentally made my first Rails app.&lt;/p&gt;
&lt;h2&gt;The devils I knew&lt;/h2&gt;
&lt;p&gt;Drupal may not be the most efficient way to solve some problems, but I’m rather familiar with it. Also, I’ve hosted many php sites over the last decade. It is pretty much a “it just works” situation. When I’m tackling a web project solo, I tend to stick with Drupal, for just that reason.&lt;/p&gt;
&lt;p&gt;Recently, I was tasked with enabling someone to make html emails. The rub was, she didn’t know html and had no desire to change that. Ultimately, the approach we decided on was a static site generator living locally. The app would compile a folder full of markdown files into a single html document with the proper styles injected inline.&lt;/p&gt;
&lt;p&gt;It made sense for it to not be a web app. She already wrote here content locally in a plain text friendly manor. She’d then throw that over the wall for someone to convert it to html. Now, she could simply run a command that would eliminate the need of a human to do that conversion. Then, she could paste the code that the app yielded into the web app that delivers the email. I spent a couple of days configuring Middleman and thought I had a pretty nice solution.&lt;/p&gt;
&lt;h2&gt;It worked on my box&lt;/h2&gt;
&lt;p&gt;I sit down to her iMac and type “gem install bundler”. All was well. It wasn’t until I typed “bundle install” that the problems started. First off, can you believe she didn’t have Xcode installed!? After discovering a handful more of missing resources, I could tell she was not comfortable with all the command line fidgeting. I needed to host this tool as a web app.&lt;/p&gt;
&lt;h2&gt;Is Rails appropriate?&lt;/h2&gt;
&lt;p&gt;At first, I thought Rails was overkill. However, the more I thought about it, the more this app needed. She was not keen on learning how to FTP, let alone use Git to transfer files to a server, so I needed to hook it up to a database instead of text files. If it’s out there on Heroku, I’ll need some sort of log in system. After a few rounds of “If you give an app a cookie”, I decided that Rails was probably the path of least resistance for me. I could add all the missing bits to Sinatra, but what’s the point in that?&lt;/p&gt;
&lt;h2&gt;Square 1 is not square 0&lt;/h2&gt;
&lt;p&gt;I started building a fresh app, but I was still able to use much from my static site generator code. It was written in Ruby, after all, so much of the logic ported over directly. The templates were in Haml, so that was no problem either. Ultimately, it was a rather smooth transition and I had the site up and running in less than a week.&lt;/p&gt;
&lt;p&gt;So, I was seduced into making my first Rails app for a client. It was not completely without issue, but what project is? Now that I’ve traversed the divide from zero to one, I look forward to creating more Rails apps.&lt;/p&gt;
</content:encoded></item><item><title>Solving a problem with code</title><link>https://ryanparsley.com/blog/2013-01-02-solving-a-problem-with-code</link><guid isPermaLink="true">https://ryanparsley.com/blog/2013-01-02-solving-a-problem-with-code</guid><pubDate>Wed, 02 Jan 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was looking for a good excuse to learn CoffeeScript so I came up with a project that takes a note and returns the blues scale in that key. It’s not a completely contrived example, I currently work this out manually using the &lt;a href=&quot;http://craftyblues.com/blog/2012/10/07/numeralCheatSheet&quot;&gt;matrix that I posted on my music blog.&lt;/a&gt; I thought it would be interesting to list out the process by which I solve this moderately complex problem.&lt;/p&gt;
&lt;h2&gt;Start with thinking&lt;/h2&gt;
&lt;p&gt;Since I already do this in a manual way, I thought a bit about the steps I currently take. First, I look at my grid of notes and find the note I want to use as my root. Then, I count over to find the relevant notes that make up the rest of the scale. I figured that this was a fairly logical way to find scales, so I’d try to model my script after this. I can always change direction if this turns out not to be straight forward.&lt;/p&gt;
&lt;h2&gt;Knock out the easy parts&lt;/h2&gt;
&lt;p&gt;Since the way you count notes is weird with sharps and flats mixed throughout, I simply copied the information from my chart into arrays like this one&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[&amp;quot;Ab&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;Bb&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;Db&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;Eb&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;Gb”,”G”]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Looking at that, I decided I’d like to display the notes in 4 different ways. Notes with flats, notes with sharps, and Roman numeral notation of both. Now I have this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;numeralFlat=[&amp;quot;I&amp;quot;,&amp;quot;IIb&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;IIIb&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;Vb&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VIb&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VIIb&amp;quot;,&amp;quot;VII&amp;quot;] numeralSharp=[&amp;quot;I&amp;quot;,&amp;quot;I#&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;II#&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;IV#&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;V#&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VI#&amp;quot;,&amp;quot;VII&amp;quot;] flat=[&amp;quot;Ab&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;Bb&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;Db&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;Eb&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;Gb&amp;quot;,&amp;quot;G&amp;quot;] sharp=[&amp;quot;G#&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;A#&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;C#&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;D#&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;F#&amp;quot;,&amp;quot;G&amp;quot;]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I won’t spend a lot of time on music theory, but it may help to explain that the relationship between notes in the minor blues progression is: I,IIIb, IV, Vb, V,VIIb.
That means that the blues in the key of “G” makes use of the notes G, Bb, C, Db, D and F. I decide at this point it makes sense to come up with the following variables:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;I = root IIIb = root + 3 IV = root + 5 Vb = root + 6 V = root + 7 VIIb = root + 10&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If the numbers that I add to rootIndex don’t make sense at first, take a look at the numeralFlat listed above.&lt;/p&gt;
&lt;h2&gt;Make a basic function&lt;/h2&gt;
&lt;p&gt;Although I’m only looking for the minor blues progression, I can easily imagine wanting to find more progressions later. Because of that, I’ll call the function “bluesScaleMinor”. It will need to take at least one argument so that the user can pass in the root note. At this point my little script looks like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;numeralFlat=[&amp;quot;I&amp;quot;,&amp;quot;IIb&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;IIIb&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;Vb&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VIb&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VIIb&amp;quot;,&amp;quot;VII&amp;quot;] numeralSharp=[&amp;quot;I&amp;quot;,&amp;quot;I#&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;II#&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;IV#&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;V#&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VI#&amp;quot;,&amp;quot;VII&amp;quot;] flat=[&amp;quot;Ab&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;Bb&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;Db&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;Eb&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;Gb&amp;quot;,&amp;quot;G&amp;quot;] sharp=[&amp;quot;G#&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;A#&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;C#&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;D#&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;F#&amp;quot;,&amp;quot;G&amp;quot;] bluesScaleMinor = (root) -&amp;gt;   I = root   IIIb = root + 3   IV = root + 5   Vb = root + 6   V = root + 7   VIIb = root + 10   notes = [I,IIIb, IV, Vb, V,VIIb] console.log (bluesScaleMinor 1) &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It doesn’t handle letters or Roman numerals yet, but we’re making good progress. This will take one number then give you an array of that number plus the other 5 numbers increasing by the amounts that we want.&lt;/p&gt;
&lt;h2&gt;Make it a little bit better&lt;/h2&gt;
&lt;p&gt;The most obvious problem with our code at this point is that it only accepts numbers. Since we have a valid list of notes in our flat array, this is pretty easy to correct with this line:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rootIndex = flat.indexOf(root)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then get the notes in the scale with:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;for note, i in notes     notes[i] = flat[note]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This sheds light on a bit of trouble with our logic so far. If we were to use the letter “G”, we’d get [11, 14, 16, 17,18, 21]. This doesn’t work because we only have 12 notes in our array. So we need to come up with a way to force our numbers to be between 0 and 11. This is can be done simply with:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;for note, i in notes     if note &amp;gt; 12       notes[i] = note - 12&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Mostly working code&lt;/h2&gt;
&lt;p&gt;Let’s take a look at the code we have so far.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;numeralFlat=[&amp;quot;I&amp;quot;,&amp;quot;IIb&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;IIIb&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;Vb&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VIb&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VIIb&amp;quot;,&amp;quot;VII&amp;quot;] numeralSharp=[&amp;quot;I&amp;quot;,&amp;quot;I#&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;II#&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;IV#&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;V#&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VI#&amp;quot;,&amp;quot;VII&amp;quot;] flat=[&amp;quot;Ab&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;Bb&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;Db&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;Eb&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;Gb&amp;quot;,&amp;quot;G&amp;quot;] sharp=[&amp;quot;G#&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;A#&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;C#&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;D#&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;F#&amp;quot;,&amp;quot;G&amp;quot;] bluesScaleMinor = (root) -&amp;gt;   rootIndex = flat.indexOf(root)   I = rootIndex   IIIb = rootIndex + 3   IV = rootIndex + 5   Vb = rootIndex + 6   V = rootIndex + 7   VIIb = rootIndex + 10   notes = [I,IIIb, IV, Vb, V,VIIb]   for note, i in notes     if note &amp;gt; 12       notes[i] = note - 12   for note, i in notes     notes[i] = flat[note] console.log (bluesScaleMinor “G”)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If we run this in the terminal, it returns exactly what we want. Success! However it could stand to be better. For instance, what if someone wants sharp notation or to simply view the Roman numeral notation? The only part of our code that forces us into flat notation is this part:&lt;/p&gt;
&lt;p&gt;&lt;code&gt; for note, i in notes     notes[i] = flat[note]&lt;/code&gt;
Let’s cut that bit out and create four new functions like these:
&lt;code&gt; toFlat = (scale) -&amp;gt;   for note, i in scale     scale[i] = flat[note] toSharp = (scale) -&amp;gt;   for note, i in scale     scale[i] = sharp[note] toNumeralFlat = (scale) -&amp;gt;   for note, i in scale     scale[i] = numeralFlat[note] toNumeralSharp = (scale) -&amp;gt;   for note, i in scale     scale[i] = numeralSharp[note]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now all we need is a mechanism for using the right one at the right time. A switch block makes sense to me so I make this function:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;setDisplayType = (scale, display) -&amp;gt;   switch display     when &amp;quot;flat&amp;quot; then toFlat scale     when &amp;quot;sharp&amp;quot; then toSharp scale     when &amp;quot;numeralFlat&amp;quot; then toNumeralFlat scale     when &amp;quot;numeralSharp&amp;quot; then toNumeralSharp scale&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We’ll also need to add a second argument to our bluesScaleMinor function so we can pick a “display type”. A sensible default for this argument is “flat”, that’s what I want pretty much all of the time. This leaves us with the following:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;numeralFlat=[&amp;quot;I&amp;quot;,&amp;quot;IIb&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;IIIb&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;Vb&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VIb&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VIIb&amp;quot;,&amp;quot;VII&amp;quot;] numeralSharp=[&amp;quot;I&amp;quot;,&amp;quot;I#&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;II#&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;IV#&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;V#&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VI#&amp;quot;,&amp;quot;VII&amp;quot;] flat=[&amp;quot;Ab&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;Bb&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;Db&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;Eb&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;Gb&amp;quot;,&amp;quot;G&amp;quot;] sharp=[&amp;quot;G#&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;A#&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;C#&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;D#&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;F#&amp;quot;,&amp;quot;G&amp;quot;] bluesScaleMinor = (root, display = &amp;quot;flat&amp;quot;) -&amp;gt;   rootIndex = flat.indexOf(root)   I = rootIndex   IIIb = rootIndex + 3   IV = rootIndex + 5   Vb = rootIndex + 6   V = rootIndex + 7   VIIb = rootIndex + 10   notes = [I,IIIb, IV, Vb, V,VIIb]   for note, i in notes     if note &amp;gt; 12       notes[i] = note - 12   notes = setDisplayType notes, display toFlat = (scale) -&amp;gt;   for note, i in scale     scale[i] = flat[note] toSharp = (scale) -&amp;gt;   for note, i in scale     scale[i] = sharp[note] toNumeralFlat = (scale) -&amp;gt;   for note, i in scale     scale[i] = numeralFlat[note] toNumeralSharp = (scale) -&amp;gt;   for note, i in scale     scale[i] = numeralSharp[note] setDisplayType = (scale, display) -&amp;gt;   switch display     when &amp;quot;flat&amp;quot; then toFlat scale     when &amp;quot;sharp&amp;quot; then toSharp scale     when &amp;quot;numeralFlat&amp;quot; then toNumeralFlat scale     when &amp;quot;numeralSharp&amp;quot; then toNumeralSharp scale console.log (bluesScaleMinor “G”)&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;One more problem with our logic&lt;/h2&gt;
&lt;p&gt;If we run this with either of the Roman numeral display types we don’t actually want them to loop around. Roman numeral notation is always relative so the root is always “I”. I’ll add a quick fix is to set the rootIndex to 0 when someone picks a Roman numeral display type. Here is our final code.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;numeralFlat=[&amp;quot;I&amp;quot;,&amp;quot;IIb&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;IIIb&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;Vb&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VIb&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VIIb&amp;quot;,&amp;quot;VII&amp;quot;] numeralSharp=[&amp;quot;I&amp;quot;,&amp;quot;I#&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;II#&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;IV#&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;V#&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VI#&amp;quot;,&amp;quot;VII&amp;quot;] flat=[&amp;quot;Ab&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;Bb&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;Db&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;Eb&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;Gb&amp;quot;,&amp;quot;G&amp;quot;] sharp=[&amp;quot;G#&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;A#&amp;quot;,&amp;quot;B&amp;quot;,&amp;quot;C&amp;quot;,&amp;quot;C#&amp;quot;,&amp;quot;D&amp;quot;,&amp;quot;D#&amp;quot;,&amp;quot;E&amp;quot;,&amp;quot;F&amp;quot;,&amp;quot;F#&amp;quot;,&amp;quot;G&amp;quot;] bluesScaleMinor = (root, display = &amp;quot;flat&amp;quot;) -&amp;gt;   if display is &amp;quot;flat&amp;quot; or &amp;quot;sharp&amp;quot;     rootIndex = flat.indexOf(root)   else     rootIndex = 0   I = rootIndex   IIIb = rootIndex + 3   IV = rootIndex + 5   Vb = rootIndex + 6   V = rootIndex + 7   VIIb = rootIndex + 10   notes = [I,IIIb, IV, Vb, V,VIIb]   for note, i in notes     if note &amp;gt; 12       notes[i] = note - 12   notes = setDisplayType notes, display toFlat = (scale) -&amp;gt;   for note, i in scale     scale[i] = flat[note] toSharp = (scale) -&amp;gt;   for note, i in scale     scale[i] = sharp[note] toNumeralFlat = (scale) -&amp;gt;   for note, i in scale     scale[i] = numeralFlat[note] toNumeralSharp = (scale) -&amp;gt;   for note, i in scale     scale[i] = numeralSharp[note] setDisplayType = (scale, display) -&amp;gt;   switch display     when &amp;quot;flat&amp;quot; then toFlat scale     when &amp;quot;sharp&amp;quot; then toSharp scale     when &amp;quot;numeralFlat&amp;quot; then toNumeralFlat scale     when &amp;quot;numeralSharp&amp;quot; then toNumeralSharp scale console.log (bluesScaleMinor “G”)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That’s enough for one blog post. I’d like to finish this up and create a demo site soon. I’m only learning CoffeeScript, so I’m sure there are more elegant ways of solving this problem.&lt;/p&gt;
</content:encoded></item><item><title>More content, less system</title><link>https://ryanparsley.com/blog/2012-12-12-more-content-less-system</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-12-12-more-content-less-system</guid><pubDate>Wed, 12 Dec 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Since I moved back to Ohio, I’ve been playing guitar more and even built a cigar box guitar with my dad. There is more of a community around cigar box guitars than I would have guessed, but not as many educational resources as I would like. Naturally, I made my own.&lt;/p&gt;
&lt;p&gt;I’ve been using &lt;a href=&quot;http://middlemanapp.com&quot;&gt;middleman&lt;/a&gt;, a static site generator, for prototyping web sites and apps for a little while. I have not, however, used it for to create a production site. Most projects that I work on need to be editable by a team that is not necessarily comfortable with the command line (or HTML for that matter). I decided that, since I was the only person likely to contribute content to the blog, I didn’t need a full fledged CMS.&lt;/p&gt;
&lt;p&gt;I decided to approach this project rather differently than I do most paid gigs. I started writing content long before it was built. I also deployed it long before it was “ready for prime time”. I continue to build this site in the open. The blog can be viewed at &lt;a href=&quot;http://craftyblues.com&quot;&gt;http://craftyblues.com&lt;/a&gt; and forked on &lt;a href=&quot;https://github.com/RyanParsley/craftyBlues&quot;&gt;GitHub&lt;/a&gt;. I’m slowly iterating the design around new needs that develop as I create new content. As such, this site is truly in a (possibly perpetual) beta state.&lt;/p&gt;
&lt;p&gt;If you’re not afraid of the command line, creating a blog (or any other site) on middleman is a joy. Since all you deploy is static html, there’s no fear of SQL injections or any other such nonsense to keep you up at night running CMS updates instead of creating content.&lt;/p&gt;
</content:encoded></item><item><title>Illustrator for web layouts</title><link>https://ryanparsley.com/blog/2012-12-07-illustrator-for-web-layouts</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-12-07-illustrator-for-web-layouts</guid><pubDate>Fri, 07 Dec 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Illustrator has some fantastic layout tools that are often overlooked by web designers. Here is a screencast overviewing some of the reasons Illustrator is in my workflow. I cover Split Into Grid, Text Wrap, Paragraph Styles, Transform, Graphic Styles and Appearance Panel.&lt;/p&gt;
&lt;iframe class=&quot;youTubeFrame&quot;  src=&quot;http://www.youtube.com/embed/GEVT6-XhrVY&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;</content:encoded></item><item><title>Illustrating on the iPad</title><link>https://ryanparsley.com/blog/2012-11-19-illustrating-on-the-ipad</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-11-19-illustrating-on-the-ipad</guid><pubDate>Mon, 19 Nov 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Up until recently, I&amp;#39;ve been reluctant to do much sketching digitally. This month, I found the perfect excuse to explore a purely digital process. I’m currently taking part in an event called &lt;a href=&quot;http://www.30characters.com/author/ryanparsley/&quot;&gt;30 Characters in 30 Days&lt;/a&gt;. There’s not much more to the challenge than the title suggests. Participants are simply tasked with creating 30 unique characters in the month of November.&lt;/p&gt;
&lt;p&gt;I decided to make things a bit more interesting by using many different illustration styles and even new tools that I’m not familiar with during this challenge. It’s been a great excuse to spend more quality time with my iPad. I thought I’d take a moment to share my thoughts on a few of the new tools I’ve added to my arsenal.&lt;/p&gt;
&lt;h2&gt;Painting &amp;amp; Sketching&lt;/h2&gt;
&lt;p&gt;I’ve spent a bit of time sketching in &lt;a href=&quot;http://bit.ly/sketchbookApp&quot;&gt;SketchBook&lt;/a&gt; and it’s great. It’s arguably the most popular painting app out there. I like this app a lot, but have read good things about a competitor. I decided to give &lt;a href=&quot;http://bit.ly/procreateApp&quot;&gt;procreate&lt;/a&gt; a go. I think I like it more. The interface for procreate is more opinionated that Sketchbook, which I mostly (eventually) agree with. Having quick access between brush, smudge, and erase is excellent for getting ideas down quickly. I do wish the space used for undo and redo could be reassigned to color toggling since undo and redo have gestures. For quick value studies, I found the placement of the brush size and opacity sliders perfect. So far, I’ve completed 4 illustrations with procreate and sketched out 2 others to be completed in other apps.&lt;/p&gt;
&lt;h2&gt;Vector&lt;/h2&gt;
&lt;p&gt;I would suspect if someone wasn’t already familiar with Illustrator, they’d not miss it if they had &lt;a href=&quot;http://bit.ly/inkpadApp&quot;&gt;Inkpad&lt;/a&gt;. As an Illustrator user, I found a few uncanny valley issues frustrating, but I mostly enjoyed the process. Some actions (like grouping) felt laborious since they required navigating a menu (can this be mitigated with gestures?). I was definitely less productive on the iPad that I would have been with Illustrator on a computer with a mouse and keyboard. Most importantly, I did like the results. I’ve only completed 1 illustration with Inkpad, but there’s still 11 days to go.&lt;/p&gt;
&lt;h2&gt;Pixel Art&lt;/h2&gt;
&lt;p&gt;For crafting pixel art, I have found the app &lt;a href=&quot;http://bit.ly/spriteSomething&quot;&gt;Sprite Something&lt;/a&gt; delightful to work with. It makes pixel art far more fun. It is a bit frustrating to not have layers for positioning elements, but that just leads to a bit more thoughtfulness in the initial mark creation. I’ve already made 6 pixel character illustrations and have cut myself off from making more for the competition. I do however plan to spend much more quality time with this app in the future.&lt;/p&gt;
&lt;p&gt;I do still tend to prefer sketching on &lt;a href=&quot;http://bit.ly/deadTree&quot;&gt;paper&lt;/a&gt; rather than &lt;a href=&quot;http://bit.ly/paperApp&quot;&gt;Paper&lt;/a&gt;, but there is a lot to be said for introducing digital tools to your workflow. I’m really warming up to the iPad as a content creation device. Now, I need to get my hands on a pressure sensitive stylus.&lt;/p&gt;
</content:encoded></item><item><title>Baseline grid in Illustrator</title><link>https://ryanparsley.com/blog/2012-05-10-baseline-grid-in-illustrator</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-05-10-baseline-grid-in-illustrator</guid><pubDate>Thu, 10 May 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I &lt;a href=&quot;http://ryanparsley.com/prose/horizontal-grids-illustrator&quot;&gt;recently wrote&lt;/a&gt; about an easy way to set up a grid in Illustrator. While that way is great for setting horizontal rhythm, I follow a different method for creating my baseline grid. This couldn&amp;#39;t be simpler thanks to the Transform Effect.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the Line Segment Tool (\)&lt;/li&gt;
&lt;li&gt;Click anywhere on the canvas&lt;/li&gt;
&lt;li&gt;Enter 1024 for length and make sure the angle is set to 0&lt;/li&gt;
&lt;li&gt;Give the path a sensible color&lt;/li&gt;
&lt;li&gt;Use the Transform Pallet to position the line at the top left corner&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Menu &amp;gt; Effect &amp;gt; Distort &amp;amp; Transform &amp;gt; Transform&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Let&amp;#39;s set a 24px baseline grid by entering 24px in &lt;code&gt;Move &amp;gt; Vertical&lt;/code&gt;. Then, enter 32 in the &amp;quot;Copies&amp;quot; input area.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;#39;s all there is to it. At this point, you can change your baseline via the appearance pallet. I tend to not turn my baseline grid lines into guides because I rarely want to snap to them. If you do want to turn them into guides:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the group of lines&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Menu &amp;gt; Object &amp;gt; Expand Appearance&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Menu &amp;gt; View &amp;gt; Guides &amp;gt; Make Guides&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The only thing missing from this is a more sophisticated baseline text alignment like InDesign has, but this is all you really need for wireframing.&lt;/p&gt;
</content:encoded></item><item><title>Easy on the polyfills</title><link>https://ryanparsley.com/blog/2012-04-28-easy-on-the-polyfills</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-04-28-easy-on-the-polyfills</guid><pubDate>Sat, 28 Apr 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It&amp;rsquo;s not that important to teach older versions of IE how to handle media queries. The point is to make layouts work on small screens. I don&amp;#39;t know of any cell phones running IE6. As an alternative to using polyfills, I tend handle legacy IE with conditional comments. The only trade off for this is, IE &amp;lt; 9 users can&amp;rsquo;t change the layout by fiddling with the window size. I can live with that. Here is how I use Sass to eliminate the redundancies associated with this kind of browser sniffing.&lt;/p&gt;
&lt;p&gt;Wrap all of the styles that would be declared within the laptop sized media query in a mixin. Then, call that mixin from inside a media query. No magic so far, just using a mixin in lieu of copy and paste.&lt;/p&gt;
&lt;h2&gt;_laptop.scss&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;@mixin laptop {
  [all of your laptop size styles go here]
}
@media only screen and (min-width: 992px) {
  @include laptop;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, I call the same mixin from within an partial made just for old versions of IE. Finally, I link to that stylesheet from within an IE conditional comment.&lt;/p&gt;
&lt;h2&gt;_ieLT9.scss&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;@include laptop;
// That is likely to be the entire contents.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;IE conditional comment&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!--[if lt IE 9 ]&amp;gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/path-to/css/ieLT9.css&amp;quot; media=&amp;quot;screen, projection&amp;quot;&amp;gt;
&amp;lt;![endif]--&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;rsquo;s it. Now you don&amp;#39;t have to worry about keeping your layout changes in sync across nearly identical sheets. Old browsers get a sensible layout. Best of all, we didn&amp;rsquo;t have to use any javascript.&lt;/p&gt;
</content:encoded></item><item><title>Horizontal grids in Illustrator</title><link>https://ryanparsley.com/blog/2012-04-23-horizontal-grids-in-illustrator</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-04-23-horizontal-grids-in-illustrator</guid><description>Here are the steps I take to efficiently sketch out grids in Illustrator.</description><pubDate>Mon, 23 Apr 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here are the steps I take to efficiently sketch out grids in Illustrator. The
best part is: step 6 can be revisited at any time and modified, should you
change your mind or want to experiment further. This kind of flexibility is
quickly winning me over to Illustrator for wireframing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a 1024px X 768px document&lt;/li&gt;
&lt;li&gt;Select the Rectangle Tool&amp;nbsp;(M)&lt;/li&gt;
&lt;li&gt;Click once anywhere on the&amp;nbsp;canvas&lt;/li&gt;
&lt;li&gt;In the Rectangle Tool Dialog, enter 960px X 768px&lt;/li&gt;
&lt;li&gt;Use the Align Pallet to center the rectangle with respect to
the&amp;nbsp;Artboard&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Menu &amp;gt; Object &amp;gt; Path &amp;gt; Split Into Grid&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Enter whatever numbers you like. Illustrator will attempt to adjust other
numbers to help you out.&lt;/li&gt;
&lt;li&gt;Now, you should have a group of paths and a bunch of paths outside of that
group. Rename the group &amp;#8220;Guides&amp;#8221;&lt;/li&gt;
&lt;li&gt;Select the paths not in the &amp;#8220;Guides&amp;#8221; group and group them&lt;/li&gt;
&lt;li&gt;Rename the new group &amp;#8220;Boxes&amp;#8221;&lt;/li&gt;
&lt;li&gt;Hide the Boxes Group. You don&amp;#8217;t need these anymore, but if you change
your mind about the grid, you can select all of them and go back to step 6.
How awesome is that!?&lt;/li&gt;
&lt;li&gt;The &amp;#8220;Guides&amp;#8221; group currently contains a bunch of paths with no
strokes. To make them actual guides, select them all and &lt;code&gt;Menu &amp;gt; View &amp;gt; Guides &amp;gt; Make Guides&lt;/code&gt; 13.Rename that layer &amp;#8220;Horizontal Grid&amp;#8221;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;#8217;s all there is to it. It looks like a lot of steps, but you get
really fast at it. I like using the &amp;#8220;Split Into Grid&amp;#8221; dialog to find
my grid when I&amp;#8217;m still working out how wide I want my gutters to be. I
find it to be an invaluable tool.&lt;/p&gt;
</content:encoded></item><item><title>Mobile mockups in Illustrator</title><link>https://ryanparsley.com/blog/2012-04-15-mobile-mockups-in-illustrator</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-04-15-mobile-mockups-in-illustrator</guid><pubDate>Sun, 15 Apr 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The Illustrator pixel is a majestic beast quite hard to pin down. The very nature of vectors makes the idea of a 300px by 300px document kind of silly. All you really know is it&amp;#39;s a square. You can take the same document you create in Illustrator and export it as big or as little as you like and it will look perfect (Some exceptions apply. See offer details. Your experience in scaling vectors may vary). A thing that frustrates many Illustrator newbies is that Illustrator displays documents as though they are 72dpi when viewing them on the screen.&lt;/p&gt;
&lt;p&gt;Since your monitor is almost certainly not 72dpi, this makes Illustrator less than convenient for previewing digital assets on the screen. For this reason I think the most sensible thing to do is declare your documents in inches instead of pixels. Then, go about your business. You only need to sweat the math at three points.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When you want to create a raster assets, make sure you use &lt;code&gt;File &amp;gt; Save for Web &amp;amp; Devices&lt;/code&gt; and input the proper pixel count you want to get back.&lt;/li&gt;
&lt;li&gt;If you want to view the mockup &amp;quot;actual size&amp;quot; on your monitor, zoom to a degree derived from this familiar looking formula: &lt;code&gt;[Monitor Resolution]/[Document Resolution] = Zoom&lt;/code&gt;. For instance, if you have a 96dpi monitor and a 72dpi document (all Illustrator documents), you would want to zoom by 133% to see the document &amp;quot;actual size&amp;quot;.&lt;/li&gt;
&lt;li&gt;When you need to express the size of something in dp, simply multiply the size in pixels by .45 ( 72/160, since Illustrator always assumes 72dpi).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you liked this, you may want to want to read my recent post about Android&amp;#39;s density independent pixels. Hopefully this little bit of math helps to demystify the size discrepancies that frustrate many designers new to Illustrator.&lt;/p&gt;
</content:encoded></item><item><title>10 Drupal modules you should be using</title><link>https://ryanparsley.com/blog/2012-04-10-10-drupal-modules-you-should-be-using</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-04-10-10-drupal-modules-you-should-be-using</guid><pubDate>Tue, 10 Apr 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I make a lot of websites with Drupal. Since I do this a lot, I&amp;#39;ve been spending time working on a way to speed up and/or automate the process as much as possible. This &lt;strike&gt;distraction&lt;/strike&gt; project had the fortunate byproduct of the following list.&lt;/p&gt;
&lt;p&gt;This is a list of modules that tend to make it into every Drupal project I build these days. These modules are mostly about extending Drupal as a CMS, so they&amp;#39;ll enhance just about any kind of site. They are the modules that will make your admin users happy and likely reduce the support calls you get (making you happy).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/markdown&quot;&gt;Markdown Filter&lt;/a&gt; - Markdown is a great compromise between WYSIWTF editors and expecting users to learn HTML. It&amp;#39;s also faster to write even if you do know HTML.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/typogrify&quot;&gt;Typogrify&lt;/a&gt; - It&amp;#39;s unreasonable to ask mortals to type the necessary html to create proper quotes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/cck_blocks&quot;&gt;CCK Blocks&lt;/a&gt; - Hide fields from the body of the content and display them in a block someplace. This is a great tool for making more interesting layouts to showcase custom content types.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/menu_block&quot;&gt;Menu Block&lt;/a&gt; - It does more, but I mostly use this to create separate navigation elements that are bound to one trail of links.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/block_class&quot;&gt;Block Class&lt;/a&gt; - Add arbitrary classes to blocks.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/block_titlelink&quot;&gt;Block Title Link&lt;/a&gt; - Handy for linking a list of teasers to a view&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/webform&quot;&gt;Webform&lt;/a&gt; - I like this better than the core contact form because it saves all entries to the database.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/scheduler&quot;&gt;Scheduler&lt;/a&gt; - Create an unpublished node and cron will publish later.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/menu_trail_by_path&quot;&gt;Menu Trail By Path&lt;/a&gt; - Usually does exactly what I need for keeping the menu path preserved.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/backup_migrate&quot;&gt;Backup and Migrate&lt;/a&gt; - Let the robots back up your content. This module not only makes backups, but also prunes your backups on a schedule you set.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I made a point to not list any of the top 10 most downloaded modules, because I figure those are easy enough to find on your own. &lt;a href=&quot;/contact&quot;&gt;Let me know&lt;/a&gt; if you find this useful.&lt;/p&gt;
</content:encoded></item><item><title>What is the point of dp?</title><link>https://ryanparsley.com/blog/2012-03-25-what-is-the-point-of-dp</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-03-25-what-is-the-point-of-dp</guid><pubDate>Sun, 25 Mar 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&amp;#39;ve been reading up on Android interface design lately. One thing that has jumped out at me as counter intuitive is the concept of &amp;quot;density independent pixels&amp;quot; or dp. At first, I was very excited to see that Google was working to provide a way for designs to &amp;quot;just work&amp;quot; on all devices. Then, I read up on how it works.&lt;/p&gt;
&lt;h2&gt;The math&lt;/h2&gt;
&lt;p&gt;According to &lt;a href=&quot;http://developer.android.com/guide/practices/screens_support.html&quot;&gt;documentation&lt;/a&gt; you can convert px to dp with the following formula:
&lt;code&gt;px = dp *(dpi/160)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Math nerds can probably already tell that this isn&amp;#39;t likely to quite &amp;quot;do what it says on the tin&amp;quot;. At least, not if you intuited the same goal of the dp that I did. I&amp;#39;ll plug in a couple of numbers to illustrate the problem.&lt;/p&gt;
&lt;h3&gt;HTC Rezound&lt;/h3&gt;
&lt;p&gt;The HTC Rezound has a 4.3&amp;quot; screen with 720px X 1280px resolution at 342dpi.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;width in dp = 720/(342/160) = 337&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Galaxy Nexus&lt;/h3&gt;
&lt;p&gt;The Galaxy Nexus has a 4.65&amp;quot; screen with a 720px X 1280px resolution screen at 316dpi&lt;/p&gt;
&lt;p&gt;&lt;code&gt;width in dp = 720/(316/160) = 365&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Wait, that doesn&amp;#39;t seem right. Does it? If we are defining a 720px X 1280px resolution in density independent terms shouldn&amp;#39;t they result in the same size? I got more than a little hung up on this. Then, I tried the following.&lt;/p&gt;
&lt;h3&gt;Droid Razr&lt;/h3&gt;
&lt;p&gt;The Droid Razr has a 4.3&amp;quot; screen with 540px x 960px resolution at 256 dpi&lt;/p&gt;
&lt;p&gt;&lt;code&gt;width in dp = 540/(256/160) = 338&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;There is the clarity I was looking for! The Droid Razr&amp;#39;s dp count is the same as the HTC Rezound because they have &lt;em&gt;the same physical dimensions&lt;/em&gt; regardless of the pixel count (or density). Density independent pixels don&amp;#39;t help you translate dimensions freely between many device sizes, they simply are a unit bound to a physical size yet separated from resolution. They work much the same way a pt is always 1/72 of an inch regardless of resolution of your printer.&lt;/p&gt;
&lt;p&gt;Is that helpful? Maybe, but not nearly as helpful as what I imagined it would be. At least now we understand the purpose of the dp. Soon, I&amp;#39;ll show you how to make this bit of information useful in Illustrator and uncover the first feature of Fireworks that made me think about switching from Illustrator. But that... is another post.&lt;/p&gt;
</content:encoded></item><item><title>Vim tip: Fun with tags</title><link>https://ryanparsley.com/blog/2012-01-08-vim-tip-fun-with-tags</link><guid isPermaLink="true">https://ryanparsley.com/blog/2012-01-08-vim-tip-fun-with-tags</guid><pubDate>Sun, 08 Jan 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;HTML can feel unwieldy and inefficient to edit. There are lots of great tools and alternate syntaxes that make this easier... but who wants to set up a whole framework and all the dependancies that come with that just to be &lt;em&gt;more efficient&lt;/em&gt;. With a basic Vim command and the &lt;a href=&quot;https://github.com/tpope/vim-surround&quot;&gt;surround plugin&lt;/a&gt; you can greatly increase your productivity traversing and manipulating tags.&lt;/p&gt;
&lt;h2&gt;Say, for instance, you have:&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;something&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;something else&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&amp;hellip; and you want to make these list items links.&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Place your curser inside the list item&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;vit&lt;/code&gt;. This will visually (v) select the text inside (i) the surrounding tag (t).&lt;/li&gt;
&lt;li&gt;Now type &lt;code&gt;s &amp;lt;a href=&amp;quot;#&amp;quot; &amp;gt;&lt;/code&gt; and the surround plugin wraps the selected text with anchor tags&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I find that the surround plugin is an excellent companion to the &lt;a href=&quot;https://github.com/rstacruz/sparkup&quot;&gt;sparkup&lt;/a&gt; plugin.&lt;/p&gt;
</content:encoded></item><item><title>CSSOff recap</title><link>https://ryanparsley.com/blog/2011-11-06-cssoff-recap</link><guid isPermaLink="true">https://ryanparsley.com/blog/2011-11-06-cssoff-recap</guid><pubDate>Sun, 06 Nov 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently competed in &lt;a href=&quot;http://www.unmatchedstyle.com/cssoff/&quot;&gt;UnmatchedStyle’s CSSOff competition&lt;/a&gt; where frontend developers each took a crack at marking up the same Photoshop document. Submissions are to be judged based on a list of parameters including: semantics, resource optimization, legacy browser support and general code cleanliness. We were encouraged to use HTML5 and tasked to support browsers as old as IE6. Here are some observations I made during and since the competition. For the TL;DR crowd, I have a version of my entry hosted in &lt;a href=&quot;http://lab.ryanparsley.com/tripledare&quot;&gt;the lab&lt;/a&gt; and I uploaded the source to &lt;a href=&quot;https://github.com/RyanParsley/TripleDare&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Whose design is it anyways?&lt;/h2&gt;
&lt;p&gt;The biggest difference between this cometition and a regular day at the offce for me was, I never markup a design some one else created. Oftentimes, I let pending logistics of markup inform my design decisions. I don’t think that this harms my designs, but it certainly shapes them to an extent. With this project, there were design decisions that seemed to have been made just to throw a monkey wrench at my markup. This was a lot of fun to deal with as I found myself leveraging CSS in ways outside of my confort zone.&lt;/p&gt;
&lt;h3&gt;Physical challenge&lt;/h3&gt;
&lt;p&gt;In an attempt to force myself to keep my code as concise as possible I opted to not use Compass &amp;amp; Sass this time. I really missed it while creating the ieLT9 style sheet. I only used one third party bit of code, Remy Sharp’s HTML5 Shim. This enabled me to easily use new HTML5 elements with IE6 support.&lt;/p&gt;
&lt;h2&gt;Is it bigger than a breadbox?&lt;/h2&gt;
&lt;p&gt;Knowing that I’d be judged on project size had me far more mindful about lines of code than I usually am. I always strive to be as concise as (practically) possibly, but with this project I was really sweating the numbers. It was interesting to find out that I wrote a little over 500 lines of CSS to style around 200 lines of html. Furthermore, it was enlightening to see that after just 440 lines of CSS handled all the style and layout for all contexts for screen sizes, I had to write an additionally 113 lines of css (not to mention how many hours to think up those 113 clever lines) just to make the layout deprecate nicely for IE6. This could really be a nice bit of data for anyone striving to make a case for dropping support for outdated browsers.&lt;/p&gt;
&lt;h3&gt;Squeeze play&lt;/h3&gt;
&lt;p&gt;I also spent a lot more time thinking about image sizes than I usually do. When browser stats warrant support of IE6, I tend to simply serve 2 copies of images. I’ll generate 24bit pngs for browsers that can handle them and gifs for those who don&amp;#39;t. For this contest, that would have double dinged me on project’s total size (not something that usually matters in the real world). This prompted me to work on seeing how good I could make legacy browser safe 8bit pngs. This mindfulness ultimately lead to much smaller file sizes across the board.&lt;/p&gt;
&lt;h2&gt;Showcase&lt;/h2&gt;
&lt;p&gt;I’m not sure when the judging will be complete. Win or lose, I’m curious to see what kind of score my project gets because, all in all, I didn’t handle this project very much differently than I approach most projects. This competition was a great learning exercise and a lot of fun.&lt;/p&gt;
</content:encoded></item><item><title>Rule them all</title><link>https://ryanparsley.com/blog/2011-08-15-rule-them-all</link><guid isPermaLink="true">https://ryanparsley.com/blog/2011-08-15-rule-them-all</guid><description>Make your own graph paper with Illustrator.</description><pubDate>Mon, 15 Aug 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here is a quick tip that comes in handy for me all the time. I like to work out
ideas analog. A frustration I have with notebooks, in general, is that lined and
graph paper tend to be of inferior paper quality than sketchbooks. My solution
is to create my own graph or ruled paper and place them behind my sketchbook
sheets so that I can see the lines through the page in the same way a
calligrapher would. Ruled or graph paper is easier to create with Illustrator
than you may think.&lt;/p&gt;
&lt;h2&gt;Here&amp;rsquo;s how I do it&lt;/h2&gt;
&lt;p&gt;First, create a new document the same size as your notebook. For this example
I&amp;#39;m going to use 8.5in x 11in. Next, select the line segment tool or simple
 press ‘\’. Then, click in the top left corner of your document. Clicking one
time with this tool selected will cause an options prompt to appear. Enter the
width of your document as the length and confirm the angle is 0 (for a
horizontal line).&lt;/p&gt;
&lt;p&gt;Confirm your line is in the right place by looking at the text boxes above your
document. The ‘X’ value will most likely be half the document’s width, ‘Y’
should be 0, ‘W’ should be the document’s width and ‘H’ should be 0.&lt;/p&gt;
&lt;h2&gt;Now for the magic&lt;/h2&gt;
&lt;p&gt;With the line segment selected click Effects &amp;gt; Distort &amp;amp; Transform &amp;gt; Transform from the menu.&lt;/p&gt;
&lt;p&gt;The values you’ll need may vary depending on your needs. Here is what you’d need
for ruled paper with lines a quarter of an inch apart.&lt;/p&gt;
&lt;h2&gt;That’s it&lt;/h2&gt;
&lt;p&gt;There are more creatively fulfilling things you can accomplish with the
transform tool, but that is another post.&lt;/p&gt;
</content:encoded></item><item><title>Stealing the underpants</title><link>https://ryanparsley.com/blog/2011-06-15-stealing-the-underpants</link><guid isPermaLink="true">https://ryanparsley.com/blog/2011-06-15-stealing-the-underpants</guid><description>The hardest part of blogging is writing the posts</description><pubDate>Wed, 15 Jun 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A few weeks ago, I decided it was time to start blogging, again. I sat down to
my computer, loaded up my blog and realized that I &lt;em&gt;needed&lt;/em&gt; to relaunch
my blog first. After all, I had rushed this iteration of the blog together in a
hurried attempt to match the business cards that I had painstakingly designed. I
had never come back to apply the last bit of fit and finish that I told myself I
would. My blog was still powered by Drupal 6! Drupal 7 has been out for nearly a
year now. How could I possibly sit down to write under such conditions?&lt;/p&gt;
&lt;p&gt;Weeks later and I’m still tinkering with the leading and column counts. I&amp;#39;m
still trying to figure out how many layouts I want in my responsive cascade. I&amp;#39;m
doing just about everything &lt;em&gt;but&lt;/em&gt; writing. Today, I put all of
those distractions aside, opened a text editor and started mashing away at the
keyboard. Finally, I started step one.&lt;/p&gt;
</content:encoded></item><item><title>Susy at first blush</title><link>https://ryanparsley.com/blog/2011-04-27-susy-at-first-blush</link><guid isPermaLink="true">https://ryanparsley.com/blog/2011-04-27-susy-at-first-blush</guid><pubDate>Wed, 27 Apr 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Susy looks to be a very nice tool for making cleanly marked-up grid-based sites. It&amp;#39;s one of the many new compass plugins that are floating about these days. It doesn&amp;#39;t claim to be an out of the box layout, and that&amp;#39;s what makes it such a strong candidate for a real life solution.&lt;/p&gt;
&lt;p&gt;From the start, Susy is highly configurable. This means you&amp;#39;re not stuck with a column count that is tightly coupled with your grid system (I&amp;#39;m looking at you blueprint). This leaves designers unburdened with a bunch of mundane math, while still free to use uniquely appropriate grids. The project can be found at &lt;a href=&quot;https://github.com/ericam/compass-susy-plugin&quot;&gt;https://github.com/ericam/compass-susy-plugin&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Initial response to Sass</title><link>https://ryanparsley.com/blog/2011-03-02-initial-response-to-sass</link><guid isPermaLink="true">https://ryanparsley.com/blog/2011-03-02-initial-response-to-sass</guid><pubDate>Wed, 02 Mar 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I really want to like Sass, I have tried to pick it up a couple of times. Granted, each time I have played with it thus far, has been a fairly short interval. My most recent forray with it underscored why I think it hasn&amp;#8217;t quite stuck with me yet. My markup/coding conventions seem to inherently solve most of the issues that Sass tackles.&lt;/p&gt;&lt;h2&gt;Things that seem redundant&lt;/h2&gt;&lt;p&gt;One of the benefits of Sass is, it makes it easy to keep style derived classes out of your markup. An example being, if you use blueprint (or some other modular css library) you may be tempted to add these predefined &amp;#8220;style centric&amp;#8221; classes to your markup. Instead of this, you could use Sass mixins. The assumption here is that people simply attach classes to their elements willie nillie. I may do this in the prototype phase of creating a layout, but ultimately I remove all that stuff. In the end I add the element&amp;#8217;s semantic class to a chain of classes in various style blocks in the in the css file.&lt;/p&gt;&lt;pre&gt;
.clearfix, header, .container, ...{
  [clearfix styles]
}
&lt;/pre&gt;&lt;p&gt;It would be nice to be able the skip the phase where I fold the semantic classes back into the modular style sheet. However, that in and of itself, is not the most compelling of reasons as it comes together rather quickly.&lt;/p&gt;&lt;h2&gt;Things I&amp;#8217;d like from SASS&lt;/h2&gt;&lt;p&gt;Another strength of Sass is the ability to add logic to your CSS. This sounds like a dream come true, until I realize the discrepancies of how I &lt;em&gt;want&lt;/em&gt; to use logic in my CSS and the way that Sass provides. The first thing I hoped to use Sass for was converting px measurements to ems. Ultimately I&apos;d like to think in pixels and let the style sheet be written in ems. Along those lines, the first mixin I wrote was to the effect of:&lt;/p&gt;&lt;pre&gt;
@mixin scaleFont($target, $context){
  font-size:#{$target/$context}em;
}
&lt;/pre&gt;&lt;p&gt;That works, as long as $target and $context are provided in the same unit of measure. The rub is, once you apply this mixin, the resulting element would have it&amp;#8217;s new context expressed in ems. This means, if you wanted to use the result of this as your new context, you need to know the new target in ems. This doesn&amp;#8217;t actually solve the problem where stepping between cascades of contexts makes it hard to keep track of what &amp;#8220;1em&amp;#8221; means at any given point. What I&amp;#8217;d really love is a way to do something to the effect of:&lt;/p&gt;&lt;pre&gt;
@mixin scaleFont($target){
  font-size:#{$target/this.context}em;
}
&lt;/pre&gt;&lt;p&gt;Furthermore, it would be epic if Sass (or some other solution) could tell more about the element being targeted (border-bottom-width, line-height&amp;#8230;). Imagine being able to write a style that was intellegent enough to subtract a bottom border from the line-height automatically to maintain your vertical rhythm for you. Along those lines&amp;#8230; wouldn&amp;#8217;t it be great if firebug displayed the effective size, in pixels, when displaying what &amp;#8220;em&amp;#8221; dimension something was?&lt;/p&gt;&lt;p&gt;Maybe I just haven&amp;#8217;t played around with Sass enough at this point. Do you have any good approaches where Sass comes closer to solving these conversion/ rhythm issues? I&amp;rsquo;d love to hear how Sass helps you.&lt;/p&gt;</content:encoded></item><item><title>Let go and let cloud</title><link>https://ryanparsley.com/blog/2011-02-19-let-go-and-let-cloud</link><guid isPermaLink="true">https://ryanparsley.com/blog/2011-02-19-let-go-and-let-cloud</guid><pubDate>Sat, 19 Feb 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m a renting kind of guy. I rent my apartment, I rent movies and I rent the comments on my podcast. I rent things that I don’t want to be burdoned with storing or maintaining. Sovereignty is old fashioned. There are many great services out there that solve all sorts of problems so you don’t have to. There’s freshbooks for invoicing, Basecamp for project management, Shopify for commerce, Twitter for… uh… tweeting, and Disqus for comments.&lt;/p&gt;
&lt;h2&gt;Why not?&lt;/h2&gt;
&lt;p&gt;A common concern about using a third party solution is “What if they go out of business or turn evil?” Lots of people will see high profile sites like Mashable or Engadget using Disqus and get the warm fuzzy feeling of endorsement. I was convinced well before those sites started using Disqus. All it took for me was for this company to not make an attempt to hold my content hostage. They make it super easy to take your ball and go home if you’re not happy with they way they are doing business. This is very important to me. While I don’t want to “own” my comments today, I can imagine a scenario in the future where I might change my mind. That chance alone could be enough to disuade me from using a service.&lt;/p&gt;
&lt;p&gt;This is pretty good food for thought if you’re making a service that derives value from the content being created within. It may be rather tempting, conventional wisdom even, to wall off user’s content. There are many savy users that may never try your service to begin with if your terms are too onerous.&lt;/p&gt;
&lt;h2&gt;It’s not about you&lt;/h2&gt;
&lt;p&gt;Typically when people talk about deciding to use a service the focus is on “build versus buy”. I’d like to talk about some less mentioned reasons to let someone else do a bit of the heavy lifting for you. I do a podcast, the podcast’s site is powered by Drupal, but the comments are powered by Disqus. I could just as easily check a box and use the default comment solution that ships with Drupal. I use Disqus because I am focused on improving the experience of my listenters. I love that this company is obsessed with making it easy for end users to leave comments. Developing a better comment system isn’t how I want to spend my time, but I’m glad it’s how they choose to spend theirs.&lt;/p&gt;
&lt;h2&gt;Assumptions…&lt;/h2&gt;
&lt;p&gt;I want to make it easy for listeners to leave comments. By the very nature of podcasts, it’s hard enough to get comments. People tend to listen to them on devices while they’re commuting, exercising or otherwise not at a computer ready to type. On the off chance someone does come back to the site (maybe to check out the shownotes) and they feel compelled to leave a comment, what are they greeted by? If this is the first time they’ve wanted to leave a comment, a registration form. They’ve never been here before, they aren’t sure if they’ll ever be back. Is it really fair to expect them to fill out a list of fields… wait for a confirmation email… bounce over to their email client… follow that link back… enter a captcha key (that is only marginally more legible to humans than it is to robots)… all so they can type “nice post!” I’m exhausted just thinking about all those steps.&lt;/p&gt;
&lt;p&gt;Disqus is a distributed comment platform. If a listener has already posted to another site that uses Disqus, they already have an account and are ready to leave a comment on my site. If they don’t have a disqus account, they can sign in with twitter or facebook credentials. If they happen to not have either of those (I believe half of everyone with internet access has a facebook account), they can post a comment simply by using an email address. How could it get easier for users?&lt;/p&gt;
&lt;h2&gt;Nail it to the perch&lt;/h2&gt;
&lt;p&gt;There are two last take aways I’d like to underscore. First, if you’re working on an app, focus on your core competancies, main differentiators, and primary goals. For all of the complexity that falls outside of those things, consider using someone else’s solution instead of building your own. If someone else has built a business on something that you consider a tertiary goal, they are most likely way more passionate about it and equally likely to be doing a better job than you have time or budget to implement. Secondly, don’t be so certain that steps are necessary just because they are often assumed. The easiest way for you to make a process simpler for your users is to eliminate as many steps as you can. This removes work for you and your users.&lt;/p&gt;
</content:encoded></item><item><title>First look at WPF</title><link>https://ryanparsley.com/blog/2011-01-24-first-look-at-wpf</link><guid isPermaLink="true">https://ryanparsley.com/blog/2011-01-24-first-look-at-wpf</guid><pubDate>Mon, 24 Jan 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I just finished the first day of a three day course I’m taking on WPF. I have looked at it briefly from time to time, but this is the first real attempt at picking up the skill. If you’re not familiar with WPF it is a relatively new way of writing user interfaces for .NET apps. I’m hoping that with a stronger understanding WPF, I’ll be better equipped to both design desktop apps.&lt;/p&gt;
&lt;p&gt;As far as I’m concerned the most interesting bit of WPF is XAML. After years of writing front-end code in HTML, my various attempts at picking up Win Forms felt ham-fisted. XAML is a bit of a “through the looking glass” kind of experience for me as it’s logically similar to HTML while syntactically different in every way. It’s refreshingly familiar because, if you squint at it just the right way, it resembles bad HTML. Following along the sample code in this particular course book, I find myself building matryoshka doll interfaces not unlike building websites from nested tables. Also, all of the styling to this point has been declared inline.&lt;/p&gt;
&lt;p&gt;I’m hoping that this is simply “quick and dirty” code examples and not “best-practices” for WPF front-end development. For the time being I’m just going to follow the conventions in the book, but for future projects I’ll definitely be investigating more elegant approaches.&lt;/p&gt;
</content:encoded></item><item><title>Out of my element</title><link>https://ryanparsley.com/blog/2010-09-06-out-of-my-element</link><guid isPermaLink="true">https://ryanparsley.com/blog/2010-09-06-out-of-my-element</guid><pubDate>Mon, 06 Sep 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently resolved a server performance issue. Here’s a recap of what I went through in hopes you find it useful. I’m running &lt;a href=&quot;http://ubuntu.com&quot;&gt;Ubuntu&lt;/a&gt; on the smallest &lt;a href=&quot;http://www.rackspacecloud.com/1345.html&quot;&gt;Rackspace Cloud Server&lt;/a&gt; available (too much foreshadow?). The server has 256MB of ram and it’s sole purpose is to run &lt;a href=&quot;http://drupal.org&quot;&gt;Drupal&lt;/a&gt; on Apache. I have set up a few servers in the past, but I do not claim to be an expert. This recent problem had me stumped for a while.&lt;/p&gt;
&lt;h2&gt;The Symptoms&lt;/h2&gt;
&lt;p&gt;Soon after Apache was started (or restarted), all was well. Over time the server would continue to grow slower. When the server was most bogged down, running “free -m” would show that all of the swap was in use. Running “ps aux --sort -rss” revealed that all of the memory was, in fact, being used by Apache.&lt;/p&gt;
&lt;p&gt;This bit of information didn’t strike me as very valuable since I was hoping to just shut down whatever was using all of the memory. If any other service was using all of the memory I’d shut it down… but Apache is the whole reason the site is up. The fact that the server seemed fine at first, then got progressively worse, made me think memory leak. I was wrong.&lt;/p&gt;
&lt;h2&gt;The Resolution&lt;/h2&gt;
&lt;p&gt;Reluctantly, I opened a chat with support. I assumed the chat would be a waste of time (based on previous experiences with other hosting providers I have dealt with in the past). I assumed that I would get a response to the effect of: “There are too many variables in a server for us to trouble shoot this issue. Might I suggest our more expensive option?” This was not my experience.&lt;/p&gt;
&lt;p&gt;Jordan C. shot me a handful of links to posts where people outlined ways they needed to reconfigure Apache for running on less than 512MB. I did not realize that 256MB was frugal enough to require special configuration. Since I have never set up a server with less than 512MB of ram, I&amp;#39;ve never needed to fuss much with the default configuration of Apache. I was used to Apache &lt;em&gt;just&lt;/em&gt; working.&lt;/p&gt;
&lt;p&gt;The links that proved most helpful for me were on &lt;a href=&quot;http://daniel.shortens.net/weblog/apache-optimization&quot;&gt;shortens.net&lt;/a&gt; and &lt;a href=&quot;http://wiki.vpslink.com/Low_memory_MySQL_/_Apache_configurations&quot;&gt; vpslink.com&lt;/a&gt;. After sniffing around the internet and a little trial &amp;amp; error this is the bit of configuration that ultimately fixed my particular issue.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;IfModule mpm_prefork_module&amp;gt;
    StartServers          5
    MinSpareServers       5
    MaxSpareServers       10
    MaxClients            100
    MaxRequestsPerChild   500
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While “fanatical” sounds a little strong, it’s not his job to help me configure Apache. Jordan didn’t provide a lot of trouble shooting, but the links he provided nudged me in the right direction. Before following these links I was in the frustrating position of not even knowing enough to ask (or google) the right questions. Now, I can get back to making things.&lt;/p&gt;
</content:encoded></item><item><title>Who would have thought? It figures</title><link>https://ryanparsley.com/blog/2010-08-24-who-would-have-thought-it-figures</link><guid isPermaLink="true">https://ryanparsley.com/blog/2010-08-24-who-would-have-thought-it-figures</guid><pubDate>Tue, 24 Aug 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While it’s not shocking &lt;em&gt;that&lt;/em&gt; &lt;a href=&quot;http://techcrunch.com/2010/08/24/motorola-snaps-up-280-north-for-20-million/&quot;&gt;someone snatched up the talent of 280 North&lt;/a&gt;, &lt;em&gt;who&lt;/em&gt; aquired them (more to the point why) brings a smile to my face that can only be conjured up by sweet… sweet irony. Motorola (the hardware company) has aquired a startup “dedicated to advancing the state of the art on the web.”&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://280north.com/&quot;&gt;280 North&lt;/a&gt; is probably most notable for creating &lt;a href=&quot;http://cappuccino.org/&quot;&gt;Cappuccino&lt;/a&gt;, “an open source framework that makes it easy to build desktop-caliber applications that run in a web browser.” The framework and team are quite impressive, but why would a phone manufacturer care? According to a Motorola spokesperson states, “We believe 280 North will be instrumental in helping us continue to foster the Android ecosystem with innovative web-based technologies and applications.”&lt;/p&gt;
&lt;p&gt;280 North is a team that so whole heartedly loves the way Apple products come together, they &lt;a href=&quot;http://arstechnica.com/apple/news/2008/06/cocoa-on-the-web-280-north-objective-j-and-cappuccino.ars&quot;&gt;reimplemented Cocoa and Objective-C in JavaScript&lt;/a&gt;. Motorola has recruited a brilliant, Apple-enthusiast, team for the betterment of Android. I wonder if they’ll be trading in their iPhones for Droids.&lt;/p&gt;
</content:encoded></item><item><title>One size never fits all</title><link>https://ryanparsley.com/blog/2010-08-17-one-size-never-fits-all</link><guid isPermaLink="true">https://ryanparsley.com/blog/2010-08-17-one-size-never-fits-all</guid><pubDate>Tue, 17 Aug 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While it is nice that new smartphones &lt;em&gt;can&lt;/em&gt; render virtually any website reasonably well, we are remiss to not take context into account. Is &amp;ldquo;reasonably well&amp;rdquo; really your end goal? I have been dipping my toe into making this site more optimized for mobile consumption by serving up some size delineated styles. I wanted to share a couple of notes on my approach.&lt;/p&gt;
&lt;p&gt;Early on, I was seduced by the siren&amp;rsquo;s call of tools that offered a &amp;ldquo;native experience&amp;rdquo;. Frameworks like &lt;a href=&quot;http://iwebkit.net&quot;&gt;iWebkit&lt;/a&gt; and &lt;a href=&quot;http://jqtouch.com&quot;&gt;jQTouch&lt;/a&gt; promised to make my website look less like &lt;em&gt;my website&lt;/em&gt; and more like &lt;em&gt;a native iPhone app&lt;/em&gt;. At first, this seemed awesome. Doubly so, considering how easy either tool was to leverage via the the way &lt;a href=&quot;http://drupal.org&quot;&gt;drupal&lt;/a&gt; handles multisite installs. Under closer consideration, however, I found two big issues with this approach. First, I didn&amp;#39;t want my site to look like a generic app&amp;hellip; I wanted it to look like &lt;strong&gt;my site&lt;/strong&gt;. Secondly, looking like a generic iPhone app on an Android phone seems even less optimal. Don&amp;rsquo;t get me wrong, these frameworks are super impressive&amp;hellip; as long as what you&amp;rsquo;re trying to do is create a usable interface with minimal effort. Most websites are, at least to some extent, about marketing. Blending in is not the first priority.&lt;/p&gt;
&lt;p&gt;Enter media queries. Overriding some of the layout and letting most of the style persist seems to be the path of least resistance. We need to regard webkit based mobile browsers more as damage control for users than a crutch for creators. Mobile isn&amp;rsquo;t the future anymore&amp;hellip; it has been the present for quite some time.&lt;/p&gt;
</content:encoded></item><item><title>My favorite 10 Android apps</title><link>https://ryanparsley.com/blog/2010-06-14-my-favorite-10-android-apps</link><guid isPermaLink="true">https://ryanparsley.com/blog/2010-06-14-my-favorite-10-android-apps</guid><description>You&apos;ve got options, I&apos;ve got opinions.</description><pubDate>Mon, 14 Jun 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&amp;rsquo;ve been talking to more and more people about what apps are good for the
Android platform. These are the 10 I use the most. They&amp;rsquo;re in no
particular order other than the first one is probably my favorite. If I
didn&amp;rsquo;t post your favorite app, drop it in the comments.&lt;/p&gt;
&lt;h2&gt;Locale&lt;/h2&gt;
&lt;p&gt;This is a stellar app that lets you automate tasks on your android phone based
on all kinds of contexts. The simplest example is my phone turns itself to
silent when I approach the office. A more robust example would be: when I am
home, it&amp;#39;s after 11pm and my phone is plugged in&amp;hellip; it turns itself to
silent. Beyond adjusting the volume (which would be enough) locale can trigger
things like: post to twitter, change the brightness, change your
wallpaper&amp;hellip;&lt;/p&gt;
&lt;h2&gt;BeyondPod&lt;/h2&gt;
&lt;p&gt;The best podcast aggregator I have tried&amp;hellip; and I have installed many of
them. Syncs with Google Reader so you can manage your feeds from any computer
also that makes your opml portable so you don&amp;#39;t get locked in if you want to try
a different aggregator.&lt;/p&gt;
&lt;h2&gt;Aldiko&lt;/h2&gt;
&lt;p&gt;This is an ebook reader that can import any (non-DRM) ebooks you have or
download from the thousands of public domain books.&lt;/p&gt;
&lt;h2&gt;Twitter&lt;/h2&gt;
&lt;p&gt;The official twitter app is becoming my favorite twitter app. Even if you prefer
another, you should install this one and turn off notifications so that you get
the os addons such as listing recent tweets near your contacts.&lt;/p&gt;
&lt;h2&gt;Voice&lt;/h2&gt;
&lt;p&gt;Visual transcriptions of voicemail is a huge boon. It saves so much time (even
though it is far from perfect).&lt;/p&gt;
&lt;h2&gt;Audible&lt;/h2&gt;
&lt;p&gt;It&amp;#39;s beta, but It&amp;#39;s out there. Audible is pretty much the only way to buy
audiobooks and now they have an android app! My phone is complete.&lt;/p&gt;
&lt;h2&gt;Dropbox&lt;/h2&gt;
&lt;p&gt;Need I say more?&lt;/p&gt;
&lt;h2&gt;AppBrain&lt;/h2&gt;
&lt;p&gt;Install AppBrain on your phone and it pings a webapp so you can more easily
manage your apps from your computer.&lt;/p&gt;
&lt;h2&gt;Astrid&lt;/h2&gt;
&lt;p&gt;The best todo list app I have seen. Simple interface, syncs with Remember The
Milk, free and opensource.&lt;/p&gt;
&lt;h2&gt;Ringdroid&lt;/h2&gt;
&lt;p&gt;This is a sound file editing app for creating ringtones right on your phone.
Android already lets you use any audio file as a ringtone, but this app lets you
cut just the snippet you want for custom ringtones.&lt;/p&gt;
</content:encoded></item><item><title>Drupal: My $0.02</title><link>https://ryanparsley.com/blog/2009-09-16-drupal-my-0-02</link><guid isPermaLink="true">https://ryanparsley.com/blog/2009-09-16-drupal-my-0-02</guid><pubDate>Wed, 16 Sep 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been using Drupal for projects here and there for 5 years. Really, once I found Drupal I haven&amp;#39;t looked back… much. At the time I was using Mambo, and there was no easy way (or, at least I didn’t know how) to override modules to make completely table-less layouts. This coupling of markup and functionality drove me to look elsewhere. I love Wordpress, and would recommend it for anyone wanting to set up a blog. However, if you are need a customizable platform for handling different kinds of content, I found it lacking in a handful of places. Some of these issues have been addressed, some are being addressed. At the time, Drupal had what I needed. Now, I am converted.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;All content management systems suck,&lt;br&gt;Drupal just happens to suck less.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;— &lt;cite&gt;Boris Mann at DrupalCON Amsterdam, August 2005&lt;/cite&gt;
{: .citation}&lt;/p&gt;
&lt;p&gt;When I found Drupal I was unaware of web development frameworks. In hindsight, when I think of what I was looking for in a CMS, I realize I was looking for a framework. Drupal fit the bill pretty well. In general, I find Drupal to be a very extensible solution that allows me to have semantically acceptable markup. The down side: although most all of the presentation can be overridden and customized as much as you like, most of Drupal’s default markup is pretty cludgy. The justification for the divitis is… it is easier for non fronted developers to skin a site by ignoring copious superfluous hooks than to figure out how to add a missing hook. I strongly disagree with this philosophy. In fact, this is probably one of the bigger reasons that drives me to consider alternatives.&lt;/p&gt;
&lt;p&gt;One thing I would like to highlight is the fact that I use Drupal to make customized content management systems. I use Drupal because it’s community has solved a majority of the problems associated with this kind of goal. If about to embark on a totally new and different kind of web app, I would probably not use Drupal as I doubt it would continue to be the path of least resistance. That said, CCK + Views + Taxonomy = FTW! I am not a developer, I don’t even play one on a podcast, but with 2 contrib modules and a couple of overridden .tpl.phps Drupal is ready to handle most any task I can throw at it.&lt;/p&gt;
</content:encoded></item><item><title>Relatively timeless web design books</title><link>https://ryanparsley.com/blog/2009-03-29-relatively-timeless-web-design-books</link><guid isPermaLink="true">https://ryanparsley.com/blog/2009-03-29-relatively-timeless-web-design-books</guid><pubDate>Sun, 29 Mar 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On multiple occasions the topic of “Where to start” with web design has come up. There are many blogs out there covering the topic of web design/ frontend development, here is a good start to creating the ultimate &lt;a href=&quot;http://astore.amazon.com/ryan0d-20&quot;&gt;web design bookshelf&lt;/a&gt;.&lt;/p&gt;
&lt;!--base32-c9t6arbb-base32--&gt;&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/0321385551?tag=ryan0d-20&amp;camp=0&amp;creative=0&amp;linkCode=as4&amp;creativeASIN=0321385551&amp;adid=1X1226D8VXGT9WAHH9WA&amp;&quot;&gt;Designing with Web Standards&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/0596527330?tag=ryan0d-20&amp;camp=0&amp;creative=0&amp;linkCode=as4&amp;creativeASIN=0596527330&amp;adid=0V257Y039HV6E5ADK5Z3&amp;&quot;&gt;CSS: the Definitive Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/1590595335?tag=ryan0d-20&amp;camp=0&amp;creative=0&amp;linkCode=as4&amp;creativeASIN=1590595335&amp;adid=02WK2AF0KPB0BBB9CVCT&amp;&quot;&gt;Dom Scripting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/1590598032?tag=ryan0d-20&amp;camp=0&amp;creative=0&amp;linkCode=as4&amp;creativeASIN=1590598032&amp;adid=0J424WF4RYT0B7YE3BP0&amp;&quot;&gt;Web Standards Creativity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/1590596145?tag=ryan0d-20&amp;camp=0&amp;creative=0&amp;linkCode=as4&amp;creativeASIN=1590596145&amp;adid=1CJK6QE3V4Q8ASB530YE&amp;&quot;&gt;CSS Mastery&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>On Skinning Cats</title><link>https://ryanparsley.com/blog/2009-01-26-on-skinning-cats</link><guid isPermaLink="true">https://ryanparsley.com/blog/2009-01-26-on-skinning-cats</guid><pubDate>Mon, 26 Jan 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Very few problems can be solved in exactly one way. By “skinning” I mean “styling” and by “cats” I mean “websites”. Clearly, every problem can have more than one solution. Few places is this more true than with web design. Sure, there can be a best practice workflow. Or, an information architect can engineer the most efficient layout. But, the creative bits, where the brand starts to enter into it can always be solved many ways. So my advice to designers is… keep that bit to yourself.&lt;/p&gt;
&lt;p&gt;If you are like I was, when you are dealing with a client, the first thing you do is pitch about 3 designs. The one you like, the one you think they want (that you are less in love with) and something else quite different as a hail mary.&lt;/p&gt;
&lt;p&gt;Now the problem of alacart web design begins. Where the client likes the color pallet of one, with the navigation of another and images from a third (and could you make the logo bigger). In the end what you can have, if you&amp;#39;re not careful, is a frankenstien of committee approved “design”. where the whole is less than the sum of all parts.&lt;/p&gt;
&lt;p&gt;Solution. Just pitch the pick of the litter. If you don&amp;#39;t think they will like your favorite design… think about why. Is it because the client&amp;#39;s subjectivity will tend a different way or because the solution is not appropriate for the target audience? Why is it your favorite? Again is it &lt;em&gt;your&lt;/em&gt; subjectivity… or do you realize it is the most appropriate for the target audience. Now that you have evaluated the design as objectively as you can… defend it. In theory you were selected because you are some sort of professional. Your client should appreciate your ability.&lt;/p&gt;
</content:encoded></item><item><title>How to be #1 on google</title><link>https://ryanparsley.com/blog/2009-01-23-how-to-be-1-on-google</link><guid isPermaLink="true">https://ryanparsley.com/blog/2009-01-23-how-to-be-1-on-google</guid><description>Could it be this simple?</description><pubDate>Fri, 23 Jan 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Through long hours of careful study, I believe I have unlocked the powers of
winning at Google. It would appear the trick is much simpler than my intuition
would have lead me to believe. I&amp;#39;ve discovered a pattern throughout the internet
and I will share it with you here.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Write a blog post called &amp;quot;How to be #1 on google&amp;quot;&lt;/li&gt;
&lt;li&gt;Count your money&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>Virtually OK</title><link>https://ryanparsley.com/blog/2009-01-23-virtually-ok</link><guid isPermaLink="true">https://ryanparsley.com/blog/2009-01-23-virtually-ok</guid><pubDate>Fri, 23 Jan 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I just attended a &lt;a href=&quot;http://socialmediaclubsf.org/&quot; title=&quot;South Florida Social Media Club&quot;&gt;Social Media Club&lt;/a&gt; Meeting where David Hinds gave a lecture titled &amp;quot;How to Grow Vibrant Virtual Communities&amp;quot;. Hinds has definitely spent some time thinking about social behavior on the web and some of the things he said really got me thinking. I&amp;#39;m not sure how often he presents, but if you get an opportunity to hear what he has to say on the subject, I would recommend giving him your ear (especially if you are new to the subject). Just be sure to think critically. Here&amp;#39;s my two cents on this particular talk.&lt;/p&gt;
&lt;p&gt;Before the presentation started, I made the assumption that &amp;quot;Virtual Community&amp;quot; would be mainly referring to the fact that the community in question is not one of geographic proximity. I was all ready to defend against the contemptuous use of &amp;quot;virtual&amp;quot; to lessen value of relationships based on physical distance. However, this turned out not to be what Dr. Hinds was talking about at all. What he discussed was at most virtual and barely a community. The definition of Virtual Community in the context of this presentation is this: Population of individuals with shared or complementary interests who interact across a host platform. By this definition, ebay buyers and sellers qualify as being in a virtual community. Multiple times, it came up that Hinds accepts the fact that members of a Virtual Community do not and shall never know much of anything about each other. To me, this lack of intimacy seems like a challenge that needs to be overcome, not a characteristic that should be accepted.&lt;/p&gt;
&lt;p&gt;Hinds listed 7 types of virtual communities with examples:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Socializing (Facebook)&lt;/li&gt;
&lt;li&gt;Gaming (Everquest)&lt;/li&gt;
&lt;li&gt;Content Sharing (youTube)&lt;/li&gt;
&lt;li&gt;Knowledge Sharing (wikipedia)&lt;/li&gt;
&lt;li&gt;Activism (Move On)&lt;/li&gt;
&lt;li&gt;Development (Linux)&lt;/li&gt;
&lt;li&gt;Exchange (ebay)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When inquired from a viewer about a community embracing more than one of the above listed characteristics, Hinds advised against. Stating, a virtual community should only strive to address one of the seven. In my humble opinion, the communities that thrive the strongest are the ones that incorporate most (if not all) of these realms. One instance of this is open source software. Most successful open source projects have a strong evangelical user base that helps each other succeed. At a glance you can see development, activism, knowledge sharing, content sharing, and socializing. There&amp;#39;s 5 without even really needing to think about it too much. Not to mention if you count aggregating street cred/good karma as having a bit of a gaming aspect. Additionally community members could sell their services to complete the virtual community bingo cover all round.&lt;/p&gt;
&lt;p&gt;Hind&amp;#39;s presentation was certainly not a step by step system for growing a vibrant community, but there was plenty of food for thought. To me, it seems, if you want your community to really shine, you should work hard at removing the virtual parts.&lt;/p&gt;
</content:encoded></item><item><title>This productivity thing is a fad</title><link>https://ryanparsley.com/blog/2009-01-19-this-productivity-thing-is-a-fad</link><guid isPermaLink="true">https://ryanparsley.com/blog/2009-01-19-this-productivity-thing-is-a-fad</guid><pubDate>Mon, 19 Jan 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I like the concept of productivity as much as the next guy (assuming the next guy isn’t &lt;a href=&quot;https://www.amazon.com/dp/0142000280?tag=ryan0d-20&amp;camp=213381&amp;creative=390973&amp;linkCode=as4&amp;creativeASIN=0142000280&amp;adid=1CY6RYGFXJB48X9VE86K&amp;&quot;&gt;David Allen&lt;/a&gt;{: target=&amp;quot;_blank&amp;quot;}). That said, I have recently returned from the torpor of spending large quantities of free time reading about productivity instead of actually &lt;em&gt;getting things done&lt;/em&gt;. If you&amp;#39;re into reading about productivity, I recommend you check out what Merlin Mann has to say about it on &lt;a href=&quot;http://www.43folders.com/&quot; title=&quot;Find the time and attention to do your best creative work.&quot;&gt;43 Folders&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Personally, paralysis by analysis is my biggest hurdle to getting things done. As such, productivity blogs/books/systems to ponder are probably not where my salvation lies. Furthermore, spending time researching productivity to get more time is like trying to get rich by clipping coupons. I’m not exactly arguing that productivity is a bad thing, just that I’m not sure it should be such a high priority. For creative workers… doubly so.&lt;/p&gt;
&lt;p&gt;If I’m always working on scratching “important” items off my todo list, I’ll never purge all of the fun stuff that is buried down on my someday/maybe list. Those are the excursions that often times spawn unique solutions for many would be mundane challenges. I’m not relinquishing my trusty moleskin, I’m just going to start filling it with creative bits instead of priorities.&lt;/p&gt;
</content:encoded></item><item><title>5 Similarities of the fold and mullets</title><link>https://ryanparsley.com/blog/2008-10-13-5-similarities-of-the-fold-and-mullets</link><guid isPermaLink="true">https://ryanparsley.com/blog/2008-10-13-5-similarities-of-the-fold-and-mullets</guid><pubDate>Mon, 13 Oct 2008 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It recently occured to me that there are more than a couple similarities between these two unfashionable things. Here is the &lt;em&gt;long and short&lt;/em&gt; of it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There is no reasoning with mullet enthusiasts.&lt;/li&gt;
&lt;li&gt;Both have fishy origins&lt;/li&gt;
&lt;li&gt;One is an ape drape one is monkey see monkey do.&lt;/li&gt;
&lt;li&gt;Both seem like a compromise between style and business (see also: business in the front… party in the back), but are really just absurd.&lt;/li&gt;
&lt;li&gt;At first glance, both concepts seem dated. Under closer evaluation you realize neither was ever a good idea.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Do not make this Achy-Breaky Big Mistakie!&lt;/p&gt;
</content:encoded></item><item><title>Fast track to nowhere.</title><link>https://ryanparsley.com/blog/2008-09-23-fast-track-to-nowhere-</link><guid isPermaLink="true">https://ryanparsley.com/blog/2008-09-23-fast-track-to-nowhere-</guid><pubDate>Tue, 23 Sep 2008 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Always on the look out for ways to improve productivity, I have looked a bit into Ruby on Rails as a development framework. I have watched numerous tutorials on how easy and fast you can deploy apps. Heck, I even bought a book to help me get &amp;quot;Up and Running&amp;quot;. I have downloaded a couple IDEs and text editors that I hear can speed things up.&lt;/p&gt;
&lt;p&gt;Everything looks rosy except for one missing detail… I can not figure out for the life of me how to set up a server. Other than the tutorial on rubyonrails.org (which states &amp;quot;These instructions need to be updated.&amp;quot;) and the instructions on &lt;a href=&quot;https://help.ubuntu.com/community/RubyOnRails&quot;&gt;Ubuntu&amp;#39;s forum&lt;/a&gt; (which also don&amp;#39;t work for me), I can find no ways to successfully make my LAMP Box accommodate ruby (and I have worked through many others).&lt;/p&gt;
&lt;p&gt;I get that I can just use web brick on port 3000 on my dev machine. This seems far from production ready. I have also seen many options to lease ruby centric hosting. I am only comfortable with as-a-service when I know I can serve myself, if need be.&lt;/p&gt;
&lt;p&gt;I am a defeated man. I fail to see efficiency benefits as I spent such a large amount of time tinkering with my server set up to no avail. I’m going back to Drupal. So much for getting things done.&lt;/p&gt;
</content:encoded></item><item><title>FOWA 08 in 80 words or less</title><link>https://ryanparsley.com/blog/2008-06-16-fowa-08-in-80-words-or-less</link><guid isPermaLink="true">https://ryanparsley.com/blog/2008-06-16-fowa-08-in-80-words-or-less</guid><pubDate>Mon, 16 Jun 2008 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I just stumbled upon some notes I took at FOWA, so this post is less than timely. Better late than never. There was copious amounts of wisdom to gather from this long weekend event, but there was one underlying theme that seemed to be woven into most, if not all, of the presentations. In the spirit of gross generalizations here is a synopsis of the most important theme to have been picked up at FOWA Miami 2008.&lt;/p&gt;
&lt;p&gt;We need to embrace the fact that we are not perfect. Instead of striving for perfection, we must simply try to suck less today than we did yesterday. Firmly setting a destination, and never checking our instruments, can easily end up with a perfectly honed path to failure. If we strive to engage our community as participants, more than just users, they will willing guide us to creating the right product (as long as we remain receptive). Focus on progress.&lt;/p&gt;
</content:encoded></item><item><title>I was going to write a punny heading for Plurk, but it is just too quirky all by itself</title><link>https://ryanparsley.com/blog/2008-06-04-i-was-going-to-write-a-punny-heading-for-plurk-but-it-is-just-too-quirky-all-by-itself</link><guid isPermaLink="true">https://ryanparsley.com/blog/2008-06-04-i-was-going-to-write-a-punny-heading-for-plurk-but-it-is-just-too-quirky-all-by-itself</guid><description>Yet another microblog enters the field</description><pubDate>Wed, 04 Jun 2008 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So it&amp;#39;s not exactly news, &lt;a href=&quot;http://www.plurk.com/&quot;&gt;Plurk&lt;/a&gt; is on the scene. For
the uninitiated, it is yet another microblog site. The big feature they tout is
a cool AJAXy dragable timeline to display your updates (plurks) on. As a graphic
designer, I really like that the folks at Plurk are carving their niche by
focusing on the interface, but as a twitter user, I need attention to ease of
post. . However, I&amp;#39;m sure I am far from the first to suggest that a liberal API
would help a startup in this market. Furthermore, I would wager this is probably
on it&amp;#39;s way.&lt;/p&gt;
&lt;p&gt;Honestly, I do not see Plurk as a competitor with Twitter. Twitter is really
going for being a communication platform where Plurk seems to be more focusing
on the community aspect. I do see Plurk as being one to watch in the realm of
microblogging in general though. Especially considering I discovered them via
Jason Calacanis, the Don King of the the internet.&lt;/p&gt;
</content:encoded></item><item><title>Learning How to Love... Twitter</title><link>https://ryanparsley.com/blog/2008-03-20-learning-how-to-love----twitter</link><guid isPermaLink="true">https://ryanparsley.com/blog/2008-03-20-learning-how-to-love----twitter</guid><pubDate>Thu, 20 Mar 2008 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have had an account for a little while now. But it was not until recently that I got “it”. There are &lt;a href=&quot;http://wtfistwitter.com/&quot; title=&quot;WTF is Twitter&quot;&gt;plenty&lt;/a&gt;{: target=&amp;quot;_blank&amp;quot;} of people &lt;a href=&quot;http://www.commoncraft.com/Twitter&quot; title=&quot;Twitter in Plain English&quot;&gt;explaining&lt;/a&gt;{: target=&amp;quot;_blank&amp;quot;} twitter, but the “it” factor can/ will not happen unless you play with it*. I dabbled, but until saw the “itis” at FOWA first hand, I resisted. Now, I follow via IM with sms forwarding while I refresh a web page and sample 2 air clients. It’s not a habit, it’s cool.&lt;/p&gt;
&lt;p&gt;One big reason of writing this post is in hopes to reach someone who was like me. “Why would I want to broadcast an IM when I really don’t have that many regular contacts that I communicate with via IM?” I mean scalability doesn’t seem that important when you don’t have a large scope (I’m looking at you Ruby On Rails Devs). I ask, “Do you have any interest in what your favorite bloggers/ Internet Superstars are doing between posts (ZeFrank… we have learned our lesson, please vlog again, we all miss the show)” or “Would you like a grand equalizer that allows you to communicate real time to these ‘A’ list internet types?” Of course you would.&lt;/p&gt;
&lt;p&gt;So, twitter is a paradigm shift in communication. Don’t be a ludite, tweet (all the &lt;a href=&quot;http://twitter.com/garyvee&quot; title=&quot;Gary Vay... Ner... Chuk!&quot;&gt;cool&lt;/a&gt;{: target=&amp;quot;_blank&amp;quot;} kid’s are doing it**).&lt;/p&gt;
&lt;p&gt;*playing with it has no proven relationship to blindness.&lt;/p&gt;
&lt;p&gt;** Cool is subjective and a particularly difficult case to argue in terms of web apps. Please consult your local Fonz if your are seeking an increase in cool.&lt;/p&gt;
</content:encoded></item><item><title>FOWA FTW</title><link>https://ryanparsley.com/blog/2008-03-05-fowa-ftw</link><guid isPermaLink="true">https://ryanparsley.com/blog/2008-03-05-fowa-ftw</guid><pubDate>Wed, 05 Mar 2008 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While still on the high of just attending the &lt;a href=&quot;http://www.futureofwebapps.com/2008/miami/&quot; title=&quot;Future Of Web Apps&quot;&gt;FOWA&lt;/a&gt; Miami, I am inspired to start blogging again. If you have not yet attended a FOWA, you should. It is an awesome event put on by some &lt;a href=&quot;http://carsonified.com/&quot; title=&quot;Carsonified&quot;&gt;excellent people&lt;/a&gt;{: target=&amp;quot;_blank&amp;quot;}. I must say, attending this conference really made me feel part of a web community, more than going to any social network ever has. Which just happened to fit in with one of the reoccurring themes in the talks &amp;ldquo;Have offline interaction with your online community&amp;rdquo;. Which, like most ideas, is as brilliant as it is simple.&lt;/p&gt;
&lt;p&gt;There is something to be said about the excitement created by being surround by that much excellence. Paraphrasing Kathy Sierra, there is still something to be said for atoms. It is one thing to read blogs from like minded individuals and see that people want to make the web a better place, but to actually interact with so many people, in the flesh, that are passionate about the same things you are is much more powerful (assuming your into that kind of thing).&lt;/p&gt;
&lt;p&gt;TTFN, I am off to the beach party. I have a notebook full of notes as well as some audio, video, and images. So there is definitely more to come about this.&lt;/p&gt;
</content:encoded></item><item><title>Dr. Strangecode or: How I Learned to Stop Worrying and Love the DOM</title><link>https://ryanparsley.com/blog/2008-02-06-dr--strangecode-or-how-i-learned-to-stop-worrying-and-love-the-dom</link><guid isPermaLink="true">https://ryanparsley.com/blog/2008-02-06-dr--strangecode-or-how-i-learned-to-stop-worrying-and-love-the-dom</guid><pubDate>Wed, 06 Feb 2008 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Narrator:&lt;/strong&gt; For more than 12 years, ominous rumors had been privately circulating among high-level web developers that a world wide consortium had been at work on what was darkly hinted to be the ultimate weapon: a doomsday device. Intelligence sources traced the home bases of this top secret international project to the perpetually fog-shrouded wasteland below the facilities of MIT/CSAIL (USA), ERCIM (France), and Keio Univ. (Japan). What they were building or why it should be located in such remote and desolate places no one could say.&lt;/p&gt;
&lt;h2&gt;Well, I’ve been to one world fair, a picnic, and a rodeo, and that’s the stupidest thing I ever heard? You sure you got today’s codes?&lt;/h2&gt;
&lt;p&gt;Do you still use tables to control your layouts or know someone who does? It is ok, I am here to help. I’ll level with you; I too once used tables to control my layouts. But thanks to &lt;a href=&quot;http://www.amazon.com/Designing-Web-Standards-Jeffrey-Zeldman/dp/0321385551/ref=pd_bbs_sr_1/104-2454640-8435942?ie=UTF8&amp;s=books&amp;qid=1192383120&amp;sr=1-1?ie=UTF8&amp;tag=ryan0d-20&amp;link_code=em1&amp;camp=212341&amp;creative=384049&amp;creativeASIN=0275954544&amp;adid=08442520-5931-460e-b021-b261d385583c/&quot;&gt;Jeff Zeldman&lt;/a&gt; and other standards evangelists, I have found the way. In this, my first post, I am going to do my best to perpetuate the standards movement by posting about the reasons web standards are good for humanity.&lt;/p&gt;
&lt;p&gt;For me the journey to smart web design began in 2003 by reading &lt;a href=&quot;http://www.amazon.com/Designing-Web-Standards-Jeffrey-Zeldman/dp/0321385551/ref=pd_bbs_sr_1/104-2454640-8435942?ie=UTF8&amp;s=books&amp;qid=1192383120&amp;sr=1-1?ie=UTF8&amp;tag=ryan0d-20&amp;link_code=em1&amp;camp=212341&amp;creative=384049&amp;creativeASIN=0275954544&amp;adid=08442520-5931-460e-b021-b261d385583c&quot;&gt;Designing with Web Standards, by Jeffrey Zeldman&lt;/a&gt;. Upon reading this book I had a bit of a “eureka” moment. If you have not yet read this book I recommend it (the &lt;a href=&quot;http://www.amazon.com/Designing-Web-Standards-Jeffrey-Zeldman/dp/0321385551/ref=pd_bbs_sr_1/104-2454640-8435942?ie=UTF8&amp;s=books&amp;qid=1192383120&amp;sr=1-1?ie=UTF8&amp;tag=ryan0d-20&amp;link_code=em1&amp;camp=212341&amp;creative=384049&amp;creativeASIN=0275954544&amp;adid=08442520-5931-460e-b021-b261d385583c&quot;&gt;second edition&lt;/a&gt; just came out recently). Many of the HTML conventions I had previously adhered to, simply because they “worked” (I mean really… spacer gifs… what was I thinking), were about to become a thing of the past. Ah, but it was not an easy transition, I had been programmed to do bad things. At first my sites were just as clunky as a table based websites… I had to fight the urge to simply swap tr and td tags for div tags. I thought that all elements needed to be bound by something or they may run away.&lt;/p&gt;
&lt;h2&gt;“through the purity and essence of our natural” fluids&lt;/h2&gt;
&lt;p&gt;If you began web development with table based layouts, you may see this whole tableless development as just another of the many fads the web development community seems so eager advocate then abandon. Table ridden websites download slower, put more stress on your server, and are more likely to break for no apparent reason.&lt;/p&gt;
&lt;p&gt;If you have been using tables for a while, it may not seem obvious, but if you think about it, how many times do you use nonsensical “hacks”? How many spacer gifs do you implement? How many times do you mark “width=100%” only to cross your fingers hoping that it fills the existing space and not force your containing table to stretch erratically? For that matter how many times does it stretch erratically?&lt;/p&gt;
&lt;h2&gt;But this is absolute madness, Ambassador! Why should you build such a thing?&lt;/h2&gt;
&lt;p&gt;Oh, I know what you?re saying, “But Ryan, that&amp;#39;s the way they did it in nineties and the internet thrived”. People tight-rolled their pant cuffs in the nineties too… we have evolved. In a nutshell, the nineties were decadent era for web design (among other things). Web development could be sloppy; there was plenty of money to write one website for IE, and a couple for Netscape. Font tags for every one! Hire another guy to make sure all in-line styles to the appropriate sidebar links are the right shade of magenta. Of course in-line, I mean, it isn&amp;#39;t as though there exists some sort of &lt;em&gt;complete&lt;/em&gt; and &lt;em&gt;recursive&lt;/em&gt; (cascading) declaration your site&amp;#39;s &lt;em&gt;appearance&lt;/em&gt; (style?) of the entire website in an external &lt;em&gt;page&lt;/em&gt; (or sheet). So that all 200 pages of this site could share a single… hmm what do we call it CRAP… certainly not, maybe just simplify it… CSS.&lt;/p&gt;
&lt;p&gt;Enter CSS. One sheet to rule them all. No need to spend sleepless nights ensuring that all of your buhgillion inline styles and are consistent amongst many pages. Less chance of those obnoxious pixel-jumping page changes that result from one of your pages using a slightly different font size. It is a kinder, gentler, more logical web.&lt;/p&gt;
&lt;h2&gt;Gentlemen, you can’t fight in here! This is the War Room&lt;/h2&gt;
&lt;p&gt;Let me just plainly state… tables are not evil. They have a purpose. To display tabular data (oh yeah, what technical blog is complete without a recursive definition), and that is where it ends. You need fields of data to line up in a grid based relational format; table is the man for the job. You need logical structure to a website that is both easy to disperse on wide scale use and won&amp;#39;t impact the market value of Maalox; table is not up to the challenge.&lt;/p&gt;
&lt;p&gt;I feel the need to say this, as all too many times, I have seen new tableless zealots spend hours (if not days) coding an all div representation of some sort of data matrix (or table). I feel Charlton Heston would agree with me when I say, tables do not make absurd websites, designer/ developers that use tables incorrectly make absurd websites. To be fair, designers/ developers can make equally if not more absurd websites with CSS. The key here is to design more intelligently.&lt;/p&gt;
</content:encoded></item><item><title>Monroy Seminar Review</title><link>https://ryanparsley.com/blog/2008-02-06-monroy-seminar-review</link><guid isPermaLink="true">https://ryanparsley.com/blog/2008-02-06-monroy-seminar-review</guid><pubDate>Wed, 06 Feb 2008 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hey Crime Stoppers, I just got back from attending a Photoshop seminar by &lt;a href=&quot;http://www.bertmonroy.com/&quot; title=&quot;Bert Monroy&quot;&gt;Bert Monroy&lt;/a&gt;{: target=&amp;quot;_blank&amp;quot;} in Ft. Lauderdale. Monroy is an illustrator whose specialty is Photoshop and as he has been doing what he does for about as long as I have been alive, to say he knows his stuff is an understatement. More or less, I would recommend this seminar to most people, but I am not sure I was the target demographic. &lt;!--base32-c9t6arbb-base32--&gt; I would say at a price of $99 for admission, you can easily get your money’s worth. However, if you are very familiar with Photoshop at all, it becomes more of a “you get what you pay for” situation. For the most part, I found myself thinking “If only I had seen this seminar when I was first learning Photoshop.” For me this seminar was a collection of practical tips that &lt;em&gt;should&lt;/em&gt; be fundamental. I must admit though, I had already been familiar with many of the “tricks/ tips” that were covered long before I sat in on this seminar. In credit to this seminar, it did take me much time reading magazines, scouring blogs/ forums, and skimming through tutorial links to obtain what was covered in this one day event. If you are new to Photoshop and value your time I would recommend this seminar.&lt;/p&gt;
&lt;p&gt;Personally, I would like to take a more advanced or at least more focused seminar (maybe a workshop?) with Mr. Monroy, as he definitely strikes me as a wealth of knowledge. As a professional with nearly four years of work experience (and an insatiable appetite for dabbling), I was not overwhelmed with new stuff I had never seen before. The thing about Monroy is he has the experience and practice to execute these projects very well. In the course of this seminar though, Monroy seemed to want to display too much too quick. During his segment about layer styles, Monroy created a remarkably realistic rendition of a weathered brick wall in a few minutes. Again, it was not that he did things I had never seen before, just that he did it better than I had ever seen before. I only fear that many attendants have seen Monroy pull off these techniques so effortlessly and well they will mistake filters and styles for “make art” buttons. Monroy displays how many aspects of Photoshop that can be type cast as tacky can, in fact, be used to successfully save time and yield excellent results.&lt;/p&gt;
&lt;p&gt;Perhaps the target audience Monroy was going after was a little less savvy. For instance, when Monroy spent a considerable amount of time explaining the difference between vector and raster images, I screamed on the inside. Later, there was a half an hour dedicated to using the pen tool (as in adding points and moving handles). And, although I say that I found many of the topics covered to be intuitive (or down right obvious), I eavesdropped on a few conversations of attendees that were blown away with new ideas that they had never encountered before. One gentleman equated trying to consume information at that seminar to trying to sip from a fire hose. And during the seminar one man interjected and asked what the difference is between flatten and merge visible. Obviously these men were getting their money’s worth.&lt;/p&gt;
&lt;p&gt;All in all, I would say this is a very nice seminar. Monroy is very knowledgeable and has nice presentation. A few times during the filter section, Monroy seemed a bit more like an illusionist than an illustrator with a very, “for my next trick?” heavy tone. Quite appropriate considering the parlor trick like stigma that oft times surrounds filters. There were many useful gems scattered throughout the seminar and I would honestly say that the hour Monroy spent covering channels made the workshop worth the money for me.&lt;/p&gt;
</content:encoded></item></channel></rss>