Even with the EE 2.0 release in December, I think the best ExpressionEngine event in 2009 was the rise of Travis Schmeisser's Structure. While 2.0 lays the groundwork for a bright future, in the short run it's more of a step backwards because so few of EE's amazing community-built addons have been ported so far - even with new functionality, a 2.0 upgrade takes away far more than it gives at the moment.
In contrast, the Structure module immediately makes major improvements to your site's UI, construction, and template codebase, and it also plays nice with other addons, creating a faster, smarter EE experience. No software I installed last year saved me as much time and effort as Structure did, and Travis deserves all the credit for that (and he got quite a bit, including Devot:ee's Module Of The Year award)
So what is Structure, and what about it makes EE so much better?
Structure is a module that overrides EE's URL/template setup and creates its own hierarchy of pages and "Listing" pages, along with a new interface that's meant to handle most user needs. Why is this such a big deal?
We had a few false starts with Structure - mostly because we were trying to bring it into existing EE sites (recommendation: don't) or use to do the wrong things, but we eventually got the hang of it. A few pointers:
Use Gypsy.
Gypsy allows for standardized field names/types across all your weblogs, which is the key to making a great "default" template that can handle most pages. It also makes custom meta tags and title tags a snap - since Structure always knows which page you're on, and Gypsy always knows what your field name is, you always have easy access to custom fields like these.
Useful site-wide fields like this should be a snap to implement and use - custom per-page meta information used to be a huge pain, but it's no problem for a Structure/Gypsy setup.
Make it browseable first.
Using a default template and some subnavs, you can make a working site super-fast with Structure - this allows clients to make sure all the pages are where they imagined and understand what's still missing. Again, if you're using Gypsy and standardized field names like "general_body" and "general_intro", this should be a snap.
Using a template or two, it should be possible to create the whole sitemap in the Structure Interface, and make an early working site for clients or project managers.
Keep specific templates for 404, Search Results, and No Search Results.
This is especially true for the search templates, because they have an unpredictable query string in the URL that Structure doesn't work well with.
Embed, embed, embed.
Most of our page templates are actually just a few lines - embed header, embed content with parameters, embed footer. Embedding a "content" chunk like this allows you to easily pass parameters into weblog tags using advanced conditionals, so you can effectively use one or two weblog tags to run the entire site if you're clever about it. This setup might require more up-front planning, but it saves you a ton of debugging and tweaking time later, and keeps the code much dryer.
We have a number of "general" and "unique" templates, most of which point to one of three "content" embeds with a few special passed parameters.
Use Assets. The Assets bucket is Structure's orphanage for non-page entries - perfect for one-off chunks of content like a footer disclaimer.
Like EE's url_title, Structure's page_slug determines the address a page appears at. Url_title and page_slug usually match, so it's easy to mix them up in your templates - but if they get out of sync, templates that use both can break. At Viget, we completely hide the URL Title field , using only {page_slug} and {page_uri} in templates.
To find the correct "parent" entry, {exp:comment} tags try to match the current URL to an entry url_title. But like I said, actual URL and url_title can get out of sync, so you might want to manually set an entry_id or url_title in your tag:
{exp:comment url_title="{url_title}" dynamic="off"}
Structure is a lot stricter about URLs than EE templates tend to be. While this is great for 404 handling, it makes some old-school EE ideas kind of tricky. For example, "blogentry/comments" will throw an error, because Structure looks for an explicit "comments" page. We like to redirect form submitters to "(currentURL)/thanks" for tracking purposes, but Structure doesn't understand that we want the last segment to be effectively ignored.
We use NSM Safe Segments to work around this, which makes EE ignore segments of your choosing.
Related to the last point, Structure kills EE's default handling of blog categories. There are a couple of workarounds for this, but our favorite is a two-step process:
1. Create new pages under your blog page for each category, and
2. Link these pages to a template that uses Low Seg2Cat to convert the segment into a category ID, then drop that ID into a weblog tag.
The incredibly useful Cloner duplicates entries, but the process can mess with a Structure page's hierarchy and listing settings. Watch out!
Have a Structure setup or trick you'd like to share? Let me know in the comments!
"It's just a big iPod Touch."
"Exactly," I say. "Isn't it great?"
There's been a lot of criticism of the new iPad around here at Viget. With most of the office working on MacBooks and living out of their iPhones, you'd think the Mac FanBoyism would be strong, but on the whole the lab rats don't seem to be impressed. But I think i think it's a fascinating product offering. Why?
As a user experience designer, I love the way Apple evolves their user interfaces. Apple warms its users up to new ways of interacting with their products by introducing simplified versions of an interface and gradually add more features and apply it in more contexts as users become comfortable. Let me give you a few examples.
Apple first introduced the Cover Flow carousel in iTunes. It was a clever idea that made your mp3s a little more "physical." An OS update or two later, the Cover Flow appeared in the Finder as a way of looking previewing files. Apple continued to iterate on the idea of giving you larger previews of your files by introducing Quicklook, where you can actually scroll through PDFs, documents and spreadsheets.
I'm always hearing people say that they'll never transition to a touchscreen keyboard, but I think those who use Apple laptops have been doing just that without knowing it. Over the last several generations of its notebooks, Apple has reduced the travel on its keyboards considerably. While at first it looked like Apple was compromising tactile feedback for form-factor, I think they've been training their users to happily use touchscreen keyboards by gradually reducing the tactile feedback they expect. Now, the iPad introduces a nearly laptop-sized touch-screen keyboard. It's like the parable of the frog in the pot.
Two-fingerscroll wasn't necessarily the most innovative feature
Apple ever introduced, but I've seen it change the way people navigate
on their lappies. Whether we knew it or not, it also changed the way we
thought about our trackpads, showing the concept of gestural commands
to users who don't spend their days geeking out on how people interact
with their computers. With the introduction of the iPhone, Apple was
able to give users a more direct way to use multi-touch and gestures to
interact with their information.
http://www.apple.com/macbook/features.html#trackpad
Yes, the iPad is a big, and I dare say, horsey-looking (thanks for the adjective, @graphicsgirl), iPod Touch. And yes, I'm a little disappointed that my next laptop purchase won't be an Apple Tablet with a 2.4GHz+ Intel Core 2 Duo processor. But when we step back from our unrealistic consumer expectations for a minute and look at the iPad next to the iPod Touch, there are two important things to note. First, size does matter. With a 9" screen, not only can we really read on the thing, but I can get most of my 10 fingers on there at once. The possibilities for multi-touch interfaces are much more compelling when you have room to use two hands and see what you're pointing at (see the Jazz Mutant Lemur for an example). Second, the iPad is aimed at entertainment and netbook consumers. The iPad is the not-so-missing link between the iPhone and a true Apple Tablet. As users become familiar with multi-touch as their primary input method the UI designers at Apple have time to identify and overcome the limitations of multi-touch for more complex and critical tasks (as @crnixon says: "It's not a computer if I can't program on it").
As for me, I'm pretty happy with this evolutionary step. As an electronic music nerd who got on the waiting list for one of the first-iteration monome and has more music-making or midi apps than info or organization apps, I'm downright giddy at the idea of having a 9" multi-touch screen I can use to control Ableton Live and Max/MSP.
Alright. You've got me. These Valentine's day ideas aren't just for designers. They can be for anyone with a divine sense of taste and style just like you and me. Here's a few ideas that came to mind as I was shopping for my better half, an English major, for Valentine's day.
Let's start with the edibles and get those out of the way. Everyone loves bacon, right? Ok, well at least things that taste like bacon. By giving your special someone bacon-flavored lip balm it's like giving yourself a gift. Just be careful not to bite down at pucker-up time.
Source: Source: BaconSalt.com
There's a newcomer added to the classic Sweethearts lineup this year. The New England Confectionary Company better known as NECCO has added a "Tweet Me" heart for all the Twitternauts out there.
Source: NECCO.com
Maybe that person loves Bodoni or Helvetica nearly as much as they love you. There's always a membership to Typofile for the type-obsessed type.
Source: Typofile.com
Other options for type lovers include these limited edition blocks of maple by the fine folks at House Industries. You can buy initials for both of you, connected by a loving heart. Or, if things don't work out, they'll make for good fuel to keep you warm during the cold month of February.
Source: HouseInd.com
For the iPhone lover there's also this sleek wood case to protect your most precious asset. Though, take heed if you feel compelled to light it on fire. Apple afficianados are a viscious crowd.

Source: CaseCrown.com
For those that love literature as much as they do their Mac, there's the BookBook. It's a leather-bound case with a classic book look and feel to protect a MacBook or MacBook Pro. It doubles as a great way to hide or disguise a laptop from suspicious predators.
Source: TwelveSouth.com
Designers love their Moleskine sketchbooks almost as much as their Macs. Choose from a set of artistically rendered covers or submit your own artwork for a custom cover. The cow it came from will be happy to know it's made with all-natural vegetable tanned leather.
Source: EngraveYourBook.com
The fine folks at Behance, makers of the Action Pad, have finally drummed up the perfect sketchbook for me. They've taken the utility of the Action Method and combined it with the elegance of the Moleskine line to create pure notetaking and sketching bliss. I wasn't patient enough to wait for Valentine's Day to start using this.
Source: CreativesOutfitter.com
If none of these ideas suit you, there's always the Boyfriend Arm Pillow or Plush Beating Heart to keep the memory of you around at all times.

Recently, Owen Shifflett and I combined forces (not unlike the Wonder Twins) to work on the design for Viget's annual holiday gift (it's still January, it's not too late to write about holiday things yet, right? Right?) This year it was VigePops, yummy handmade lollipops (read all about them and maybe even snag some VigePops for yourself here). The project included branding, packaging, web design, and illustration. Lots of illustration.
Our design director Tom, Owen, and I brainstormed the concept and style of Vigepops. The words "candyland" and "winter wonderland" came up more than once. From there I quickly threw together a style board. I drew mainly from the classic, bright look of the 50s and early 60s, with excited, wide-eyed kids, and a few touches of sophisticated illustration.

The illustration style was meant to keep in that fun, 50s look with casual lines and roughly textured blocks of color. The kids used in the packaging and website were first hand sketched, then inked in. After I scanned them in, I brought them into Illustrator, live traced them, and gave the strokes a little more style. I wouldn't always recommend live tracing, but in this case, the results using it were great.
From there the newly-vectorized VigeKids went into Photoshop to get painted and shaded in Viget orange and blue.
The Dry Media brushes that come with Photoshop were used for coloring and drawing. Lots. Tons. A mega ton. Serendipidously, Onextrapixel had recently published this post listing some great Photoshop brushes that were also used gratuitously.

For the winter wonder-candy-land, I hand drew the most basic shapes, trees, lollipops, candy canes, etc. to use as a stencil in Photoshop and roughly painted over them with my trusty Wacom tablet.

The logo and packaging are meant to keep in that same line of fun and bouncy, like a candy wrapper that would put Willy Wonka to shame. Owen designed the logo in Illustrator. The label wraps around the box and holds the whole package together, while showing off the logo in style.

The web site is only one page, so it didn't need to be too complex; just get in, show our message, our photos, and get out. (And throw in a wallpaper version of the illustrations as a little bonus, so you can enjoy VigePops on your desktop year-round, with no threat of cavities or sticky-sugar fingers.) The same style from the packaging is used on the webpage, surrounded by winter wonderland illustrations to give it a fun, storybook aesthetic.
Then, like the code ninja he is, Trevor Davis built out the design in a matter of hours, so that VigePops were ready and on their way in time for the holidays (unlike this blog post).
Viget.com Blog post about VigePops and the Viget holiday gift tradition
This month’s Middle School Marketing was a discussion on saying “no” to clients. It’s probably not surprising that individuals from professional services firms were the most vocal/passionate contributors to the conversation. It was an interesting time to have the discussion - the start of a new year and on the heels of financially challenging times, which have impacted all of our companies in one way or another.
The concept of “no” or maybe more often times, getting a client to say “yes” is complex in the marketing/design arena for myriad reasons. Here were some of the big points of discussion/takeaways:
First off, go enjoy The Beatles.
The language of marketing has many dialects. As marketers and creatives, we need to speak the language of a varied client audience. A client contact that is automatically on the same page is rare, so if we want to be compelling in rationale for certain initiatives or decisions, we need to be well versed in Sales, CEO and CFO. You will get “no” more times than not from contacts that don’t understand what’s in it for them.
Clients may have distinct opinions about what they want that don’t mesh
with your sense of what is best. Those with distinct opinions likely
brought you in to execute and may not be that interested in your
opinion. You may get more latitude as the relationship progresses (or
maybe you never will), but you need to be comfortable that your
company’s reputation is associated with the work.
Josh Chambers blogged about and initiated Taco Bell’s “Drive-thru Diet”
as a likely example of a creative situation where push back was clearly
warranted and in even greater likelihood was not an option for the
executives behind this campaign.
On the opposite spectrum, many clients are really looking for our expertise and confidence in counsel and it’s our job to make sure they get it. “No,” realistically, is rarely an option. What is an option is presenting well thought out validation for why you believe in a certain decision, and then executing it with precision and passion OR doing your best to steer your client down the path of success when you’ve come to a compromise on an alternate idea.
Being a good steward of a client’s money is paramount. Sometimes “no” comes in the form of being very clear about the financial output/reward or being candid about services that the client is just not ready for. Similarly when a decision to end a client relationship is made, it’s critical to respect the money that has already been invested and set the client up for a smooth transition and best opportunity for future success.
We also discussed the internal politics of business development and client services team. BD teams work hard to get new clients in and client teams work hard to service accounts. Seems pretty simple until a client relationship goes wrong. It can seem logical to an account team to pull the “fire them” card, while the BD stresses about how to replace that revenue. Some of us have experienced scenarios of toxic client relationships that cause weight gain, tears, sleepless nights, and have the serious impact of creating negative employee morale. Of course it makes sense to sunset those relationship, but even in very black and white instances, a thoughtful process needs to be followed and can take time. It’s important for BD and service teams to work together to identify the “right” kind of clients that have the best chance for success, which often hinges on an alignment of agency and client values and culture.
At the end of the day, we all want what’s good for our clients and employees. While there are examples of blissful agency/client relationships—most are just that—relationships, requiring strong communication, compromise and yes, sometimes, “no.”
And for the record, I probably fall into both of these categories, making me the worst person alive.
You've seen the "Drive-Thu Diet" commercials from Taco Bell, right? It's a perfect example of what happens when a company loses its brand focus. What is it that people love about Taco Bell? Words like "late night," "delicious," and "guilty pleasure" come to mind. According to Zeta Buzz, the most common words surrounding Taco Bell in the online space were once "favorite," "delicious," and "love." (AdAge source). Then the Drive-Thru Diet happened.
A company that has brand focus knows what it's good at, and what it's not. Taco Bell has never been about healthy food or dieting, and by introducing the concept of "dieting" into this brand you're asking it to be something it isn't. As Jim Collins puts it in his book Good to Great, "Focusing solely on what you can potentially do better than any other organization is the only path to greatness."
"Dieting" is not the path to greatness for Taco Bell.
Furthermore, by putting the spotlight on health and dieting, Taco Bell is practically begging customers to dwell on, and comment on, the weakest part of their business (the part that destroys arteries).
In the aftermath of this new initiative, the most popular words surrounding Taco Bell are now "fat," "joke," and "stop." (AdAge source).
The Taco Bell YouTube channel hosts the above video, and the results are telling.
Fast food companies are so evil. Shame on you Taco Bell!!
Wow, total bullshit
This is THE BIGGEST JOKE ever-No wonder most Americans are walking around obese and riddled with disease. Seriously!?!?!
Did customers suddenly awake from their grease induced coma? Did they just realize Taco Bell is unhealthy? I don't think so. I'm pretty sure customers knew that all along. The problem here is that customers thought Taco Bell knew that too.
Perhaps the new Taco Bell Fresco menu isn't terribly unhealthy; but this just looks dishonest. Everyone is jumping on the health band wagon, the ad is littered with fine print, and the before and after photos look like a bad banner ad. Regardless of it's validity, it just doesn't feel honest. Customers now feel like Taco Bell is trying to pull the ol' switcheroo.
I hesitate to criticize another agency's work, and I hesitate even more to call it "harmful to advertising", but I can't get around it. This type of advertising is what gives the industry a bad rap. Ridiculous claims about weight loss, partial truths, fine print, and capitalizing on a bandwagon movement. Not to mention the fact that it looks an awful lot like the Jared campaign from Subway.
Big companies keep learning the hard way. When you lose brand focus, when you try to be something you're not, your customers will notice. Back in the day, the extent of their "noticing" took the form of complaining to a few friends. Now, however, they have a giant digital megaphone to hold companies accountable.
Taco Bell could have avoided this whole thing by recognizing what they're good at and sticking with it.
If nothing else, they could have just made a campaign that had a more realistic vibe: "Yeah, you might want a slightly healthier version of fake Mexican food, so here it is." Maybe customers would have been ok with that. But, when you claim to be a company who condones dieting and you insinuate customers could lose 50lbs by eating food that has made a name for itself by being anything but healthy, you've lost your brand focus, and you've lost customers.
One of the coolest aspects of named_scopes in ActiveRecord is their ability to chain together. One can chain a number of named_scopes onto each other, and the result is a single SQL query. Using this feature, I've come up with an easy way to do some simple searching.
To begin I'll create a search form with fields for searching on 'Artist' and 'Song title.'
<div>
<% form_tag search_songs_path, :method => :get do %>
<p>
<%= label_tag "Artist" %><br/>
<%= text_field_tag "search[by_artist]", params[:search].try(:[], :by_artist) %>
</p>
<p>
<%= label_tag "Song title" %><br/>
<%= text_field_tag "search[by_name]", params[:search].try(:[], :by_name) %>
</p>
<p>
<%= submit_tag "Search" %>
</p>
<% end %>
</div>
This form will submit it's elements as GET parameters to a search action. This is a custom action, so we need to define it in our routes file:
map.resources :songs, :collection => {:search => :get}
And in app/controllers/songs_controller.rb
....
def search
@songs = Song.search(params[:search])
render :index
end
....
The search action will call Song.search, and pass the param values attached to :search.
We need a class method in our Song model that will find records based on our params. What I want is to turn this:
{:by_name => "Twist and Shout", :by_artist => "The Beatles"}
Into this:
Song.by_name("Twist and Shout").by_artist("The Beatles")
Here's what I came up with:
def self.search(params)
params.keys.inject(scoped({})) do |found, k|
params[k].blank? ? found : found.send(:"#{k}", params[k])
end
end
And a named_scope for every key:
named_scope :by_artist, lambda {|name| {:conditions => {:artist => name}}}
named_scope :by_name, lambda {|name| {:conditions => {:name => name}}}
Let's step through what's happening in Song.search:
If I had to preform searching of a more complex nature, I might choose an library like ThinkingSphinx. If you want something quick, simple, and relativly flexible, I think this approach is worth a try.
Those of us who have iPhones had a few favorite apps over the last year (I sort of count, I got mine at the end of the year, but I've been living vicariously for quite some time now). We laughed, we cried, we sang, we shot each other. While some of our favorites are new in 2009, some of them are oldies-but-goodies that keep getting better. I thought I'd share the list with you. As a point of full disclosure (because, as you know, bloggers are transparent), I haven't used every single one of these apps (almost every one, though), but someone here has! Enjoy!





Great recipe app with filtering, a nifty feature called "Spinner" that "spins" up recipes by shaking your phone, and the ability to save favorites.

Never before has converting numbers been so beautiful and fun.

Track packages via shipment carriers and stores. Also, check your packages location on a map.

Do I even need to explain this one? Recipes! Smooth interface, favorite-ing ability, and a shopping list integration.

Create notes, sync your notes, access your notes - and it accepts notes of all media types.

Great Flickr uploader with a nice Twitter integration.

It is what you'd think it is. Some like it, some like third party apps like "Flickit" a bit better.

Track where you visit, and your friends. Unlock badges, compete with your friends, and become the "Mayor". Businesses are even offering deals to people who check in frequently. Really fun!

Similar to Foursquare, track the places you visit most. Also, track your friend's whereabouts, find items, and unlock pins. Beautiful design.

Create grocery lists and sync via the cloud with others. My wife adds something on her phone, I see it on mine. Also, it learns the order you go through the store and rearranges your list accordingly.

Use a Safari bookmarklet to save websites to read later, syncs automatically to the iPhone and a few other apps also.

The famous Amazon reader on the iPhone.

Stream live video one-to-one with anyone who has the app. Look for this genre of apps to take off in the near future.

Amazing way to measure your environmental impact through eating. Tells you what food is local, where it's coming from, and where your closest farmers market is. Also, really nice design.

Mint continues to shine with efficiency, a great UI, and a fun way of tracking your finances. Tag your purchases on the go and view your budget. Also, it has an additional security pass-code option.

There are a few apps out there for the DC Metro, but this one is pretty cool. It provides next train information, maps, and a snazzy augmented reality integration to find the closest metro stop.

Love this app. Shows you new cinema releases, the latest DVDs, trailers, and all with a wonderfully integrated Rotten Tomatoes score. Also, it flawlessly integrates with Netflix.

For our favorite NYC employee - displays alerts, subway maps, and a Google Map mashup.

Book your reservation with OpenTable's iPhone app. It's been around for awhile, but it's still great.

A simple weather app for those of us who care about how the app looks. Super simple with fun animations and gestures.

For those of you who aren't a big fan of the Google Reader interface, Reeder provides a desktop integration for Google Reader.

Uses GEO location to keep track of your runs. A cheaper (and some people claim "better") alternative to Nike+.

Does anyone not yet know what this is? It magically hears a song and tells you what it is. Magic.

We still love it! Ahtough, I can't seem to find it in the iTunes store right now...very strange.

An alternative to Last.FM and Pandora, this has a few extra tweaks that make it just the right fit for some of us.

This actually pulls in real radio (yes, it still exists). Listen to NPR...or...ummm...annoying loud people in the morning.

For the photo lovers, Tiltshift Generator generate fake depth-of-field effects on your photos.

This is a late contender, but it basically allows you to manage your Mac via your iPhone. It's incredibly powerful, but tough to describe. Check it out.

If you're not using TripIt, you're really missing out. TripIt has a free and paid app (much like their free and paid service) and both make traveling much, much better.

One of our favorite Twitter apps - when it's not hogging all the memory.

This gets the most votes from Viget peoples.

And yet another (free) Twitter app we enjoy.

Through a series of filters and questions, UrbanDaddy helps you determine where you should go next (food & drink related). Pretty cool idea, and a great example of branded utility.

Love! It's free texting/chatting with push notifications, theme customization, and full MMS capabilities. It integrates flawlessly with your contacts, and has a super smooth interface.

If you find yourself whiteboarding quite a bit, this app helps take photos of those whiteboards that are actually legible.

If you're into impulse purchasing, this is your app! If not, the descriptions are always hilarious.

Addicting game in which you have to jump your way to freedom. Decent music too.

This thing won more awards than I can count (I can't count very high). It calls itself the "Tetris and Bejeweled of the Tower Defense genre" whatever that means.

This game is so addicting. Land aircraft on runways while listening to your iPod music (or, go with sounds off). Amazing.

Take a crash test dummy to its limit by knocking him around. Sounds violent...it sort of is, but fun violence!.

Man this game is just fun! You roll these little fella's around by tilting your iPhone, and their voices take me back to the days of ToeJam & Earl.

Guns. Design. Web. iPhone. Yes please.

This app is good old fashioned humor. Carl repeats whatever you say in a crazy voice. Also, you can actually interact with him to get him to laugh, yell, etc. (think Elmo, but less creepy).

Sort of like TalkingCarl, you just record a message and select a funny voice to repeat back what you said. It also happens to be quite pleasant to look at.

Very similar to Scrabble, allows you to play "Words" with friends. Words and friends sold separately.
Hoped you enjoyed the list!
NOTE: If you are using the standard tracking method (ga.js), please see the post entitled How to Track Internal Links in Google Analytics. This post is intended for Asynchronous Tracking.
What do you do if you want to track how often a link on your site is clicked? How do you see those links in Google Analytics (GA)? What filters and profiles should you create to accurately track this information? Hopefully, I can accurately answer these questions -- some of them in this post, some in the future. *Warning* if you don't care about web analytics, reading this post may produce seizures and a strong desire to fall asleep on the job.
First, why in the world would you want to track internal links? Isn't that stupid idea?
Second, which method of link tagging should you use?
_trackPageview or utm?_trackPageview JavaScript creates a fake pageview. This method is most commonly used to track outgoing links or downloads. The _trackPageview attribute is placed within the <a> tag and looks like this: onClick="javascript: pageTracker._trackPageview('/example/blue-link'). The /example/blue-link becomes a new pageview and it is tracked in GA as a new pageview - so it will increase your pageview count. These fake pageviews will now show up in your "content" reports. More information._trackPageview method.
utm, it erases all referral information. In other words, if someone arrived at your site via a Google organic search; but they then clicked on an internal link that had the utm tagging, you lose the insight that the visitor originally came from the Google organic search (the source and medium). Instead, you see the new source and medium information that you manually entered. For me, that's not a good option. I need to know where the visitor came from as much as I need to know what internal links they're clicking on. Again, check out Epikone's post._trackPageview creates a new pageview, you can use custom filters to include and exclude those pageviews from GA profiles (more on that below). Also, you don't lose any referral information.Now then, to answer the questions I began this post with...
<body> tag. We do this so that if GA is loading slowly (which rarely happens), your whole page can load before GA kicks in and it won't have to wait up for a slow GA server. However, in this method we're tagging links within the <body> tag, so GA needs to kick in before those tagged links load on the page. The GA tracking script should be placed after the opening of the <body> tag, and before your tagged links, to ensure your tagged links load after the GA tracking script. Make sense? _trackPageview variable to your links.onclick="_gaq.push(['_trackPageview', '/outgoing/example.com']);" to your links. Once done, your link should look like this:<a href="http://www.example.com" onclick="_gaq.push(['_trackPageview', '/outgoing/example.com']);"internal-links/blue-link. Even better: internal-links/homepage/blue-link. The reason? If you're trying to quickly find and filter your manually tagged internal links, having a generic base directory like "internal-links" makes your life a lot easier. Creating each link with only one subdirectory like /blue-link and /pink-link and /red-button means you would have to search for each of those URLs manually -- boorrrrrring./internal-links/homepage/blue-link, we would enter this into the "Subdirectory" field
^/internal-links/homepage/_trackPageview code exactly as is and check to ensure your filters are implemented properly if you're using them. If you're stuck, feel free to drop a comment and we'll get back to you.There you have it! I'll write another post on how to use these pageviews for goal tracking, but in the meantime please don't hesitate to ask questions if this was confusing, and please let me know if I'm off base.
I'm going to go get some exercise or something...my head hurts, and I've fulfilled my nerd quota for the week.
When reflecting on my creative intake and output over the past year, they simply stand out: the three guys that cursed a lot but admittedly got me fired up creatively. Simple as that, but with warnings:
Let us know if something has you passionate going into the new year.
I am, to put it mildly, not a fan of the typical New Year’s resolution. Generally, it’s much too vague, it runs over much too long a time scale, and it’s not supported by appropriate subgoals (monthly, weekly, and daily). In effect, they’re feel-good affirmations, but they’ve got no teeth.
I’m always optimistic about well-structured and supported resolutions, however, and the start of a new year is the second-best time to launch them (the best time, of course, is today). Think of these as overarching priorities that can help guide your shorter-term planning efforts throughout the year to come; as you sit down at the beginning of every month, week, and day, these resolutions provide a framework to fit projects and tasks into.
So, without further ado, here are some suggested New Year’s resolutions for developers:
The "years of experience" requirement that so many HR departments rely on is bunk; we’ve all met developers who’ve been working continuously for a decade, but they’ve not grown at all. In effect, they’ve repeated the same year (or month, even) of experience over and over. Make a commitment that this year will be different!
The technical world presents boundless opportunities to learn, from new languages (as the Prags suggest), to new frameworks and applications. My personal pick for the most exciting area of development at the moment is the alternative database (e.g., NoSQL) scene, but there are changes afoot everywhere.
The best way to improve at something is to practice deliberately – work at a task specifically designed to help you improve, pay careful attention to your results, and modify your performance appropriately when repeating the task. Note that this isn’t the same as, say, starting a side-project to learn a new web framework. If your goal is something other than pure practice, then you won’t actually be practicing.
There’s always a problem out there to be tackled, so start a new open-source project or contribute to one that already exists.
OK, this one’s a bit more specific than the others, but let’s face it: most people don’t test their JavaScript. There’s been a surge of development in testing tools over the past year, so isn’t it about time you took a look at some of them?
Happy New Year!
With the release of Vanity and the availability of the Google Analytics Data Export API, we now have two additional tools to further understand how users are navigating our web applications. I’d like to share how we go about comparing metrics tracked in Vanity to data pulled from Google Analytics with Garb.
Goals can be simply defined as important destinations on our websites and in our applications. When a user gets to a page, or submits a form, or books/buys something, etc., we count that as a goal.
Bounces are another way of saying “leaves”. When a user visits a single page, and then exits their browser, or navigates to another site entirely, we see a bounce.
Vanity is a new library for A/B testing and for making it much easier to track goals. Vanity may or may not have been a result of Nathaniel Talbott’s talk on EDD (Experiment Driven Development) which stressed the importance of feature development through experiments (A/B testing) and encouraged the audience to create a tool to make this sort of testing much simpler. Assaf Arkin has achieved this or at least moved us all much closer to the ideal.
I’m not going to talk about using Vanity to set up A/B tests. I am going to show how easy it
is to make a comparison between data collected with Vanity via the track!
method as compared to data pulled from GA with Garb.
Given the case where we are tracking a registration form, on the page “/users/new”, being submitted:
metric "Registration Goal" do
description "Measure registration goals."
end
This would go in experiments/metrics/registration_goal.rb as per the Vanity documentation.
We can now track this metric in our controller:
def create
@user = User.new(params[:user])
if @user.save
# track the metric "registration"
track! :registration_goal
self.current_user = @user
redirect_to root_url
else
render :action => :new
end
end
Once we’re tracking that data we can work to pull the other half of our analytics from Google.
Using Garb we’ll be creating a report that tracks pageviews for a particular page path (/users/new) from earlier.
To work with Vanity — having it report the metrics from GA and graph them for comparison — we need to add
a method: values which takes start and end dates.
Here’s what our metric looks like with these added:
metric "Registration Pageview" do
description "Measure page views for /users/new"
def values(from, to)
report = Garb::Report.new(GARB_PROFILE, {:start_date => from, :end_date => to})
report.metrics :pageviews
report.dimensions :page_path, :date
report.sort :date
report.filter :page_path.eql => '/'
# hack because GA data isn't returned if it's 0
data = report.results.inject({}) do |hash, result|
hash.merge(result.date => result.pageviews.to_i)
end
(from..to).map do |day|
key = day.strftime('%Y%m%d')
data[key] || 0
end
end
end
Note that I’ve set a constant GARB_PROFILE to the profile I wish to use. I have
yet to determine a more effective way to pass the profile for use by Garb.
We can see that Garb + Vanity Metric (first image) matches the graph provided in GA (second image):


With this data pulled into the Vanity dashboard, it is trivial to compare the number of visitors to a registration form to the number who completed registration. Thus, we’ve created accurate goal tracking and, by comparing the two sets of data, it is fairly easy to see the bounce rate for the registration page.
This past Saturday we hosted the third Hackday at Viget Labs (photos). For this round we decided to tackle meta-programming, one of the more mystical features of Ruby. We found, through several conversations at Hacknight, that people were getting the hang of Ruby through their work with Rails, but weren't clear on how things like the find_by_* methods were implemented. Unlike the previous Hackdays we took a more structured presentation approach, but that didn't reduce the usual amount of collaboration and discussion that makes Hackday unique. We even had a brave audience member do some live meta-programming!
The morning was divided up into two topics: The Ruby Object Model and Meta-Programming in Ruby. I covered the first section in an effort to lay down some foundational knowledge regarding Ruby's internals. Matt did a great job building on top of that material and was able to show some real world examples of meta-programming. He had an audience member come up and live code a pure Ruby implementation of attr_accessor and followed that up with a break down of how Rails uses meta-programming to dynamically build find_by_* methods.
Thanks to everyone who was able to make it to the event! If you weren't able to make it, or want to go over the topics we covered, we've put up our notes and code examples on GitHub. Feel free to email me or Matt with any follow up questions. We hope to see you all at the next HackNight.
As 2009 comes to an end, we decided to use our last Middle School Marketing of the year to review our predictions for the past year and discuss our new ones for 2010. Here goes...!
So there you have it: MSM’s collective predictions for 2010. Do you have any predictions that we didn't cover here? If so, please share!
We are certainly at an interesting point in time with the web. There are new techniques being created every day, and as developers, we have the privilege of deciding how and when to use them. I'm the new guy at Viget (only been here a few weeks), and every company is different, so it is interesting adapting to Viget's standards. Some companies utilize progressive enhancement more than others, and I love that we utilize it when we can.
One big item for me is how much we use CSS3. Yes I know, it is not fully supported across all browsers. If you still want everything to look exactly the same across all browsers, you should probably just close this article and not read about CSS for another 10 years. A user is not going to pull up your site in two different browsers to compare the experience, so they won't even know what they are missing. Just because something is not fully supported, that does not mean that we can't use it to an extent. In this article I'll show you some practical uses for CSS3.
Note: If you are viewing this page in an inferior browser (ahem: Internet Explorer), you will see the degraded versions of the examples. So go download a new browser: Firefox, Safari, Chrome
This is probably the most used CSS3 property. If you want rounded corners, and they aren't essential to your design (which they rarely should be), then this is the way to go. If a user's browser doesn't support it, then you get square corners, no big deal.
One simple use of border radius is for form elements. Here is an example of your standard text input with a button with some simple styling:
Submit
Definitely nothing special and some people may not even realize that the submit button is actually a button. Sure, you could swap it out with an image, but that solution can be a pain if you have a site with a lot of different buttons. So let's just add a little CSS3 and make the input and button a little more attractive.
Submit
Who would have thought simple rounded corners make the form so much more appealing?
.radius {
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}
So this code is just saying to give each corner a 3px radius. Just like properties like margin and padding, you can pass in multiple values to account for each corner.
.radius2 {
border-radius: 3px 6px 8px 10px;
-moz-border-radius: 3px 6px 8px 10px;
-webkit-border-radius: 3px 6px 8px 10px;
}
Submit
It is unfortunate that we have to add the -moz-border-radius and -webkit-border-radius properties, but like a lot of the other CSS3 properties, browser makers are adding proprietary properties until the official properties are finalized.
One really confusing thing to note here is that the syntax for rounding a single corner is different for Firefox than the W3c spec:
Using an RGBa color value is the same as using an RGB color, except that you can pass in a value to specify the amount of transparency (the "a" in RGBa stands for alpha).
RGBa is awesome because unlike opacity, only the background is given transparency. You can get a nice effect by applying RGBa to a caption overlayed on top of a photo.
The code necessary to use RGBa is very simple:
.caption { background: rgba(255, 255, 255, .5); }
But, if you are using a browser that does not support RGBa, there will be no background color at all. So, you should specify a normal background color first:
.caption {
background: rgb(235, 243, 240);
background: rgba(255, 255, 255, .5);
}
"The Bean" - Chicago
Just so you can see the difference between RGBa and opacity, let's use the same example but use opacity instead of RGBa.
"The Bean" - Chicago
It may be a little hard to notice, but the actual text in this example is semi-transparent as well.
.caption {
background: #fff;
filter: alpha(opacity = 30);
opacity: 0.3;
}
How many times have you had to nest multiple elements just because you couldn't add multiple background images to a single element? I can't even count the number of times. So let's pretend that you wanted to create a box like the following image, but it could have varying amounts of height.

To create a box like this, you would need to split our larger image into the following pieces:
Top:
![]()
Bottom:![]()
Repeating Background:![]()
Since we have multiple images, we would need three elements to attach them to. So that stinks, we are adding extra elements just for presentational purpose. This is a perfect example where we can utilize multiple backgrounds on a single element, and if a browser didn't support it, then it would just get a solid background color.
Here is the code we would need to attach multiple background images to a single element:
.multi-bg {
background: url(/image/css3-multi-top.png) no-repeat,
url(/image/css3-multi-bottom.png) no-repeat 0 100%,
url(/image/css3-multi-repeat.png) repeat-y;
background-color: #516ac4;
}
The images are layered onto the element with the first image being the top-most image. You add the images in a comma separated list, and I would recommend setting the background-color as another property so that browsers that don't support multiple background images at least get the solid color.
Note: Mozilla browsers don't even support this property yet, check it out in a Webkit browser.
Drop shadows are such a pain to add to elements, especially when there can be a varying amount of height or width. Adding drop shadows is now as easy as adding a few lines of code, instead of messing around with multiple images and multiple elements.
.box-shadow {
box-shadow: 6px 6px 4px #cecece;
-moz-box-shadow: 6px 6px 4px #cecece;
-webkit-box-shadow: 6px 6px 4px #cecece;
}
The first value is the horizonal offset, and the second is the vertical offset. The third vaue is the blur radius. The higher the number, the less sharp and larger the shadow becomes. The final value is the color of the shadow. Note: RGBa colors are allowed too.
If a browser does not support box shadow, then there is just no drop shadow. No big deal in my opinion.
Text shadow is actually a CSS2 property, but it's finally starting to be supported, so that's why I'm mentioning it. It has the exact same syntax as box shadow, and if a browser doesn't support it, then you get no shadow.
.text-shadow {
text-shadow: 1px 2px 3px #1a1a1a;
}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vitae risus ac nisl imperdiet semper.
I actually don't think multi column layout is that useful, except for shorter columns of text, but it is pretty cool.
.columns {
-moz-column-count: 3;
-moz-column-gap: 1em;
-moz-column-rule: 1px solid black;
-moz-column-width: 200px;
-webkit-column-count: 3;
-webkit-column-gap: 1em;
-webkit-column-rule: 1px solid black;
-webkit-column-width: 200px;
}
You actually wouldn't use all of these properties together, but these are the supported properties for multiple colummn layout. As of right now, these properties are only supported by Mozilla and Webkit browsers, so the -moz and -webkit prefixes are needed. If a user is not using a Mozilla or Webkit browser, they would just see a single column of text.
The column count property takes an integer value that specifies the number of columns. The column gap specifies the width of the gap that should be between the columns. The column rule describes the rule between the columns, and the format of this property is just like the border property. The column width property accepts a width as a value that describes how wide each column should be. Typically you would use column count or column width to set the number of columns.
In this example, we will use column count instead of column width:
.columns {
-moz-column-count: 2;
-moz-column-gap: 20px;
-moz-column-rule: 1px dotted #666;
-webkit-column-count: 2;
-webkit-column-gap: 20px;
-webkit-column-rule: 1px dotted #666;
}
The reason that I think this type of layout is only good for shorter passages of text is because if the columns were taller than the height of the viewport, I don't think it would be a great user experience.
This was honestly one of the most confusing properties that I had ever read about. But once you take your time to understand it, it's very useful.
.border-img {
background-color: #516ac4;
border: 10px solid;
border-image: url(/image/css3-border-img.png) 10 10 10 10 repeat repeat;
-moz-border-image: url(/image/css3-border-img.png) 10 10 10 10 repeat repeat;
-webkit-border-image: url(/image/css3-border-img.png) 10 10 10 10 repeat repeat;
}
We are going to recreate the same example that we used for the multiple background images, using a single image. Here is the image we will use:
![]()
The value specified in the border property is how wide we want our image to be bordered around the element. The first value in the border-image property is the actual image we are using for the border, and the next four values are setting up the coordinates so it knows which piece of the image to use where. Just like many other CSS properties, the first coordinate is the top value and works its way around clockwise. The final values in the property tells the code to repeat the portion of the image on all sides to fill in the gaps.
In the next image, you can see our small image, enlarged, and with guides setup to match the coordinates in the CSS.

It's pretty cool that we can create this box with just one tiny image, thus reducing our number of HTTP requests.
These are just a few examples of practical uses of the new features in CSS3 that I think you can start using today. I'm sure there are many other examples, so get in the holiday spirit and share in the comments.
Shopping for a nerd this holiday season? A difficult proposition, to be sure. We are, after all, complicated creatures. Fortunately, Viget Extend is here to help. Here are some gifts your nerd is sure to love.
Lacie iamaKey Flash Drive ($30)
If your nerd goes to tech conferences with any regularity, your residence is already littered with these things. USB flash drives are a dime a dozen, but this one’s different: stylish and rugged, and since it’s designed to be carried on a keychain, it’ll always around when your nerd needs it.
AeroPress ($25)
A simple device that makes a cup of espresso better than machines costing twenty times as much. Buy this one for your nerd and wake up to delicious, homemade espresso every morning. In other words, it's the gift that keeps on giving. If espresso gives your nerd the jitters, you can’t go wrong with a french press.
SimpleBits Charge Tee ($22)
Simple, vaguely Mac-ish graphic printed on an American Apparel Tri-Blend tee, no lie the greatest and best t-shirt ever created.
Hard Graft iPhone Case ($60)
Your nerd probably already has a case for her iPhone, but it’s made of rubber or plastic. Class it up with this handmade leather-and-wool case. Doubles as a slim wallet if your nerd is of the minimalist mindset, and here’s a hint: we all are.
Ignore Everybody by Hugh MacLeod ($16)
Give your nerd the motivation to finish that web application he’s been talking about for the last two years so you can retire.
Moleskine Notebook ($10)
What nerd doesn’t love a new notebook? Just make sure it’s graph paper; unlined paper was not created for mathematical formulae and drawings of robots. Alternatively, take a look at Field Notes. As for pens, I highly, highly recommend the Uni-ball Signo.
Canon PowerShot S90 ($400)
Packs the low-light photographic abilities of your nerd’s DSLR into a compact form factor that fits in his shirt pocket, right next to his slide rule.
If all else fails, a gift card from Newegg shows you know your nerd a little better than the usual from Amazon.
Moto Guzzi V7 Classic ($8500)
Actually, this one’s probably just me.
If your nerd is a little more design-oriented, check out Viget Inspire for ideas from Owen and Rob. Got any other gift suggestions for the nerd in your life, or ARE YOU YOURSELF a nerd? Link it up in the comments.
As you know, Facebook recently changed its privacy policy. What you may not know, is this seeming act of goodwill is actually a pretty sketchy effort to make user information even more public. There is definitely value in the change; but the roll-out of those changes, and the accompanying spin, is (for me) disconcerting.
It's important to know where I'm coming from when writing this post. What makes Facebook worthwhile for me (and many of us) is the "privacy." I am, of course, aware that if it's online it's not really "private" in the traditional sense. However, aside from email, Facebook is my last "private" digital hangout. Whereas from a privacy perspective I care very little who sees/follows my Twitter account, and I actually want lots of people visiting my web sites; I'm very selective with my Facebook friends. I want a place to talk with family and friends without strangers po pping by to say hello (especially search engines).
There, I said it. Before you keep reading, I want to be clear that this post is in no way suggesting this is a catastrophe. It's up to you to decide, but in the end it's just making Facebook...different. The settings themselves are actually great; it's the reason for, and presentation of, the settings I'm not thrilled with.
The Spin Is Nauseating
[Becoming less private and more public is] 'a change just like it was a change in 2006 when Facebook became more than just people from colleges...Facebook is changing.'
Still not convinced? In the middle of their "Guide to Privacy on Facebook" you will find this:
We recommend Everyone be able to see information that will make it easier for friends to find, identify and learn about you. This includes basic information like your About Me description, Family and Relationships, Work and Education Info, and Website, as well as posts that you create, like photo albums and status updates.
Thanks for the recommendation, Facebook. You're so great to look out for me like that.
And what will happen to all those public facing status updates? "Bing will be getting access to Facebook ‘Everyone’ status updates in early 2010."
I understand Facebook has to make money. If ads are the only way to keep it alive, then so be it. Just shoot me straight on why you're doing what you're doing. And don't go changing the entire purpose of the web site just because you need more ads. I'll pay to use the darn service if that means it stays true to its nature and I can stop seeing those god-awful "Cartoon Yourself" and "Get-Rich-Quick" ads.
The "Default" Settings Are Public
If you changed your Facebook privacy settings in the past, your settings will persist. However, if you're one of the 80 to 85 percent of users who have never changed your privacy settings, your default settings will now be "public." In other words, Facebook is banking on 80 to 85 percent of their user base "accidentally" being fully public. Shay-dee.
Again, it would be unfair to not point out how easy the new privacy settings are to change and understand; but that doesn't seem to be the point, does it?
Most of us don't have anything to hide in terms of inappropriate content (most being anyone over the age of 18...have you seen high schooler's Walls?). Honestly, I'm not even mad that Facebook is changing so drastically, perhaps it really will result in a "better" experience for some - I'm just not really one of those "some." What makes me nervous is the underhanded tactics Facebook seems to regularly employ when undergoing "improvements."
I recently had a chance to implement a custom design for a Shopify storefront. This was my first time using the e-commerce site, but Shopify's known to be easy to customize and work with, so I was excited to dive in and take on the challenge. I was ready for Mount Everest or Iditarod levels of difficulty; Triathalon-hard at least. But luckily, Shopify makes customizing a shop's design very easy even for the less-than code savvy like myself, and its customizing tools make the process as painless as possible.
What makes Shopify extra easy (and fun) for designers to hit the ground running is Vision. Vision is an application that lets you edit and run a sample Shopify store locally on your computer. I just downloaded, installed, and within a few minutes I was editing free templates provided by Shopify and already seeing the storefront in action. There were sample products and pages already set up, so I could focus solely on the design. When I'd finished my masterpiece, I simply zipped up the files, uploaded it to Shopify, and Shopify did all the installing for me.

Vision: Create it, Modify It, and Export. Don't Get Much Easier.
Of course there's always changes and little edits to make to the design once it's up. Shopify lets you edit in your browser easily and automatically saves all past versions of your code, so you can quickly backtrack if something goes terribly awry. It saved me a time or two... dozen.
These easy editing tools, for working either online or off, make implementing a design for Shopify downright enjoyable.
Shopify uses its very own templating language, Liquid, to create a store. It's pretty simple to get the hang of, but start by editing one of the free templates provided rather than writing the code from scratch, at least until you feel comfortable with Liquid.
There are a number of liquid templates that make up a storefront. The general layout formatting is mostly housed in theme.liquid, so you really only need to go into the other templates when changing what content wil be displayed, not how it'll be displayed.
For exact and easy control over which products show up where, categorize them by collections.
Each product page automatically has a dropdown menu from which the user picks a version of the product. This is great if the store sells clothes in small, medium, or large sizes or in different colors. But if there are no variations of the product, there's not an automatic way to take out the dropdown menu. Hunting through the forums for a solution was harder than I'd imagined, since it seems like it's a fairly common issue. After finally finding it, I feel like I must share my hard-won code knowledge:
{% if product.variants.size > 1 %}
<select id="variants" name='id'>
{% for variant in product.variants %}
<option value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}</option>
{% endfor %}
</select>
{% else %}
<div id="no-var-price"><input type="hidden" name="id" value="{{ product.variants.first.id }}" /> {{ product.price | money }}
</div>
{% endif %}
Overall I found implementing a design on Shopify fun, with a quick learning curve. Anyone else work with Shopify before? How'd you like it? Got some good tips or suggestions for us beyond my most basic of offerings?
For the times I have needed to pass parameters to my Rake tasks from the command-line, I have always used environment variables (as Ryan describes in this post from 2007). I recently checked out the parallel_specs plugin to see if I could get some noticeable performance improvements when running the test suite for one of my current projects. I didn't in this case, but I saw something in the documentation that caught my eye:
$ rake parallel:spec[1]
$ rake parallel:spec[models]
$ rake parallel:test[something/else]
I have been using Rake forever, but those bracketed options were something new. I thought that it was something Michael added as part of his plugin, but it turns out that this feature is built directly into Rake itself. It was first introduced in this commit and made available as part of version 0.8.1.10. Configuring your tasks to use this feature is simple. I'll show you how.
The basic task definition needs no explanation:
desc "Basic call and response"
task :call do
response = 'Task'
puts "When I say Rake, you say '#{response}'!"
sleep 1
puts "Rake!"
sleep 1
puts "#{response}!"
end
To configure the response each time the task was called, I would modify it to take the response as an argument using this syntax:
task :call, :response do |t, args|
response = args[:response]
...
end
The task description now reflects this change:
$ rake -T call
rake call[response] # Basic call and response
Now when I invoke this task with a parameter, the args hash will contain :response as the key and the value I supply to the parameter:
$ rake call[Task]
When I say Rake, you say 'Task'!
Rake!
Task!
However, the argument is not required so I get some strange results when I don't provide one. I can fix that quickly by setting a default value:
task :call, :response do |t, args|
response = args[:response] || 'Task'
...
end
This feature isn't limited to a single argument. In fact, I can pass as many as I want:
task :call, :response, :repeat do |t, args|
response = args[:response] || 'Task'
repeat = (args[:repeat] || 1).to_i
puts "When I say Rake, you say '#{response}'!"
repeat.times do
sleep 1
puts "Rake!"
sleep 1
puts "#{response}!"
end
end
And I call it in a similar fashion:
$ rake call[Hoe,2]
When I say Rake, you say 'Hoe'!
Rake!
Hoe!
Rake!
Hoe!
If you're used to specifying task dependencies using the hash syntax, don't worry. It's still possible to do this when passing arguments, but the syntax is a bit different:
task :microphone do
puts "Check 1, 2, 3"
end
task :call, :response, :repeat, :needs => :microphone do |t, args|
...
end
The dependency is called as expected:
$ rake call[Hoe,2]
Check 1, 2, 3
When I say Rake, you say 'Hoe'!
Rake!
Hoe!
Rake!
Hoe!
FeedStitch has a task that starts the update process for a subset of the feeds users have added. It looks something like this:
namespace :feedstitch do
desc "Perform a rolling update of all feeds"
task :update_feeds => :env do
hours_for_full_update = 24
Updater.rolling_update(hours_for_full_update)
end
end
The hours_for_full_update local variable is there for clarity, but having that value hardcoded into the Rake task means a code change and deploy each time we need to change it. Specifying this update frequency when the task is called would eliminate that complexity:
namespace :feedstitch do
desc "Perform a rolling update of all feeds"
task :update_feeds, :hours_for_full_update, :needs => :env do |t, args|
hours_for_full_update = (args[:hours_for_full_update] || 24).to_i
Updater.rolling_update(hours_for_full_update)
end
end
Now, when calling it I can configure how many feeds are updated at a time:
$ rake feedstitch:update_feeds[12]
This approach works great in those cases where you can use Rake for command-line scripts, but you need just a bit more configurability. If your project requires a custom Ruby script to be run from the command-line, I recommend Trollop for a lighter-weight solution or even Thor if you need something more advanced.
This group is pulling updates from:
© 2009 Pointless Corp. All Rights Reserved
Feedback or feature suggestions? Get in touch: feedback@feedstitch.com