zerosum dirt(nap)

evolution through a series of accidents

zerosum dirt(nap)

Rails + Angular + Jasmine: A Modern Testing Stack

January 17, 2014

When I started on my first Angular+Rails project around 12 months ago, there wasn't a lot of guidance around code organization, interop, and testing, and we got a lot of these things wrong. Since then, I've worked on several additional projects using the same tech stack and have had several more chances to screw it up all over again. After a few of these, I feel like I've finally got some conventions in place that work well for better code organization, interop, and testing.

This morning the team over at Localytics (hi Raj!) wrote up a good retrospective on their use of Angular + Rails over the past year, including lessons they learned and ongoing challenges. They touch on several of the same issues that my colleagues and I have run into, and the writeup inspired me to dust off my old busted blog to document some of my own findings.

Testing Your JavaScript Has Never Been Easier

One area that I felt like needed some further clarity was testing. In particular, how a Rails-centric application can cleanly and easily integrate tests around Angular frontend logic. Fortunately, once you figure out how to set this up, you'll find that unit testing Angular code in Jasmine -- especially controller and factory code -- is surprisingly easy to do. It's really the first time I've been sufficiently happy with a frontend testing configuration.

To see a working example for yourself and hack around with it, go snag the sample project I pushed up to GitHub. Bundle and run it, and play around with the shockingly awesome todo list application. Because the world really needed another one of those. When you've had enough of that, take a look at the contents of the spec/javascripts directory.

We're using the jasmine-rails test runner with CoffeeScript here, because that's what works for me (sorry Karma). Pay close attention to the spec_helper.coffee, which does much of the dependency injection needed to provide clean and intuitively named interfaces in our example controller spec.

This gives us nice ways to interface with the factories and controllers we're defining, as well as Angular's own ngMock library (super useful for stubbing server-side endpoints), the event loop, and even template compilation for partials and directives. A couple of these are illustrated in the sample controller spec shown here:

Jasmine's syntax should be very familiar to anyone who does RSpec BDD work, and the work we've done in our spec helper really cleans up the beforeEach setup that's required in each individual controller spec. These particular tests make heavy use of ngMock, which you won't always need to use, and the calls to flush() are required to fulfill pending requests, preserving the async nature of the backend but allowing the tests to execute synchronously.

Testing Continuously With Guard

Although the Jasmine web interface is nice, but I'm a big fan of using Guard in order to watch for filesystem events and kick off automated test runs from the command line. By including the guard-jasmine gem and updating our Guardfile we can continuously test both our server-side RSpec logic and the Jasmine unit tests all at the same time through a single interface:

One thing I haven't addressed here is directive testing, which can be a bit more difficult. I'll try to address that in a future post, or if you have your own recipes, feel free to link em up in the comments.

Special thanks to Mark Bates for working with me on early versions of this approach, and convincing me that Angular was worth looking at in the first place.

comments »


 

The Wefunder Petition

January 31, 2012

Kickstarter is awesome for funding creative projects. It's one of my favorite startups at the moment, and it's important. I lurves it. But I wish I could use something like it to invest in actual companies -- both the tech startup variety that my friends work tirelessly on as well as the hyper local variety that make those special tapas that my neighbors are raving about.

Soon, new laws may allow us to do just that.

To raise awareness of these initiatives, a few friends and I tossed together the Wefunder petition to support HR2930 and Brown's Democratizing Access to Capital Act (S.1791) that's currently being debated in the US Senate. Please go sign it (click the "learn more" link on the site for more background information and links).

These changes could have huge impacts for both entrepreneurs and investors, allowing bold new ideas to surface and creating a ton of important jobs and opportunities. I know that sounds like marketing speak, but it's true. This matters.

We'll be going to DC next week to talk to Senator Brown's people and see what else we can do to push this forward. We've also been fortunate enough to get some great coverage for our efforts at BoingBoing and ReadWriteWeb.

Now go sign it already and help us send a message. And thanks!

comments »


 

Building Android Apps with Mirah and Pindah

July 24, 2011

I gave a talk earlier today at the Beijing Ruby group today about my experiences building native Android applications with Mirah and Pindah.

Although I generally prefer building mobile web apps over native development when possible, Mirah is a really promising alternative to Java if you have to go native. Check out the preso below and get involved in the growing Mirah / Android community.

 

Make sure to grab the code for the example app on the GitHubs.

comments »


 

Geeks on a Train

July 21, 2011

Geeks on a Train starts today!

This afternoon I'm leaving Dalian, Liaoning, China for a 10-day journey across China's tech ecosystem with a bunch of awesome startup founders. By train. Because planes and buses are so last year (kidding!)

With stops scheduled in both Beijing and Shanghai and a number of events on the schedule including tech meetups, company visits, mentor meetings, and a 10x10 mini conference in each city, it's bound to be a fun -- and delightfully chaotic -- experience. I've really been looking forward to it.

Geeks on a Train (affectionately referred to as "GOAT") is part of the Dalian-based Chinaccelerator program, which is a Chinese startup accelerator run by program director Cyril Ebersweiler and the good folks at SOS Ventures. I was fortunate enough to meet them through TechStars in 2010 and am really honored that they invited me over to work with some of the young teams that were accepted into the program this year. How could I say no?

Chinaccelerator itself is much like TechStars, but with a brilliant international twist. The startups here aren't just from China, they're from all over the world, with founders from Malaysia, Canada, Italy, the Phillipines, India, and England, as well as China and the United States. It's really fascinating to experience both the similarities and differences of startup life on the other side of the world, but a couple philosophies remain as constants: heartfelt motivation and JFDI are key.

In the two weeks I've been here thus far, I've seen the founders produce some truly great work and make impressive progress. It's good stuff. There's nothing more inspiring than being trapped in a room full of crazy entrepreneurs with wildly different backgrounds who are trying to change the world.

But I can only imagine what it's going to be like trapped on an overnight train with them :).

comments »


 

Rails Rumble Alumni Archive Project

July 02, 2011

2011 has been a busy year so far, full of interesting contract work, side projects, research for the next startup biz, upcoming travel plans, and not-so-fun family health issues. It's also been a busy year for the other Rails Rumble organizers, and as such there's currently no date set for the 2011 contest. Sorry about that :(.

Rails Rumble

However, I did find a few hours last weekend to design / develop a Rumble-related mini-site that's been long, long overdue...

The new Rumble Alumni Archive is a searchable archive of websites and applications that were developed during contests past and present. It's a great way to see what entries are still online and browse them by year (2007-2010), country of origin, and award.

Over 80 entries that were developed during Rumble events are still online, most of which have evolved pretty significantly since their initial weekend development sprint. For just a couple examples, check out Mocksup, Awesome Fontstacks, and IOU Mate (formerly "nDebted"). Go poke around the archive to see others; there's some really great stuff in there. And please let us know (contact organizers at railsrumble dot com) if we've left anybody out.

I know I've said it before, but it's really inspiring to me when I think about how many of these polished web apps started off as as disposable weekend experiments. Kick. Ass.

comments »


 

Song Lyrics Without the Suck

June 26, 2011

Over the past few years I’ve been involved with a number of music-related web projects, including Loudwerkz, DMOD (RIP), and Ink19. Although I make my living on the web and love what I do, I suppose I’ve always been secretly jealous of those snobby record store employees who hear the latest cuts first, troll naive customers, and argue with their coworkers about song meanings :).

Fortunately, the web has lowered the bar to all of that stuff. What would we do without Pandora, Last.fm, and (more recently) Turntable? The one missing piece for me has always been a good song lyrics site. Sure, there are lots of lyrics engines on the web, but they’re all dreadfully designed, spam-centric, and just scuzzy feeling in general. Search for lyrics for your favorite song on Google, and you’re bound to end up looking at popup ads for diet pills.

If you’re like me, this makes you sad. And irritated. So there I was, bitching about this on Twitter one evening, when Seth Banks proposed that we actually do something about it. So we did.

Last week, Seth and I launched Lyricful, which aims to be the first classy lyrics site. We started with a nice clean design and paired it with a sizable lyrics database (growing every day), an intuitive search interface, and some SEO know-how. We’ve also added a few other things we felt fans would find useful, like song previews, concert information, and easy sharing features.

Most importantly, Lyricful isn’t running any invasive eyeball-bleeding advertising. The only ads on the site are in the form of song preview links and concert ticket referrals, relevant to the artist you’re currently browsing (which directly benefits the artist). We built this for ourselves, as music lovers and fans, which means that we wanted to make it as easy as possible to get right to transcribing, discussing, and sharing your favorite lyrics.

Since the soft launch, we’ve started working with a number of artists who were interested in Lyricful and its sister site, MusicNewsHQ. To start with, we’ve added featured / verified artist spots, which will help promote up and coming artists and ensure accuracy of the site contents. Double win. I’m really looking forward to seeing how this project evolves, having both artists and their fans involved in the process. Got feedback for us? We’d love to hear it!

comments »


 

Startup Workaway - Coworking in Costa Rica

May 03, 2011

Are you a hacker? Working on a startup? How about coworking and networking with other people who are building inspiring new stuff? Sure you do.

And who doesn’t like the rain forest? See also: howler monkeys.

StartupWorkaway is something Nick, Zach, and I have been talking about for quite awhile. We actually built most of the website back in February when we rented a house together up in Quebec City during Winter Carnaval. Nick polished it up and posted it to Hacker News the other day and the response has been pretty incredible so far.

Visit StartupWorkaway for all the gory details. The application deadline is May 6th.

comments »


 

Easy Rails OAuth Integration Testing

March 19, 2011

A long while back I wrote a post about Twitter auth integration testing using Cucumber. Back then OAuth was just beginning to see widespread use, but it was nowhere near as popular as it seems to be today. In my latest side project, all logins are from Twitter or Facebook because there’s simply no need for local accounts (due to the nature of the app). But that also means that we needed an easy way to support third-party authentication in our integration tests.

We’re using OmniAuth to handle auth here, which is absolutely awesome. And we’re also using the equally-awesome Capybara for integration testing. Cucumber is no longer part of my standard testing repertoire. I could go into a long rambling post as to why, but chances are you already have some pretty good ideas about that. If you haven’t felt that pain, and Cucumber works for you, that’s great. Seriously. I’m extremely happy for you.

On the other hand, if you’re dissatisfied… Capy 1.0 comes with a swanky new minimalist RSpec-friendly acceptance testing DSL that you really ought to take a look at. The 1.0 gem hasn’t been released yet, but you can pull it into your project directly from the GitHub source if you specify it in your Gemfile. It’s really quite nice and simple, and I’m very happy with it so far.

If you want OAuth integration testing to be easy, you’ll want to make sure you’re using the OmniAuth 0.2.0 or later. Among it’s many fine features is a new integration test_mode that makes testing OAuth logins less sucky than it’s ever been before. Fortunately for us, Capybara and OmniAuth go together like giant chocholate swamp rat and creamy peanut butter. It pretty much works right out of the box.

To demonstrate, let’s add a nice little integration auth helper method that we can use in our request specs to log a user in (I like to put this in spec/support/integration_spec_helper.rb):

module IntegrationSpecHelper
  def login_with_oauth(service = :twitter)
    visit "/auth/#{service}"
  end
end

Now, in your spec_helper.rb, add the following:

RSpec.configure do |config|
  # ...
  config.include IntegrationSpecHelper, :type => :request
end

Capybara.default_host = 'http://example.org'

OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:twitter, {
  :uid => '12345',
  :nickname => 'zapnap'
})

Finally, let’s take a look at an example RSpec request spec that leverages this helper to test a workflow where a user login is required:

feature 'testing oauth' do
  scenario 'should create a new tiger' do
    login_with_oauth
    visit new_tiger_path

    fill_in 'tiger_name', :with => 'Charlie'
    fill_in 'tiger_blood', :with => 'yes'
    
    click_on 'Create Tiger'
    
    page.should have_content("Thanks! You are a winner!")
  end
end

That’s it. OmniAuth’s test mode automatically mocks out the authentication workflow, allowing us to supply our own auth results hash. Requests made to the usual /auth/provider URL will redirect immediately to the provider callback, which means we never have to hit the actual provider while testing (which also means that webmock or fakeweb remain happy).

This might seem simple, and it is. And that’s kind of a big deal. So now we can get back to writing stuff that isn’t plumbing and focusing on solving real problems ;).

comments »


 

Better AWS Access Control with IAM (and Fog)

March 02, 2011

Amazon are the undisputed kings of the developer plumbing revolution. EC2 and S3 are so ubiquitious to deploying web applications that sometimes it seems difficult to remember when we had to self-host so much of this stuff. But sometimes managing my access keys can be a pain. Or even worse: a security hazard.

On one hand, I don’t want to create a new account with different billing information for every single client project demo or collaboration that I roll out. Yet on the other, I’m hesitant to use my own set of keys in places where another person might have access to them. One set of keys to the entire kingdom seems like a bad idea.

The solution to this problem is Amazon’s Identity and Access Management product beta. IAM allows you to create different users and groups, and attach those to your account. So, for example, if you’re a company or a group of people who needs to share a single AWS account, you could create separate identities with separate security credentials (access key ID + secret access key) for each individual. Or if you run several different projects and want to segregate those by account, you can do that too.

Even better, you can control which AWS services that those users (or groups) have access to via policy documents. A common example might be giving a user identified as ‘projectx’ access to only a ‘projectx’ S3 bucket and no others. This way, you can keep your accounts separate, and limit them to only what they need. And if you ever need to revoke or regenerate keys because a single application is compromised, it’s far less of a problem.

Anyway, outside of Amazon’s own documentation, I couldn’t find a hell of a lot of information about using IAM in the real world, so I thought I’d demonstrate how I’m using it with Fog, which is an absolutely awesome cloud services library for Ruby.

Annotated example code seems like the right way to go here. Let’s generate a new IAM user and a set of keys for him, and give him permission to only a single bucket, which we’ll create (note that your bucket name must be unique so don’t use the sample one shown here).

First, we set up the new user name and the bucket name that we want to create, and establish our AWS credentials:

require 'fog'

username = 'testuser'
bucket = 'uniquebucketname1234'

aws_credentials = { 
  :aws_access_key_id => 'YOUR-ACCESS-KEY-ID', 
  :aws_secret_access_key => 'YOUR-SECRET-ACCESS-KEY'
}

Creating a new S3 bucket is easy with Fog:

storage = Fog::Storage.new(aws_credentials.merge(:provider => 'AWS'))
storage.put_bucket(bucket)

Fog also provides a lightweight wrapper around IAM API methods. Using this, we can create the new user and create an access key. Assuming this is successful, we’ll end up with the user ARN (which we’ll need later) and a new set of credentials.

iam = Fog::AWS::IAM.new(aws_credentials)

user_response = iam.create_user(username)
key_response  = iam.create_access_key('UserName' => username)

access_key_id     = key_response.body['AccessKey']['AccessKeyId']
secret_access_key = key_response.body['AccessKey']['SecretAccessKey']
arn               = user_response.body['User']['Arn']

We want to give this user the ability to manage their own keys (as shown in Amazon’s Getting Started guide so we create our first policy document as they suggest.

iam.put_user_policy(username, 'UserKeyPolicy', {
  'Statement' => [
    'Effect' => 'Allow',
    'Action' => 'iam:*AccessKey*',
    'Resource' => arn 
  ]
})

We also want to grant them access to the bucket we created. But only our bucket and no others. If you think constructing policy documents by hand is shitty, you’re right. Fortunately the AWS Policy Generator can help us generate the nasty bits. Check it out.

iam.put_user_policy(username, 'UserS3Policy', {
  'Statement' => [
    {   
      'Effect' => 'Allow',
      'Action' => ['s3:*'],
      'Resource' => [
        "arn:aws:s3:::#{bucket_name}",
        "arn:aws:s3:::#{bucket_name}/*"
      ]   
    }, {
      'Effect' => 'Deny',
      'Action' => ['s3:*'],
      'NotResource' => [
        "arn:aws:s3:::#{bucket_name}",
        "arn:aws:s3:::#{bucket_name}/*"
      ]   
    }   
  ]
})

Finally, let’s reset our IAM access credentials and verify we can access the bucket and upload a file to it:

aws_credentials = { 
  :aws_access_key_id => access_key_id,
  :aws_secret_access_key => secret_access_key
}

storage = Fog::Storage.new(aws_credentials.merge(:provider => 'AWS'))
storage.get_bucket(bucket)
storage.put_object(bucket, 'image.png', File.open('/path/to/image.png'))

Note that you can use the keys that are generated in place of your normal account keys pretty much anywhere, as demonstrated. However, if you’re limiting bucket access as we’ve done here, be aware that you won’t be able to do a list command to retrieve all buckets accessible to a user. I had an incredibly frustrating couple of hours the other day trying to figure out why s3cmd and the Firefox S3 Organizer wouldn’t work with my newly generated IAM keys, and the reason was simply that they were trying to get a list of buckets first, which was returning an access denied message. D’oh. (and thanks to crazed for helping me see the light on that.)

If you’re not a Ruby developer, or you just need command line access, you can also use the IAM command line tools provided by Amazon, and you should definitely check out their Getting Started guide. Hopefully IAM will find its way into the AWS Console so routine management tasks are a bit easier in the future. In the meantime, creating policy documents by hand is a big pain in the ass, but at least the Policy Generator takes a little bit of the suck out of it.

Get the full example source

comments »


 

Stomp in the Android Market

February 26, 2011

My pal and “prolific prototyper” Nick Tommarello just released his latest project into the wild… Stomp.io is a social adventure and travel guide that aims to make it easier for you to find fun things to do while at home or on the go. Stomp debuted at the Launch conference run by former TC50 founder Jason Calacanis, and has gotten some great media coverage over the past couple days. Like pitches? You can watch Nick’s presentation here.

If you’re in SF, NYC, or Boston, make sure to give it a try. Hopefully it won’t take too long to spread to other areas too. Some of the challenges are… kind of nuts. Others are a bit more laid back. And there’s a lot more in-between so I’m pretty sure you’ll find something you like there. You can install the mobile version of the app on both the iPhone and Android

Oh and if you’re having problems with the Android version of the app you can yell at me. Working with Nick on Stomp was a great excuse for me to level up on Android development. There’s no better way to come up to speed on a new platform than to dive in and deliver a working application. That said, the app is still young and a bit sparse, and I’m sure there are still a few bugs hiding in the woods; Crash reports and feature requests are very welcome :). Thanks in advance!

comments »


 
← Older