Adrian Unger Hi there, I’m Adrian Unger, a software developer who prefers to be outdoors. 2020 Why Redux over a more basic Global Store Adrian Unger Tue, 12 Nov 2019 00:00:00 -0800 <p>Lately there's been some &quot;<a href="">discussion</a>&quot; about the boilerplate around using Redux for application state. It's easy to look at Redux and think it's just an over-complicated global state. You could strip away much of the API and just implement an immutable object state, even mimicking the React state API of just <code>setState</code> and <code>getState</code>. </p> <div class="note">For a Global Store that supports <code>getState</code>, <code>setState</code> and Redux compatible actions and reducers, I created <a href="" target="_blank">atom</a>.</div> <p>And, this will get you pretty far. Then, throw in a few helpers to map or select parts of the state object and use them as props in your Components, and you actually have a solution that's very friendly for rapid prototyping. Without the need to create specific Actions and Reducers, you reduce boilerplate, and can just start modifying state from your components:</p> <pre><code class="language-javascript">globalStore.setState({ todos: globalState.todos.push(newTodo) })</code></pre> <p>When creating prototypes or MVPs, where speed is a priority, a simple global store is a good solution. So, why would you bother with the added complexity of Redux, Actions, and Reducers?</p> <h2>Traceable state changes</h2> <p>The origin of state change is easier to follow since the change is triggered by an Action. An action is basically just a descriptor of what should happen. ie. <code>ADD_TODO</code>, <code>UPDATE_TODO</code>, <code>REMOVE_TODO</code>. This becomes more important when you have Components at various points in your React tree that can change the same data in your store.</p> <p>You can also observe the order in which Actions were triggered and make sure the changes are happening in a sequence that makes sense for your app. This can be thought about in terms of data-integrity as well as the User Experience.</p> <p>With Redux Devtools, this is a really pleasant experience.</p> <h2>Refactoring</h2> <p>Because state changes are triggered by Actions, you can easily search your project for all instances of a specific Action being triggered. Perhaps, later on in the life of your project, you need to include some additional data for a specific type of state change. It's much easier to update a single Reducer to ensure that data now exists, and then search for all trigger instances for that Action.</p> <p>With a global <code>setState</code> solution, it's much harder to search for the triggers for a specific change to state, ie. </p> <pre><code class="language-javascript">setState({ todos: todos.filter(({ id }) =&gt; id !== idToRemove) })</code></pre> <p>If you don't follow the exact formatting in every case where you remove a todo, you will not be able to perform a quick, project-wide search and replace.</p> <h2>Finding balance</h2> <p>The sentiment from that above embedded tweet isn't wrong. I've worked on several React/Redux projects where there were many dozens of files trying to group even more numerous action definitions, and reducers, and — oh my — middleware.</p> <p><strong>So how can you keep the number of Actions and Reducers in your project under control?</strong></p> <p>First, you need a generalized way to affect state change. <a href="">Here's the <code>pathReducer</code></a> I use on every project.</p> <p>If a specific state change only occurs in one or two places (components) you should be fine to rely on the generalized state change. Once you have state changes that partially overlap, defining a specific action and reducer can help with any potential debugging (see above about Traceability).</p> <p>Then, once a project becomes more mature (re: Launched and with a userbase!), you may want to set time aside to write specific actions and reducers to replace those generalized state changes. Because of the improved traceability and refactoring, your project can become easier to maintain.</p> <h2>Anything else?</h2> <p>Did I miss anything? From my experience, the above two points are what make Redux and the concept of Actions and Reducers stand out.</p> <p>I still believe a simpler global <code>setState</code> can really help get an MVP out the door quickly. But, eventually, as an app and codebase grow, you'll likely benefit from the traceability and easy refactoring that comes with specific Actions and Reducers.</p> Migrating to React Hooks Adrian Unger Sun, 25 Aug 2019 00:00:00 -0700 <p>React has <a href="">introduced</a> Hooks as an alternative to writing Components without the JavaScript <code>class</code> syntax. This is wonderful, as I find the <code>class</code> syntax confusing, and I'm happy to recommend foregoing it entirely!</p> <p>I eventually <a href="">got past</a> abstracting away the <code>class</code> syntax, but I never felt great about it. Moreover, Hooks are quite powerful, readable, and an easy way to break away shared functionality.</p> <h2><code>class</code> is Confusing</h2> <p>I don't want to get too far into this (since we can just leave <code>class</code> behind now!) so I'll try to be brief: JavaScript is a language with <a href="">prototypal inheritance</a>. This is different than classical inheritance. <code>class</code> is syntactic sugar that abstracts the prototypal inheritance, making it appear more familiar to those coming from languages with classical inheritance, but does not actually introduce a classical object-oriented inheritance model.</p> <p>I don't recall the precise history, but React and Babel go hand-in-hand, and one of those communities was ready to push <code>class</code>. This meant Babel introduced the class syntax before most (any?) browsers had native support. Abstracted code can <a href="">have surprises</a>, and the <a href="">React devs don't recommend</a> using inheritance anyway! Seeing <code>class</code> based Components in a codebase where you wish to avoid inheritance definitely feels confusing. </p> <h2>Getting to Know Hooks</h2> <p>The React team did a fantastic job with the <a href="">Hooks documentation</a>. Make sure you read that. The goal for the rest of the post is to provide quick comparisons of how you (generally) used to solve certain problems with class based Components, and how you might approach the same problems with Hooks.</p> <h2>From <code>class</code> based Components to Functional Components with Hooks</h2> <p>To rid yourself of the lie that is class, you'll need to know how to write function-based React Components and how to utilize Hooks. Previously, you may have had <em>dumb</em> and <em>smart</em> components. <em>Dumb</em> just meant a function that returned JSX without any coupled lifecycle and state logic. So, you'd have a mix of function-based Components and class-based Components. With hooks, all your Components can be functions!</p> <h3>Forget <code>setState</code> and Embrace <code>useState</code></h3> <p>The goal of state in React Components is to &quot;remember&quot; things like user interaction (clicks, typing, etc.) or API requests.</p> <pre><code class="language-javascript">// Inside a class component this.state = { loaded: false } // ... after some async thing loads this.setState({ loaded: true })</code></pre> <p>Now with hooks:</p> <pre><code class="language-javascript">// inside a function that returns JSX const [loaded, setLoaded] = useState(false) // ... after some async thing setLoaded(true)</code></pre> <p>If you use an object or array as a <code>useState</code> value, you need to pass in a new reference to trigger an update.</p> <pre><code class="language-javascript">const [items, setItems] = useState([1, 2, 3]) items.push(4) setItems(items) // Won't re-render setItems([...items, 4]) // Does re-render!</code></pre> <h3>Goodbye &quot;lifecycle&quot; Methods... Hello, <code>useEffect</code>?</h3> <p>With lifecycle methods (<code>shouldComponentUpdate</code>, <code>componentDidUpdate</code>, etc.) you are managing when your Component should render. But, managing your component's rendering lifecycle is usually an abstraction from what you're really trying to manage: when your state should change, and with what state-changes should a side-effect run?</p> <p>This is such an improvement in concept. When working with side-effects, instead of thinking about the Component Lifecycle, you can more directly think about synchronizing state changes based on your side-effects. Or more generally: think about your data not your component rendering lifecycle.</p> <blockquote> <p>The question is not &quot;when does this effect run&quot; the question is &quot;with which state does this effect synchronize with&quot;</p> <p>useEffect(fn) // all state useEffect(fn, []) // no state useEffect(fn, [these, states])</p> <p>— Ryan Florence (@ryanflorence) <a href="">May 5, 2019</a></p> </blockquote> <h3>Okay, But What About &quot;mount&quot; Events?</h3> <pre><code class="language-javascript">// We'll need to track if the component is mounted. We'll use // useRef which acts as instance variables without the class syntax. // And a useEffect call with no inputs, so it's only called once on mount. // Returning a function in `useEffect` that will be called when the // Component unmounts. const mountedRef = useRef(false) useEffect(() =&gt; { mountedRef.current = true return () =&gt; (mountedRef.current = false) }, [])</code></pre> <h3>What about <code>useMemo</code> and <code>useCallback</code>?</h3> <p>When getting started, I would avoid these until you encounter a slow Component. Wrapping all of your handlers and variables in <code>useCallback</code> and <code>useMemo</code> can actually <a href="">hurt performance</a>. So, until you actually encounter a performance issue, you shouldn't worry about these too much.</p> <h3>Did I miss anything?</h3> <p>I'm already knee-deep authoring React Components using Hooks, and may have overlooked something. If you think there's more to know when migrating from class-based Components to Hook-based, let me know!</p> The Difference Between Static and Bound Methods Adrian Unger Tue, 08 Jan 2019 00:00:00 -0800 <p>Recently, at <a href="">work</a>, we were coming up with our <a href="">React Style Guide</a> (based on <a href="">AirBnBs</a>). One of the &quot;rules&quot; is to avoid binding methods within the render method (as this creates a new function each render), and a teammate mentioned, &quot;why not just use static arrow functions?&quot;</p> <p>While both solutions were practically equivalent, I worried about the technical differences. Static methods are defined on the Class <em>not</em> on each prototype instance. </p> <h2>Wait, so how is the arrow function properly binding the correct <code>this</code> to the static?!</h2> <p>This magic had me worried. So I pulled up a <a href="">Babel REPL</a> and wrote two classes:</p> <pre><code class="language-jsx">class Static extends React.Component { onClickDiv = () =&gt; { // do stuff } render () { return &lt;div onClick={this.onClickDiv} /&gt; } } class Bound extends React.Component { constructor (props) { super(props) this.onClickDiv = this.onClickDiv.bind(this) } onClickDiv () { // do stuff } render () { return &lt;div onClick={this.onClickDiv} /&gt; } }</code></pre> <p>The transpiled Bound class was as you would expect, but the Static class resulted in: </p> <pre><code class="language-jsx">class Static extends React.Component { constructor(...args) { var _temp; return _temp = super(...args), this.onClickDiv = () =&gt; { // do stuff }, _temp; } render() { return React.createElement("div", { onClick: this.onClickDiv }); } }</code></pre> <p>Oh! So maybe these are technically equivilant! But defining a Class method is still a different syntax than defining a function on a <code>this</code> reference. So I dropped the transpiled code into a <a href="">FireFox scratchpad</a>.</p> <img src="/assets/media/class-prototypes.png" alt="Screenshot of the Firefox Console showing the Static and Bound prototypes." /> <p>The only difference seems to be a bound class method exists on the prototype whereas the static arrow function does not.</p> <p>But, how or why would this matter? In my experience, it usually doesn't, unless you want to mock a class method in your tests (by overriding the definition on the prototype), or — <a href="">god forbid!</a> — you plan on extending the class.</p> <p>In the end, because of the techincal difference, we aren't enforcing static methods over bound methods in our style guide, rather both are fine, as long as you're aware of the implications.</p> <p>Have I missed any other differences between the two? I'd love to know if there's other details to consider.</p> Debugging a Slow Rails App Adrian Unger Mon, 02 Jul 2018 00:00:00 -0700 <p>This past winter, I was tasked with figuring out why certain views on a Rails app were slowing to a crawl. While this was my first foray into Ruby/Rails debugging and performance tuning, I had decent experience with JavaScript and SQL query optimizing. </p> <p>Now, the notes I made are rough, and it's been about 6 months since the work was performed, so I may miss some details. But, I wanted to share my process in hopes of figuring out where and how I could improve.</p> <h2>Some project background</h2> <p>The app was actually 2 Rails apps: 1 for front-end and 1 for the API. The front-end Rails app had custom controllers/models that represented API resources. The API app was rather standard Rails API setup. </p> <p>The original developers were not available to discuss and walk through any code.</p> <p>And, the project was rather low-traffic, so this wasn't an issue at massive scale. This gave me hope the issue would be easier to spot :)</p> <h2>Inspecting the database</h2> <p>As this was a totally unfamiliar app to me, and the original developers were not around to help walk-through the code, I tried what would be the lowest amount of effort to improve performance: SQL query performance tuning!</p> <div class="note">I also should have prefixed this by saying, I'm really not that experienced in DevOps or SysAdmin tasks. I guess I'd call myself a Full Stack Developer, with a focus on Front-end.</div> <p>Luckily, for me, the app was hosted on Heroku and had a paid Heroku PostgreSQL database. Lucky, because you can simply click on the Database resource and then choose the &quot;Diagnose&quot; tab to inspect frequent and potentially slow queries. This will show you the SQL queries on the right and 2 graphs on the left. The graphs show <em>Number of Invocations</em> and <em>Average Time Per Invocation</em>. Here's a sample from a different app:</p> <img src="" alt="Example Heroku PostgreSQL Diagnostics" /> <p>Short story: SQL did not appear to be the problem.</p> <h2>Inspecting Gems</h2> <p><a data-flickr-embed="true" href="" title="Gems"><img src="" width="640" height="388" alt="Gems"></a><script async src="//" charset="utf-8"></script></p> <p>My next plan was to inspect dependencies. This was a tactic I've picked up in tuning front-end JavaScript apps. Again, this is another potential low effort win. Basically, check the version of major libraries (including the runtime), and then check GitHub Issues or other bug tracking for those libraries for any issues raised about performance.</p> <p>The first thing I noticed was this project was using an old version of Ruby, which was nearing end of life for support. And, many versions since touted performance gains. I made note of this as a last resort. Really, I didn't think updating runtime would or should solve the problem by an acceptable magnitude. Sure, perhaps some of the Ruby language improvements would help this project, but I doubted (still do to this day) that it would have been significant enough to call it a day.</p> <h2>Enter Skylight</h2> <iframe src="" width="480" height="480" frameBorder="0" class="giphy-embed" allowFullScreen></iframe> <p><a href="">via GIPHY</a></p> <p>After my generalized approaches failed to find anything significant, I moved on to a more specialized tool, <a href="">Skylight</a>. Skylight is a Ruby specific performance reporting service. And, thankfully, the client had Skylight enabled for a few weeks (IIRC they added it only after experiencing slow load times).</p> <p>Just like the Heroku monitoring graphs, Skylight identified the same views as being slow. Where the advantage came in, was Skylight also showed Ruby/Rails specific runtime details, matching response times to Controller methods, rather than just URL endpoints.</p> <p>Right away, I noticed something about &quot;Object Allocations&quot; and that they were through the freaking roof. This meant, Ruby was using wayyyy too much memory.</p> <h2>Narrowing in on the offending code</h2> <p>Skylight was nice enough to point me to the exact Controller method that was being called, which results in the massive amount of object allocations. But, it didn't tell me exact code (you know, like a line number!).</p> <p>So I went hunting for &quot;complex&quot; looking code. Meaning, some function or method that has loops, maybe too many references being created, that sort of thing. Nothing stood out. I inspected the classes that were being inherited, didn't see anything obvious.</p> <p>Okay, so I was closer to the source, but could not pinpoint it. I needed to profile the code locally.</p> <h2>Rack Mini Profiler</h2> <p><a href="">rack-mini-profiler</a> [RMP] is rack middleware that displays a speed badge for every HTML page. Well, the first issue is I was inspecting the JSON API Rails app. But, it still had a HTML 404 page, yay!</p> <p>So, we can hit our API locally with some requests, RMP will do it's thing, and then I can manually load the 404 HTML page in a browser, which shows the previous requests via the RMP badge.</p> <p>Similar to what Skylight was showing me, I can clearly see a slow request stand out.</p> <p>I wanted to add some query string parameters to customize my mini-profiler usage. I Loaded up <a href="">Insomnia</a> for making HTTP requests to my local API server, hitting the endpoint that has been identified in the above steps.</p> <p>Following <a href="">this blog article</a> by Nate Berkopec, I figured the Garbage Collection feature was what I was after and added the <code>?pp=profile-gc</code> flag to my POST request.</p> <p>This Garbage Collection flag would track object allocations. So, I needed a baseline. I enabled the default Rails homepage and hit that:</p> <p><code>New objects: 3962</code></p> <p>Then, I made a request against the slow endpoint:</p> <p><code>New objects: 161271</code></p> <p>Yikes.</p> <p>But, still I have not yet been able to narrow down a chunk of code that is causing this excessive object allocation. I have only confirmed it in more detail.</p> <p>I then tried the <code>profile-memory</code> feature of RMP.</p> <pre><code>Total allocated: 14772397 bytes (162495 objects) Total retained: 1647863 bytes (12389 objects) allocated memory by gem ----------------------------------- 5765128 aws-sdk-v1-1.64.0 3202115 activesupport-4.1.5 2962795 activerecord-4.1.5 1764149 2.1.3/lib ...</code></pre> <p>After double-checking the AWS library for performance issues or a new version with improvements. I moved down into the ActiveSupport and ActiveRecord libraries. These are part of Rails and are related to accessing your database. But, we knew at this point, that SQL wasn't an issue. The lead me to conclude the issue was something on the lines of</p> <blockquote> <p>Too much memory is being used when accessing data from the PostgreSQL database, causing the Ruby garbage collector to jump in, stressing the CPU and crashing the app.</p> </blockquote> <p>I eventually identified an Active Record method being used in a loop, <a href="">serialize</a>. This view had quite a lot of records, and had nested Model records, and the way in which it was being serialized caused all the records' attributes to be initialized in memory.</p> <p>Unfortunately, I don't have access to the codebase anymore, and it's been ~6 months. But, from what I can recall, I ended up caching references for only the attributes that were needed by the front-end, instead of serializing all attributes of the records.</p> <p>I believe I made symbols for the attributes I wanted (which are only initialized once in Ruby, if I understand correctly).</p> <p>Then I reduced the amount of nested/related model data being serialized, again by reducing it to only what the front-end needed. As well as caching nested references. There were parent model instances that had arrays of children. Many of those children were shared across the parents. The original ActiveRecord code was re-initializing every single child instance, instead of re-using a potentially already initialized instance.</p> <p>In the end, this was enough to significantly reduce memory allocations, preventing the Ruby garbage collector from eating CPU, resulting in manageable response times.</p> <h2>The End</h2> <p>Anyway, that's the path I took. I had very little previous experience with Ruby/Rails prior to this. But, with the reporting that comes with Heroku and their PostgreSQL hosting, as well as Skylight and the rather awesome Rack Mini Profiler, the task was much less daunting.</p> <p>This isn't my area of expertize, but I certainly enjoy it. So if anyone has tips or advice, let me know!</p> Expired Kodak BWC Adrian Unger Sun, 18 Feb 2018 00:00:00 -0800 <p>I've been getting more and more into shooting 35mm film these past few months. So much so, that I'm starting to head out and only bring my film SLR, leaving my Sony NEX 6 behind.</p> <p>Recently, Angela found a bunch of rolls she bought maybe 15 years ago. I've now shot two of them, compensating for their expiration by dropping the ISO on the camera down a stop. For this roll of Kodak BWC, I could have stopped down another stop &endash; quite a few photos were underexposed.</p> <h3>Expired Kodak bwc (C41 process BNW) 200 shot at ISO 100</h3> <img src="/assets/media/Scan 0-2.jpg" alt="Expired Kodak bwc (C41 process BNW) 200 shot at ISO 100" /> <img src="/assets/media/Scan 1-2.jpg" alt="Expired Kodak bwc (C41 process BNW) 200 shot at ISO 100" /> <img src="/assets/media/Scan 2-2.jpg" alt="Expired Kodak bwc (C41 process BNW) 200 shot at ISO 100" /> <img src="/assets/media/Scan 4-2.jpg" alt="Expired Kodak bwc (C41 process BNW) 200 shot at ISO 100" /> <p>All shot on a Yashica FX-D, with a 28mm or 50mm lens.</p> <p>Apologies for the crummy scans. Working on that...</p> How To Keep API Calls Decoupled From React Components Adrian Unger Sun, 08 Oct 2017 00:00:00 -0700 <p>Keeping API calls decoupled from React components keeps your views pure and without side-effects! This makes it easier to test your views without having to worry about API calls. Your API and views can be tested separately.</p> <div class="note">I no longer agree with any of this! It was a nice thought experiment but, it's downfall is that Components become less encapsulated. I'd advocate that if you want to test a Component without worrying about API data, then break the Component in two: 1. Getting API data, and the child 2. Rendering some data. July 2, 2018</div> <h2>Some Assumptions</h2> <p>Before I dive any deeper, here are some assumptions: You are using React to render your views (HTML). You are using Redux, or something similar to handle state. Myself, I actually use Preact and Atom instead of React and Redux -- They are both smaller and their source codes are easier (for me) to understand, which I favour. That said, the APIs are basically the same. And, I'm also assuming you rely heavily on Redux/Atom and not on React's <code>setState</code> method.</p> <h2>Moving Request Logic Outside Of Your Views</h2> <p>If you're coming from MVC, then you know your views should be simple and without complex logic. Instead, you would use the Controller to load remote data and do any normalization. But where is the Controller when using React and Redux? There isn't <em>really</em> a controller, but that logic can be moved into <em>subscribers</em>.</p> <h3>What's a 'subscriber'?</h3> <p>With Redux (or Atom), your store reference has a <code>subscribe</code> method. See <a href="">Redux docs</a> and <a href="">Atom</a>. I lack any experience with MobX, but it looks like you can get a similar result with just an Observable or a Spy, <a href="">see doc</a> (Anyone with more experience, please chime in!). <em>This</em> is where we can write our Controller-<em>esque</em> logic.</p> <h2>A Blog Example</h2> <p>So, let's say you have a blog page, and it loads a Parent or Page Component like <code>&lt;BlogPage ... /&gt;</code>, and you need to load some featured posts. Usually, this is where I would add a <code>componentDidMount</code> call to <code>BlogPage</code> and make an API request for some featured posts. But, like I said, this couples request logic with the view, which we don't want to do.</p> <p>Thankfully, we <a href="">store the current URL</a> in our Atom store. If you don't, then you should!</p> <p>Now, <code>subscribe</code> is fired when any part of your state tree has changed. And, it does not tell you which part changed! So, we can use the following utility function to watch a specific path on our state tree and only invoke our callback if the value at that path has changed. (<em>Note: the next version of Atom will include this <code>watchStore</code> type API</em>)</p> <pre><code class="language-javascript">import check from 'check-arg-types' import pathOr from 'ramda/src/pathOr' import merge from 'ramda/src/merge' import {subscribe, getState} from './store' // this is your local store file const toType = check.prototype.toType // Cache previous value for given path to allow easy diffing in the listener. const cache = {} // Small wrapper around adding a subscriber to your store, that only calls the // given cb if the value at the given path has changed since the last time // the cb was invoked. export default function watchStore (path, cb) { const key = path.join(',') const getPathVal = pathOr(undefined, path) const diff = () =&gt; { const newVal = getPathVal(getState()) if (newVal === undefined) { return } const oldVal = cache[key] if (oldVal === undefined || oldVal !== newVal) { if (toType(newVal) === 'object') { cache[key] = merge({}, newVal) } else { cache[key] = newVal } cb(newVal, oldVal) } } // Let's invoke diff right away as we may want to react to // our initialState set in our store. diff() subscribe(diff) }</code></pre> <p>Ok, now we have the helpful wrapper around our store's <code>subscribe</code> method. It let's us target a specific path on our state tree, and only invoke our given callback function when the value at that path actually changes.</p> <p>Again, with the assumption that we keep the current URL up-to-date in our state tree, we can subscribe to changes like so:</p> <pre><code class="language-javascript">watchStore(['url'], function loadData (url) { switch (url) { case '/blog': // Load blog posts from our API loadBlogPosts().then(...) break } })</code></pre> <p>I'm not going to cover the actual API request here. But, let's assume we get some array back from the API server and we're ready to store that in our state tree, we can fill in that <code>loadBlogPosts</code> promise callback with something like:</p> <pre><code class="language-javascript">import {set} from 'atom-lens-reducer' ... loadBlogPosts.then((posts) =&gt; dispatch(set('blogPosts', posts)))</code></pre> <p>Now when we update our state tree (and we already have a component connected to our Redux/Atom store) then we can pass that <code>blogPosts</code> value down to our <code>&lt;BlogPage posts={props.blogPosts} /&gt;</code> Component!</p> <h2>Another Example</h2> <p>Maybe your app as an API-backed autocomplete/type-ahead input field. You want to load and present results <em>as the user types</em>. Well, first you need to make sure you are storing your form values in your state tree. At this point, let's assume you are already syncing the input value of your autocomplete form in your Redux/Atom store.</p> <pre><code class="language-javascript">&lt;Form name='globalSearch'&gt; &lt;MySyncedInput name='input' ... /&gt; &lt;/Form&gt;</code></pre> <p>And this input field is synced to your state tree as:</p> <pre><code class="language-javascript">{ forms: { globalSearch: { input: '&lt;User Input Here!&gt;' } } }</code></pre> <p>Then all we have to do is use that <code>watchStore</code> utility above to listen for when this input value changes and perform our API logic then:</p> <pre><code class="language-javascript">watchStore(['forms', 'globalSearch', 'input'], (input) =&gt; { // only perform request if input is 3 or more characters if (input &amp;&amp; input.length &gt; 2) { // Make API request } // You could also debounce or throttle this call to reduce hits on your API })</code></pre> <p>No we don't have any of that logic in our hypothetical <code>&lt;GlobalSearchForm /&gt;</code> Component! It's just a simple function that accepts props and returns HTML! This should help with complexity by keeping your view (Components) simple, and your <em>&quot;Controller&quot;</em> logic separate. This makes writing tests much easier as you don't have to mock or intercept the API calls. Instead, you can directly pass in mocked API response data as props on a given Component.</p> <p>This is part of an evolving pattern of how I write React applications. I aim to write a document about the organization and libraries used. But, I felt this pattern (of keeping API calls separate from Components) was worthy of a more in-depth explanation -- I hope it helps reduce complexity in your React apps!</p> Where'd the Boardwalk Go? Adrian Unger Wed, 04 Oct 2017 00:00:00 -0700 <p>This past weekend we hiked  a similar route that we did about the same time last year. Only, we found one of our favourite sections had changed.</p> <p>Last year:</p> <img src="/assets/media/2016-02-06-13.20.56.jpg" alt="Forest Bathing Photo By Adrian Unger" /> <p>&nbsp;</p> <p>This year:</p> <img src="/assets/media/2017-01-29-12.25.11-1.jpg" /> So Long Cyanogen OS Adrian Unger Fri, 20 Jan 2017 00:00:00 -0800 <p>Almost exactly 2 years ago today, I bought a OnePlus One. It was (and is) my first real smartphone after spending $100 on the <a href="" target="_blank">Geeksphone Peak</a>. And, I have next to no qualms with it. </p> <p>I don't know the specs off the top of my head, but it's more than capable for my needs. I watch videos, edit photos (even raw), read, and play Sodoku. I don't need it to be thinner, or lighter. The battery easily lasts a whole day. The screen and camera are good enough.</p> <p>Basically, it's a perfectly fine smartphone.</p> <p>The OS is also just peachy. My iOS experience is limited to the first (maybe 2nd?) iPad and the occasional use of my partners' iOS 10 device. In comparison to iOS, the main issue I had with Android was the app drawer and launcher. iOS was just so much nicer. Why would I want apps to appear in the drawer and on my home screen? Then, <a href="">Evie</a> came out. And it's the best.</p> <p>The Cyanogen OS flavour of Android was nice as well. It added some nice enough customizations, privacy and security features, and well... I'm not sure what else was unique to Cyanogen, or part of stock Android. Over-the-air updates <em>were </em>the best. They just happened, and never broke anything.</p> <p>Actually, I've only ever had one hiccup with my phone where the GPS stopped working. This happened after Pokemon Go came out &gt;:(. A factory reset, and everything's back to working.</p> <p>More recently, OTA updates did try to bundle some Microsoft apps with the updates... Then the updates stopped.</p> <p><a href="">CyanogenOS has shutdown.</a></p> <p>Well. That sucks.</p> <p>Now I need to find a new Android &quot;ROM&quot;. Hopefully one that has official builds for my OnePlus One (nicknamed &quot;Bacon&quot; apparently), and ideally also includes OTA updates. Though, it kind of feels like a complete gamble. I wonder how long I can wait, until I need to worry about security vulernabilities?</p> <p>Anyway, I guess this is goodbye.</p> Creating Reusable View Components for Android Adrian Unger Thu, 14 Jul 2016 00:00:00 -0700 <p>View components are all the rage in the frontend JavaScript world, with React popularizing their usage. Many <a href="">other frameworks</a> have added APIs to support this pattern as well. I've been pretty happy to adopt view components for web developement, so when I started learning Android, I quickly found myself wanting to breakdown my UI into components. </p> <p>Android/Java is full-OOP so it really just comes down to extending a built-in ViewGroup class and defining an XML layout.</p> <p>Here's an example of a stacked date view that could appear as a child of several different parent views. </p> <h2>First define the layout</h2> <pre><code class="language-xml">&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;merge xmlns:android="" android:layout_width="wrap_content" android:layout_height="wrap_content" &gt; &lt;TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Apr" android:textAllCaps="true" /&gt; &lt;TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="-4dp" android:text="24" android:textSize="24sp"/&gt; &lt;/merge&gt;</code></pre> <p>Simple enough. </p> <p>Note: the <code>&lt;merge&gt;</code> tag avoids redundant view groups within your layouts &mdash; check out the <a href="">official docs</a> for more information. </p> <p>It's not apparent by just looking at the XML, but our component will be contained in a <code>LinearLayout</code>, that comes with our Java class.</p> <h2>Extend a view group</h2> <p>Create a new class that extends one of the built-in view groups. In this case, we'll extend the <code>LinearLayout</code>.</p> <pre><code class="language-java">// imports have been left out public class StackedDateView extends LinearLayout { private TextView month; private TextView day; public StackedDateView(Context context, AttributeSet attrs) { super(context, attrs); setOrientation(LinearLayout.VERTICAL); setGravity(Gravity.CENTER_HORIZONTAL); LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.view_stacked_date, this, true); month = (TextView) getChildAt(0); day = (TextView) getChildAt(1); } public void setDate(Date date) { SimpleDateFormat monthFormat = new SimpleDateFormat("MMM", Locale.US); month.setText(monthFormat.format(date)); SimpleDateFormat dayFormat = new SimpleDateFormat("dd", Locale.US); day.setText(dayFormat.format(date)); } }</code></pre> <p>It's a super basic view component, but it still saves me from repeating code unnecessarily. All it takes to embed in one of your layouts is:</p> <pre><code class="language-xml">&lt; android:id="@+id/stacked_date" android:layout_width="wrap_content" android:layout_height="wrap_content" /&gt;</code></pre> <p>And to populate the TextViews from your Java:</p> <pre><code class="language-java">StackedDateView stackedDate = (StackedDateView) root.findViewById(; stackedDate.setDate(someModel.getPublishedDate());</code></pre> <p>That's it.</p> AngularJS Separation of Concerns Adrian Unger Mon, 28 Sep 2015 00:00:00 -0700 <p>Recently, I was added to a couple of existing AngularJS projects to implement new features and refactor some existing ones. Specifically, the goal of refactoring was to move logic from places where it was not needed and to ensure everything was <a href="">DRY</a>.</p> <p>After reading through much of the official documentation and discussing with the original developers who started these projects, it was still not 100% clear how code should be separated and when each AngularJS concept should be used.</p> <p>Reading more developer blogs on the topic, the separation of concerns starts becoming more clear. But, to better understand each concept -- and to hopefully be able to succinctly convey to others -- I wanted to be able to explain the use for each AngularJS concept with a single terse sentence.</p> <p>Here is what I came up with:</p> <ul> <li>Directives should be used when you need to manipulate the DOM.</li> <li>Controllers define your 2-way databinding models.</li> <li>Services are for everything else ;) Rather, Services are for sharing domain logic and state.</li> </ul> <h3>Don't hesitate to create a new Service</h3> <p>The main issues I encountered when working on these existing AngularJS projects was reasoning about inheritance, the source of event triggers, and passing state between controllers. I found all of those issues could be solved by introducing a new Service and in some cases, expanding an existing one.</p> <p><code>$scope</code> should be explicit. Exposing an inhertited $scope method to your templates is not as easy to reason about than:</p> <pre><code class="language-javascript">$scope.someFunc = MyService.sharedMethod;</code></pre> <p>Services can also be used to hold state instead of relying on various <code>emit</code> or <code>broadcast</code> events. And, again, is much easier to reason about that a property defined on <code>$rootScope</code>.</p> <p>If you have a Service that is the single source of truth for some state object, you do not need to worry about inheriting some <code>$rootScope</code> property or parent controller property.</p> <p>Regarding MVC, Services are not limited to defining Models to persist data and hold the most up-to-date reference of that data. You can also have services for representing volatile state, file uploads, API wrappers etc.</p> <p>Having plenty of focused, moderately sized Services is definitely better than having fat Controllers.</p> <p>Anyway, my AngularJS experience is limited but this has been my takeaway so far. Here are some articles that helped form these views:</p> <ul> <li><a href="">AngularJS Sticky Notes Pt 1 – Architecture</a></li> <li><a href="">Rethinking AngularJS Controllers</a></li> <li><a href="">AngularJS Style Guide</a></li> </ul> Authenticating LoopBack AngularJS SDK Routes Adrian Unger Wed, 12 Aug 2015 00:00:00 -0700 <p>I am currently working on writing an API backend with <a href="">LoopBack</a>. This API is a rewrite of an older Python codebase which has an AngularJS client. While the backend needs improving the current AngularJS client is holding up just fine.</p> <p><a href="">ExpressJS</a> was the front-runner since the whole team has experience with it.But, when I came across LoopBack, which is a framework built on top of ExpressJS for quickly creating dynamic REST APIs, it seemed like a perfect fit. Not only do you get the time saving JSON based API generation, but also the <a href="">LoopBack AngularJS SDK</a>.</p> <p>The AngularJS SDK will auto-generate AngularJS services for accessing your models and remote methods on the server. It's basically an ORM for your RESTful API endpoints. And, you can always keep it up-to-date by re-running the CLI tool or by using the Grunt build plugin.</p> <p>Taking the <a href="">example code</a> for an authentication service you can see how easy it is to use the generated <code>lb-services</code> for accessing your API endpoints. After calling <code>login</code> the generated services will also handle passing the access token for subsequent calls to your resources.</p> <h3>But, what I could not find in the documentation was how to manage an active user session on the client.</h3> <p>Following the <a href="">LoopBack AngularJS Example</a> I was using <code>angular-ui-router</code>. Without a clear example of authenticating routes with LoopBack and AngularJS I instead looked for a general example for <code>angular-ui-router</code>.</p> <p>I found this great article by Jorge Silva: <a href="">Angular.js Authentication with UI-Router</a>. This succinctly covers requiring a logged in user for specific angular.ui routes. Which is great, since I am new to Angular and angular-ui-router.</p> <p>Wait! If the <code>lb-services</code> are handling passing the access token for each subsequent call, they must be storing that data somewhere!</p> <p>Indeed. Inspecting my generated <code>lb-services</code> source, I found a <code>LoopBackAuth</code> service that would load the <code>localStorage</code> values onto itself. Easy enough. In your custom <a href="">authentication service</a> you can inject the <code>LoopBackAuth</code> service and write a simple helper:</p> <pre><code class="language-javascript">// client/js/services/auth.js function isLoggedIn() { return LoopBackAuth.currentUserId &amp;&amp; LoopBackAuth.accessTokenId; }</code></pre> <p>Then, the <code>authenticated</code> promise <a href="">outlined by Jorge Silva</a> can be adapted:</p> <pre><code class="language-javascript">// client/js/site.js var authenticated = ['$q', 'AuthService', function ($q, AuthService) { var dfd = $q.defer(); if (AuthService.isLoggedIn()) { dfd.resolve(); } else { dfd.reject('Not logged in.'); } return dfd.promise; }];</code></pre> <p>Well, that's working for now. I am quite new to Angular and actually have yet to read any of the official documentation. So ... I could be way off on this one ;)</p> Thoughts On React And Mithril Adrian Unger Thu, 28 May 2015 00:00:00 -0700 <p>I've been using Facebook's React for most new UI components in recent months at Ecquire. And, only more recently, using Mithril.</p> <p>They are both awesome for one very simple reason: No templates. Especially with how small I break out my UI components. I don't like having a file with 3 lines of HTML with Mustaches thrown in.</p> <p>Both, Mithril and React, use Virtual DOMs to avoid the need for separate template files. Other than that, they really are not that similar.</p> <p>Lately, I have been enjoying rigidness over flexibility in my coding. I have found it forces specific functionality over generalizing. Which I have found easier to test and maintain. Because of this, I am biased towards choosing Mithril instead of React.</p> <p>React is quite flexible. It has about twice as many API methods as Mithril. This is necessary since React rather you not reference the DOM directly. Instead, you rely on Lifecycle Methods, which inform on the state of the DOM and when to perform operations at the <em>correct time</em>.</p> <p>Mithril is not flexible. You render a Virtual DOM and you assign event handlers. Mithril offers a few nice methods for common patterns (HTTP Requests, Promises, Getter-Setters etc.).</p> <p>If you were to migrate React components to Mithril, your main entry point for converting React's Lifecycle methods is the non-HTML-standard attribute called config: <em>&quot;This special parameter allows you to call methods on the DOM element after it gets created.&quot;</em></p> <p>If you process data that should affect the UI/DOM you need to rely on Mithril's built-in helpers, or you will have to <a href="" target="_blank">manually tell Mithril to compute changes</a>. Because Mithril does less to abstract away how it computes changes and renders a Virtual DOM you are forced to learn a bit more of the Mithril source and how it actually works.</p> <p>Good thing the source is tiny. About 5kb gzipped. This is a stark contrast to React. The logic is really not that hard to follow, and stack traces (in my anecdotal experience) were tiny and easy to follow compared with monstrous and dense with React.</p> <p>There are pros and cons to either. For me, debugging and maintenance are the worst part of programming and with Mithril I was able to reduce friction for both.</p> GMOs Are Neither Good Nor Bad Adrian Unger Sat, 02 May 2015 00:00:00 -0700 <p>I came across <a href="">How I Got Converted to GMOs</a> via <a href=""></a>. This is a response to that article. </p> <p>I think it’s wrong to assume this is black and white or that the issue of GMOs is the centre of feeding the world. There are actual issues threatening how we feed everyone going forward. The UN has pretty much got <a href="">it</a> <a href="">covered</a>. But agriculture is not the same the world over, issues in America may not translate to Brazil. </p> <p>I think we all need to be honest and not conflate the issues that differ around the world with each other. So, looking at America, many questions can be asked:</p> <ul> <li>How many GMO crops are even grown for human consumption?</li> <li>How many monocultures exist to feed cows? For ethanol? For tobacco? For alchohol?</li> <li>Why is 30% of all food grown, wasted? (Jones, 2004 cited in Lundqvist et al., 2008)</li> <li>How can we control pest issues?</li> <li>How can we have crops adapt with climate change?</li> <li>How can we prevent (even reverse) soil degradation?</li> <li>How can we prevent (even reverse) nitrogen run-off?</li> </ul> <p>My point is, instead of trying to determine if A) GMOs are good, or B) GMOs are bad, we should instead be looking at actual issues of agriculture. GMOs may end up being part of the problem, or part of the solution but on either side, GMOs will not be the sole culprit. </p> <p>Converting crops to GMOs does not address all of the issues I mentioned above. And neither does converting all GM crops to non-GM. </p> <p>This conversation is a waste of time when it comes to actually fixing issues of agriculture. Specific to GMOs, sure, let’s talk about labeling which is a social/political issue. Or, let’s talk about the <a href="">precautionary principle</a> which has specific implications with GMOs. </p> <p>But there is way too much variability in the usage of GMOs to argue, at a global scale, that GMOs are good or bad. For instance, having a GM eggplant benefit a farmer in Bangladesh does not give a pass to GM rapeseed in Europe. Those are two separate cases with their own implications.</p> <blockquote> <p>Oilseed rape can be described as a high-risk crop for crop-to-crop gene flow and from crop to wild relatives.</p> </blockquote> <p>(From Genetically modified organisms (GMOs): The significance of gene flow through pollen transfer) </p> <p>That issue of cross-pollination and biodiversity is specific to GM rapeseed and does not conclude much in terms of eggplants in Bangladesh. </p> <p>I think we need to stop arguing GMOs and start addressing the actual problems facing agriculture around the world. There is and will continue to be a wide array of causes to those problems and probably even more solutions to consider.</p> Roy Choi On Tradition Adrian Unger Wed, 22 Oct 2014 00:00:00 -0700 <p>Roy Choi is a pioneer of awesome street food with the Kogi, korean taco, truck.</p> <blockquote> <p>nothing is authentic and tradition is in the human mind. we can create new ones as long as we are honest. I think it's more important to challenge authenticity and tradition than accepting them if they are old and irrelevant. </p> </blockquote> How We Work Remotely At Ecquire Adrian Unger Tue, 14 Oct 2014 00:00:00 -0700 <p>This is how Ecquire caught my attention ~2 years ago. Then, just a couple weeks after joining, I got to meet the team at <a title="Nerds on vacation" href="">Nerds on Vacation</a> in Tofino, BC. Which was awesome.</p> My First East Coast Fall Adrian Unger Mon, 06 Oct 2014 00:00:00 -0700 <img class="aligncenter wp-image-291 size-large" src="" alt="Processed with VSCOcam with n3 preset" /> <p>Still not used to being on the east coast but I am starting to appreciate the more subtle beauty.</p> Veganism Adrian Unger Mon, 31 Mar 2014 00:00:00 -0700 <p class="big">Veganism is not the end-all answer to a better world. <a href="">Leo Babauta</a> is <a href="" target="_blank">claiming that is is</a>. To me, Leo is still reacting. He is presenting Veganism as the only answer to numerous injustices. And yet, closing his eyes to other perspectives.</p> <p>I'd like to highlight that Veganism does not take in account all injustices and can potentially perpetuate some.</p> <p><strong>Agriculture is complex</strong></p> <p>Food, one of life's staples, has been left in the hands of the few. This results in large, industrial, mono-cultures to make up for the quantity needed to feed the world. Moreover, <a href="" target="_blank">nearly one-third of all food produced is needlessly wasted</a>. This falsifies the apparent need to increase production and convert more acreage into industrial farming practices.</p> <p>Agriculture is one of the leaders in emissions and other pollutants. Considering just food waste, the global carbon footprint is <a href="" target="_blank">twice that from the US transport sector</a>.</p> <p>The majority of Almonds and soft-fruit trees in California require bees to be trucked in from around the country. Because these almond farms are so large, there is no natural habitat for bees to live year-round. One Washington-based company is responsible for <a href="" target="_blank">shipping 3.2 million bees each year.</a> Should Vegans give up Almonds?</p> <p>Declining bee populations are <a href="" target="_blank">threatening global agriculture</a>. But, to many vegans, <a href="" target="_blank">Honey is exploitation of bees</a>. So, how do we incentivize increasing bee populations if collecting and selling honey is unethical? Should we expect our already stressed food system and workers to take care of bee populations without increasing profit and wages?</p> <p>Overall, Veganism doesn't take into account sustainability. Not eating animals does not mean a more sustainable farming system as external inputs such as blood meal (often byproducts of the industrial animal system) and synthetic nitrogen fertilizers (mainly, petroleum based) are required to be brought into the farming system. There are different ways to close to loop, and allow food-producers to not rely on external inputs. One rather successful system is <a href="" target="_blank">Biodynamic Farming</a>, which often uses manure based fertilizers (produced on-site).</p> <p>I guess my point is, Veganism is not an end. You can be vegan and support monocultures, food waste and land degradation. Which could <a href="" target="_blank">lead to imperilling millions of human lives</a>.</p> <p><strong>Human well-being is often ignored</strong></p> <p>The majority of manual labour performed on farms is by migrant or temporary workers. <a href="" target="_blank">In the US</a>, &quot;migrant farmworkers still suffer mortality and morbidity rates greater than the vast majority of the American population, due in part to the combination of poverty, limited access to health care, and hazardous working conditions. Farmwork is listed as the second most dangerous occupation in the United States behind mining.&quot;</p> <p><a href="" target="_blank">In Canada</a>, &quot;The Canadian government insists that foreign agricultural workers are treated the same as Canadian workers but nothing can be further from the truth.&quot;</p> <p>Eating Vegan does not directly address the issue of human well-being.</p> <p><strong>Veganism is not the only answer</strong></p> <p>I am not attacking Veganism. I am attacking the view that non-Vegans need to be called out. There are so many shitty situations in our world. We can't expect everyone to rank each injustice in the world the same. I agree we all have a duty to educate, but <strong>never</strong> to inscribe how one should react to that knowledge.</p> Falling Water Adrian Unger Tue, 25 Feb 2014 00:00:00 -0800 <img class="aligncenter size-full wp-image-178" src="" alt="falling-water" /> Burnaby Lake Adrian Unger Sat, 22 Feb 2014 00:00:00 -0800 <img class="size-full wp-image-172" src="" alt="Burnaby lake" /> <img class="size-full wp-image-173" src="" alt="Burnaby lake" /> Sointula Adrian Unger Mon, 13 Jan 2014 00:00:00 -0800 <p class="big">My partner and I have been scouring British Columbia, checking out potential properties to purchase. While the truth of BC's high cost of land may have hit hard, the excuse to venture into previously looked-over corners of this truly beautiful province has been a thrill.</p> <p>More recently, we jumped on a ferry from Horseshoe Bay and hit the road nearing the northern end of Vancouver Island. From there, another ferry took us to Malcolm Island, home of Sointula – a Finnish founded, yet failed, utopia.</p> <img class="alignnone size-sd-full-image wp-image-47" src="" alt="2013-12-07 10.19" width="940" height="625" /> <p>The property we were checking out was a bust for us – but we quickly rebounded with the joy of exploring this fishing village.</p> <img class="alignleft size-sd-half-image wp-image-50" src="" alt="2013-12-07 12.01.36" width="470" height="470" /><img class="alignright size-sd-half-image wp-image-49" src="" alt="2013-12-07 11.57.42" width="470" height="470" /><img class="alignnone size-sd-full-image wp-image-48" src="" alt="2013-12-07 11.57.19" width="940" height="625" /> <p>Impressed by the south-western views towards Vancouver Island, we headed to see the eastern view over the Queen Charlotte strait. Before we even got the other other coast, we were aghast by Salal bushes, taller then ourselves!</p> <img class="alignnone size-sd-full-image wp-image-56" src="" alt="2013-12-07 15.51.42" width="940" height="625" /><img class="alignleft size-sd-half-image wp-image-55" src="" alt="2013-12-07 15.45.39" width="470" height="470" /><img class="alignright size-sd-half-image wp-image-63" src="" alt="2013-12-08 12.32.59" width="470" height="470" /> <p>The forest opened up to one of the most spectacular views I will likely ever have the privilege to enjoy. Zero clouds and just as the sun was setting we looked over the Queen Charlotte Straight towards the Coastal Mountains.</p> <img class="alignnone size-sd-full-image wp-image-58" src="" alt="2013-12-07 16.03.43" width="940" height="625" /> <img class="alignnone size-sd-full-image wp-image-60" src="" alt="2013-12-07 16.08.36" width="940" height="625" /> <img class="alignnone size-sd-full-image wp-image-61" src="" alt="2013-12-07 16.12.25" width="940" height="625" /> <p>A fine spot to attempt a utopia.</p> A birthday story from the 135 Adrian Unger Tue, 17 Dec 2013 00:00:00 -0800 <p class="big">Awhile back, I lived atop Burnaby Mountain. Often, I would take the 135 bus in and out of downtown Vancouver. One of these times, returning from downtown, I happened to sit next to a fellow who wished to share a story.</p> <p>The man overtly smelled of alcohol.</p> <p>Not sensing any danger, there was no reason for me not to hold a conversation. Though, the light banter quickly took a turn as he mentioned that it was his birthday and he's off to see his father for the first time in years.</p> <p>Trying to not get too personal, nor trigger any sour reaction, I politely reply &quot;I hope everything goes well with your dad.&quot; Perhaps sensing my timidness, he quickly acknowledges his sent and demeanour, &quot;My birthday is not a good day, I try to celebrate, but the memories are awful.&quot; Again, timidly I reply, &quot;it's good you're staying positive.&quot;</p> <p>&quot;When I was a kid my mom overdosed on my birthday.&quot;</p> <p>I tried not to react. With overbearing solemn, I looked him in the eyes and nodded. Trying to avoid words altogether I utter, &quot;that's tough.&quot;</p> <p>He goes through the details. How he remembers lights, sirens, police, his mom. He stays away from drugs because of it. But, openly admitting, &quot;I struggle as an alcoholic.&quot;</p> <p>His stop is up. I acknowledge his openness and wish him, with as much conviction as I can, a happy birthday.</p> The Value of a Web Developer Adrian Unger Fri, 26 Jul 2013 00:00:00 -0700 <p class="big">James Somers asks if coders are worth it. Well, I’d like to answer that.</p> <p>I may have missed the point of <a href="">the article</a>, but he seems to be drawing attention to web developers making <em>good</em> money while working for web startups, which aren’t serious business. Serious, in the sense that the problems being solved aren’t grand or meaningful enough.</p> <blockquote> <p>A lot of the stuff going on just isn’t very ambitious.</p> </blockquote> <p>Some web developers work on problems that fail, or don’t evoke passion. But their work shouldn’t affect the value of all web developers, or current salary trends. Well, unless you want it to.</p> <h2>A short story</h2> <p>A couple of years ago I had an empty feeling from the low valuation of the work I did. I <em>felt</em> I had this practical skill – which by the way, I did sit in the basement of my moms house to foster – where I was paid good money (especially for a community-college dropout) to do some stuff for people or businesses that I did not gain satisfaction from. Which left only the monetary gains as the measurement of value of what I was doing.</p> <p>So I did something about it. I reached a point as a freelancer that I could start turning down work. And instead of evaluating potential work based on the monetary gains, I started seeking out companies that were doing something big—something that I could garner some passion for.</p> <p>And, we’re not all passionate about the same things. So we can’t really evaluate the whole industry of web developers without knowing what each person is passionate about.</p> <h2>What are you passionate about?</h2> <p>For me, I have a few passions, though they all potentially stem from the same source. But, what’s interesting (for this blog post at least) is that web development is not one of them. For me, web development is a practical skill — one that I enjoy immensely and still do as an unpaid hobby outside of my job, but not a passion.</p> <p>I needed to satisfy my empty feeling. And that’s what I set out to do. I picked clients and industries more aligned to my passions. James Somers (at least the one portrayed in the article), could do the same. He could take his practical skill as a web developer and apply it to businesses and clients in the writing/publishing industry. It’s not a perfect solution, but things take time, and incremental improvements should not be overlooked.</p> <p>As skilled web developers, you decide your own worth. Measure it how you want, just remember—like much of life—money isn’t everything.</p> Living and Working From a Trailer Adrian Unger Tue, 12 Feb 2013 00:00:00 -0800 <p class="big">Last year, I spent 8 months living and working out of a 5th Wheeler. I did this with my partner, on a public farm, in the city of Vancouver. What were we thinking?!</p> <p>When the oppurtunity presented itself, we knew we had to take it. After moving every year for the past 5 years, ending up in a basement suite we couldn't afford, and growing ever more sick of <em>city life</em> the choice was clear. At least in retrospect.</p> <h2>A Unique Oppurtunity</h2> <p>Soon after taking a job at the <a href="">UBC Farm</a>, my partner came across another oppurtinty there: being a caretaker. After some deliberation, we jumped on it, moving onto the farm and into the trailer in February of 2012. </p> <p>The farm has two caretaker couples, each in their own trailer. We purchased our trailer from the caretakers we were replacing. Caretaking, for those curious, means looking after buildings, irrigation, chickens, gates and locks.</p> <p>But, this ain't no trailer park, nor the side of the road. The farm provides city hookups for water, sewer, electricty and we even had access to Univercity WiFi. Even better, we had our own private growing space. And a pretty substantial front-yard.</p> <img src="" alt="Our front-yard at the UBC Farm" title="Our front-yard at the UBC Farm" /> <h2>Trailer Life</h2> <p>Again, take into account, this is <em>Trailer Life</em> on some of the most beautiful land in Vancouver. But, basically, this is how it breaks down:</p> <ul> <li>You feel like a giant for the first few days.</li> <li>Leaks are pretty much your biggest concern.</li> <li>Do your dishes! (otherwise there's no room to cook).</li> <li>Don't forget to empty the poop-tank!</li> <li>Only one person can be doing a <em>standing</em> activity.</li> <li>His and Her sinks?! Yeah right, morning routines are done in succession.</li> <li>Did I mention do you dishes? Oh, and tidy anything else, otherwise you have no surfaces.</li> </ul> <img src="" alt="Tiny Apartment" title="The inside of our 5th-wheeler" /> <p>Everything about living in a trailer (with city hookups) is just the same as living in an apartment. A tiny, tiny apartment.</p> <h2>Working in the Trailer</h2> <p>Honestly, there isn't anything interesting to say about this. The desk was a bit small. Sometimes I felt cornered in. But the view was absolutely fantastic. <em>I work well with a good view.</em></p> <img src="" alt="Trailer Workspace" title="Sometimes I sit, sometimes I stand" /> <h2>Looking back</h2> <p>We are lucky to have had such a unique living oppurtunity. Living in nature (even if the farm was a <em>popular</em> public place) within such close proximity to the city, was the perfect situation for us. And, the forced simplicity (re: downsizing) was liberating. I've always wanted my posssesions to be focused, and when two people share a closest (that doubles as storage and knive holder), you have no choice.</p> <img src="" alt="Our garden at UBC Farm" title="Our garden at its peak, even the weeds" /> <p>Even now, <a href="">sitting in Mexico</a> evaluating our next steps, I've realised how much that space to grow food meant to both of us. Though, we can't afford our farm yet, we know that we want <em>some</em> land of some size. And that's going to dictate or next location.</p> <p><em>View all photos on <a href="">Imgur</a></em></p> Freelance Rumination Adrian Unger Mon, 17 Dec 2012 00:00:00 -0800 <p class="big">I've been out of the freelance game since I joined Ecquire back in May. And, I feel I need to reflect on those years freelancing and see if I learned anything.</p> <p>I think it's better that I'm writing this 8 months after leaving the freelance business – there are a tonne of articles that get into the nitty-gritty details of freelancing – this way, I can offer a broad overview. And, since significant time has passed, whatever lessons I remember will <em>scientifically</em> be more important!</p> <h2>Communication</h2> <p>When anyone asks my advice regarding freelancing, this is it: Communicate. Often. effectively. And, be polite.</p> <p>A good portion of your clients won't know a thing about the web design (nor development) process. They may even try to fit their knowledge of their industry or domain onto you. So, you'll have to communicate. Every single step. And you'll need to be able to explain your decisions, obiously.</p> <p>Most of my clients were located in different parts of the world. And, besides the couple Skype calls I would have during the life of a project, all communication was via Email. When it comes to email, <em>brevity is best</em>. if your client wants more details, they'll ask. </p> <blockquote> <p>Hey John, I'm working on getting the Contact Page designed. Can we talk about the Gallery page closer to Friday?</p> <p>The infinte scrolling is taking longer than hoped, I'll let you know tomorrow where it's at.</p> <p>I'm going to start developing all of the page designs. I'm aiming to have the initial versions up next week. I'll update you this Friday on my progress.</p> </blockquote> <h2>Charge more</h2> <p>Ha. How young and stupid I was. Everytime I raised my rates (sometimes by 100%), I was met with acceptance. I've never lost a project from a high-rate. I've come down to a lower rate before, but still didn't lose the project. I clearly should have had higher rates much sooner than I did.</p> <h3>When should I raise my rates?</h3> <p>If you're ever turning down work, because you're too busy. Double your rate. If they turn you down, oh well, you're too busy anyway. If they're fine with it, you've got a hellish couple weeks coming up, but you just doubled your rate!</p> <h3>The thing about hourly rates</h3> <p>They don't reflect the value you provide. Yeah, I can go in and fix that crappy jQuery or WordPress plugin in under an hour. Is the value I'm providing worth my hourly rate? No.</p> <p>Think like a consultant. You aren't selling 3 hours of code-monkeying nor 2 days of design-monkeying! You are selling yourself (O boy) as a product. You have experience, opinions and knowledge that informs the code you spit out each hour. You are oncall to answer and educate your client on the work being done, and the problems being solved.</p> <h2>I guess that's it</h2> <p>Five years of freelancing. All I learned was I should charge more and that communicating effectively and often is key. <em>Whew</em>.</p> Well-Functioning Ecosystems Adrian Unger Sat, 06 Oct 2012 00:00:00 -0700 <p class="big">John Lius, a documentary film-maker and ecologist, has dedicated his life spreading the knowledge and successes of rehabilitating large-scale, damaged ecosystems.</p> <p><a href="">Watch the Green Gold Documentary</a> by John Lius on YouTube. Spoilers below.</p> <p>The results of the projects covered in the above documentary are astounding. People in Jordan, China, Ethiopia and elsewhere have turned baron, dry, desert-like landscapes back into lush, hydrated, functioning ecosystems. These rejuvenated lands, bring back native species of plants (and even leopards in the case of Ethiopia), as well as open the door to growing foods to <em>sustain</em> local communities.</p> <p>Interesting, as many of these baron lands were, at some point, fertile and used for agriculture. But, through unsustainable practices &mdash; mainly mismanaged water and over-grazing by livestock &mdash; these lands were devastated.</p> <p>John Lius' documentary contains a wealth of information, but I'd like to highlight three points. (if you don't like spoilers, watch the documentary first!)</p> <h2>First, Learn from the past mistakes of Agriculture</h2> <p>Like always, we can learn much from the past. Look at places like Jordan, Ethiopia and China. In each of these places, humans have lived for thousands of years. We can directly see the impact of poor agricultural practices on the land. And, how it negatively affects communities today.</p> <h2>Second, Desertified lands can be brought back to life</h2> <p>Hearing about droughts, pollution, starvation etc. one can feel hopeless. Have hope! John Lius has documented, what has been in my opinion, the most profound success humans have achieved in regards to ecosystems: <strong>Desertified lands can be brought back to lush, fertile, food-bearing, functioning ecosystems!</strong> If this does not astound you. Just repeat it in your head for a few hours.</p> <p>Even more astounding, the proven technique does not require modern agricultural technologies! So succinctly summarized by Ta Fuyuan:</p> <blockquote> <p>The objective was to give the hills a 'hat' on top, a 'belt' in the middle and 'shoes' at the bottom. The trees planted on top of the hill form the hat; The terraces form the belt; And, constructed dams at the bottom of the hills form the shoes.</p> </blockquote> <p>Once the hat, belt and shoes are in place, the once baron lands become fertile. The Hat, Belt &amp; Shoes trap rain, improving the hydrology. This prevents mudslides and loss of rainwater, and increases the ability to sustain food for local communities, presently and for future generations.</p> <h2>Third, Economies should be based on well-functioning Ecosystems</h2> <p>Finally, I can coherently explain why Canada's current economic stability makes me uncomfortable. Canada's economy, like many others, puts monetary value on the goods and services that <em>rely</em> on ecosystems. If an ecosystem shows signs of degradation or instability, this does not immediately affect the economies based on it. Only, once you reach a breaking point, and things collapse, do the economies reflect the reality of the failing ecological systems. At which point, rectifying the situation is magnitudes more difficult.</p> <p>John makes the argument that economies should be based <em>directly</em> on the ecosystems that feed the goods and services. This way, ecosystems could not be exploited (and depleted) for short but large monetary gains. Instead, economies would rely on <em>well-functioning ecosystems</em>, and would experience a more real-time association with them.</p> <p>Well, something to ponder at least.</p> Should We Care About Organic Adrian Unger Fri, 21 Sep 2012 00:00:00 -0700 <p class="big">Anna Bronnes recently <a href="" target="_blank">published on Ecosalon</a> a link-bait article&mdash;with good intentions&mdash;that unfortunately fell a bit flat.</p> <p>I entirely agree with the intention:</p> <blockquote> <p>In the modern age, if you are able to comfortably put food on the table, it is inexcusable to not think about what you are eating.</p> </blockquote> <p>But, the article lacks significant information to help those wishing to become more informed about the food system. It also, subtly, suggests devaluing what &quot;Organic&quot; labels actually represent.</p> <h2>Thinking About What You're Eating</h2> <p>Anna Brones brings up a good point that personal health is not the only reason to consider Organic&mdash;or to question your food in general:</p> <blockquote> <p>Food cannot be reduced to single elements. It’s not just about antioxidants or carbohydrates or omega 3s. Food is a process, a compilation of nutrition, environment and experience.</p> </blockquote> <p>So, what is it about? What should people be aware of? What are the impacts of food production, and how can I as a consumer influence those impacts? The issues surrounding producing and consuming food can be pretty comfortably categorized into the following (I'm ignoring health for now):</p> <ol> <li><strong>Social</strong> <em>How are the workers treated?</em></li> <li><strong>Environmental</strong> <em>Can the environment sustain the current production, and for how long?</em></li> <li><strong>Ethical</strong> <em>How are the Livestock treated?</em></li> <li><strong>Economical</strong> <em>How is the increased price distributed?</em></li> </ol> <p>But, if you're unaware of current negative issues surrounding food production, then it's easy to give each of those questions the benefit of the doubt. But, with <a href="">mistreated migrant workers</a> in British Columbia, monocultures and their <a href="">negative long-term yield variability</a>, Synthetic Nitrogen Fertilizers <a href="">actually depleting soil nitrogen levels</a> and how <a href="">rising energy costs affects food costs (PDF)</a>, there are plenty of issues to be concerned about.</p> <h2>Some Thoughts on Organic</h2> <p>I'm sure Anna means no harm, but when she says &quot;Slap the big O on anything and you’re sure to attract a certain demographic.&quot; Her tone and and adjective devalue the significance of Organic, and what small-to-medium farmers endure to gain that status. In Canada and the US, <a href="">gaining Organic Certification</a> is no simple matter, nor is it <a href="">cheap</a>. We, should respect that the Farmers' are being more thoughtful of how they choose to produce.</p> <p>Organic, is not a perfect indicator of a <em>good</em> product, but it is a highly controlled word, with many positive attributes associated with it. And, when you do reach that point when Organic is <em>not</em> a good enough indicator for <em>good</em> food you can look into bio-dynamic farming, permaculture, Fairtrade, Community-Support Agriculture etc.</p> <p>Really, there is just <em>so</em> much to learn when it comes to producing and consuming food! And, Anna is right:</p> <blockquote> <p>If we are going to move the food system forward, in a progressive and sustainable manner, we have to be asking the hard questions, and that takes more than just reading a headline.</p> </blockquote> <p>In response to: <a href="" target="_blank"><a href=""></a></a></p> Browser Testing as a Chrome Extension Adrian Unger Tue, 28 Aug 2012 00:00:00 -0700 <p class="big">Running Jasmine tests against a webpage? Forget headless-browser testing. Why not inject your tests right into the actual web page?</p> <p>At <a href="//">Ecquire</a>, we integrate with a bunch of services to move your data where it needs to be. Testing our code, to assert that we can properly identify data from different websites (Gmail, LinkedIn, Twitter, Facebook etc.), is something we'd like to do easily and frequently.</p> <p>I spent a day researching (re: playing) with different JavaScript testing libraries and Headless Browsers. I ended up trying <a href="">Zombie.js</a> and <a href="">Casper.js</a> (ontop of <a href="">Phantom.js</a>). I found Zombie to be the most pleasant, mainly because of it's documentation&mdash;it's organized as a linear storyline of a zombie attack! </p> <p>Sigh, but headless browsers were a dead-end, as our code needs to run on pages only accesible after logging in (I found captcha's hard to automate against) and JavaScript wasn't being executed as expected. </p> <p><strong>Duh!</strong></p> <p><em>Ecquire is a chrome extension</em>. Webkit based headless-browsers would render <em>close</em> at best. Oh, right. And we already have a Jasmine Test suite in a Chrome Extension (though, those ones didn't inject themselves into the active tab).</p> <h2>Cool, let's inject some tests!</h2> <p>The following will assume you are familiar with <a href="">Jasmine</a> and JavaScript in general. If anything is unclear, let me know, and I'll try to clarify.</p> <p>The biggest barrier for this to work, was the <em title="the output of our test results">reporting</em>. I didn't want to spend much more time on this, so I did some <a href="//">DDGing</a> and found this awesome <a href="">Console Reporter</a>.</p> <div class="note">Note: turns out Jasmine comes with its own Console Reporter inspired by Larry Myers. I prefer Larry's output style, so I stuck with it.</div> <p>Great. The unfamiliar territory is taken care of. Now, to write the Chrome Extension code that actually runs the Jasmine specs inside an active tab, outputting the test results to console!</p> <h3>The Chrome Extension</h3> <p>I wrote a boilerplate Chrome Extension, for <a href="">Browser Testing with Jasmine</a>. Here's an explanation of what it does:</p> <h4>1. Whitelist some domains, where we want our tests to run. For Ecquire, that meant,, etc.</h4> <p>Google is cracking down on security when it comes the Chrome Web Store. Which means we have to whitelist <em>everything</em> in our <dfn title="Every chrome extension needs one, it declares unique attributes about the app or extension.">manifest.json</dfn> file. Since our test extension won't be public-facing, we can be overly zealous in our whitelisting:</p> <pre><code>"permissions": [ "https://*/", "http://*/", "tabs" ],</code></pre> <h4>2. Declare some <a href="">content scripts</a> that will be executed within active tabs on those domains.</h4> <p>In <em>manifest.json</em> again:</p> <pre><code>"content_scripts" : [ { "matches" : [ // Again, overly zealous in our whitelisting ;) "http://*/*", "https://*/*" ], "js" : [ // The JS libraries we want available in an active tab "jasmine/lib/jasmine.js", "jasmine/lib/jasmine.console_reporter.js", "plugins/jquery-1.7.2.min.js", "myapp/some_dir/code_that_needs_testing.js" ], "run_at" : "document_idle", "all_frames" : false } ],</code></pre> <h4>3. Check if we're on a good domain, to show our <a href="">page action</a> button</h4> <p>First up, we'll need to declare a script file to run in the background, in <em>manifest.json</em>:</p> <pre><code>"background": { "scripts": ["background.js"] },</code></pre> <p>In <em>background.js</em> we'll listen for any changes to the URL of any tab.</p> <pre><code>chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { if (tab.url.indexOf('google') &gt; -1) { // ... show the page action.; } });</code></pre> <h4>4. When are page action is clicked, run some Jasmine specs!</h4> <p>We'll also need an event listener for when the page action is clicked. Check out the full <a href="">background.js</a> file to see that (it's the same idea as jQuery click events). When clicked, we'll inject our Jasmine specs and run them via Chrome's Extension APIs:</p> <pre><code>// load our spec file chrome.tabs.executeScript(, { file: 'my_spec_file.js' }, function() { // once loaded, run jasmine chrome.tabs.executeScript(, { file: 'path_to/JasmineSpecRunner.js' }); });</code></pre> <p><em>That's it!</em></p> <p>With this, you can now test your JavaScript against <em>real</em> webpages, and actually see the output. It's not as automated as using headless browsers, but it's pretty much 1-click. And, I'm guessing the same idea can be brought to Safari and FireFox extensions.</p> <p>Though the use-case for this is pretty specific to Ecquire, I thought it was pretty neat&mdash;and worth sharing. Would love to hear any reactions or thoughts on this!</p> Nerds on Vacation Adrian Unger Fri, 20 Jul 2012 00:00:00 -0700 <p class="big">I spent 10 days this July, in the breathtaking town of <a href="">Tofino</a>, surfing and writing code as part of <a href="//" target="_blank">Nerds on Vacation</a>.</p> <p>It was an entirely positive experience, filled with practical lessons for startups, people who work from laptops, and remote teams. My teammate <a href="">Tal</a> wrote about <a href="">The Nerds on Vacation Effect</a>, which basically describes the huge boost in productivity, even when surfing almost every day.</p> <h2>Increased Productivity</h2> <p><a href="//">Tal</a> talks a bit about the <em>why</em> behind The Nerds on Vacation Effect. And, I agree with him. Being totally relaxed in an idyllic setting allowed me to push out 6 hours worth of code in 4. Well, I can't prove that. But, it certainly felt as though I was accomplishing more in less time. Maybe seeing the <a href="">beautiful weather from our &quot;office&quot;</a> and checking the <a href="">surf report</a> in the morning was enough to push me to work more efficiently.</p> <p>In any case, I'm hoping to test this effect a few more times. Maybe it won't be surfing, but some other idyllic setting like being close to hiking, biking, yoga or a nice knitting chair. Whatever takes 100% focus away from work.</p> <h2>Remote teams</h2> <p>There's lots of <a href="">talk</a> around about if <a href="">remote teams</a> work or not. I'm most definitely a proponent. I don't like cities, buildings or concrete. Even though I love technology and building for the web, I much prefer being near nature and in small towns. And, I think there are plenty of <a href="">successful companies</a> out there, proving remote works. </p> <p>For a remote team to <em>actually</em> work, I think they need stronger ties than a team sharing office space. The <em>culture</em> needs to be strong and present for every team member. And, those people, though living in different areas of the world, need to be able to get along when they all get together and share a house for a couple weeks!</p> <p>If you're running a remote team, I highly recommend you take a look at getting those Nerds on Vacation! Once, twice or four times a year. Whatever, just get some facetime. </p> <h2>Laptops == Lifestyle</h2> <p>Being able to work from a Laptop is a fact that I am immensly grateful for. Most of the world is now connected via WiFi, even <a href="">off-the-grid huts</a>. Being a part of a remote team means I get to work and live in a trailer on a <a href="//">farm</a>, join the team in Tofino to work and surf, and eventually I'll try being a <a href="//">Digital Nomad</a>.</p> Comparing Responsive Image Proposals Adrian Unger Fri, 11 May 2012 00:00:00 -0700 <p class="big">Responsive Web Design has gained in usage and ease of use. But one, rather large, missing piece is responsive images.</p> <p>The <a href="">W3 Responsive Images Community Group</a> and <a href="">WHATWG</a> have been coming up with some ideas.</p> <p>I should preface this post as such: I very well may have a lack of understanding or even misunderstanding regarding HTML spec proposals. Also, If you're not aware of <a href="">Responsive Web Design</a>, this post won't be very interesting.</p> <h2>Two Proposals</h2> <p>As it stands there are two (popular) proposals for responsive images:</p> <pre><code>&lt;img src="face-600-200@1.jpeg" alt="" set="face-600-200@1.jpeg 600w 200h 1x, face-600-200@2.jpeg 600w 200h 2x, face-icon.png 200w 200h"&gt;</code></pre> <p>And:</p> <pre><code>&lt;picture alt=""&gt; &lt;source src="mobile.jpg" /&gt; &lt;source src="large.jpg" media="min-width: 600px" /&gt; &lt;source src="large_1.5x-res.jpg" media="min-width: 600px, min-device-pixel-ratio: 1.5" /&gt; &lt;img src="mobile.jpg" /&gt; &lt;/picture&gt;</code></pre> <p>With many voicing their preference for the latter. Mainly because:</p> <ul> <li>It's easier to read</li> <li>It's familiar (see the <code>&lt;video&gt;</code> and <code>&lt;source&gt;</code> tags)</li> </ul> <p>And, I agree. But, <a href="!/zcorpan">Simon Pieters</a> brings up some points on the <a href="">complexity of implementation</a>. In brief, the <code>srcset</code> attribute is easier to implement in the spec, and would avoid some pitfalls that the <code>&lt;picture&gt;</code> element would create for JavaScript developers. Read the above link for details.</p> <h2>Pick one</h2> <p>So, besides smaller semantic grievances, the main argument for each proposal is: </p> <ul> <li><code>&lt;picture&gt;</code> is easier to read and write as a developer</li> <li>The <code>srcset</code> attribute is easier to implement and would have less pitfalls for JavaScript developers</li> </ul> <p>This is no easy decision. And, in getting a little more familiar with the community discussions regarding new HTML specs, I have gained much respect for those involved.</p> <p>In the end, I still feel the <code>&lt;picture&gt;</code> element fits better along the existing HTML spec and elements. If <code>srcset</code> were to be implemented it would really be crossing a new boundary in terms of power given to an attribute&mdash;which I don't currently support. <code>&lt;picture&gt;</code> would take longer to implement, and JavaScript developers would have to be wary of creating bugs. But, I think friendly and familiar syntax outweighs both of those issues.</p> jQuery Multiline Text Input Adrian Unger Fri, 20 Apr 2012 00:00:00 -0700 <p class="big">A jQuery plugin that replaces a textarea with multiple text inputs, automatically appending a new input when the last is filled with content.</p> <p>I created this plugin as part of a provisionary coding task for an unnamed startup. The idea is to simplify server-side processing while having more control of the user input&mdash;And, hopefully, a better experience for the user. </p> <img src="" alt="Screenshot of a similar implementation by Google" title="Inspired by Google Profiles" /> <h2>The Concept</h2> <p>Instead of asking a user to enter each item (Ex. Places lived) on a new line or comma separating them within a textarea, each item is entered in an individual text input. But, we want to allow an indefinite amount of items and asking the user to click a button (to add another input) would be annoying. So, the plugin intelligently appends an empty text input as soon as the user starts typing the last input.</p> <p><em>But, how do you know how many inputs need to be processed on the server-side?</em></p> <p>Good question! This is handled by the plugin on the client-side; the plugin combines all of the single line inputs back into a single textarea, with each item on a newline, before submitting the data.</p> <h2>Demonstration</h2> <p>Select the &quot;Result&quot; tab in the below JSFiddle frame to test-drive a basic example.</p> <iframe style="width: 100%; height: 380px" src="" allowfullscreen="allowfullscreen" frameborder="0"></iframe> <h2>Usage</h2> <p><code>$('textarea.multiline').multilineText();</code></p> <p>Check out the <a href="">GitHub Repo</a> for more information and to grab the code.</p> The Essence of Blogging Adrian Unger Fri, 23 Mar 2012 00:00:00 -0700 <p class="big">Inspired by Dustin Curtis' <a href="">Svbtle</a> and the subsequent open-source project <a href="">Obtvse</a>, I took a shot at simplifying the blogging interface.</p> <p>Actually, even before Dustin <a href="">unveiled</a> the interface for Svbtle, the concept had inspired me&mdash;with the three words <em>essence of blogging</em> I began to think about what that meant to me. I took it to mean two types of posts, those which are ones purely written word, and those that are commentary on some link. The main difference being that the latter would have its' title link to the external source.</p> <p>So, I started dreaming up an interface that began with a single text field.</p> <h2>A demo of sorts</h2> <p>To catch the wave of this <a href="">fiasco</a>, I tested out that interface concept today. So, the demo is limited to only the writing interface, though it can save the post data to <em>localStorage</em>.</p> <p>View the demo <a href="" target="_blank">here</a>.</p> <h3>Some notes:</h3> <ul> <li>I've only tested it on Chrome.</li> <li>Try pressing 'tab' or 'enter' ;)</li> <li>Try pasting a URL</li> <li>'DRAFT' and 'PUBLIC' do nothing</li> <li>type <code>clearPost()</code> in console to clear localStorage data</li> </ul> <h2>A bit of an explanation</h2> <p>Like I said, the goal is to accommodate two types of posts, purely written word, and <em>link posts</em>. The type of post is determined based on what you enter in the initial field (which is actually the title field). If a URL, we'll fetch the title from that page, and populate the title field with it (moving the URL to the link field). If the initial field is just text, we wait until you press <em>enter</em> or <em>tab</em> and generate a <em>local</em> permalink for the post.</p> <p>In both cases, once the initial field is dealt with, we move the focus to the body field.</p> <h2>A note about the name</h2> <p>You may notice that the Demo comes with the title <em>Gumblog</em>. It will eventually be working blog software, made purely to allow me to experiment with PHP 5.4, <a href="">PJAX</a> and this interface concept. You can view the base framework, Gum, <a href="">here</a>.</p> <p>I would love to hear your thoughts about this interface concept. Feel free to reach out via email or Twitter.</p> Moving Off App Engine Adrian Unger Fri, 24 Feb 2012 00:00:00 -0800 <p class="big">Google App Engine provided a slew of learning, and allowed me to rapidly build my side-projects. But, it's time to move on.</p> <p>Google App Engine was exciting, and gave me an easy to run environment to learn Python. I built my portfolio and blog on GAE, as well as the original <a href="">Fullio App</a> and the current <a href="">Cook It Local</a>. But, each of those projects, for their own reasons, has outgrown (or no longer feels at home) on Google App Engine. Let's go over the migration of each project!</p> <h2></h2> <p>The GAE version of my website is pretty awesome. It's flexible, fast and allows me to update everything <a href="">by email</a>. But, I also have a few static mini-sites for clients, fun projects and tests that I like to keep under the domain. Uploading a folder of HTML and CSS isn't so simple on GAE compared to Apache. I also want to have my entire site backed up with Git. After finding <a href="">Jekyll</a> inflexible (at least with my lack of Ruby knowledge) I spent a day building a <a href="">ridiculous <em>build</em> script</a> for a static website. It seems to be working&mdash;you're looking at it!</p> <h2>Fullio App</h2> <p>This was the most painful. Trying to get this idea out fast, while battling with actual client work, I hit a major roadblock with GAE. Fullio processes emails with large, full-sized image file attachments and uploads them to Amazon S3. GAE has (had?) a limit that prevented me from sending requests larger than 10 mb to S3. This just would not fly, as I wanted to allow people to host their mega-images. Failing to get Flask or Django (or Python for that matter) running smoothly on my Windows based laptop wasted days! So, I wrote a <a href="">framework inspired by GAE's webapp</a>, so even though I had to rewrite all the code, at least the concepts were the exact same. I also used <a href="">MongoDB</a> to keep my data structures relatively the same. Fullio has seem some huge delays, but it's super close to a beta-launch. If you're interested, please bug me to get it out.</p> <h2>Cook It Local</h2> <p>Holy neglected project, Batman! Food, nature and sustainability seem to be my passions, so it's saddening to see this project sit idly by. Cook It Local, is an ever adapting idea, web app, resource, tool etc. to inspire people to eat and cook with local, seasonal ingredients. So, many mini-apps and components will end up existing. I don't feel GAE allows for rapid, frequent iterations. So, I am creating a set of libraries for rapid prototyping. The base of this endeavor starts with my HTTP router, <a href="">Gum</a>. I'll soon have a post on <em>rapid prototyping with PHP</em>.</p> <h2>Closed platforms never win</h2> <p>Is that the lesson? Google App Engine is quite an amazing platform. The cost was basically zero to get my ideas up and running&mdash;I've been hosting for zero cost for over a year now. But, my current emphasis is on flexibility and so I'll continue to move away from App Engine.</p> Responsive Web Design Adrian Unger Sun, 12 Jun 2011 00:00:00 -0700 <p>The implementation and advocacy of responsive web design is increasing, and possibly fully manifested in the web design community. Are you taking part?</p> <h3>What Is Responsive Design?</h3> <p>Responsive web design, basically means your website responds to different screen sizes. So, people browsing your site on a Smartphone, Tablet, Netbook or Desktop all get a usable (and hopefully similar, but not exact) experience.</p> <h4>Types of Responses</h4> <p>There are two main ways to respond to various screen sizes and devices, with: <em>responsive designs</em> or <em>adaptive designs</em>. Responsive design usually means a fluid grid, using percentages. See: <a href="">Stephen Caver's Portfolio</a> and <a href="">Maykel Loomans' Blog</a>. And, adaptive design usually means you have fixed grid, that adapts (changes) depending on the screen size. See: <a href="">Mr. Simon Collison</a> and <a href="">Authentic Jobs</a>. You could also use a combination of both, sort of like my Blog that you’re reading now.</p> <h3>Media Queries</h3> <p>CSS3 media queries, quite literally, let you query different media types, <code>screen</code>, <code>print</code> and <code>handheld</code> as well as other helpful properties such as <code>min-width</code>, <code>max-width</code>, <code>orientation</code> and others. <strong>Media queries easily let you define your CSS for any screen size by querying the <code>min-width</code> and <code>max-width</code>.</strong> So, all you really need to know are the common screen widths. According to <a href="!/bryanrieger/status/75597655870406656">Bryan Rieger</a> the “major breakpoints are 0-320, 320-720, 720+ with minor breakpoints dependent on content and key devices (ie: 360, 480, 768, etc).”</p> <h4>What Does This Mean?</h4> <p>In the below snippet, I have included the exact media queries that I use, as well as a few (one, for now) examples from popular CSS frameworks and authors.</p> <script src=""></script> <p>So, if you’re like me and usually develop a new grid for each website you design (which, I highly recommend) those media queries are really helpful in adjusting your design for specific devices and screen widths. And, if you like using boilerplates/frameworks and often design a 960px grid, <a href="">Skeleton</a> is great.</p> <h3>But How Should I Adjust My Design For Each Query</h3> <p>Obviously, you can’t just continuously shrink your column widths—eventually you’d have content trying to fit into 20px columns! So, commonly, you'll end up dropping the number of columns. If you’re full-width design has 6 blocks floating together, you might have to drop that for each query: 4 blocks a row; 3 blocks a row; 2 blocks a row; everything in a single column.</p> <h3>That’s It!</h3> <p>Really. Responsive web design is not much work. I would say, for the bare minimum, design your full width site, Ipad width of 768px and mobile width of 320px.</p> <p>For corrections, comments and questions message me <a href="">@staydecent</a>.</p> Omtask, A Simpler Task List Adrian Unger Wed, 04 May 2011 00:00:00 -0700 <p>Omtask is an omnibox task manager for people that want to finish tasks, not organize them.</p> <h3>Why Omtask?</h3> <p>When I came across <a href="">Steve Losh's t</a>, I loved the idea of it. But, I also realised the extent of my <em>bashing</em> was updating git and svn repositories. Being mostly, a front-end web developer—and addicted to the internet—I spend a huge amount of time in my Web Browser, which happens to be Chrome. <em>Hey, wait a minute! Aren't I able to run commands from Chrome's address bar?</em> Yes! So, I set out to implement the exact same feature set as <code>t</code> in a Google Chrome Extension.</p> <p><em>Not Quite There</em></p> <p>There is no marking tasks as <em>complete</em> and editing still needs to be added. But, you can add tasks, view them, and delete them. All from the Omnibar (this is what Chrome calls its address bar)! But, this is enough functionality for me to already begin using it. I stopped using <a href="">Task Thing</a> awhile ago.</p> <h3>It's So Easy!</h3> <p><code>t Blog about omtask.</code></p> <p>There is no package yet, as I think it needs some more testing and editing of tasks implemented. But, you can check out the source on <a href="">GitHub</a>. If you'd like to try it out right away, clone the repo, then on Chrome's Extension page click <em>Developer Mode</em> and <em>Load unpacked extension</em>.</p> Managing Coworking Adrian Unger Sat, 29 Jan 2011 00:00:00 -0800 <p>When I was working in a team of about ~15 at an Advertising Agency, we were commonly trying to perfect how we all interacted with each other.</p> <p>Building off of 37Signals concept that work doesn't get done at work, we wanted to prevent intrusive chit-chatting and undelivered IMs. The main problems were: <strong>Is this person currently in the office? What is the person currently working on? Is this person available to talk about current projects, new projects or other matters?</strong> I recently saw a <a href="">TechCrunch post</a> about <a href="">Yammer</a> which was a potential solution to those problems that never really worked. It's very hard to integrate <em>yet another</em> app into a teams workflow. We already had Basecamp, Jira, Google Docs, IM, etc. so adding Yammer, which needed to be consciously updated every time someone switch gears (left the office, started a project, switched to another project, had a meeting), had a high barrier for most people.</p> <p>Every team has a suite of apps they use to manage all of their internal workings; the problem of perfecting internal interactions shouldn't be handled by another app to add to the suite. Instead, we should refine the way we access that suite of apps.</p> <h3>The Internal Start Page</h3> <p>Lots of you already use a start page for your web browsing and if you're like me you probably have your business apps (time tracking, invoicing etc.) pinned to your start page for easy access. This has always helped me in remembering to sign into Basecamp and log my hours, or check out the bug tracking app to see what should be resolved today. But, it's not a perfect fit. This is where the <strong>internal start page</strong> comes in.</p> <p><img src="" alt="Internal Start Page porotype" /></p> <p>The above is a very simple prototype, that could potentially solve this inter-office interaction problem. Like the <em>suite</em> offered from 37Signals for accounts using more than one product, there is an area containing unique icons for each app used by a team. This provides the basis of an internal start page. Where it becomes useful in improving interactions with coworker is the team list on the right (this layout would only suffice for small &lt; 25 person teams) where it shows all members of the team, and their current status.</p> <h3>Where's The Magic?</h3> <p>On it's own, this is just the same nuisance as when we tried to use Yammer—each person is required to update their status every time the are busy, available, in office, on the phone, etc. The magic idea is in the <em>portal</em>. Each linked app would have API access (most web based apps for teams have API's). When a person accesses an app and does some kind of unique activity, the <em>start page</em> would know, automatically adjusting that person's status.</p> <h3>Why Am I Sharing This Idea?</h3> <p>This <em>internal start page</em> idea would require a decent barrier to setup each app and team member's account. It would also require a decent amount of programming to make this work. And, could it <em>really</em> work? Let me know what you think <a href="">@staydecent</a>!</p> Being Self Taught Revisted Adrian Unger Wed, 05 Jan 2011 00:00:00 -0800 <p class="big">A few weeks ago I posted about possible drawbacks of being a self-taught web developer and I got some really good feedback.</p> <p>The two main problems I illustrated were: <strong>How to your asses your own ability and properly convey it</strong> and after assessing your ability <strong>how to properly determine the next step of progression.</strong> I now think both problems are futile. I'm self-taught because I didn't agree with the requirements and structure of schools and assessments are inherently part of schooling&mdash;So, why am I trying to assess myself?</p> <p>You can't just tell people you are an expert; that's for them to determine. Rather, show them what you do, and how it's helped people in the past.</p> <blockquote> <p>&ldquo;I&rsquo;ve never been asked for proof of my qualifications &mdash; my work speaks for itself and the majority of my new work comes from current, happy clients telling others about me.&rdquo; &mdash; <a href="">webecho</a></p> </blockquote> <h3>How to properly progress without assessment</h3> <p>Being a self-taught web developer, I already know how to keep up with a radically evolving industry. Where as, graduating from a rigid web design program, you may assume you have reached a certain level of knowledge and skill that will maintain your elligibility in getting work. But, <strong>Regardless of a formal or informal educational background in this field, the technology will permeate anyones&rsquo; experience and knowledge if they don't constantly learn.</strong> Being self-taught, I've become a proactive learner: When I wanted to know how to create a webpage I scoured the internet for tutorials, guides, blog posts and spec sheets.</p> <blockquote> <p>&ldquo;An earnest personal motivation to learn is not something you pick up in a structured learning environment, and this skill will help you stay on top of trends and developments in the field.&ldquo; &mdash; <a href="">eggNrice</a></p> </blockquote> <p>However, being solely self-taught may not work for every field related to web design and develoment. When it comes to designers <a href="">benek</a> argues:</p> <p><q>&ldquo;[&hellip;] having formal education in design is huge if you&rsquo;re a designer. Most self-taught designers I see with little experience are just copying trendy tutorials online and they have no foundation in the principles of design.&rdquo;</q></p> <p>Though, it's very possible one could also teach themselves the fundamentals. I suppose it's just more common place that purely self-taught designers often miss the foundation of good design.</p> <p>In the end, the educational background of someone shouldn't be the primary tell for their perceived ability. Your passion and hunger within your respective field guarantees much more. It's actually pretty prevalant in the design and web industry that a your work outweighs your educational experience.</p> The Unforeseen Consequence of Being Self-Taught Adrian Unger Sun, 12 Dec 2010 00:00:00 -0800 <p>I had yet to notice any drawbacks of working in a field where I have either taught myself what I need to know or learned through work experience. Until now.</p> <p>Hopefully you have realized, that I am a Web Designer &amp; Developer. And, I'll now let you know, that I learned nearly everything for this career with trial &amp; error and the internet. And, perhaps more surprising, I can honestly say that my gateway into this career was playing a <a href="">popular</a> computer game nearly nine years ago.</p> <h3>Becoming A Self-Teacher</h3> <p>Sure, Counter-Strike itself was rather addicting but what really got my attention was all those files (just left there for me to explore!) inside the install directory. There were graphics, sounds, levels and even a unique <strong>scripting language</strong>! So, through the power of the internet, I taught myself how to write Counter-Strike scripts. But, after finding or reading about most of the exploits, scripting became dull—I wasn't actually playing the game that much at this point, so there was no utility left. Soon after, I discovered <a href="">hacking</a>. Naturally, I <a href="">taught</a> myself <strong>how to create multiplayer hacks</strong>. Actually, I even worked on some of most popular public cheats of a rather limited time. From the amazing scripting community to the even more amazing hacking communities, I found a viable way to learn whatever I was interested in. And, I continue this approach even today.</p> <h3>Relevant Lessons</h3> <p>My foray into creating websites was <a href="">Geocities</a>. Thankfully, I desired more control and eventually learned HTML; my resource was the <a href="">HTML Goodies Primers</a>. Which, looking back on it now, seems like a well structured lesson plan that could resemble what's taught in school. Sadly, I can't recall what my main resource was for learning CSS, but for PHP <a href="">Tizag</a> covers the basics pretty well. From that point, I had developed a strong enough base, that the rest of my learning could be achieved by reading the <a href="">docs</a> and <a href="">spec sheets</a> for the technologies I was interested in.</p> <h3>The Problems</h3> <p>Recently, I was reading a bunch of the latest articles on <a href="">A List Apart</a> and I noticed something I didn't have an answer too. Which was, half the articles seemed useful and applicable while the other half seemed obvious. My opinion is that A List Apart offers the highest level of HTML (and similar technologies) understanding. Which lead to my confusion: are only some of the articles aimed for professionals while others are well-suited for beginners? Or, since after following the HTML Goodies Primers I learned much of everything else <em>as it came up</em> in actual work experience, that my knowledge is scattered? Perhaps, I have an understanding of some seemingly expert-level front-end development concepts, while missing other, intermediate or even beginner-level concepts? At the moment, I can't answer those questions. And, that brings up two problems: <strong>Even though I spend almost every hour living inside Web Development, can I claim expertise?</strong> I don't have any physical or widely-accepted <em>certification</em>—just experience. This is a problem, because I don't want to lie and I don't want to claim more than I mean. Another problem is, where do I go from here? <strong>How do I determine what level of knowledge I have of Web Development, and how do I properly determine the next step of progression?</strong></p> <p>Is anyone else self-taught? Have you found any problems associated with being self-taught? I'd love to hear from you either via <a href="mailto:">email</a> or <a href="">Twitter</a>.</p> My First Visit To Seattle Adrian Unger Sun, 28 Nov 2010 00:00:00 -0800 <p>Since I missed the chance to see one of my all-time favourite people live, in Vancouver, I had an all-too-good reason to visit Seattle for the first time.</p> <p>Grinderman, the newest project of Nick Cave and most of the Bad Seeds, is finishing up their North American tour. It seems the show in Vancouver sold out too fast for me and, without hesitation, caused me to buy tickets to the Seattle show. Incredibly excited to see Nick Cave in the flesh—I just can't help but enjoy <em>almost</em> everything he produces—I was also excited to visit Seattle for the first time. Which is a strange thing as I have, thanks mostly in part by my Dad, seen more American soil than Canadian.</p> <h3>Belltown</h3> <p>My girlfriend and I decided to stay at the Belltown Inn(in Belltown!), which I booked through The process was easy enough, and it landed us right in between the concert Venue and Pike Place Market. The Hotel itself isn't much to talk about: it's clean; friendly staff; free bicycles; whatever. Thanks to we stayed one night at half price, but I can't see myself going back at full price.</p> <p>Now, the neighbourhood of Belltown is pretty interesting. Though I haven't seen much of Seattle(stayed within Belltown, West Edge and the Seattle Centre) I came to like it immediately. Maybe because it's like Gastown but not dark, dirty &amp; depressing. Belltown is interesting because there is no apparent sign of generic and duplicated business. Actually, from what I saw, Seattle has far less fast-food joints and chain restaurants than Vancouver(I'm probably thinking of Downtown and Gastown). I was really surprised that I only came across two Starbucks, and yet both of them had unique and rather interesting storefronts.</p> <h3>Eating</h3> <p>It was rather easy to find online restaurant recommendations for Seattle and perhaps through instinct shortlisted about 6-8 for my Girlfriend and I to choose from once there. Le Pichet easily got both of us excited and we went for our first meal(brunch/lunch thing) there. Le Pichet serves traditional and regional French food. We shared a plate of charcuterie and a Black Cod, Orange &amp; watercress salad. Awesome. It may not have been the best plate of prepared meats I've had, but it was damn tasty, as was the bread and salad. What I really enjoyed was the atmosphere: totally relaxed, subtle decor, really attentive service and Massive Attack on the playlist.</p> <p>As a snack while browsing the Pike Place Market, I grabbed a BLT from Three Sisters Cafe. Perhaps a bit pricey, but good ingredients and just made simply. No fluff.</p> <p>For dinner, we went to the frequently recommended Zig Zag Cafe. First impressions are tough, and it didn't go too well for this Cafe: a mentally distant hostess, red lighting and purple velvet. Not my vibe. I think the high-mark of this place is the drinks—the beverage menu is massive—but we don't care much for this as we're more interested in food. Oh well, we ordered the Boar Bacon Risotto and Full Quail. The food is well prepared and mostly flavourful. The risotto came with big cubes of Boar, which was great! But, Istill prefer a simple Mushroom risotto. The quail was good…just a bit boring. Maybe, if the skin was crispy, or there was more fat, or a tasty sauce all over it. But it was basically just a seasoned bird on top of some seasoned vegetables. Well made, but not exciting.</p> <h3>Grinderman</h3> <p>We caught Grinderman at the King Cat Theatre, a movie-theatre-turned-venue which works quite well. I'm never really sure how to describe Grinderman. I've heard the term, Garage Rock, thrown around. But it's not exactly the sound I think of when I hear that term. Basically, they're a bunch of bearded baddasses(and Nick) who've been playing music for at least 30 years. Nick Cave's writings and lyrics are usually pretty dark and disturbing, but Grinderman really pushes the dirt. The show was loud, dirty and full of energy—they are just amazing to watch.</p> <h3>I'll be back</h3> <p>I <em>really</em> enjoyed Seattle. I couldn't help but compare it to Vancouver and, though my experience is limited, Seattle is a golden city to me right now. It was just so relaxed: big sidewalks, much less inner city traffic, and just so many <strong>unique</strong> restaurants, cafe's, bars and shops. Walking aimlessly, we never were far from yet another local food spot.</p> <p>Seattle is just easy.</p> Logicalism Over Minimalism Adrian Unger Wed, 08 Sep 2010 00:00:00 -0700 <p class="big">I don't subscribe to any sort of minimalist way of life. But, that word seems to have a meaning that I can often attest to my life. The funny thing about words and their meaning is that they aren't consistent across all means.</p> <p>In my very limited scope of <a href="">minimalist inspiration</a>, <em>the word</em> often is attributed to Apple—or the other way around. I mention this, firstly, because labeling and brands are things I attempt to avoid. Secondly, when I said <em>often</em> I meant blindly and always. If anyone shares this limited scope with me, I would like to let them know: <strong>Minimalism is a jaded word with lost meaning, and is more keen to be a fad or popular tag.</strong> And, on the reverse, Windows is hailed as the anti-christ of minimalism. Don't be fooled.</p> <p>In all senses and progressions of my life, I tend to believe I think about things logically. And especially when I started freelancing. I think logically about the way I <strong>work, and the devices used for work and communication; about food, where it comes from and how it's prepared; material items I own, like my clothing, furniture and other equipment</strong>; and, basically anything else I deal with on a day-to-day basis.</p> <p>I'm going to split up each one of these things into separate posts. And, because Apple, computers and gadgets seemed to spark my definition debacle(maybe not quite <em>that</em> serious), I will start there:</p> <h3>The End Of The Branded, Labeled Minimalism In The Office</h3> <p>These <a href="">blogs</a> and <a href="">semi-popular internet figures</a> will have you believe that you need to own 2 to 4 Apple made products to complete your minimalist lifestyle. Now, they may not make this statement outright, but in their act of sharing their all-too-similar ways of life a very pretty, brushed-steel picture is painted. Actually, <a href="" title="Laptop and desktop? Not very minimal">this picture</a>. And, <a href="" title="Laptop and desktop? Not very minimal">this one</a>.</p> <p>In multiple, thousand-word essays these authors have shown that you <em>need</em> an Ikea tabletop, some kind of fancy desk chair, the prominent desk lamp, a mac computer, a mighty mouse(such garbage), a cinema display, an iphone, a sleek dock, an ipod, an ipad, and no one cares anymore.</p> <p>In absolute, hellish contrast I am able to Design(even for print!), write markup, code(in multiple languages!), record, produce, take notes(<em>GASP</em> without Word! And, without awesomely, cult loved &quot;minimal&quot; note taking apps exclusive for some other OS!) all with my…Windows 7 based laptop. Now, I'm not going to go into great detail about how <strong>you can work on windows just as minimally and simply as on a mac</strong>. Because you're a grown-up, and you know that you can have a happy computing life without selling your soul to some <a href="">corporation</a> or <a href="">brand</a>. You don't have to do any one thing any one way, for the rest of your life and never fell that way of life. You can adapt. You can stop caring. You can still be efficient.</p> <p>Now, I'd like to believe that people will agree with me that this is a rather unimpressive yet beautiful space to work in:</p> <img src="" alt="My Current Workspace. I am still looking for a desk." class="photo" /> <p>And now, you may argue &quot;it would look <em>even better</em> with a macpro.&quot; And I retort &quot;Suckas! I don't have an office—I just put my computer away in its hiding place, when I'm finished working.&quot;</p> <p>And even now, you may think that I, the author, has gone astray, and lost his original point. But, I will emphasize that my point is to stop caring. This is so silly. If you <strong>honestly</strong> believe that you cannot share the same minimalist lifestyle of simplicity and happiness and zen and organization and flowing water and grooveshark and starbucks and meetups with a Windows based machine in place of a Macintosh, then you should add the following disclaimer to your blog and every accompanying brand hugging post:</p> <p><strong>I am unwilling to be adaptable. I am unwilling to take matters into my own hand and consider all possibilities. I believe minimalism is always the easiest most obvious solution, and let the past dictate the future. I am set in stone. I am a minimalist without logic.</strong></p> Minor Update to Task Thing Adrian Unger Wed, 01 Sep 2010 00:00:00 -0700 <p>While remaining rather minimal, Task Thing has a new feature—more specifically a helpful piece of data is now displayed on completed tasks.</p> <p>For anyone who is unaware, I created a little app called <a href="">Task Thing</a> a <a href="">few months ago</a>. It hasn't gained much traction, but a couple people seem to be using it(Woo-ee!).</p> <p>With the hope that I will be more efficient and precise at tracking my time, <strong>Task Thing now tells you how long it took for you to complete a task</strong>. This functionality was already implemented as Task Thing logs the creation date and last modified date for each task. Using this data, when a task is marked as completed(which logs the time of this action) Task Thing displays that time relative to the time since creation.</p> <p>Because I use Task Thing at a very micro scale—I only add tasks that I am tackling that day, and usually as I begin working on them—I can actually reference this <em>time to complete</em> when logging my hours for the day. Obviously, if I create a task one day, ignore it for a few days, then work on it, and mark it complete, it's not an accurate measure of <strong>actual time</strong> committed to that task. But, still an interesting piece of data. Maybe I should start logging average time to complete tasks?</p> <p><strong><a href="">Try Task Thing today</a></strong></p> Publishing By Email With Google App Engine Adrian Unger Sat, 03 Jul 2010 00:00:00 -0700 <p>I really like the Posterous approach to publishing online—I often write my ideas down in GMail. So I decided to enable posting by email on my blog.</p> <p>The concept needs some work, but could very easily be adapted to a multi-user environment. In that case, some serious security work would be required. But for my personal blog, it should suffice.</p> <h3>How It Works</h3> <p>Google App Engine has amazing Email handling built in. Just check the <a href="">docs</a>. Because of this, not much code is required. Anyway, here's what I've accomplished:</p> <ol> <li>Write posts in email</li> <li>Process those emails for <em>YAML Front Matter</em></li> <li>Continue using Markdown</li> </ol> <p>Pretty basic. I haven't got into checking attachments, but I don't think it would be too much more work. As for the <a href="">YAML Front Matter</a>, a concept taken from <a href="">Jekyll</a>, I follow <a href="">Rasmus Andersson's</a> formatting exactly as it <a href="">appears</a> in <a href="">Gitblog</a></p> <h3>How I Did It</h3> <p>I just read through those <a href="">docs</a> I mentioned above and wrote an Email Handler. My blog has a very simple Entry handler, so saving new entries is pretty easy.</p> <script src=""></script> <p>If you have any questions or input, message me on [Twitter]( <a href=""></a> Staydecent on Twitter).</p> Installing App Engine On Windows Adrian Unger Wed, 30 Jun 2010 00:00:00 -0700 <p>The most difficult part of installing Google App Engine on Windows is the SSL module. But I've decided to compile a very simple guide for the whole process.</p> <p>I am not going to cover any sort of usage guides. This is just a list to installing everything required(on Windows) to deploy an App to the GAE cloud.</p> <ol> <li>Download &amp; Install <a href="">Python25)</a></li> <li>Download &amp; Install <a href="">App Engine</a></li> <li>Download &amp; Install <a href="">Python25 SSL Installer</a></li> </ol> <p>The hardest part: Installing the SSL Module. Python25, unlike 26 and newer, does not come with the SSL Module and must be installed. The normal route of <code>python install</code> will produce errors. Something about wrong compilers. There are <a href="">tutorials</a> out there for installing all the dependencies manually. Luckily, someone has created an installer exe. I have tested this on WindowsXP and Windows7 and it works.</p> <h3>32bit Versus 64bit</h3> <p>Do not install the 64 bit version of Python on Windows. If you do you will have to compile everything yourself. Including that installer, that installer is 32bit only. I really don't know any benefit to installing the 64bit version of Python.</p> <p>Unless I am forgetting something, that is all you need.</p> Wordpress Featured Post Slider Adrian Unger Thu, 24 Jun 2010 00:00:00 -0700 <p class="big">WordPress 3 is out: And it makes developing feature rich, user maintainable websites possible with the just the core.</p> <p>WordPress is already known for its ease of development, deployment &amp; maintainability. Well, maybe not always the last one due to unmaintained plugins. This is addressed in WordPress 3.0 by <a href="">adding</a> to the expansive core. Anyway, here's an example of how to build a common blog feature with just core functions and some jQuery.</p> <h3>Featured Image Slider</h3> <div class="aside">Note: Featured Image—as it's called within the admin UI—is synonymous with Post Thumbnail—as it's called in the function references.</div> <p>What is it? It's a way of showcasing all of the posts with a <a href="">featured image</a> in a simple slider (or other jQuery animation). All we have to do, is add support for post thumbnails and display them within the loop, and jQuery will handle the animation.</p> <h3>The WordPress Code</h3> <p>For a deeper explanation of post thumbnails, read <a href="">Mark Jaquith's Post</a>. For now, here is the code to add support and define the sizes of our post thumbnails:</p> <pre><code>add_theme_support( 'post-thumbnails' ); set_post_thumbnail_size( 300, 100, true ); // Normal thumb size add_image_size( 'featured-image', 900, 300, true ); // Slider size</code></pre> <p>I displayed the slider on the homepage only, but pick a theme file and add the following within the loop:</p> <pre><code>&lt;?php if (in_category('10') &amp;&amp; has_post_thumbnail()) : // Category 10 is my 'Featured' category ?&gt; &lt;a href="&lt;?php the_permalink() ?&gt;" title="Permalink to &lt;?php the_title() ?&gt;" rel="bookmark"&gt; &lt;?php the_post_thumbnail('featured-image'); // Make sure you call the image size you defined in functions.php ?&gt; &lt;/a&gt; &lt;?php endif; ?&gt;</code></pre> <h3>The jQuery Code</h3> <p>This is covered everywhere. You can use <a href="">jCarousel</a> or any other jQuery(or other framework) plugin. Otherwise, check out my live <a href="">implementation</a> and feel free to view the source.</p> Staying Productive With Task Thing Adrian Unger Tue, 25 May 2010 00:00:00 -0700 <p class="big">I have recently jumped back into the freelance world. And I need ways to stay productive.</p> <h3>Why I am freelancing</h3> <p>Growing sick of the <em>office</em> &amp; meetings &amp; commuting &amp; packed lunches &amp; disagreements, I decided to get back into freelancing—effectively taking control of my life. The first day I sat down at my desk, I asked “What the fuck am I supposed to do today?” I needed a list of things(tasks) to keep me in check.</p> <h3>Available tools</h3> <p>Of course, the first thing I did was consider all of the to-do lists and task lists I have used in the past or just heard of(there are lots): <a href="">Todoist</a>, too much; <a href="">Ta-da List</a>, I may as well used pen &amp; paper; <a href="">Teux Deux</a>, this was a close winner, but I don't like the multiple lists concept, or the small font-size. Naturally, if I wanted something I wouldn't complain about and could use every day, I needed to create my own.</p> <h3>Introducing Task Thing</h3> <p>This brings us to <a href="">Task Thing</a>! I would say this is very similar to Ta-da List, but with actual benefits over using just pen &amp; paper. I say so because of drag &amp; drop, inline editing and an understandable interface. Maybe I'm wrong, but feel free to <a href="">give it a try</a> yourself!</p> <div style="background:url(;width:500px;height:200px;margin:0 auto 2em;-webkit-box-shadow: rgba(0, 0, 0, 0.199219) 0px 0px 15px;"></div> <h3>Building Task Thing</h3> <p>I built Task Thing over the better part of a day. This was possible because I was using a bunch of code I had written for another concept of mine(Hint: Panic Status Board). I won't go into too much detail, but Task Things' functionality is broken up into two models: list &amp; task. The list model represents the page which gets one owner and any amount of tasks(owned by the same owner). A task has two editable fields, title &amp; description as well as some other internal data(dates, priority, etc). Linking the tasks to the proper list is rather simple: the list attributes a unique slug and owner. When you view that list, you must login as the owner(logging in, you are redirected to your list). The tasks also have an owner field, which is matched with the list. Learning how to link different values in the App Engine datastore has been a lot of fun and so much lighter than MySQL joins. Also, I <a href="">Ajaxed</a> the shit out of it.</p> <h3>Staying Productive</h3> <p>Creating a task list was the first step I took in helping myself <em>stay productive</em>, and I am sure there will be more. So, if you care, <a href="" title="the Staydecent™ Blog feed">check this blog often</a> as I post more things.</p> jQuery Random Each() Function Adrian Unger Fri, 19 Mar 2010 00:00:00 -0700 <p class="big">Fading in elements incrementally in order of the dom tree is cool, but so is fading in elements in random order.</p> <p>The former is pretty simple, you iterate through each element while setting a timeout for fading in:</p> <pre><code>$("div").each(function(i) { var e = $(this); e.fadeTo(0, 0.05); setTimeout(function(){ e.fadeTo(250, 1); }, i*25); });</code></pre> <p><code>i</code> is the index of each found element in order of the dom tree. As we get further down the tree the timeout is longer and longer—since the animation lasts longer than a couple timeouts the animations will overlap.</p> <p>To make it random, we still follow the dom tree in order, but we set the timeouts at differentiating lengths(randomly). Say we have 10 elements total, we can assume the indices are <code>0,1,2,3,4,5,6,7,8,9</code>, We just need to sort those numbers randomly. Enter <code>randsort()</code>:</p> <pre><code>function randsort(c) { var o = new Array(); for (var i = 0; i &lt; c; i++) { var n = Math.floor(Math.random()*c); if( jQuery.inArray(n, o) &gt; 0 ) --i; else o.push(n); } return o; }</code></pre> <p>We pass the function the number of found elements and it loops through them creating new numbers(within the range of <code>c</code>). To avoid duplicated numbers we use jQuery's <code>inArray</code> and “skip” it if true. In the end, you have those indices in a random order. Moving along:</p> <pre><code>var e = $('div') // The elements we're searching var c = e.size() // Total number of those elements var r = randsort(c) // an array of the element indices in random order</code></pre> <p>Now an ever so slight variance on that original each function:</p> <pre><code>// the jQuery selector could be replaced with e $("div").each(function(i) { var e = $(this); e.fadeTo(0, 0.05); setTimeout(function(){ e.fadeTo(250, 1); }, r[i]*10); });</code></pre> <p>So what's different? <code>r[i]*10</code>. We are using the <code>i</code> index to call the <code>r</code> array in order, but since the values are a random representation of the element indices the timeouts differentiate. Boom!</p> <p>Check out a <a href="">demo here.</a></p> An Introduction Adrian Unger Thu, 18 Mar 2010 00:00:00 -0700 <p class="big">I have relaunched my website and launched my blog as a separate website. Here's how and why.</p> <div class="clearfix"> <div class="col size1of3"> <h3>In The Old Days</h3> <p>A few(or several) years back I had an account at a host named FreePGS, they offered PHP and MySQL at no cost! It housed numerous iterations of my homepage even up until Staydecent. FreePGS' offered service changed numerous times while I was there: From completely free, to $3 a year, to no longer accepting new accounts. Even when they started charging, I won a design contest and got to remain there for free, surviving through the death of FreePGS. In the end, I was plopped onto some reject GoDaddy reseller server that barely had enough power to load Plesk, while they asked me for $3 a year. Eventually, I stopped paying for that account.</p> </div> <div class="col size1of3"> <h3>The Hiatus</h3> <p>I spent too much time researching hosts. Web Faction, Linode, Slicehost, PRGMR, all promising and none were a clear winner for me. So, I ended up with a static landing page for maybe a year? Anyway, I was serving this static page up on Nearly Free Speech, a pay for what you get service. Simple and cheap. Going back to those other hosts: the main reason I was considering them was because I wanted to learn the ins and outs of a VPS(or similarly free[as in freedom] service). But, VPS is dated and <em>The Cloud</em> is in. So, I started playing around with Google App Engine.</p> </div> <div class="col size1of3 last"> <h3>In The Cloud</h3> <p><em>Here's lookin’ at you, kid!</em> What's that mean? Who cares, Rejoice! I built a Blog in Python on GAE.</p> <p>This blog is hosted on App Engine, but what about the portfolio? It's still on Nearly Free Speech, serving up static files. Why not? And, this is the first time I've split my blog away from the main site. Now, I have <strong>two</strong> sites to mess around with, constantly!</p> <p>If you haven't noticed, or realized from this post: I change my website all the time. It constantly evolves as I learn new things and try new things with it. And so will this blog! I will try and be more consistent and make sure permalinks live on and keep downtime to a minimum. And, at least for now, the current Staydecent Blog and Staydecent Portfolio are not going to change much.</p> </div> </div> An Interview With Chris Allen Adrian Unger Wed, 04 Feb 2009 00:00:00 -0800 <p class="big">Chris Allen is a Vancouver based freelance Graphic Designer and Art Director, an Emily Carr Institute of Art and Design graduate and has strong roots in the Skateboarding industry.</p> <p><em>Check out this interview I conducted with him to find out more!</em></p> <img class="photo" src="" /> <h4>Hey Chris, could you tell me a bit about your background(live, born, age, work, school, whatever!)?</h4> <p>Age 31; Work and live in Vancouver, BC Canada; Studied graphic design at ECIAD; Freelance designer currently working with Studio: Blackwell.</p> <h4>What do you think of Vancouver, in terms of graphic design work and whatever else stands out?</h4> <p>Vancouver is a beautiful city and [has] recently been rated as one of the worlds move livable cities. In regards to graphic design, I feel opportunity is a little lacking and as a result Vancouver has an over abundance of interactive studios and advertising agencies. Vancouver is not on the international radar of design hotspots by any means, but I feel confident in saying that good work is good work and can be produced anywhere. At the moment Vancouver seems to be working for me but [I] would love to experience living and working in a large city like London or New York.</p> <h4>Do you remember working for your first client? Anything to tell?</h4> <p>My fist client was a friend who required help in branding a small clothing line he was developing. With no formal training, technically or theoretically the intent was there and at the level we were operating it that was the most important thing. We knew what we wanted to do and how things should look it was just a matter of making it happen. No rationale, no theory—just make it look good. We photographed and produced a brand book/catalogue and developed a logo. It was all very simple but proved to be effective and as a result landing him the Urban Outfitters account. </p> <blockquote class="col size2of5">We knew what we wanted to do and how things should look it was just a matter of making it happen. No rationale, no theory—just make it look good.</blockquote> <h4>I noticed your older work is largely based around the skateboarding and street cultures, how did that come about?</h4> <p>As a freelance designer I try and pursue projects that compliment and reflect my interest. Much like the work I did with Transworld Skateboarding. At that point of my life I was was heavily involved in skateboarding industry and also being a skateboarder myself it was only natural that I pursued work in which I was knowledgeable and had a love for. A Canadian friend of mine was the art director at Transworld in San Diego while I was attending Emily Carr. He contacted me to design an editorial feature figuring I could use the break, not to mention the cash. This proved to be a great way to get my feet wet in editorial design and also achieve a childhood dream of working at a skateboard magazine. </p> <h4>And now, your new work shows some great strategic branding, was this a natural progression moving to that kind of work? Which type of work do you prefer?</h4> <p>I think this is a natural progression for most designers. The more experience a designer gains the more knowledge they can apply to larger more complex projects. Honestly I prefer whatever project allows for the greatest opportunity for a new experience and a chance to create something I have not done before allowing me to constantly progress as a designer. My fear is doing the same thing over and over again, I think that is why I have been so reluctant to work in advertising, I am scared of the formula, the punch lines. </p> <img class="photo" src="" /> <img class="photo" src="" /> <h4>Are you able to tell us about any current or recent projects you are working on?</h4> <p>Currently I am working with Studio: Blackwell designing exhibition catalogues and artist monographs for the Gagosian Gallery and Rizzoli. Kelsey Blackwell is amazing and has taught me so many valuable lessons, not just with regards to design but theory and life itself. I feel honored to have such an incredible mentor. </p> <h4>What would be your ultimate dream project?</h4> <p>At this point I can't really give you a specific answer to that question. I guess my dream project would be to start a small publishing company and work remotely from a detached home studio with floor to ceiling glass in the middle of a forest. </p> <h4>What tracks, artists or albums are you listening to right now?</h4> <p>This is an interview in itself. The following are what I pulled off the records shelf in no particular order:</p> <ul> <li>Led Zeppelin - II </li> <li>Booker T &amp; The M.G.'s - Melting Pot </li> <li>Bob Dylan - Blood On The Tracks </li> <li>Black Sabbath - Sabbath Bloody Sabbath </li> <li>The Doors - Strange Days </li> <li>David Bowie - The Rise And Fall OF Ziggy Stardust And The Spiders From Mars </li> <li>Bill Withers - Still Bill </li> <li>Pink Floyd - Dark Side Of The Moon </li> <li>Heart - Little Queen </li> <li>Jane Birkin, Serge Gainsbourg - Self Titled </li> <li>Santana - Abraxas </li> <li>Unicorn - Tyrannosaurus Rex </li> </ul> <h4>Some Artists, designers, companies your into right now?</h4> <p>There are too many to list but I do find the following people although contemporary very interesting and influential: Storm Thorgerson (Hipgnosis), Bruce Mau Design, Spin, Build, Underline Studio, 2x4, Made Thought, Browns, Pentagram, Experimental Jetset, Non Format, Graphic Thought Facility, NB: Studio........ </p> <h4>The essentials of every day life?</h4> <p>Family, my lovely girlfriend, friends, my animals, nature, food, music, photography, art and design.</p> <p><em>Many thanks to Chris Allen for being my first interviewee! Please check out his <a href="" target="_blank">website</a> for more work and info.</em></p>